commit 3d36f2421ec83a41ee4f0958ea10dc7f6ed71854 Author: penguinehis Date: Wed Jul 22 17:42:32 2026 -0300 Launch diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7129750 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.gradle/ +.idea/ +**/build/ +**/.cxx/ +local.properties +*.iml +captures/ +.externalNativeBuild/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..87377bb --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# DragonSSH XHTTP Public Example + +A deliberately small Android reference client for the DragonSSH XHTTP-SSH transport. +It contains one home screen, the XHTTP split-stream transport, SSH password authentication, +an Android `VpnService`, a local SOCKS relay, and BadVPN tun2socks with UDPGW. + +## Public scope + +Included: + +- XHTTP over TLS/HTTP/2 +- streamed downlink: `GET {path}/{sessionId}` +- ordered packet uplink: `POST {path}/{sessionId}/{sequence}` +- simple Server, Port, SNI, XHTTP Host, and XHTTP Path configuration +- SSH username/password authentication +- silent SSH/XHTTP transport reconnect while the Android VPN remains established +- configurable VPN DNS resolvers +- UDPGW forwarding through BadVPN tun2socks +- separate Home, Settings, VPN/UDPGW, and Logs screens + +Not included: + +- paid application screens, branding, panel URLs, certificates, signing fingerprints, or secrets +- remote/online configuration +- config import/export or config protection +- Xray, DNSTT/SlowDNS, SSL payload modes, ads, analytics, accounts, profiles, or reseller features +- production server addresses or credentials + +The large `com.trilead`, `com.jcraft`, `org.spongycastle`, and `badvpn` trees are vendored +transport dependencies. The native build compiles only `libancillary`, the socket-protection +bridge, and the UDPGW-enabled `tun2socks` executable. + +## Dependency note + +The ConnectBot bcrypt fork is resolved from Maven Central as `org.connectbot:jbcrypt:1.0.2`. The former relocated coordinate `org.connectbot.jbcrypt:jbcrypt:1.0.0` is intentionally not used. + +## Build fix in 1.0.7 + +- Corrected the password storage notice so it compiles with Android AAPT2. +- Updated the password storage notice and its resource reference. +- No tunnel behavior or saved-password behavior changed. + +## Build + +1. Open the root folder in Android Studio. +2. Install Android SDK 36 and an Android NDK compatible with Gradle's `ndkBuild` integration. +3. Let Android Studio create `local.properties` with your SDK path. +4. Build and install the `app` module. + +The minimum Android version is API 26. Four ABIs are enabled: ARMv7, ARM64, x86, and x86_64. + +## System-bar behavior + +The sample handles Android edge-to-edge window insets explicitly. The configuration screen adds +safe padding for the status bar, display cutouts, gesture navigation, three-button navigation, +and the on-screen keyboard, so controls remain visible on Android 15/16 and earlier versions. + +## Configuration mapping + +The app uses the same names as SocksRevive VOID: + +- **Server**: the XHTTP proxy IP address or hostname. +- **Port**: the XHTTP listener port, normally `443`. +- **User name / Password**: SSH credentials carried inside the XHTTP stream. +- **SNI**: the hostname sent during the TLS handshake. +- **XHTTP Host**: the CDN or reverse-proxy routing hostname. +- **XHTTP Path**: the base path, such as `/ssh`. +- **XHTTP TLS**: enables TLS and HTTP/2 for the XHTTP connection. + +There is no separate SSH destination. **Server is the XHTTP proxy**, and the XHTTP session exposes the SSH byte stream used for authentication. + +## TLS certificate behavior + +To match the original SocksRevive VOID implementation, XHTTP TLS intentionally accepts every server certificate and skips hostname verification. Self-signed, expired, mismatched, and otherwise untrusted certificates are accepted. SNI is still sent for CDN/fronting routing, but it is not used to validate the certificate. + +This is intentionally insecure against man-in-the-middle attacks and is included only because it is required by this transport example. There is no certificate-validation toggle in the sample. + +## Log viewer + +The sample log is structured rather than rendered as one large text block: + +- timestamp per entry +- INFO, WARNING, ERROR, DEBUG, or VERBOSE badge +- color-coded rows +- DEBUG/VERBOSE filter +- follow-newest toggle +- copy and clear actions + +Passwords are never written to the log. + +## Protocol behavior + +The SSH library receives a virtual duplex socket: + +- its input stream is the body of one long-lived XHTTP GET response; +- its output stream is buffered and sent as strictly ordered POST requests; +- only one POST is in flight at a time, preserving sequence order; +- bounded buffering applies backpressure instead of creating an unbounded HTTP request queue; +- control sockets are excluded from the Android VPN to prevent routing loops. + +See [`docs/XHTTP_PROTOCOL.md`](docs/XHTTP_PROTOCOL.md) for the request contract. + +## License + +The combined sample is distributed under GPL-3.0-or-later because its VPN layer contains +GPL-covered source. Third-party components retain their own notices; see +[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) and license files kept beside native sources. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..30efe37 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,16 @@ +# Third-party notices + +This repository includes or derives from the following components. Their original copyright +headers and license files remain in the source tree where available. + +- **Psiphon Android tunnel/VPN code** — GPL-3.0-or-later. Relevant source files retain the original notices. +- **Trilead SSH-2 for Java** — BSD-style license; copyright Trilead AG, ETH Zurich, and contributors. +- **JZlib** — BSD-style license; copyright JCraft, Inc. and contributors. +- **Bouncy Castle / Spongy Castle-derived cryptographic classes** — permissive Bouncy Castle license; original headers retained. +- **BadVPN tun2socks** — BSD 3-Clause-style license. Full text: `service/src/main/jni/badvpn/COPYING`. +- **lwIP** — BSD-style license. Full text: `service/src/main/jni/badvpn/lwip/COPYING`. +- **libancillary** — BSD-style license. Full text: `service/src/main/jni/libancillary/COPYING`. +- **Material icons and AndroidX/Material libraries** — respective Apache-2.0 and upstream licenses. + +The top-level GPL license governs the combined sample without replacing the independent notices +and attribution requirements of these components. diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..e62be99 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,40 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'com.dragonssh.xhttpdemo' + compileSdk 36 + + defaultConfig { + applicationId 'com.dragonssh.xhttpdemo' + minSdk 26 + targetSdk 36 + versionCode 8 + versionName '1.0.7' + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + // tun2socks is a native executable that must be run from nativeLibraryDir, + // which is the only app-owned location Android lets you exec from (API 29+ + // blocks exec out of the writable data dir under SELinux). Legacy packaging + // extracts libtun2socks.so to nativeLibraryDir at install so CustomNativeLoader + // can run it directly. Without this the loader falls back to files/libtun2socks + // and exec fails with "error=13, Permission denied". + packagingOptions { + jniLibs { + useLegacyPackaging = true + } + } +} + +dependencies { + implementation project(':service') + implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'com.google.android.material:material:1.13.0' + implementation 'androidx.recyclerview:recyclerview:1.4.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..243f697 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1 @@ +# Public example: no custom obfuscation rules required. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a8eae7a --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/ConfigUtils.java b/app/src/main/java/com/dragonssh/xhttpdemo/ConfigUtils.java new file mode 100644 index 0000000..e908242 --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/ConfigUtils.java @@ -0,0 +1,44 @@ +package com.dragonssh.xhttpdemo; + +final class ConfigUtils { + private ConfigUtils() { } + + static String normalizePath(String path) { + if (path == null || path.trim().isEmpty()) return ""; + String normalized = path.trim(); + if (!normalized.startsWith("/")) normalized = "/" + normalized; + while (normalized.length() > 1 && normalized.endsWith("/")) { + normalized = normalized.substring(0, normalized.length() - 1); + } + return normalized; + } + + static boolean isValidPort(String value) { + try { + int port = Integer.parseInt(value); + return port >= 1 && port <= 65535; + } catch (NumberFormatException e) { + return false; + } + } + + static boolean isHostPort(String value) { + if (value == null || value.trim().isEmpty()) return false; + String clean = value.trim(); + int separator = clean.lastIndexOf(':'); + if (separator <= 0 || separator == clean.length() - 1) return false; + return isValidPort(clean.substring(separator + 1)); + } + + static String displayValue(String value) { + return value == null || value.trim().isEmpty() ? "—" : value.trim(); + } + + static String formatHostPort(String host, String port) { + String cleanHost = displayValue(host); + if (cleanHost.contains(":") && !cleanHost.startsWith("[") && !"—".equals(cleanHost)) { + cleanHost = "[" + cleanHost + "]"; + } + return cleanHost + ":" + displayValue(port); + } +} diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/DemoApplication.java b/app/src/main/java/com/dragonssh/xhttpdemo/DemoApplication.java new file mode 100644 index 0000000..d572d4e --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/DemoApplication.java @@ -0,0 +1,43 @@ +package com.dragonssh.xhttpdemo; + +import android.app.Application; +import android.content.SharedPreferences; + +import com.dragonssh.xhttpdemo.core.XHttpSshCore; +import com.dragonssh.xhttpdemo.core.config.Settings; + +public final class DemoApplication extends Application { + private static final String BOOTSTRAP_PREFS = "demo_bootstrap"; + private static final String DEFAULTS_CREATED = "defaults_created"; + + private volatile String savedPassword = ""; + private Settings settings; + + @Override + public void onCreate() { + super.onCreate(); + SharedPreferences prefs = getSharedPreferences(BOOTSTRAP_PREFS, MODE_PRIVATE); + if (!prefs.getBoolean(DEFAULTS_CREATED, false)) { + Settings.setDefaultConfig(this); + prefs.edit().putBoolean(DEFAULTS_CREATED, true).apply(); + } + settings = new Settings(this); + savedPassword = settings.getPrivString(Settings.SSH_PASSWORD_KEY); + XHttpSshCore.init(this); + } + + String getSessionPassword() { + return savedPassword; + } + + void setSessionPassword(String password) { + savedPassword = password == null ? "" : password; + settings.getPrefsPrivate().edit() + .putString(Settings.SSH_PASSWORD_KEY, savedPassword) + .apply(); + } + + boolean hasSessionPassword() { + return savedPassword != null && !savedPassword.isEmpty(); + } +} diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/LogAdapter.java b/app/src/main/java/com/dragonssh/xhttpdemo/LogAdapter.java new file mode 100644 index 0000000..5b3a47d --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/LogAdapter.java @@ -0,0 +1,216 @@ +package com.dragonssh.xhttpdemo; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.recyclerview.widget.RecyclerView; + +import com.dragonssh.xhttpdemo.core.logger.LogItem; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.google.android.material.card.MaterialCardView; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +final class LogAdapter extends RecyclerView.Adapter { + private static final int MAX_STORED_ENTRIES = 500; + private static final int MAX_VISIBLE_ENTRIES = 250; + + private final Context context; + private final List allEntries = new ArrayList<>(); + private final List visibleEntries = new ArrayList<>(); + private final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.US); + private boolean showDebug; + + LogAdapter(Context context) { + this.context = context.getApplicationContext(); + } + + void replace(LogItem[] items) { + allEntries.clear(); + if (items != null) { + int start = Math.max(0, items.length - MAX_STORED_ENTRIES); + for (int i = start; i < items.length; i++) { + Entry entry = toEntry(items[i]); + if (entry != null) allEntries.add(entry); + } + } + rebuildVisible(); + } + + void add(LogItem item) { + Entry entry = toEntry(item); + if (entry == null) return; + + allEntries.add(entry); + while (allEntries.size() > MAX_STORED_ENTRIES) allEntries.remove(0); + + if (isVisible(entry)) { + visibleEntries.add(entry); + notifyItemInserted(visibleEntries.size() - 1); + if (visibleEntries.size() > MAX_VISIBLE_ENTRIES) { + visibleEntries.remove(0); + notifyItemRemoved(0); + } + } + } + + void setShowDebug(boolean showDebug) { + if (this.showDebug == showDebug) return; + this.showDebug = showDebug; + rebuildVisible(); + } + + void clear() { + int count = visibleEntries.size(); + allEntries.clear(); + visibleEntries.clear(); + if (count > 0) notifyItemRangeRemoved(0, count); + } + + boolean isEmpty() { + return visibleEntries.isEmpty(); + } + + String asPlainText() { + StringBuilder out = new StringBuilder(); + for (Entry entry : visibleEntries) { + if (out.length() > 0) out.append('\n'); + out.append(entry.time) + .append(" [") + .append(entry.level.name()) + .append("] ") + .append(entry.message); + } + return out.toString(); + } + + @NonNull + @Override + public LogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_log, parent, false); + return new LogViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull LogViewHolder holder, int position) { + Entry entry = visibleEntries.get(position); + int accent = colorFor(entry.level); + + holder.time.setText(entry.time); + holder.level.setText(entry.level.name()); + holder.message.setText(entry.message); + holder.card.setStrokeColor(accent); + + Drawable background = holder.level.getBackground(); + if (background != null) { + Drawable wrapped = DrawableCompat.wrap(background.mutate()); + DrawableCompat.setTintList(wrapped, ColorStateList.valueOf(accent)); + holder.level.setBackground(wrapped); + } + } + + @Override + public int getItemCount() { + return visibleEntries.size(); + } + + private void rebuildVisible() { + visibleEntries.clear(); + for (Entry entry : allEntries) { + if (isVisible(entry)) visibleEntries.add(entry); + } + while (visibleEntries.size() > MAX_VISIBLE_ENTRIES) visibleEntries.remove(0); + notifyDataSetChanged(); + } + + private boolean isVisible(Entry entry) { + return showDebug + || (entry.level != SkStatus.LogLevel.DEBUG + && entry.level != SkStatus.LogLevel.VERBOSE); + } + + private Entry toEntry(LogItem item) { + if (item == null) return null; + String raw; + try { + raw = item.getString(context); + } catch (Exception e) { + raw = item.getMessage(); + } + if (raw == null) return null; + + String clean = Html.fromHtml(raw, Html.FROM_HTML_MODE_LEGACY) + .toString() + .replace('\u00A0', ' ') + .trim(); + if (clean.isEmpty()) return null; + + long timestamp = item.getLogtime(); + SkStatus.LogLevel level = item.getLogLevel() != null + ? item.getLogLevel() : SkStatus.LogLevel.INFO; + return new Entry(timeFormat.format(new Date(timestamp)), level, clean); + } + + private int colorFor(SkStatus.LogLevel level) { + int resource; + switch (level) { + case ERROR: + resource = R.color.log_error; + break; + case WARNING: + resource = R.color.log_warning; + break; + case DEBUG: + resource = R.color.log_debug; + break; + case VERBOSE: + resource = R.color.log_verbose; + break; + case INFO: + default: + resource = R.color.log_info; + break; + } + return ContextCompat.getColor(context, resource); + } + + static final class LogViewHolder extends RecyclerView.ViewHolder { + final MaterialCardView card; + final TextView time; + final TextView level; + final TextView message; + + LogViewHolder(@NonNull View itemView) { + super(itemView); + card = itemView.findViewById(R.id.log_card); + time = itemView.findViewById(R.id.log_time); + level = itemView.findViewById(R.id.log_level); + message = itemView.findViewById(R.id.log_message); + } + } + + private static final class Entry { + final String time; + final SkStatus.LogLevel level; + final String message; + + Entry(String time, SkStatus.LogLevel level, String message) { + this.time = time; + this.level = level; + this.message = message; + } + } +} diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/LogsActivity.java b/app/src/main/java/com/dragonssh/xhttpdemo/LogsActivity.java new file mode 100644 index 0000000..232115d --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/LogsActivity.java @@ -0,0 +1,91 @@ +package com.dragonssh.xhttpdemo; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.os.Bundle; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.dragonssh.xhttpdemo.core.logger.LogItem; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.google.android.material.switchmaterial.SwitchMaterial; + +public final class LogsActivity extends AppCompatActivity implements SkStatus.LogListener { + private RecyclerView logList; + private LogAdapter logAdapter; + private SwitchMaterial followLogsSwitch; + private SwitchMaterial showDebugLogsSwitch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_logs); + WindowInsetsHelper.apply(this, R.id.logs_root); + + logList = findViewById(R.id.log_list); + followLogsSwitch = findViewById(R.id.follow_logs); + showDebugLogsSwitch = findViewById(R.id.show_debug_logs); + + logAdapter = new LogAdapter(this); + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + layoutManager.setStackFromEnd(true); + logList.setLayoutManager(layoutManager); + logList.setAdapter(logAdapter); + logList.setItemAnimator(null); + + findViewById(R.id.back).setOnClickListener(v -> finish()); + findViewById(R.id.copy_logs).setOnClickListener(v -> copyLogs()); + findViewById(R.id.clear_logs).setOnClickListener(v -> SkStatus.clearLog()); + showDebugLogsSwitch.setOnCheckedChangeListener((button, checked) -> { + logAdapter.setShowDebug(checked); + if (followLogsSwitch.isChecked()) scrollLogsToBottom(); + }); + } + + @Override + protected void onStart() { + super.onStart(); + SkStatus.addLogListener(this); + logAdapter.replace(SkStatus.getlogbuffer()); + scrollLogsToBottom(); + } + + @Override + protected void onStop() { + SkStatus.removeLogListener(this); + super.onStop(); + } + + @Override + public void newLog(LogItem logItem) { + runOnUiThread(() -> { + logAdapter.add(logItem); + if (followLogsSwitch.isChecked()) scrollLogsToBottom(); + }); + } + + @Override + public void onClear() { + runOnUiThread(logAdapter::clear); + } + + private void scrollLogsToBottom() { + if (logAdapter == null || logAdapter.getItemCount() == 0) return; + logList.post(() -> logList.scrollToPosition(logAdapter.getItemCount() - 1)); + } + + private void copyLogs() { + if (logAdapter.isEmpty()) { + Toast.makeText(this, R.string.no_logs_to_copy, Toast.LENGTH_SHORT).show(); + return; + } + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(ClipData.newPlainText( + "DragonSSH XHTTP logs", logAdapter.asPlainText())); + Toast.makeText(this, R.string.logs_copied, Toast.LENGTH_SHORT).show(); + } +} diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/MainActivity.java b/app/src/main/java/com/dragonssh/xhttpdemo/MainActivity.java new file mode 100644 index 0000000..dee68ed --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/MainActivity.java @@ -0,0 +1,220 @@ +package com.dragonssh.xhttpdemo; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.VpnService; +import android.os.Build; +import android.os.Bundle; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.dragonssh.xhttpdemo.core.config.Settings; +import com.dragonssh.xhttpdemo.core.logger.ConnectionStatus; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.dragonssh.xhttpdemo.core.tunnel.TunnelManagerHelper; +import com.google.android.material.button.MaterialButton; + +public final class MainActivity extends AppCompatActivity implements SkStatus.StateListener { + private static final int NOTIFICATION_PERMISSION_REQUEST = 42; + + private Settings settings; + private TextView statusView; + private TextView serverValue; + private TextView usernameValue; + private TextView sniValue; + private TextView hostValue; + private TextView pathValue; + private TextView tlsModeValue; + private TextView xhttpTlsValue; + private MaterialButton startStopButton; + private boolean tunnelActive; + + private final ActivityResultLauncher settingsLauncher = + registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> refreshSummary()); + + private final ActivityResultLauncher vpnPermissionLauncher = + registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() == RESULT_OK) { + startTunnel(); + } else { + Toast.makeText(this, R.string.vpn_permission_required, Toast.LENGTH_LONG).show(); + } + }); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + WindowInsetsHelper.apply(this, R.id.main_root); + + settings = new Settings(this); + bindViews(); + + startStopButton.setOnClickListener(v -> { + if (tunnelActive || SkStatus.isTunnelActive()) { + TunnelManagerHelper.stopXHttpSsh(this); + } else { + requestVpnAndConnect(); + } + }); + findViewById(R.id.open_settings).setOnClickListener(v -> openSettings()); + findViewById(R.id.open_vpn_settings).setOnClickListener(v -> + settingsLauncher.launch(new Intent(this, VpnSettingsActivity.class))); + findViewById(R.id.open_logs).setOnClickListener(v -> + startActivity(new Intent(this, LogsActivity.class))); + + refreshSummary(); + requestNotificationPermissionIfNeeded(); + } + + @Override + protected void onStart() { + super.onStart(); + SkStatus.addStateListener(this); + refreshSummary(); + } + + @Override + protected void onStop() { + SkStatus.removeStateListener(this); + super.onStop(); + } + + private void bindViews() { + statusView = findViewById(R.id.status); + serverValue = findViewById(R.id.summary_server); + usernameValue = findViewById(R.id.summary_username); + sniValue = findViewById(R.id.summary_sni); + hostValue = findViewById(R.id.summary_xhttp_host); + pathValue = findViewById(R.id.summary_xhttp_path); + tlsModeValue = findViewById(R.id.summary_tls_mode); + xhttpTlsValue = findViewById(R.id.summary_xhttp_tls); + startStopButton = findViewById(R.id.start_stop); + } + + private void refreshSummary() { + if (settings == null) return; + String server = settings.getPrivString(Settings.XHTTP_ENDPOINT_KEY); + String port = settings.getPrivString(Settings.XHTTP_PORT_KEY); + String username = settings.getPrivString(Settings.USUARIO_KEY); + String sni = settings.getPrivString(Settings.CUSTOM_SNI); + String host = settings.getPrivString(Settings.XHTTP_HOST_KEY); + String path = ConfigUtils.normalizePath(settings.getPrivString(Settings.XHTTP_PATH_KEY)); + boolean tls = !"0".equals(settings.getPrivString(Settings.XHTTP_TLS_KEY)); + + serverValue.setText(ConfigUtils.formatHostPort(server, port)); + usernameValue.setText(ConfigUtils.displayValue(username)); + sniValue.setText(ConfigUtils.displayValue(sni)); + hostValue.setText(ConfigUtils.displayValue(host)); + pathValue.setText(ConfigUtils.displayValue(path)); + tlsModeValue.setText(tls ? R.string.tls_mode_automatic : R.string.tls_mode_disabled); + xhttpTlsValue.setText(tls ? R.string.enabled : R.string.disabled); + } + + private void openSettings() { + settingsLauncher.launch(new Intent(this, SettingsActivity.class)); + } + + private void requestVpnAndConnect() { + String error = validateSavedConfiguration(); + if (error != null) { + Toast.makeText(this, error, Toast.LENGTH_LONG).show(); + openSettings(); + return; + } + + DemoApplication application = (DemoApplication) getApplication(); + if (!application.hasSessionPassword()) { + Toast.makeText(this, R.string.password_needed_for_session, Toast.LENGTH_LONG).show(); + openSettings(); + return; + } + + Intent permissionIntent = VpnService.prepare(this); + if (permissionIntent != null) { + vpnPermissionLauncher.launch(permissionIntent); + } else { + startTunnel(); + } + } + + private String validateSavedConfiguration() { + String server = settings.getPrivString(Settings.XHTTP_ENDPOINT_KEY).trim(); + String port = settings.getPrivString(Settings.XHTTP_PORT_KEY).trim(); + String username = settings.getPrivString(Settings.USUARIO_KEY).trim(); + String path = ConfigUtils.normalizePath(settings.getPrivString(Settings.XHTTP_PATH_KEY)); + + if (server.isEmpty()) return getString(R.string.error_server_required); + if (!ConfigUtils.isValidPort(port)) return getString(R.string.error_port_invalid); + if (username.isEmpty()) return getString(R.string.error_username_required); + if (path.isEmpty()) return getString(R.string.error_path_required); + if (settings.getVpnDnsForward() && settings.getVpnDnsResolver().trim().isEmpty()) { + return getString(R.string.error_dns_primary_required); + } + if (settings.getVpnUdpForward() && !ConfigUtils.isHostPort(settings.getVpnUdpResolver())) { + return getString(R.string.error_udpgw_invalid); + } + return null; + } + + private void startTunnel() { + DemoApplication application = (DemoApplication) getApplication(); + String password = application.getSessionPassword(); + if (password.isEmpty()) { + Toast.makeText(this, R.string.password_needed_for_session, Toast.LENGTH_LONG).show(); + openSettings(); + return; + } + + String server = settings.getPrivString(Settings.XHTTP_ENDPOINT_KEY); + String port = settings.getPrivString(Settings.XHTTP_PORT_KEY); + String username = settings.getPrivString(Settings.USUARIO_KEY); + String sni = settings.getPrivString(Settings.CUSTOM_SNI); + String host = settings.getPrivString(Settings.XHTTP_HOST_KEY); + String path = ConfigUtils.normalizePath(settings.getPrivString(Settings.XHTTP_PATH_KEY)); + boolean tls = !"0".equals(settings.getPrivString(Settings.XHTTP_TLS_KEY)); + + statusView.setText(R.string.status_starting); + SkStatus.logInfo("Server: " + ConfigUtils.formatHostPort(server, port)); + SkStatus.logInfo("XHTTP: SNI=" + ConfigUtils.displayValue(sni) + + ", Host=" + ConfigUtils.displayValue(host) + + ", Path=" + ConfigUtils.displayValue(path) + + ", TLS=" + (tls ? "on" : "off")); + SkStatus.logInfo("SSH login: " + username + " (password hidden)"); + TunnelManagerHelper.startXHttpSsh(this, password); + } + + @Override + public void updateState(String state, String logMessage, int localizedResId, + ConnectionStatus level, Intent intent) { + runOnUiThread(() -> { + String label; + try { + label = getString(localizedResId); + } catch (Exception e) { + label = state; + } + statusView.setText(label); + tunnelActive = level != ConnectionStatus.LEVEL_NOTCONNECTED + && level != ConnectionStatus.LEVEL_AUTH_FAILED; + startStopButton.setText(tunnelActive ? R.string.stop : R.string.start); + }); + } + + private void requestNotificationPermissionIfNeeded() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) + != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.POST_NOTIFICATIONS}, + NOTIFICATION_PERMISSION_REQUEST); + } + } +} diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/SettingsActivity.java b/app/src/main/java/com/dragonssh/xhttpdemo/SettingsActivity.java new file mode 100644 index 0000000..e0df39e --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/SettingsActivity.java @@ -0,0 +1,132 @@ +package com.dragonssh.xhttpdemo; + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import com.dragonssh.xhttpdemo.core.config.Settings; +import com.google.android.material.switchmaterial.SwitchMaterial; +import com.google.android.material.textfield.TextInputLayout; + +public final class SettingsActivity extends AppCompatActivity { + private Settings settings; + private EditText serverField; + private EditText portField; + private EditText usernameField; + private EditText passwordField; + private EditText sniField; + private EditText xhttpHostField; + private EditText pathField; + private SwitchMaterial xhttpTlsSwitch; + private TextInputLayout passwordLayout; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + WindowInsetsHelper.apply(this, R.id.settings_root); + + settings = new Settings(this); + bindViews(); + loadConfiguration(); + + findViewById(R.id.back).setOnClickListener(v -> finish()); + findViewById(R.id.save_settings).setOnClickListener(v -> saveConfiguration()); + } + + private void bindViews() { + serverField = findViewById(R.id.server); + portField = findViewById(R.id.port); + usernameField = findViewById(R.id.username); + passwordField = findViewById(R.id.password); + sniField = findViewById(R.id.sni); + xhttpHostField = findViewById(R.id.xhttp_host); + pathField = findViewById(R.id.xhttp_path); + xhttpTlsSwitch = findViewById(R.id.xhttp_tls); + passwordLayout = findViewById(R.id.password_layout); + } + + private void loadConfiguration() { + serverField.setText(settings.getPrivString(Settings.XHTTP_ENDPOINT_KEY)); + portField.setText(settings.getPrivString(Settings.XHTTP_PORT_KEY)); + usernameField.setText(settings.getPrivString(Settings.USUARIO_KEY)); + sniField.setText(settings.getPrivString(Settings.CUSTOM_SNI)); + xhttpHostField.setText(settings.getPrivString(Settings.XHTTP_HOST_KEY)); + pathField.setText(settings.getPrivString(Settings.XHTTP_PATH_KEY)); + xhttpTlsSwitch.setChecked(!"0".equals(settings.getPrivString(Settings.XHTTP_TLS_KEY))); + + DemoApplication application = (DemoApplication) getApplication(); + passwordField.setText(application.getSessionPassword()); + passwordLayout.setHelperText(application.hasSessionPassword() + ? getString(R.string.password_loaded_for_session) + : getString(R.string.password_storage_notice)); + } + + private void saveConfiguration() { + String server = text(serverField); + String port = text(portField); + String username = text(usernameField); + String password = rawText(passwordField); + String path = ConfigUtils.normalizePath(text(pathField)); + + if (server.isEmpty()) { + showError(R.string.error_server_required); + serverField.requestFocus(); + return; + } + if (!ConfigUtils.isValidPort(port)) { + showError(R.string.error_port_invalid); + portField.requestFocus(); + return; + } + if (username.isEmpty()) { + showError(R.string.error_username_required); + usernameField.requestFocus(); + return; + } + + DemoApplication application = (DemoApplication) getApplication(); + if (password.isEmpty()) { + showError(R.string.error_password_required); + passwordField.requestFocus(); + return; + } + if (path.isEmpty()) { + showError(R.string.error_path_required); + pathField.requestFocus(); + return; + } + + SharedPreferences.Editor editor = settings.getPrefsPrivate().edit(); + editor.putString(Settings.XHTTP_ENDPOINT_KEY, server) + .putString(Settings.XHTTP_PORT_KEY, port) + .putString(Settings.USUARIO_KEY, username) + .putString(Settings.PORTA_LOCAL_KEY, "1080") + .putString(Settings.CUSTOM_SNI, text(sniField)) + .putString(Settings.XHTTP_HOST_KEY, text(xhttpHostField)) + .putString(Settings.XHTTP_PATH_KEY, path) + .putString(Settings.XHTTP_TLS_KEY, xhttpTlsSwitch.isChecked() ? "1" : "0") + .apply(); + + application.setSessionPassword(password); + + setResult(RESULT_OK); + Toast.makeText(this, R.string.settings_saved, Toast.LENGTH_SHORT).show(); + finish(); + } + + private void showError(int messageRes) { + Toast.makeText(this, messageRes, Toast.LENGTH_LONG).show(); + } + + private static String text(EditText view) { + return view.getText() == null ? "" : view.getText().toString().trim(); + } + + private static String rawText(EditText view) { + return view.getText() == null ? "" : view.getText().toString(); + } +} diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/VpnSettingsActivity.java b/app/src/main/java/com/dragonssh/xhttpdemo/VpnSettingsActivity.java new file mode 100644 index 0000000..70ae5bf --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/VpnSettingsActivity.java @@ -0,0 +1,96 @@ +package com.dragonssh.xhttpdemo; + +import android.os.Bundle; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import com.dragonssh.xhttpdemo.core.config.Settings; +import com.google.android.material.switchmaterial.SwitchMaterial; + +public final class VpnSettingsActivity extends AppCompatActivity { + private Settings settings; + private SwitchMaterial customDnsSwitch; + private EditText primaryDnsField; + private EditText secondaryDnsField; + private SwitchMaterial udpGatewaySwitch; + private EditText udpGatewayField; + private SwitchMaterial ipv6Switch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_vpn_settings); + WindowInsetsHelper.apply(this, R.id.vpn_settings_root); + + settings = new Settings(this); + bindViews(); + loadConfiguration(); + + customDnsSwitch.setOnCheckedChangeListener((button, checked) -> updateEnabledState()); + udpGatewaySwitch.setOnCheckedChangeListener((button, checked) -> updateEnabledState()); + findViewById(R.id.back).setOnClickListener(v -> finish()); + findViewById(R.id.save_vpn_settings).setOnClickListener(v -> saveConfiguration()); + } + + private void bindViews() { + customDnsSwitch = findViewById(R.id.use_custom_dns); + primaryDnsField = findViewById(R.id.dns_primary); + secondaryDnsField = findViewById(R.id.dns_secondary); + udpGatewaySwitch = findViewById(R.id.enable_udpgw); + udpGatewayField = findViewById(R.id.udpgw_address); + ipv6Switch = findViewById(R.id.enable_ipv6); + } + + private void loadConfiguration() { + customDnsSwitch.setChecked(settings.getVpnDnsForward()); + primaryDnsField.setText(settings.getVpnDnsResolver()); + secondaryDnsField.setText(settings.getVpnDnsResolverSecondary()); + udpGatewaySwitch.setChecked(settings.getVpnUdpForward()); + udpGatewayField.setText(settings.getVpnUdpResolver()); + // Switch shows IPv6 ENABLED; the stored flag is the inverse (disableIpv6Tunnel). + ipv6Switch.setChecked(!settings.getDisableIpv6Tunnel()); + updateEnabledState(); + } + + private void updateEnabledState() { + primaryDnsField.setEnabled(customDnsSwitch.isChecked()); + secondaryDnsField.setEnabled(customDnsSwitch.isChecked()); + udpGatewayField.setEnabled(udpGatewaySwitch.isChecked()); + } + + private void saveConfiguration() { + String primaryDns = text(primaryDnsField); + String secondaryDns = text(secondaryDnsField); + String udpGateway = text(udpGatewayField); + + if (customDnsSwitch.isChecked() && primaryDns.isEmpty()) { + Toast.makeText(this, R.string.error_dns_primary_required, Toast.LENGTH_LONG).show(); + primaryDnsField.requestFocus(); + return; + } + if (udpGatewaySwitch.isChecked() && !ConfigUtils.isHostPort(udpGateway)) { + Toast.makeText(this, R.string.error_udpgw_invalid, Toast.LENGTH_LONG).show(); + udpGatewayField.requestFocus(); + return; + } + + settings.getVpnPrefs().edit() + .putBoolean(Settings.DNSFORWARD_KEY, customDnsSwitch.isChecked()) + .putString(Settings.DNSRESOLVER_KEY, primaryDns) + .putString(Settings.DNSRESOLVER_SECONDARY_KEY, secondaryDns) + .putBoolean(Settings.UDPFORWARD_KEY, udpGatewaySwitch.isChecked()) + .putString(Settings.UDPRESOLVER_KEY, udpGateway) + .putBoolean(Settings.DISABLE_IPV6_TUNNEL_KEY, !ipv6Switch.isChecked()) + .apply(); + + setResult(RESULT_OK); + Toast.makeText(this, R.string.settings_saved, Toast.LENGTH_SHORT).show(); + finish(); + } + + private static String text(EditText view) { + return view.getText() == null ? "" : view.getText().toString().trim(); + } +} diff --git a/app/src/main/java/com/dragonssh/xhttpdemo/WindowInsetsHelper.java b/app/src/main/java/com/dragonssh/xhttpdemo/WindowInsetsHelper.java new file mode 100644 index 0000000..fe92eab --- /dev/null +++ b/app/src/main/java/com/dragonssh/xhttpdemo/WindowInsetsHelper.java @@ -0,0 +1,39 @@ +package com.dragonssh.xhttpdemo; + +import android.app.Activity; +import android.view.View; + +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; + +final class WindowInsetsHelper { + private WindowInsetsHelper() { } + + static void apply(Activity activity, int rootViewId) { + WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false); + View root = activity.findViewById(rootViewId); + if (root == null) return; + + final int initialLeft = root.getPaddingLeft(); + final int initialTop = root.getPaddingTop(); + final int initialRight = root.getPaddingRight(); + final int initialBottom = root.getPaddingBottom(); + + ViewCompat.setOnApplyWindowInsetsListener(root, (view, windowInsets) -> { + Insets systemBars = windowInsets.getInsets( + WindowInsetsCompat.Type.systemBars() + | WindowInsetsCompat.Type.displayCutout()); + Insets ime = windowInsets.getInsets(WindowInsetsCompat.Type.ime()); + + view.setPadding( + initialLeft + systemBars.left, + initialTop + systemBars.top, + initialRight + systemBars.right, + initialBottom + Math.max(systemBars.bottom, ime.bottom)); + return windowInsets; + }); + ViewCompat.requestApplyInsets(root); + } +} diff --git a/app/src/main/res/drawable/ic_launcher.xml b/app/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 0000000..d384df0 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/drawable/log_background.xml b/app/src/main/res/drawable/log_background.xml new file mode 100644 index 0000000..87b0163 --- /dev/null +++ b/app/src/main/res/drawable/log_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/log_level_badge.xml b/app/src/main/res/drawable/log_level_badge.xml new file mode 100644 index 0000000..6bbf7ad --- /dev/null +++ b/app/src/main/res/drawable/log_level_badge.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/layout/activity_logs.xml b/app/src/main/res/layout/activity_logs.xml new file mode 100644 index 0000000..135bf08 --- /dev/null +++ b/app/src/main/res/layout/activity_logs.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..83374c7 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..d6557d6 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_vpn_settings.xml b/app/src/main/res/layout/activity_vpn_settings.xml new file mode 100644 index 0000000..578c0b3 --- /dev/null +++ b/app/src/main/res/layout/activity_vpn_settings.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_log.xml b/app/src/main/res/layout/item_log.xml new file mode 100644 index 0000000..cb6e1f1 --- /dev/null +++ b/app/src/main/res/layout/item_log.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..14cae17 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ + + #159FE6 + #101216 + #171A20 + #171A20 + #343945 + #F2F4F8 + #AEB6C4 + #3F8CFF + #D68A00 + #D94A4A + #687386 + #7C5BD6 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..adb2429 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,70 @@ + + DragonSSH XHTTP Example + Minimal XHTTP + SSH client with UDPGW. + + START + STOP + Connection Method + XHTTP + SSH + Settings + VPN / UDPGW + Logs + Current configuration + + SSH Tunnel + Authentication + XHTTP + Server + The XHTTP proxy IP or hostname. + Port + User name + Password + The password will be saved in private app storage. + The saved password is shown masked. Edit it to replace it. + SNI + TLS hostname, for example app.example.com. + XHTTP Host + Host used by your XHTTP server or CDN. + XHTTP Path + Example: /ssh + TLS MODE + Automatic (TLS 1.3 preferred) + Disabled + XHTTP TLS + Enabled + Disabled + SAVE + + + DNS + Use custom DNS + DNS 1 + DNS 2 + UDPGW + Enable UDPGW + UDPGW host:port + Example: 127.0.0.1:7300 + IPv6 + Enable IPv6 tunnel + Route IPv6 through the tunnel. Leave off if your server or network has no working IPv6. + + Copy + Clear + Follow newest entries + Show DEBUG and VERBOSE + Logs copied to the clipboard. + There are no logs to copy. + + Settings saved. + Disconnected + Starting… + VPN permission is required to connect. + Open Settings and save the SSH password. + Enter the Server. This is the XHTTP proxy IP or hostname. + Enter a valid port from 1 to 65535. + Enter the SSH user name. + Enter the SSH password. + Enter the XHTTP Path. + Enter DNS 1 or disable custom DNS. + Enter UDPGW as host:port. + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..5d08a41 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,11 @@ + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..209879f --- /dev/null +++ b/build.gradle @@ -0,0 +1,4 @@ +plugins { + id 'com.android.application' version '8.13.2' apply false + id 'com.android.library' version '8.13.2' apply false +} diff --git a/docs/XHTTP_PROTOCOL.md b/docs/XHTTP_PROTOCOL.md new file mode 100644 index 0000000..be0015d --- /dev/null +++ b/docs/XHTTP_PROTOCOL.md @@ -0,0 +1,58 @@ +# XHTTP-SSH request contract + +## Public configuration names + +The app exposes these names: + +- **Server** — the XHTTP proxy address that receives the TCP connection. +- **Port** — the XHTTP listener port. +- **SNI** — the hostname sent in the TLS handshake for CDN/fronting routing. +- **XHTTP Host** — the HTTP `Host` value used for CDN or reverse-proxy routing. +- **XHTTP Path** — the base request path. +- **User name / Password** — SSH authentication inside the XHTTP stream. + +The **Server is the XHTTP proxy**. There is no separate client-side SSH destination because the XHTTP session itself carries the raw SSH byte stream. + +XHTTP TLS uses a trust-all certificate manager and disables hostname verification, matching SocksRevive VOID. The configured SNI is still sent, but certificates are not validated. + + +## Session creation and downlink + +For each SSH transport connection, the client generates a random hexadecimal session ID and opens one long-lived request: + +```text +GET {basePath}/{sessionId} +Host: {xhttpHost} +Accept: */* +``` + +With TLS enabled, the URL host is the configured SNI. A custom DNS implementation pins that URL host to the configured Server, so the socket still connects to the XHTTP proxy address. The response body is consumed continuously as the server-to-client SSH stream. + +## Uplink + +Client-to-server SSH bytes use monotonically increasing POST sequence numbers: + +```text +POST {basePath}/{sessionId}/0 +POST {basePath}/{sessionId}/1 +POST {basePath}/{sessionId}/2 +... +Content-Type: application/octet-stream +``` + +Only one POST is in flight at a time. The client waits for a successful response before sending the next sequence, preserving order and preventing an unbounded reorder queue. Pending writes are coalesced up to 900 KiB per POST and use an 8 MiB bounded backpressure buffer. + +## Failure and reconnect + +A failed GET stream, failed or timed-out POST, SSH ping failure, or VPN-service loss closes the current XHTTP/SSH transport. For transient transport failures, the local SOCKS relay and Android TUN remain alive while the client creates a new XHTTP session and SSH connection. + +## Server expectations + +A compatible server must: + +1. map GET and POST requests by session ID; +2. stream server-to-client bytes immediately in the GET response body; +3. accept sequence-numbered POST bodies and append them in numeric order; +4. return success only after a POST body has been accepted for delivery; +5. close the session when either direction is unusable; +6. avoid website-style request rate limits on this path, because the POST requests are VPN tunnel packets rather than API traffic. diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3bfd21f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true +android.enableJetifier=true +org.gradle.configuration-cache=false diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 0000000..5429d6b --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e99bae143b75f9a10ead10248f02055e/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/658299a896470fbb3103ba3a430ee227/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e55dccbfe27cb97945148c61a39c89c5/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/dbd05c4936d573642f94cd149e1356c8/redirect +toolchainVendor=JETBRAINS +toolchainVersion=21 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..05ef575 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d441df3 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Jun 22 23:40:05 BRT 2021 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/service/.gitignore b/service/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/service/.gitignore @@ -0,0 +1 @@ +/build diff --git a/service/build.gradle b/service/build.gradle new file mode 100644 index 0000000..f795851 --- /dev/null +++ b/service/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'com.android.library' +} + +android { + namespace 'com.dragonssh.xhttpdemo.core' + compileSdk 36 + ndkVersion '27.0.12077973' + + defaultConfig { + minSdk 26 + targetSdk 36 + ndk { + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + } + } + + buildFeatures { + aidl false + buildConfig true + } + + externalNativeBuild { + ndkBuild { + path file('src/main/jni/Android.mk') + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'androidx.core:core:1.17.0' + implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'org.conscrypt:conscrypt-android:2.5.3' + api 'net.i2p.crypto:eddsa:0.3.0' + implementation 'org.connectbot:jbcrypt:1.0.2' +} diff --git a/service/proguard-rules.pro b/service/proguard-rules.pro new file mode 100644 index 0000000..ac0cdf7 --- /dev/null +++ b/service/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:/tools/adt-bundle-windows-x86_64-20131030/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +-keep class go.** { *; } +-keep class libv2ray.** { *; } +-keep class com.service.** { *; } diff --git a/service/src/main/AndroidManifest.xml b/service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ec89b7f --- /dev/null +++ b/service/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/MainReceiver.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/MainReceiver.java new file mode 100644 index 0000000..34e071f --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/MainReceiver.java @@ -0,0 +1,37 @@ +package com.dragonssh.xhttpdemo.core; + +import android.content.Context; +import android.content.Intent; +import android.content.BroadcastReceiver; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dragonssh.xhttpdemo.core.tunnel.TunnelManagerHelper; + +public class MainReceiver extends BroadcastReceiver +{ + public static final String ACTION_SERVICE_RESTART = "sshTunnelServiceRestsrt", + ACTION_SERVICE_STOP = "sshtunnelservicestop"; + + @Override + public void onReceive(Context context, Intent intent) + { + String acao = intent.getAction(); + + if (acao == null) { + return; + } + + switch (acao) { + + case ACTION_SERVICE_STOP: + TunnelManagerHelper.stopXHttpSsh(context); + break; + + case ACTION_SERVICE_RESTART: + Intent restartTunnel = new Intent(XHttpSshService.TUNNEL_SSH_RESTART_SERVICE); + LocalBroadcastManager.getInstance(context) + .sendBroadcast(restartTunnel); + break; + } + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/StatisticGraphData.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/StatisticGraphData.java new file mode 100644 index 0000000..c64567d --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/StatisticGraphData.java @@ -0,0 +1,282 @@ +package com.dragonssh.xhttpdemo.core; + + +import android.net.TrafficStats; +import android.os.SystemClock; + +import java.util.ArrayList; + +public class StatisticGraphData +{ + + private static StatisticGraphData statisticData; + private boolean m_displayDataTransferStats; + private DataTransferStats m_dataTransferStats; + + private StatisticGraphData() + { + m_dataTransferStats = new DataTransferStats(); + m_displayDataTransferStats = false; + + } + public static synchronized StatisticGraphData getStatisticData() + { + if (statisticData == null) + { + statisticData = new StatisticGraphData(); + } + + return statisticData; + } + public synchronized void setDisplayDataTransferStats(boolean displayDataTransferStats) + { + m_displayDataTransferStats = displayDataTransferStats; + } + + public synchronized boolean getDisplayDataTransferStats() + { + return m_displayDataTransferStats; + } + + public synchronized DataTransferStats getDataTransferStats() + { + return m_dataTransferStats; + } + + public class DataTransferStats + { + private boolean m_isConnected; + private long m_connectedTime; + + private long m_totalBytesSent; + private long m_totalBytesReceived; + + public final static long SLOW_BUCKET_PERIOD_MILLISECONDS = 5*60*1000; + public final static long FAST_BUCKET_PERIOD_MILLISECONDS = 1000; + public final static int MAX_BUCKETS = 24 * 60 / 5; + + private long mLastSent; + + private long mSent; + + private long mLastReceived; + + private long mReceived; + + private class Bucket + { + public long m_bytesSent = 0; + public long m_bytesReceived = 0; + } + + private ArrayList m_slowBuckets; + private long m_slowBucketsLastStartTime; + private ArrayList m_fastBuckets; + private long m_fastBucketsLastStartTime; + + DataTransferStats() + { + m_totalBytesSent = 0; + m_totalBytesReceived = 0; + + stop(); + } + + public synchronized void startSession() + { + + resetBytesTransferred(); + + } + + public synchronized void startConnected() + { + this.m_isConnected = true; + this.m_connectedTime = SystemClock.elapsedRealtime(); + + } + + public synchronized void stop() + { + + this.m_totalBytesReceived = 0; + this.m_totalBytesSent = 0; + this.m_isConnected = false; + this.m_connectedTime = 0; + resetBytesTransferred(); + } + + private void resetBytesTransferred() + { + long now = SystemClock.elapsedRealtime(); + this.m_slowBucketsLastStartTime = bucketStartTime(now, SLOW_BUCKET_PERIOD_MILLISECONDS); + this.m_slowBuckets = newBuckets(); + this.m_fastBucketsLastStartTime = bucketStartTime(now, FAST_BUCKET_PERIOD_MILLISECONDS); + this.m_fastBuckets = newBuckets(); + } + + public synchronized void addBytesSent(long bytes) + { + this.m_totalBytesSent += bytes; + + manageBuckets(); + addSentToBuckets(bytes); + } + + public synchronized void addBytesReceived(long bytes) + { + this.m_totalBytesReceived += bytes; + + manageBuckets(); + addReceivedToBuckets(bytes); + } + + private long bucketStartTime(long now, long period) + { + return period*(now/period); + } + + private ArrayList newBuckets() + { + ArrayList buckets = new ArrayList(); + for (int i = 0; i < MAX_BUCKETS; i++) + { + buckets.add(new Bucket()); + } + return buckets; + } + + private void shiftBuckets(long diff, long period, ArrayList buckets) + { + for (int i = 0; i < diff/period + 1; i++) + { + buckets.add(buckets.size(), new Bucket()); + if (buckets.size() >= MAX_BUCKETS) + { + buckets.remove(0); + } + } + } + + private void manageBuckets() + { + long now = SystemClock.elapsedRealtime(); + + long diff = now - this.m_slowBucketsLastStartTime; + if (diff >= SLOW_BUCKET_PERIOD_MILLISECONDS) + { + shiftBuckets(diff, SLOW_BUCKET_PERIOD_MILLISECONDS, m_slowBuckets); + this.m_slowBucketsLastStartTime = bucketStartTime(now, SLOW_BUCKET_PERIOD_MILLISECONDS); + } + + diff = now - this.m_fastBucketsLastStartTime; + if (diff >= FAST_BUCKET_PERIOD_MILLISECONDS) + { + shiftBuckets(diff, FAST_BUCKET_PERIOD_MILLISECONDS, m_fastBuckets); + this.m_fastBucketsLastStartTime = bucketStartTime(now, FAST_BUCKET_PERIOD_MILLISECONDS); + } + } + + private ArrayList getSentSeries(ArrayList buckets) + { + ArrayList series = new ArrayList(); + for (int i = 0; i < buckets.size(); i++) + { + series.add(buckets.get(i).m_bytesSent); + } + return series; + } + + private ArrayList getReceivedSeries(ArrayList buckets) + { + ArrayList series = new ArrayList(); + for (int i = 0; i < buckets.size(); i++) + { + series.add(buckets.get(i).m_bytesReceived); + } + return series; + } + + private void addSentToBuckets(long bytes) + { + this.m_slowBuckets.get(this.m_slowBuckets.size()-1).m_bytesSent += bytes; + this.m_fastBuckets.get(this.m_fastBuckets.size()-1).m_bytesSent += bytes; + } + + private void addReceivedToBuckets(long bytes) + { + this.m_slowBuckets.get(this.m_slowBuckets.size()-1).m_bytesReceived += bytes; + this.m_fastBuckets.get(this.m_fastBuckets.size()-1).m_bytesReceived += bytes; + } + + public synchronized boolean isConnected() + { + return this.m_isConnected; + } + + public synchronized long getElapsedTime() + { + long now = SystemClock.elapsedRealtime(); + + return now - this.m_connectedTime; + } + + public synchronized long getTotalBytesSent() + { + return this.m_totalBytesSent; + } + + public synchronized long getTotalBytesReceived() + { + return this.m_totalBytesReceived; + } + + public synchronized ArrayList getSlowSentSeries() + { + manageBuckets(); + return getSentSeries(this.m_slowBuckets); + } + + public synchronized ArrayList getSlowReceivedSeries() + { + manageBuckets(); + return getReceivedSeries(this.m_slowBuckets); + } + + public synchronized ArrayList getFastSentSeries() + { + manageBuckets(); + return getSentSeries(this.m_fastBuckets); + } + + public synchronized ArrayList getFastReceivedSeries() + { + manageBuckets(); + return getReceivedSeries(this.m_fastBuckets); + } + public String byteCountToDisplaySize(long bytes, boolean si) + { + // http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java/3758880#3758880 + int unit = si ? 1000 : 1024; + if (bytes < unit) return bytes + " B"; + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i"); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } + public synchronized long getBytesSent() { + long xSent = TrafficStats.getTotalTxBytes(); + mSent = xSent - mLastSent; + mLastSent = xSent; + return this.mSent; + } + + public synchronized long getBytesReceived() + { + long xReceived = TrafficStats.getTotalRxBytes(); + mReceived = xReceived - mLastReceived; + mLastReceived = xReceived; + return this.mReceived; + + } + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/XHttpSshCore.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/XHttpSshCore.java new file mode 100644 index 0000000..c6937a8 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/XHttpSshCore.java @@ -0,0 +1,25 @@ +package com.dragonssh.xhttpdemo.core; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; + +/** Creates the quiet foreground-service channel used by the sample. */ +public final class XHttpSshCore { + private XHttpSshCore() {} + + public static void init(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; + NotificationManager manager = context.getSystemService(NotificationManager.class); + if (manager == null) return; + NotificationChannel channel = new NotificationChannel( + XHttpSshService.NOTIFICATION_CHANNEL_ID, + context.getString(R.string.channel_name_background), + NotificationManager.IMPORTANCE_LOW); + channel.setDescription(context.getString(R.string.channel_description_background)); + channel.setSound(null, null); + channel.enableVibration(false); + manager.createNotificationChannel(channel); + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/XHttpSshService.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/XHttpSshService.java new file mode 100644 index 0000000..b4720ac --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/XHttpSshService.java @@ -0,0 +1,168 @@ +package com.dragonssh.xhttpdemo.core; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ServiceInfo; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; + +import androidx.core.app.NotificationCompat; +import androidx.core.app.ServiceCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dragonssh.xhttpdemo.core.logger.ConnectionStatus; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.dragonssh.xhttpdemo.core.tunnel.TunnelManagerThread; + +/** Foreground service that owns the SSH/XHTTP transport. */ +public final class XHttpSshService extends Service implements SkStatus.StateListener { + public static final String NOTIFICATION_CHANNEL_ID = "xhttp_ssh_status"; + public static final int NOTIFICATION_ID = 7001; + public static final String EXTRA_SSH_PASSWORD = "ssh_password"; + public static final String TUNNEL_SSH_RESTART_SERVICE = + XHttpSshService.class.getName() + ".RESTART"; + public static final String TUNNEL_SSH_STOP_SERVICE = + XHttpSshService.class.getName() + ".STOP"; + + private final Handler mainHandler = new Handler(Looper.getMainLooper()); + private TunnelManagerThread tunnelManager; + private Thread tunnelThread; + private boolean receiverRegistered; + + @Override + public void onCreate() { + super.onCreate(); + XHttpSshCore.init(this); + registerControlReceiver(); + SkStatus.addStateListener(this); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + ServiceCompat.startForeground(this, NOTIFICATION_ID, + buildNotification(getString(R.string.state_starting)), + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC); + if (tunnelThread == null || !tunnelThread.isAlive()) { + String password = intent != null ? intent.getStringExtra(EXTRA_SSH_PASSWORD) : null; + if (password == null || password.isEmpty()) { + SkStatus.logError("SSH password was not supplied"); + stopForeground(STOP_FOREGROUND_REMOVE); + stopSelf(startId); + return START_NOT_STICKY; + } + startTunnel(password); + } + return START_NOT_STICKY; + } + + private synchronized void startTunnel(String password) { + tunnelManager = new TunnelManagerThread(mainHandler, this, password); + tunnelManager.setOnStopClientListener(this::finishService); + tunnelThread = new Thread(tunnelManager, "xhttp-ssh-manager"); + tunnelThread.start(); + } + + private synchronized void stopTunnel() { + TunnelManagerThread manager = tunnelManager; + tunnelManager = null; + if (manager != null) manager.stopAll(); + Thread thread = tunnelThread; + tunnelThread = null; + if (thread != null) thread.interrupt(); + } + + private void finishService() { + mainHandler.post(() -> { + stopTunnel(); + stopForeground(STOP_FOREGROUND_REMOVE); + stopSelf(); + }); + } + + @Override + public void onDestroy() { + stopTunnel(); + if (receiverRegistered) { + LocalBroadcastManager.getInstance(this).unregisterReceiver(controlReceiver); + receiverRegistered = false; + } + SkStatus.removeStateListener(this); + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private Notification buildNotification(String text) { + Intent open = getPackageManager().getLaunchIntentForPackage(getPackageName()); + PendingIntent contentIntent = null; + if (open != null) { + open.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) flags |= PendingIntent.FLAG_IMMUTABLE; + contentIntent = PendingIntent.getActivity(this, 1, open, flags); + } + + Intent stop = new Intent(this, MainReceiver.class).setAction(MainReceiver.ACTION_SERVICE_STOP); + int actionFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) actionFlags |= PendingIntent.FLAG_IMMUTABLE; + PendingIntent stopIntent = PendingIntent.getBroadcast(this, 2, stop, actionFlags); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_cloud_black_24dp) + .setContentTitle(getString(R.string.app_name)) + .setContentText(text) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setSilent(true) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .addAction(R.drawable.ic_power_settings_new_black_24dp, + getString(R.string.stop), stopIntent); + if (contentIntent != null) builder.setContentIntent(contentIntent); + return builder.build(); + } + + @Override + public void updateState(String state, String logMessage, int localizedResId, + ConnectionStatus level, Intent intent) { + String text; + try { + text = getString(localizedResId); + } catch (Exception ignored) { + text = state; + } + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + if (nm != null) nm.notify(NOTIFICATION_ID, buildNotification(text)); + } + + private void registerControlReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(TUNNEL_SSH_STOP_SERVICE); + filter.addAction(TUNNEL_SSH_RESTART_SERVICE); + LocalBroadcastManager.getInstance(this).registerReceiver(controlReceiver, filter); + receiverRegistered = true; + } + + private final BroadcastReceiver controlReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null || intent.getAction() == null) return; + if (TUNNEL_SSH_STOP_SERVICE.equals(intent.getAction())) { + finishService(); + } else if (TUNNEL_SSH_RESTART_SERVICE.equals(intent.getAction())) { + TunnelManagerThread manager = tunnelManager; + if (manager != null) new Thread(manager::reconnectSSH, "xhttp-manual-reconnect").start(); + } + } + }; +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/config/Settings.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/config/Settings.java new file mode 100644 index 0000000..9ad3918 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/config/Settings.java @@ -0,0 +1,111 @@ +package com.dragonssh.xhttpdemo.core.config; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Minimal local configuration store for the public sample. + * No online panel, config encryption, import/export, HWID, OTP, or paid-app settings. + */ +public final class Settings implements SettingsConstants { + private final SharedPreferences privatePrefs; + private final SharedPreferences vpnPrefs; + + public Settings(Context context) { + Context app = context.getApplicationContext(); + privatePrefs = app.getSharedPreferences("xhttp_demo_private", Context.MODE_PRIVATE); + vpnPrefs = app.getSharedPreferences("xhttp_demo_vpn", Context.MODE_PRIVATE); + } + + public String getPrivString(String key) { + String fallback = ""; + if (PORTA_LOCAL_KEY.equals(key)) fallback = "1080"; + if (XHTTP_PATH_KEY.equals(key)) fallback = "/ssh"; + if (XHTTP_TLS_KEY.equals(key)) fallback = "1"; + return privatePrefs.getString(key, fallback); + } + + public SharedPreferences getPrefsPrivate() { + return privatePrefs; + } + + public SharedPreferences getVpnPrefs() { + return vpnPrefs; + } + + public int getMaximoThreadsSocks() { + return Math.max(1, vpnPrefs.getInt(MAXIMO_THREADS_KEY, 8)); + } + + public int getSSHPinger() { + return Math.max(0, vpnPrefs.getInt(PINGER_KEY, 3)); + } + + public boolean ssh_compression() { + return vpnPrefs.getBoolean(SSH_COMPRESSION, false); + } + + + public boolean getIsDisabledDelaySSH() { + return true; + } + + public boolean getDisableIpv6Tunnel() { + return vpnPrefs.getBoolean(DISABLE_IPV6_TUNNEL_KEY, true); + } + + public boolean getVpnDnsForward() { + return vpnPrefs.getBoolean(DNSFORWARD_KEY, true); + } + + public String getVpnDnsResolver() { + return vpnPrefs.getString(DNSRESOLVER_KEY, "1.1.1.1"); + } + + public String getVpnDnsResolverSecondary() { + return vpnPrefs.getString(DNSRESOLVER_SECONDARY_KEY, "1.0.0.1"); + } + + public boolean getVpnUdpForward() { + return vpnPrefs.getBoolean(UDPFORWARD_KEY, true); + } + + public String getVpnUdpResolver() { + return vpnPrefs.getString(UDPRESOLVER_KEY, "127.0.0.1:7300"); + } + + public String[] getVpnBypassList() { + return new String[0]; + } + + public boolean getIsFilterBypassMode() { + return false; + } + + public String[] getFilterApps() { + return new String[0]; + } + + public boolean getIsTetheringSubnet() { + return false; + } + + public static void setDefaultConfig(Context context) { + Settings settings = new Settings(context); + settings.privatePrefs.edit() + .putString(PORTA_LOCAL_KEY, "1080") + .putString(XHTTP_PATH_KEY, "/ssh") + .putString(XHTTP_TLS_KEY, "1") + .apply(); + settings.vpnPrefs.edit() + .putBoolean(UDPFORWARD_KEY, true) + .putString(UDPRESOLVER_KEY, "127.0.0.1:7300") + .putBoolean(DNSFORWARD_KEY, true) + .putString(DNSRESOLVER_KEY, "1.1.1.1") + .putString(DNSRESOLVER_SECONDARY_KEY, "1.0.0.1") + .putBoolean(DISABLE_IPV6_TUNNEL_KEY, true) + .putInt(MAXIMO_THREADS_KEY, 8) + .putInt(PINGER_KEY, 3) + .apply(); + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/config/SettingsConstants.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/config/SettingsConstants.java new file mode 100644 index 0000000..3aeb677 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/config/SettingsConstants.java @@ -0,0 +1,33 @@ +package com.dragonssh.xhttpdemo.core.config; + +/** Keys used by the public XHTTP + SSH example. */ +public interface SettingsConstants { + // Legacy preference values are kept so upgrades preserve saved configurations. + String XHTTP_ENDPOINT_KEY = "sshServer"; + String XHTTP_PORT_KEY = "sshPort"; + + /** @deprecated Use {@link #XHTTP_ENDPOINT_KEY}. */ + @Deprecated String SERVIDOR_KEY = XHTTP_ENDPOINT_KEY; + /** @deprecated Use {@link #XHTTP_PORT_KEY}. */ + @Deprecated String SERVIDOR_PORTA_KEY = XHTTP_PORT_KEY; + String USUARIO_KEY = "sshUser"; + String SSH_PASSWORD_KEY = "sshPass"; + String PORTA_LOCAL_KEY = "localSocksPort"; + String SSH_HOST_KEY_PREFIX = "sshHostKey."; + + String CUSTOM_SNI = "xhttpSni"; + String XHTTP_PATH_KEY = "xhttpPath"; + String XHTTP_HOST_KEY = "xhttpHost"; + String XHTTP_TLS_KEY = "xhttpTls"; + String TLS12 = "tls12"; + + String DNSFORWARD_KEY = "dnsForward"; + String DNSRESOLVER_KEY = "dnsResolver"; + String DNSRESOLVER_SECONDARY_KEY = "dnsResolverSecondary"; + String UDPFORWARD_KEY = "udpForward"; + String UDPRESOLVER_KEY = "udpResolver"; + String DISABLE_IPV6_TUNNEL_KEY = "disableIpv6Tunnel"; + String MAXIMO_THREADS_KEY = "numberMaxThreadSocks"; + String PINGER_KEY = "pingerSSH"; + String SSH_COMPRESSION = "sshCompression"; +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/ConnectionStatus.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/ConnectionStatus.java new file mode 100644 index 0000000..48b3137 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/ConnectionStatus.java @@ -0,0 +1,39 @@ +package com.dragonssh.xhttpdemo.core.logger; + +import android.os.Parcel; +import android.os.Parcelable; + +public enum ConnectionStatus implements Parcelable { + LEVEL_CONNECTED, + LEVEL_CONNECTING_SERVER_REPLIED, + LEVEL_CONNECTING_NO_SERVER_REPLY_YET, + LEVEL_NONETWORK, + LEVEL_NOTCONNECTED, + LEVEL_START, + LEVEL_AUTH_FAILED, + LEVEL_WAITING_FOR_USER_INPUT, + UNKNOWN_LEVEL; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(ordinal()); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public ConnectionStatus createFromParcel(Parcel in) { + return ConnectionStatus.values()[in.readInt()]; + } + + @Override + public ConnectionStatus[] newArray(int size) { + return new ConnectionStatus[size]; + } + }; +} + diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/LogItem.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/LogItem.java new file mode 100644 index 0000000..379ece2 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/LogItem.java @@ -0,0 +1,107 @@ +package com.dragonssh.xhttpdemo.core.logger; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Locale; + +/** A small, application-neutral log record for the public example. */ +public final class LogItem implements Parcelable { + private final Object[] args; + private final String message; + private final int resourceId; + private final SkStatus.LogLevel level; + private final long logTime; + + public LogItem(int resourceId, Object... args) { + this(SkStatus.LogLevel.INFO, resourceId, args); + } + + public LogItem(SkStatus.LogLevel level, int verbosityLevel, String message) { + this(level, message); + } + + public LogItem(SkStatus.LogLevel level, int resourceId, Object... args) { + this.level = level; + this.resourceId = resourceId; + this.args = args; + this.message = null; + this.logTime = System.currentTimeMillis(); + } + + public LogItem(SkStatus.LogLevel level, String message) { + this.level = level; + this.message = message; + this.resourceId = 0; + this.args = null; + this.logTime = System.currentTimeMillis(); + } + + public LogItem(SkStatus.LogLevel level, int resourceId) { + this(level, resourceId, (Object[]) null); + } + + private LogItem(Parcel in) { + args = in.readArray(Object.class.getClassLoader()); + message = in.readString(); + resourceId = in.readInt(); + int levelValue = in.readInt(); + SkStatus.LogLevel parsed = SkStatus.LogLevel.getEnumByValue(levelValue); + level = parsed != null ? parsed : SkStatus.LogLevel.INFO; + logTime = in.readLong(); + } + + public SkStatus.LogLevel getLogLevel() { + return level; + } + + public long getLogtime() { + return logTime; + } + + public String getMessage() { + return message; + } + + public String getString(Context context) { + if (message != null) { + return message; + } + if (context == null || resourceId == 0) { + return String.format(Locale.US, "Log resource %d", resourceId); + } + return args == null ? context.getString(resourceId) : context.getString(resourceId, args); + } + + @Override + public String toString() { + return getString(null); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeArray(args); + dest.writeString(message); + dest.writeInt(resourceId); + dest.writeInt(level.getInt()); + dest.writeLong(logTime); + } + + public static final Creator CREATOR = new Creator() { + @Override + public LogItem createFromParcel(Parcel in) { + return new LogItem(in); + } + + @Override + public LogItem[] newArray(int size) { + return new LogItem[size]; + } + }; +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/SkStatus.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/SkStatus.java new file mode 100644 index 0000000..6a541aa --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/logger/SkStatus.java @@ -0,0 +1,394 @@ +package com.dragonssh.xhttpdemo.core.logger; + +import java.io.StringWriter; +import java.io.PrintWriter; +import java.util.LinkedList; +import java.util.Vector; + +import com.dragonssh.xhttpdemo.core.R; +import android.content.Intent; +import android.content.Context; + +import java.util.Locale; + +public class SkStatus +{ + private static final LinkedList logbuffer; + + private static Vector logListener; + private static Vector stateListener; + + private static ConnectionStatus mLastLevel = ConnectionStatus.LEVEL_NOTCONNECTED; + + private static String mLaststatemsg = ""; + private static String mLaststate = "NOPROCESS"; + private static int mLastStateresid = R.string.state_noprocess; + private static Intent mLastIntent = null; + + + static final int MAXLOGENTRIES = 10000; + + public static boolean isTunnelActive() { + return mLastLevel != ConnectionStatus.LEVEL_AUTH_FAILED && !(mLastLevel == ConnectionStatus.LEVEL_NOTCONNECTED); + } + + public static String getLastState() { + return mLaststate; + } + + public static String getLastCleanLogMessage(Context c) { + String message = mLaststatemsg; + switch (mLastLevel) { + case LEVEL_CONNECTED: + String[] parts = mLaststatemsg.split(","); + /* + (a) the integer unix date/time, + (b) the state name, + 0 (c) optional descriptive string (used mostly on RECONNECTING + and EXITING to show the reason for the disconnect), + + 1 (d) optional TUN/TAP local IPv4 address + 2 (e) optional address of remote server, + 3 (f) optional port of remote server, + 4 (g) optional local address, + 5 (h) optional local port, and + 6 (i) optional TUN/TAP local IPv6 address. + */ + // Return only the assigned IP addresses in the UI + if (parts.length >= 7) + message = String.format(Locale.US, "%s %s", parts[1], parts[6]); + break; + } + + while (message.endsWith(",")) + message = message.substring(0, message.length() - 1); + + String status = mLaststate; + if (status.equals("NOPROCESS")) + return message; + + if (mLastStateresid == R.string.state_waitconnectretry) { + return c.getString(R.string.state_waitconnectretry, mLaststatemsg); + } + + String prefix = c.getString(mLastStateresid); + if (mLastStateresid == R.string.unknown_state) + message = status + message; + if (message.length() > 0) + prefix += ": "; + + return prefix + message; + + } + + + public static enum LogLevel { + + INFO(2), + ERROR(-2), + WARNING(1), + VERBOSE(3), + DEBUG(4); + + protected int mValue; + + LogLevel(int value) { + mValue = value; + } + + public int getInt() { + return mValue; + } + + public static LogLevel getEnumByValue(int value) { + switch (value) { + case 2: + return INFO; + case -2: + return ERROR; + case 1: + return WARNING; + case 3: + return VERBOSE; + case 4: + return DEBUG; + + default: + return null; + } + } + } + + + static { + logbuffer = new LinkedList<>(); + logListener = new Vector<>(); + stateListener = new Vector<>(); + + logInformation(); + } + + + public synchronized static void clearLog() { + logbuffer.clear(); + + for (LogListener li : logListener) { + li.onClear(); + } + + // Add the header after listeners clear their UI so it remains visible. + logInformation(); + } + + public synchronized static LogItem[] getlogbuffer() { + + // The stoned way of java to return an array from a vector + // brought to you by eclipse auto complete + return logbuffer.toArray(new LogItem[logbuffer.size()]); + } + + private static void logInformation() { + logInfo("DragonSSH XHTTP public example"); + } + + + /** + * Listeners + */ + + public interface LogListener { + void newLog(LogItem logItem); + void onClear(); + } + + public interface StateListener { + void updateState(String state, String logMessage, int localizedResId, ConnectionStatus level, Intent intent); + } + + public synchronized static void addLogListener(LogListener ll) { + if (!logListener.contains(ll)) { + logListener.add(ll); + } + } + + public synchronized static void removeLogListener(LogListener ll) { + if (logListener.contains(ll)) { + logListener.remove(ll); + } + } + + public synchronized static void addStateListener(StateListener sl) { + if (!stateListener.contains(sl)) { + stateListener.add(sl); + if (mLaststate != null) + sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel, mLastIntent); + } + } + + public synchronized static void removeStateListener(StateListener sl) { + if (stateListener.contains(sl)) { + stateListener.remove(sl); + } + } + + + /** + * State + */ + + public static final String + SSH_CONECTANDO = "CONECTANDO", + SSH_AGUARDANDO_REDE = "AGUARDANDO", + SSH_AUTENTICANDO = "AUTENTICANDO", + SSH_CONECTADO = "CONECTADO", + SSH_DESCONECTADO = "DESCONECTADO", + SSH_RECONECTANDO = "RECONECTANDO", + SSH_INICIANDO = "INICIANDO", + SSH_PARANDO = "PARANDO"; + + public static int getLocalizedState(String state) { + switch (state) { + case SSH_CONECTANDO: + return R.string.state_connecting; + case SSH_AGUARDANDO_REDE: + return R.string.state_nonetwork; + case SSH_AUTENTICANDO: + return R.string.state_auth; + case "GET_CONFIG": + return R.string.state_get_config; + case "ASSIGN_IP": + return R.string.state_assign_ip; + case "ADD_ROUTES": + return R.string.state_add_routes; + case SSH_CONECTADO: + return R.string.state_connected; + case SSH_DESCONECTADO: + return R.string.state_disconnected; + case SSH_RECONECTANDO: + return R.string.state_reconnecting; + case SSH_INICIANDO: + return R.string.state_starting; + case SSH_PARANDO: + return R.string.state_stopping; + case "RESOLVE": + return R.string.state_resolve; + case "TCP_CONNECT": + return R.string.state_tcp_connect; + case "AUTH_PENDING": + return R.string.state_auth_pending; + default: + return R.string.unknown_state; + } + + } + + private static ConnectionStatus getLevel(String state) { + String[] noreplyet = {SSH_INICIANDO, SSH_CONECTANDO, SSH_AGUARDANDO_REDE, SSH_RECONECTANDO, "RESOLVE", "TCP_CONNECT"}; + String[] reply = {SSH_AUTENTICANDO, "GET_CONFIG", "ASSIGN_IP", "ADD_ROUTES", "AUTH_PENDING"}; + String[] connected = {SSH_CONECTADO}; + String[] notconnected = {SSH_DESCONECTADO}; + + for (String x : noreplyet) + if (state.equals(x)) + return ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET; + + for (String x : reply) + if (state.equals(x)) + return ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED; + + for (String x : connected) + if (state.equals(x)) + return ConnectionStatus.LEVEL_CONNECTED; + + for (String x : notconnected) + if (state.equals(x)) + return ConnectionStatus.LEVEL_NOTCONNECTED; + + return ConnectionStatus.UNKNOWN_LEVEL; + } + + public static void updateStateString(String state, String msg) { + int rid = getLocalizedState(state); + ConnectionStatus level = getLevel(state); + updateStateString(state, msg, rid, level); + } + + public synchronized static void updateStateString(String state, String msg, int resid, ConnectionStatus level) + { + updateStateString(state, msg, resid, level, null); + } + + public synchronized static void updateStateString(String state, String msg, int resid, ConnectionStatus level, Intent intent) { + // Workound for OpenVPN doing AUTH and wait and being connected + // Simply ignore these state + if (mLastLevel == ConnectionStatus.LEVEL_CONNECTED && + (state.equals(SSH_AUTENTICANDO))) { + newLogItem(new LogItem((LogLevel.DEBUG), String.format("Ignoring XHTTP SSH status in CONNECTED state (%s->%s): %s", state, level.toString(), msg))); + return; + } + + mLaststate = state; + mLaststatemsg = msg; + mLastStateresid = resid; + mLastLevel = level; + mLastIntent = intent; + + + for (StateListener sl : stateListener) { + sl.updateState(state, msg, resid, level, intent); + } + + // State transition: (%s->%s): %s",state,level.toString(),msg))); + } + + + /** + * NewLog + */ + + static void newLogItem(LogItem logItem) { + newLogItem(logItem, false); + } + + synchronized static void newLogItem(LogItem logItem, boolean cachedLine) { + if (cachedLine) { + logbuffer.addFirst(logItem); + } else { + logbuffer.addLast(logItem); + } + + // Keep a bounded history without deleting a large block of logs at once. + // The previous 1.5x threshold made thousands of older entries disappear + // suddenly, which looked like the log had been cropped. + while (logbuffer.size() > MAXLOGENTRIES) + logbuffer.removeFirst(); + + for (LogListener ll : logListener) { + ll.newLog(logItem); + } + } + + + /** + * Logger static methods + */ + + public static void logException(String context, Exception e) { + logException(LogLevel.ERROR, context, e); + } + + public static void logException(LogLevel ll, String context, Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + + LogItem li; + + if (context != null) + li = new LogItem(ll, String.format("%s: %s, %s", context, e.getMessage(), sw.toString())); + else + li = new LogItem(ll, String.format("Error: %s, %s", e.getMessage(), sw.toString())); + + newLogItem(li); + } + + public static void logException(Exception e) { + logException(LogLevel.ERROR, null, e); + } + + public static void logInfo(String message) { + newLogItem(new LogItem(LogLevel.INFO, message)); + } + + public static void logDebug(String message) { + newLogItem(new LogItem(LogLevel.DEBUG, message)); + } + + public static void logInfo(int resourceId, Object... args) { + newLogItem(new LogItem(LogLevel.INFO, resourceId, args)); + } + + public static void logDebug(int resourceId, Object... args) { + newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args)); + } + + public static void logError(String msg) { + newLogItem(new LogItem(LogLevel.ERROR, msg)); + } + + public static void logWarning(int resourceId, Object... args) { + newLogItem(new LogItem(LogLevel.WARNING, resourceId, args)); + } + + public static void logWarning(String msg) { + newLogItem(new LogItem(LogLevel.WARNING, msg)); + } + + public static void logError(int resourceId) { + newLogItem(new LogItem(LogLevel.ERROR, resourceId)); + } + + public static void logError(int resourceId, Object... args) { + newLogItem(new LogItem(LogLevel.ERROR, resourceId, args)); + } + +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/PersistentSocksRelay.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/PersistentSocksRelay.java new file mode 100644 index 0000000..8ad927f --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/PersistentSocksRelay.java @@ -0,0 +1,296 @@ +package com.dragonssh.xhttpdemo.core.tunnel; + +import com.dragonssh.xhttpdemo.core.logger.SkStatus; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +/** + * Stable loopback TCP relay used between tun2socks and Trilead's dynamic SOCKS + * forwarder. + * + *

The Android VPN always points to this relay. During an SSH/XHTTP reconnect, + * only the relay's backend port changes. Keeping the public SOCKS listener alive + * prevents tun2socks from treating a transient SSH outage as a fatal local proxy + * failure and tearing down the VPN process.

+ */ +final class PersistentSocksRelay { + + private static final String LOOPBACK_HOST = "127.0.0.1"; + private static final int ACCEPT_BACKLOG = 128; + private static final int MAX_CLIENTS = 256; + private static final int BACKEND_CONNECT_TIMEOUT_MS = 1500; + private static final int BACKEND_RETRY_DELAY_MS = 150; + private static final long BACKEND_WAIT_TIMEOUT_MS = 90_000L; + private static final int COPY_BUFFER_SIZE = 32 * 1024; + + private final int listenPort; + private final Object backendLock = new Object(); + private final Semaphore clientSlots = new Semaphore(MAX_CLIENTS); + private final Set openSockets = Collections.synchronizedSet(new HashSet()); + + private volatile boolean running; + private volatile int backendPort = -1; + private ServerSocket serverSocket; + private Thread acceptThread; + + PersistentSocksRelay(int listenPort) { + this.listenPort = listenPort; + } + + synchronized void start() throws IOException { + if (running) { + return; + } + + ServerSocket server = new ServerSocket(); + server.setReuseAddress(true); + server.bind(new InetSocketAddress(LOOPBACK_HOST, listenPort), ACCEPT_BACKLOG); + + serverSocket = server; + running = true; + acceptThread = new Thread(this::acceptLoop, "vpn-socks-relay-accept"); + acceptThread.setDaemon(true); + acceptThread.start(); + } + + boolean isRunning() { + return running; + } + + int getListenPort() { + return listenPort; + } + + void setBackendPort(int port) { + synchronized (backendLock) { + backendPort = port > 0 ? port : -1; + backendLock.notifyAll(); + } + } + + void clearBackendPort() { + setBackendPort(-1); + } + + synchronized void stop() { + if (!running) { + return; + } + + running = false; + clearBackendPort(); + + ServerSocket server = serverSocket; + serverSocket = null; + closeQuietly(server); + + synchronized (openSockets) { + for (Socket socket : openSockets) { + closeQuietly(socket); + } + openSockets.clear(); + } + + Thread thread = acceptThread; + acceptThread = null; + if (thread != null) { + thread.interrupt(); + } + } + + private void acceptLoop() { + while (running) { + Socket client = null; + try { + ServerSocket server = serverSocket; + if (server == null) { + break; + } + client = server.accept(); + client.setTcpNoDelay(true); + client.setKeepAlive(true); + + if (!clientSlots.tryAcquire(1, TimeUnit.SECONDS)) { + closeQuietly(client); + continue; + } + + final Socket accepted = client; + openSockets.add(accepted); + Thread worker = new Thread(() -> handleClient(accepted), "vpn-socks-relay-client"); + worker.setDaemon(true); + worker.start(); + } catch (SocketException e) { + if (running) { + SkStatus.logDebug("SOCKS relay accept failed: " + e.getMessage()); + } + break; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + closeQuietly(client); + break; + } catch (IOException e) { + closeQuietly(client); + if (running) { + SkStatus.logDebug("SOCKS relay accept failed: " + e.getMessage()); + } + } + } + } + + private void handleClient(Socket client) { + Socket backend = null; + try { + backend = connectBackend(); + if (backend == null) { + return; + } + + openSockets.add(backend); + relayBidirectional(client, backend); + } catch (IOException ignored) { + // A backend channel closing during reconnect is expected. + } finally { + closeQuietly(client); + closeQuietly(backend); + openSockets.remove(client); + if (backend != null) { + openSockets.remove(backend); + } + clientSlots.release(); + } + } + + private Socket connectBackend() throws IOException { + final long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(BACKEND_WAIT_TIMEOUT_MS); + IOException lastError = null; + + while (running && System.nanoTime() < deadline) { + int port = awaitBackendPort(deadline); + if (port <= 0) { + continue; + } + + Socket socket = new Socket(); + try { + socket.setTcpNoDelay(true); + socket.setKeepAlive(true); + socket.connect(new InetSocketAddress(LOOPBACK_HOST, port), + BACKEND_CONNECT_TIMEOUT_MS); + return socket; + } catch (IOException e) { + lastError = e; + closeQuietly(socket); + + try { + Thread.sleep(BACKEND_RETRY_DELAY_MS); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + return null; + } + } + } + + if (lastError != null) { + throw lastError; + } + return null; + } + + private int awaitBackendPort(long deadlineNanos) { + synchronized (backendLock) { + while (running && backendPort <= 0) { + long remainingNanos = deadlineNanos - System.nanoTime(); + if (remainingNanos <= 0) { + return -1; + } + + long waitMillis = Math.max(1L, + Math.min(1000L, TimeUnit.NANOSECONDS.toMillis(remainingNanos))); + try { + backendLock.wait(waitMillis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return -1; + } + } + return backendPort; + } + } + + private static void relayBidirectional(Socket left, Socket right) throws IOException { + final IOException[] upstreamError = new IOException[1]; + Thread upstream = new Thread(() -> { + try { + copy(left, right); + } catch (IOException e) { + upstreamError[0] = e; + } finally { + closeQuietly(left); + closeQuietly(right); + } + }, "vpn-socks-relay-up"); + upstream.setDaemon(true); + upstream.start(); + + try { + copy(right, left); + } finally { + closeQuietly(left); + closeQuietly(right); + try { + upstream.join(1000L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + if (upstreamError[0] != null) { + throw upstreamError[0]; + } + } + + private static void copy(Socket source, Socket destination) throws IOException { + InputStream in = source.getInputStream(); + OutputStream out = destination.getOutputStream(); + byte[] buffer = new byte[COPY_BUFFER_SIZE]; + int read; + while ((read = in.read(buffer)) >= 0) { + if (read == 0) { + continue; + } + out.write(buffer, 0, read); + } + } + + private static void closeQuietly(Socket socket) { + if (socket == null) { + return; + } + try { + socket.close(); + } catch (IOException ignored) { + } + } + + private static void closeQuietly(ServerSocket socket) { + if (socket == null) { + return; + } + try { + socket.close(); + } catch (IOException ignored) { + } + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TLSSocketFactory.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TLSSocketFactory.java new file mode 100644 index 0000000..f126b11 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TLSSocketFactory.java @@ -0,0 +1,138 @@ +package com.dragonssh.xhttpdemo.core.tunnel; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import android.annotation.SuppressLint; +import android.content.Context; + +import com.dragonssh.xhttpdemo.core.config.Settings; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.SocketProtector; + +import org.conscrypt.Conscrypt; + +import java.security.SecureRandom; +import java.util.Objects; + + +public class TLSSocketFactory extends SSLSocketFactory { + private SSLSocketFactory internalSSLSocketFactory; + + + + static { + try { + Security.insertProviderAt(Conscrypt.newProvider(), 1); + + } catch (NoClassDefFoundError e) { + e.printStackTrace(); + }} + + public SSLContext sslctx; + private Settings mConfig; + public TLSSocketFactory(Context context) throws KeyManagementException, NoSuchAlgorithmException { + mConfig = new Settings(context); + // For easier debugging purpose, trust all certificates + + //dsp = ApplicationBase.getDefSharedPreferences(); + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @SuppressLint({"TrustAllX509TrustManager"}) + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + @SuppressLint({"TrustAllX509TrustManager"}) + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + sslctx = SSLContext.getInstance("TLS"); + sslctx.init(null, trustAllCerts, new SecureRandom()); + internalSSLSocketFactory = sslctx.getSocketFactory(); + + } + + @Override + public String[] getDefaultCipherSuites() { + return internalSSLSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return internalSSLSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + Socket socket = enableTLSOnSocket(internalSSLSocketFactory.createSocket()); + SocketProtector.protect(socket); + return socket; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + Socket socket = createSocket(); + socket.connect(new InetSocketAddress(host, port)); + return socket; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + Socket socket = createSocket(); + socket.bind(new InetSocketAddress(localHost, localPort)); + socket.connect(new InetSocketAddress(host, port)); + return socket; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + Socket socket = createSocket(); + socket.connect(new InetSocketAddress(host, port)); + return socket; + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + Socket socket = createSocket(); + socket.bind(new InetSocketAddress(localAddress, localPort)); + socket.connect(new InetSocketAddress(address, port)); + return socket; + } + + private Socket enableTLSOnSocket(Socket socket) { + + if (socket instanceof SSLSocket) { + if (Objects.equals(mConfig.getPrivString(Settings.TLS12), "1")){ + ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"}); + + }else{ + ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}); + } + + // ((SSLSocket) socket).setEnabledProtocols(((SSLSocket) socket).getSupportedProtocols()); + + } + return socket; + } + +} \ No newline at end of file diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelManagerHelper.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelManagerHelper.java new file mode 100644 index 0000000..3d583cf --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelManagerHelper.java @@ -0,0 +1,29 @@ +package com.dragonssh.xhttpdemo.core.tunnel; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dragonssh.xhttpdemo.core.XHttpSshService; + +/** Small public API used by the sample activity. */ +public final class TunnelManagerHelper { + private TunnelManagerHelper() {} + + public static void startXHttpSsh(Context context, String password) { + Intent intent = new Intent(context, XHttpSshService.class) + .putExtra(XHttpSshService.EXTRA_SSH_PASSWORD, password); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent); + } else { + context.startService(intent); + } + } + + public static void stopXHttpSsh(Context context) { + LocalBroadcastManager.getInstance(context).sendBroadcast( + new Intent(XHttpSshService.TUNNEL_SSH_STOP_SERVICE)); + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelManagerThread.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelManagerThread.java new file mode 100644 index 0000000..70ce434 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelManagerThread.java @@ -0,0 +1,1049 @@ +package com.dragonssh.xhttpdemo.core.tunnel; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.ProxyInfo; +import android.os.Build; +import android.os.Handler; +//import android.util.Log; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dragonssh.xhttpdemo.core.R; +import com.dragonssh.xhttpdemo.core.config.Settings; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.TunnelState; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.TunnelVpnManager; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.TunnelVpnService; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.TunnelVpnSettings; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.VpnUtils; +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.ConnectionMonitor; +import com.trilead.ssh2.DebugLogger; +import com.trilead.ssh2.DynamicPortForwarder; +import com.trilead.ssh2.InteractiveCallback; +import com.trilead.ssh2.KnownHosts; +import com.trilead.ssh2.ProxyData; +import com.trilead.ssh2.ServerHostKeyVerifier; +import com.trilead.ssh2.transport.TransportManager; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +public class TunnelManagerThread + implements Runnable, ConnectionMonitor, InteractiveCallback, + ServerHostKeyVerifier, DebugLogger +{ + private static final String TAG = TunnelManagerThread.class.getSimpleName(); + + private OnStopClient mListener; + private Context mContext; + private Handler mHandler; + private Settings mConfig; + + // Immutable XHTTP settings for this service session. Automatic reconnect must + // recreate the same transport that originally established the VPN, regardless + // of any UI/preference change that happens while the tunnel is active. + private final String mSessionXhttpEndpoint; + private final String mSessionXhttpPort; + private final String mSessionUsername; + private final String mSessionPassword; + private final String mSessionXhttpSni; + private final String mSessionXhttpHost; + private final String mSessionXhttpPath; + private final boolean mSessionXhttpTls; + + private volatile boolean mRunning = false, mStopping = false, mStarting = false; + + private CountDownLatch mTunnelThreadStopSignal; + //private ConnectivityManager mCmgr; + + public interface OnStopClient { + void onStop(); + } + + public TunnelManagerThread(Handler handler, Context context, String password) { + mContext = context; + mHandler = handler; + + mConfig = new Settings(context); + mSessionXhttpEndpoint = mConfig.getPrivString(Settings.XHTTP_ENDPOINT_KEY); + mSessionXhttpPort = mConfig.getPrivString(Settings.XHTTP_PORT_KEY); + mSessionUsername = mConfig.getPrivString(Settings.USUARIO_KEY); + mSessionPassword = password; + mSessionXhttpSni = mConfig.getPrivString(Settings.CUSTOM_SNI); + mSessionXhttpHost = mConfig.getPrivString(Settings.XHTTP_HOST_KEY); + String configuredPath = mConfig.getPrivString(Settings.XHTTP_PATH_KEY); + mSessionXhttpPath = configuredPath == null || configuredPath.isEmpty() + ? "/xhttp" : configuredPath; + mSessionXhttpTls = !"0".equals( + mConfig.getPrivString(Settings.XHTTP_TLS_KEY)); + } + + public void setOnStopClientListener(OnStopClient listener) { + mListener = listener; + } + + @Override + public void run() + { + mStarting = true; + mTunnelThreadStopSignal = new CountDownLatch(1); + + SkStatus.logInfo(mContext.getString(R.string.starting_service_ssh)); + + int tries = 0; + while (!mStopping) { + try { + if (!TunnelUtils.isNetworkOnline(mContext)) { + SkStatus.updateStateString(SkStatus.SSH_AGUARDANDO_REDE, mContext.getString(R.string.state_nonetwork)); + + SkStatus.logInfo(R.string.state_nonetwork); + + try { + Thread.sleep(5000); + } catch(InterruptedException e2) { + stopAll(); + break; + } + } + else { + if (tries > 0) + SkStatus.logInfo(mContext.getString(R.string.state_reconnecting)); + + try { + Thread.sleep(500); + } catch(InterruptedException e2) { + stopAll(); + break; + } + + startSshClient(); + break; + } + } catch(Exception e) { + + SkStatus.logError(mContext.getString(R.string.state_disconnected)); + closeSSH(); + + try { + Thread.sleep(500); + } catch(InterruptedException e2) { + stopAll(); + break; + } + } + + tries++; + } + + mStarting = false; + + if (!mStopping) { + try { + mTunnelThreadStopSignal.await(); + } catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + if (mListener != null) { + mListener.onStop(); + } + } + + public void stopAll() { + if (mStopping) return; + + SkStatus.updateStateString(SkStatus.SSH_PARANDO, mContext.getString(R.string.stopping_service_ssh)); + SkStatus.logInfo(mContext.getString(R.string.stopping_service_ssh)); + + new Thread(new Runnable() { + @Override + public void run() { + mStopping = true; + + if (mTunnelThreadStopSignal != null) + mTunnelThreadStopSignal.countDown(); + + closeSSH(); + + try { + Thread.sleep(1000); + } catch(InterruptedException e){ + SkStatus.updateStateString(SkStatus.SSH_DESCONECTADO, mContext.getString(R.string.state_disconnected)); + mRunning = false; + mStarting = false; + mReconnecting = false; + } + + SkStatus.updateStateString(SkStatus.SSH_DESCONECTADO, mContext.getString(R.string.state_disconnected)); + //SkStatus.logInfo("Wakelock desativado!"); + mRunning = false; + mStarting = false; + mReconnecting = false; + } + }).start(); + } + + + /** + * Forwarder + */ + + protected void startForwarder(int portaLocal) throws Exception { + if (!mConnected) { + throw new Exception(); + } + + startForwarderSocks(portaLocal); + startPinger(15); + + startTunnelVpnService(); + + new Thread(new Runnable() { + @Override + public void run() { + while (true) { + if (!mConnected) break; + + try { + Thread.sleep(2000); + } catch(InterruptedException e) { + break; + } + + if (lastPingLatency > 0) { + // SkStatus.logInfo(String.format("Average ping: %d ms", lastPingLatency)); + break; + } + } + } + }).start(); + } + + protected void stopForwarder() { + stopTunnelVpnService(); + + stopForwarderSocks(); + } + + + /** + * Cliente SSH + */ + + private final static int AUTH_TRIES = 1; + private final static int RECONNECT_TRIES = 5; + + private volatile Connection mConnection; + + private volatile boolean mConnected = false; + + protected void startSshClient() throws Exception { + mStopping = false; + mRunning = true; + + String server = mSessionXhttpEndpoint; + int port = Integer.parseInt(mSessionXhttpPort); + String username = mSessionUsername; + String password = mSessionPassword; + int localPort = Integer.parseInt(mConfig.getPrivString(Settings.PORTA_LOCAL_KEY)); + + try { + connectSshTransport(server, port); + for (int i = 0; i < AUTH_TRIES; i++) { + if (mStopping) return; + try { + authenticate(username, password, null); + break; + } catch (IOException e) { + if (i + 1 >= AUTH_TRIES) throw e; + Thread.sleep(3000); + } + } + + SkStatus.updateStateString(SkStatus.SSH_CONECTADO, mContext.getString(R.string.conectssh)); + SkStatus.logInfo(mContext.getString(R.string.state_connected)); + startForwarder(localPort); + } catch (Exception e) { + mConnected = false; + throw e; + } + } + + + /** + * Closes the complete SSH stack, including the Android VPN interface. + * This path is used for an explicit disconnect or after reconnect retries + * have been exhausted. + */ + public synchronized void closeSSH() { + closeSSH(false); + } + + /** + * Closes only the replaceable SSH/XHTTP transport when {@code keepVpnAlive} + * is true. The VpnService, TUN file descriptor, routes and tun2socks process + * remain alive and continue pointing at the same local SOCKS address. Once + * SSH reconnects, the dynamic SOCKS listener is recreated on that address, + * avoiding an Android VPN interface teardown during a transient outage. + */ + private synchronized void closeSSH(boolean keepVpnAlive) { + stopVpnWatchdog(); + + if (keepVpnAlive && isServiceVpnRunning()) { + SkStatus.logDebug("Silent reconnect: keeping Android VPN and local SOCKS relay active"); + stopForwarderSocks(true); + } else { + stopForwarder(); + } + + stopPinger(); + mConnected = false; + + + Connection connection = mConnection; + mConnection = null; + if (connection != null) { + SkStatus.logDebug(mContext.getString(R.string.stpssh)); + connection.close(); + } + } + + protected void connectSshTransport(String server, int port) throws Exception { + if (!mStarting) { + throw new Exception(); + } + + + // Establish the SSH transport through XHTTP. + try { + + mConnection = new Connection(server, port); + + + // delay sleep + if (mConfig.getIsDisabledDelaySSH()) { + mConnection.setTCPNoDelay(true); + } + + if (mConfig.ssh_compression()){ + mConnection.setCompression(true); + SkStatus.logInfo(mContext.getString(R.string.compressg)); + } + + // XHTTP is the only transport exposed by this sample. + addProxy(mConnection); + + // Monitor only this concrete Connection instance. During a silent + // reconnect the old transport is closed intentionally, and Trilead may + // deliver its close callback after the replacement connection is already + // authenticated. Without this identity check, that stale callback can + // stop or reconnect the new session. + final Connection monitoredConnection = mConnection; + monitoredConnection.addConnectionMonitor(new ConnectionMonitor() { + @Override + public void connectionLost(Throwable reason) { + if (mConnection != monitoredConnection) { + SkStatus.logDebug("Ignoring stale SSH/XHTTP close callback"); + return; + } + TunnelManagerThread.this.connectionLost(reason); + } + + @Override + public void onReceiveInfo(int infoId, String infoMsg) { + if (mConnection != monitoredConnection) { + SkStatus.logDebug("Ignoring stale SSH/XHTTP info callback"); + return; + } + TunnelManagerThread.this.onReceiveInfo(infoId, infoMsg); + } + }); + + if (Build.VERSION.SDK_INT >= 23) { + ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ProxyInfo proxy = cm.getDefaultProxy(); + if (proxy != null) { + SkStatus.logInfo("Network proxy: " + String.format("%s:%d", proxy.getHost(), proxy.getPort())); + } + } + SkStatus.updateStateString(SkStatus.SSH_CONECTANDO, mContext.getString(R.string.state_connecting)); + SkStatus.logInfo(R.string.state_connecting); + + mConnection.connect(this, 10*1000, 20*1000); + + mConnected = true; + + } catch(Exception e) { + + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + + Throwable root = e.getCause() != null ? e.getCause() : e; + String cause = root.toString(); + String stage = useProxy && cause.contains("Key exchange was not finished") + ? "SSH key exchange failed" : "SSH/XHTTP connection failed"; + SkStatus.logError(stage + ": " + cause); + + throw new Exception(e); + } + } + + + /** + * Authentication + */ + + private static final String AUTH_PASSWORD = "password"; + + protected void authenticate(String username, String password, String ignoredKeyPath) throws IOException { + if (!mConnected) throw new IOException("SSH transport is not connected"); + SkStatus.updateStateString(SkStatus.SSH_AUTENTICANDO, mContext.getString(R.string.state_auth)); + boolean authenticated = false; + try { + if (mConnection.isAuthMethodAvailable(username, AUTH_PASSWORD)) { + authenticated = mConnection.authenticateWithPassword(username, password); + } + } catch (Exception e) { + throw new IOException("SSH password authentication failed", e); + } + if (!authenticated || !mConnection.isAuthenticationComplete()) { + stopAll(); + throw new IOException(mContext.getString(R.string.dataerror)); + } + SkStatus.logInfo(mContext.getString(R.string.state_auth_success)); + } + + // XXX: Is it right? + @Override + public String[] replyToChallenge(String name, String instruction, + int numPrompts, String[] prompt, boolean[] echo) throws Exception { + String[] responses = new String[numPrompts]; + for (int i = 0; i < numPrompts; i++) { + if (prompt[i].toLowerCase().contains("password")) { + responses[i] = mSessionPassword; + } + } + return responses; + } + + + /** + * ServerHostKeyVerifier + * Fingerprint + */ + + @Override + public boolean verifyServerHostKey(String hostname, int port, + String serverHostKeyAlgorithm, byte[] serverHostKey) + throws Exception { + + String fingerPrint = KnownHosts.createHexFingerprint( + serverHostKeyAlgorithm, serverHostKey); + String key = Settings.SSH_HOST_KEY_PREFIX + hostname + ":" + port; + String pinned = mConfig.getPrefsPrivate().getString(key, ""); + if (pinned == null || pinned.isEmpty()) { + mConfig.getPrefsPrivate().edit().putString(key, fingerPrint).apply(); + SkStatus.logInfo("SSH host key trusted on first use: " + fingerPrint); + return true; + } + if (!pinned.equals(fingerPrint)) { + SkStatus.logError("SSH host key changed. Expected " + pinned + " but received " + fingerPrint); + return false; + } + SkStatus.logDebug("SSH host key verified: " + fingerPrint); + return true; + } + + + /** + * Proxy + */ + + private boolean useProxy = false; + + protected void addProxy(Connection conn) throws Exception { + useProxy = true; + try { + ProxyData xhttpData = new XHttpProxy( + mSessionXhttpEndpoint, + Integer.parseInt(mSessionXhttpPort), + mSessionXhttpTls, + mSessionXhttpSni, + mSessionXhttpPath, + mSessionXhttpHost, + mContext); + conn.setProxyData(xhttpData); + } catch (Exception e) { + throw new Exception("Unable to create XHTTP transport", e); + } + } + + + + + /** + * Socks5 Forwarder + */ + + private DynamicPortForwarder dpf; + private PersistentSocksRelay socksRelay; + private int socksRelayPort = -1; + + private synchronized void startForwarderSocks(int portaLocal) throws Exception { + if (!mConnected || mConnection == null) { + throw new Exception(); + } + + try { + ensurePersistentSocksRelay(portaLocal); + + // A reconnect gets a fresh internal SOCKS port. The Android VPN keeps + // using portaLocal, which belongs to the persistent relay. + closeDynamicForwarderOnly(); + + int nThreads = mConfig.getMaximoThreadsSocks(); + InetSocketAddress backendAddress = new InetSocketAddress("127.0.0.1", 0); + + if (nThreads > 0) { + dpf = mConnection.createDynamicPortForwarder(backendAddress, nThreads); + } else { + dpf = mConnection.createDynamicPortForwarder(backendAddress); + } + + int backendPort = dpf.getLocalPort(); + if (backendPort <= 0) { + throw new IOException("Invalid SSH SOCKS backend port"); + } + + socksRelay.setBackendPort(backendPort); + SkStatus.logDebug("Persistent SOCKS relay backend ready"); + } catch (Exception e) { + closeDynamicForwarderOnly(); + if (socksRelay != null) { + socksRelay.clearBackendPort(); + } + throw new Exception(e); + } + } + + private void ensurePersistentSocksRelay(int portaLocal) throws IOException { + if (socksRelay != null && socksRelay.isRunning() && socksRelayPort == portaLocal) { + return; + } + + stopPersistentSocksRelay(); + PersistentSocksRelay relay = new PersistentSocksRelay(portaLocal); + relay.start(); + socksRelay = relay; + socksRelayPort = portaLocal; + } + + private synchronized void stopForwarderSocks() { + stopForwarderSocks(false); + } + + private synchronized void stopForwarderSocks(boolean keepRelayAlive) { + if (socksRelay != null) { + socksRelay.clearBackendPort(); + } + closeDynamicForwarderOnly(); + + if (!keepRelayAlive) { + stopPersistentSocksRelay(); + } + } + + private void closeDynamicForwarderOnly() { + DynamicPortForwarder forwarder = dpf; + dpf = null; + if (forwarder != null) { + try { + forwarder.close(); + } catch (IOException ignored) { + } + } + } + + private void stopPersistentSocksRelay() { + PersistentSocksRelay relay = socksRelay; + socksRelay = null; + socksRelayPort = -1; + if (relay != null) { + relay.stop(); + } + } + + + /** + * Pinger + */ + + private Thread thPing; + private long lastPingLatency = -1; + private static final long SSH_PING_TIMEOUT_MS = 20_000L; + + private void startPinger(final int timePing) throws Exception { + if (!mConnected) { + throw new Exception(); + } + + + thPing = new Thread() { + @Override + public void run() { + while (mConnected) { + try { + makePinger(); + } catch(InterruptedException e) { + break; + } + } + } + + private synchronized void makePinger() throws InterruptedException { + try { + Connection connection = mConnection; + if (connection != null) { + long ping = connection.ping(SSH_PING_TIMEOUT_MS); + if (lastPingLatency < 0) { + lastPingLatency = ping; + } + } + else throw new InterruptedException(); + } catch(Exception e) { + if (!mStopping && !mReconnecting) { + String detail = e.getMessage(); + requestVpnReconnect("SSH ping failed" + + (detail != null && !detail.isEmpty() ? ": " + detail : "")); + } + throw new InterruptedException(); + } + + if (timePing == 0) + return; + + if (timePing > 0) + sleep(timePing*1000); + else { + + throw new InterruptedException(); + } + } + }; + + // Start the pinger thread. + thPing.start(); + } + + private synchronized void stopPinger() { + if (thPing != null && thPing.isAlive()) { + + + thPing.interrupt(); + thPing = null; + } + } + + /** + * Connection Monitor + */ + + @Override + public void connectionLost(Throwable reason) + { + if (mStarting || mStopping || mReconnecting) { + return; + } + + SkStatus.logError(mContext.getString(R.string.log_conection_lost)); + + if (reason != null) { + String reasonMessage = reason.getMessage(); + SkStatus.logDebug("SSH transport closed: " + reason); + if (reasonMessage != null && reasonMessage.contains( + "There was a problem during connect")) { + reconnectSSH(); + return; + } else if (reasonMessage != null && reasonMessage.contains( + "Closed due to user request")) { + // Local close operations are already coordinated by stopAll() or + // closeSSH(true). Never turn this asynchronous library callback into + // another full service shutdown. + SkStatus.logDebug("Ignoring locally requested transport close"); + return; + } else if (reasonMessage != null && reasonMessage.contains( + "The connect timeout expired")) { + reconnectSSH(); + return; + } + } else { + reconnectSSH(); + return; + } + + reconnectSSH(); + } + + public volatile boolean mReconnecting = false; + + public void reconnectSSH() { + if (mStarting || mStopping || mReconnecting) { + return; + } + + mReconnecting = true; + + // Keep the Android VpnService/TUN alive. Only the failed SSH/XHTTP + // transport and its local dynamic forwarder are replaced. + closeSSH(true); + + SkStatus.updateStateString(SkStatus.SSH_RECONECTANDO, mContext.getString(R.string.reconecti)); + + try { + Thread.sleep(1000); + } catch(InterruptedException e) { + mReconnecting = false; + return; + } + + for (int i = 0; i < RECONNECT_TRIES; i++) { + if (mStopping) { + mReconnecting = false; + return; + } + + int sleepTime = 5; + if (!TunnelUtils.isNetworkOnline(mContext)) { + SkStatus.updateStateString(SkStatus.SSH_AGUARDANDO_REDE, mContext.getString(R.string.wnetwork)); + + SkStatus.logInfo(R.string.state_nonetwork); + } + else { + sleepTime = 3; + mStarting = true; + SkStatus.updateStateString(SkStatus.SSH_RECONECTANDO, mContext.getString(R.string.reconecti)); + + SkStatus.logWarning(mContext.getString(R.string.state_reconnecting)); + + try { + startSshClient(); + + mStarting = false; + mReconnecting = false; + //mConnected = true; + + return; + } catch(Exception e) { + SkStatus.logError(mContext.getString(R.string.state_disconnected)); + closeSSH(true); + } + + mStarting = false; + } + + try { + Thread.sleep(sleepTime*1000); + i--; + } catch(InterruptedException e2){ + mReconnecting = false; + return; + } + } + + mReconnecting = false; + + stopAll(); + } + + @Override + public void onReceiveInfo(int id, String msg) { + if (id == SERVER_BANNER) { + SkStatus.logInfo(mContext.getString(R.string.log_server_banner) + " " + msg); + } + } + + + /** + * Debug Logger + */ + + @Override + public void log(int level, String className, String message) + { + SkStatus.logDebug(String.format("%s: %s", className, message)); + } + + + /** + * Vpn Tunnel + */ + + String serverAddr; + private static final int VPN_WATCHDOG_GRACE_MS = 8000; + private static final int VPN_WATCHDOG_INTERVAL_MS = 3000; + private static final int VPN_WATCHDOG_MAX_MISSES = 3; + private volatile boolean mVpnTunnelBroadcastRegistered = false; + private volatile boolean mVpnWatchdogRunning = false; + private volatile boolean mVpnReconnectRequested = false; + private Thread mVpnWatchdogThread; + + protected void startTunnelVpnService() throws IOException { + if (!mConnected) { + throw new IOException(); + } + + + registerVpnTunnelReceiver(); + + String m_socksServerAddress = String.format("127.0.0.1:%s", mConfig.getPrivString(Settings.PORTA_LOCAL_KEY)); + boolean m_dnsForward = mConfig.getVpnDnsForward(); + String m_udpResolver = mConfig.getVpnUdpForward() ? mConfig.getVpnUdpResolver() : null; + + String servidorIP = mConfig.getPrivString(Settings.XHTTP_ENDPOINT_KEY); + + try { + InetAddress servidorAddr = TransportManager.createInetAddress(servidorIP); + serverAddr = servidorIP = servidorAddr.getHostAddress(); + } catch(UnknownHostException e) { + throw new IOException(mContext.getString(R.string.error_server_ip_invalid)); + } + + String[] m_excludeIps = mergeExcludeIps(servidorIP, mConfig.getVpnBypassList()); + + String[] m_dnsResolvers = null; + if (m_dnsForward) { + List configuredDnsResolvers = new ArrayList(); + String primaryDnsResolver = mConfig.getVpnDnsResolver(); + String secondaryDnsResolver = mConfig.getVpnDnsResolverSecondary(); + + if (primaryDnsResolver != null && !primaryDnsResolver.trim().isEmpty()) { + configuredDnsResolvers.add(primaryDnsResolver.trim()); + } + + if (secondaryDnsResolver != null && !secondaryDnsResolver.trim().isEmpty() && + !secondaryDnsResolver.trim().equals(primaryDnsResolver != null ? primaryDnsResolver.trim() : "")) { + configuredDnsResolvers.add(secondaryDnsResolver.trim()); + } + + if (configuredDnsResolvers.isEmpty()) { + configuredDnsResolvers.add("8.8.8.8"); + configuredDnsResolvers.add("8.8.4.4"); + } + + m_dnsResolvers = configuredDnsResolvers.toArray(new String[0]); + } + else { + List lista = VpnUtils.getNetworkDnsServer(mContext); + m_dnsResolvers = lista.toArray(new String[0]); + } + + if (isServiceVpnRunning()) { + //Log.d(TAG, "already running service"); + + TunnelVpnManager tunnelManager = TunnelState.getTunnelState() + .getTunnelManager(); + + if (tunnelManager != null) { + tunnelManager.restartTunnel(m_socksServerAddress); + } + + return; + } + + Intent startTunnelVpn = new Intent(mContext, TunnelVpnService.class); + startTunnelVpn.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + TunnelVpnSettings settings = new TunnelVpnSettings(m_socksServerAddress, m_dnsForward, m_dnsResolvers, + (m_dnsForward && m_udpResolver == null || !m_dnsForward && m_udpResolver != null), m_udpResolver, m_excludeIps, + true, mConfig.getIsFilterBypassMode(), mConfig.getFilterApps(), mConfig.getIsTetheringSubnet(), mConfig.getDisableIpv6Tunnel()); + startTunnelVpn.putExtra(TunnelVpnManager.VPN_SETTINGS, settings); + + if (mContext.startService(startTunnelVpn) == null) { + SkStatus.logInfo(mContext.getString(R.string.failedvpn)); + + throw new IOException(mContext.getString(R.string.failedvpn)); + } + + TunnelState.getTunnelState().setStartingTunnelManager(); + } + + public static boolean isServiceVpnRunning() { + TunnelState tunnelState = TunnelState.getTunnelState(); + return tunnelState.getStartingTunnelManager() || tunnelState.getTunnelManager() != null; + } + + + private String[] mergeExcludeIps(String serverIp, String[] extraIps) { + LinkedHashSet merged = new LinkedHashSet(); + + // Do not automatically bypass the SSH/proxy server IP. + // The control sockets are protected with VpnService.protect(). + + if (extraIps != null) { + for (String host : extraIps) { + if (host == null) { + continue; + } + String normalized = host.trim(); + if (normalized.isEmpty()) { + continue; + } + + try { + InetAddress[] resolved = InetAddress.getAllByName(normalized); + for (InetAddress address : resolved) { + if (address != null && address.getHostAddress() != null && !address.getHostAddress().trim().isEmpty()) { + merged.add(address.getHostAddress().trim()); + } + } + } catch (UnknownHostException e) { + SkStatus.logDebug("Unable to resolve VPN bypass host: " + normalized + " -> " + e); + } + } + } + + return merged.toArray(new String[0]); + } + + protected synchronized void stopTunnelVpnService() { + stopVpnWatchdog(); + unregisterVpnTunnelReceiver(); + + if (!isServiceVpnRunning()) { + return; + } + + // Use signalStopService to asynchronously stop the service. + // 1. VpnService doesn't respond to stopService calls + // 2. The UI will not block while waiting for stopService to return + // This scheme assumes that the UI will monitor that the service is + // running while the Activity is not bound to it. This is the state + // while the tunnel is shutting down. + + + TunnelVpnManager currentTunnelManager = TunnelState.getTunnelState() + .getTunnelManager(); + + if (currentTunnelManager != null) { + currentTunnelManager.signalStopService(); + } + + /*if (mThreadLocation != null && mThreadLocation.isAlive()) { + mThreadLocation.interrupt(); + } + mThreadLocation = null;*/ + + } + + private synchronized void registerVpnTunnelReceiver() { + if (mVpnTunnelBroadcastRegistered) { + return; + } + + IntentFilter broadcastFilter = + new IntentFilter(TunnelVpnService.TUNNEL_VPN_DISCONNECT_BROADCAST); + broadcastFilter.addAction(TunnelVpnService.TUNNEL_VPN_START_BROADCAST); + + LocalBroadcastManager.getInstance(mContext) + .registerReceiver(m_vpnTunnelBroadcastReceiver, broadcastFilter); + mVpnTunnelBroadcastRegistered = true; + } + + private synchronized void unregisterVpnTunnelReceiver() { + if (!mVpnTunnelBroadcastRegistered) { + return; + } + + try { + LocalBroadcastManager.getInstance(mContext) + .unregisterReceiver(m_vpnTunnelBroadcastReceiver); + } catch (Exception ignored) { + } + mVpnTunnelBroadcastRegistered = false; + } + + private synchronized void startVpnWatchdog() { + /* + * Do not use ConnectivityManager.getActiveNetwork() to validate this + * VPN. V4 intentionally excludes this package UID from the TUN so its + * control sockets can reconnect on the physical network. For a disallowed + * UID Android correctly reports Wi-Fi/mobile as the active network, not + * TRANSPORT_VPN. The previous watchdog therefore generated a false VPN + * loss every few seconds and tore down a healthy connection. + * + * Real VPN loss is already reported by TunnelVpnService.onDestroy(), + * onRevoke(), VPN-start failure, and TunnelVpnManager.onTunnelStopped(). + */ + stopVpnWatchdog(); + } + + private synchronized void stopVpnWatchdog() { + mVpnWatchdogRunning = false; + if (mVpnWatchdogThread != null) { + mVpnWatchdogThread.interrupt(); + mVpnWatchdogThread = null; + } + } + + private synchronized void requestVpnReconnect(final String reason) { + if (mStopping || mReconnecting || mVpnReconnectRequested) { + return; + } + + mVpnReconnectRequested = true; + SkStatus.logWarning("VPN lost. Reconnecting: " + reason); + + new Thread(new Runnable() { + @Override + public void run() { + try { + for (int i = 0; i < 10 && mStarting && !mStopping; i++) { + Thread.sleep(500); + } + reconnectSSH(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + mVpnReconnectRequested = false; + } + } + }, "vpn-reconnect-request").start(); + } + + //private Thread mThreadLocation; + + // Local BroadcastReceiver + private BroadcastReceiver m_vpnTunnelBroadcastReceiver = new BroadcastReceiver() { + @Override + public synchronized void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + + if (TunnelVpnService.TUNNEL_VPN_START_BROADCAST.equals(action)) { + boolean startSuccess = intent.getBooleanExtra(TunnelVpnService.TUNNEL_VPN_START_SUCCESS_EXTRA, true); + + if (!startSuccess) { + requestVpnReconnect("VPN start failed"); + } else { + startVpnWatchdog(); + } + + } else if (TunnelVpnService.TUNNEL_VPN_DISCONNECT_BROADCAST.equals(action)) { + requestVpnReconnect("VPN service disconnected"); + } + } + }; + +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelUtils.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelUtils.java new file mode 100644 index 0000000..547ddea --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/TunnelUtils.java @@ -0,0 +1,29 @@ +package com.dragonssh.xhttpdemo.core.tunnel; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.os.Build; + +/** Network check used by the connection and reconnect loops. */ +public final class TunnelUtils { + private TunnelUtils() {} + + public static boolean isNetworkOnline(Context context) { + ConnectivityManager manager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (manager == null) return false; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Network network = manager.getActiveNetwork(); + if (network == null) return false; + NetworkCapabilities capabilities = manager.getNetworkCapabilities(network); + return capabilities != null + && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + } + + android.net.NetworkInfo info = manager.getActiveNetworkInfo(); + return info != null && info.isConnected(); + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/XHttpBridgeSocket.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/XHttpBridgeSocket.java new file mode 100644 index 0000000..0f891a9 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/XHttpBridgeSocket.java @@ -0,0 +1,73 @@ +package com.dragonssh.xhttpdemo.core.tunnel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * A virtual duplex {@link Socket} handed to the Trilead SSH client by + * {@link XHttpProxy}. Its input stream is the XHTTP downlink (the HTTP/2 GET + * response body) and its output stream is the XHTTP uplink (fed into the HTTP/2 + * POST request body). The underlying HTTP/2 connection is managed by OkHttp, so + * this class only bridges the two streams and cleans up on close. + * + *

Trilead's {@code TransportManager} only calls {@link #getInputStream()}, + * {@link #getOutputStream()}, {@link #close()} and — via {@code Connection} — + * {@link #setTcpNoDelay(boolean)} / {@link #setSoTimeout(int)}. The latter two + * are no-ops here because OkHttp owns the real sockets (read timeout is disabled + * for the streaming tunnel; a stalled SSH handshake is aborted by Trilead's own + * kex-timeout watchdog, which closes this socket).

+ */ +final class XHttpBridgeSocket extends Socket { + + private final InputStream in; + private final OutputStream out; + private final Runnable onClose; + private volatile boolean closed = false; + + XHttpBridgeSocket(InputStream in, OutputStream out, Runnable onClose) { + this.in = in; + this.out = out; + this.onClose = onClose; + } + + @Override + public InputStream getInputStream() { + return in; + } + + @Override + public OutputStream getOutputStream() { + return out; + } + + @Override + public void setTcpNoDelay(boolean on) { /* managed by OkHttp */ } + + @Override + public void setSoTimeout(int timeout) { /* streaming tunnel: no read timeout */ } + + @Override + public boolean isConnected() { + return !closed; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public synchronized void close() throws IOException { + if (closed) { + return; + } + closed = true; + try { out.close(); } catch (Exception ignored) {} + try { in.close(); } catch (Exception ignored) {} + if (onClose != null) { + try { onClose.run(); } catch (Exception ignored) {} + } + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/XHttpProxy.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/XHttpProxy.java new file mode 100644 index 0000000..9ed6d5e --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/XHttpProxy.java @@ -0,0 +1,538 @@ +package com.dragonssh.xhttpdemo.core.tunnel; + +import android.content.Context; + +import com.dragonssh.xhttpdemo.core.BuildConfig; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.SocketProtector; +import com.trilead.ssh2.ProxyData; + +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import javax.net.SocketFactory; +import javax.net.ssl.X509TrustManager; + +import okhttp3.Call; +import okhttp3.Dispatcher; +import okhttp3.Dns; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +/** + * XHTTP (SplitHTTP) transport for the SSH tunnel, over HTTP/2. + * + *

Implements {@link ProxyData} and returns a + * {@link Socket} whose duplex byte stream is consumed by the Trilead SSH client. + * The SSH stream is carried over the server's native XHTTP inbound as a real + * Xray-style XHTTP client would:

+ * + *
    + *
  • Downlink — {@code GET {path}/{session}} : one long-lived streamed + * HTTP/2 response body = server→client SSH stream (stream-down).
  • + *
  • Uplink — {@code POST {path}/{session}/{seq}} : one discrete POST + * per SSH packet, sequence-numbered (packet-up). The server reassembles by + * {@code seq}.
  • + *
+ * + *

Why this exact shape: CDNs (e.g. Azion) buffer HTTP/1.1 and buffer + * streaming request bodies, but pass HTTP/2 responses and discrete POSTs through + * in real time. So the download must be HTTP/2 stream-down and the upload must be + * packet-up — anything else stalls the SSH handshake behind the CDN.

+ * + *

Domain fronting: OkHttp derives the TLS SNI from the URL host and the + * HTTP routing host from the request {@code Host} header, so the URL host + * is the SNI (e.g. a carrier "bug" domain) while {@code Host} is the CDN edge, + * and a custom {@link Dns} pins the connection to the configured server IP.

+ */ +public class XHttpProxy implements ProxyData { + + private static final String USER_AGENT = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"; + private static final MediaType OCTET = MediaType.parse("application/octet-stream"); + private static final long UPLINK_READ_TIMEOUT_SECONDS = 20; + private static final long UPLINK_CALL_TIMEOUT_SECONDS = 30; + + private final String server; // IP/host to actually connect to (the "proxy") + private final int port; + private final boolean useTls; + private final String sni; // TLS SNI (URL host) + private final String path; + private final String host; // XHTTP Host used by the CDN/reverse proxy + private final Context mContext; + + private volatile XHttpBridgeSocket bridge; + + public XHttpProxy(String server, int port, boolean useTls, String sni, String path, String host, Context context) { + this.server = server; + this.port = port; + this.useTls = useTls; + this.sni = trimOrNull(sni); + this.path = path; + this.host = trimOrNull(host); + this.mContext = context; + } + + @Override + public Socket openConnection(String hostname, int port, int connectTimeout, int readTimeout) throws IOException { + final String sessionId = UUID.randomUUID().toString().replace("-", ""); + final String urlHost = (useTls && sni != null) ? sni : stripBrackets(server); + final String hostHeader = hostHeader(); + final String reqPath = buildPath(sessionId); + final String scheme = useTls ? "https" : "http"; + + final HttpUrl sessionUrl = new HttpUrl.Builder() + .scheme(scheme) + .host(urlHost) + .port(this.port) + .encodedPath(reqPath) + .build(); + + if (BuildConfig.DEBUG) { + SkStatus.logInfo("XHTTP(h2): connect=" + server + ":" + this.port + + " sni=" + (sni != null ? sni : "(none)") + " host=" + hostHeader + + " url=" + sessionUrl); + } else { + SkStatus.logInfo(useTls + ? "Iniciando XHTTP (TLS)..." + : "Iniciando XHTTP (sem TLS)..."); + } + + final OkHttpClient client = buildClient(connectTimeout); + + try { + // --- Downlink: GET, streamed HTTP/2 response body (server -> client) --- + Request getReq = new Request.Builder() + .url(sessionUrl) + .header("Host", hostHeader) + .header("User-Agent", USER_AGENT) + .header("Accept", "*/*") + .get() + .build(); + + Response dlResp = client.newCall(getReq).execute(); + if (!dlResp.isSuccessful() || dlResp.body() == null) { + int code = dlResp.code(); + dlResp.close(); + throw new IOException("XHTTP download rejected: HTTP " + code); + } + final Response downResp = dlResp; + final InputStream rawDownIn = dlResp.body().byteStream(); + final InputStream downIn = new FilterInputStream(rawDownIn) { + private boolean terminationLogged; + + private void logTermination(String detail) { + if (!terminationLogged) { + terminationLogged = true; + SkStatus.logError("XHTTP downlink stopped: " + detail); + } + } + + @Override public int read() throws IOException { + try { + int value = super.read(); + if (value < 0) logTermination("EOF"); + return value; + } catch (IOException e) { + logTermination(e.getMessage() != null ? e.getMessage() : e.toString()); + throw e; + } + } + + @Override public int read(byte[] b, int off, int len) throws IOException { + try { + int count = super.read(b, off, len); + if (count < 0) logTermination("EOF"); + return count; + } catch (IOException e) { + logTermination(e.getMessage() != null ? e.getMessage() : e.toString()); + throw e; + } + } + }; + // h2 is required to traverse CDNs; surface the negotiated protocol so a + // silent fallback to HTTP/1.1 (which CDNs buffer) is visible in the log. + SkStatus.logInfo("XHTTP proto=" + dlResp.protocol()); + + // --- Uplink: packet-up (one POST per SSH packet) --- + final PacketUpOutputStream upOut = + new PacketUpOutputStream(client, sessionUrl, hostHeader); + + Runnable onClose = new Runnable() { + @Override public void run() { + upOut.markClosed(); + try { downResp.close(); } catch (Exception ignored) {} + } + }; + + XHttpBridgeSocket b = new XHttpBridgeSocket(downIn, upOut, onClose); + upOut.setBridge(b); + this.bridge = b; + return b; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException("XHTTP connect failed: " + e); + } + } + + private OkHttpClient buildClient(int connectTimeout) throws IOException { + // Pin every lookup to the configured server IP (the "proxy"); the URL host + // is used only for SNI / routing, never for DNS. + final InetAddress[] pinned; + try { + pinned = InetAddress.getAllByName(stripBrackets(server)); + } catch (UnknownHostException e) { + throw new IOException("XHTTP: cannot resolve server " + server + ": " + e); + } + Dns dns = new Dns() { + @Override public List lookup(String hostname) { + return Arrays.asList(pinned); + } + }; + + // This client only needs one stream-down GET plus one packet-up POST at a + // time. Keep a small bounded dispatcher so a transport bug cannot create an + // unbounded HTTP queue inside the VPN process. + Dispatcher dispatcher = new Dispatcher(); + dispatcher.setMaxRequests(8); + dispatcher.setMaxRequestsPerHost(8); + + OkHttpClient.Builder b = new OkHttpClient.Builder() + .dns(dns) + .dispatcher(dispatcher) + .socketFactory(new ProtectedSocketFactory()) + .connectTimeout(connectTimeout > 0 ? connectTimeout : 15000, TimeUnit.MILLISECONDS) + .readTimeout(0, TimeUnit.MILLISECONDS) // streaming download: no read timeout + .writeTimeout(30, TimeUnit.SECONDS) + .callTimeout(0, TimeUnit.MILLISECONDS) + .pingInterval(25, TimeUnit.SECONDS) // keep the h2 connection alive + .retryOnConnectionFailure(false); + + if (useTls) { + X509TrustManager trustAll = new X509TrustManager() { + @Override public void checkClientTrusted(java.security.cert.X509Certificate[] c, String a) {} + @Override public void checkServerTrusted(java.security.cert.X509Certificate[] c, String a) {} + @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[0]; + } + }; + try { + b.sslSocketFactory(new TLSSocketFactory(mContext), trustAll); + } catch (Exception e) { + throw new IOException("XHTTP: TLS setup failed: " + e); + } + b.hostnameVerifier((h, s) -> true); + b.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)); + } else { + // Plaintext is only for direct (no-CDN) testing, where HTTP/1.1 streams + // fine; h2 is only required to defeat CDN buffering over TLS. + b.protocols(Arrays.asList(Protocol.HTTP_1_1)); + } + + return b.build(); + } + + /** SocketFactory that excludes control sockets from the VPN (avoids routing loops). */ + private static final class ProtectedSocketFactory extends SocketFactory { + @Override public Socket createSocket() throws IOException { + Socket s = new Socket(); + boolean vpnReady = SocketProtector.isVpnServiceReady(); + boolean protectedOk = SocketProtector.protect(s); + + /* + * Some Samsung/vendor builds return false here for an unconnected + * socket even though the VPN is valid. Do not destroy the reconnect: + * the active VPN was established with this package UID excluded, so + * this socket is already outside the TUN at the routing-policy layer. + */ + if (vpnReady && !protectedOk && !SocketProtector.isProcessBypassConfigured()) { + try { s.close(); } catch (IOException ignored) {} + throw new IOException("XHTTP control socket has no VPN bypass"); + } + if (vpnReady && !protectedOk) { + SkStatus.logDebug("XHTTP protect(Socket) returned false; using package UID VPN bypass"); + } + return s; + } + @Override public Socket createSocket(String host, int port) { throw new UnsupportedOperationException(); } + @Override public Socket createSocket(String host, int port, InetAddress lh, int lp) { throw new UnsupportedOperationException(); } + @Override public Socket createSocket(InetAddress host, int port) { throw new UnsupportedOperationException(); } + @Override public Socket createSocket(InetAddress host, int port, InetAddress lh, int lp) { throw new UnsupportedOperationException(); } + } + + /** + * Uplink as sequence-numbered POSTs (packet-up): {@code POST {session}/{seq}}. + * + *

Sent strictly in order, single-flight — one POST fully completes + * before the next starts. The server sends its {@code 200} only after pushing + * each packet to the SSH reader, so waiting for each response guarantees the + * server receives seq 0,1,2,… in order and its reorder buffer never grows + * (concurrent/out-of-order POSTs used to overflow it and kill the tunnel).

+ * + *

SSH writes just append to a buffer; a single worker thread drains it, + * coalescing whatever accumulated during the previous round-trip into one POST + * (up to {@link #MAX_POST}) so throughput isn't limited to one packet per RTT. + * Writers block once the buffer hits {@link #MAX_BUFFER} (backpressure).

+ */ + private static final class PacketUpOutputStream extends OutputStream { + private static final int MAX_POST = 900 * 1024; // stay under server per-POST cap (1MB) + private static final int MAX_BUFFER = 8 * 1024 * 1024; // backpressure ceiling + private static final long MAX_BACKPRESSURE_WAIT_MS = 35_000; + + private final OkHttpClient uplinkClient; + private final HttpUrl base; // .../{session} + private final String hostHeader; + + private final Object lock = new Object(); + private final ByteArrayOutputStream buf = new ByteArrayOutputStream(); + private long seq = 0; // touched only by the worker thread + private volatile IOException error; + private volatile boolean closed; + private volatile XHttpBridgeSocket bridge; + private volatile Call activeCall; + private final Thread worker; + + PacketUpOutputStream(OkHttpClient client, HttpUrl base, String hostHeader) { + // The stream-down call must have no read/call timeout, but packet-up POSTs + // absolutely must. A CDN can keep a POST open forever without returning an + // error; with the old shared no-timeout client the SSH writer blocked, the + // ping thread blocked behind it, and automatic reconnect never ran. + this.uplinkClient = client.newBuilder() + .readTimeout(UPLINK_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .callTimeout(UPLINK_CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .build(); + this.base = base; + this.hostHeader = hostHeader; + this.worker = new Thread(this::runUplink, "xhttp-uplink"); + this.worker.setDaemon(true); + this.worker.start(); + } + + void setBridge(XHttpBridgeSocket b) { this.bridge = b; } + + void markClosed() { + Call call; + synchronized (lock) { + closed = true; + buf.reset(); + call = activeCall; + lock.notifyAll(); + } + if (call != null) { + call.cancel(); + } + } + + @Override public void write(int b) throws IOException { + write(new byte[]{(byte) b}, 0, 1); + } + + @Override public void write(byte[] b, int off, int len) throws IOException { + if (len <= 0) { + return; + } + IOException timeoutFailure = null; + synchronized (lock) { + checkState(); + // Backpressure: don't let the buffer grow unbounded if SSH out-runs the link. + long deadlineNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(MAX_BACKPRESSURE_WAIT_MS); + while (buf.size() >= MAX_BUFFER && error == null && !closed) { + long remainingNanos = deadlineNanos - System.nanoTime(); + if (remainingNanos <= 0) { + timeoutFailure = new IOException("XHTTP uplink backpressure timed out"); + break; + } + waitOn(remainingNanos); + } + if (timeoutFailure == null) { + checkState(); + buf.write(b, off, len); + lock.notifyAll(); + } + } + if (timeoutFailure != null) { + fail(timeoutFailure); + throw timeoutFailure; + } + } + + @Override public void flush() throws IOException { + synchronized (lock) { + checkState(); + lock.notifyAll(); // the worker already sends promptly; nudge it + } + } + + @Override public void close() { + markClosed(); + } + + private void runUplink() { + while (true) { + byte[] data; + synchronized (lock) { + while (buf.size() == 0 && !closed && error == null) { + try { + lock.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + fail(new IOException("XHTTP uplink worker interrupted", e)); + return; + } + } + if (error != null || closed) { + return; + } + byte[] all = buf.toByteArray(); + buf.reset(); + if (all.length > MAX_POST) { + data = Arrays.copyOfRange(all, 0, MAX_POST); + buf.write(all, MAX_POST, all.length - MAX_POST); // keep remainder for next round + } else { + data = all; + } + lock.notifyAll(); // release any writer blocked on backpressure + } + if (!sendPacket(data)) { + return; // error already recorded and bridge closed + } + } + } + + /** Sends one packet POST synchronously (single-flight) so ordering is guaranteed. */ + private boolean sendPacket(byte[] data) { + if (closed) { + return false; + } + long s = seq++; + HttpUrl u = base.newBuilder().addPathSegment(Long.toString(s)).build(); + Request req = new Request.Builder() + .url(u) + .header("Host", hostHeader) + .header("User-Agent", USER_AGENT) + .post(RequestBody.create(OCTET, data)) + .build(); + Call call = uplinkClient.newCall(req); + activeCall = call; + if (closed) { + call.cancel(); + activeCall = null; + return false; + } + try (Response r = call.execute()) { + if (!r.isSuccessful()) { + fail(new IOException("XHTTP uplink seq=" + s + " HTTP " + r.code())); + return false; + } + return true; + } catch (IOException e) { + if (closed) { + return false; + } + fail(new IOException("XHTTP uplink seq=" + s + " failed: " + e.getMessage(), e)); + return false; + } finally { + if (activeCall == call) { + activeCall = null; + } + } + } + + private void fail(IOException e) { + boolean first; + synchronized (lock) { + first = error == null; + if (first) { + error = e; + } + lock.notifyAll(); + } + if (first) { + SkStatus.logError("XHTTP uplink stopped: " + e.getMessage()); + } + XHttpBridgeSocket b = bridge; + if (b != null) { + try { b.close(); } catch (Exception ignored) {} + } + } + + private void waitOn(long remainingNanos) throws IOException { + try { + long waitMillis = remainingNanos / 1_000_000L; + int waitNanos = (int) (remainingNanos % 1_000_000L); + lock.wait(waitMillis, waitNanos); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("XHTTP uplink interrupted"); + } + } + + private void checkState() throws IOException { + if (error != null) { + throw error; + } + if (closed) { + throw new IOException("XHTTP uplink closed"); + } + } + } + + private String buildPath(String sessionId) { + String p = (path == null || path.trim().isEmpty()) ? "/" : path.trim(); + if (!p.startsWith("/")) { + p = "/" + p; + } + while (p.endsWith("/")) { + p = p.substring(0, p.length() - 1); + } + return p + "/" + sessionId; + } + + private String hostHeader() { + if (host != null) { + return host; + } + if (sni != null) { + return sni; + } + return stripBrackets(server); + } + + @Override + public void close() { + XHttpBridgeSocket b = this.bridge; + if (b != null) { + try { b.close(); } catch (IOException ignored) {} + } + } + + private static String trimOrNull(String s) { + if (s == null) return null; + s = s.trim(); + return s.isEmpty() ? null : s; + } + + private static String stripBrackets(String h) { + if (h == null) return null; + if (h.startsWith("[") && h.endsWith("]")) { + return h.substring(1, h.length() - 1); + } + return h; + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/CIDRIP.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/CIDRIP.java new file mode 100644 index 0000000..c56fcb1 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/CIDRIP.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ + +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import java.net.InetAddress; +import java.util.Locale; + +public class CIDRIP { + String mIp; + int len; + + public CIDRIP(String address, int prefix_length) { + len = prefix_length; + mIp = address; + } + + public CIDRIP(String address, long prefix_length) { + len = (int) prefix_length; + mIp = address; + } + + @Override + public String toString() { + if (InetAddressUtils.isIPv4Address(mIp)) { + return String.format(Locale.ENGLISH, "%s/%d", mIp, len); + } else if (InetAddressUtils.isIPv6Address(mIp)) { + return String.format(Locale.ENGLISH, "%s/%d", mIp, (long) len); + } else { + return mIp; + } + } + + static class InetAddressUtils { + private static final int INET4_ADDRESS_LENGTH = 4; + private static final int INET6_ADDRESS_LENGTH = 16; + + public static boolean isIPv4Address(String input) { + try { + InetAddress inetAddress = InetAddress.getByName(input); + return inetAddress.getAddress().length == INET4_ADDRESS_LENGTH; + } catch (Exception e) { + return false; + } + } + + public static boolean isIPv6Address(String input) { + try { + InetAddress inetAddress = InetAddress.getByName(input); + return inetAddress.getAddress().length == INET6_ADDRESS_LENGTH; + } catch (Exception e) { + return false; + } + } + } + + static long getInt(String ipaddr) { + try { + InetAddress inetAddress = InetAddress.getByName(ipaddr); + byte[] addressBytes = inetAddress.getAddress(); + long ip = 0; + + if (InetAddressUtils.isIPv4Address(ipaddr)) { + ip += (addressBytes[0] & 0xFFL) << 24; + ip += (addressBytes[1] & 0xFFL) << 16; + ip += (addressBytes[2] & 0xFFL) << 8; + ip += addressBytes[3] & 0xFFL; + } else if (InetAddressUtils.isIPv6Address(ipaddr)) { + for (byte b : addressBytes) { + ip = (ip << 8) | (b & 0xFFL); + } + } + + return ip; + } catch (Exception e) { + return 0; + } + } + + public long getInt() { + return getInt(mIp); + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/NetworkSpace.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/NetworkSpace.java new file mode 100644 index 0000000..96dc503 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/NetworkSpace.java @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ + +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import android.os.Build; +import androidx.annotation.NonNull; + +import java.math.BigInteger; +import java.net.Inet6Address; +import java.util.Collection; +import java.util.Locale; +import java.util.PriorityQueue; +import java.util.TreeSet; +import java.util.Vector; +import com.dragonssh.xhttpdemo.core.BuildConfig; + + +public class NetworkSpace { + + static void assertTrue(boolean f) + { + if (!f) + throw new IllegalStateException(); + } + + public static class IpAddress implements Comparable { + private BigInteger netAddress; + public int networkMask; + private boolean included; + private boolean isV4; + private BigInteger firstAddress; + private BigInteger lastAddress; + + + /** + * sorts the networks with following criteria: + * 1. compares first 1 of the network + * 2. smaller networks are returned as smaller + */ + @Override + public int compareTo(@NonNull IpAddress another) { + int comp = getFirstAddress().compareTo(another.getFirstAddress()); + if (comp != 0) + return comp; + + + if (networkMask > another.networkMask) + return -1; + else if (another.networkMask == networkMask) + return 0; + else + return 1; + } + + /** + * Warning ignores the included integer + * + * @param o the object to compare this instance with. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof IpAddress)) + return super.equals(o); + + + IpAddress on = (IpAddress) o; + return (networkMask == on.networkMask) && on.getFirstAddress().equals(getFirstAddress()); + } + + public IpAddress(CIDRIP ip, boolean include) { + included = include; + netAddress = BigInteger.valueOf(ip.getInt()); + networkMask = ip.len; + isV4 = true; + } + + public IpAddress(Inet6Address address, int mask, boolean include) { + networkMask = mask; + included = include; + + int s = 128; + + netAddress = BigInteger.ZERO; + for (byte b : address.getAddress()) { + s -= 8; + netAddress = netAddress.add(BigInteger.valueOf((b & 0xFF)).shiftLeft(s)); + } + } + + public BigInteger getLastAddress() { + if (lastAddress == null) + lastAddress = getMaskedAddress(true); + return lastAddress; + } + + + public BigInteger getFirstAddress() { + if (firstAddress == null) + firstAddress = getMaskedAddress(false); + return firstAddress; + } + + + private BigInteger getMaskedAddress(boolean one) { + BigInteger numAddress = netAddress; + + int numBits; + if (isV4) { + numBits = 32 - networkMask; + } else { + numBits = 128 - networkMask; + } + + for (int i = 0; i < numBits; i++) { + if (one) + numAddress = numAddress.setBit(i); + else + numAddress = numAddress.clearBit(i); + } + return numAddress; + } + + + @Override + public String toString() { + //String in = included ? "+" : "-"; + if (isV4) + return String.format(Locale.US, "%s/%d", getIPv4Address(), networkMask); + else + return String.format(Locale.US, "%s/%d", getIPv6Address(), networkMask); + } + + IpAddress(BigInteger baseAddress, int mask, boolean included, boolean isV4) { + this.netAddress = baseAddress; + this.networkMask = mask; + this.included = included; + this.isV4 = isV4; + } + + + public IpAddress[] split() { + IpAddress firstHalf = new IpAddress(getFirstAddress(), networkMask + 1, included, isV4); + IpAddress secondHalf = new IpAddress(firstHalf.getLastAddress().add(BigInteger.ONE), networkMask + 1, included, isV4); + if (BuildConfig.DEBUG) + assertTrue(secondHalf.getLastAddress().equals(getLastAddress())); + return new IpAddress[]{firstHalf, secondHalf}; + } + + public String getIPv4Address() { + if (BuildConfig.DEBUG) { + assertTrue(isV4); + assertTrue(netAddress.longValue() <= 0xffffffffl); + assertTrue(netAddress.longValue() >= 0); + } + long ip = netAddress.longValue(); + return String.format(Locale.US, "%d.%d.%d.%d", (ip >> 24) % 256, (ip >> 16) % 256, (ip >> 8) % 256, ip % 256); + } + + public String getIPv6Address() { + if (BuildConfig.DEBUG) assertTrue(!isV4); + BigInteger r = netAddress; + + String ipv6str = null; + boolean lastPart = true; + + while (r.compareTo(BigInteger.ZERO) == 1) { + + long part = r.mod(BigInteger.valueOf(0x10000)).longValue(); + if (ipv6str != null || part != 0) { + if (ipv6str == null && !lastPart) + ipv6str = ":"; + + if (lastPart) + ipv6str = String.format(Locale.US, "%x", part, ipv6str); + else + ipv6str = String.format(Locale.US, "%x:%s", part, ipv6str); + } + + r = r.shiftRight(16); + lastPart = false; + } + if (ipv6str == null) + return "::"; + + + return ipv6str; + } + + public boolean containsNet(IpAddress network) { + // this.first >= net.first && this.last <= net.last + BigInteger ourFirst = getFirstAddress(); + BigInteger ourLast = getLastAddress(); + BigInteger netFirst = network.getFirstAddress(); + BigInteger netLast = network.getLastAddress(); + + boolean a = ourFirst.compareTo(netFirst) != 1; + boolean b = ourLast.compareTo(netLast) != -1; + return a && b; + + } + } + + + TreeSet mIpAddresses = new TreeSet(); + + + public Collection getNetworks(boolean included) { + Vector ips = new Vector(); + for (IpAddress ip : mIpAddresses) { + if (ip.included == included) + ips.add(ip); + } + return ips; + } + + public void clear() { + mIpAddresses.clear(); + } + + + public void addIP(CIDRIP cidrIp, boolean include) { + + mIpAddresses.add(new IpAddress(cidrIp, include)); + } + + public void addIPSplit(CIDRIP cidrIp, boolean include) { + IpAddress newIP = new IpAddress(cidrIp, include); + IpAddress[] splitIps = newIP.split(); + for (IpAddress split : splitIps) + mIpAddresses.add(split); + } + + public void addIPv6(Inet6Address address, int mask, boolean included) { + mIpAddresses.add(new IpAddress(address, mask, included)); + } + + TreeSet generateIPList() { + + PriorityQueue networks = new PriorityQueue(mIpAddresses); + + TreeSet ipsDone = new TreeSet(); + + IpAddress currentNet = networks.poll(); + if (currentNet == null) + return ipsDone; + + while (currentNet != null) { + // Check if it and the next of it are compatible + IpAddress nextNet = networks.poll(); + + if (BuildConfig.DEBUG) assertTrue(currentNet!=null); + if (nextNet == null || currentNet.getLastAddress().compareTo(nextNet.getFirstAddress()) == -1) { + // Everything good, no overlapping nothing to do + ipsDone.add(currentNet); + + currentNet = nextNet; + } else { + // This network is smaller or equal to the next but has the same base address + if (currentNet.getFirstAddress().equals(nextNet.getFirstAddress()) && currentNet.networkMask >= nextNet.networkMask) { + if (currentNet.included == nextNet.included) { + // Included in the next next and same type + // Simply forget our current network + currentNet = nextNet; + } else { + // our currentNet is included in next and types differ. Need to split the next network + IpAddress[] newNets = nextNet.split(); + + + // TODO: The contains method of the Priority is stupid linear search + + // First add the second half to keep the order in networks + if (!networks.contains(newNets[1])) + networks.add(newNets[1]); + + if (newNets[0].getLastAddress().equals(currentNet.getLastAddress())) { + if (BuildConfig.DEBUG) + assertTrue(newNets[0].networkMask == currentNet.networkMask); + // Don't add the lower half that would conflict with currentNet + } else { + if (!networks.contains(newNets[0])) + networks.add(newNets[0]); + } + // Keep currentNet as is + } + } else { + if (BuildConfig.DEBUG) { + assertTrue(currentNet.networkMask < nextNet.networkMask); + assertTrue(nextNet.getFirstAddress().compareTo(currentNet.getFirstAddress()) == 1); + assertTrue(currentNet.getLastAddress().compareTo(nextNet.getLastAddress()) != -1); + } + // This network is bigger than the next and last ip of current >= next + + //noinspection StatementWithEmptyBody + if (currentNet.included == nextNet.included) { + // Next network is in included in our network with the same type, + // simply ignore the next and move on + } else { + // We need to split our network + IpAddress[] newNets = currentNet.split(); + + + if (newNets[1].networkMask == nextNet.networkMask) { + if (BuildConfig.DEBUG) { + assertTrue(newNets[1].getFirstAddress().equals(nextNet.getFirstAddress())); + assertTrue(newNets[1].getLastAddress().equals(currentNet.getLastAddress())); + // split second equal the next network, do not add it + } + networks.add(nextNet); + } else { + // Add the smaller network first + networks.add(newNets[1]); + networks.add(nextNet); + } + currentNet = newNets[0]; + + } + } + } + + } + + return ipsDone; + } + + public Collection getPositiveIPList() { + TreeSet ipsSorted = generateIPList(); + + Vector ips = new Vector(); + for (IpAddress ia : ipsSorted) { + if (ia.included) + ips.add(ia); + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + // Include postive routes from the original set under < 4.4 since these might overrule the local + // network but only if no smaller negative route exists + for (IpAddress origIp : mIpAddresses) { + if (!origIp.included) + continue; + + // The netspace exists + if (ipsSorted.contains(origIp)) + continue; + + boolean skipIp = false; + // If there is any smaller net that is excluded we may not add the positive route back + + for (IpAddress calculatedIp : ipsSorted) { + if (!calculatedIp.included && origIp.containsNet(calculatedIp)) { + skipIp = true; + break; + } + } + if (skipIp) + continue; + + // It is safe to include the IP + ips.add(origIp); + } + + } + + return ips; + } + +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Pdnsd.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Pdnsd.java new file mode 100644 index 0000000..2f69896 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Pdnsd.java @@ -0,0 +1,143 @@ +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import java.io.File; + +import android.content.Context; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import com.dragonssh.xhttpdemo.core.R; +import com.dragonssh.xhttpdemo.core.util.StreamGobbler; +import com.dragonssh.xhttpdemo.core.util.FileUtils; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.dragonssh.xhttpdemo.core.util.CustomNativeLoader; + +public class Pdnsd extends Thread { + private final static String TAG = "PdnsdThread"; + private final static String PDNSD_SERVER = "server {\n label= \"%1$s\";\n ip = %2$s;\n port = %3$d;\n uptest = none;\n }\n"; + private final static String PDNSD_SERVER_TEST = "server {\n label= \"%1$s\";\n ip = %2$s;\n port = %3$d;\n reject = ::/0;\n reject_policy = negate;\n reject_recursively = on;\n timeout = 5;\n }\n"; + private final static String PDNSD_BIN = "libpdnsd"; + + private OnPdnsdListener mListener; + public interface OnPdnsdListener { + public void onStart(); + public void onStop(); + } + + private Process mProcess; + private File filePdnsd; + + private Context mContext; + private String[] mDnsHosts; + private int mDnsPort; + private String mPdnsdHost; + private int mPdnsdPort; + + public Pdnsd(Context context, String[] dnsHosts, int dnsPort, String pdnsdHost, int pdnsdPort) { + mContext = context; + + mDnsHosts = dnsHosts; + mDnsPort = dnsPort; + mPdnsdHost = pdnsdHost; + mPdnsdPort = pdnsdPort; + } + + @Override + public void run() { + + if (mListener != null) { + mListener.onStart(); + } + + try { + + filePdnsd = CustomNativeLoader.loadNativeBinary(mContext, PDNSD_BIN, new File(mContext.getFilesDir(), PDNSD_BIN)); + + if (filePdnsd == null) { + throw new IOException("Pdnsd binary not found"); + } + + File fileConf = makePdnsdConf(mContext.getFilesDir(), mDnsHosts, mDnsPort, mPdnsdHost, mPdnsdPort); + + String cmdString = filePdnsd.getCanonicalPath() + " -v9 -c " + fileConf.toString(); + + mProcess = Runtime.getRuntime().exec(cmdString); + + StreamGobbler.OnLineListener onLineListener = new StreamGobbler.OnLineListener(){ + @Override + public void onLine(String log){ + SkStatus.logDebug("Pdnsd: " + log); + } + }; + + StreamGobbler stdoutGobbler = new StreamGobbler(mProcess.getInputStream(), onLineListener); + StreamGobbler stderrGobbler = new StreamGobbler(mProcess.getErrorStream(), onLineListener); + + stdoutGobbler.start(); + stderrGobbler.start(); + + mProcess.waitFor(); + + } catch (IOException e) { + SkStatus.logException("Pdnsd Error", e); + } catch(Exception e){ + SkStatus.logDebug("Pdnsd Error: " + e); + } + + mProcess = null; + if (mListener != null) { + mListener.onStop(); + } + + } + + @Override + public synchronized void interrupt() + { + super.interrupt(); + + if (mProcess != null) + mProcess.destroy(); + + try { + if (filePdnsd != null) + VpnUtils.killProcess(filePdnsd); + } catch(Exception e) {} + + mProcess = null; + filePdnsd = null; + } + + private File makePdnsdConf(File fileDir, String[] dnsHosts, int dnsPort, String pdnsdHost, int pdnsdPort) throws FileNotFoundException, IOException { + String content = FileUtils.readFromRaw(mContext, R.raw.pdnsd_local); + + // dns servers + StringBuilder server_dns = new StringBuilder(); + for (int i = 0; i < dnsHosts.length; i++){ + String dnsHost = dnsHosts[i]; + server_dns.append(String.format(PDNSD_SERVER, "server" + Integer.toString(i+1), dnsHost, dnsPort)); + } + + String conf = String.format(content, server_dns.toString(), fileDir.getCanonicalPath(), pdnsdHost, pdnsdPort); + + File f = new File(fileDir,"pdnsd.conf"); + if (f.exists()) { + f.delete(); + } + FileUtils.saveTextFile(f, conf); + + File cache = new File(fileDir,"pdnsd.cache"); + if (!cache.exists()) { + try { + cache.createNewFile(); + } catch (Exception e) {} + } + + return f; + } + + public void setOnPdnsdListener(OnPdnsdListener listener){ + this.mListener = listener; + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/SocketProtector.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/SocketProtector.java new file mode 100644 index 0000000..b86c5da --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/SocketProtector.java @@ -0,0 +1,167 @@ +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import android.net.VpnService; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import java.net.DatagramSocket; +import java.net.Socket; + +/** + * Centralized helper to ensure control sockets (SSH / proxy / TLS) are excluded + * from the VPN routing. + * + *

When a VPN is established and a control socket is not protected, Android + * will route that socket into the VPN itself, creating a loop (VPN -> SOCKS -> + * VPN). This is especially visible with IPv6, where ::/0 routing is commonly + * installed. + */ +public final class SocketProtector { + + private static volatile VpnService sVpnService; + + // True only after the active VPN interface has been established with this + // application UID excluded through Builder.addDisallowedApplication(). + // In that mode every control-plane socket created by this process already + // uses the underlying network, even on devices where protect(Socket) + // incorrectly returns false during a reconnect. + private static volatile boolean sProcessBypassConfigured; + + // Sockets created before the VpnService instance is available. + // We keep weak refs to avoid leaking in edge cases. + private static final Object sLock = new Object(); + private static final List> sPending = new ArrayList<>(); + + private SocketProtector() { + } + + public static void setVpnService(VpnService vpnService) { + sVpnService = vpnService; + if (vpnService != null) { + flushPending(vpnService); + } + } + + /** + * Clears the active VpnService only if it is still the same owner. + * This prevents a stopped XRAY service from clearing the SSH VPN service + * after the user switches modes. + */ + public static void clearVpnService(VpnService vpnService) { + if (vpnService == null) { + return; + } + synchronized (sLock) { + if (sVpnService == vpnService) { + sVpnService = null; + sProcessBypassConfigured = false; + sPending.clear(); + } + } + } + + public static boolean isVpnServiceReady() { + return sVpnService != null; + } + + public static void setProcessBypassConfigured(boolean configured) { + sProcessBypassConfigured = configured; + } + + public static boolean isProcessBypassConfigured() { + return sProcessBypassConfigured; + } + + public static boolean protect(Socket socket) { + if (socket == null) return false; + final VpnService svc = sVpnService; + if (svc == null) { + enqueue(socket); + return false; + } + return doProtect(svc, socket); + } + + public static boolean protect(DatagramSocket socket) { + if (socket == null) return false; + final VpnService svc = sVpnService; + if (svc == null) { + enqueue(socket); + return false; + } + return doProtect(svc, socket); + } + + /** + * Protect a raw native socket file descriptor. + * + * Use this from gomobile/JNI callbacks before the native code connects + * the socket. Raw FDs cannot be safely queued: the native side may connect + * or close/reuse the descriptor before the VPN service becomes available. + */ + public static boolean protectFd(int fd) { + if (fd < 0) return false; + final VpnService svc = sVpnService; + if (svc == null) return false; + return doProtectFd(svc, fd); + } + + private static void enqueue(Object socket) { + synchronized (sLock) { + // Avoid unlimited growth if protect() is called repeatedly. + if (sPending.size() > 128) { + // Drop oldest. + sPending.remove(0); + } + sPending.add(new WeakReference<>(socket)); + } + } + + private static void flushPending(VpnService svc) { + final List toProtect = new ArrayList<>(); + synchronized (sLock) { + for (WeakReference ref : sPending) { + Object obj = ref.get(); + if (obj != null) { + toProtect.add(obj); + } + } + sPending.clear(); + } + + for (Object obj : toProtect) { + if (obj instanceof Socket) { + doProtect(svc, (Socket) obj); + } else if (obj instanceof DatagramSocket) { + doProtect(svc, (DatagramSocket) obj); + } + } + } + + private static boolean doProtect(VpnService svc, Socket socket) { + try { + return svc.protect(socket); + } catch (Throwable ignored) { + return false; + } + } + + private static boolean doProtect(VpnService svc, DatagramSocket socket) { + try { + return svc.protect(socket); + } catch (Throwable ignored) { + return false; + } + } + + private static boolean doProtectFd(VpnService svc, int fd) { + try { + return svc.protect(fd); + } catch (Throwable ignored) { + return false; + } + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Tun2Socks.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Tun2Socks.java new file mode 100644 index 0000000..d550395 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Tun2Socks.java @@ -0,0 +1,293 @@ +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import android.os.ParcelFileDescriptor; + +import java.io.IOException; +import java.io.File; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import java.io.FileDescriptor; +import android.content.Context; + +import com.dragonssh.xhttpdemo.core.util.StreamGobbler; +import androidx.core.content.ContextCompat; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.dragonssh.xhttpdemo.core.util.CustomNativeLoader; + +public class Tun2Socks extends Thread implements StreamGobbler.OnLineListener { + + private static final String TAG = Tun2Socks.class.getSimpleName(); + private static final String TUN2SOCKS_BIN = "libtun2socks"; + + private OnTun2SocksListener mListener; + public interface OnTun2SocksListener { + public void onStart(); + public void onStop(); + } + + private Process tun2SocksProcess; + private ParcelFileDescriptor mVpnInterfaceFileDescriptor; + private int mVpnInterfaceMTU; + /** + * IPv4 address assigned to the VPN interface. Historically only a single + * IPv4 address was supported. To support IPv6 natively, a separate + * {@code mVpnIp6Address} field has been added. Existing code that + * constructs {@link Tun2Socks} without specifying an IPv6 address will + * continue to work – the IPv6 address will simply remain {@code null} and + * no {@code --netif-ip6addr} argument will be passed to the underlying + * tun2socks binary. + */ + private String mVpnIpAddress; + /** + * IPv6 address assigned to the VPN interface. This may be {@code null} + * when only IPv4 connectivity is required. When provided, the + * constructor will append a {@code --netif-ip6addr} flag when spawning + * the tun2socks process. + */ + private String mVpnIp6Address; + private String mVpnNetMask; + private String mSocksServerAddress; + private String mUdpgwServerAddress; + private String mDnsResolverAddress; + private boolean mUdpgwTransparentDNS; + private Context mContext; + + private File fileTun2Socks; + + /** + * Construct a {@link Tun2Socks} instance. This constructor has been + * updated to accept an optional IPv6 address. If the provided + * {@code vpnIp6Address} is {@code null} or empty, only an IPv4 address will + * be configured on the tun interface. To maintain backwards + * compatibility with existing callers, an overloaded constructor without + * the {@code vpnIp6Address} parameter is provided below. + * + * @param context the context used to resolve resources + * @param vpnInterfaceFileDescriptor file descriptor of the VPN interface + * @param vpnInterfaceMTU MTU of the VPN interface + * @param vpnIpAddress IPv4 address assigned to the interface + * @param vpnIp6Address IPv6 address assigned to the interface (may be null) + * @param vpnNetMask network mask for the IPv4 address + * @param socksServerAddress address of the SOCKS proxy + * @param udpgwServerAddress address of the UDP gateway (may be null) + * @param dnsResolverAddress address of the DNS resolver (may be null) + * @param udpgwTransparentDNS whether to enable transparent DNS over UDP + */ + public Tun2Socks(Context context, ParcelFileDescriptor vpnInterfaceFileDescriptor, int vpnInterfaceMTU, + String vpnIpAddress, String vpnIp6Address, String vpnNetMask, + String socksServerAddress, String udpgwServerAddress, + String dnsResolverAddress, boolean udpgwTransparentDNS) { + mContext = context; + + mVpnInterfaceFileDescriptor = vpnInterfaceFileDescriptor; + mVpnInterfaceMTU = vpnInterfaceMTU; + mVpnIpAddress = vpnIpAddress; + mVpnIp6Address = vpnIp6Address; + mVpnNetMask = vpnNetMask; + mSocksServerAddress = socksServerAddress; + mUdpgwServerAddress = udpgwServerAddress; + mDnsResolverAddress = dnsResolverAddress; + mUdpgwTransparentDNS = udpgwTransparentDNS; + } + + /** + * Backwards compatible constructor. Invokes the full constructor without + * assigning an IPv6 address. + */ + public Tun2Socks(Context context, ParcelFileDescriptor vpnInterfaceFileDescriptor, int vpnInterfaceMTU, + String vpnIpAddress, String vpnNetMask, String socksServerAddress, + String udpgwServerAddress, String dnsResolverAddress, boolean udpgwTransparentDNS) { + this(context, vpnInterfaceFileDescriptor, vpnInterfaceMTU, vpnIpAddress, null, vpnNetMask, + socksServerAddress, udpgwServerAddress, dnsResolverAddress, udpgwTransparentDNS); + } + + @Override + public void run() { + + if (mListener != null) { + mListener.onStart(); + } + + try { + + StringBuilder cmd = new StringBuilder(); + + //File fileTun2Socks = CustomNativeLoader.loadExecutableBinary(mContext, "libtun2socks.so"); + fileTun2Socks = CustomNativeLoader.loadNativeBinary(mContext, TUN2SOCKS_BIN, new File(mContext.getFilesDir(),TUN2SOCKS_BIN)); + + if (fileTun2Socks == null){ + throw new IOException("Tun2Socks binary not found"); + } + + if (mVpnInterfaceFileDescriptor != null){ + File file_path = new File(ContextCompat.getDataDir(mContext), "sock_path"); + + try { + if (!file_path.exists()) + file_path.createNewFile(); + } catch(IOException e){ + throw new IOException("Failed to create socket file: " + file_path.getCanonicalPath()); + } + + cmd.append(fileTun2Socks.getCanonicalPath()); + // Always configure the IPv4 address on the tun interface + cmd.append(" --netif-ipaddr " + mVpnIpAddress); + // Optionally configure an IPv6 address when provided. When + // mVpnIp6Address is null or empty, no IPv6 address is passed + // to the tun2socks binary. This allows clients to continue + // operating in IPv4-only environments while enabling native + // dual-stack operation when possible. + if (mVpnIp6Address != null && !mVpnIp6Address.isEmpty()) { + cmd.append(" --netif-ip6addr " + mVpnIp6Address); + } + cmd.append(" --netif-netmask " + mVpnNetMask); + cmd.append(" --socks-server-addr " + mSocksServerAddress); + cmd.append(" --tunmtu " + Integer.toString(mVpnInterfaceMTU)); + cmd.append(" --tunfd " + mVpnInterfaceFileDescriptor.getFd()); + cmd.append(" --sock " + file_path.getAbsolutePath()); + cmd.append(" --loglevel " + Integer.toString(3)); + + if (mUdpgwServerAddress != null) { + if (mUdpgwTransparentDNS) { + cmd.append(" --udpgw-transparent-dns"); + } + String udpgwAddr = mUdpgwServerAddress; + // Normalize IPv6 host:port into [host]:port for badvpn-tun2socks parser. + // Accept user input as: + // - host:port (IPv4/hostname) + // - ipv6 (no port) -> default 7300 + // - ipv6:port (no brackets) -> brackets will be added + // - [ipv6]:port (already OK) + if (udpgwAddr != null) { + udpgwAddr = udpgwAddr.trim(); + if (!udpgwAddr.isEmpty()) { + // If it's a bare IPv6 without brackets and has multiple ':' then bracket it. + boolean looksIpv6 = udpgwAddr.contains(":") && udpgwAddr.indexOf(':') != udpgwAddr.lastIndexOf(':'); + if (looksIpv6 && !udpgwAddr.startsWith("[")) { + // Split last ':' as port if present, otherwise default. + int lastColon = udpgwAddr.lastIndexOf(':'); + String hostPart = udpgwAddr; + String portPart = "7300"; + if (lastColon > 0 && lastColon < udpgwAddr.length() - 1) { + hostPart = udpgwAddr.substring(0, lastColon); + String maybePort = udpgwAddr.substring(lastColon + 1); + if (maybePort.matches("\\d+")) { + portPart = maybePort; + } else { + hostPart = udpgwAddr; // treat as no-port + } + } + udpgwAddr = "[" + hostPart + "]:" + portPart; + } else if (udpgwAddr.matches("^[0-9a-fA-F:]+$")) { + // Pure IPv6 without port + udpgwAddr = "[" + udpgwAddr + "]:7300"; + } + } + } + cmd.append(" --udpgw-remote-server-addr " + udpgwAddr); + } + + if (mDnsResolverAddress != null) { + cmd.append(" --dnsgw " + mDnsResolverAddress); + } + + // Start the native tun2socks process. + tun2SocksProcess = Runtime.getRuntime().exec(cmd.toString()); + + StreamGobbler stdoutGobbler = new StreamGobbler(tun2SocksProcess.getInputStream(), this); + StreamGobbler stderrGobbler = new StreamGobbler(tun2SocksProcess.getErrorStream(), this); + + stdoutGobbler.start(); + stderrGobbler.start(); + + // send Fd + if (!sendFd(mVpnInterfaceFileDescriptor, file_path)) { + throw new IOException("Failed to pass the TUN file descriptor to tun2socks"); + } + + tun2SocksProcess.waitFor(); + } + + } catch (IOException e) { + SkStatus.logException("Tun2Socks Error", e); + } catch (Exception e) { + SkStatus.logDebug("Tun2Socks Error: " + e.getMessage()); + } + + tun2SocksProcess = null; + if (mListener != null) { + mListener.onStop(); + } + } + + @Override + public synchronized void interrupt() + { + // TODO: Implement this method + super.interrupt(); + + //net.typeblog.socks.System.jniclose(mVpnInterfaceFileDescriptor.getFd()); + + if (tun2SocksProcess != null) + tun2SocksProcess.destroy(); + + try { + if (fileTun2Socks != null) + VpnUtils.killProcess(fileTun2Socks); + } catch (Exception e) {} + + tun2SocksProcess = null; + fileTun2Socks = null; + } + + public void setOnTun2SocksListener(OnTun2SocksListener listener){ + this.mListener = listener; + } + + + /** + * StreamGobbler OnLine Listener + * implementation + */ + + @Override + public void onLine(String log){ + SkStatus.logDebug("Tun2Socks: " + log); + } + + + //---------------------------------------------------------------------------- + // Utils + //---------------------------------------------------------------------------- + + private boolean sendFd(ParcelFileDescriptor fileDescriptor, File toFile) throws InterruptedException { + + SkStatus.logDebug("Enviando Fd para sock"); + + for (int tries = 10; tries >= 0; tries--) { + try { + LocalSocket localSocket = new LocalSocket(); + localSocket.connect(new LocalSocketAddress(toFile.getAbsolutePath(), LocalSocketAddress.Namespace.FILESYSTEM)); + localSocket.setFileDescriptorsForSend(new FileDescriptor[]{ + fileDescriptor.getFileDescriptor() + }); + localSocket.getOutputStream().write(42); + localSocket.shutdownOutput(); + localSocket.close(); + return true; + } catch(IOException unused) { + Thread.sleep(500); + } + + /*if (net.typeblog.socks.System.sendfd(fileDescriptor.getFd(), toFile.getAbsolutePath()) != -1) { + return true; + } + + try { + Thread.sleep(500); + } catch(InterruptedException e){}*/ + } + + return false; + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Tunnel.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Tunnel.java new file mode 100644 index 0000000..28e3882 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/Tunnel.java @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2015, Psiphon Inc. + * All rights reserved. + * + * 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 . + * + */ + +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.net.VpnService; +import android.os.Build; +import android.os.ParcelFileDescriptor; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import com.dragonssh.xhttpdemo.core.config.Settings; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; + +/** + * Baseado no Cordova Plugin Tun2Socks + * + * @author dFiR30n + */ + +public class Tunnel { + + public interface HostService { + public String getAppName(); + + public Context getContext(); + + // Object must be a VpnService; Android < 4 cannot reference this class name + public Object getVpnService(); + + // Object must be a VpnService.Builder; + // Android < 4 cannot reference this class name + public Object newVpnServiceBuilder(); + + public void onDiagnosticMessage(String message); + + public void onTunnelConnected(); + + public void onVpnEstablished(); + + public void onTunnelStopped(); + } + + private final HostService mHostService; + private VpnUtils.PrivateAddress mPrivateAddress; + private AtomicReference mTunFd; + private AtomicBoolean mRoutingThroughTunnel; + private Tun2Socks mTun2Socks; + private Pdnsd mPdnsd; + private NetworkSpace mRoutes; + private Settings mConfig; + // Only StartNET VPNService instance may exist at a time, as the underlying + // tun2socks implementation contains global state. + private static Tunnel mTunnel; + + public static synchronized Tunnel newTunnel(HostService hostService) { + if (mTunnel != null) { + mTunnel.stop(); + } + mTunnel = new Tunnel(hostService); + return mTunnel; + } + + private Tunnel(HostService hostService) { + mHostService = hostService; + mTunFd = new AtomicReference(); + mRoutingThroughTunnel = new AtomicBoolean(false); + mRoutes = new NetworkSpace(); + + //org.uproxy.tun2socks.Tun2SocksJni.init(); + } + + public Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + //---------------------------------------------------------------------------------------------- + // Public API + //---------------------------------------------------------------------------------------------- + + // To start, call in sequence: startRouting(), then startTunneling(). After startRouting() + // succeeds, the caller must call stop() to clean up. + + // Returns true when the VPN routing is established; returns false if the VPN could not + // be started due to lack of prepare or revoked permissions (called should re-prepare and + // try again); throws exception for other error conditions. + public synchronized boolean startRouting(TunnelVpnSettings settings) throws java.lang.Exception { + return startVpn(settings.mDnsForward, settings.mDnsResolver, settings.mExcludeIps, + settings.mEnableFilterApps, settings.mFilterBypassMode, settings.mFilterApps, settings.mTetheringSubnet, settings.mDisableIpv6Tunnel); + } + + // Starts tun2socks. Returns true on success. + public synchronized boolean startTunneling(String socksServerAddress, String[] dnsResolver, boolean forwardDns, String udpResolver, boolean udpDnsRelay) + throws Exception { + return routeThroughTunnel(socksServerAddress, dnsResolver, forwardDns, udpResolver, udpDnsRelay); + } + + // Stops routing traffic through the tunnel by stopping tun2socks. + // The VPN is unaffected by this method. + public synchronized void stopTunneling() { + stopRoutingThroughTunnel(); + } + + // Note: to avoid deadlock, do not call directly from a HostService callback; + // instead post to a Handler if necessary to trigger from a HostService callback. + public synchronized void stop() { + stopVpn(); + } + + //---------------------------------------------------------------------------- + // VPN Routing + //---------------------------------------------------------------------------- + + private static final String VPN_INTERFACE_NETMASK = "255.255.255.0"; + // IPv6 addressing for the TUN interface and the internal tun2socks router. + // Using an RFC4193 ULA prefix avoids collisions with real networks. + private static final String VPN_INTERFACE_IPV6_ADDRESS = "fd00:1:fd00:1::1"; + private static final int VPN_INTERFACE_IPV6_PREFIX_LENGTH = 64; + private static final String VPN_ROUTER_IPV6_ADDRESS = "fd00:1:fd00:1::2"; + private static final String DNS_RESOLVER_IP = "8.8.8.8"; + private static final int DNS_RESOLVER_PORT = 53; + //private String dns6 = "0"; + private int mMtu = 1500; + + // Note: Atomic variables used for getting/setting local proxy port, routing flag, and + // tun fd, as these functions may be called via callbacks. Do not use + // synchronized functions as stop() is synchronized and a deadlock is possible as callbacks + // can be called while stop holds the lock. + // + private boolean startVpn(boolean forwardDns, String[] dnsResolver, String[] excludeIps, + boolean enabledFilter, boolean filterBypassMode, String[] filterApps, boolean enableTethering, boolean disableIpv6Tunnel) throws java.lang.Exception { + + mPrivateAddress = VpnUtils.selectPrivateAddress(); + + // Make sure control sockets (SSH/proxy/TLS) are excluded from the VPN. + // This prevents routing loops, especially when IPv6 ::/0 is installed. + SocketProtector.setVpnService((VpnService) mHostService.getVpnService()); + + // routes list + // For IPv4 we use NetworkSpace to compute a minimal set of routes excluding the server. + // For IPv6, Android does not provide a direct "exclude this single address" API when + // installing ::/0. As a defensive fallback (in addition to VpnService.protect), we build + // a set of IPv6 prefixes that cover all IPv6 addresses *except* the excluded server /128. + // This avoids the control connection being routed into the VPN when the server is IPv6. + String excludedServerIpv6 = null; + for (String ip : excludeIps) { + if (ip != null && ip.contains(":")) { + excludedServerIpv6 = ip; + SkStatus.logInfo("IPV6 DETECTED"); + } else { + SkStatus.logInfo("IPV4 DETECTED"); + mRoutes.addIP(new CIDRIP(ip, 32), false); + } + } + + + Locale previousLocale = Locale.getDefault(); + + final String errorMessage = "startVpn failed"; + try { + + // Workaround for https://code.google.com/p/android/issues/detail?id=61096 + Locale.setDefault(new Locale("en")); + + ParcelFileDescriptor tunFd = null; + + // Create the Android VPN builder. + VpnService.Builder builder = ((VpnService.Builder) mHostService.newVpnServiceBuilder()) + .addAddress(mPrivateAddress.mIpAddress, mPrivateAddress.mPrefixLength) + .allowFamily(AF_INET); + + /* + * Keep the VPN implementation's own UID outside the TUN at the Android + * policy layer. All SSH, TLS, proxy and XHTTP control sockets are created + * by this package. Excluding the package prevents those reconnect sockets + * from ever entering tun2socks, even on vendor ROMs where + * VpnService.protect(Socket) returns false for a fresh socket. + * + * The traffic of other applications is still routed through the VPN. + */ + try { + builder.addDisallowedApplication(mHostService.getContext().getPackageName()); + } catch (PackageManager.NameNotFoundException e) { + throw new Exception("Unable to exclude VPN control process from TUN", e); + } + + if (!disableIpv6Tunnel) { + builder.allowFamily(AF_INET6); + + // Configure an IPv6 address on the VPN interface so IPv6 flows can be + // routed through tun2socks. Without an IPv6 address on the interface, + // Android will not reliably deliver IPv6 packets to the TUN device. + builder.addAddress(VPN_INTERFACE_IPV6_ADDRESS, VPN_INTERFACE_IPV6_PREFIX_LENGTH); + if (excludedServerIpv6 != null) { + SkStatus.logInfo("Excluding server IPv6 from VPN routes: " + excludedServerIpv6); + addIpv6RoutesExcludingSingleAddress(builder, excludedServerIpv6); + } else { + builder.addRoute("::", 0); + } + } else { + SkStatus.logInfo("IPv6 tunnel disabled by config"); + } + mRoutes.addIP(new CIDRIP("0.0.0.0", 0), true); + mRoutes.addIP(new CIDRIP("10.0.0.0", 8), false); + mRoutes.addIP(new CIDRIP(mPrivateAddress.mSubnet, mPrivateAddress.mPrefixLength), false); + + // ainda pra testar, subnet routing pesquisar "allow lan" + if (enableTethering) { + // USB tethering 192.168.42.x + // Wi-Fi tethering 192.168.43.x + mRoutes.addIP(new CIDRIP("192.168.42.0", 23), false); + // Bluetooth tethering 192.168.44.x + mRoutes.addIP(new CIDRIP("192.168.44.0", 24), false); + // Wi-Fi direct 192.168.49.x + mRoutes.addIP(new CIDRIP("192.168.49.0", 24), false); + } + + // Add Dns + for (String dns : dnsResolver) { + try { + builder.addDnsServer(dns); + if (CIDRIP.InetAddressUtils.isIPv4Address(dns)) { + SkStatus.logInfo("DNS IPV4 ON"); + mRoutes.addIP(new CIDRIP(dns, 32), forwardDns); + } + if (CIDRIP.InetAddressUtils.isIPv6Address(Arrays.toString(excludeIps))) { + SkStatus.logInfo("DNS IPV6 ON"); + builder.addDnsServer("2606:4700:4700::1111"); + builder.addDnsServer("2606:4700:4700::1001"); + } + } catch (IllegalArgumentException iae) { + mHostService.onDiagnosticMessage(String.format("Failed to add DNS server %s: %s", dns, iae.getLocalizedMessage())); + } + } + + // set MTU + String release = Build.VERSION.RELEASE; + if ((Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3") + && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6")) + && mMtu < 1280) { + SkStatus.logInfo(String.format(Locale.US, "Forcing MTU to 1280 instead of %d to workaround Android Bug #70916", mMtu)); + mMtu = 1280; + } + builder.setMtu(mMtu); + + // loop routes + NetworkSpace.IpAddress multicastRange = new NetworkSpace.IpAddress(new CIDRIP("224.0.0.0", 3), true); + + for (NetworkSpace.IpAddress route : mRoutes.getPositiveIPList()) { + try { + if (multicastRange.containsNet(route)) + SkStatus.logDebug("VPN: Ignoring multicast route: " + route.toString()); + else + builder.addRoute(route.getIPv4Address(), route.networkMask); + } catch (IllegalArgumentException ia) { + //mHostService.onDiagnosticMessage("Route rejeitada: " + route + " " + ia.getLocalizedMessage()); + } + } + + + // The UID bypass is considered active only after establish() succeeds. + SocketProtector.setProcessBypassConfigured(false); + + // TunFd + tunFd = builder + .setSession(mHostService.getAppName()) + .establish(); + + if (tunFd == null) { + // As per http://developer.android.com/reference/android/net/VpnService.Builder.html#establish%28%29, + // this application is no longer prepared or was revoked. + return false; + } + + mTunFd.set(tunFd); + SocketProtector.setProcessBypassConfigured(true); + mRoutingThroughTunnel.set(false); + mHostService.onVpnEstablished(); + + mRoutes.clear(); + + } catch (IllegalArgumentException e) { + throw new Exception("IllegalArgumentException", e); + } catch (SecurityException e) { + throw new Exception("IllegalArgumentException", e); + } catch (IllegalStateException e) { + throw new Exception("IllegalStateException", e); + } finally { + // Restore the original locale. + Locale.setDefault(previousLocale); + } + + return true; + } + + private boolean routeThroughTunnel(final String socksServerAddress, final String[] dnsResolver, boolean forwardDns, final String udpResolver, final boolean transparentDns) { + if (!mRoutingThroughTunnel.compareAndSet(false, true)) { + return false; + } + + final ParcelFileDescriptor tunFd = mTunFd.get(); + if (tunFd == null) { + return false; + } + + // Pdnsd + String dnsgwRelay = null; + if (forwardDns) { + + int pdnsdPort = VpnUtils.findAvailablePort(8091, 10); + + String[] mServidorDNS = dnsResolver; + dnsgwRelay = String.format("%s:%d", mPrivateAddress.mIpAddress, pdnsdPort); + + mPdnsd = new Pdnsd(mHostService.getContext(), mServidorDNS, DNS_RESOLVER_PORT, + mPrivateAddress.mIpAddress, pdnsdPort); + mPdnsd.setOnPdnsdListener(new Pdnsd.OnPdnsdListener() { + @Override + public void onStart() { + } + + @Override + public void onStop() { + stop(); + mHostService.onTunnelStopped(); + } + }); + + mPdnsd.start(); + } + + // Tun2socks + boolean disableIpv6Tunnel = new Settings(mHostService.getContext()).getDisableIpv6Tunnel(); + mTun2Socks = new Tun2Socks(mHostService.getContext(), tunFd, mMtu, + mPrivateAddress.mRouter, disableIpv6Tunnel ? null : VPN_ROUTER_IPV6_ADDRESS, VPN_INTERFACE_NETMASK, + socksServerAddress, udpResolver, dnsgwRelay, transparentDns); + + mTun2Socks.setOnTun2SocksListener(new Tun2Socks.OnTun2SocksListener() { + @Override + public void onStart() { + } + + @Override + public void onStop() { + stop(); + mHostService.onTunnelStopped(); + } + }); + + mTun2Socks.start(); + + mHostService.onTunnelConnected(); + + // TODO: should double-check tunnel routing; see: + // https://bitbucket.org/psiphon/psiphon-circumvention-system/src/1dc5e4257dca99790109f3bf374e8ab3a0ead4d7/Android/PsiphonAndroidLibrary/src/com/psiphon3/psiphonlibrary/TunnelCore.java?at=default#cl-779 + return true; + } + + /** + * Adds IPv6 routes that cover the entire IPv6 space except for a single excluded /128. + * + *

This is a defensive fallback for cases where {@link VpnService#protect(java.net.Socket)} + * is not effective on a given device/ROM. For a single excluded address, the complement can be + * represented by at most 128 prefixes. + */ + private static void addIpv6RoutesExcludingSingleAddress(VpnService.Builder builder, String excludedIpv6) { + if (builder == null || excludedIpv6 == null) { + return; + } + try { + java.net.InetAddress ia = java.net.InetAddress.getByName(excludedIpv6); + if (!(ia instanceof java.net.Inet6Address)) { + builder.addRoute("::", 0); + return; + } + + final byte[] excluded = ia.getAddress(); // 16 bytes + + // For each bit position i, add a prefix (i+1) that matches the excluded address for the + // first i bits and has the i-th bit flipped. The union of these prefixes equals the + // full IPv6 space minus the excluded /128. + for (int i = 0; i < 128; i++) { + byte[] prefix = excluded.clone(); + + // Flip the i-th bit (MSB-first). + int byteIndex = i / 8; + int bitIndex = 7 - (i % 8); + prefix[byteIndex] = (byte) (prefix[byteIndex] ^ (1 << bitIndex)); + + // Zero all bits after i to create a proper network prefix address. + int nextBit = i + 1; + int zeroFromByte = nextBit / 8; + int zeroFromBit = 7 - (nextBit % 8); + + // If nextBit is byte-aligned, zero whole bytes from there. + if (nextBit % 8 == 0) { + for (int b = zeroFromByte; b < 16; b++) { + prefix[b] = 0; + } + } else { + // Mask the current byte to keep only the prefix bits, then zero subsequent bytes. + int mask = 0xFF << (zeroFromBit + 1); + prefix[zeroFromByte] = (byte) (prefix[zeroFromByte] & mask); + for (int b = zeroFromByte + 1; b < 16; b++) { + prefix[b] = 0; + } + } + + String routeAddr = java.net.InetAddress.getByAddress(prefix).getHostAddress(); + builder.addRoute(routeAddr, i + 1); + } + } catch (Throwable t) { + // If anything goes wrong, fall back to routing all IPv6 through the VPN. + try { + builder.addRoute("::", 0); + } catch (Throwable ignored) { + } + } + } + + private void stopRoutingThroughTunnel() { + if (mTun2Socks != null && mTun2Socks.isAlive()) { + mTun2Socks.interrupt(); + } + + mTun2Socks = null; + + if (mPdnsd != null && mPdnsd.isAlive()) { + mPdnsd.interrupt(); + } + + mPdnsd = null; + + } + + private void stopVpn() { + stopRoutingThroughTunnel(); + SocketProtector.setProcessBypassConfigured(false); + + // Clear only this VPN service instance. A different transport mode may + // already have installed a newer protector owner. + SocketProtector.clearVpnService((VpnService) mHostService.getVpnService()); + + ParcelFileDescriptor tunFd = mTunFd.getAndSet(null); + if (tunFd != null) { + try { + tunFd.close(); + } catch (IOException e) { + } + } + } + +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelState.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelState.java new file mode 100644 index 0000000..7b094ce --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelState.java @@ -0,0 +1,40 @@ +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +// Singleton class to maintain state related to VPN Tunnel service. +public class TunnelState { + + private static TunnelState m_tunnelState; + + public Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + public static synchronized TunnelState getTunnelState() { + if (m_tunnelState == null) { + m_tunnelState = new TunnelState(); + } + return m_tunnelState; + } + + private TunnelVpnManager m_tunnelManager = null; + private boolean m_startingTunnelManager = false; + + private TunnelState() {} + + public synchronized void setTunnelManager(TunnelVpnManager tunnelManager) { + m_tunnelManager = tunnelManager; + m_startingTunnelManager = false; + } + + public synchronized TunnelVpnManager getTunnelManager() { + return m_tunnelManager; + } + + public synchronized void setStartingTunnelManager() { + m_startingTunnelManager = true; + } + + public synchronized boolean getStartingTunnelManager() { + return m_startingTunnelManager; + } +}; diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnManager.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnManager.java new file mode 100644 index 0000000..e01cd55 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnManager.java @@ -0,0 +1,294 @@ +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +/* + * Copyright (c) 2016, Psiphon Inc. + * All rights reserved. + * + * 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 . + * + */ + +import android.annotation.TargetApi; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.net.VpnService; +import android.os.Build; +import android.util.Log; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import com.dragonssh.xhttpdemo.core.R; +import com.dragonssh.xhttpdemo.core.config.Settings; + +public class TunnelVpnManager implements Tunnel.HostService +{ + + private static final String TAG = "TunnelManager"; + public static final String VPN_SETTINGS = "vpnSettings"; + + private TunnelVpnService m_parentService = null; + private CountDownLatch m_tunnelThreadStopSignal; + private Thread m_tunnelThread; + private AtomicBoolean m_isStopping; + private Tunnel m_tunnel = null; + private AtomicBoolean m_isReconnecting; + private TunnelVpnSettings mSettings; + + public interface ManagerListener { + public void onLog(String msg); + } + + public TunnelVpnManager(TunnelVpnService parentService) + { + m_parentService = parentService; + m_isStopping = new AtomicBoolean(false); + m_isReconnecting = new AtomicBoolean(false); + m_tunnel = Tunnel.newTunnel(this); + } + + // Implementation of android.app.Service.onStartCommand + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "onStartCommand"); + + if (intent == null) { + Log.e(TAG, "Failed to receive intent"); + m_parentService.broadcastVpnStart(false); + return Service.START_NOT_STICKY; + } + + mSettings = intent.getParcelableExtra(VPN_SETTINGS); + if (mSettings == null) { + Log.e(TAG, "Failed to receive the Vpn Settings."); + m_parentService.broadcastVpnStart(false); + return Service.START_NOT_STICKY; + } + + if (mSettings.mSocksServer == null) + { + Log.e(TAG, "Failed to receive the socks server address."); + m_parentService.broadcastVpnStart(false); + return Service.START_NOT_STICKY; + } + + if (mSettings.mDnsResolver == null) + { + Log.e(TAG, "Failed to receive the dns resolvers."); + m_parentService.broadcastVpnStart(false); + return Service.START_NOT_STICKY; + } + + try + { + if (!m_tunnel.startRouting(mSettings)) + { + Log.e(TAG, "Failed to establish VPN"); + m_parentService.broadcastVpnStart(false); + } + } + catch (Exception e) + { + Log.e(TAG, String.format("Failed to establish VPN: %s", e.getMessage())); + m_parentService.broadcastVpnStart(false); + } + + return Service.START_REDELIVER_INTENT; + } + + // Implementation of android.app.Service.onDestroy + public void onDestroy() + { + if (m_tunnelThread == null) + { + return; + } + // signalStopService should have been called, but in case is was not, call here. + // If signalStopService was not already called, the join may block the calling + // thread for some time. + signalStopService(); + + try + { + m_tunnelThread.join(); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + } + m_tunnelThreadStopSignal = null; + m_tunnelThread = null; + } + + // Signals the runTunnel thread to stop. The thread will self-stop the service. + // This is the preferred method for stopping the tunnel service: + // 1. VpnService doesn't respond to stopService calls + // 2. The UI will not block while waiting for stopService to return + public void signalStopService() + { + if (m_tunnelThreadStopSignal != null) + { + m_tunnelThreadStopSignal.countDown(); + } + } + + // Stops the tunnel thread and restarts it with |socksServerAddress|. + public void restartTunnel(final String socksServerAddress) + { + Log.i(TAG, "Restarting tunnel."); + if (socksServerAddress == null || + socksServerAddress.equals(mSettings.mSocksServer)) + { + // Don't reconnect if the socks server address hasn't changed. + m_parentService.broadcastVpnStart(true); + return; + } + mSettings.mSocksServer = socksServerAddress; + m_isReconnecting.set(true); + + // Signaling stop to the tunnel thread with the reconnect flag set causes + // the thread to stop the tunnel (but not the VPN or the service) and send + // the new SOCKS server address to the DNS resolver before exiting itself. + // When the DNS broadcasts its local address, the tunnel will restart. + signalStopService(); + } + + private void startTunnel() + { + m_tunnelThreadStopSignal = new CountDownLatch(1); + m_tunnelThread = + new Thread( + new Runnable() { + @Override + public void run() + { + runTunnel(mSettings.mSocksServer, mSettings.mDnsResolver, mSettings.mDnsForward, mSettings.mUdpResolver, mSettings.mUdpDnsRelay); + } + }); + m_tunnelThread.start(); + } + + private void runTunnel(String socksServerAddress, String[] dnsResolver, boolean forwardDns, String udpResolver, boolean udpDnsRelay) + { + m_isStopping.set(false); + + try + { + if (!m_tunnel.startTunneling(socksServerAddress, dnsResolver, forwardDns, udpResolver, udpDnsRelay)) + { + throw new Exception("application is not prepared or revoked"); + } + Log.i(TAG, "VPN service running"); + m_parentService.broadcastVpnStart(true); + + try + { + m_tunnelThreadStopSignal.await(); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + } + + m_isStopping.set(true); + + } + catch (Exception e) + { + Log.e(TAG, String.format("Start tunnel failed: %s", e.getMessage())); + m_parentService.broadcastVpnStart(false); + } + finally + { + if (m_isReconnecting.get()) + { + // Stop tunneling only, not VPN, if reconnecting. + Log.i(TAG, "Stopping tunnel."); + m_tunnel.stopTunneling(); + } + else + { + // Stop VPN tunnel and service only if not reconnecting. + Log.i(TAG, "Stopping VPN and tunnel."); + m_tunnel.stop(); + m_parentService.stopForeground(true); + m_parentService.stopSelf(); + } + m_isReconnecting.set(false); + } + } + + //---------------------------------------------------------------------------- + // Tunnel.HostService + //---------------------------------------------------------------------------- + + @Override + public String getAppName() + { + return getContext().getString(R.string.app_name); + } + + @Override + public Context getContext() + { + return m_parentService; + } + + @Override + public VpnService getVpnService() + { + return m_parentService; + } + + @Override + public VpnService.Builder newVpnServiceBuilder() + { + return m_parentService.newBuilder(); + } + + @Override + public void onDiagnosticMessage(String message) + { + SkStatus.logInfo(message); + } + + @Override + public void onTunnelConnected() + { + } + + @Override + @TargetApi(Build.VERSION_CODES.M) + public void onVpnEstablished() + { + //SkStatus.logInfo("VPN Estabelecida"); + + startTunnel(); + } + + @Override + public void onTunnelStopped() + { + if (!m_isStopping.get() && !m_isReconnecting.get()) { + Log.w(TAG, "VPN tunnel process stopped unexpectedly"); + SkStatus.logInfo("VPN tunnel stopped. Reconnecting..."); + m_parentService.broadcastVpnDisconnect(); + } + + // Release runTunnel() so the VpnService can clean up instead of hanging + // forever while the main SSH service starts a fresh connection. + signalStopService(); + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnService.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnService.java new file mode 100644 index 0000000..097e92a --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnService.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016, Psiphon Inc. + * All rights reserved. + * + * 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 . + * + */ + +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import android.annotation.TargetApi; +import android.content.Intent; +import android.net.VpnService; +import android.os.Binder; +import android.os.Build; +import android.os.IBinder; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dragonssh.xhttpdemo.core.logger.SkStatus; + +@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) +public class TunnelVpnService extends VpnService { + + private static final String LOG_TAG = "TunnelVpnService"; + public static final String TUNNEL_VPN_DISCONNECT_BROADCAST = + "tunnelVpnDisconnectBroadcast"; + public static final String TUNNEL_VPN_START_BROADCAST = + "tunnelVpnStartBroadcast"; + public static final String TUNNEL_VPN_START_SUCCESS_EXTRA = + "tunnelVpnStartSuccessExtra"; + + private TunnelVpnManager m_tunnelManager = new TunnelVpnManager(this); + + public class LocalBinder extends Binder { + public TunnelVpnService getService() { + return TunnelVpnService.this; + } + } + + private final IBinder m_binder = new LocalBinder(); + + @Override + public IBinder onBind(Intent intent) { + String action = intent.getAction(); + if (action != null && action.equals(SERVICE_INTERFACE)) { + return super.onBind(intent); + } + return m_binder; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + //Log.d(LOG_TAG, "on start"); + return m_tunnelManager.onStartCommand(intent, flags, startId); + } + + @Override + public void onCreate() { + //Log.d(LOG_TAG, "on create"); + SocketProtector.setVpnService(this); + TunnelState.getTunnelState().setTunnelManager(m_tunnelManager); + } + + @Override + public void onDestroy() { + SkStatus.logInfo("VPN service destroyed"); + + // Keep the SSH service and the Android VPN state synchronized. If Android + // kills or destroys only the VpnService, the main tunnel manager must be + // notified so it can reconnect instead of leaving the app marked connected + // while the phone VPN interface is gone. + broadcastVpnDisconnect(); + + TunnelState.getTunnelState().setTunnelManager(null); + SocketProtector.clearVpnService(this); + m_tunnelManager.onDestroy(); + + super.onDestroy(); + } + + @Override + public void onRevoke() { + SkStatus.logInfo("VPN service revoked"); + broadcastVpnDisconnect(); + // stopSelf will trigger onDestroy in the main thread. + stopSelf(); + } + + public VpnService.Builder newBuilder() { + return new VpnService.Builder(); + } + + // Broadcast non-user-initiated VPN disconnect. + public void broadcastVpnDisconnect() { + dispatchBroadcast(new Intent(TUNNEL_VPN_DISCONNECT_BROADCAST)); + } + + // Broadcast VPN start. |success| is true if the VPN and tunnel were started + // successfully, and false otherwise. + public void broadcastVpnStart(boolean success) { + Intent vpnStart = new Intent(TUNNEL_VPN_START_BROADCAST); + vpnStart.putExtra(TUNNEL_VPN_START_SUCCESS_EXTRA, success); + dispatchBroadcast(vpnStart); + } + + private void dispatchBroadcast(final Intent broadcast) { + LocalBroadcastManager.getInstance(TunnelVpnService.this) + .sendBroadcast(broadcast); + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnSettings.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnSettings.java new file mode 100644 index 0000000..a9fcc68 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/TunnelVpnSettings.java @@ -0,0 +1,87 @@ +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import android.os.Parcelable; +import android.os.Parcel; + +public class TunnelVpnSettings + implements Parcelable { + + public String mSocksServer; + public boolean mDnsForward; + public String[] mDnsResolver; + public String mUdpResolver; + public String[] mExcludeIps; + public boolean mUdpDnsRelay; + + public boolean mEnableFilterApps; + public boolean mFilterBypassMode; + public String[] mFilterApps; + + public boolean mTetheringSubnet; + public boolean mDisableIpv6Tunnel; + + public TunnelVpnSettings(String socksServer, boolean dnsForward, String[] dnsResolver, + boolean udpDnsRelay, String udpResolver, String[] excludeIps, + boolean enableFilterApps, boolean filterBypassMode, String[] filterApps, + boolean enableTethering, boolean disableIpv6Tunnel) + { + mSocksServer = socksServer; + mDnsForward = dnsForward; + mUdpDnsRelay = udpDnsRelay; + mDnsResolver = dnsResolver; + mUdpResolver = udpResolver; + mExcludeIps = excludeIps; + + mEnableFilterApps = enableFilterApps; + mFilterBypassMode = filterBypassMode; + mFilterApps = filterApps; + + mTetheringSubnet = enableTethering; + mDisableIpv6Tunnel = disableIpv6Tunnel; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mSocksServer); + dest.writeInt(mDnsForward ? 1 : 0); + dest.writeInt(mUdpDnsRelay ? 1 : 0); + dest.writeStringArray(mDnsResolver); + dest.writeString(mUdpResolver); + dest.writeStringArray(mExcludeIps); + dest.writeInt(mFilterBypassMode ? 1 : 0); + dest.writeStringArray(mFilterApps); + dest.writeInt(mEnableFilterApps ? 1 : 0); + dest.writeInt(mTetheringSubnet ? 1 : 0); + dest.writeInt(mDisableIpv6Tunnel ? 1 : 0); + } + + public TunnelVpnSettings(Parcel in) { + mSocksServer = in.readString(); + mDnsForward = in.readInt() == 1; + mUdpDnsRelay = in.readInt() == 1; + mDnsResolver = in.createStringArray(); + mUdpResolver = in.readString(); + mExcludeIps = in.createStringArray(); + mFilterBypassMode = in.readInt() == 1; + mFilterApps = in.createStringArray(); + mEnableFilterApps = in.readInt() == 1; + mTetheringSubnet = in.readInt() == 1; + mDisableIpv6Tunnel = in.readInt() == 1; + } + + public static final Creator CREATOR + = new Creator() { + public TunnelVpnSettings createFromParcel(Parcel in) { + return new TunnelVpnSettings(in); + } + + public TunnelVpnSettings[] newArray(int size) { + return new TunnelVpnSettings[size]; + } + }; +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/VpnUtils.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/VpnUtils.java new file mode 100644 index 0000000..1d9c2a7 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/tunnel/vpn/VpnUtils.java @@ -0,0 +1,373 @@ +package com.dragonssh.xhttpdemo.core.tunnel.vpn; + +import android.content.Context; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +import static java.lang.Runtime.getRuntime; +import java.util.Map; +import java.util.List; +import java.net.NetworkInterface; +import java.util.Collections; +import java.net.SocketException; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.util.HashMap; +import android.annotation.TargetApi; +import android.os.Build; +import java.util.Collection; +import java.util.ArrayList; +import android.net.ConnectivityManager; +import java.lang.reflect.Method; +import android.net.LinkProperties; +import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; +import java.util.regex.Pattern; +import com.dragonssh.xhttpdemo.core.logger.SkStatus; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; + +public class VpnUtils { + + public static int findProcessId(String command) throws IOException { + + String[] cmds = {"ps -ef","ps -A","toolbox ps"}; + + for (int i = 0; i < cmds.length;i++) { + Process procPs = getRuntime().exec(cmds[i]); + + BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream())); + + String line; + while ((line = reader.readLine()) != null) { + if (!line.contains("PID") && line.contains(command)) { + String[] lineParts = line.split("\\s+"); + try { + return Integer.parseInt(lineParts[1]); //for most devices it is the second + } catch (NumberFormatException e) { + return Integer.parseInt(lineParts[0]); //but for samsungs it is the first + } finally { + try { + procPs.destroy(); + } catch (Exception e) { + } + } + } + } + } + + return -1; + } + + public static void killProcess(File fileProcBin) throws Exception { + killProcess(fileProcBin, "-9"); // this is -KILL + } + + public static int killProcess(File fileProcBin, String signal) throws Exception { + + int procId = -1; + int killAttempts = 0; + + while ((procId = findProcessId(fileProcBin.getName())) != -1) { + killAttempts++; + String pidString = String.valueOf(procId); + + String[] cmds = {"","busybox ","toolbox "}; + + for (int i = 0; i < cmds.length;i++) { + try { + getRuntime().exec(cmds[i] + "killall " + signal + " " + fileProcBin.getName + ()); + } catch (IOException ioe) { + } + try { + getRuntime().exec(cmds[i] + "killall " + signal + " " + fileProcBin.getCanonicalPath()); + } catch (IOException ioe) { + } + } + + killProcess(pidString, signal); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // ignored + } + + if (killAttempts > 4) + throw new Exception("Cannot kill: " + fileProcBin.getAbsolutePath()); + } + + return procId; + } + + public static void killProcess(String pidString, String signal) throws Exception { + + String[] cmds = {"","toolbox ","busybox "}; + + for (int i = 0; i < cmds.length;i++) { + try { + getRuntime().exec(cmds[i] + "kill " + signal + " " + pidString); + } catch (IOException ioe) { + SkStatus.logDebug(String.format("error killing process: %s, %s", pidString, ioe.getMessage())); + } + } + } + + public static List getActiveNetworkDnsResolver(Context context) throws Exception { + Collection dnsResolvers = getActiveNetworkDnsResolvers(context); + + if (!dnsResolvers.isEmpty()) { + ArrayList lista = new ArrayList(); + int max = 2; + + Iterator it = dnsResolvers.iterator(); + while (it.hasNext()) { + String dnsResolver = it.next().toString(); + + // strip the leading slash e.g., "/192.168.1.1" + if (dnsResolver.startsWith("/")) { + dnsResolver = dnsResolver.substring(1); + } + + // Accept both IPv4 and IPv6 DNS resolvers. Previously IPv6 + // addresses were filtered out to avoid issues on devices + // lacking dual-stack support. With native IPv6 forwarding, + // clients may be connected to an IPv6-only network, so we + // no longer discard resolvers containing a colon (':'). + + lista.add(dnsResolver); + + max -= 1; + if (max <= 0) break; + } + + return lista; + } + else throw new Exception("no active network DNS resolver"); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private static Collection getActiveNetworkDnsResolvers(Context context) + throws Exception { + final String errorMessage = "getActiveNetworkDnsResolvers failed"; + ArrayList dnsAddresses = new ArrayList(); + try { + // Hidden API + // - only available in Android 4.0+ + // - no guarantee will be available beyond 4.2, or on all vendor devices + ConnectivityManager connectivityManager = + (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + Class LinkPropertiesClass = Class.forName("android.net.LinkProperties"); + Method getActiveLinkPropertiesMethod = ConnectivityManager.class.getMethod("getActiveLinkProperties", new Class []{}); + Object linkProperties = getActiveLinkPropertiesMethod.invoke(connectivityManager); + if (linkProperties != null) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + Method getDnsesMethod = LinkPropertiesClass.getMethod("getDnses", new Class []{}); + Collection dnses = (Collection)getDnsesMethod.invoke(linkProperties); + for (Object dns : dnses) { + dnsAddresses.add((InetAddress)dns); + } + } else { + // LinkProperties is public in API 21 (and the DNS function signature has changed) + for (InetAddress dns : ((LinkProperties)linkProperties).getDnsServers()) { + dnsAddresses.add(dns); + } + } + } + } catch (ClassNotFoundException e) { + throw new Exception(errorMessage, e); + } catch (NoSuchMethodException e) { + throw new Exception(errorMessage, e); + } catch (IllegalArgumentException e) { + throw new Exception(errorMessage, e); + } catch (IllegalAccessException e) { + throw new Exception(errorMessage, e); + } catch (InvocationTargetException e) { + throw new Exception(errorMessage, e); + } catch (NullPointerException e) { + throw new Exception(errorMessage, e); + } + + return dnsAddresses; + } + + private final static String DEFAULT_PRIMARY_DNS_SERVER = "8.8.4.4"; + private final static String DEFAULT_SECONDARY_DNS_SERVER = "8.8.8.8"; + + public static List getNetworkDnsServer(Context context) { + List dnsResolver = new ArrayList(); + try { + dnsResolver = VpnUtils.getActiveNetworkDnsResolver(context); + } catch (Exception e) { + dnsResolver.add(DEFAULT_PRIMARY_DNS_SERVER); + dnsResolver.add(DEFAULT_SECONDARY_DNS_SERVER); + } + return dnsResolver; + } + + + /** + * Utils ip + */ + + private static final Pattern IPV4_PATTERN = + Pattern.compile( + "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"); + + private static final Pattern IPV6_STD_PATTERN = + Pattern.compile( + "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); + + private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = + Pattern.compile( + "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"); + + public static boolean isIPv4Address(final String input) { + return IPV4_PATTERN.matcher(input).matches(); + } + + public static boolean isIPv6StdAddress(final String input) { + return IPV6_STD_PATTERN.matcher(input).matches(); + } + + public static boolean isIPv6HexCompressedAddress(final String input) { + return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches(); + } + + public static boolean isIPv6Address(final String input) { + return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input); + } + + + //---------------------------------------------------------------------------- + // Implementation: Network Utils + //---------------------------------------------------------------------------- + + public static class PrivateAddress + { + public final String mIpAddress; + public final String mSubnet; + public final int mPrefixLength; + public final String mRouter; + + public PrivateAddress(String ipAddress, String subnet, int prefixLength, String router) + { + mIpAddress = ipAddress; + mSubnet = subnet; + mPrefixLength = prefixLength; + mRouter = router; + } + } + + public static PrivateAddress selectPrivateAddress() throws Exception + { + // Select one of 10.0.0.1, 172.16.0.1, or 192.168.0.1 depending on + // which private address range isn't in use. + Map candidates = new HashMap(); + candidates.put("10", new PrivateAddress("10.0.0.1", "10.0.0.0", 8, "10.0.0.2")); + candidates.put("172", new PrivateAddress("172.16.0.1", "172.16.0.0", 12, "172.16.0.2")); + candidates.put("192", new PrivateAddress("192.168.0.1", "192.168.0.0", 16, "192.168.0.2")); + candidates.put("169", new PrivateAddress("169.254.1.1", "169.254.1.0", 24, "169.254.1.2")); + + List netInterfaces; + try + { + netInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); + } + catch (SocketException e) + { + e.printStackTrace(); + throw new Exception("selectPrivateAddress failed", e); + } + + for (NetworkInterface netInterface : netInterfaces) + { + for (InetAddress inetAddress : Collections.list(netInterface.getInetAddresses())) + { + + if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) + { + String ipAddress = inetAddress.getHostAddress(); + if (ipAddress.startsWith("10.")) + { + candidates.remove("10"); + } + else if (ipAddress.length() >= 6 + && ipAddress.substring(0, 6).compareTo("172.16") >= 0 + && ipAddress.substring(0, 6).compareTo("172.31") <= 0) + { + candidates.remove("172"); + } + else if (ipAddress.startsWith("192.168")) + { + candidates.remove("192"); + } + } + } + } + + if (candidates.size() > 0) + { + return candidates.values().iterator().next(); + } + + throw new Exception("no private address available"); + } + + private static boolean isPortAvailable(int port) + { + Socket socket = new Socket(); + SocketAddress sockaddr = new InetSocketAddress("127.0.0.1", port); + + try + { + socket.connect(sockaddr, 1000); + // The connect succeeded, so there is already something running on that port + return false; + } + catch (SocketTimeoutException e) + { + // The socket is in use, but the server didn't respond quickly enough + return false; + } + catch (IOException e) + { + // The connect failed, so the port is available + return true; + } + finally + { + if (socket != null) + { + try + { + socket.close(); + } + catch (IOException e) + { + /* should not be thrown */ + } + } + } + } + + public static int findAvailablePort(int start_port, int max_increment) + { + for(int port = start_port; port < (start_port + max_increment); port++) + { + if (isPortAvailable(port)) + { + return port; + } + } + + return 0; + } + +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/util/CustomNativeLoader.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/util/CustomNativeLoader.java new file mode 100644 index 0000000..a3b8b3f --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/util/CustomNativeLoader.java @@ -0,0 +1,163 @@ +package com.dragonssh.xhttpdemo.core.util; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.os.Build; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.List; +import java.util.ArrayList; +import android.text.TextUtils; + +public class CustomNativeLoader { + + private final static String TAG = "CNL"; + + private static boolean loadFromZip(Context context, String libname, File destLocalFile, String arch) { + + + ZipFile zipFile = null; + InputStream stream = null; + + try { + zipFile = new ZipFile(context.getApplicationInfo().sourceDir); + ZipEntry entry = zipFile.getEntry("lib/" + arch + "/" + libname + ".so"); + if (entry == null) { + entry = zipFile.getEntry("jni/" + arch + "/" + libname + ".so"); + if (entry == null) + throw new Exception("Unable to find file in apk:" + "lib/" + arch + "/" + libname); + } + + //how we wrap this in another stream because the native .so is zipped itself + stream = zipFile.getInputStream(entry); + + OutputStream out = new FileOutputStream(destLocalFile); + byte[] buf = new byte[4096]; + int len; + while ((len = stream.read(buf)) > 0) { + Thread.yield(); + out.write(buf, 0, len); + } + out.close(); + + destLocalFile.setReadable(true, false); + destLocalFile.setExecutable(true, false); + destLocalFile.setWritable(true); + + return true; + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } + } + if (zipFile != null) { + try { + zipFile.close(); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } + } + } + return false; + } + + public static File loadNativeBinary(Context context, String libname, File destLocalFile) { + + try { + + + File fileNativeBin = new File(getNativeLibraryDir(context), libname + ".so"); + + if (fileNativeBin.exists()) + { + if (fileNativeBin.canExecute()) + return fileNativeBin; + else + { + setExecutable(fileNativeBin); + + if (fileNativeBin.canExecute()) + return fileNativeBin; + } + } + else if (destLocalFile.exists()) { + if (destLocalFile.canExecute()) + return destLocalFile; + else + { + setExecutable(destLocalFile); + + if (destLocalFile.canExecute()) + return destLocalFile; + } + } + + List abisList = new ArrayList<>(); + + if (Build.VERSION.SDK_INT >= 21) { + String[] abis = Build.SUPPORTED_ABIS; + if (abis != null) { + for (String abi : abis) { + if (!TextUtils.isEmpty(abi)) { + abisList.add(abi); + } + } + } + } + else { + String[] abis = new String[]{ + Build.CPU_ABI, + Build.CPU_ABI2 + }; + for (String abi : abis) { + if (!TextUtils.isEmpty(abi)) { + abisList.add(abi); + } + } + } + + for (String folder : abisList) { + String javaArch = System.getProperty("os.arch"); + if (javaArch != null && javaArch.contains("686")) { + folder = "x86"; + } + + if (loadFromZip(context, libname, destLocalFile, folder)) { + return destLocalFile; + } + } + + } catch (Throwable e) { + Log.e(TAG, e.getMessage(), e); + } + + + return null; + } + + private static void setExecutable(File fileBin) { + fileBin.setReadable(true); + fileBin.setExecutable(true); + fileBin.setWritable(false); + fileBin.setWritable(true, true); + } + + // Return Full path to the directory where native JNI libraries are stored. + private static String getNativeLibraryDir(Context context) { + ApplicationInfo appInfo = context.getApplicationInfo(); + return appInfo.nativeLibraryDir; + } + +} + diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/util/FileUtils.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/util/FileUtils.java new file mode 100644 index 0000000..cd0ea10 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/util/FileUtils.java @@ -0,0 +1,76 @@ +package com.dragonssh.xhttpdemo.core.util; + +import android.content.Context; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Scanner; + +public class FileUtils { + private static final String TAG = "FileUtils"; + + public static void copiarArquivo(InputStream in, OutputStream out) throws IOException { + byte[] buf = new byte[8192]; + int len; + try { + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.flush(); + } finally { + try { + in.close(); + } finally { + out.close(); + } + } + } + + public static String readFromRaw(Context context, int resId) { + InputStream in = context.getResources().openRawResource(resId); + Scanner scanner = new Scanner(in, "UTF-8").useDelimiter("\\A"); + StringBuilder sb = new StringBuilder(); + while (scanner.hasNext()) { + sb.append(scanner.next()); + } + scanner.close(); + return sb.toString(); + } + + public static String readTextFile(File f) { + StringBuilder text = new StringBuilder(); + + try { + BufferedReader br = new BufferedReader(new FileReader(f)); + String st; + while ((st = br.readLine()) != null) { + text.append(st).append('\n'); + } + br.close(); + return text.toString(); + } catch (Exception e) { + return null; + } + } + + public static boolean saveTextFile(File file, String contents) { + try { + if (!file.exists()) { + file.createNewFile(); + } + + FileWriter writer = new FileWriter(file, false); + writer.write(contents); + writer.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/service/src/main/java/com/dragonssh/xhttpdemo/core/util/StreamGobbler.java b/service/src/main/java/com/dragonssh/xhttpdemo/core/util/StreamGobbler.java new file mode 100644 index 0000000..7a458e7 --- /dev/null +++ b/service/src/main/java/com/dragonssh/xhttpdemo/core/util/StreamGobbler.java @@ -0,0 +1,54 @@ +package com.dragonssh.xhttpdemo.core.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +public class StreamGobbler extends Thread { + private final BufferedReader reader; + private List writer; + private StreamGobbler.OnLineListener listener; + + public StreamGobbler(InputStream inputStream, List outputList) { + this.reader = new BufferedReader(new InputStreamReader(inputStream)); + this.writer = outputList; + } + + public StreamGobbler(InputStream inputStream, StreamGobbler.OnLineListener onLineListener) { + this.reader = new BufferedReader(new InputStreamReader(inputStream)); + this.listener = onLineListener; + } + + public void run() { + while(true) { + try { + String line; + + if ((line = this.reader.readLine()) != null) { + if (this.writer != null) { + this.writer.add(line); + } + + if (this.listener != null) { + this.listener.onLine(line); + } + + continue; + } + + } catch (IOException var3) {} + + try { + this.reader.close(); + } catch (IOException var2) {} + + return; + } + } + + public interface OnLineListener { + void onLine(String var1); + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/Adler32.java b/service/src/main/java/com/jcraft/jzlib/Adler32.java new file mode 100644 index 0000000..a789a5a --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/Adler32.java @@ -0,0 +1,139 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final public class Adler32 implements Checksum { + + // largest prime smaller than 65536 + static final private int BASE=65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + static final private int NMAX=5552; + + private long s1=1L; + private long s2=0L; + + public void reset(long init){ + s1=init&0xffff; + s2=(init>>16)&0xffff; + } + + public void reset(){ + s1=1L; + s2=0L; + } + + public long getValue(){ + return ((s2<<16)|s1); + } + + public void update(byte[] buf, int index, int len){ + + if(len==1){ + s1+=buf[index++]&0xff; s2+=s1; + s1%=BASE; + s2%=BASE; + return; + } + + int len1 = len/NMAX; + int len2 = len%NMAX; + while(len1-->0) { + int k=NMAX; + len-=k; + while(k-->0){ + s1+=buf[index++]&0xff; s2+=s1; + } + s1%=BASE; + s2%=BASE; + } + + int k=len2; + len-=k; + while(k-->0){ + s1+=buf[index++]&0xff; s2+=s1; + } + s1%=BASE; + s2%=BASE; + } + + public Adler32 copy(){ + Adler32 foo = new Adler32(); + foo.s1 = this.s1; + foo.s2 = this.s2; + return foo; + } + + // The following logic has come from zlib.1.2. + static long combine(long adler1, long adler2, long len2){ + long BASEL = (long)BASE; + long sum1; + long sum2; + long rem; // unsigned int + + rem = len2 % BASEL; + sum1 = adler1 & 0xffffL; + sum2 = rem * sum1; + sum2 %= BASEL; // MOD(sum2); + sum1 += (adler2 & 0xffffL) + BASEL - 1; + sum2 += ((adler1 >> 16) & 0xffffL) + ((adler2 >> 16) & 0xffffL) + BASEL - rem; + if (sum1 >= BASEL) sum1 -= BASEL; + if (sum1 >= BASEL) sum1 -= BASEL; + if (sum2 >= (BASEL << 1)) sum2 -= (BASEL << 1); + if (sum2 >= BASEL) sum2 -= BASEL; + return sum1 | (sum2 << 16); + } + +/* + private java.util.zip.Adler32 adler=new java.util.zip.Adler32(); + public void update(byte[] buf, int index, int len){ + if(buf==null) {adler.reset();} + else{adler.update(buf, index, len);} + } + public void reset(){ + adler.reset(); + } + public void reset(long init){ + if(init==1L){ + adler.reset(); + } + else{ + System.err.println("unsupported operation"); + } + } + public long getValue(){ + return adler.getValue(); + } +*/ +} diff --git a/service/src/main/java/com/jcraft/jzlib/CRC32.java b/service/src/main/java/com/jcraft/jzlib/CRC32.java new file mode 100644 index 0000000..3045796 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/CRC32.java @@ -0,0 +1,179 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final public class CRC32 implements Checksum { + + /* + * The following logic has come from RFC1952. + */ + private int v = 0; + private static int[] crc_table = null; + static { + crc_table = new int[256]; + for (int n = 0; n < 256; n++) { + int c = n; + for (int k = 8; --k >= 0; ) { + if ((c & 1) != 0) + c = 0xedb88320 ^ (c >>> 1); + else + c = c >>> 1; + } + crc_table[n] = c; + } + } + + public void update (byte[] buf, int index, int len) { + int c = ~v; + while (--len >= 0) + c = crc_table[(c^buf[index++])&0xff]^(c >>> 8); + v = ~c; + } + + public void reset(){ + v = 0; + } + + public void reset(long vv){ + v = (int)(vv&0xffffffffL); + } + + public long getValue(){ + return (long)(v&0xffffffffL); + } + + // The following logic has come from zlib.1.2. + private static final int GF2_DIM = 32; + static long combine(long crc1, long crc2, long len2){ + long row; + long[] even = new long[GF2_DIM]; + long[] odd = new long[GF2_DIM]; + + // degenerate case (also disallow negative lengths) + if (len2 <= 0) + return crc1; + + // put operator for one zero bit in odd + odd[0] = 0xedb88320L; // CRC-32 polynomial + row = 1; + for (int n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + // put operator for two zero bits in even + gf2_matrix_square(even, odd); + + // put operator for four zero bits in odd + gf2_matrix_square(odd, even); + + // apply len2 zeros to crc1 (first square will put the operator for one + // zero byte, eight zero bits, in even) + do { + // apply zeros operator for this bit of len2 + gf2_matrix_square(even, odd); + if ((len2 & 1)!=0) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + // if no more bits set, then done + if (len2 == 0) + break; + + // another iteration of the loop with odd and even swapped + gf2_matrix_square(odd, even); + if ((len2 & 1)!=0) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + // if no more bits set, then done + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; + } + + private static long gf2_matrix_times(long[] mat, long vec){ + long sum = 0; + int index = 0; + while (vec!=0) { + if ((vec & 1)!=0) + sum ^= mat[index]; + vec >>= 1; + index++; + } + return sum; + } + + static final void gf2_matrix_square(long[] square, long[] mat) { + for (int n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); + } + + /* + private java.util.zip.CRC32 crc32 = new java.util.zip.CRC32(); + + public void update(byte[] buf, int index, int len){ + if(buf==null) {crc32.reset();} + else{crc32.update(buf, index, len);} + } + public void reset(){ + crc32.reset(); + } + public void reset(long init){ + if(init==0L){ + crc32.reset(); + } + else{ + System.err.println("unsupported operation"); + } + } + public long getValue(){ + return crc32.getValue(); + } +*/ + public CRC32 copy(){ + CRC32 foo = new CRC32(); + foo.v = this.v; + return foo; + } + + public static int[] getCRC32Table(){ + int[] tmp = new int[crc_table.length]; + System.arraycopy(crc_table, 0, tmp, 0, tmp.length); + return tmp; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/Checksum.java b/service/src/main/java/com/jcraft/jzlib/Checksum.java new file mode 100644 index 0000000..1139093 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/Checksum.java @@ -0,0 +1,43 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +interface Checksum { + void update(byte[] buf, int index, int len); + void reset(); + void reset(long init); + long getValue(); + Checksum copy(); +} diff --git a/service/src/main/java/com/jcraft/jzlib/Deflate.java b/service/src/main/java/com/jcraft/jzlib/Deflate.java new file mode 100644 index 0000000..cfda0f0 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/Deflate.java @@ -0,0 +1,1757 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +public +final class Deflate implements Cloneable { + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_DEFAULT_COMPRESSION=-1; + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_MEM_LEVEL=8; + + static class Config{ + int good_length; // reduce lazy search above this match length + int max_lazy; // do not perform lazy search above this match length + int nice_length; // quit search above this match length + int max_chain; + int func; + Config(int good_length, int max_lazy, + int nice_length, int max_chain, int func){ + this.good_length=good_length; + this.max_lazy=max_lazy; + this.nice_length=nice_length; + this.max_chain=max_chain; + this.func=func; + } + } + + static final private int STORED=0; + static final private int FAST=1; + static final private int SLOW=2; + static final private Config[] config_table; + static{ + config_table=new Config[10]; + // good lazy nice chain + config_table[0]=new Config(0, 0, 0, 0, STORED); + config_table[1]=new Config(4, 4, 8, 4, FAST); + config_table[2]=new Config(4, 5, 16, 8, FAST); + config_table[3]=new Config(4, 6, 32, 32, FAST); + + config_table[4]=new Config(4, 4, 16, 16, SLOW); + config_table[5]=new Config(8, 16, 32, 32, SLOW); + config_table[6]=new Config(8, 16, 128, 128, SLOW); + config_table[7]=new Config(8, 32, 128, 256, SLOW); + config_table[8]=new Config(32, 128, 258, 1024, SLOW); + config_table[9]=new Config(32, 258, 258, 4096, SLOW); + } + + static final private String[] z_errmsg = { + "need dictionary", // Z_NEED_DICT 2 + "stream end", // Z_STREAM_END 1 + "", // Z_OK 0 + "file error", // Z_ERRNO (-1) + "stream error", // Z_STREAM_ERROR (-2) + "data error", // Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + // block not completed, need more input or more output + static final private int NeedMore=0; + + // block flush performed + static final private int BlockDone=1; + + // finish started, need only more output at next deflate + static final private int FinishStarted=2; + + // finish done, accept no more input or output + static final private int FinishDone=3; + + // preset dictionary flag in zlib header + static final private int PRESET_DICT=0x20; + + static final private int Z_FILTERED=1; + static final private int Z_HUFFMAN_ONLY=2; + static final private int Z_DEFAULT_STRATEGY=0; + + static final private int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + static final private int INIT_STATE=42; + static final private int BUSY_STATE=113; + static final private int FINISH_STATE=666; + + // The deflate compression method + static final private int Z_DEFLATED=8; + + static final private int STORED_BLOCK=0; + static final private int STATIC_TREES=1; + static final private int DYN_TREES=2; + + // The three kinds of block type + static final private int Z_BINARY=0; + static final private int Z_ASCII=1; + static final private int Z_UNKNOWN=2; + + static final private int Buf_size=8*2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + static final private int REP_3_6=16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + static final private int REPZ_3_10=17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + static final private int REPZ_11_138=18; + + static final private int MIN_MATCH=3; + static final private int MAX_MATCH=258; + static final private int MIN_LOOKAHEAD=(MAX_MATCH+MIN_MATCH+1); + + static final private int MAX_BITS=15; + static final private int D_CODES=30; + static final private int BL_CODES=19; + static final private int LENGTH_CODES=29; + static final private int LITERALS=256; + static final private int L_CODES=(LITERALS+1+LENGTH_CODES); + static final private int HEAP_SIZE=(2*L_CODES+1); + + static final private int END_BLOCK=256; + + ZStream strm; // pointer back to this zlib stream + int status; // as the name implies + byte[] pending_buf; // output still pending + int pending_buf_size; // size of pending_buf + int pending_out; // next pending byte to output to the stream + int pending; // nb of bytes in the pending buffer + int wrap = 1; + byte data_type; // UNKNOWN, BINARY or ASCII + byte method; // STORED (for zip only) or DEFLATED + int last_flush; // value of flush param for previous deflate call + + int w_size; // LZ77 window size (32K by default) + int w_bits; // log2(w_size) (8..16) + int w_mask; // w_size - 1 + + byte[] window; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least wSize + // bytes. With this organization, matches are limited to a distance of + // wSize-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: use the user input buffer as sliding window. + + int window_size; + // Actual size of window: 2*wSize, except when the user input buffer + // is directly used as sliding window. + + short[] prev; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + + short[] head; // Heads of the hash chains or NIL. + + int ins_h; // hash index of string to be inserted + int hash_size; // number of elements in hash table + int hash_bits; // log2(hash_size) + int hash_mask; // hash_size-1 + + // Number of bits by which ins_h must be shifted at each input + // step. It must be such that after MIN_MATCH steps, the oldest + // byte no longer takes part in the hash key, that is: + // hash_shift * MIN_MATCH >= hash_bits + int hash_shift; + + // Window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + int block_start; + + int match_length; // length of best match + int prev_match; // previous match + int match_available; // set if previous match exists + int strstart; // start of string to insert + int match_start; // start of matching string + int lookahead; // number of valid bytes ahead in window + + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + int prev_length; + + // To speed up deflation, hash chains are never searched beyond this + // length. A higher limit improves compression ratio but degrades the speed. + int max_chain_length; + + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + int max_lazy_match; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + int level; // compression level (1..9) + int strategy; // favor or force Huffman coding + + // Use a faster search when the previous match is longer than this + int good_match; + + // Stop searching when current match exceeds this + int nice_match; + + short[] dyn_ltree; // literal and length tree + short[] dyn_dtree; // distance tree + short[] bl_tree; // Huffman tree for bit lengths + + Tree l_desc=new Tree(); // desc for literal tree + Tree d_desc=new Tree(); // desc for distance tree + Tree bl_desc=new Tree(); // desc for bit length tree + + // number of codes at each bit length for an optimal tree + short[] bl_count=new short[MAX_BITS+1]; + // working area to be used in Tree#gen_codes() + short[] next_code=new short[MAX_BITS+1]; + + // heap used to build the Huffman trees + int[] heap=new int[2*L_CODES+1]; + + int heap_len; // number of elements in the heap + int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + // Depth of each subtree used as tie breaker for trees of equal frequency + byte[] depth=new byte[2*L_CODES+1]; + + byte[] l_buf; // index for literals or lengths */ + + // Size of match buffer for literals/lengths. There are 4 reasons for + // limiting lit_bufsize to 64K: + // - frequencies can be kept in 16 bit counters + // - if compression is not successful for the first block, all input + // data is still in the window so we can still emit a stored block even + // when input comes from standard input. (This can also be done for + // all blocks if lit_bufsize is not greater than 32K.) + // - if compression is not successful for a file smaller than 64K, we can + // even emit a stored file instead of a stored block (saving 5 bytes). + // This is applicable only for zip (not gzip or zlib). + // - creating new Huffman trees less frequently may not provide fast + // adaptation to changes in the input data statistics. (Take for + // example a binary file with poorly compressible code followed by + // a highly compressible string table.) Smaller buffer sizes give + // fast adaptation but have of course the overhead of transmitting + // trees more frequently. + // - I can't count above 4 + int lit_bufsize; + + int last_lit; // running index in l_buf + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + int d_buf; // index of pendig_buf + + int opt_len; // bit length of current block with optimal trees + int static_len; // bit length of current block with static trees + int matches; // number of string matches in current block + int last_eob_len; // bit length of EOB code for last block + + // Output buffer. bits are inserted starting at the bottom (least + // significant bits). + short bi_buf; + + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + int bi_valid; + + GZIPHeader gheader = null; + + Deflate(ZStream strm){ + this.strm=strm; + dyn_ltree=new short[HEAP_SIZE*2]; + dyn_dtree=new short[(2*D_CODES+1)*2]; // distance tree + bl_tree=new short[(2*BL_CODES+1)*2]; // Huffman tree for bit lengths + } + + void lm_init() { + window_size=2*w_size; + + head[hash_size-1]=0; + for(int i=0; i= 3; max_blindex--) { + if (bl_tree[Tree.bl_order[max_blindex]*2+1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3*(max_blindex+1) + 5+5+4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + void send_all_trees(int lcodes, int dcodes, int blcodes){ + int rank; // index in bl_order + + send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { + send_bits(bl_tree[Tree.bl_order[rank]*2+1], 3); + } + send_tree(dyn_ltree, lcodes-1); // literal tree + send_tree(dyn_dtree, dcodes-1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + void send_tree (short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ){ + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0*2+1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0){ max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[(n+1)*2+1]; + if(++count < max_count && curlen == nextlen) { + continue; + } + else if(count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if(curlen != 0){ + if(curlen != prevlen){ + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count-3, 2); + } + else if(count <= 10){ + send_code(REPZ_3_10, bl_tree); + send_bits(count-3, 3); + } + else{ + send_code(REPZ_11_138, bl_tree); + send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if(nextlen == 0){ + max_count = 138; min_count = 3; + } + else if(curlen == nextlen){ + max_count = 6; min_count = 3; + } + else{ + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + final void put_byte(byte[] p, int start, int len){ + System.arraycopy(p, start, pending_buf, pending, len); + pending+=len; + } + + final void put_byte(byte c){ + pending_buf[pending++]=c; + } + final void put_short(int w) { + put_byte((byte)(w/*&0xff*/)); + put_byte((byte)(w>>>8)); + } + final void putShortMSB(int b){ + put_byte((byte)(b>>8)); + put_byte((byte)(b/*&0xff*/)); + } + + final void send_code(int c, short[] tree){ + int c2=c*2; + send_bits((tree[c2]&0xffff), (tree[c2+1]&0xffff)); + } + + void send_bits(int value, int length){ + int len = length; + if (bi_valid > (int)Buf_size - len) { + int val = value; +// bi_buf |= (val << bi_valid); + bi_buf |= ((val << bi_valid)&0xffff); + put_short(bi_buf); + bi_buf = (short)(val >>> (Buf_size - bi_valid)); + bi_valid += len - Buf_size; + } else { +// bi_buf |= (value) << bi_valid; + bi_buf |= (((value) << bi_valid)&0xffff); + bi_valid += len; + } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + void _tr_align(){ + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) { + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + boolean _tr_tally (int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ){ + + pending_buf[d_buf+last_lit*2] = (byte)(dist>>>8); + pending_buf[d_buf+last_lit*2+1] = (byte)dist; + + l_buf[last_lit] = (byte)lc; last_lit++; + + if (dist == 0) { + // lc is the unmatched char + dyn_ltree[lc*2]++; + } + else { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Tree._length_code[lc]+LITERALS+1)*2]++; + dyn_dtree[Tree.d_code(dist)*2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > 2) { + // Compute an upper bound for the compressed length + int out_length = last_lit*8; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (int)dyn_dtree[dcode*2] * + (5L+Tree.extra_dbits[dcode]); + } + out_length >>>= 3; + if ((matches < (last_lit/2)) && out_length < in_length/2) return true; + } + + return (last_lit == lit_bufsize-1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + void compress_block(short[] ltree, short[] dtree){ + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0){ + do{ + dist=((pending_buf[d_buf+lx*2]<<8)&0xff00)| + (pending_buf[d_buf+lx*2+1]&0xff); + lc=(l_buf[lx])&0xff; lx++; + + if(dist == 0){ + send_code(lc, ltree); // send a literal byte + } + else{ + // Here, lc is the match length - MIN_MATCH + code = Tree._length_code[lc]; + + send_code(code+LITERALS+1, ltree); // send the length code + extra = Tree.extra_lbits[code]; + if(extra != 0){ + lc -= Tree.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.d_code(dist); + + send_code(code, dtree); // send the distance code + extra = Tree.extra_dbits[code]; + if (extra != 0) { + dist -= Tree.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK*2+1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + void set_data_type(){ + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while(n<7){ bin_freq += dyn_ltree[n*2]; n++;} + while(n<128){ ascii_freq += dyn_ltree[n*2]; n++;} + while(n (ascii_freq >>> 2) ? Z_BINARY : Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + void bi_flush(){ + if (bi_valid == 16) { + put_short(bi_buf); + bi_buf=0; + bi_valid=0; + } + else if (bi_valid >= 8) { + put_byte((byte)bi_buf); + bi_buf>>>=8; + bi_valid-=8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + void bi_windup(){ + if (bi_valid > 8) { + put_short(bi_buf); + } else if (bi_valid > 0) { + put_byte((byte)bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + void copy_block(int buf, // the input data + int len, // its length + boolean header // true if block header must be written + ){ + int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(window, buf, len); + } + + void flush_block_only(boolean eof){ + _tr_flush_block(block_start>=0 ? block_start : -1, + strstart-block_start, + eof); + block_start=strstart; + strm.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + int deflate_stored(int flush){ + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if(max_block_size > pending_buf_size - 5) { + max_block_size = pending_buf_size - 5; + } + + // Copy as much as possible from input to output: + while(true){ + // Fill the window as much as possible: + if(lookahead<=1){ + fill_window(); + if(lookahead==0 && flush==Z_NO_FLUSH) return NeedMore; + if(lookahead==0) break; // flush the current block + } + + strstart+=lookahead; + lookahead=0; + + // Emit a stored block if pending_buf will be full: + max_start=block_start+max_block_size; + if(strstart==0|| strstart>=max_start) { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = (int)(strstart-max_start); + strstart = (int)max_start; + + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if(strstart-block_start >= w_size-MIN_LOOKAHEAD) { + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + } + } + + flush_block_only(flush == Z_FINISH); + if(strm.avail_out==0) + return (flush == Z_FINISH) ? FinishStarted : NeedMore; + + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + boolean eof // true if this is the last block for a file + ){ + send_bits((STORED_BLOCK<<1)+(eof?1:0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + boolean eof // true if this is the last block for a file + ) { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if(level > 0) { + // Check if the file is ascii or binary + if(data_type == Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.build_tree(this); + + d_desc.build_tree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex=build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb=(opt_len+3+7)>>>3; + static_lenb=(static_len+3+7)>>>3; + + if(static_lenb<=opt_lenb) opt_lenb=static_lenb; + } + else { + opt_lenb=static_lenb=stored_len+5; // force a stored block + } + + if(stored_len+4<=opt_lenb && buf != -1){ + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if(static_lenb == opt_lenb){ + send_bits((STATIC_TREES<<1)+(eof?1:0), 3); + compress_block(StaticTree.static_ltree, StaticTree.static_dtree); + } + else{ + send_bits((DYN_TREES<<1)+(eof?1:0), 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if(eof){ + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + void fill_window(){ + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do{ + more = (window_size-lookahead-strstart); + + // Deal with !@#$% 64K limit: + if(more==0 && strstart==0 && lookahead==0){ + more = w_size; + } + else if(more==-1) { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if(strstart >= w_size+ w_size-MIN_LOOKAHEAD) { + System.arraycopy(window, w_size, window, 0, w_size); + match_start-=w_size; + strstart-=w_size; // we now have strstart >= MAX_DIST + block_start-=w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p=n; + do { + m = (head[--p]&0xffff); + head[p]=(m>=w_size ? (short)(m-w_size) : 0); + } + while (--n != 0); + + n = w_size; + p = n; + do { + m = (prev[--p]&0xffff); + prev[p] = (m >= w_size ? (short)(m-w_size) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n!=0); + more += w_size; + } + + if (strm.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = strm.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if(lookahead >= MIN_MATCH) { + ins_h = window[strstart]&0xff; + ins_h=(((ins_h)<= MIN_MATCH){ + ins_h=(((ins_h)<=MIN_MATCH){ + // check_match(strstart, match_start, match_length); + + bflush=_tr_tally(strstart-match_start, match_length-MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if(match_length <= max_lazy_match && + lookahead >= MIN_MATCH) { + match_length--; // string at strstart already in hash table + do{ + strstart++; + + ins_h=((ins_h<= MIN_MATCH) { + ins_h=(((ins_h)< 4096))) { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH-1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if(prev_length >= MIN_MATCH && match_length <= prev_length) { + int max_insert = strstart + lookahead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush=_tr_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= prev_length-1; + prev_length -= 2; + do{ + if(++strstart <= max_insert) { + ins_h=(((ins_h)<(w_size-MIN_LOOKAHEAD) ? + strstart-(w_size-MIN_LOOKAHEAD) : 0; + int nice_match=this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = w_mask; + + int strend = strstart + MAX_MATCH; + byte scan_end1 = window[scan+best_len-1]; + byte scan_end = window[scan+best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= good_match) { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > lookahead) nice_match = lookahead; + + do { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (window[match+best_len] != scan_end || + window[match+best_len-1] != scan_end1 || + window[match] != window[scan] || + window[++match] != window[scan+1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do { + } while (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if(len>best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = window[scan+best_len-1]; + scan_end = window[scan+best_len]; + } + + } while ((cur_match = (prev[cur_match & wmask]&0xffff)) > limit + && --chain_length != 0); + + if (best_len <= lookahead) return best_len; + return lookahead; + } + + int deflateInit(int level, int bits, int memlevel){ + return deflateInit(level, Z_DEFLATED, bits, memlevel, + Z_DEFAULT_STRATEGY); + } + + int deflateInit(int level, int bits){ + return deflateInit(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY); + } + int deflateInit(int level){ + return deflateInit(level, MAX_WBITS); + } + private int deflateInit(int level, int method, int windowBits, + int memLevel, int strategy){ + int wrap = 1; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == Z_DEFAULT_COMPRESSION) level = 6; + + if (windowBits < 0) { // undocumented feature: suppress zlib header + wrap = 0; + windowBits = -windowBits; + } + else if(windowBits > 15){ + wrap = 2; + windowBits -= 16; + strm.adler=new CRC32(); + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + strm.dstate = (Deflate)this; + + this.wrap = wrap; + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits+MIN_MATCH-1)/MIN_MATCH); + + window = new byte[w_size*2]; + prev = new short[w_size]; + head = new short[hash_size]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize*3]; + pending_buf_size = lit_bufsize*3; + + d_buf = lit_bufsize; + l_buf = new byte[lit_bufsize]; + + this.level = level; + + this.strategy = strategy; + this.method = (byte)method; + + return deflateReset(); + } + + int deflateReset(){ + strm.total_in = strm.total_out = 0; + strm.msg = null; // + strm.data_type = Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if(wrap < 0){ + wrap = -wrap; + } + status = (wrap==0) ? BUSY_STATE : INIT_STATE; + strm.adler.reset(); + + last_flush = Z_NO_FLUSH; + + tr_init(); + lm_init(); + return Z_OK; + } + + int deflateEnd(){ + if(status!=INIT_STATE && status!=BUSY_STATE && status!=FINISH_STATE){ + return Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf=null; + l_buf=null; + head=null; + prev=null; + window=null; + // free + // dstate=null; + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; + } + + int deflateParams(int _level, int _strategy){ + int err=Z_OK; + + if(_level == Z_DEFAULT_COMPRESSION){ + _level = 6; + } + if(_level < 0 || _level > 9 || + _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + if(config_table[level].func!=config_table[_level].func && + strm.total_in != 0) { + // Flush the last buffer: + err = strm.deflate(Z_PARTIAL_FLUSH); + } + + if(level != _level) { + level = _level; + max_lazy_match = config_table[level].max_lazy; + good_match = config_table[level].good_length; + nice_match = config_table[level].nice_length; + max_chain_length = config_table[level].max_chain; + } + strategy = _strategy; + return err; + } + + int deflateSetDictionary (byte[] dictionary, int dictLength){ + int length = dictLength; + int index=0; + + if(dictionary == null || status != INIT_STATE) + return Z_STREAM_ERROR; + + strm.adler.update(dictionary, 0, dictLength); + + if(length < MIN_MATCH) return Z_OK; + if(length > w_size-MIN_LOOKAHEAD){ + length = w_size-MIN_LOOKAHEAD; + index=dictLength-length; // use the tail of the dictionary + } + System.arraycopy(dictionary, index, window, 0, length); + strstart = length; + block_start = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0]&0xff; + ins_h=(((ins_h)<Z_FINISH || flush<0){ + return Z_STREAM_ERROR; + } + + if(strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (status == FINISH_STATE && flush != Z_FINISH)) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_STREAM_ERROR)]; + return Z_STREAM_ERROR; + } + if(strm.avail_out == 0){ + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if(status == INIT_STATE) { + if(wrap == 2){ + getGZIPHeader().put(this); + status=BUSY_STATE; + strm.adler.reset(); + } + else{ + int header = (Z_DEFLATED+((w_bits-8)<<4))<<8; + int level_flags=((level-1)&0xff)>>1; + + if(level_flags>3) level_flags=3; + header |= (level_flags<<6); + if(strstart!=0) header |= PRESET_DICT; + header+=31-(header % 31); + + status=BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if(strstart!=0){ + long adler=strm.adler.getValue(); + putShortMSB((int)(adler>>>16)); + putShortMSB((int)(adler&0xffff)); + } + strm.adler.reset(); + } + } + + // Flush as much pending output as possible + if(pending != 0) { + strm.flush_pending(); + if(strm.avail_out == 0) { + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = -1; + return Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if(strm.avail_in==0 && flush <= old_flush && + flush != Z_FINISH) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if(status == FINISH_STATE && strm.avail_in != 0) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if(strm.avail_in!=0 || lookahead!=0 || + (flush != Z_NO_FLUSH && status != FINISH_STATE)) { + int bstate=-1; + switch(config_table[level].func){ + case STORED: + bstate = deflate_stored(flush); + break; + case FAST: + bstate = deflate_fast(flush); + break; + case SLOW: + bstate = deflate_slow(flush); + break; + default: + } + + if (bstate==FinishStarted || bstate==FinishDone) { + status = FINISH_STATE; + } + if (bstate==NeedMore || bstate==FinishStarted) { + if(strm.avail_out == 0) { + last_flush = -1; // avoid BUF_ERROR next call, see above + } + return Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate==BlockDone) { + if(flush == Z_PARTIAL_FLUSH) { + _tr_align(); + } + else { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if(flush == Z_FULL_FLUSH) { + //state.head[s.hash_size-1]=0; + for(int i=0; i>8)&0xff)); + put_byte((byte)((adler>>16)&0xff)); + put_byte((byte)((adler>>24)&0xff)); + put_byte((byte)(strm.total_in&0xff)); + put_byte((byte)((strm.total_in>>8)&0xff)); + put_byte((byte)((strm.total_in>>16)&0xff)); + put_byte((byte)((strm.total_in>>24)&0xff)); + + getGZIPHeader().setCRC(adler); + } + else{ + // Write the zlib trailer (adler32) + long adler=strm.adler.getValue(); + putShortMSB((int)(adler>>>16)); + putShortMSB((int)(adler&0xffff)); + } + + strm.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + + if(wrap > 0) wrap = -wrap; // write the trailer only once! + return pending != 0 ? Z_OK : Z_STREAM_END; + } + + static int deflateCopy(ZStream dest, ZStream src){ + + if(src.dstate == null){ + return Z_STREAM_ERROR; + } + + if(src.next_in!=null){ + dest.next_in = new byte[src.next_in.length]; + System.arraycopy(src.next_in, 0, dest.next_in, 0, src.next_in.length); + } + dest.next_in_index = src.next_in_index; + dest.avail_in = src.avail_in; + dest.total_in = src.total_in; + + if(src.next_out!=null){ + dest.next_out = new byte[src.next_out.length]; + System.arraycopy(src.next_out, 0, dest.next_out ,0 , src.next_out.length); + } + + dest.next_out_index = src.next_out_index; + dest.avail_out = src.avail_out; + dest.total_out = src.total_out; + + dest.msg = src.msg; + dest.data_type = src.data_type; + dest.adler = src.adler.copy(); + + try{ + dest.dstate = (Deflate)src.dstate.clone(); + dest.dstate.strm = dest; + } + catch(CloneNotSupportedException e){ + // + } + return Z_OK; + } + + public Object clone() throws CloneNotSupportedException { + Deflate dest = (Deflate)super.clone(); + + dest.pending_buf = dup(dest.pending_buf); + dest.d_buf = dest.d_buf; + dest.l_buf = dup(dest.l_buf); + dest.window = dup(dest.window); + + dest.prev = dup(dest.prev); + dest.head = dup(dest.head); + dest.dyn_ltree = dup(dest.dyn_ltree); + dest.dyn_dtree = dup(dest.dyn_dtree); + dest.bl_tree = dup(dest.bl_tree); + + dest.bl_count = dup(dest.bl_count); + dest.next_code = dup(dest.next_code); + dest.heap = dup(dest.heap); + dest.depth = dup(dest.depth); + + dest.l_desc.dyn_tree = dest.dyn_ltree; + dest.d_desc.dyn_tree = dest.dyn_dtree; + dest.bl_desc.dyn_tree = dest.bl_tree; + + /* + dest.l_desc.stat_desc = StaticTree.static_l_desc; + dest.d_desc.stat_desc = StaticTree.static_d_desc; + dest.bl_desc.stat_desc = StaticTree.static_bl_desc; + */ + + if(dest.gheader!=null){ + dest.gheader = (GZIPHeader)dest.gheader.clone(); + } + + return dest; + } + + private byte[] dup(byte[] buf){ + byte[] foo = new byte[buf.length]; + System.arraycopy(buf, 0, foo, 0, foo.length); + return foo; + } + private short[] dup(short[] buf){ + short[] foo = new short[buf.length]; + System.arraycopy(buf, 0, foo, 0, foo.length); + return foo; + } + private int[] dup(int[] buf){ + int[] foo = new int[buf.length]; + System.arraycopy(buf, 0, foo, 0, foo.length); + return foo; + } + + synchronized GZIPHeader getGZIPHeader(){ + if(gheader==null){ + gheader = new GZIPHeader(); + } + return gheader; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/Deflater.java b/service/src/main/java/com/jcraft/jzlib/Deflater.java new file mode 100644 index 0000000..ce0580d --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/Deflater.java @@ -0,0 +1,171 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final public class Deflater extends ZStream{ + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_WBITS=MAX_WBITS; + + static final private int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + private boolean finished = false; + + public Deflater(){ + super(); + } + + public Deflater(int level) throws GZIPException { + this(level, MAX_WBITS); + } + + public Deflater(int level, boolean nowrap) throws GZIPException { + this(level, MAX_WBITS, nowrap); + } + + public Deflater(int level, int bits) throws GZIPException { + this(level, bits, false); + } + + public Deflater(int level, int bits, boolean nowrap) throws GZIPException { + super(); + int ret = init(level, bits, nowrap); + if(ret!=Z_OK) + throw new GZIPException(ret+": "+msg); + } + + public Deflater(int level, int bits, int memlevel, JZlib.WrapperType wrapperType) throws GZIPException { + super(); + int ret = init(level, bits, memlevel, wrapperType); + if(ret!=Z_OK) + throw new GZIPException(ret+": "+msg); + } + + public Deflater(int level, int bits, int memlevel) throws GZIPException { + super(); + int ret = init(level, bits, memlevel); + if(ret!=Z_OK) + throw new GZIPException(ret+": "+msg); + } + + public int init(int level){ + return init(level, MAX_WBITS); + } + public int init(int level, boolean nowrap){ + return init(level, MAX_WBITS, nowrap); + } + public int init(int level, int bits){ + return init(level, bits, false); + } + public int init(int level, int bits, int memlevel, JZlib.WrapperType wrapperType){ + if(bits < 9 || bits > 15){ + return Z_STREAM_ERROR; + } + if(wrapperType == JZlib.W_NONE) { + bits *= -1; + } + else if(wrapperType == JZlib.W_GZIP) { + bits += 16; + } + else if(wrapperType == JZlib.W_ANY) { + return Z_STREAM_ERROR; + } + else if(wrapperType == JZlib.W_ZLIB) { + } + return init(level, bits, memlevel); + } + public int init(int level, int bits, int memlevel){ + finished = false; + dstate=new Deflate(this); + return dstate.deflateInit(level, bits, memlevel); + } + public int init(int level, int bits, boolean nowrap){ + finished = false; + dstate=new Deflate(this); + return dstate.deflateInit(level, nowrap?-bits:bits); + } + + public int deflate(int flush){ + if(dstate==null){ + return Z_STREAM_ERROR; + } + int ret = dstate.deflate(flush); + if(ret == Z_STREAM_END) + finished = true; + return ret; + } + public int end(){ + finished = true; + if(dstate==null) return Z_STREAM_ERROR; + int ret=dstate.deflateEnd(); + dstate=null; + free(); + return ret; + } + public int params(int level, int strategy){ + if(dstate==null) return Z_STREAM_ERROR; + return dstate.deflateParams(level, strategy); + } + public int setDictionary (byte[] dictionary, int dictLength){ + if(dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateSetDictionary(dictionary, dictLength); + } + + public boolean finished(){ + return finished; + } + + public int copy(Deflater src){ + this.finished = src.finished; + return Deflate.deflateCopy(this, src); + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/DeflaterOutputStream.java b/service/src/main/java/com/jcraft/jzlib/DeflaterOutputStream.java new file mode 100644 index 0000000..3c18836 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/DeflaterOutputStream.java @@ -0,0 +1,181 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jzlib; +import java.io.*; + +public class DeflaterOutputStream extends FilterOutputStream { + + protected final Deflater deflater; + + protected byte[] buffer; + + private boolean closed = false; + + private boolean syncFlush = false; + + private final byte[] buf1 = new byte[1]; + + protected boolean mydeflater = false; + + private boolean close_out = true; + + protected static final int DEFAULT_BUFSIZE = 512; + + public DeflaterOutputStream(OutputStream out) throws IOException { + this(out, + new Deflater(JZlib.Z_DEFAULT_COMPRESSION), + DEFAULT_BUFSIZE, true); + mydeflater = true; + } + + public DeflaterOutputStream(OutputStream out, Deflater def) throws IOException { + this(out, def, DEFAULT_BUFSIZE, true); + } + + public DeflaterOutputStream(OutputStream out, + Deflater deflater, + int size) throws IOException { + this(out, deflater, size, true); + } + public DeflaterOutputStream(OutputStream out, + Deflater deflater, + int size, + boolean close_out) throws IOException { + super(out); + if (out == null || deflater == null) { + throw new NullPointerException(); + } + else if (size <= 0) { + throw new IllegalArgumentException("buffer size must be greater than 0"); + } + this.deflater = deflater; + buffer = new byte[size]; + this.close_out = close_out; + } + + public void write(int b) throws IOException { + buf1[0] = (byte)(b & 0xff); + write(buf1, 0, 1); + } + + public void write(byte[] b, int off, int len) throws IOException { + if (deflater.finished()) { + throw new IOException("finished"); + } + else if (off<0 | len<0 | off+len>b.length) { + throw new IndexOutOfBoundsException(); + } + else if (len == 0) { + return; + } + else { + int flush = syncFlush ? JZlib.Z_SYNC_FLUSH : JZlib.Z_NO_FLUSH; + deflater.setInput(b, off, len, true); + while (deflater.avail_in>0) { + int err = deflate(flush); + if (err == JZlib.Z_STREAM_END) + break; + } + } + } + + public void finish() throws IOException { + while (!deflater.finished()) { + deflate(JZlib.Z_FINISH); + } + } + + public void close() throws IOException { + if (!closed) { + finish(); + if (mydeflater){ + deflater.end(); + } + if(close_out) + out.close(); + closed = true; + } + } + + protected int deflate(int flush) throws IOException { + deflater.setOutput(buffer, 0, buffer.length); + int err = deflater.deflate(flush); + switch(err) { + case JZlib.Z_OK: + case JZlib.Z_STREAM_END: + break; + case JZlib.Z_BUF_ERROR: + if(deflater.avail_in<=0 && flush!=JZlib.Z_FINISH){ + // flush() without any data + break; + } + default: + throw new IOException("failed to deflate: error="+err+" avail_out="+deflater.avail_out); + } + int len = deflater.next_out_index; + if (len > 0) { + out.write(buffer, 0, len); + } + return err; + } + + public void flush() throws IOException { + if (syncFlush && !deflater.finished()) { + while (true) { + int err = deflate(JZlib.Z_SYNC_FLUSH); + if (deflater.next_out_index < buffer.length) + break; + if (err == JZlib.Z_STREAM_END) + break; + } + } + out.flush(); + } + + public long getTotalIn() { + return deflater.getTotalIn(); + } + + public long getTotalOut() { + return deflater.getTotalOut(); + } + + public void setSyncFlush(boolean syncFlush){ + this.syncFlush = syncFlush; + } + + public boolean getSyncFlush(){ + return this.syncFlush; + } + + public Deflater getDeflater(){ + return deflater; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/GZIPException.java b/service/src/main/java/com/jcraft/jzlib/GZIPException.java new file mode 100644 index 0000000..0beef40 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/GZIPException.java @@ -0,0 +1,44 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +public class GZIPException extends java.io.IOException { + public GZIPException() { + super(); + } + public GZIPException(String s) { + super(s); + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/GZIPHeader.java b/service/src/main/java/com/jcraft/jzlib/GZIPHeader.java new file mode 100644 index 0000000..0405e00 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/GZIPHeader.java @@ -0,0 +1,214 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +import java.io.UnsupportedEncodingException; + +/** + * @see "http://www.ietf.org/rfc/rfc1952.txt" + */ +public class GZIPHeader implements Cloneable { + + public static final byte OS_MSDOS = (byte) 0x00; + public static final byte OS_AMIGA = (byte) 0x01; + public static final byte OS_VMS = (byte) 0x02; + public static final byte OS_UNIX = (byte) 0x03; + public static final byte OS_ATARI = (byte) 0x05; + public static final byte OS_OS2 = (byte) 0x06; + public static final byte OS_MACOS = (byte) 0x07; + public static final byte OS_TOPS20 = (byte) 0x0a; + public static final byte OS_WIN32 = (byte) 0x0b; + public static final byte OS_VMCMS = (byte) 0x04; + public static final byte OS_ZSYSTEM = (byte) 0x08; + public static final byte OS_CPM = (byte) 0x09; + public static final byte OS_QDOS = (byte) 0x0c; + public static final byte OS_RISCOS = (byte) 0x0d; + public static final byte OS_UNKNOWN = (byte) 0xff; + + boolean text = false; + private boolean fhcrc = false; + long time; + int xflags; + int os = 255; + byte[] extra; + byte[] name; + byte[] comment; + int hcrc; + long crc; + boolean done = false; + long mtime = 0; + + public void setModifiedTime(long mtime) { + this.mtime = mtime; + } + + public long getModifiedTime() { + return mtime; + } + + public void setOS(int os) { + if((0<=os && os <=13) || os==255) + this.os=os; + else + throw new IllegalArgumentException("os: "+os); + } + + public int getOS(){ + return os; + } + + public void setName(String name) { + try{ + this.name=name.getBytes("ISO-8859-1"); + } + catch(UnsupportedEncodingException e){ + throw new IllegalArgumentException("name must be in ISO-8859-1 "+name); + } + } + + public String getName(){ + if(name==null) return ""; + try { + return new String(name, "ISO-8859-1"); + } + catch (UnsupportedEncodingException e) { + throw new InternalError(e.toString()); + } + } + + public void setComment(String comment) { + try{ + this.comment=comment.getBytes("ISO-8859-1"); + } + catch(UnsupportedEncodingException e){ + throw new IllegalArgumentException("comment must be in ISO-8859-1 "+name); + } + } + + public String getComment(){ + if(comment==null) return ""; + try { + return new String(comment, "ISO-8859-1"); + } + catch (UnsupportedEncodingException e) { + throw new InternalError(e.toString()); + } + } + + public void setCRC(long crc){ + this.crc = crc; + } + + public long getCRC(){ + return crc; + } + + void put(Deflate d){ + int flag = 0; + if(text){ + flag |= 1; // FTEXT + } + if(fhcrc){ + flag |= 2; // FHCRC + } + if(extra!=null){ + flag |= 4; // FEXTRA + } + if(name!=null){ + flag |= 8; // FNAME + } + if(comment!=null){ + flag |= 16; // FCOMMENT + } + int xfl = 0; + if(d.level == JZlib.Z_BEST_SPEED){ + xfl |= 4; + } + else if (d.level == JZlib.Z_BEST_COMPRESSION){ + xfl |= 2; + } + + d.put_short((short)0x8b1f); // ID1 ID2 + d.put_byte((byte)8); // CM(Compression Method) + d.put_byte((byte)flag); + d.put_byte((byte)mtime); + d.put_byte((byte)(mtime>>8)); + d.put_byte((byte)(mtime>>16)); + d.put_byte((byte)(mtime>>24)); + d.put_byte((byte)xfl); + d.put_byte((byte)os); + + if(extra!=null){ + d.put_byte((byte)extra.length); + d.put_byte((byte)(extra.length>>8)); + d.put_byte(extra, 0, extra.length); + } + + if(name!=null){ + d.put_byte(name, 0, name.length); + d.put_byte((byte)0); + } + + if(comment!=null){ + d.put_byte(comment, 0, comment.length); + d.put_byte((byte)0); + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + GZIPHeader gheader = (GZIPHeader)super.clone(); + byte[] tmp; + if(gheader.extra!=null){ + tmp=new byte[gheader.extra.length]; + System.arraycopy(gheader.extra, 0, tmp, 0, tmp.length); + gheader.extra = tmp; + } + + if(gheader.name!=null){ + tmp=new byte[gheader.name.length]; + System.arraycopy(gheader.name, 0, tmp, 0, tmp.length); + gheader.name = tmp; + } + + if(gheader.comment!=null){ + tmp=new byte[gheader.comment.length]; + System.arraycopy(gheader.comment, 0, tmp, 0, tmp.length); + gheader.comment = tmp; + } + + return gheader; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/GZIPInputStream.java b/service/src/main/java/com/jcraft/jzlib/GZIPInputStream.java new file mode 100644 index 0000000..5d29dca --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/GZIPInputStream.java @@ -0,0 +1,145 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jzlib; +import java.io.*; + +public class GZIPInputStream extends InflaterInputStream { + + public GZIPInputStream(InputStream in) throws IOException { + this(in, DEFAULT_BUFSIZE, true); + } + + public GZIPInputStream(InputStream in, + int size, + boolean close_in) throws IOException { + this(in, new Inflater(15+16), size, close_in); + myinflater = true; + } + + public GZIPInputStream(InputStream in, + Inflater inflater, + int size, + boolean close_in) throws IOException { + super(in, inflater, size, close_in); + } + + public long getModifiedtime() { + return inflater.istate.getGZIPHeader().getModifiedTime(); + } + + public int getOS() { + return inflater.istate.getGZIPHeader().getOS(); + } + + public String getName() { + return inflater.istate.getGZIPHeader().getName(); + } + + public String getComment() { + return inflater.istate.getGZIPHeader().getComment(); + } + + public long getCRC() throws GZIPException { + if(inflater.istate.mode != 12 /*DONE*/) + throw new GZIPException("checksum is not calculated yet."); + return inflater.istate.getGZIPHeader().getCRC(); + } + + public void readHeader() throws IOException { + + byte[] empty = "".getBytes(); + inflater.setOutput(empty, 0, 0); + inflater.setInput(empty, 0, 0, false); + + byte[] b = new byte[10]; + + int n = fill(b); + if(n!=10){ + if(n>0){ + inflater.setInput(b, 0, n, false); + //inflater.next_in_index = n; + inflater.next_in_index = 0; + inflater.avail_in = n; + } + throw new IOException("no input"); + } + + inflater.setInput(b, 0, n, false); + + byte[] b1 = new byte[1]; + do{ + if(inflater.avail_in<=0){ + int i = in.read(b1); + if(i<=0) + throw new IOException("no input"); + inflater.setInput(b1, 0, 1, true); + } + + int err = inflater.inflate(JZlib.Z_NO_FLUSH); + + if(err!=0/*Z_OK*/){ + int len = 2048-inflater.next_in.length; + if(len>0){ + byte[] tmp = new byte[len]; + n = fill(tmp); + if(n>0){ + inflater.avail_in += inflater.next_in_index; + inflater.next_in_index = 0; + inflater.setInput(tmp, 0, n, true); + } + } + //inflater.next_in_index = inflater.next_in.length; + inflater.avail_in += inflater.next_in_index; + inflater.next_in_index = 0; + throw new IOException(inflater.msg); + } + } + while(inflater.istate.inParsingHeader()); + } + + private int fill(byte[] buf) { + int len = buf.length; + int n = 0; + do{ + int i = -1; + try { + i = in.read(buf, n, buf.length - n); + } + catch(IOException e){ + } + if(i == -1){ + break; + } + n+=i; + } + while(n>> 1){ + case 0: // stored + {b>>>=(3);k-=(3);} + t = k & 7; // go to byte boundary + + {b>>>=(t);k-=(t);} + mode = LENS; // get length of stored block + break; + case 1: // fixed + InfTree.inflate_trees_fixed(bl, bd, tl, td, z); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0); + + {b>>>=(3);k-=(3);} + + mode = CODES; + break; + case 2: // dynamic + + {b>>>=(3);k-=(3);} + + mode = TABLE; + break; + case 3: // illegal + + {b>>>=(3);k-=(3);} + mode = BAD; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + break; + case LENS: + + while(k<(32)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>> 16) & 0xffff) != (b & 0xffff)){ + mode = BAD; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE); + break; + case STORED: + if (n == 0){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + if(m==0){ + if(q==end&&read!=0){ + q=0; m=(int)(qn) t = n; + if(t>m) t = m; + System.arraycopy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last!=0 ? DRY : TYPE; + break; + case TABLE: + + while(k<(14)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)< 29 || ((t >> 5) & 0x1f) > 29) + { + mode = BAD; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if(blens==null || blens.length>>=(14);k-=(14);} + + index = 0; + mode = BTREE; + case BTREE: + while (index < 4 + (table >>> 10)){ + while(k<(3)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(3);k-=(3);} + } + + while(index < 19){ + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK){ + r = t; + if (r == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + index = 0; + mode = DTREE; + case DTREE: + while (true){ + t = table; + if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){ + break; + } + + int[] h; + int i, j, c; + + t = bb[0]; + + while(k<(t)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); + blens[index++] = c; + } + else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while(k<(t+i)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); + + j += (b & inflate_mask[i]); + + b>>>=(i);k-=(i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)){ + blens=null; + mode = BAD; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + c = c == 16 ? blens[i-1] : 0; + do{ + blens[i++] = c; + } + while (--j!=0); + index = i; + } + } + + tb[0]=-1; + { + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tli, tdi, hufts, z); + + if (t != Z_OK){ + if (t == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + r = t; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + codes.init(bl[0], bd[0], hufts, tli[0], hufts, tdi[0]); + } + mode = CODES; + case CODES: + bitb=b; bitk=k; + z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + + if ((r = codes.proc(r)) != Z_STREAM_END){ + return inflate_flush(r); + } + r = Z_OK; + codes.free(z); + + p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk; + q=write;m=(int)(q z.avail_out) n = z.avail_out; + if(n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(check && n>0){ + z.adler.update(window, q, n); + } + + // copy as far as end of window + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end){ + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(check && n>0){ + z.adler.update(window, q, n); + } + + // copy + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/InfCodes.java b/service/src/main/java/com/jcraft/jzlib/InfCodes.java new file mode 100644 index 0000000..aaf69cd --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/InfCodes.java @@ -0,0 +1,610 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final class InfCodes{ + + static final private int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + static final private int START=0; // x: set up for LEN + static final private int LEN=1; // i: get length/literal/eob next + static final private int LENEXT=2; // i: getting length extra (have base) + static final private int DIST=3; // i: get distance next + static final private int DISTEXT=4;// i: getting distance extra + static final private int COPY=5; // o: copying bytes in window, waiting for space + static final private int LIT=6; // o: got literal, waiting for output space + static final private int WASH=7; // o: got eob, possibly still output waiting + static final private int END=8; // x: got eob and all data flushed + static final private int BADCODE=9;// x: got error + + int mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index=0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + private final ZStream z; + private final InfBlocks s; + InfCodes(ZStream z, InfBlocks s){ + this.z=z; + this.s=s; + } + + void init(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index){ + mode=START; + lbits=(byte)bl; + dbits=(byte)bd; + ltree=tl; + ltree_index=tl_index; + dtree = td; + dtree_index=td_index; + tree=null; + } + + int proc(int r){ + int j; // temporary storage + int[] t; // temporary pointer + int tindex; // temporary pointer + int e; // extra bits or operation + int b=0; // bit buffer + int k=0; // bits in bit buffer + int p=0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q= 258 && n >= 10){ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q>>=(tree[tindex+1]); + k-=(tree[tindex+1]); + + e=tree[tindex]; + + if(e == 0){ // literal + lit = tree[tindex+2]; + mode = LIT; + break; + } + if((e & 16)!=0 ){ // length + get = e & 15; + len = tree[tindex+2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3+tree[tindex+2]; + break; + } + if ((e & 32)!=0){ // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + need = dbits; + tree = dtree; + tree_index=dtree_index; + mode = DIST; + case DIST: // i: get distance next + j = need; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=tree[tindex+1]; + k-=tree[tindex+1]; + + e = (tree[tindex]); + if((e & 16)!=0){ // distance + get = e & 15; + dist = tree[tindex+2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3 + tree[tindex+2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + + case DISTEXT: // i: getting distance extra + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while(f < 0){ // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len!=0){ + + if(m==0){ + if(q==s.end&&s.read!=0){q=0;m=q 7){ // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write=q; r=s.inflate_flush(r); + q=s.write;m=q= 258 && n >= 10 + // get literal/length code + while(k<(20)){ // max bits for literal/length code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++] = (byte)tp[tp_index_t_3+2]; + m--; + continue; + } + do { + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + e &= 15; + c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]); + + b>>=e; k-=e; + + // decode distance base of block to copy + while(k<(15)){ // max bits for distance code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + // get extra bits to add to distance base + e &= 15; + while(k<(e)){ // get extra bits (up to 13) + n--; + b|=(z.next_in[p++]&0xff)<>=(e); k-=(e); + + // do the copy + m -= c; + if (q >= d){ // offset before dest + // just copy + r=q-d; + if(q-r>0 && 2>(q-r)){ + s.window[q++]=s.window[r++]; // minimum count is three, + s.window[q++]=s.window[r++]; // so unroll loop a little + c-=2; + } + else{ + System.arraycopy(s.window, r, s.window, q, 2); + q+=2; r+=2; c-=2; + } + } + else{ // else offset after destination + r=q-d; + do{ + r+=s.end; // force pointer in window + }while(r<0); // covers invalid distances + e=s.end-r; + if(c>e){ // if source crosses, + c-=e; // wrapped copy + if(q-r>0 && e>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--e!=0); + } + else{ + System.arraycopy(s.window, r, s.window, q, e); + q+=e; r+=e; e=0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if(q-r>0 && c>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--c!=0); + } + else{ + System.arraycopy(s.window, r, s.window, q, c); + q+=c; r+=c; c=0; + } + break; + } + else if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + e=tp[tp_index_t_3]; + } + else{ + z.msg = "invalid distance code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + break; + } + + if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + if((e=tp[tp_index_t_3])==0){ + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++]=(byte)tp[tp_index_t_3+2]; + m--; + break; + } + } + else if((e&32)!=0){ + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_STREAM_END; + } + else{ + z.msg="invalid literal/length code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + } + while(m>=258 && n>= 10); + + // not enough input or output--restore pointers and return + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_OK; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/InfTree.java b/service/src/main/java/com/jcraft/jzlib/InfTree.java new file mode 100644 index 0000000..80a2b7b --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/InfTree.java @@ -0,0 +1,518 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final class InfTree{ + + static final private int MANY=1440; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + static final int fixed_bl = 9; + static final int fixed_bd = 5; + + static final int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static final int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static final int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static final int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static final int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static final int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + static final int BMAX=15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ){ + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do { + c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX + }while(i!=0); + + if(c[0] == n){ // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if(c[j]!=0) break; + k = j; // minimum code length + if(l < j){ + l = j; + } + for (i = BMAX; i!=0; i--){ + if(c[i]!=0) break; + } + g = i; // maximum code length + if(l > i){ + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1){ + if ((y -= c[j]) < 0){ + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0){ + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i!=0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do { + if ((j = b[bindex+p]) != 0){ + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++){ + a = c[k]; + while (a--!=0){ + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l){ + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if((f=1<<(j=k-w))>a+1){ // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if(j < z){ + while (++j < z){ // try smaller tables up to z bits + if((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY){ // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if(h!=0){ + x[h]=i; // save pattern for backing up + r[0]=(byte)j; // bits in this table + r[1]=(byte)l; // bits to dump before this table + j=i>>>(w - l); + r[2] = (int)(q - u[h-1] - j); // offset to this table + System.arraycopy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table + } + else{ + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n){ + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s){ + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else{ + r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists + r[2]=d[v[p++] - s]; + } + + // fill code-like entries with r + f=1<<(k-w); + for (j=i>>>w;j>>= 1){ + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]){ + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + int inflate_trees_bits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + initWorkArea(19); + hn[0]=0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if(result == Z_BUF_ERROR || bb[0] == 0){ + z.msg = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + int inflate_trees_dynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + + // build literal/length tree + initWorkArea(288); + hn[0]=0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0){ + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed literal/length tree"; + } + else if (result != Z_MEM_ERROR){ + z.msg = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)){ + if (result == Z_DATA_ERROR){ + z.msg = "oversubscribed distance tree"; + } + else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } + else if (result != Z_MEM_ERROR){ + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ){ + bl[0]=fixed_bl; + bd[0]=fixed_bd; + tl[0]=fixed_tl; + td[0]=fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize){ + if(hn==null){ + hn=new int[1]; + v=new int[vsize]; + c=new int[BMAX+1]; + r=new int[3]; + u=new int[BMAX]; + x=new int[BMAX+1]; + } + if(v.length> 4) + 1; + if(w < 48) + w &= 15; + } + + if(w<8 ||w>15){ + inflateEnd(); + return Z_STREAM_ERROR; + } + if(blocks != null && wbits != w){ + blocks.free(); + blocks=null; + } + + // set window size + wbits=w; + + this.blocks=new InfBlocks(z, 1<>8))&0xff; + + if(((wrap&1)==0 || // check if zlib header allowed + (((this.method << 8)+b) % 31)!=0) && + (this.method&0xf)!=Z_DEFLATED){ + if(wrap == 4){ + z.next_in_index -= 2; + z.avail_in += 2; + z.total_in -= 2; + wrap = 0; + this.mode = BLOCKS; + break; + } + this.mode = BAD; + z.msg = "incorrect header check"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + if((this.method&0xf)!=Z_DEFLATED){ + this.mode = BAD; + z.msg="unknown compression method"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + if(wrap == 4){ + wrap = 1; + } + + if((this.method>>4)+8>this.wbits){ + this.mode = BAD; + z.msg="invalid window size"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + z.adler=new Adler32(); + + if((b&PRESET_DICT)==0){ + this.mode = BLOCKS; + break; + } + this.mode = DICT4; + case DICT4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + this.mode=DICT3; + case DICT3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + this.mode=DICT2; + case DICT2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + this.mode=DICT1; + case DICT1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need += (z.next_in[z.next_in_index++]&0xffL); + z.adler.reset(this.need); + this.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + this.mode = BAD; + z.msg = "need dictionary"; + this.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + r = this.blocks.proc(r); + if(r == Z_DATA_ERROR){ + this.mode = BAD; + this.marker = 0; // can try inflateSync + break; + } + if(r == Z_OK){ + r = f; + } + if(r != Z_STREAM_END){ + return r; + } + r = f; + this.was=z.adler.getValue(); + this.blocks.reset(); + if(this.wrap==0){ + this.mode=DONE; + break; + } + this.mode=CHECK4; + case CHECK4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + this.mode=CHECK3; + case CHECK3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + this.mode = CHECK2; + case CHECK2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + this.mode = CHECK1; + case CHECK1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=(z.next_in[z.next_in_index++]&0xffL); + + if(flags!=0){ // gzip + this.need = ((this.need&0xff000000)>>24 | + (this.need&0x00ff0000)>>8 | + (this.need&0x0000ff00)<<8 | + (this.need&0x0000ffff)<<24)&0xffffffffL; + } + + if(((int)(this.was)) != ((int)(this.need))){ + z.msg = "incorrect data check"; + // chack is delayed + /* + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + */ + } + else if(flags!=0 && gheader!=null){ + gheader.crc = this.need; + } + + this.mode = LENGTH; + case LENGTH: + if (wrap!=0 && flags!=0) { + + try { r=readBytes(4, r, f); } + catch(Return e){ return e.r; } + + if(z.msg!=null && z.msg.equals("incorrect data check")){ + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + + if (this.need != (z.total_out & 0xffffffffL)) { + z.msg = "incorrect length check"; + this.mode = BAD; + break; + } + z.msg = null; + } + else { + if(z.msg!=null && z.msg.equals("incorrect data check")){ + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + } + + this.mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + + case FLAGS: + + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + + flags = ((int)this.need)&0xffff; + + if ((flags & 0xff) != Z_DEFLATED) { + z.msg = "unknown compression method"; + this.mode = BAD; + break; + } + if ((flags & 0xe000)!=0) { + z.msg = "unknown header flags set"; + this.mode = BAD; + break; + } + + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + + this.mode = TIME; + + case TIME: + try { r=readBytes(4, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null) + gheader.time = this.need; + if ((flags & 0x0200)!=0){ + checksum(4, this.need); + } + this.mode = OS; + case OS: + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.xflags = ((int)this.need)&0xff; + gheader.os = (((int)this.need)>>8)&0xff; + } + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + this.mode = EXLEN; + case EXLEN: + if ((flags & 0x0400)!=0) { + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.extra = new byte[((int)this.need)&0xffff]; + } + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + } + else if(gheader!=null){ + gheader.extra=null; + } + this.mode = EXTRA; + + case EXTRA: + if ((flags & 0x0400)!=0) { + try { + r=readBytes(r, f); + if(gheader!=null){ + byte[] foo = tmp_string.toByteArray(); + tmp_string=null; + if(foo.length == gheader.extra.length){ + System.arraycopy(foo, 0, gheader.extra, 0, foo.length); + } + else{ + z.msg = "bad extra field length"; + this.mode = BAD; + break; + } + } + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.extra=null; + } + this.mode = NAME; + case NAME: + if ((flags & 0x0800)!=0) { + try { + r=readString(r, f); + if(gheader!=null){ + gheader.name=tmp_string.toByteArray(); + } + tmp_string=null; + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.name=null; + } + this.mode = COMMENT; + case COMMENT: + if ((flags & 0x1000)!=0) { + try { + r=readString(r, f); + if(gheader!=null){ + gheader.comment=tmp_string.toByteArray(); + } + tmp_string=null; + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.comment=null; + } + this.mode = HCRC; + case HCRC: + if ((flags & 0x0200)!=0) { + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.hcrc=(int)(this.need&0xffff); + } + if(this.need != (z.adler.getValue()&0xffffL)){ + this.mode = BAD; + z.msg = "header crc mismatch"; + this.marker = 5; // can't try inflateSync + break; + } + } + z.adler = new CRC32(); + + this.mode = BLOCKS; + break; + default: + return Z_STREAM_ERROR; + } + } + } + + int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(z==null || (this.mode != DICT0 && this.wrap != 0)){ + return Z_STREAM_ERROR; + } + + int index=0; + int length = dictLength; + + if(this.mode==DICT0){ + long adler_need=z.adler.getValue(); + z.adler.reset(); + z.adler.update(dictionary, 0, dictLength); + if(z.adler.getValue()!=adler_need){ + return Z_DATA_ERROR; + } + } + + z.adler.reset(); + + if(length >= (1<0){ + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + this.need = this.need | + ((z.next_in[z.next_in_index++]&0xff)<<((n-need_bytes)*8)); + need_bytes--; + } + if(n==2){ + this.need&=0xffffL; + } + else if(n==4) { + this.need&=0xffffffffL; + } + need_bytes=-1; + return r; + } + class Return extends Exception{ + int r; + Return(int r){this.r=r; } + } + + private java.io.ByteArrayOutputStream tmp_string = null; + private int readString(int r, int f) throws Return{ + if(tmp_string == null){ + tmp_string=new java.io.ByteArrayOutputStream(); + } + int b=0; + do { + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + b = z.next_in[z.next_in_index]; + if(b!=0) tmp_string.write(z.next_in, z.next_in_index, 1); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + }while(b!=0); + return r; + } + + private int readBytes(int r, int f) throws Return{ + if(tmp_string == null){ + tmp_string=new java.io.ByteArrayOutputStream(); + } + int b=0; + while(this.need>0){ + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + b = z.next_in[z.next_in_index]; + tmp_string.write(z.next_in, z.next_in_index, 1); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + this.need--; + } + return r; + } + + private void checksum(int n, long v){ + for(int i=0; i>=8; + } + z.adler.update(crcbuf, 0, n); + } + + public GZIPHeader getGZIPHeader(){ + return gheader; + } + + boolean inParsingHeader(){ + switch(mode){ + case HEAD: + case DICT4: + case DICT3: + case DICT2: + case DICT1: + case FLAGS: + case TIME: + case OS: + case EXLEN: + case EXTRA: + case NAME: + case COMMENT: + case HCRC: + return true; + default: + return false; + } + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/Inflater.java b/service/src/main/java/com/jcraft/jzlib/Inflater.java new file mode 100644 index 0000000..0fb0b09 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/Inflater.java @@ -0,0 +1,168 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final public class Inflater extends ZStream{ + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_WBITS=MAX_WBITS; + + static final private int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + public Inflater() { + super(); + init(); + } + + public Inflater(JZlib.WrapperType wrapperType) throws GZIPException { + this(DEF_WBITS, wrapperType); + } + + public Inflater(int w, JZlib.WrapperType wrapperType) throws GZIPException { + super(); + int ret = init(w, wrapperType); + if(ret!=Z_OK) + throw new GZIPException(ret+": "+msg); + } + + public Inflater(int w) throws GZIPException { + this(w, false); + } + + public Inflater(boolean nowrap) throws GZIPException { + this(DEF_WBITS, nowrap); + } + + public Inflater(int w, boolean nowrap) throws GZIPException { + super(); + int ret = init(w, nowrap); + if(ret!=Z_OK) + throw new GZIPException(ret+": "+msg); + } + + private boolean finished = false; + + public int init(){ + return init(DEF_WBITS); + } + + public int init(JZlib.WrapperType wrapperType){ + return init(DEF_WBITS, wrapperType); + } + + public int init(int w, JZlib.WrapperType wrapperType) { + boolean nowrap = false; + if(wrapperType == JZlib.W_NONE){ + nowrap = true; + } + else if(wrapperType == JZlib.W_GZIP) { + w += 16; + } + else if(wrapperType == JZlib.W_ANY) { + w |= Inflate.INFLATE_ANY; + } + else if(wrapperType == JZlib.W_ZLIB) { + } + return init(w, nowrap); + } + + public int init(boolean nowrap){ + return init(DEF_WBITS, nowrap); + } + + public int init(int w){ + return init(w, false); + } + + public int init(int w, boolean nowrap){ + finished = false; + istate=new Inflate(this); + return istate.inflateInit(nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + int ret = istate.inflate(f); + if(ret == Z_STREAM_END) + finished = true; + return ret; + } + + public int end(){ + finished = true; + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(); +// istate = null; + return ret; + } + + public int sync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + + public int syncPoint(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + + public int setDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + + public boolean finished(){ + return istate.mode==12 /*DONE*/; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/InflaterInputStream.java b/service/src/main/java/com/jcraft/jzlib/InflaterInputStream.java new file mode 100644 index 0000000..0420582 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/InflaterInputStream.java @@ -0,0 +1,247 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jzlib; +import java.io.*; + +public class InflaterInputStream extends FilterInputStream { + protected final Inflater inflater; + protected byte[] buf; + + private boolean closed = false; + + private boolean eof = false; + + private boolean close_in = true; + + protected static final int DEFAULT_BUFSIZE = 512; + + public InflaterInputStream(InputStream in) throws IOException { + this(in, false); + } + + public InflaterInputStream(InputStream in, boolean nowrap) throws IOException { + this(in, new Inflater(nowrap)); + myinflater = true; + } + + public InflaterInputStream(InputStream in, Inflater inflater) throws IOException { + this(in, inflater, DEFAULT_BUFSIZE); + } + + public InflaterInputStream(InputStream in, + Inflater inflater, int size) throws IOException { + this(in, inflater, size, true); + } + + public InflaterInputStream(InputStream in, + Inflater inflater, + int size, boolean close_in) throws IOException { + super(in); + if (in == null || inflater == null) { + throw new NullPointerException(); + } + else if (size <= 0) { + throw new IllegalArgumentException("buffer size must be greater than 0"); + } + this.inflater = inflater; + buf = new byte[size]; + this.close_in = close_in; + } + + protected boolean myinflater = false; + + private byte[] byte1 = new byte[1]; + + public int read() throws IOException { + if (closed) { throw new IOException("Stream closed"); } + return read(byte1, 0, 1) == -1 ? -1 : byte1[0] & 0xff; + } + + public int read(byte[] b, int off, int len) throws IOException { + if (closed) { throw new IOException("Stream closed"); } + if (b == null) { + throw new NullPointerException(); + } + else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } + else if (len == 0) { + return 0; + } + else if (eof) { + return -1; + } + + int n = 0; + inflater.setOutput(b, off, len); + while(!eof) { + if(inflater.avail_in==0) + fill(); + int err = inflater.inflate(JZlib.Z_NO_FLUSH); + n += inflater.next_out_index - off; + off = inflater.next_out_index; + switch(err) { + case JZlib.Z_DATA_ERROR: + throw new IOException(inflater.msg); + case JZlib.Z_STREAM_END: + case JZlib.Z_NEED_DICT: + eof = true; + if(err == JZlib.Z_NEED_DICT) + return -1; + break; + default: + } + if(inflater.avail_out==0) + break; + } + return n; + } + + public int available() throws IOException { + if (closed) { throw new IOException("Stream closed"); } + if (eof) { + return 0; + } + else { + return 1; + } + } + + private byte[] b = new byte[512]; + + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("negative skip length"); + } + + if (closed) { throw new IOException("Stream closed"); } + + int max = (int)Math.min(n, Integer.MAX_VALUE); + int total = 0; + while (total < max) { + int len = max - total; + if (len > b.length) { + len = b.length; + } + len = read(b, 0, len); + if (len == -1) { + eof = true; + break; + } + total += len; + } + return total; + } + + public void close() throws IOException { + if (!closed) { + if (myinflater) + inflater.end(); + if(close_in) + in.close(); + closed = true; + } + } + + protected void fill() throws IOException { + if (closed) { throw new IOException("Stream closed"); } + int len = in.read(buf, 0, buf.length); + if (len == -1) { + if(inflater.istate.wrap == 0 && + !inflater.finished()){ + buf[0]=0; + len=1; + } + else if(inflater.istate.was != -1){ // in reading trailer + throw new IOException("footer is not found"); + } + else{ + throw new EOFException("Unexpected end of ZLIB input stream"); + } + } + inflater.setInput(buf, 0, len, true); + } + + public boolean markSupported() { + return false; + } + + public synchronized void mark(int readlimit) { + } + + public synchronized void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } + + public long getTotalIn() { + return inflater.getTotalIn(); + } + + public long getTotalOut() { + return inflater.getTotalOut(); + } + + public byte[] getAvailIn() { + if(inflater.avail_in<=0) + return null; + byte[] tmp = new byte[inflater.avail_in]; + System.arraycopy(inflater.next_in, inflater.next_in_index, + tmp, 0, inflater.avail_in); + return tmp; + } + + public void readHeader() throws IOException { + + byte[] empty = "".getBytes(); + inflater.setInput(empty, 0, 0, false); + inflater.setOutput(empty, 0, 0); + + int err = inflater.inflate(JZlib.Z_NO_FLUSH); + if(!inflater.istate.inParsingHeader()){ + return; + } + + byte[] b1 = new byte[1]; + do{ + int i = in.read(b1); + if(i<=0) + throw new IOException("no input"); + inflater.setInput(b1); + err = inflater.inflate(JZlib.Z_NO_FLUSH); + if(err!=0/*Z_OK*/) + throw new IOException(inflater.msg); + } + while(inflater.istate.inParsingHeader()); + } + + public Inflater getInflater(){ + return inflater; + } +} \ No newline at end of file diff --git a/service/src/main/java/com/jcraft/jzlib/JZlib.java b/service/src/main/java/com/jcraft/jzlib/JZlib.java new file mode 100644 index 0000000..a4bb341 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/JZlib.java @@ -0,0 +1,92 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final public class JZlib{ + private static final String version="1.1.0"; + public static String version(){return version;} + + static final public int MAX_WBITS=15; // 32K LZ77 window + static final public int DEF_WBITS=MAX_WBITS; + + public enum WrapperType { + NONE, ZLIB, GZIP, ANY + } + + public static final WrapperType W_NONE = WrapperType.NONE; + public static final WrapperType W_ZLIB = WrapperType.ZLIB; + public static final WrapperType W_GZIP = WrapperType.GZIP; + public static final WrapperType W_ANY = WrapperType.ANY; + + // compression levels + static final public int Z_NO_COMPRESSION=0; + static final public int Z_BEST_SPEED=1; + static final public int Z_BEST_COMPRESSION=9; + static final public int Z_DEFAULT_COMPRESSION=(-1); + + // compression strategy + static final public int Z_FILTERED=1; + static final public int Z_HUFFMAN_ONLY=2; + static final public int Z_DEFAULT_STRATEGY=0; + + static final public int Z_NO_FLUSH=0; + static final public int Z_PARTIAL_FLUSH=1; + static final public int Z_SYNC_FLUSH=2; + static final public int Z_FULL_FLUSH=3; + static final public int Z_FINISH=4; + + static final public int Z_OK=0; + static final public int Z_STREAM_END=1; + static final public int Z_NEED_DICT=2; + static final public int Z_ERRNO=-1; + static final public int Z_STREAM_ERROR=-2; + static final public int Z_DATA_ERROR=-3; + static final public int Z_MEM_ERROR=-4; + static final public int Z_BUF_ERROR=-5; + static final public int Z_VERSION_ERROR=-6; + + // The three kinds of block type + static final public byte Z_BINARY = 0; + static final public byte Z_ASCII = 1; + static final public byte Z_UNKNOWN = 2; + + public static long adler32_combine(long adler1, long adler2, long len2){ + return Adler32.combine(adler1, adler2, len2); + } + + public static long crc32_combine(long crc1, long crc2, long len2){ + return CRC32.combine(crc1, crc2, len2); + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/StaticTree.java b/service/src/main/java/com/jcraft/jzlib/StaticTree.java new file mode 100644 index 0000000..e35931c --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/StaticTree.java @@ -0,0 +1,148 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final class StaticTree{ + static final private int MAX_BITS=15; + + static final private int BL_CODES=19; + static final private int D_CODES=30; + static final private int LITERALS=256; + static final private int LENGTH_CODES=29; + static final private int L_CODES=(LITERALS+1+LENGTH_CODES); + + // Bit length codes must not exceed MAX_BL_BITS bits + static final int MAX_BL_BITS=7; + + static final short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + static final short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + + static StaticTree static_l_desc = + new StaticTree(static_ltree, Tree.extra_lbits, + LITERALS+1, L_CODES, MAX_BITS); + + static StaticTree static_d_desc = + new StaticTree(static_dtree, Tree.extra_dbits, + 0, D_CODES, MAX_BITS); + + static StaticTree static_bl_desc = + new StaticTree(null, Tree.extra_blbits, + 0, BL_CODES, MAX_BL_BITS); + + short[] static_tree; // static tree or null + int[] extra_bits; // extra bits for each code or null + int extra_base; // base index for extra_bits + int elems; // max number of elements in the tree + int max_length; // max bit length for the codes + + private StaticTree(short[] static_tree, + int[] extra_bits, + int extra_base, + int elems, + int max_length){ + this.static_tree=static_tree; + this.extra_bits=extra_bits; + this.extra_base=extra_base; + this.elems=elems; + this.max_length=max_length; + } +} diff --git a/service/src/main/java/com/jcraft/jzlib/Tree.java b/service/src/main/java/com/jcraft/jzlib/Tree.java new file mode 100644 index 0000000..38cb40f --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/Tree.java @@ -0,0 +1,367 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +final class Tree{ + static final private int MAX_BITS=15; + static final private int BL_CODES=19; + static final private int D_CODES=30; + static final private int LITERALS=256; + static final private int LENGTH_CODES=29; + static final private int L_CODES=(LITERALS+1+LENGTH_CODES); + static final private int HEAP_SIZE=(2*L_CODES+1); + + // Bit length codes must not exceed MAX_BL_BITS bits + static final int MAX_BL_BITS=7; + + // end of block literal code + static final int END_BLOCK=256; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + static final int REP_3_6=16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + static final int REPZ_3_10=17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + static final int REPZ_11_138=18; + + // extra bits for each length code + static final int[] extra_lbits={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + static final int[] extra_dbits={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + static final int[] extra_blbits={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + static final byte[] bl_order={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit + // length codes. + + static final int Buf_size=8*2; + + // see definition of array dist_code below + static final int DIST_CODE_LEN=512; + + static final byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + static final byte[] _length_code={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + static final int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + static final int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // Mapping from a distance to a distance code. dist is the distance - 1 and + // must not have side effects. _dist_code[256] and _dist_code[257] are never + // used. + static int d_code(int dist){ + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>>7)]); + } + + short[] dyn_tree; // the dynamic tree + int max_code; // largest code with non zero frequency + StaticTree stat_desc; // the corresponding static tree + + // Compute the optimal bit lengths for a tree and update the total bit length + // for the current block. + // IN assertion: the fields freq and dad are set, heap[heap_max] and + // above are the tree nodes sorted by increasing frequency. + // OUT assertions: the field len is set to the optimal bit length, the + // array bl_count contains the frequencies for each bit length. + // The length opt_len is updated; static_len is also updated if stree is + // not null. + void gen_bitlen(Deflate s){ + short[] tree = dyn_tree; + short[] stree = stat_desc.static_tree; + int[] extra = stat_desc.extra_bits; + int base = stat_desc.extra_base; + int max_length = stat_desc.max_length; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max]*2+1] = 0; // root of the heap + + for(h=s.heap_max+1; h max_length){ bits = max_length; overflow++; } + tree[n*2+1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > max_code) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n*2]; + s.opt_len += f * (bits + xbits); + if (stree!=null) s.static_len += f * (stree[n*2+1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do { + bits = max_length-1; + while(s.bl_count[bits]==0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits+1]+=2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) { + n = s.bl_count[bits]; + while (n != 0) { + m = s.heap[--h]; + if (m > max_code) continue; + if (tree[m*2+1] != bits) { + s.opt_len += ((long)bits - (long)tree[m*2+1])*(long)tree[m*2]; + tree[m*2+1] = (short)bits; + } + n--; + } + } + } + + // Construct one Huffman tree and assigns the code bit strings and lengths. + // Update the total bit length for the current block. + // IN assertion: the field freq is set for all tree elements. + // OUT assertions: the fields len and code are set to the optimal bit length + // and corresponding code. The length opt_len is updated; static_len is + // also updated if stree is not null. The field max_code is set. + void build_tree(Deflate s){ + short[] tree=dyn_tree; + short[] stree=stat_desc.static_tree; + int elems=stat_desc.elems; + int n, m; // iterate over heap elements + int max_code=-1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for(n=0; n=1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node=elems; // next internal node of the tree + do{ + // n = node of least frequency + n=s.heap[1]; + s.heap[1]=s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m=s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node*2] = (short)(tree[n*2] + tree[m*2]); + s.depth[node] = (byte)(Math.max(s.depth[n],s.depth[m])+1); + tree[n*2+1] = tree[m*2+1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while(s.heap_len>=2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + gen_bitlen(s); + + // The field len is now set, we can generate the bit codes + gen_codes(tree, max_code, s.bl_count, s.next_code); + } + + // Generate the codes for a given tree and bit counts (which need not be + // optimal). + // IN assertion: the array bl_count contains the bit length statistics for + // the given tree and the field len is set for all tree elements. + // OUT assertion: the field code is set for all tree elements of non + // zero code length. + private final static void gen_codes( + short[] tree, // the tree to decorate + int max_code, // largest code with non zero frequency + short[] bl_count, // number of codes at each bit length + short[] next_code){ + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + next_code[0]=0; + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (short)((code + bl_count[bits-1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1<>>=1; + res<<=1; + } + while(--len>0); + return res>>>1; + } +} + diff --git a/service/src/main/java/com/jcraft/jzlib/ZInputStream.java b/service/src/main/java/com/jcraft/jzlib/ZInputStream.java new file mode 100644 index 0000000..cbd38e1 --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/ZInputStream.java @@ -0,0 +1,126 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jzlib; +import java.io.*; + +/** + * ZInputStream + * + * @deprecated use DeflaterOutputStream or InflaterInputStream + */ +@Deprecated +public class ZInputStream extends FilterInputStream { + + protected int flush=JZlib.Z_NO_FLUSH; + protected boolean compress; + protected InputStream in=null; + + protected Deflater deflater; + protected InflaterInputStream iis; + + public ZInputStream(InputStream in) throws IOException { + this(in, false); + } + public ZInputStream(InputStream in, boolean nowrap) throws IOException { + super(in); + iis = new InflaterInputStream(in, nowrap); + compress=false; + } + + public ZInputStream(InputStream in, int level) throws IOException { + super(in); + this.in=in; + deflater = new Deflater(); + deflater.init(level); + compress=true; + } + + private byte[] buf1 = new byte[1]; + public int read() throws IOException { + if(read(buf1, 0, 1)==-1) return -1; + return(buf1[0]&0xFF); + } + + private byte[] buf = new byte[512]; + + public int read(byte[] b, int off, int len) throws IOException { + if(compress){ + deflater.setOutput(b, off, len); + while(true){ + int datalen = in.read(buf, 0, buf.length); + if(datalen == -1) return -1; + deflater.setInput(buf, 0, datalen, true); + int err = deflater.deflate(flush); + if(deflater.next_out_index>0) + return deflater.next_out_index; + if(err == JZlib.Z_STREAM_END) + return 0; + if(err == JZlib.Z_STREAM_ERROR || + err == JZlib.Z_DATA_ERROR){ + throw new ZStreamException("deflating: "+deflater.msg); + } + } + } + else{ + return iis.read(b, off, len); + } + } + + public long skip(long n) throws IOException { + int len=512; + if(n0){ + inflater.setOutput(buf, 0, buf.length); + err = inflater.inflate(flush); + if(inflater.next_out_index>0) + out.write(buf, 0, inflater.next_out_index); + if(err != JZlib.Z_OK) + break; + } + if(err != JZlib.Z_OK) + throw new ZStreamException("inflating: "+inflater.msg); + return; + } + } + + public int getFlushMode() { + return flush; + } + + public void setFlushMode(int flush) { + this.flush=flush; + } + + public void finish() throws IOException { + int err; + if(compress){ + int tmp = flush; + int flush = JZlib.Z_FINISH; + try{ + write("".getBytes(), 0, 0); + } + finally { flush = tmp; } + } + else{ + dos.finish(); + } + flush(); + } + public synchronized void end() { + if(end) return; + if(compress){ + try { dos.finish(); } catch(Exception e){} + } + else{ + inflater.end(); + } + end=true; + } + public void close() throws IOException { + try{ + try{finish();} + catch (IOException ignored) {} + } + finally{ + end(); + out.close(); + out=null; + } + } + + public long getTotalIn() { + if(compress) return dos.getTotalIn(); + else return inflater.total_in; + } + + public long getTotalOut() { + if(compress) return dos.getTotalOut(); + else return inflater.total_out; + } + + public void flush() throws IOException { + out.flush(); + } + +} diff --git a/service/src/main/java/com/jcraft/jzlib/ZStream.java b/service/src/main/java/com/jcraft/jzlib/ZStream.java new file mode 100644 index 0000000..0afa4fd --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/ZStream.java @@ -0,0 +1,377 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +/** + * ZStream + * + * @deprecated Not for public use in the future. + */ +@Deprecated +public class ZStream{ + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_WBITS=MAX_WBITS; + + static final private int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + public byte[] next_in; // next input byte + public int next_in_index; + public int avail_in; // number of bytes available at next_in + public long total_in; // total nb of input bytes read so far + + public byte[] next_out; // next output byte should be put there + public int next_out_index; + public int avail_out; // remaining free space at next_out + public long total_out; // total nb of bytes output so far + + public String msg; + + Deflate dstate; + Inflate istate; + + int data_type; // best guess about the data type: ascii or binary + + Checksum adler; + + public ZStream(){ + this(new Adler32()); + } + + public ZStream(Checksum adler){ + this.adler=adler; + } + + public int inflateInit(){ + return inflateInit(DEF_WBITS); + } + public int inflateInit(boolean nowrap){ + return inflateInit(DEF_WBITS, nowrap); + } + public int inflateInit(int w){ + return inflateInit(w, false); + } + public int inflateInit(JZlib.WrapperType wrapperType) { + return inflateInit(DEF_WBITS, wrapperType); + } + public int inflateInit(int w, JZlib.WrapperType wrapperType) { + boolean nowrap = false; + if(wrapperType == JZlib.W_NONE){ + nowrap = true; + } + else if(wrapperType == JZlib.W_GZIP) { + w += 16; + } + else if(wrapperType == JZlib.W_ANY) { + w |= Inflate.INFLATE_ANY; + } + else if(wrapperType == JZlib.W_ZLIB) { + } + return inflateInit(w, nowrap); + } + public int inflateInit(int w, boolean nowrap){ + istate=new Inflate(this); + return istate.inflateInit(nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + return istate.inflate(f); + } + public int inflateEnd(){ + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(); +// istate = null; + return ret; + } + public int inflateSync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + public int inflateSyncPoint(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + public int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + public boolean inflateFinished(){ + return istate.mode==12 /*DONE*/; + } + + public int deflateInit(int level){ + return deflateInit(level, MAX_WBITS); + } + public int deflateInit(int level, boolean nowrap){ + return deflateInit(level, MAX_WBITS, nowrap); + } + public int deflateInit(int level, int bits){ + return deflateInit(level, bits, false); + } + public int deflateInit(int level, int bits, int memlevel, JZlib.WrapperType wrapperType){ + if(bits < 9 || bits > 15){ + return Z_STREAM_ERROR; + } + if(wrapperType == JZlib.W_NONE) { + bits *= -1; + } + else if(wrapperType == JZlib.W_GZIP) { + bits += 16; + } + else if(wrapperType == JZlib.W_ANY) { + return Z_STREAM_ERROR; + } + else if(wrapperType == JZlib.W_ZLIB) { + } + return this.deflateInit(level, bits, memlevel); + } + public int deflateInit(int level, int bits, int memlevel){ + dstate=new Deflate(this); + return dstate.deflateInit(level, bits, memlevel); + } + public int deflateInit(int level, int bits, boolean nowrap){ + dstate=new Deflate(this); + return dstate.deflateInit(level, nowrap?-bits:bits); + } + public int deflate(int flush){ + if(dstate==null){ + return Z_STREAM_ERROR; + } + return dstate.deflate(flush); + } + public int deflateEnd(){ + if(dstate==null) return Z_STREAM_ERROR; + int ret=dstate.deflateEnd(); + dstate=null; + return ret; + } + public int deflateParams(int level, int strategy){ + if(dstate==null) return Z_STREAM_ERROR; + return dstate.deflateParams(level, strategy); + } + public int deflateSetDictionary (byte[] dictionary, int dictLength){ + if(dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateSetDictionary(dictionary, dictLength); + } + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + void flush_pending(){ + int len=dstate.pending; + + if(len>avail_out) len=avail_out; + if(len==0) return; + + if(dstate.pending_buf.length<=dstate.pending_out || + next_out.length<=next_out_index || + dstate.pending_buf.length<(dstate.pending_out+len) || + next_out.length<(next_out_index+len)){ + //System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + //System.out.println("avail_out="+avail_out); + } + + System.arraycopy(dstate.pending_buf, dstate.pending_out, + next_out, next_out_index, len); + + next_out_index+=len; + dstate.pending_out+=len; + total_out+=len; + avail_out-=len; + dstate.pending-=len; + if(dstate.pending==0){ + dstate.pending_out=0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + int read_buf(byte[] buf, int start, int size) { + int len=avail_in; + + if(len>size) len=size; + if(len==0) return 0; + + avail_in-=len; + + if(dstate.wrap!=0) { + adler.update(next_in, next_in_index, len); + } + System.arraycopy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public long getAdler(){ + return adler.getValue(); + } + + public void free(){ + next_in=null; + next_out=null; + msg=null; + } + + public void setOutput(byte[] buf){ + setOutput(buf, 0, buf.length); + } + + public void setOutput(byte[] buf, int off, int len){ + next_out = buf; + next_out_index = off; + avail_out = len; + } + + public void setInput(byte[] buf){ + setInput(buf, 0, buf.length, false); + } + + public void setInput(byte[] buf, boolean append){ + setInput(buf, 0, buf.length, append); + } + + public void setInput(byte[] buf, int off, int len, boolean append){ + if(len<=0 && append && next_in!=null) return; + + if(avail_in>0 && append){ + byte[] tmp = new byte[avail_in+len]; + System.arraycopy(next_in, next_in_index, tmp, 0, avail_in); + System.arraycopy(buf, off, tmp, avail_in, len); + next_in=tmp; + next_in_index=0; + avail_in+=len; + } + else{ + next_in=buf; + next_in_index=off; + avail_in=len; + } + } + + public byte[] getNextIn(){ + return next_in; + } + + public void setNextIn(byte[] next_in){ + this.next_in = next_in; + } + + public int getNextInIndex(){ + return next_in_index; + } + + public void setNextInIndex(int next_in_index){ + this.next_in_index = next_in_index; + } + + public int getAvailIn(){ + return avail_in; + } + + public void setAvailIn(int avail_in){ + this.avail_in = avail_in; + } + + public byte[] getNextOut(){ + return next_out; + } + + public void setNextOut(byte[] next_out){ + this.next_out = next_out; + } + + public int getNextOutIndex(){ + return next_out_index; + } + + public void setNextOutIndex(int next_out_index){ + this.next_out_index = next_out_index; + } + + public int getAvailOut(){ + return avail_out; + + } + + public void setAvailOut(int avail_out){ + this.avail_out = avail_out; + } + + public long getTotalOut(){ + return total_out; + } + + public long getTotalIn(){ + return total_in; + } + + public String getMessage(){ + return msg; + } + + /** + * Those methods are expected to be override by Inflater and Deflater. + * In the future, they will become abstract methods. + */ + public int end(){ return Z_OK; } + public boolean finished(){ return false; } +} diff --git a/service/src/main/java/com/jcraft/jzlib/ZStreamException.java b/service/src/main/java/com/jcraft/jzlib/ZStreamException.java new file mode 100644 index 0000000..308bb8a --- /dev/null +++ b/service/src/main/java/com/jcraft/jzlib/ZStreamException.java @@ -0,0 +1,44 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package com.jcraft.jzlib; + +public class ZStreamException extends java.io.IOException { + public ZStreamException() { + super(); + } + public ZStreamException(String s) { + super(s); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/ChannelCondition.java b/service/src/main/java/com/trilead/ssh2/ChannelCondition.java new file mode 100644 index 0000000..711e47b --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/ChannelCondition.java @@ -0,0 +1,61 @@ + +package com.trilead.ssh2; + +/** + * Contains constants that can be used to specify what conditions to wait for on + * a SSH-2 channel (e.g., represented by a {@link Session}). + * + * @see Session#waitForCondition(int, long) + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ChannelCondition.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public abstract interface ChannelCondition +{ + /** + * A timeout has occurred, none of your requested conditions is fulfilled. + * However, other conditions may be true - therefore, NEVER use the "==" + * operator to test for this (or any other) condition. Always use + * something like ((cond & ChannelCondition.CLOSED) != 0). + */ + public static final int TIMEOUT = 1; + + /** + * The underlying SSH-2 channel, however not necessarily the whole connection, + * has been closed. This implies EOF. Note that there may still + * be unread stdout or stderr data in the local window, i.e, STDOUT_DATA + * or/and STDERR_DATA may be set at the same time. + */ + public static final int CLOSED = 2; + + /** + * There is stdout data available that is ready to be consumed. + */ + public static final int STDOUT_DATA = 4; + + /** + * There is stderr data available that is ready to be consumed. + */ + public static final int STDERR_DATA = 8; + + /** + * EOF on has been reached, no more _new_ stdout or stderr data will arrive + * from the remote server. However, there may be unread stdout or stderr + * data, i.e, STDOUT_DATA or/and STDERR_DATA + * may be set at the same time. + */ + public static final int EOF = 16; + + /** + * The exit status of the remote process is available. + * Some servers never send the exist status, or occasionally "forget" to do so. + */ + public static final int EXIT_STATUS = 32; + + /** + * The exit signal of the remote process is available. + */ + public static final int EXIT_SIGNAL = 64; + +} diff --git a/service/src/main/java/com/trilead/ssh2/Connection.java b/service/src/main/java/com/trilead/ssh2/Connection.java new file mode 100644 index 0000000..cbfc69e --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/Connection.java @@ -0,0 +1,1634 @@ +package com.trilead.ssh2; + +import com.trilead.ssh2.auth.AgentProxy; +import com.trilead.ssh2.auth.AuthenticationManager; +import com.trilead.ssh2.channel.ChannelManager; +import com.trilead.ssh2.crypto.CryptoWishList; +import com.trilead.ssh2.crypto.cipher.BlockCipherFactory; +import com.trilead.ssh2.crypto.digest.MessageMac; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.PacketIgnore; +import com.trilead.ssh2.transport.ClientServerHello; +import com.trilead.ssh2.transport.KexManager; +import com.trilead.ssh2.transport.TransportManager; +import com.trilead.ssh2.util.TimeoutService; +import com.trilead.ssh2.util.TimeoutService.TimeoutToken; + +import java.io.CharArrayWriter; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.security.SecureRandom; +import java.util.Vector; + +/** + * A Connection is used to establish an encrypted TCP/IP + * connection to a SSH-2 server. + *

+ * Typically, one + *

    + *
  1. creates a {@link #Connection(String) Connection} object.
  2. + *
  3. calls the {@link #connect() connect()} method.
  4. + *
  5. calls some of the authentication methods (e.g., + * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).
  6. + *
  7. calls one or several times the {@link #openSession() openSession()} + * method.
  8. + *
  9. finally, one must close the connection and release resources with the + * {@link #close() close()} method.
  10. + *
+ * + * @author Christian Plattner, plattner@trilead.com + * @version $Id : Connection.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $ + */ +public class Connection +{ + /** + * The identifier presented to the SSH-2 server. + */ + public final static String identification = "TrileadSSH2Java_213"; + + /** + * Will be used to generate all random data needed for the current + * connection. Note: SecureRandom.nextBytes() is thread safe. + */ + private SecureRandom generator; + + /** + * Unless you know what you are doing, you will never need this. + * + * @return The list of supported cipher algorithms by this implementation. + */ + public static synchronized String[] getAvailableCiphers() + { + return BlockCipherFactory.getDefaultCipherList(); + } + + /** + * Unless you know what you are doing, you will never need this. + * + * @return The list of supported MAC algorthims by this implementation. + */ + public static synchronized String[] getAvailableMACs() + { + return MessageMac.getMacs(); + } + + /** + * Unless you know what you are doing, you will never need this. + * + * @return The list of supported server host key algorthims by this implementation. + */ + public static synchronized String[] getAvailableServerHostKeyAlgorithms() + { + return KexManager.getDefaultServerHostkeyAlgorithmList(); + } + + private static final Logger log = Logger.getLogger(Connection.class); + + private AuthenticationManager am; + + private boolean authenticated = false; + private ChannelManager cm; + + private CryptoWishList cryptoWishList = new CryptoWishList(); + + private DHGexParameters dhgexpara = new DHGexParameters(); + + private final String hostname; + private final String sourceAddress; + + private final int port; + + private TransportManager tm; + + private boolean tcpNoDelay = false; + + private ProxyData proxyData = null; + + private boolean compression; + + private Vector connectionMonitors = new Vector<>(); + + /** + * Prepares a fresh Connection object which can then be used + * to establish a connection to the specified SSH-2 server. + *

+ * Same as {@link #Connection(String, int) Connection(hostname, 22)}. + * + * @param hostname the hostname of the SSH-2 server. + */ + public Connection(String hostname) + { + this(hostname, 22); + } + + /** + * Prepares a fresh Connection object which can then be used + * to establish a connection to the specified SSH-2 server. + * + * @param hostname the host where we later want to connect to. + * @param port port on the server, normally 22. + */ + public Connection(String hostname, int port) + { + this(hostname, port, null); + } + + /** + * Prepares a fresh Connection object which can then be used + * to establish a connection to the specified SSH-2 server. + * + * @param hostname the host where we later want to connect to. + * @param port port on the server, normally 22. + * @param sourceAddress the source address + */ + public Connection(String hostname, int port, String sourceAddress) + { + this.hostname = hostname; + this.port = port; + this.sourceAddress = sourceAddress; + } + + /** + * After a successful connect, one has to authenticate oneself. This method + * is based on DSA (it uses DSA to sign a challenge sent by the server). + *

+ * If the authentication phase is complete, true will be + * returned. If the server does not accept the request (or if further + * authentication steps are needed), false is returned and + * one can retry either by using this or any other authentication method + * (use the getRemainingAuthMethods method to get a list of + * the remaining possible methods). + * + * @param user A String holding the username. + * @param pem A String containing the DSA private key of the user in OpenSSH key format (PEM, you can't miss the "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain linefeeds. + * @param password If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you must specify the password. Otherwise, this argument will be ignored and can be set to null. + * @return whether the connection is now authenticated. + * @throws IOException the io exception + * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()} methods, this method is just a wrapper for it and will disappear in future builds. + */ + public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException + { + if (tm == null) + throw new IllegalStateException("Connection is not established!"); + + if (authenticated) + throw new IllegalStateException("Connection is already authenticated!"); + + if (am == null) { + am = new AuthenticationManager(tm); + am.setConnectionMonitors(connectionMonitors); + } + + if (cm == null) + cm = new ChannelManager(tm); + + if (user == null) + throw new IllegalArgumentException("user argument is null"); + + if (pem == null) + throw new IllegalArgumentException("pem argument is null"); + + authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND()); + + return authenticated; + } + + /** + * A wrapper that calls + * {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback) + * authenticateWithKeyboardInteractivewith}* a null submethod + * list. + * + * @param user A String holding the username. + * @param cb An InteractiveCallback which will be used to determine the responses to the questions asked by the server. + * @return whether the connection is now authenticated. + * @throws IOException the io exception + */ + public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb) + throws IOException + { + return authenticateWithKeyboardInteractive(user, null, cb); + } + + /** + * Authenticate with agent boolean. + * + * @param user the user + * @param proxy the proxy + * @return the boolean + * @throws IOException the io exception + */ + public synchronized boolean authenticateWithAgent(String user, AgentProxy proxy) throws IOException { + if (tm == null) + throw new IllegalStateException("Connection is not established!"); + + if (authenticated) + throw new IllegalStateException("Connection is already authenticated!"); + + if (am == null) { + am = new AuthenticationManager(tm); + am.setConnectionMonitors(connectionMonitors); + } + + if (cm == null) + cm = new ChannelManager(tm); + + if (user == null) + throw new IllegalArgumentException("user argument is null"); + + authenticated = am.authenticatePublicKey(user, proxy); + + return authenticated; + } + + /** + * After a successful connect, one has to authenticate oneself. This method + * is based on "keyboard-interactive", specified in + * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a + * callback object which will be feeded with challenges generated by the + * server. Answers are then sent back to the server. It is possible that the + * callback will be called several times during the invocation of this + * method (e.g., if the server replies to the callback's answer(s) with + * another challenge...) + *

+ * If the authentication phase is complete, true will be + * returned. If the server does not accept the request (or if further + * authentication steps are needed), false is returned and + * one can retry either by using this or any other authentication method + * (use the getRemainingAuthMethods method to get a list of + * the remaining possible methods). + *

+ * Note: some SSH servers advertise "keyboard-interactive", however, any + * interactive request will be denied (without having sent any challenge to + * the client). + * + * @param user A String holding the username. + * @param submethods An array of submethod names, see draft-ietf-secsh-auth-kbdinteract-XX. May be null to indicate an empty list. + * @param cb An InteractiveCallback which will be used to determine the responses to the questions asked by the server. + * @return whether the connection is now authenticated. + * @throws IOException the io exception + */ + public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods, + InteractiveCallback cb) throws IOException + { + if (cb == null) + throw new IllegalArgumentException("Callback may not ne NULL!"); + + if (tm == null) + throw new IllegalStateException("Connection is not established!"); + + if (authenticated) + throw new IllegalStateException("Connection is already authenticated!"); + + if (am == null) { + am = new AuthenticationManager(tm); + am.setConnectionMonitors(connectionMonitors); + } + + if (cm == null) + cm = new ChannelManager(tm); + + if (user == null) + throw new IllegalArgumentException("user argument is null"); + + authenticated = am.authenticateInteractive(user, submethods, cb); + + return authenticated; + } + + /** + * After a successful connect, one has to authenticate oneself. This method + * sends username and password to the server. + *

+ * If the authentication phase is complete, true will be + * returned. If the server does not accept the request (or if further + * authentication steps are needed), false is returned and + * one can retry either by using this or any other authentication method + * (use the getRemainingAuthMethods method to get a list of + * the remaining possible methods). + *

+ * Note: if this method fails, then please double-check that it is actually + * offered by the server (use + * {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}. + *

+ * Often, password authentication is disabled, but users are not aware of + * it. Many servers only offer "publickey" and "keyboard-interactive". + * However, even though "keyboard-interactive" *feels* like password + * authentication (e.g., when using the putty or openssh clients) it is + * *not* the same mechanism. + * + * @param user the user + * @param password the password + * @return if the connection is now authenticated. + * @throws IOException the io exception + */ + public synchronized boolean authenticateWithPassword(String user, String password) throws IOException + { + if (tm == null) + throw new IllegalStateException("Connection is not established!"); + + if (authenticated) + throw new IllegalStateException("Connection is already authenticated!"); + + if (am == null) { + am = new AuthenticationManager(tm); + am.setConnectionMonitors(connectionMonitors); + } + + if (cm == null) + cm = new ChannelManager(tm); + + if (user == null) + throw new IllegalArgumentException("user argument is null"); + + if (password == null) + throw new IllegalArgumentException("password argument is null"); + + authenticated = am.authenticatePassword(user, password); + + return authenticated; + } + + /** + * After a successful connect, one has to authenticate oneself. This method + * can be used to explicitly use the special "none" authentication method + * (where only a username has to be specified). + *

+ * Note 1: The "none" method may always be tried by clients, however as by + * the specs, the server will not explicitly announce it. In other words, + * the "none" token will never show up in the list returned by + * {@link #getRemainingAuthMethods(String)}. + *

+ * Note 2: no matter which one of the authenticateWithXXX() methods you + * call, the library will always issue exactly one initial "none" + * authentication request to retrieve the initially allowed list of + * authentication methods by the server. Please read RFC 4252 for the + * details. + *

+ * If the authentication phase is complete, true will be + * returned. If further authentication steps are needed, false + * is returned and one can retry by any other authentication method (use the + * getRemainingAuthMethods method to get a list of the + * remaining possible methods). + * + * @param user the user + * @return if the connection is now authenticated. + * @throws IOException the io exception + */ + public synchronized boolean authenticateWithNone(String user) throws IOException + { + if (tm == null) + throw new IllegalStateException("Connection is not established!"); + + if (authenticated) + throw new IllegalStateException("Connection is already authenticated!"); + + if (am == null) { + am = new AuthenticationManager(tm); + am.setConnectionMonitors(connectionMonitors); + } + + if (cm == null) + cm = new ChannelManager(tm); + + if (user == null) + throw new IllegalArgumentException("user argument is null"); + + /* Trigger the sending of the PacketUserauthRequestNone packet */ + /* (if not already done) */ + + authenticated = am.authenticateNone(user); + + return authenticated; + } + + /** + * After a successful connect, one has to authenticate oneself. The + * authentication method "publickey" works by signing a challenge sent by + * the server. The signature is either DSA or RSA based - it just depends on + * the type of private key you specify, either a DSA or RSA private key in + * PEM format. And yes, this is may seem to be a little confusing, the + * method is called "publickey" in the SSH-2 protocol specification, however + * since we need to generate a signature, you actually have to supply a + * private key =). + *

+ * The private key contained in the PEM file may also be encrypted + * ("Proc-Type: 4,ENCRYPTED"). The library supports DES-CBC and DES-EDE3-CBC + * encryption, as well as the more exotic PEM encrpytions AES-128-CBC, + * AES-192-CBC and AES-256-CBC. + *

+ * If the authentication phase is complete, true will be + * returned. If the server does not accept the request (or if further + * authentication steps are needed), false is returned and + * one can retry either by using this or any other authentication method + * (use the getRemainingAuthMethods method to get a list of + * the remaining possible methods). + *

+ * NOTE PUTTY USERS: Event though your key file may start with + * "-----BEGIN..." it is not in the expected format. You have to convert it + * to the OpenSSH key format by using the "puttygen" tool (can be downloaded + * from the Putty website). Simply load your key and then use the + * "Conversions/Export OpenSSH key" functionality to get a proper PEM file. + * + * @param user A String holding the username. + * @param pemPrivateKey A char[] containing a DSA or RSA private key of the user in OpenSSH key format (PEM, you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----" tag). The char array may contain linebreaks/linefeeds. + * @param password If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then you must specify a password. Otherwise, this argument will be ignored and can be set to null. + * @return whether the connection is now authenticated. + * @throws IOException the io exceptionn + */ + public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password) + throws IOException + { + if (tm == null) + throw new IllegalStateException("Connection is not established!"); + + if (authenticated) + throw new IllegalStateException("Connection is already authenticated!"); + + if (am == null) { + am = new AuthenticationManager(tm); + am.setConnectionMonitors(connectionMonitors); + } + + if (cm == null) + cm = new ChannelManager(tm); + + if (user == null) + throw new IllegalArgumentException("user argument is null"); + + if (pemPrivateKey == null) + throw new IllegalArgumentException("pemPrivateKey argument is null"); + + authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND()); + + return authenticated; + } + + /** + * A convenience wrapper function which reads in a private key (PEM format, + * either DSA or RSA) and then calls + * authenticateWithPublicKey(String, char[], String). + *

+ * NOTE PUTTY USERS: Event though your key file may start with + * "-----BEGIN..." it is not in the expected format. You have to convert it + * to the OpenSSH key format by using the "puttygen" tool (can be downloaded + * from the Putty website). Simply load your key and then use the + * "Conversions/Export OpenSSH key" functionality to get a proper PEM file. + * + * @param user A String holding the username. + * @param pemFile A File object pointing to a file containing a DSA or RSA private key of the user in OpenSSH key format (PEM, you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----" tag). + * @param password If the PEM file is encrypted then you must specify the password. Otherwise, this argument will be ignored and can be set to null. + * @return whether the connection is now authenticated. + * @throws IOException the io exception + */ + public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password) + throws IOException + { + if (pemFile == null) + throw new IllegalArgumentException("pemFile argument is null"); + + char[] buff = new char[256]; + + CharArrayWriter cw = new CharArrayWriter(); + + FileReader fr = new FileReader(pemFile); + + while (true) + { + int len = fr.read(buff); + if (len < 0) + break; + cw.write(buff, 0, len); + } + + fr.close(); + + return authenticateWithPublicKey(user, cw.toCharArray(), password); + } + + /** + * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any + * time, but it is best to add connection monitors before invoking + * connect() to avoid glitches (e.g., you add a connection + * monitor after a successful connect(), but the connection has died in the + * mean time. Then, your connection monitor won't be notified.) + *

+ * You can add as many monitors as you like. + * + * @param cmon An object implementing the ConnectionMonitor interface. + * @see ConnectionMonitor + */ + public synchronized void addConnectionMonitor(ConnectionMonitor cmon) + { + if (cmon == null) + throw new IllegalArgumentException("cmon argument is null"); + + connectionMonitors.addElement(cmon); + + if (tm != null) + tm.setConnectionMonitors(connectionMonitors); + if (am != null) + am.setConnectionMonitors(connectionMonitors); + } + + /** + * Close the connection to the SSH-2 server. All assigned sessions will be + * closed, too. Can be called at any time. Don't forget to call this once + * you don't need a connection anymore - otherwise the receiver thread may + * run forever. + */ + public synchronized void close() + { + if (log.isEnabled()) + log.log(50, "Closing All"); + + Throwable t = new Throwable("Closed due to user request."); + close(t, false); + } + + // testando synchronized + private synchronized void close(Throwable t, boolean hard) + { + if (cm != null) + cm.closeAllChannels(); + + if (tm != null) + { + tm.close(t, hard == false); + tm = null; + } + am = null; + cm = null; + authenticated = false; + } + + /** + * Same as + * {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}. + * + * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method. + * @throws IOException the io exception + */ + public ConnectionInfo connect() throws IOException + { + return connect(null, 0, 0); + } + + /** + * Same as + * {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}. + * + * @param verifier the verifier + * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method. + * @throws IOException the io exception + */ + public ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException + { + return connect(verifier, 0, 0); + } + + /** + * Connect to the SSH-2 server and, as soon as the server has presented its + * host key, use the + * {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, String, + * byte[]) ServerHostKeyVerifier.verifyServerHostKey()}* method of the + * verifier to ask for permission to proceed. If + * verifier is null, then any host key will + * be accepted - this is NOT recommended, since it makes man-in-the-middle + * attackes VERY easy (somebody could put a proxy SSH server between you and + * the real server). + *

+ * Note: The verifier will be called before doing any crypto calculations + * (i.e., diffie-hellman). Therefore, if you don't like the presented host + * key then no CPU cycles are wasted (and the evil server has less + * information about us). + *

+ * However, it is still possible that the server presented a fake host key: + * the server cheated (typically a sign for a man-in-the-middle attack) and + * is not able to generate a signature that matches its host key. Don't + * worry, the library will detect such a scenario later when checking the + * signature (the signature cannot be checked before having completed the + * diffie-hellman exchange). + *

+ * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, + * String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}* method will + * *NOT* be called from the current thread, the call is being made from a + * background thread (there is a background dispatcher thread for every + * established connection). + *

+ * Note 3: This method will block as long as the key exchange of the + * underlying connection has not been completed (and you have not specified + * any timeouts). + *

+ * Note 4: If you want to re-use a connection object that was successfully + * connected, then you must call the {@link #close()} method before invoking + * connect() again. + * + * @param verifier An object that implements the {@link ServerHostKeyVerifier} interface. Pass null to accept any server host key - NOT recommended. + * @param connectTimeout Connect the underlying TCP socket to the server with the given timeout value (non-negative, in milliseconds). Zero means no timeout. If a proxy is being used (see {@link #setProxyData(ProxyData)}), then this timeout is used for the connection establishment to the proxy. + * @param kexTimeout Timeout for complete connection establishment (non-negative, in milliseconds). Zero means no timeout. The timeout counts from the moment you invoke the connect() method and is cancelled as soon as the first key-exchange round has finished. It is possible that the timeout event will be fired during the invocation of the verifier callback, but it will only have an effect after the verifier returns. + * @return A {@link ConnectionInfo} object containing the details of the established connection. + * @throws IOException If any problem occurs, e.g., the server's host key is not accepted by the verifier or there is problem during the initial crypto setup (e.g., the signature sent by the server is wrong).

In case of a timeout (either connectTimeout or kexTimeout) a SocketTimeoutException is thrown.

An exception may also be thrown if the connection was already successfully connected (no matter if the connection broke in the mean time) and you invoke connect() again without having called {@link #close()} first.

If a HTTP proxy is being used and the proxy refuses the connection, then a {@link HTTPProxyException} may be thrown, which contains the details returned by the proxy. If the proxy is buggy and does not return a proper HTTP response, then a normal IOException is thrown instead. + */ + public ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout) throws IOException { + return connect(verifier, connectTimeout, 0, kexTimeout); + } + + /** + * Connect connection info. + * + * Syncronized removido, pois não estava fechando a conexão. + * + * @param verifier the verifier + * @param connectTimeout the connect timeout + * @param readTimeout the read timeout + * @param kexTimeout the kex timeout + * @return the connection info + * @throws IOException the io exception + */ + public ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int readTimeout, int kexTimeout) + throws IOException { + final class TimeoutState + { + boolean isCancelled = false; + boolean timeoutSocketClosed = false; + } + + if (tm != null) + throw new IOException("Connection to " + hostname + " is already in connected state!"); + + if (connectTimeout < 0) + throw new IllegalArgumentException("connectTimeout must be non-negative!"); + + if (kexTimeout < 0) + throw new IllegalArgumentException("kexTimeout must be non-negative!"); + + final TimeoutState state = new TimeoutState(); + + tm = new TransportManager(hostname, port, sourceAddress); + + tm.setConnectionMonitors(connectionMonitors); + + if (!compression) { + cryptoWishList.c2s_comp_algos = new String[] { "none" }; + cryptoWishList.s2c_comp_algos = new String[] { "none" }; + } + + /* + * Make sure that the runnable below will observe the new value of "tm" + * and "state" (the runnable will be executed in a different thread, + * which may be already running, that is why we need a memory barrier + * here). See also the comment in Channel.java if you are interested in + * the details. + * + * OKOK, this is paranoid since adding the runnable to the todo list of + * the TimeoutService will ensure that all writes have been flushed + * before the Runnable reads anything (there is a synchronized block in + * TimeoutService.addTimeoutHandler). + */ + + synchronized (tm) + { + /* We could actually synchronize on anything. */ + } + + try + { + TimeoutToken token = null; + + try + { + if (kexTimeout > 0) + { + final Runnable timeoutHandler = new Runnable() + { + public void run() + { + synchronized (state) + { + if (state.isCancelled) + return; + state.timeoutSocketClosed = true; + close(new SocketTimeoutException("The connect timeout expired"), false); + } + } + }; + + long timeoutHorizont = System.currentTimeMillis() + kexTimeout; + + token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler); + } + + try + { + tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, readTimeout, getOrCreateSecureRND(), proxyData); + } + catch (SocketTimeoutException se) + { + throw (SocketTimeoutException) new SocketTimeoutException( + "The connect() operation on the socket timed out.").initCause(se); + } + + tm.setTcpNoDelay(tcpNoDelay); + + /* Wait until first KEX has finished */ + return tm.getConnectionInfo(1); + } + finally + { + /* Now try to cancel the timeout, if needed */ + + if (token != null) + { + TimeoutService.cancelTimeoutHandler(token); + + /* Were we too late? */ + + synchronized (state) + { + if (state.timeoutSocketClosed) + throw new IOException("This exception will be replaced by the one below =)"); + /* + * Just in case the "cancelTimeoutHandler" invocation came + * just a little bit too late but the handler did not enter + * the semaphore yet - we can still stop it. + */ + state.isCancelled = true; + } + } + } + } + catch (SocketTimeoutException ste) + { + throw ste; + } + catch (IOException e1) + { + /* This will also invoke any registered connection monitors */ + close(new Throwable("There was a problem during connect.").initCause(e1), false); + + synchronized (state) + { + /* + * Show a clean exception, not something like "the socket is + * closed!?!" + */ + if (state.timeoutSocketClosed) + throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired."); + } + + /* Do not wrap a HTTPProxyException */ + if (e1 instanceof HTTPProxyException) + throw e1; + + throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port) + .initCause(e1); + } + } + + public synchronized void setCompression(boolean enabled) throws IOException { + if (tm != null) + throw new IOException("Connection to " + hostname + + " is already in connected state!"); + + compression = enabled; + } + + /** + * Creates a new {@link DynamicPortForwarder}. A + * DynamicPortForwarder forwards TCP/IP connections that arrive + * at a local port via the secure tunnel to another host that is chosen via + * the SOCKS protocol. + *

+ * This method must only be called after one has passed successfully the + * authentication step. There is no limit on the number of concurrent + * forwardings. + * + * @param addr + * specifies the InetSocketAddress where the local socket shall + * be bound to. + * @return A {@link DynamicPortForwarder} object. + * @throws IOException + */ + public synchronized DynamicPortForwarder createDynamicPortForwarder( + InetSocketAddress addr) throws IOException { + + return createDynamicPortForwarder(addr, 0); + } + + public synchronized DynamicPortForwarder createDynamicPortForwarder( + InetSocketAddress addr, int maxThreads) throws IOException { + if (tm == null) + throw new IllegalStateException( + "Cannot forward ports, you need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException( + "Cannot forward ports, connection is not authenticated."); + + return new DynamicPortForwarder(cm, addr, maxThreads); + } + + /** + * Creates a new {@link DynamicPortForwarder}. A + * DynamicPortForwarder forwards TCP/IP connections that arrive + * at a local port via the secure tunnel to another host that is chosen via + * the SOCKS protocol. + *

+ * This method must only be called after one has passed successfully the + * authentication step. There is no limit on the number of concurrent + * forwardings. + * + * @param local_port + * @return A {@link DynamicPortForwarder} object. + * @throws IOException + */ + public synchronized DynamicPortForwarder createDynamicPortForwarder( + int local_port) throws IOException { + + return new DynamicPortForwarder(cm, local_port, 0); + } + + public synchronized DynamicPortForwarder createDynamicPortForwarder( + int local_port, int maxThreads) throws IOException { + if (tm == null) + throw new IllegalStateException( + "Cannot forward ports, you need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException( + "Cannot forward ports, connection is not authenticated."); + + return new DynamicPortForwarder(cm, local_port, maxThreads); + } + + /** + * Creates a new {@link LocalPortForwarder}. A + * LocalPortForwarder forwards TCP/IP connections that arrive + * at a local port via the secure tunnel to another host (which may or may + * not be identical to the remote SSH-2 server). + *

+ * This method must only be called after one has passed successfully the + * authentication step. There is no limit on the number of concurrent + * forwardings. + * + * @param local_port the local port the LocalPortForwarder shall bind to. + * @param host_to_connect target address (IP or hostname) + * @param port_to_connect target port + * @return A {@link LocalPortForwarder} object. + * @throws IOException the io exception + */ + public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect, + int port_to_connect) throws IOException + { + if (tm == null) + throw new IllegalStateException("Cannot forward ports, you need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("Cannot forward ports, connection is not authenticated."); + + return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect); + } + + /** + * Creates a new {@link LocalPortForwarder}. A + * LocalPortForwarder forwards TCP/IP connections that arrive + * at a local port via the secure tunnel to another host (which may or may + * not be identical to the remote SSH-2 server). + *

+ * This method must only be called after one has passed successfully the + * authentication step. There is no limit on the number of concurrent + * forwardings. + * + * @param addr specifies the InetSocketAddress where the local socket shall be bound to. + * @param host_to_connect target address (IP or hostname) + * @param port_to_connect target port + * @return A {@link LocalPortForwarder} object. + * @throws IOException the io exception + */ + public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect, + int port_to_connect) throws IOException + { + if (tm == null) + throw new IllegalStateException("Cannot forward ports, you need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("Cannot forward ports, connection is not authenticated."); + + return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect); + } + + /** + * Creates a new {@link LocalStreamForwarder}. A + * LocalStreamForwarder manages an Input/Outputstream pair + * that is being forwarded via the secure tunnel into a TCP/IP connection to + * another host (which may or may not be identical to the remote SSH-2 + * server). + * + * @param host_to_connect the host to connect + * @param port_to_connect the port to connect + * @return A {@link LocalStreamForwarder} object. + * @throws IOException the io exception + */ + public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect) + throws IOException + { + if (tm == null) + throw new IllegalStateException("Cannot forward, you need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("Cannot forward, connection is not authenticated."); + + return new LocalStreamForwarder(cm, host_to_connect, port_to_connect); + } + + /** + * Create a very basic {@link SCPClient} that can be used to copy files + * from/to the SSH-2 server. + *

+ * Works only after one has passed successfully the authentication step. + * There is no limit on the number of concurrent SCP clients. + *

+ * Note: This factory method will probably disappear in the future. + * + * @return A {@link SCPClient} object. + * @throws IOException the io exception + */ + public synchronized SCPClient createSCPClient() throws IOException + { + if (tm == null) + throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("Cannot create SCP client, connection is not authenticated."); + + return new SCPClient(this); + } + + /** + * Force an asynchronous key re-exchange (the call does not block). The + * latest values set for MAC, Cipher and DH group exchange parameters will + * be used. If a key exchange is currently in progress, then this method has + * the only effect that the so far specified parameters will be used for the + * next (server driven) key exchange. + *

+ * Note: This implementation will never start a key exchange (other than the + * initial one) unless you or the SSH-2 server ask for it. + * + * @throws IOException In case of any failure behind the scenes. + */ + public synchronized void forceKeyExchange() throws IOException + { + if (tm == null) + throw new IllegalStateException("You need to establish a connection first."); + + tm.forceKeyExchange(cryptoWishList, dhgexpara); + } + + /** + * Returns the hostname that was passed to the constructor. + * + * @return the hostname + */ + public synchronized String getHostname() + { + return hostname; + } + + /** + * Returns the port that was passed to the constructor. + * + * @return the TCP port + */ + public synchronized int getPort() + { + return port; + } + + /** + * Returns a {@link ConnectionInfo} object containing the details of the + * connection. Can be called as soon as the connection has been established + * (successfully connected). + * + * @return A {@link ConnectionInfo} object. + * @throws IOException In case of any failure behind the scenes. + */ + public synchronized ConnectionInfo getConnectionInfo() throws IOException + { + if (tm == null) + throw new IllegalStateException( + "Cannot get details of connection, you need to establish a connection first."); + return tm.getConnectionInfo(1); + } + + /** + * Gets version info. + * + * @return the version info + * @throws IOException the io exception + */ + public synchronized ClientServerHello getVersionInfo() throws IOException { + if (tm == null) + throw new IllegalStateException( + "Cannot get details of connection, you need to establish a connection first."); + return tm.getVersionInfo(); + } + + + /** + * After a successful connect, one has to authenticate oneself. This method + * can be used to tell which authentication methods are supported by the + * server at a certain stage of the authentication process (for the given + * username). + *

+ * Note 1: the username will only be used if no authentication step was done + * so far (it will be used to ask the server for a list of possible + * authentication methods by sending the initial "none" request). Otherwise, + * this method ignores the user name and returns a cached method list (which + * is based on the information contained in the last negative server + * response). + *

+ * Note 2: the server may return method names that are not supported by this + * implementation. + *

+ * After a successful authentication, this method must not be called + * anymore. + * + * @param user A String holding the username. + * @return a (possibly emtpy) array holding authentication method names. + * @throws IOException the io exception + */ + public synchronized String[] getRemainingAuthMethods(String user) throws IOException + { + if (user == null) + throw new IllegalArgumentException("user argument may not be NULL!"); + + if (tm == null) + throw new IllegalStateException("Connection is not established!"); + + if (authenticated) + throw new IllegalStateException("Connection is already authenticated!"); + + if (am == null) { + am = new AuthenticationManager(tm); + am.setConnectionMonitors(connectionMonitors); + } + + if (cm == null) + cm = new ChannelManager(tm); + + return am.getRemainingMethods(user); + } + + /** + * Determines if the authentication phase is complete. Can be called at any + * time. + * + * @return true if no further authentication steps are needed. + */ + public synchronized boolean isAuthenticationComplete() + { + return authenticated; + } + + /** + * Returns true if there was at least one failed authentication request and + * the last failed authentication request was marked with "partial success" + * by the server. This is only needed in the rare case of SSH-2 server + * setups that cannot be satisfied with a single successful authentication + * request (i.e., multiple authentication steps are needed.) + *

+ * If you are interested in the details, then have a look at RFC4252. + * + * @return if the there was a failed authentication step and the last one was marked as a "partial success". + */ + public synchronized boolean isAuthenticationPartialSuccess() + { + if (am == null) + return false; + + return am.getPartialSuccess(); + } + + /** + * Checks if a specified authentication method is available. This method is + * actually just a wrapper for {@link #getRemainingAuthMethods(String) + * getRemainingAuthMethods()}*. + * + * @param user A String holding the username. + * @param method An authentication method name (e.g., "publickey", "password", "keyboard-interactive") as specified by the SSH-2 standard. + * @return if the specified authentication method is currently available. + * @throws IOException the io exception + */ + public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException + { + if (method == null) + throw new IllegalArgumentException("method argument may not be NULL!"); + + String methods[] = getRemainingAuthMethods(user); + + for (int i = 0; i < methods.length; i++) + { + if (methods[i].compareTo(method) == 0) + return true; + } + + return false; + } + + private final SecureRandom getOrCreateSecureRND() + { + if (generator == null) + generator = RandomFactory.create(); + + return generator; + } + + /** + * Open a new {@link Session} on this connection. Works only after one has + * passed successfully the authentication step. There is no limit on the + * number of concurrent sessions. + * + * @return A {@link Session} object. + * @throws IOException the io exception + */ + public synchronized Session openSession() throws IOException + { + if (tm == null) + throw new IllegalStateException("Cannot open session, you need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("Cannot open session, connection is not authenticated."); + + return new Session(cm, getOrCreateSecureRND()); + } + + /** + * Send an SSH_MSG_IGNORE packet. This method will generate a random data + * attribute (length between 0 (invlusive) and 16 (exclusive) bytes, + * contents are random bytes). + *

+ * This method must only be called once the connection is established. + * + * @throws IOException the io exception + */ + public synchronized void sendIgnorePacket() throws IOException + { + SecureRandom rnd = getOrCreateSecureRND(); + + byte[] data = new byte[rnd.nextInt(16)]; + rnd.nextBytes(data); + + sendIgnorePacket(data); + } + + /** + * Send an SSH_MSG_IGNORE packet with the given data attribute. + *

+ * This method must only be called once the connection is established. + * + * @param data the data + * @throws IOException the io exception + */ + public synchronized void sendIgnorePacket(byte[] data) throws IOException + { + if (data == null) + throw new IllegalArgumentException("data argument must not be null."); + + if (tm == null) + throw new IllegalStateException( + "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first."); + + PacketIgnore pi = new PacketIgnore(); + pi.setData(data); + + tm.sendMessage(pi.getPayload()); + } + + /** + * Removes duplicates from a String array, keeps only first occurence of + * each element. Does not destroy order of elements; can handle nulls. Uses + * a very efficient O(N^2) algorithm =) + * + * @param list + * a String array. + * @return a cleaned String array. + */ + private String[] removeDuplicates(String[] list) + { + if ((list == null) || (list.length < 2)) + return list; + + String[] list2 = new String[list.length]; + + int count = 0; + + for (int i = 0; i < list.length; i++) + { + boolean duplicate = false; + + String element = list[i]; + + for (int j = 0; j < count; j++) + { + if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j])))) + { + duplicate = true; + break; + } + } + + if (duplicate) + continue; + + list2[count++] = list[i]; + } + + if (count == list2.length) + return list2; + + String[] tmp = new String[count]; + System.arraycopy(list2, 0, tmp, 0, count); + + return tmp; + } + + /** + * Unless you know what you are doing, you will never need this. + * + * @param ciphers ciphers + */ + public synchronized void setClient2ServerCiphers(String[] ciphers) + { + if ((ciphers == null) || (ciphers.length == 0)) + throw new IllegalArgumentException(); + ciphers = removeDuplicates(ciphers); + BlockCipherFactory.checkCipherList(ciphers); + cryptoWishList.c2s_enc_algos = ciphers; + } + + /** + * Unless you know what you are doing, you will never need this. + * + * @param macs macs + */ + public synchronized void setClient2ServerMACs(String[] macs) + { + if ((macs == null) || (macs.length == 0)) + throw new IllegalArgumentException(); + macs = removeDuplicates(macs); + MessageMac.checkMacs(macs); + cryptoWishList.c2s_mac_algos = macs; + } + + /** + * Sets the parameters for the diffie-hellman group exchange. Unless you + * know what you are doing, you will never need this. Default values are + * defined in the {@link DHGexParameters} class. + * + * @param dgp {@link DHGexParameters}, non null. + */ + public synchronized void setDHGexParameters(DHGexParameters dgp) + { + if (dgp == null) + throw new IllegalArgumentException(); + + dhgexpara = dgp; + } + + /** + * Unless you know what you are doing, you will never need this. + * + * @param ciphers ciphers + */ + public synchronized void setServer2ClientCiphers(String[] ciphers) + { + if ((ciphers == null) || (ciphers.length == 0)) + throw new IllegalArgumentException(); + ciphers = removeDuplicates(ciphers); + BlockCipherFactory.checkCipherList(ciphers); + cryptoWishList.s2c_enc_algos = ciphers; + } + + /** + * Unless you know what you are doing, you will never need this. + * + * @param macs macs + */ + public synchronized void setServer2ClientMACs(String[] macs) + { + if ((macs == null) || (macs.length == 0)) + throw new IllegalArgumentException(); + + macs = removeDuplicates(macs); + MessageMac.checkMacs(macs); + cryptoWishList.s2c_mac_algos = macs; + } + + /** + * Define the set of allowed server host key algorithms to be used for the + * following key exchange operations. + *

+ * Unless you know what you are doing, you will never need this. + * + * @param algos An array of allowed server host key algorithms. The entries of the array must be ordered after preference, i.e., the entry at index 0 is the most preferred one. You must specify at least one entry. + */ + public synchronized void setServerHostKeyAlgorithms(String[] algos) + { + if ((algos == null) || (algos.length == 0)) + throw new IllegalArgumentException(); + + algos = removeDuplicates(algos); + KexManager.checkServerHostkeyAlgorithmsList(algos); + cryptoWishList.serverHostKeyAlgorithms = algos; + } + + /** + * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the + * underlying socket. + *

+ * Can be called at any time. If the connection has not yet been established + * then the passed value will be stored and set after the socket has been + * set up. The default value that will be used is false. + * + * @param enable the argument passed to the Socket.setTCPNoDelay() method. + * @throws IOException the io exception + */ + public synchronized void setTCPNoDelay(boolean enable) throws IOException + { + tcpNoDelay = enable; + + if (tm != null) + tm.setTcpNoDelay(enable); + } + + /** + * Used to tell the library that the connection shall be established through + * a proxy server. It only makes sense to call this method before calling + * the {@link #connect() connect()} method. + *

+ * At the moment, only HTTP proxies are supported. + *

+ * Note: This method can be called any number of times. The + * {@link #connect() connect()} method will use the value set in the last + * preceding invocation of this method. + * + * @param proxyData Connection information about the proxy. If null, then no proxy will be used (non surprisingly, this is also the default). + * @see HTTPProxyData + */ + public synchronized void setProxyData(ProxyData proxyData) + { + this.proxyData = proxyData; + } + + /** + * Request a remote port forwarding. If successful, then forwarded + * connections will be redirected to the given target address. You can + * cancle a requested remote port forwarding by calling + * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}. + *

+ * A call of this method will block until the peer either agreed or + * disagreed to your request- + *

+ * Note 1: this method typically fails if you + *

    + *
  • pass a port number for which the used remote user has not enough + * permissions (i.e., port < 1024)
  • + *
  • or pass a port number that is already in use on the remote server
  • + *
  • or if remote port forwarding is disabled on the server.
  • + *
+ *

+ * Note 2: (from the openssh man page): By default, the listening socket on + * the server will be bound to the loopback interface only. This may be + * overriden by specifying a bind address. Specifying a remote bind address + * will only succeed if the server's GatewayPorts option is enabled + * (see sshd_config(5)). + * + * @param bindAddress address to bind to on the server:

  • "" means that connections are to be accepted on all protocol families supported by the SSH implementation
  • "0.0.0.0" means to listen on all IPv4 addresses
  • "::" means to listen on all IPv6 addresses
  • "localhost" means to listen on all protocol families supported by the SSH implementation on loopback addresses only, [RFC3330] and RFC3513]
  • "127.0.0.1" and "::1" indicate listening on the loopback interfaces for IPv4 and IPv6 respectively
+ * @param bindPort port number to bind on the server (must be > 0) + * @param targetAddress the target address (IP or hostname) + * @param targetPort the target port + * @throws IOException the io exception + */ + public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress, + int targetPort) throws IOException + { + if (tm == null) + throw new IllegalStateException("You need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("The connection is not authenticated."); + + if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0)) + throw new IllegalArgumentException(); + + cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort); + } + + /** + * Cancel an earlier requested remote port forwarding. Currently active + * forwardings will not be affected (e.g., disrupted). Note that further + * connection forwarding requests may be received until this method has + * returned. + * + * @param bindPort the allocated port number on the server + * @throws IOException if the remote side refuses the cancel request or another low level error occurs (e.g., the underlying connection is closed) + */ + public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException + { + if (tm == null) + throw new IllegalStateException("You need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("The connection is not authenticated."); + + cm.requestCancelGlobalForward(bindPort); + } + + /** + * Provide your own instance of SecureRandom. Can be used, e.g., if you want + * to seed the used SecureRandom generator manually. + *

+ * The SecureRandom instance is used during key exchanges, public key + * authentication, x11 cookie generation and the like. + * + * @param rnd a SecureRandom instance + */ + public synchronized void setSecureRandom(SecureRandom rnd) + { + if (rnd == null) + throw new IllegalArgumentException(); + + this.generator = rnd; + } + + /** + * Enable/disable debug logging. Only do this when requested by Trilead + * support. + *

+ * For speed reasons, some static variables used to check whether debugging + * is enabled are not protected with locks. In other words, if you + * dynamicaly enable/disable debug logging, then some threads may still use + * the old setting. To be on the safe side, enable debugging before doing + * the connect() call. + * + * @param enable on/off + * @param logger a {@link DebugLogger DebugLogger} instance, null means logging using the simple logger which logs all messages to to stderr. Ignored if enabled is false + * @deprecated Logging is now sent automatically to java.util.logging, and never to the {@link DebugLogger}. + */ + public synchronized void enableDebugging(boolean enable, DebugLogger logger) + { + Logger.enabled = enable; + + if (enable == false) + { + Logger.logger = null; + } + else + { + if (logger == null) + { + Logger.logger = new DebugLogger() + { + + public void log(int level, String className, String message) + { + long now = System.currentTimeMillis(); + System.err.println(now + " : " + className + ": " + message); + } + }; + } else { + Logger.logger = logger; + } + } + } + + /** + * This method can be used to perform end-to-end connection testing. It + * sends a 'ping' message to the server and waits for the 'pong' from the + * server. + *

+ * When this method throws an exception, then you can assume that the + * connection should be abandoned. + *

+ * Note: Works only after one has passed successfully the authentication + * step. + *

+ * Implementation details: this method sends a SSH_MSG_GLOBAL_REQUEST + * request ('trilead-ping') to the server and waits for the + * SSH_MSG_REQUEST_FAILURE reply packet from the server. + * + * @throws IOException in case of any problem + */ + public synchronized long ping() throws IOException + { + return ping(0); + } + + /** + * Performs the same end-to-end SSH ping as {@link #ping()}, but limits how + * long the client may wait for the server's reply. A timeout is essential for + * HTTP-based transports: a CDN can leave the HTTP/2 stream open while silently + * black-holing data, in which case the socket never reports EOF. + * + * @param timeoutMillis maximum reply wait in milliseconds; {@code 0} means + * wait indefinitely for backwards compatibility. + */ + public synchronized long ping(long timeoutMillis) throws IOException + { + if (timeoutMillis < 0) + throw new IllegalArgumentException("timeoutMillis must not be negative"); + + if (tm == null) + throw new IllegalStateException("You need to establish a connection first."); + + if (!authenticated) + throw new IllegalStateException("The connection is not authenticated."); + + return cm.requestGlobalTrileadPing(timeoutMillis); + } + + /** + * If the socket connection is lost (either by this side closing down or the other side closing down), + * return a non-null object indicating the cause of the connection loss. + * + * @return the reason closed cause + */ + public Throwable getReasonClosedCause() { + return tm != null ? tm.getReasonClosedCause() : null; + } + + /** + * Executes a process remotely and blocks until its completion. + * + * @param command the command + * @param output The stdout/stderr will be sent to this stream. + * @return the int + * @throws IOException the io exception + * @throws InterruptedException the interrupted exception + */ + public int exec(String command, OutputStream output) throws IOException, InterruptedException { + Session session = openSession(); + try { + session.execCommand(command); + PumpThread t1 = new PumpThread(session.getStdout(), output); + t1.start(); + PumpThread t2 = new PumpThread(session.getStderr(), output); + t2.start(); + session.getStdin().close(); + t1.join(); + t2.join(); + + // wait for some time since the delivery of the exit status often gets delayed + session.waitForCondition(ChannelCondition.EXIT_STATUS,3000); + Integer r = session.getExitStatus(); + if(r!=null) return r.intValue(); + return -1; + } finally { + session.close(); + } + } + + /** + * Pumps {@link InputStream} to {@link OutputStream}. + * + * @author Kohsuke Kawaguchi + */ + private static final class PumpThread extends Thread { + private final InputStream in; + private final OutputStream out; + + /** + * Instantiates a new Pump thread. + * + * @param in the in + * @param out the out + */ + public PumpThread(InputStream in, OutputStream out) { + super("pump thread"); + this.in = in; + this.out = out; + } + + public void run() { + byte[] buf = new byte[1024]; + try { + while(true) { + int len = in.read(buf); + if(len<0) { + in.close(); + return; + } + out.write(buf,0,len); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/ConnectionInfo.java b/service/src/main/java/com/trilead/ssh2/ConnectionInfo.java new file mode 100644 index 0000000..67cf84c --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/ConnectionInfo.java @@ -0,0 +1,52 @@ + +package com.trilead.ssh2; + +/** + * In most cases you probably do not need the information contained in here. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ConnectionInfo.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class ConnectionInfo +{ + /** + * The used key exchange (KEX) algorithm in the latest key exchange. + */ + public String keyExchangeAlgorithm; + + /** + * The currently used crypto algorithm for packets from to the client to the + * server. + */ + public String clientToServerCryptoAlgorithm; + /** + * The currently used crypto algorithm for packets from to the server to the + * client. + */ + public String serverToClientCryptoAlgorithm; + + /** + * The currently used MAC algorithm for packets from to the client to the + * server. + */ + public String clientToServerMACAlgorithm; + /** + * The currently used MAC algorithm for packets from to the server to the + * client. + */ + public String serverToClientMACAlgorithm; + + /** + * The type of the server host key + */ + public String serverHostKeyAlgorithm; + /** + * The server host key that was sent during the latest key exchange. + */ + public byte[] serverHostKey; + + /** + * Number of kex exchanges performed on this connection so far. + */ + public int keyExchangeCounter = 0; +} diff --git a/service/src/main/java/com/trilead/ssh2/ConnectionMonitor.java b/service/src/main/java/com/trilead/ssh2/ConnectionMonitor.java new file mode 100644 index 0000000..9714613 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/ConnectionMonitor.java @@ -0,0 +1,38 @@ +package com.trilead.ssh2; + +/** + * A ConnectionMonitor is used to get notified when the + * underlying socket of a connection is closed. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ConnectionMonitor.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public interface ConnectionMonitor +{ + public static final int SERVER_BANNER = 101; + + /** + * This method is called after the connection's underlying + * socket has been closed. E.g., due to the {@link Connection#close()} request of the + * user, if the peer closed the connection, due to a fatal error during connect() + * (also if the socket cannot be established) or if a fatal error occured on + * an established connection. + *

+ * This is an experimental feature. + *

+ * You MUST NOT make any assumption about the thread that invokes this method. + *

+ * Please note: if the connection is not connected (e.g., there was no successful + * connect() call), then the invocation of {@link Connection#close()} will NOT trigger + * this method. + * + * @see Connection#addConnectionMonitor(ConnectionMonitor) + * + * @param reason Includes an indication why the socket was closed. + */ + public void connectionLost(Throwable reason); + + + public void onReceiveInfo(int infoId, String infoMsg); +} diff --git a/service/src/main/java/com/trilead/ssh2/DHGexParameters.java b/service/src/main/java/com/trilead/ssh2/DHGexParameters.java new file mode 100644 index 0000000..81cbe53 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/DHGexParameters.java @@ -0,0 +1,121 @@ + +package com.trilead.ssh2; + +/** + * A DHGexParameters object can be used to specify parameters for + * the diffie-hellman group exchange. + *

+ * Depending on which constructor is used, either the use of a + * SSH_MSG_KEX_DH_GEX_REQUEST or SSH_MSG_KEX_DH_GEX_REQUEST_OLD + * can be forced. + * + * @see Connection#setDHGexParameters(DHGexParameters) + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DHGexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class DHGexParameters +{ + private final int min_group_len; + private final int pref_group_len; + private final int max_group_len; + + private static final int MIN_ALLOWED = 1024; + private static final int MAX_ALLOWED = 8192; + + /** + * Same as calling {@link #DHGexParameters(int, int, int) DHGexParameters(1024, 2048, 4096)}. + * This is also the default used by the Connection class. + * + */ + public DHGexParameters() + { + this(1024, 2048, 4096); + } + + /** + * This constructor can be used to force the sending of a + * SSH_MSG_KEX_DH_GEX_REQUEST_OLD request. + * Internally, the minimum and maximum group lengths will + * be set to zero. + * + * @param pref_group_len has to be >= 1024 and <= 8192 + */ + public DHGexParameters(int pref_group_len) + { + if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED)) + throw new IllegalArgumentException("pref_group_len out of range!"); + + this.pref_group_len = pref_group_len; + this.min_group_len = 0; + this.max_group_len = 0; + } + + /** + * This constructor can be used to force the sending of a + * SSH_MSG_KEX_DH_GEX_REQUEST request. + *

+ * Note: older OpenSSH servers don't understand this request, in which + * case you should use the {@link #DHGexParameters(int)} constructor. + *

+ * All values have to be >= 1024 and <= 8192. Furthermore, + * min_group_len <= pref_group_len <= max_group_len. + * + * @param min_group_len min_group_len + * @param pref_group_len pref_group_len + * @param max_group_len max_group_len + */ + public DHGexParameters(int min_group_len, int pref_group_len, int max_group_len) + { + if ((min_group_len < MIN_ALLOWED) || (min_group_len > MAX_ALLOWED)) + throw new IllegalArgumentException("min_group_len out of range!"); + + if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED)) + throw new IllegalArgumentException("pref_group_len out of range!"); + + if ((max_group_len < MIN_ALLOWED) || (max_group_len > MAX_ALLOWED)) + throw new IllegalArgumentException("max_group_len out of range!"); + + if ((pref_group_len < min_group_len) || (pref_group_len > max_group_len)) + throw new IllegalArgumentException("pref_group_len is incompatible with min and max!"); + + if (max_group_len < min_group_len) + throw new IllegalArgumentException("max_group_len must not be smaller than min_group_len!"); + + this.min_group_len = min_group_len; + this.pref_group_len = pref_group_len; + this.max_group_len = max_group_len; + } + + /** + * Get the maximum group length. + * + * @return the maximum group length, may be zero if + * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested + */ + public int getMax_group_len() + { + return max_group_len; + } + + /** + * Get the minimum group length. + * + * @return minimum group length, may be zero if + * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested + */ + public int getMin_group_len() + { + return min_group_len; + } + + /** + * Get the preferred group length. + * + * @return the preferred group length + */ + public int getPref_group_len() + { + return pref_group_len; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/DebugLogger.java b/service/src/main/java/com/trilead/ssh2/DebugLogger.java new file mode 100644 index 0000000..ad9b301 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/DebugLogger.java @@ -0,0 +1,25 @@ +package com.trilead.ssh2; + +/** + * An interface which needs to be implemented if you + * want to capture debugging messages. + * + * @see Connection#enableDebugging(boolean, DebugLogger) + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DebugLogger.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $ + * @deprecated + * Logging all goes to JDK java.util.logging + */ +public interface DebugLogger +{ + +/** + * Log a debug message. + * + * @param level 0-99, 99 is a the most verbose level + * @param className the class that generated the message + * @param message the debug message + */ + public void log(int level, String className, String message); +} diff --git a/service/src/main/java/com/trilead/ssh2/DynamicPortForwarder.java b/service/src/main/java/com/trilead/ssh2/DynamicPortForwarder.java new file mode 100644 index 0000000..09ce637 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/DynamicPortForwarder.java @@ -0,0 +1,70 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import com.trilead.ssh2.channel.ChannelManager; +import com.trilead.ssh2.channel.DynamicAcceptThread; + +/** + * A DynamicPortForwarder forwards TCP/IP connections to a local + * port via the secure tunnel to another host which is selected via the SOCKS + * protocol. Checkout {@link Connection#createDynamicPortForwarder(int)} on how + * to create one. + * + * @author Kenny Root + * @version $Id: $ + */ +public class DynamicPortForwarder { + ChannelManager cm; + + DynamicAcceptThread dat; + + DynamicPortForwarder(ChannelManager cm, InetSocketAddress addr, int maxThreads) + throws IOException { + this.cm = cm; + + dat = new DynamicAcceptThread(cm, addr, maxThreads); + dat.setDaemon(true); + dat.start(); + } + + DynamicPortForwarder(ChannelManager cm, int local_port, int maxThreads) throws IOException { + this.cm = cm; + + dat = new DynamicAcceptThread(cm, local_port, maxThreads); + dat.setDaemon(true); + dat.start(); + } + + /** Returns the actual loopback port. Useful when the forwarder was bound to port 0. */ + public int getLocalPort() { + return dat.getLocalPort(); + } + + /** + * Stop TCP/IP forwarding of newly arriving connections. + * + * @throws IOException + */ + public void close() throws IOException { + dat.stopWorking(); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/HTTPProxyData.java b/service/src/main/java/com/trilead/ssh2/HTTPProxyData.java new file mode 100644 index 0000000..c43a595 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/HTTPProxyData.java @@ -0,0 +1,220 @@ + +package com.trilead.ssh2; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import com.dragonssh.xhttpdemo.core.tunnel.vpn.SocketProtector; + +import com.trilead.ssh2.crypto.Base64; +import com.trilead.ssh2.transport.ClientServerHello; +import java.net.UnknownHostException; +import com.trilead.ssh2.transport.TransportManager; + +/** + * A HTTPProxyData object is used to specify the needed connection data + * to connect through a HTTP proxy. + * + * @see Connection#setProxyData(ProxyData) + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: HTTPProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class HTTPProxyData implements ProxyData +{ + private final String proxyHost; + private final int proxyPort; + private final String proxyUser; + private final String proxyPass; + private final String[] requestHeaderLines; + + private Socket sock; + + /** + * Same as calling {@link #HTTPProxyData(String, int, String, String) HTTPProxyData(proxyHost, proxyPort, null, null)} + * + * @param proxyHost Proxy hostname. + * @param proxyPort Proxy port. + */ + public HTTPProxyData(String proxyHost, int proxyPort) + { + this(proxyHost, proxyPort, null, null); + } + + /** + * Same as calling {@link #HTTPProxyData(String, int, String, String, String[]) HTTPProxyData(proxyHost, proxyPort, null, null, null)} + * + * @param proxyHost Proxy hostname. + * @param proxyPort Proxy port. + * @param proxyUser Username for basic authentication (null if no authentication is needed). + * @param proxyPass Password for basic authentication (null if no authentication is needed). + */ + public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass) + { + this(proxyHost, proxyPort, proxyUser, proxyPass, null); + } + + /** + * Connection data for a HTTP proxy. It is possible to specify a username and password + * if the proxy requires basic authentication. Also, additional request header lines can + * be specified (e.g., "User-Agent: CERN-LineMode/2.15 libwww/2.17b3"). + *

+ * Please note: if you want to use basic authentication, then both proxyUser + * and proxyPass must be non-null. + *

+ * Here is an example: + *

+ * + * new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: TrileadBasedClient/1.0", "X-My-Proxy-Option: something"}); + * + * + * @param proxyHost Proxy hostname. + * @param proxyPort Proxy port. + * @param proxyUser Username for basic authentication (null if no authentication is needed). + * @param proxyPass Password for basic authentication (null if no authentication is needed). + * @param requestHeaderLines An array with additional request header lines (without end-of-line markers) + * that have to be sent to the server. May be null. + */ + + public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass, + String[] requestHeaderLines) + { + if (proxyHost == null) + throw new IllegalArgumentException("proxyHost must be non-null"); + + if (proxyPort < 0) + throw new IllegalArgumentException("proxyPort must be non-negative"); + + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.proxyUser = proxyUser; + this.proxyPass = proxyPass; + this.requestHeaderLines = requestHeaderLines; + } + + @Override + public Socket openConnection(String hostname, int port, int connectTimeout,int readTimeout) throws IOException { + sock = new Socket(); + // Ensure the proxy socket bypasses the VPN to prevent routing loops. + SocketProtector.protect(sock); + + InetAddress addr = TransportManager.createInetAddress(proxyHost); + sock.connect(new InetSocketAddress(addr, proxyPort), connectTimeout); + sock.setSoTimeout(readTimeout); + + /* OK, now tell the proxy where we actually want to connect to */ + + StringBuffer sb = new StringBuffer(); + + sb.append("CONNECT "); + sb.append(hostname); + sb.append(':'); + sb.append(port); + sb.append(" HTTP/1.0\r\n"); + + if ((proxyUser != null) && (proxyPass != null)) + { + String credentials = proxyUser + ":" + proxyPass; + char[] encoded; + try { + encoded = Base64.encode(credentials.getBytes("ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + encoded = Base64.encode(credentials.getBytes()); + } + sb.append("Proxy-Authorization: Basic "); + sb.append(encoded); + sb.append("\r\n"); + } + + if (requestHeaderLines != null) + { + for (int i = 0; i < requestHeaderLines.length; i++) + { + if (requestHeaderLines[i] != null) + { + sb.append(requestHeaderLines[i]); + sb.append("\r\n"); + } + } + } + + sb.append("\r\n"); + + OutputStream out = sock.getOutputStream(); + + try { + out.write(sb.toString().getBytes("ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + out.write(sb.toString().getBytes()); + } + out.flush(); + + /* Now parse the HTTP response */ + + byte[] buffer = new byte[1024]; + InputStream in = sock.getInputStream(); + + int len = ClientServerHello.readLineRN(in, buffer); + + String httpReponse; + try { + httpReponse = new String(buffer, 0, len, "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + httpReponse = new String(buffer, 0, len); + } + + if (!httpReponse.startsWith("HTTP/")) + throw new IOException("The proxy did not send back a valid HTTP response."); + + /* "HTTP/1.X XYZ X" => 14 characters minimum */ + + if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' ')) + throw new IOException("The proxy did not send back a valid HTTP response."); + + int errorCode = 0; + + try + { + errorCode = Integer.parseInt(httpReponse.substring(9, 12)); + } + catch (NumberFormatException ignore) + { + throw new IOException("The proxy did not send back a valid HTTP response."); + } + + if ((errorCode < 0) || (errorCode > 999)) + throw new IOException("The proxy did not send back a valid HTTP response."); + + if (errorCode != 200) + { + throw new HTTPProxyException(httpReponse.substring(13), errorCode); + } + + /* OK, read until empty line */ + + while (true) + { + len = ClientServerHello.readLineRN(in, buffer); + if (len == 0) + break; + } + + return sock; + } + + @Override + public void close() + { + if (sock != null) { + try { + sock.close(); + } catch(IOException e) {} + } + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/HTTPProxyException.java b/service/src/main/java/com/trilead/ssh2/HTTPProxyException.java new file mode 100644 index 0000000..4687dd1 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/HTTPProxyException.java @@ -0,0 +1,29 @@ + +package com.trilead.ssh2; + +import java.io.IOException; + +/** + * May be thrown upon connect() if a HTTP proxy is being used. + * + * @see Connection#connect() + * @see Connection#setProxyData(ProxyData) + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: HTTPProxyException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class HTTPProxyException extends IOException +{ + private static final long serialVersionUID = 2241537397104426186L; + + public final String httpResponse; + public final int httpErrorCode; + + public HTTPProxyException(String httpResponse, int httpErrorCode) + { + super("HTTP Proxy Error (" + httpErrorCode + " " + httpResponse + ")"); + this.httpResponse = httpResponse; + this.httpErrorCode = httpErrorCode; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/IOWarningException.java b/service/src/main/java/com/trilead/ssh2/IOWarningException.java new file mode 100644 index 0000000..3aebdd6 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/IOWarningException.java @@ -0,0 +1,17 @@ +package com.trilead.ssh2; + +import java.io.IOException; + +/** + * @author Thomas Singer + */ +public final class IOWarningException extends IOException { + + private static final long serialVersionUID = 1L; + + // Setup ================================================================== + + public IOWarningException(String message) { + super(message); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/InteractiveCallback.java b/service/src/main/java/com/trilead/ssh2/InteractiveCallback.java new file mode 100644 index 0000000..1db865f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/InteractiveCallback.java @@ -0,0 +1,56 @@ + +package com.trilead.ssh2; + +/** + * An InteractiveCallback is used to respond to challenges sent + * by the server if authentication mode "keyboard-interactive" is selected. + * + * @see Connection#authenticateWithKeyboardInteractive(String, + * String[], InteractiveCallback) + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: InteractiveCallback.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public interface InteractiveCallback +{ + /** + * This callback interface is used during a "keyboard-interactive" + * authentication. Every time the server sends a set of challenges (however, + * most often just one challenge at a time), this callback function will be + * called to give your application a chance to talk to the user and to + * determine the response(s). + *

+ * Some copy-paste information from the standard: a command line interface + * (CLI) client SHOULD print the name and instruction (if non-empty), adding + * newlines. Then for each prompt in turn, the client SHOULD display the + * prompt and read the user input. The name and instruction fields MAY be + * empty strings, the client MUST be prepared to handle this correctly. The + * prompt field(s) MUST NOT be empty strings. + *

+ * Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details. + *

+ * Note: clients SHOULD use control character filtering as discussed in + * RFC4251 to avoid attacks by including + * terminal control characters in the fields to be displayed. + * + * @param name + * the name String sent by the server. + * @param instruction + * the instruction String sent by the server. + * @param numPrompts + * number of prompts - may be zero (in this case, you should just + * return a String array of length zero). + * @param prompt + * an array (length numPrompts) of Strings + * @param echo + * an array (length numPrompts) of booleans. For + * each prompt, the corresponding echo field indicates whether or + * not the user input should be echoed as characters are typed. + * @return an array of reponses - the array size must match the parameter + * numPrompts. + * @throws Exception exception + */ + public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) + throws Exception; +} diff --git a/service/src/main/java/com/trilead/ssh2/KnownHosts.java b/service/src/main/java/com/trilead/ssh2/KnownHosts.java new file mode 100644 index 0000000..f7ba8c2 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/KnownHosts.java @@ -0,0 +1,812 @@ + +package com.trilead.ssh2; + +import java.io.BufferedReader; +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Vector; + +import com.trilead.ssh2.crypto.Base64; +import com.trilead.ssh2.crypto.digest.Digest; +import com.trilead.ssh2.crypto.digest.MD5; +import com.trilead.ssh2.crypto.digest.MessageMac; +import com.trilead.ssh2.crypto.digest.SHA1; +import com.trilead.ssh2.log.Logger; + +import com.trilead.ssh2.signature.KeyAlgorithm; +import com.trilead.ssh2.signature.KeyAlgorithmManager; + + +/** + * The KnownHosts class is a handy tool to verify received server hostkeys + * based on the information in known_hosts files (the ones used by OpenSSH). + *

+ * It offers basically an in-memory database for known_hosts entries, as well as some + * helper functions. Entries from a known_hosts file can be loaded at construction time. + * It is also possible to add more keys later (e.g., one can parse different + * known_hosts files). + * + *

+ * It is a thread safe implementation, therefore, you need only to instantiate one + * KnownHosts for your whole application. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: KnownHosts.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ + +public class KnownHosts +{ + private static final Logger LOGGER = Logger.getLogger(KnownHosts.class); + + public static final int HOSTKEY_IS_OK = 0; + public static final int HOSTKEY_IS_NEW = 1; + public static final int HOSTKEY_HAS_CHANGED = 2; + private static final SecureRandom SECURE_RANDOM = RandomFactory.create(); + + private class KnownHostsEntry + { + private final String[] patterns; + private final PublicKey key; + private final String algorithm; + + private KnownHostsEntry(String[] patterns, PublicKey key, String algorithm) + { + this.patterns = patterns; + this.key = key; + this.algorithm = algorithm; + } + } + + private final LinkedList publicKeys = new LinkedList<>(); + + public KnownHosts() + { + } + + public KnownHosts(char[] knownHostsData) throws IOException + { + initialize(knownHostsData); + } + + public KnownHosts(File knownHosts) throws IOException + { + initialize(knownHosts); + } + + /** + * Adds a single public key entry to the database. Note: this will NOT add the public key + * to any physical file (e.g., "~/.ssh/known_hosts") - use addHostkeyToFile() for that purpose. + * This method is designed to be used in a {@link ServerHostKeyVerifier}. + * + * @param hostnames a list of hostname patterns - at least one most be specified. Check out the + * OpenSSH sshd man page for a description of the pattern matching algorithm. + * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}. + * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}. + * @throws IOException on failure trying to convert the host key to a saveable format + */ + public void addHostkey(String[] hostnames, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException { + if (hostnames == null) { + throw new IllegalArgumentException("hostnames may not be null"); + } + + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + if (serverHostKeyAlgorithm.equals(algorithm.getKeyFormat())) { + PublicKey publicKey = algorithm.decodePublicKey(serverHostKey); + synchronized (publicKeys) { + publicKeys.add(new KnownHostsEntry(hostnames, publicKey, serverHostKeyAlgorithm)); + } + return; + } + } + + throw new IOWarningException("Unknwon host key type (" + serverHostKeyAlgorithm + ")"); + } + + /** + * Parses the given known_hosts data and adds entries to the database. + * + * @param knownHostsData the known hosts data to parse + * @throws IOException on failure reading the parsing the known hosts data + */ + public void addHostkeys(char[] knownHostsData) throws IOException + { + initialize(knownHostsData); + } + + /** + * Parses the given known_hosts file and adds entries to the database. + * + * @param knownHosts the file to read the existing known hosts entries feom, add to add any new entries to + * @throws IOException on failure reading the existing known hosts file + */ + public void addHostkeys(File knownHosts) throws IOException + { + initialize(knownHosts); + } + + /** + * Generate the hashed representation of the given hostname. Useful for adding entries + * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen). + * + * @param hostname the hostname to hash + * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA=" + */ + public static String createHashedHostname(String hostname) + { + SHA1 sha1 = new SHA1(); + + byte[] salt = new byte[sha1.getDigestLength()]; + + SECURE_RANDOM.nextBytes(salt); + + byte[] hash = hmacSha1Hash(salt, hostname); + + String base64_salt = new String(Base64.encode(salt)); + String base64_hash = new String(Base64.encode(hash)); + + return "|1|" + base64_salt + "|" + base64_hash; + } + + private static byte[] hmacSha1Hash(byte[] salt, String hostname) + { + + if (salt.length != 20) { + throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")"); + } + + MessageMac messageMac = new MessageMac("hmac-sha1", salt); + + try { + byte[] message = hostname.getBytes("ISO-8859-1"); + messageMac.update(message, 0, message.length); + } catch (UnsupportedEncodingException ignore) { + /* Actually, ISO-8859-1 is supported by all correct + * Java implementations. But... you never know. */ + byte[] message = hostname.getBytes(); + messageMac.update(message, 0, message.length); + } + + byte[] dig = new byte[20]; + + messageMac.getMac(dig, 0); + + return dig; + } + + private boolean checkHashed(String entry, String hostname) + { + if (!entry.startsWith("|1|")) + return false; + + int delim_idx = entry.indexOf('|', 3); + + if (delim_idx == -1) + return false; + + String salt_base64 = entry.substring(3, delim_idx); + String hash_base64 = entry.substring(delim_idx + 1); + + byte[] salt = null; + byte[] hash = null; + + try + { + salt = Base64.decode(salt_base64.toCharArray()); + hash = Base64.decode(hash_base64.toCharArray()); + } + catch (IOException e) + { + return false; + } + + SHA1 sha1 = new SHA1(); + + if (salt.length != sha1.getDigestLength()) + return false; + + byte[] dig = hmacSha1Hash(salt, hostname); + + for (int i = 0; i < dig.length; i++) + if (dig[i] != hash[i]) + return false; + + return true; + } + + private int checkKey(String remoteHostname, PublicKey remoteKey) + { + int result = HOSTKEY_IS_NEW; + + synchronized (publicKeys) + { + + for (KnownHostsEntry ke : publicKeys) { + if (!hostnameMatches(ke.patterns, remoteHostname)) + continue; + + boolean res = matchKeys(ke.key, remoteKey); + + if (res) + return HOSTKEY_IS_OK; + + result = HOSTKEY_HAS_CHANGED; + } + } + return result; + } + + private Vector getAllKnownHostEntries(String hostname) + { + Vector knownHostsEntries = new Vector<>(); + + synchronized (publicKeys) + { + + for (KnownHostsEntry ke : publicKeys) { + if (hostnameMatches(ke.patterns, hostname)) { + knownHostsEntries.addElement(ke); + } + } + } + + return knownHostsEntries; + } + + /** + * Try to find the preferred order of hostkey algorithms for the given hostname. + * Based on the type of hostkey that is present in the internal database. + * an ordered list of hostkey algorithms is returned which can be passed + * to Connection.setServerHostKeyAlgorithms. + * + * @param hostname the hostname (or hostname pattern) to search for + * @return null if no key for the given hostname is present or + * there are keys of multiple types present for the given hostname. Otherwise, + * an array with hostkey algorithms is returned. + */ + public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname) + { + String[] algos = recommendHostkeyAlgorithms(hostname); + + if (algos != null) + return algos; + + InetAddress[] ipAddresses; + + try + { + ipAddresses = InetAddress.getAllByName(hostname); + } + catch (UnknownHostException e) + { + return null; + } + + for (InetAddress ipAddress : ipAddresses) { + algos = recommendHostkeyAlgorithms(ipAddress.getHostAddress()); + + if (algos != null) + return algos; + } + + return null; + } + + private boolean hostnameMatches(String[] hostpatterns, String hostname) + { + boolean isMatch = false; + boolean negate = false; + + hostname = hostname.toLowerCase(); + + for (String hostpattern : hostpatterns) { + if (hostpattern == null) + continue; + + String pattern = null; + + /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed + * entries in lines with multiple entries). + */ + + if ((hostpattern.length() > 0) && (hostpattern.charAt(0) == '!')) { + pattern = hostpattern.substring(1); + negate = true; + } else { + pattern = hostpattern; + negate = false; + } + + /* Optimize, no need to check this entry */ + + if (isMatch && !negate) + continue; + + /* Now compare */ + + if (pattern.charAt(0) == '|') { + if (checkHashed(pattern, hostname)) { + if (negate) + return false; + isMatch = true; + } + } else { + pattern = pattern.toLowerCase(); + + if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1)) { + if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0)) { + if (negate) + return false; + isMatch = true; + } + } else if (pattern.compareTo(hostname) == 0) { + if (negate) + return false; + isMatch = true; + } else { + final int indexColon = pattern.indexOf(':'); + final int indexLastColon = pattern.indexOf(':'); + if (indexColon > 0 && indexColon < pattern.length() - 2 && indexColon == indexLastColon) { + final String bracketizedHost = '[' + hostname + ']'; + if (pattern.startsWith(bracketizedHost)) { + if (negate) + return false; + isMatch = true; + } + } + } + } + } + + return isMatch; + } + + private void initialize(char[] knownHostsData) throws IOException { + final BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData)); + for (String line = br.readLine(); line != null; line = br.readLine()) { + line = line.trim(); + if (line.startsWith("#")) { + continue; + } + + final String[] arr = line.split(" "); + if (arr.length < 3) { + continue; + } + + final String serverHostKeyAlgorithm = arr[1]; + + boolean supportedKeyType = false; + + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + if (algorithm.getKeyFormat().equals(serverHostKeyAlgorithm)) { + supportedKeyType = true; + break; + } + } + + if (!supportedKeyType) { + LOGGER.log(1, "Unsupported key type: " + serverHostKeyAlgorithm); + continue; + } + + final String[] hostnames = arr[0].split(","); + final byte[] msg = Base64.decode(arr[2].toCharArray()); + + try { + addHostkey(hostnames, serverHostKeyAlgorithm, msg); + } + catch (IOWarningException ex) { + LOGGER.log(30, "Ignored invalid line '" + line + "'",ex); + } + } + } + + private void initialize(File knownHosts) throws IOException { + final char[] buffer = new char[512]; + + final CharArrayWriter charWriter = new CharArrayWriter(); + + if (!knownHosts.createNewFile()) { + LOGGER.log(10, "Could not create known hosts file"); + } + + try (Reader reader = new FileReader(knownHosts)) { + while (true) { + final int readCharCount = reader.read(buffer); + if (readCharCount < 0) { + break; + } + + charWriter.write(buffer, 0, readCharCount); + } + } + + initialize(charWriter.toCharArray()); + } + + private boolean matchKeys(PublicKey key1, PublicKey key2) + { + if (null == key1) { + return null == key2; + } + return key1.equals(key2); + } + + private boolean pseudoRegex(char[] pattern, int i, char[] match, int j) + { + /* This matching logic is equivalent to the one present in OpenSSH 4.1 */ + + while (true) + { + /* Are we at the end of the pattern? */ + + if (pattern.length == i) + return (match.length == j); + + if (pattern[i] == '*') + { + i++; + + if (pattern.length == i) + return true; + + if ((pattern[i] != '*') && (pattern[i] != '?')) + { + while (true) + { + if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1)) + return true; + j++; + if (match.length == j) + return false; + } + } + + while (true) + { + if (pseudoRegex(pattern, i, match, j)) + return true; + j++; + if (match.length == j) + return false; + } + } + + if (match.length == j) + return false; + + if ((pattern[i] != '?') && (pattern[i] != match[j])) + return false; + + i++; + j++; + } + } + + private String[] recommendHostkeyAlgorithms(String hostname) + { + String preferredAlgo = null; + + Vector keys = getAllKnownHostEntries(hostname); + + for (KnownHostsEntry key : keys) { + String thisAlgo = key.algorithm; + + if (preferredAlgo != null) { + /* If we find different key types, then return null */ + + if (!preferredAlgo.equals(thisAlgo)) { + return null; + } + + } else { + preferredAlgo = thisAlgo; + } + } + + /* If we did not find anything that we know of, return null */ + + if (preferredAlgo == null) + return null; + + /* Now put the preferred algo to the start of the array. + * You may ask yourself why we do it that way - basically, we could just + * return only the preferred algorithm: since we have a saved key of that + * type (sent earlier from the remote host), then that should work out. + * However, imagine that the server is (for whatever reasons) not offering + * that type of hostkey anymore (e.g., "algorithm-a" was disabled and + * now "algorithm-b" is being used). If we then do not let the server send us + * a fresh key of the new type, then we shoot ourself into the foot: + * the connection cannot be established and hence the user cannot decide + * if he/she wants to accept the new key. + */ + + List supportedAlgorithms = new ArrayList<>(); + + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + supportedAlgorithms.add(supportedAlgorithms.size(), algorithm.getKeyFormat()); + } + + if (supportedAlgorithms.contains(preferredAlgo)) { + supportedAlgorithms.remove(preferredAlgo); + supportedAlgorithms.add(0, preferredAlgo); + } + return supportedAlgorithms.toArray(new String[supportedAlgorithms.size()]); + + } + + /** + * Checks the internal hostkey database for the given hostkey. + * If no matching key can be found, then the hostname is resolved to an IP address + * and the search is repeated using that IP address. + * + * @param hostname the server's hostname, will be matched with all hostname patterns + * @param serverHostKeyAlgorithm type of hostkey being verified + * @param serverHostKey the key blob + * @return

    + *
  • HOSTKEY_IS_OK: the given hostkey matches an entry for the given hostname
  • + *
  • HOSTKEY_IS_NEW: no entries found for this hostname and this type of hostkey
  • + *
  • HOSTKEY_HAS_CHANGED: hostname is known, but with another key of the same type + * (man-in-the-middle attack?)
  • + *
+ * @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type. + */ + public int verifyHostkey(String hostname, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException + { + PublicKey remoteKey = decodeHostKey(serverHostKeyAlgorithm, serverHostKey); + + int result = checkKey(hostname, remoteKey); + + if (result == HOSTKEY_IS_OK) + return result; + + InetAddress[] ipAdresses; + + try + { + ipAdresses = InetAddress.getAllByName(hostname); + } + catch (UnknownHostException e) + { + return result; + } + + for (InetAddress ipAdress : ipAdresses) { + int newresult = checkKey(ipAdress.getHostAddress(), remoteKey); + + if (newresult == HOSTKEY_IS_OK) + return newresult; + + if (newresult == HOSTKEY_HAS_CHANGED) + result = HOSTKEY_HAS_CHANGED; + } + + return result; + } + + private PublicKey decodeHostKey(String hostKeyAlgorithm, byte[] encodedHostKey) throws IOException { + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + if (algorithm.getKeyFormat().equals(hostKeyAlgorithm)) { + return algorithm.decodePublicKey(encodedHostKey); + } + } + + throw new IllegalArgumentException("Unknown hostkey type " + hostKeyAlgorithm); + } + + /** + * Adds a single public key entry to the a known_hosts file. + * This method is designed to be used in a {@link ServerHostKeyVerifier}. + * + * @param knownHosts the file where the publickey entry will be appended. + * @param hostnames a list of hostname patterns - at least one most be specified. Check out the + * OpenSSH sshd man page for a description of the pattern matching algorithm. + * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}. + * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}. + * @throws IOException on failure parsing the key or writing to file + */ + public static void addHostkeyToFile(File knownHosts, String[] hostnames, String serverHostKeyAlgorithm, + byte[] serverHostKey) throws IOException + { + if ((hostnames == null) || (hostnames.length == 0)) + throw new IllegalArgumentException("Need at least one hostname specification"); + + if ((serverHostKeyAlgorithm == null) || (serverHostKey == null)) + throw new IllegalArgumentException(); + + CharArrayWriter writer = new CharArrayWriter(); + + for (int i = 0; i < hostnames.length; i++) + { + if (i != 0) + writer.write(','); + writer.write(hostnames[i]); + } + + writer.write(' '); + writer.write(serverHostKeyAlgorithm); + writer.write(' '); + writer.write(Base64.encode(serverHostKey)); + writer.write("\n"); + + char[] entry = writer.toCharArray(); + + RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw"); + + long len = raf.length(); + + if (len > 0) + { + raf.seek(len - 1); + int last = raf.read(); + if (last != '\n') + raf.write('\n'); + } + + raf.write(new String(entry).getBytes("ISO-8859-1")); + raf.close(); + } + + /** + * Generates a "raw" fingerprint of a hostkey. + * + * @param type either "md5" or "sha1" + * @param keyType the type of key being fingerprinted + * @param hostkey the hostkey + * @return the raw fingerprint + */ + private static byte[] rawFingerPrint(String type, String keyType, byte[] hostkey) + { + Digest dig; + + if ("md5".equals(type)) + { + dig = new MD5(); + } + else if ("sha1".equals(type)) + { + dig = new SHA1(); + } + else + throw new IllegalArgumentException("Unknown hash type " + type); + + boolean supportedKeyType = false; + + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + if (algorithm.getKeyFormat().equals(keyType)) { + supportedKeyType = true; + break; + } + } + + if (!supportedKeyType) { + throw new IllegalArgumentException("Unknown key type " + keyType); + } + + if (hostkey == null) + throw new IllegalArgumentException("hostkey is null"); + + dig.update(hostkey); + byte[] res = new byte[dig.getDigestLength()]; + dig.digest(res); + return res; + } + + /** + * Convert a raw fingerprint to hex representation (XX:YY:ZZ...). + * @param fingerprint raw fingerprint + * @return the hex representation + */ + private static String rawToHexFingerprint(byte[] fingerprint) + { + final char[] alpha = "0123456789abcdef".toCharArray(); + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < fingerprint.length; i++) + { + if (i != 0) + sb.append(':'); + int b = fingerprint[i] & 0xff; + sb.append(alpha[b >> 4]); + sb.append(alpha[b & 15]); + } + + return sb.toString(); + } + + /** + * Convert a raw fingerprint to bubblebabble representation. + * @param raw raw fingerprint + * @return the bubblebabble representation + */ + private static String rawToBubblebabbleFingerprint(byte[] raw) + { + final char[] v = "aeiouy".toCharArray(); + final char[] c = "bcdfghklmnprstvzx".toCharArray(); + + StringBuilder sb = new StringBuilder(); + + int seed = 1; + + int rounds = (raw.length / 2) + 1; + + sb.append('x'); + + for (int i = 0; i < rounds; i++) + { + if (((i + 1) < rounds) || ((raw.length) % 2 != 0)) + { + sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]); + sb.append(c[(raw[2 * i] >> 2) & 15]); + sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]); + + if ((i + 1) < rounds) + { + sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]); + sb.append('-'); + sb.append(c[(((raw[(2 * i) + 1]))) & 15]); + // As long as seed >= 0, seed will be >= 0 afterwards + seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36; + } + } + else + { + sb.append(v[seed % 6]); // seed >= 0, therefore index positive + sb.append('x'); + sb.append(v[seed / 6]); + } + } + + sb.append('x'); + + return sb.toString(); + } + + /** + * Convert a ssh2 key-blob into a human readable hex fingerprint. + * Generated fingerprints are identical to those generated by OpenSSH. + *

+ * Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47. + + * @param keytype the type of key being fingerprinted + * @param publickey key blob + * @return Hex fingerprint + */ + public static String createHexFingerprint(String keytype, byte[] publickey) + { + byte[] raw = rawFingerPrint("md5", keytype, publickey); + return rawToHexFingerprint(raw); + } + + /** + * Convert a ssh2 key-blob into a human readable bubblebabble fingerprint. + * The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints + * that are easier to remember for humans. + *

+ * Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux. + * + * @param keytype the type of key being fingerprinted + * @param publickey key data + * @return Bubblebabble fingerprint + */ + public static String createBubblebabbleFingerprint(String keytype, byte[] publickey) + { + byte[] raw = rawFingerPrint("sha1", keytype, publickey); + return rawToBubblebabbleFingerprint(raw); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/LocalPortForwarder.java b/service/src/main/java/com/trilead/ssh2/LocalPortForwarder.java new file mode 100644 index 0000000..37774be --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/LocalPortForwarder.java @@ -0,0 +1,63 @@ + +package com.trilead.ssh2; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import com.trilead.ssh2.channel.ChannelManager; +import com.trilead.ssh2.channel.LocalAcceptThread; + + +/** + * A LocalPortForwarder forwards TCP/IP connections to a local + * port via the secure tunnel to another host (which may or may not be identical + * to the remote SSH-2 server). Checkout {@link Connection#createLocalPortForwarder(int, String, int)} + * on how to create one. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: LocalPortForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class LocalPortForwarder +{ + ChannelManager cm; + + String host_to_connect; + + int port_to_connect; + + LocalAcceptThread lat; + + LocalPortForwarder(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect) + throws IOException + { + this.cm = cm; + this.host_to_connect = host_to_connect; + this.port_to_connect = port_to_connect; + + lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect); + lat.setDaemon(true); + lat.start(); + } + + LocalPortForwarder(ChannelManager cm, InetSocketAddress addr, String host_to_connect, int port_to_connect) + throws IOException + { + this.cm = cm; + this.host_to_connect = host_to_connect; + this.port_to_connect = port_to_connect; + + lat = new LocalAcceptThread(cm, addr, host_to_connect, port_to_connect); + lat.setDaemon(true); + lat.start(); + } + + /** + * Stop TCP/IP forwarding of newly arriving connections. + * + * @throws IOException the io exception + */ + public void close() throws IOException + { + lat.stopWorking(); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/LocalStreamForwarder.java b/service/src/main/java/com/trilead/ssh2/LocalStreamForwarder.java new file mode 100644 index 0000000..20dfd01 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/LocalStreamForwarder.java @@ -0,0 +1,78 @@ + +package com.trilead.ssh2; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.trilead.ssh2.channel.Channel; +import com.trilead.ssh2.channel.ChannelManager; +import com.trilead.ssh2.channel.LocalAcceptThread; + + +/** + * A LocalStreamForwarder forwards an Input- and Outputstream + * pair via the secure tunnel to another host (which may or may not be identical + * to the remote SSH-2 server). + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: LocalStreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class LocalStreamForwarder +{ + ChannelManager cm; + + String host_to_connect; + int port_to_connect; + LocalAcceptThread lat; + + Channel cn; + + LocalStreamForwarder(ChannelManager cm, String host_to_connect, int port_to_connect) throws IOException + { + this.cm = cm; + this.host_to_connect = host_to_connect; + this.port_to_connect = port_to_connect; + + cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, "127.0.0.1", 0); + } + + /** + * @return An InputStream object. + * @throws IOException the io exception + */ + public InputStream getInputStream() throws IOException + { + return cn.getStdoutStream(); + } + + /** + * Get the OutputStream. Please be aware that the implementation MAY use an + * internal buffer. To make sure that the buffered data is sent over the + * tunnel, you have to call the flush method of the + * OutputStream. To signal EOF, please use the + * close method of the OutputStream. + * + * @return An OutputStream object. + * @throws IOException the io exception + */ + public OutputStream getOutputStream() throws IOException + { + return cn.getStdinStream(); + } + + /** + * Close the underlying SSH forwarding channel and free up resources. + * You can also use this method to force the shutdown of the underlying + * forwarding channel. Pending output (OutputStream not flushed) will NOT + * be sent. Pending input (InputStream) can still be read. If the shutdown + * operation is already in progress (initiated from either side), then this + * call is a no-op. + * + * @throws IOException the io exception + */ + public void close() throws IOException + { + cm.closeChannel(cn, "Closed due to user request.", true); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/ProxyData.java b/service/src/main/java/com/trilead/ssh2/ProxyData.java new file mode 100644 index 0000000..ad5f82e --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/ProxyData.java @@ -0,0 +1,28 @@ + +package com.trilead.ssh2; + +import java.io.IOException; +import java.net.Socket; + +/** + * An abstract interface implemented by all proxy data implementations. + * + * @see HTTPProxyData + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public interface ProxyData +{ + /** + * Connects the socket to the given destination using the proxy method that this instance + * represents. + */ + Socket openConnection(String hostname, int port, int connectTimeout, int readTimeout) throws IOException; + + /** + * Fecha conexão do proxy + */ + void close(); +} diff --git a/service/src/main/java/com/trilead/ssh2/RandomFactory.java b/service/src/main/java/com/trilead/ssh2/RandomFactory.java new file mode 100644 index 0000000..6585ea9 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/RandomFactory.java @@ -0,0 +1,31 @@ +package com.trilead.ssh2; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * Creates {@link SecureRandom} + * + * @author Kohsuke Kawaguchi + */ +class RandomFactory { + static SecureRandom create() { + try { + // JENKINS-20108 + // on Unix, "new SecureRandom()" uses NativePRNG that uses a VM-wide lock, which results in + // SecureRandom.nextInt() contention when there are lots of concurrent connections. + // SHA1PRNG avoids this problem. This PRNG still gets seeded from (blocking) /dev/random, + // which assures security. + // + // note that SHA1PRNG is not a standard. See http://security.stackexchange.com/questions/47871/ + // + // there's also http://coding.tocea.com/scertify-code/dont-use-the-sha1-prng-randomness-generator/ + // which claims SHA1PRNG has "statistical defects" without details. I discount the credibility of + // this claim based on the lack of details, and that this is not reported as a vulnerability upstream. + return SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + // fall back + return new SecureRandom(); + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/SCPClient.java b/service/src/main/java/com/trilead/ssh2/SCPClient.java new file mode 100644 index 0000000..29dcb9d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/SCPClient.java @@ -0,0 +1,724 @@ + +package com.trilead.ssh2; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A very basic SCPClient that can be used to copy files from/to + * the SSH-2 server. On the server side, the "scp" program must be in the PATH. + *

+ * This scp client is thread safe - you can download (and upload) different sets + * of files concurrently without any troubles. The SCPClient is + * actually mapping every request to a distinct {@link Session}. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SCPClient.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ + +public class SCPClient +{ + Connection conn; + + class LenNamePair + { + long length; + String filename; + } + + public SCPClient(Connection conn) + { + if (conn == null) + throw new IllegalArgumentException("Cannot accept null argument!"); + this.conn = conn; + } + + private void readResponse(InputStream is) throws IOException + { + int c = is.read(); + + if (c == 0) + return; + + if (c == 1) { + String err = receiveLine(is); + throw new IOException("Remote scp terminated with error (" + err + ")."); + } + + throw new IOException("Remote scp terminated with error code " + c); + } + + private String receiveLine(InputStream is) throws IOException + { + StringBuffer sb = new StringBuffer(30); + + while (true) + { + /* + * This is a random limit - if your path names are longer, then + * adjust it + */ + + if (sb.length() > 8192) + throw new IOException("Remote scp sent a too long line"); + + int c = is.read(); + + if (c < 0) + throw new IOException("Remote scp terminated unexpectedly."); + + if (c == '\n') + break; + + sb.append((char) c); + + } + return sb.toString(); + } + + private LenNamePair parseCLine(String line) throws IOException + { + /* Minimum line: "xxxx y z" ---> 8 chars */ + + long len; + + if (line.length() < 8) + throw new IOException("Malformed C line sent by remote SCP binary, line too short."); + + if ((line.charAt(4) != ' ') || (line.charAt(5) == ' ')) + throw new IOException("Malformed C line sent by remote SCP binary."); + + int length_name_sep = line.indexOf(' ', 5); + + if (length_name_sep == -1) + throw new IOException("Malformed C line sent by remote SCP binary."); + + String length_substring = line.substring(5, length_name_sep); + String name_substring = line.substring(length_name_sep + 1); + + if ((length_substring.length() <= 0) || (name_substring.length() <= 0)) + throw new IOException("Malformed C line sent by remote SCP binary."); + + if ((6 + length_substring.length() + name_substring.length()) != line.length()) + throw new IOException("Malformed C line sent by remote SCP binary."); + + try + { + len = Long.parseLong(length_substring); + } + catch (NumberFormatException e) + { + throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length."); + } + + if (len < 0) + throw new IOException("Malformed C line sent by remote SCP binary, illegal file length."); + + LenNamePair lnp = new LenNamePair(); + lnp.length = len; + lnp.filename = name_substring; + + return lnp; + } + + private void sendBytes(Session sess, byte[] data, String fileName, String mode) throws IOException + { + OutputStream os = sess.getStdin(); + InputStream is = new BufferedInputStream(sess.getStdout(), 512); + + readResponse(is); + + String cline = "C" + mode + " " + data.length + " " + fileName + "\n"; + + os.write(cline.getBytes("ISO-8859-1")); + os.flush(); + + readResponse(is); + + os.write(data, 0, data.length); + os.write(0); + os.flush(); + + readResponse(is); + + os.write("E\n".getBytes("ISO-8859-1")); + os.flush(); + } + + private void sendFiles(Session sess, String[] files, String[] remoteFiles, String mode) throws IOException + { + byte[] buffer = new byte[8192]; + + OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000); + InputStream is = new BufferedInputStream(sess.getStdout(), 512); + + readResponse(is); + + for (int i = 0; i < files.length; i++) + { + File f = new File(files[i]); + long remain = f.length(); + + String remoteName; + + if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null)) + remoteName = remoteFiles[i]; + else + remoteName = f.getName(); + + String cline = "C" + mode + " " + remain + " " + remoteName + "\n"; + + os.write(cline.getBytes("ISO-8859-1")); + os.flush(); + + readResponse(is); + + FileInputStream fis = null; + + try + { + fis = new FileInputStream(f); + + while (remain > 0) + { + int trans; + if (remain > buffer.length) + trans = buffer.length; + else + trans = (int) remain; + + if (fis.read(buffer, 0, trans) != trans) + throw new IOException("Cannot read enough from local file " + files[i]); + + os.write(buffer, 0, trans); + + remain -= trans; + } + } + finally + { + if (fis != null) + fis.close(); + } + + os.write(0); + os.flush(); + + readResponse(is); + } + + os.write("E\n".getBytes("ISO-8859-1")); + os.flush(); + } + + private void receiveFiles(Session sess, OutputStream[] targets) throws IOException + { + byte[] buffer = new byte[8192]; + + OutputStream os = new BufferedOutputStream(sess.getStdin(), 512); + InputStream is = new BufferedInputStream(sess.getStdout(), 40000); + + os.write(0x0); + os.flush(); + + for (int i = 0; i < targets.length; i++) + { + LenNamePair lnp = null; + + while (true) + { + int c = is.read(); + if (c < 0) + throw new IOException("Remote scp terminated unexpectedly."); + + String line = receiveLine(is); + + if (c == 'T') + { + /* Ignore modification times */ + + continue; + } + + if ((c == 1) || (c == 2)) + throw new IOException("Remote SCP error: " + line); + + if (c == 'C') + { + lnp = parseCLine(line); + break; + + } + throw new IOException("Remote SCP error: " + ((char) c) + line); + } + + os.write(0x0); + os.flush(); + + long remain = lnp.length; + + while (remain > 0) + { + int trans; + if (remain > buffer.length) + trans = buffer.length; + else + trans = (int) remain; + + int this_time_received = is.read(buffer, 0, trans); + + if (this_time_received < 0) + { + throw new IOException("Remote scp terminated connection unexpectedly"); + } + + targets[i].write(buffer, 0, this_time_received); + + remain -= this_time_received; + } + + readResponse(is); + + os.write(0x0); + os.flush(); + } + } + + private void receiveFiles(Session sess, String[] files, String target) throws IOException + { + byte[] buffer = new byte[8192]; + + OutputStream os = new BufferedOutputStream(sess.getStdin(), 512); + InputStream is = new BufferedInputStream(sess.getStdout(), 40000); + + os.write(0x0); + os.flush(); + + for (int i = 0; i < files.length; i++) + { + LenNamePair lnp = null; + + while (true) + { + int c = is.read(); + if (c < 0) + throw new IOException("Remote scp terminated unexpectedly."); + + String line = receiveLine(is); + + if (c == 'T') + { + /* Ignore modification times */ + + continue; + } + + if ((c == 1) || (c == 2)) + throw new IOException("Remote SCP error: " + line); + + if (c == 'C') + { + lnp = parseCLine(line); + break; + + } + throw new IOException("Remote SCP error: " + ((char) c) + line); + } + + os.write(0x0); + os.flush(); + + File f = new File(target + File.separatorChar + lnp.filename); + FileOutputStream fop = null; + + try + { + fop = new FileOutputStream(f); + + long remain = lnp.length; + + while (remain > 0) + { + int trans; + if (remain > buffer.length) + trans = buffer.length; + else + trans = (int) remain; + + int this_time_received = is.read(buffer, 0, trans); + + if (this_time_received < 0) + { + throw new IOException("Remote scp terminated connection unexpectedly"); + } + + fop.write(buffer, 0, this_time_received); + + remain -= this_time_received; + } + } + finally + { + if (fop != null) + fop.close(); + } + + readResponse(is); + + os.write(0x0); + os.flush(); + } + } + + /** + * Copy a local file to a remote directory, uses mode 0600 when creating the + * file on the remote side. + * + * @param localFile + * Path and name of local file. + * @param remoteTargetDirectory + * Remote target directory. Use an empty string to specify the + * default directory. + * + * @throws IOException the io exception + */ + public void put(String localFile, String remoteTargetDirectory) throws IOException + { + put(new String[] { localFile }, remoteTargetDirectory, "0600"); + } + + /** + * Copy a set of local files to a remote directory, uses mode 0600 when + * creating files on the remote side. + * + * @param localFiles + * Paths and names of local file names. + * @param remoteTargetDirectory + * Remote target directory. Use an empty string to specify the + * default directory. + * + * @throws IOException the io exception + */ + + public void put(String[] localFiles, String remoteTargetDirectory) throws IOException + { + put(localFiles, remoteTargetDirectory, "0600"); + } + + /** + * Copy a local file to a remote directory, uses the specified mode when + * creating the file on the remote side. + * + * @param localFile + * Path and name of local file. + * @param remoteTargetDirectory + * Remote target directory. Use an empty string to specify the + * default directory. + * @param mode + * a four digit string (e.g., 0644, see "man chmod", "man open") + * @throws IOException the io exception + */ + public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException + { + put(new String[] { localFile }, remoteTargetDirectory, mode); + } + + /** + * Copy a local file to a remote directory, uses the specified mode and + * remote filename when creating the file on the remote side. + * + * @param localFile + * Path and name of local file. + * @param remoteFileName + * The name of the file which will be created in the remote + * target directory. + * @param remoteTargetDirectory + * Remote target directory. Use an empty string to specify the + * default directory. + * @param mode + * a four digit string (e.g., 0644, see "man chmod", "man open") + * @throws IOException the io exception + */ + public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode) + throws IOException + { + put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode); + } + + /** + * Create a remote file and copy the contents of the passed byte array into + * it. Uses mode 0600 for creating the remote file. + * + * @param data + * the data to be copied into the remote file. + * @param remoteFileName + * The name of the file which will be created in the remote + * target directory. + * @param remoteTargetDirectory + * Remote target directory. Use an empty string to specify the + * default directory. + * @throws IOException the io exception + */ + + public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException + { + put(data, remoteFileName, remoteTargetDirectory, "0600"); + } + + /** + * Create a remote file and copy the contents of the passed byte array into + * it. The method use the specified mode when creating the file on the + * remote side. + * + * @param data + * the data to be copied into the remote file. + * @param remoteFileName + * The name of the file which will be created in the remote + * target directory. + * @param remoteTargetDirectory + * Remote target directory. Use an empty string to specify the + * default directory. + * @param mode + * a four digit string (e.g., 0644, see "man chmod", "man open") + * @throws IOException the io exception + */ + public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException + { + Session sess = null; + + if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null)) + throw new IllegalArgumentException("Null argument."); + + if (mode.length() != 4) + throw new IllegalArgumentException("Invalid mode."); + + for (int i = 0; i < mode.length(); i++) + if (Character.isDigit(mode.charAt(i)) == false) + throw new IllegalArgumentException("Invalid mode."); + + remoteTargetDirectory = remoteTargetDirectory.trim(); + remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : "."; + + String cmd = "scp -t -d " + remoteTargetDirectory; + + try + { + sess = conn.openSession(); + sess.execCommand(cmd); + sendBytes(sess, data, remoteFileName, mode); + } + catch (IOException e) + { + throw (IOException) new IOException("Error during SCP transfer.").initCause(e); + } + finally + { + if (sess != null) + sess.close(); + } + } + + /** + * Copy a set of local files to a remote directory, uses the specified mode + * when creating the files on the remote side. + * + * @param localFiles + * Paths and names of the local files. + * @param remoteTargetDirectory + * Remote target directory. Use an empty string to specify the + * default directory. + * @param mode + * a four digit string (e.g., 0644, see "man chmod", "man open") + * @throws IOException the io exception + */ + public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException + { + put(localFiles, null, remoteTargetDirectory, mode); + } + + public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode) + throws IOException + { + Session sess = null; + + /* + * remoteFiles may be null, indicating that the local filenames shall be + * used + */ + + if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null)) + throw new IllegalArgumentException("Null argument."); + + if (mode.length() != 4) + throw new IllegalArgumentException("Invalid mode."); + + for (int i = 0; i < mode.length(); i++) + if (Character.isDigit(mode.charAt(i)) == false) + throw new IllegalArgumentException("Invalid mode."); + + if (localFiles.length == 0) + return; + + remoteTargetDirectory = remoteTargetDirectory.trim(); + remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : "."; + + String cmd = "scp -t -d " + remoteTargetDirectory; + + for (int i = 0; i < localFiles.length; i++) + { + if (localFiles[i] == null) + throw new IllegalArgumentException("Cannot accept null filename."); + } + + try + { + sess = conn.openSession(); + sess.execCommand(cmd); + sendFiles(sess, localFiles, remoteFiles, mode); + } + catch (IOException e) + { + throw (IOException) new IOException("Error during SCP transfer.").initCause(e); + } + finally + { + if (sess != null) + sess.close(); + } + } + + /** + * Download a file from the remote server to a local directory. + * + * @param remoteFile + * Path and name of the remote file. + * @param localTargetDirectory + * Local directory to put the downloaded file. + * + * @throws IOException the io exception + */ + public void get(String remoteFile, String localTargetDirectory) throws IOException + { + get(new String[] { remoteFile }, localTargetDirectory); + } + + /** + * Download a file from the remote server and pipe its contents into an + * OutputStream. Please note that, to enable flexible usage + * of this method, the OutputStream will not be closed nor + * flushed. + * + * @param remoteFile + * Path and name of the remote file. + * @param target + * OutputStream where the contents of the file will be sent to. + * @throws IOException the io exception + */ + public void get(String remoteFile, OutputStream target) throws IOException + { + get(new String[] { remoteFile }, new OutputStream[] { target }); + } + + private void get(String remoteFiles[], OutputStream[] targets) throws IOException + { + Session sess = null; + + if ((remoteFiles == null) || (targets == null)) + throw new IllegalArgumentException("Null argument."); + + if (remoteFiles.length != targets.length) + throw new IllegalArgumentException("Length of arguments does not match."); + + if (remoteFiles.length == 0) + return; + + String cmd = "scp -f"; + + for (int i = 0; i < remoteFiles.length; i++) + { + if (remoteFiles[i] == null) + throw new IllegalArgumentException("Cannot accept null filename."); + + String tmp = remoteFiles[i].trim(); + + if (tmp.length() == 0) + throw new IllegalArgumentException("Cannot accept empty filename."); + + cmd += (" " + tmp); + } + + try + { + sess = conn.openSession(); + sess.execCommand(cmd); + receiveFiles(sess, targets); + } + catch (IOException e) + { + throw (IOException) new IOException("Error during SCP transfer.").initCause(e); + } + finally + { + if (sess != null) + sess.close(); + } + } + + /** + * Download a set of files from the remote server to a local directory. + * + * @param remoteFiles + * Paths and names of the remote files. + * @param localTargetDirectory + * Local directory to put the downloaded files. + * + * @throws IOException the io exception + */ + public void get(String remoteFiles[], String localTargetDirectory) throws IOException + { + Session sess = null; + + if ((remoteFiles == null) || (localTargetDirectory == null)) + throw new IllegalArgumentException("Null argument."); + + if (remoteFiles.length == 0) + return; + + String cmd = "scp -f"; + + for (int i = 0; i < remoteFiles.length; i++) + { + if (remoteFiles[i] == null) + throw new IllegalArgumentException("Cannot accept null filename."); + + String tmp = remoteFiles[i].trim(); + + if (tmp.length() == 0) + throw new IllegalArgumentException("Cannot accept empty filename."); + + cmd += (" " + tmp); + } + + try + { + sess = conn.openSession(); + sess.execCommand(cmd); + receiveFiles(sess, remoteFiles, localTargetDirectory); + } + catch (IOException e) + { + throw (IOException) new IOException("Error during SCP transfer.").initCause(e); + } + finally + { + if (sess != null) + sess.close(); + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/SFTPException.java b/service/src/main/java/com/trilead/ssh2/SFTPException.java new file mode 100644 index 0000000..d97723f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/SFTPException.java @@ -0,0 +1,91 @@ + +package com.trilead.ssh2; + +import java.io.IOException; + +import com.trilead.ssh2.sftp.ErrorCodes; + + +/** + * Used in combination with the SFTPv3Client. This exception wraps + * error messages sent by the SFTP server. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SFTPException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class SFTPException extends IOException +{ + private static final long serialVersionUID = 578654644222421811L; + + private final String sftpErrorMessage; + private final int sftpErrorCode; + + private static String constructMessage(String s, int errorCode) + { + String[] detail = ErrorCodes.getDescription(errorCode); + + if (detail == null) + return s + " (UNKNOW SFTP ERROR CODE)"; + + return s + " (" + detail[0] + ": " + detail[1] + ")"; + } + + SFTPException(String msg, int errorCode) + { + super(constructMessage(msg, errorCode)); + sftpErrorMessage = msg; + sftpErrorCode = errorCode; + } + + /** + * Get the error message sent by the server. Often, this + * message does not help a lot (e.g., "failure"). + * + * @return the plain string as sent by the server. + */ + public String getServerErrorMessage() + { + return sftpErrorMessage; + } + + /** + * Get the error code sent by the server. + * + * @return an error code as defined in the SFTP specs. + */ + public int getServerErrorCode() + { + return sftpErrorCode; + } + + /** + * Get the symbolic name of the error code as given in the SFTP specs. + * + * @return e.g., "SSH_FX_INVALID_FILENAME". + */ + public String getServerErrorCodeSymbol() + { + String[] detail = ErrorCodes.getDescription(sftpErrorCode); + + if (detail == null) + return "UNKNOW SFTP ERROR CODE " + sftpErrorCode; + + return detail[0]; + } + + /** + * Get the description of the error code as given in the SFTP specs. + * + * @return e.g., "The filename is not valid." + */ + public String getServerErrorCodeVerbose() + { + String[] detail = ErrorCodes.getDescription(sftpErrorCode); + + if (detail == null) + return "The error code " + sftpErrorCode + " is unknown."; + + return detail[1]; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/SFTPv3Client.java b/service/src/main/java/com/trilead/ssh2/SFTPv3Client.java new file mode 100644 index 0000000..4769809 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/SFTPv3Client.java @@ -0,0 +1,1516 @@ +package com.trilead.ssh2; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Vector; + +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; +import com.trilead.ssh2.sftp.AttribFlags; +import com.trilead.ssh2.sftp.ErrorCodes; +import com.trilead.ssh2.sftp.Packet; + + +/** + * A SFTPv3Client represents a SFTP (protocol version 3) + * client connection tunnelled over a SSH-2 connection. This is a very simple + * (synchronous) implementation. + *

+ * Basically, most methods in this class map directly to one of + * the packet types described in draft-ietf-secsh-filexfer-02.txt. + *

+ * Note: this is experimental code. + *

+ * Error handling: the methods of this class throw IOExceptions. However, unless + * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will + * be thrown (a subclass of IOException). Therefore, you can implement more verbose + * behavior by checking if a thrown exception if of this type. If yes, then you + * can cast the exception and access detailed information about the failure. + *

+ * Notes about file names, directory names and paths, copy-pasted + * from the specs: + *

    + *
  • SFTP v3 represents file names as strings. File names are + * assumed to use the slash ('/') character as a directory separator.
  • + *
  • File names starting with a slash are "absolute", and are relative to + * the root of the file system. Names starting with any other character + * are relative to the user's default directory (home directory).
  • + *
  • Servers SHOULD interpret a path name component ".." as referring to + * the parent directory, and "." as referring to the current directory. + * If the server implementation limits access to certain parts of the + * file system, it must be extra careful in parsing file names when + * enforcing such restrictions. There have been numerous reported + * security bugs where a ".." in a path name has allowed access outside + * the intended area.
  • + *
  • An empty path name is valid, and it refers to the user's default + * directory (usually the user's home directory).
  • + *
+ *

+ * If you are still not tired then please go on and read the comment for + * {@link #setCharset(String)}. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $ + */ +public class SFTPv3Client +{ + final Connection conn; + final Session sess; + final PrintStream debug; + + boolean flag_closed = false; + + InputStream is; + OutputStream os; + + int protocol_version = 0; + HashMap server_extensions = new HashMap(); + + int next_request_id = 1000; + + String charsetName = null; + + /** + * Create a SFTP v3 client. + * + * @param conn The underlying SSH-2 connection to be used. + * @param debug debug + * @throws IOException the io exception + * + * @deprecated this constructor (debug version) will disappear in the future, + * use {@link #SFTPv3Client(Connection)} instead. + */ + public SFTPv3Client(Connection conn, PrintStream debug) throws IOException + { + if (conn == null) + throw new IllegalArgumentException("Cannot accept null argument!"); + + this.conn = conn; + this.debug = debug; + + if (debug != null) + debug.println("Opening session and starting SFTP subsystem."); + + sess = conn.openSession(); + sess.startSubSystem("sftp"); + + is = sess.getStdout(); + os = new BufferedOutputStream(sess.getStdin(), 2048); + + if ((is == null) || (os == null)) + throw new IOException("There is a problem with the streams of the underlying channel."); + + init(); + } + + /** + * Create a SFTP v3 client. + * + * @param conn The underlying SSH-2 connection to be used. + * @throws IOException the io exception + */ + public SFTPv3Client(Connection conn) throws IOException + { + this(conn, null); + } + + /** + * Set the charset used to convert between Java Unicode Strings and byte encodings + * used by the server for paths and file names. Unfortunately, the SFTP v3 draft + * says NOTHING about such conversions (well, with the exception of error messages + * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names + * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3 + * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with + * filenames containing german umlauts). "windows-1252" seems to work better for Europe. + * Luckily, "windows-1252" is the platform default in my case =). + *

+ * If you don't set anything, then the platform default will be used (this is the default + * behavior). + * + * @see #getCharset() + * + * @param charset the name of the charset to be used or null to use the platform's + * default encoding. + * @throws IOException the io exception + */ + public void setCharset(String charset) throws IOException + { + if (charset == null) + { + charsetName = charset; + return; + } + + try + { + Charset.forName(charset); + } + catch (Exception e) + { + throw (IOException) new IOException("This charset is not supported").initCause(e); + } + charsetName = charset; + } + + /** + * The currently used charset for filename encoding/decoding. + * + * @see #setCharset(String) + * + * @return The name of the charset (null if the platform's default charset is being used) + */ + public String getCharset() + { + return charsetName; + } + + private final void checkHandleValidAndOpen(SFTPv3FileHandle handle) throws IOException + { + if (handle.client != this) + throw new IOException("The file handle was created with another SFTPv3FileHandle instance."); + + if (handle.isClosed == true) + throw new IOException("The file handle is closed."); + } + + private final void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException + { + int msglen = len + 1; + + if (type != Packet.SSH_FXP_INIT) + msglen += 4; + + os.write(msglen >> 24); + os.write(msglen >> 16); + os.write(msglen >> 8); + os.write(msglen); + os.write(type); + + if (type != Packet.SSH_FXP_INIT) + { + os.write(requestId >> 24); + os.write(requestId >> 16); + os.write(requestId >> 8); + os.write(requestId); + } + + os.write(msg, off, len); + os.flush(); + } + + private final void sendMessage(int type, int requestId, byte[] msg) throws IOException + { + sendMessage(type, requestId, msg, 0, msg.length); + } + + private final void readBytes(byte[] buff, int pos, int len) throws IOException + { + while (len > 0) + { + int count = is.read(buff, pos, len); + if (count < 0) + throw new IOException("Unexpected end of sftp stream."); + if ((count == 0) || (count > len)) + throw new IOException("Underlying stream implementation is bogus!"); + len -= count; + pos += count; + } + } + + /** + * Read a message and guarantee that the contents is not larger than + * maxlen bytes. + *

+ * Note: receiveMessage(34000) actually means that the message may be up to 34004 + * bytes (the length attribute preceeding the contents is 4 bytes). + * + * @param maxlen + * @return the message contents + * @throws IOException the io exception + */ + private final byte[] receiveMessage(int maxlen) throws IOException + { + byte[] msglen = new byte[4]; + + readBytes(msglen, 0, 4); + + int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff)); + + if ((len > maxlen) || (len <= 0)) + throw new IOException("Illegal sftp packet len: " + len); + + byte[] msg = new byte[len]; + + readBytes(msg, 0, len); + + return msg; + } + + private final int generateNextRequestID() + { + synchronized (this) + { + return next_request_id++; + } + } + + private final void closeHandle(byte[] handle) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(handle, 0, handle.length); + + sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException + { + /* + * uint32 flags + * uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE + * uint32 uid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID + * uint32 gid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID + * uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS + * uint32 atime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME + * uint32 mtime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME + * uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED + * string extended_type + * string extended_data + * ... more extended data (extended_type - extended_data pairs), + * so that number of pairs equals extended_count + */ + + SFTPv3FileAttributes fa = new SFTPv3FileAttributes(); + + int flags = tr.readUINT32(); + + if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0) + { + if (debug != null) + debug.println("SSH_FILEXFER_ATTR_SIZE"); + fa.size = new Long(tr.readUINT64()); + } + + if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0) + { + if (debug != null) + debug.println("SSH_FILEXFER_ATTR_V3_UIDGID"); + fa.uid = new Integer(tr.readUINT32()); + fa.gid = new Integer(tr.readUINT32()); + } + + if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) + { + if (debug != null) + debug.println("SSH_FILEXFER_ATTR_PERMISSIONS"); + fa.permissions = new Integer(tr.readUINT32()); + } + + if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0) + { + if (debug != null) + debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME"); + fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl); + fa.mtime = new Long(((long)tr.readUINT32()) & 0xffffffffl); + + } + + if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0) + { + int count = tr.readUINT32(); + + if (debug != null) + debug.println("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")"); + + /* Read it anyway to detect corrupt packets */ + + while (count > 0) + { + tr.readByteString(); + tr.readByteString(); + count--; + } + } + + return fa; + } + + /** + * Retrieve the file attributes of an open file. + * + * @param handle a SFTPv3FileHandle handle. + * @return a SFTPv3FileAttributes object. + * @throws IOException the io exception + */ + public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException + { + checkHandleValidAndOpen(handle); + + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); + + if (debug != null) + { + debug.println("Sending SSH_FXP_FSTAT..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes()); + + byte[] resp = receiveMessage(34000); + + if (debug != null) + { + debug.println("Got REPLY."); + debug.flush(); + } + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_ATTRS) + { + return readAttrs(tr); + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + throw new SFTPException(tr.readString(), errorCode); + } + + private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(path, charsetName); + + if (debug != null) + { + debug.println("Sending SSH_FXP_STAT/SSH_FXP_LSTAT..."); + debug.flush(); + } + + sendMessage(statMethod, req_id, tw.getBytes()); + + byte[] resp = receiveMessage(34000); + + if (debug != null) + { + debug.println("Got REPLY."); + debug.flush(); + } + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_ATTRS) + { + return readAttrs(tr); + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + throw new SFTPException(tr.readString(), errorCode); + } + + /** + * Retrieve the file attributes of a file. This method + * follows symbolic links on the server. + * + * @see #lstat(String) + * + * @param path See the {@link SFTPv3Client comment} for the class for more details. + * @return a SFTPv3FileAttributes object. + * @throws IOException the io exception + */ + public SFTPv3FileAttributes stat(String path) throws IOException + { + return statBoth(path, Packet.SSH_FXP_STAT); + } + + /** + * Retrieve the file attributes of a file. This method + * does NOT follow symbolic links on the server. + * + * @see #stat(String) + * + * @param path See the {@link SFTPv3Client comment} for the class for more details. + * @return a SFTPv3FileAttributes object. + * @throws IOException the io exception + */ + public SFTPv3FileAttributes lstat(String path) throws IOException + { + return statBoth(path, Packet.SSH_FXP_LSTAT); + } + + /** + * Read the target of a symbolic link. + * + * @param path See the {@link SFTPv3Client comment} for the class for more details. + * @return The target of the link. + * @throws IOException the io exception + */ + public String readLink(String path) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(path, charsetName); + + if (debug != null) + { + debug.println("Sending SSH_FXP_READLINK..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes()); + + byte[] resp = receiveMessage(34000); + + if (debug != null) + { + debug.println("Got REPLY."); + debug.flush(); + } + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_NAME) + { + int count = tr.readUINT32(); + + if (count != 1) + throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); + + return tr.readString(charsetName); + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + throw new SFTPException(tr.readString(), errorCode); + } + + private void expectStatusOKMessage(int id) throws IOException + { + byte[] resp = receiveMessage(34000); + + if (debug != null) + { + debug.println("Got REPLY."); + debug.flush(); + } + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != id) + throw new IOException("The server sent an invalid id field."); + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + if (errorCode == ErrorCodes.SSH_FX_OK) + return; + + throw new SFTPException(tr.readString(), errorCode); + } + + /** + * Modify the attributes of a file. Used for operations such as changing + * the ownership, permissions or access times, as well as for truncating a file. + * + * @param path See the {@link SFTPv3Client comment} for the class for more details. + * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be + * made to the attributes of the file. Empty fields will be ignored. + * @throws IOException the io exception + */ + public void setstat(String path, SFTPv3FileAttributes attr) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(path, charsetName); + tw.writeBytes(createAttrs(attr)); + + if (debug != null) + { + debug.println("Sending SSH_FXP_SETSTAT..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + /** + * Modify the attributes of a file. Used for operations such as changing + * the ownership, permissions or access times, as well as for truncating a file. + * + * @param handle a SFTPv3FileHandle handle + * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be + * made to the attributes of the file. Empty fields will be ignored. + * @throws IOException the io exception + */ + public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException + { + checkHandleValidAndOpen(handle); + + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); + tw.writeBytes(createAttrs(attr)); + + if (debug != null) + { + debug.println("Sending SSH_FXP_FSETSTAT..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + /** + * Create a symbolic link on the server. Creates a link "src" that points + * to "target". + * + * @param src See the {@link SFTPv3Client comment} for the class for more details. + * @param target See the {@link SFTPv3Client comment} for the class for more details. + * @throws IOException the io exception + */ + public void createSymlink(String src, String target) throws IOException + { + int req_id = generateNextRequestID(); + + /* Either I am too stupid to understand the SFTP draft + * or the OpenSSH guys changed the semantics of src and target. + */ + + TypesWriter tw = new TypesWriter(); + tw.writeString(target, charsetName); + tw.writeString(src, charsetName); + + if (debug != null) + { + debug.println("Sending SSH_FXP_SYMLINK..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + /** + * Have the server canonicalize any given path name to an absolute path. + * This is useful for converting path names containing ".." components or + * relative pathnames without a leading slash into absolute paths. + * + * @param path See the {@link SFTPv3Client comment} for the class for more details. + * @return An absolute path. + * @throws IOException the io exception + */ + public String canonicalPath(String path) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(path, charsetName); + + if (debug != null) + { + debug.println("Sending SSH_FXP_REALPATH..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes()); + + byte[] resp = receiveMessage(34000); + + if (debug != null) + { + debug.println("Got REPLY."); + debug.flush(); + } + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_NAME) + { + int count = tr.readUINT32(); + + if (count != 1) + throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); + + return tr.readString(charsetName); + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + throw new SFTPException(tr.readString(), errorCode); + } + + private final Vector scanDirectory(byte[] handle) throws IOException + { + Vector files = new Vector(); + + while (true) + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(handle, 0, handle.length); + + if (debug != null) + { + debug.println("Sending SSH_FXP_READDIR..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes()); + + /* Some servers send here a packet with size > 34000 */ + /* To whom it may concern: please learn to read the specs. */ + + byte[] resp = receiveMessage(65536); + + if (debug != null) + { + debug.println("Got REPLY."); + debug.flush(); + } + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_NAME) + { + int count = tr.readUINT32(); + + if (debug != null) + debug.println("Parsing " + count + " name entries..."); + + while (count > 0) + { + SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry(); + + dirEnt.filename = tr.readString(charsetName); + dirEnt.longEntry = tr.readString(charsetName); + + dirEnt.attributes = readAttrs(tr); + files.addElement(dirEnt); + + if (debug != null) + debug.println("File: '" + dirEnt.filename + "'"); + count--; + } + continue; + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + if (errorCode == ErrorCodes.SSH_FX_EOF) + return files; + + throw new SFTPException(tr.readString(), errorCode); + } + } + + private final byte[] openDirectory(String path) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(path, charsetName); + + if (debug != null) + { + debug.println("Sending SSH_FXP_OPENDIR..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes()); + + byte[] resp = receiveMessage(34000); + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_HANDLE) + { + if (debug != null) + { + debug.println("Got SSH_FXP_HANDLE."); + debug.flush(); + } + + byte[] handle = tr.readByteString(); + return handle; + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + String errorMessage = tr.readString(); + + throw new SFTPException(errorMessage, errorCode); + } + + private final String expandString(byte[] b, int off, int len) + { + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < len; i++) + { + int c = b[off + i] & 0xff; + + if ((c >= 32) && (c <= 126)) + { + sb.append((char) c); + } + else + { + sb.append("{0x" + Integer.toHexString(c) + "}"); + } + } + + return sb.toString(); + } + + private void init() throws IOException + { + /* Send SSH_FXP_INIT (version 3) */ + + final int client_version = 3; + + if (debug != null) + debug.println("Sending SSH_FXP_INIT (" + client_version + ")..."); + + TypesWriter tw = new TypesWriter(); + tw.writeUINT32(client_version); + sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes()); + + /* Receive SSH_FXP_VERSION */ + + if (debug != null) + debug.println("Waiting for SSH_FXP_VERSION..."); + + TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */ + + int type = tr.readByte(); + + if (type != Packet.SSH_FXP_VERSION) + { + throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + type + ")"); + } + + protocol_version = tr.readUINT32(); + + if (debug != null) + debug.println("SSH_FXP_VERSION: protocol_version = " + protocol_version); + + if (protocol_version != 3) + throw new IOException("Server version " + protocol_version + " is currently not supported"); + + /* Read and save extensions (if any) for later use */ + + while (tr.remain() != 0) + { + String name = tr.readString(); + byte[] value = tr.readByteString(); + server_extensions.put(name, value); + + if (debug != null) + debug.println("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length) + + "'"); + } + } + + /** + * Returns the negotiated SFTP protocol version between the client and the server. + * + * @return SFTP protocol version, i.e., "3". + * + */ + public int getProtocolVersion() + { + return protocol_version; + } + + /** + * Close this SFTP session. NEVER forget to call this method to free up + * resources - even if you got an exception from one of the other methods. + * Sometimes these other methods may throw an exception, saying that the + * underlying channel is closed (this can happen, e.g., if the other server + * sent a close message.) However, as long as you have not called the + * close() method, you are likely wasting resources. + * + */ + public void close() + { + sess.close(); + } + + /** + * List the contents of a directory. + * + * @param dirName See the {@link SFTPv3Client comment} for the class for more details. + * @return A Vector containing {@link SFTPv3DirectoryEntry} objects. + * @throws IOException the io exception + */ + public Vector ls(String dirName) throws IOException + { + byte[] handle = openDirectory(dirName); + Vector result = scanDirectory(handle); + closeHandle(handle); + return result; + } + + /** + * Create a new directory. + * + * @param dirName See the {@link SFTPv3Client comment} for the class for more details. + * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that + * this is octal noation). The server will likely apply a umask. + * + * @throws IOException the io exception + */ + public void mkdir(String dirName, int posixPermissions) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(dirName, charsetName); + tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS); + tw.writeUINT32(posixPermissions); + + sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + /** + * Remove a file. + * + * @param fileName See the {@link SFTPv3Client comment} for the class for more details. + * @throws IOException the io exception + */ + public void rm(String fileName) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(fileName, charsetName); + + sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + /** + * Remove an empty directory. + * + * @param dirName See the {@link SFTPv3Client comment} for the class for more details. + * @throws IOException the io exception + */ + public void rmdir(String dirName) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(dirName, charsetName); + + sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + /** + * Move a file or directory. + * + * @param oldPath See the {@link SFTPv3Client comment} for the class for more details. + * @param newPath See the {@link SFTPv3Client comment} for the class for more details. + * @throws IOException the io exception + */ + public void mv(String oldPath, String newPath) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(oldPath, charsetName); + tw.writeString(newPath, charsetName); + + sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes()); + + expectStatusOKMessage(req_id); + } + + /** + * Open a file for reading. + * + * @param fileName See the {@link SFTPv3Client comment} for the class for more details. + * @return a SFTPv3FileHandle handle + * @throws IOException the io exception + */ + public SFTPv3FileHandle openFileRO(String fileName) throws IOException + { + return openFile(fileName, 0x00000001, null); // SSH_FXF_READ + } + + /** + * Open a file for reading and writing. + * + * @param fileName See the {@link SFTPv3Client comment} for the class for more details. + * @return a SFTPv3FileHandle handle + * @throws IOException the io exception + */ + public SFTPv3FileHandle openFileRW(String fileName) throws IOException + { + return openFile(fileName, 0x00000003, null); // SSH_FXF_READ | SSH_FXF_WRITE + } + + // Append is broken (already in the specification, because there is no way to + // send a write operation (what offset to use??)) + // public SFTPv3FileHandle openFileRWAppend(String fileName) throws IOException + // { + // return openFile(fileName, 0x00000007, null); // SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND + // } + + /** + * Create a file and open it for reading and writing. + * Same as {@link #createFile(String, SFTPv3FileAttributes) createFile(fileName, null)}. + * + * @param fileName See the {@link SFTPv3Client comment} for the class for more details. + * @return a SFTPv3FileHandle handle + * @throws IOException the io exception + */ + public SFTPv3FileHandle createFile(String fileName) throws IOException + { + return createFile(fileName, null); + } + + /** + * Create a file and open it for reading and writing. + * You can specify the default attributes of the file (the server may or may + * not respect your wishes). + * + * @param fileName See the {@link SFTPv3Client comment} for the class for more details. + * @param attr may be null to use server defaults. Probably only + * the uid, gid and permissions + * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} + * structure make sense. You need only to set those fields where you want + * to override the server's defaults. + * @return a SFTPv3FileHandle handle + * @throws IOException the io exception + */ + public SFTPv3FileHandle createFile(String fileName, SFTPv3FileAttributes attr) throws IOException + { + return openFile(fileName, 0x00000008 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE + } + + /** + * Create a file (truncate it if it already exists) and open it for reading and writing. + * Same as {@link #createFileTruncate(String, SFTPv3FileAttributes) createFileTruncate(fileName, null)}. + * + * @param fileName See the {@link SFTPv3Client comment} for the class for more details. + * @return a SFTPv3FileHandle handle + * @throws IOException the io exception + */ + public SFTPv3FileHandle createFileTruncate(String fileName) throws IOException + { + return createFileTruncate(fileName, null); + } + + /** + * reate a file (truncate it if it already exists) and open it for reading and writing. + * You can specify the default attributes of the file (the server may or may + * not respect your wishes). + * + * @param fileName See the {@link SFTPv3Client comment} for the class for more details. + * @param attr may be null to use server defaults. Probably only + * the uid, gid and permissions + * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} + * structure make sense. You need only to set those fields where you want + * to override the server's defaults. + * @return a SFTPv3FileHandle handle + * @throws IOException the io exception + */ + public SFTPv3FileHandle createFileTruncate(String fileName, SFTPv3FileAttributes attr) throws IOException + { + return openFile(fileName, 0x00000018 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_READ | SSH_FXF_WRITE + } + + private byte[] createAttrs(SFTPv3FileAttributes attr) + { + TypesWriter tw = new TypesWriter(); + + int attrFlags = 0; + + if (attr == null) + { + tw.writeUINT32(0); + } + else + { + if (attr.size != null) + attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE; + + if ((attr.uid != null) && (attr.gid != null)) + attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID; + + if (attr.permissions != null) + attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS; + + if ((attr.atime != null) && (attr.mtime != null)) + attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME; + + tw.writeUINT32(attrFlags); + + if (attr.size != null) + tw.writeUINT64(attr.size.longValue()); + + if ((attr.uid != null) && (attr.gid != null)) + { + tw.writeUINT32(attr.uid.intValue()); + tw.writeUINT32(attr.gid.intValue()); + } + + if (attr.permissions != null) + tw.writeUINT32(attr.permissions.intValue()); + + if ((attr.atime != null) && (attr.mtime != null)) + { + tw.writeUINT32(attr.atime.intValue()); + tw.writeUINT32(attr.mtime.intValue()); + } + } + + return tw.getBytes(); + } + + private SFTPv3FileHandle openFile(String fileName, int flags, SFTPv3FileAttributes attr) throws IOException + { + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(fileName, charsetName); + tw.writeUINT32(flags); + tw.writeBytes(createAttrs(attr)); + + if (debug != null) + { + debug.println("Sending SSH_FXP_OPEN..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes()); + + byte[] resp = receiveMessage(34000); + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_HANDLE) + { + if (debug != null) + { + debug.println("Got SSH_FXP_HANDLE."); + debug.flush(); + } + + return new SFTPv3FileHandle(this, tr.readByteString()); + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + String errorMessage = tr.readString(); + + throw new SFTPException(errorMessage, errorCode); + } + + /** + * Read bytes from a file. No more than 32768 bytes may be read at once. + * Be aware that the semantics of read() are different than for Java streams. + * + *

    + *
  • The server will read as many bytes as it can from the file (up to len), + * and return them.
  • + *
  • If EOF is encountered before reading any data, -1 is returned. + *
  • If an error occurs, an exception is thrown
  • + *
  • For normal disk files, it is guaranteed that the server will return the specified + * number of bytes, or up to end of file. For, e.g., device files this may return + * fewer bytes than requested.
  • + *
+ * + * @param handle a SFTPv3FileHandle handle + * @param fileOffset offset (in bytes) in the file + * @param dst the destination byte array + * @param dstoff offset in the destination byte array + * @param len how many bytes to read, 0 < len <= 32768 bytes + * @return the number of bytes that could be read, may be less than requested if + * the end of the file is reached, -1 is returned in case of EOF + * @throws IOException the io exception + */ + public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException + { + checkHandleValidAndOpen(handle); + + if ((len > 32768) || (len <= 0)) + throw new IllegalArgumentException("invalid len argument"); + + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); + tw.writeUINT64(fileOffset); + tw.writeUINT32(len); + + if (debug != null) + { + debug.println("Sending SSH_FXP_READ..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_READ, req_id, tw.getBytes()); + + byte[] resp = receiveMessage(34000); + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t == Packet.SSH_FXP_DATA) + { + if (debug != null) + { + debug.println("Got SSH_FXP_DATA..."); + debug.flush(); + } + + int readLen = tr.readUINT32(); + + if ((readLen < 0) || (readLen > len)) + throw new IOException("The server sent an invalid length field."); + + tr.readBytes(dst, dstoff, readLen); + + return readLen; + } + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + if (errorCode == ErrorCodes.SSH_FX_EOF) + { + if (debug != null) + { + debug.println("Got SSH_FX_EOF."); + debug.flush(); + } + + return -1; + } + + String errorMessage = tr.readString(); + + throw new SFTPException(errorMessage, errorCode); + } + + /** + * Write bytes to a file. If len > 32768, then the write operation will + * be split into multiple writes. + * + * @param handle a SFTPv3FileHandle handle. + * @param fileOffset offset (in bytes) in the file. + * @param src the source byte array. + * @param srcoff offset in the source byte array. + * @param len how many bytes to write. + * @throws IOException the io exception + */ + public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException + { + checkHandleValidAndOpen(handle); + + while (len > 0) + { + int writeRequestLen = len; + + if (writeRequestLen > 32768) + writeRequestLen = 32768; + + int req_id = generateNextRequestID(); + + TypesWriter tw = new TypesWriter(); + tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); + tw.writeUINT64(fileOffset); + tw.writeString(src, srcoff, writeRequestLen); + + if (debug != null) + { + debug.println("Sending SSH_FXP_WRITE..."); + debug.flush(); + } + + sendMessage(Packet.SSH_FXP_WRITE, req_id, tw.getBytes()); + + fileOffset += writeRequestLen; + + srcoff += writeRequestLen; + len -= writeRequestLen; + + byte[] resp = receiveMessage(34000); + + TypesReader tr = new TypesReader(resp); + + int t = tr.readByte(); + + int rep_id = tr.readUINT32(); + if (rep_id != req_id) + throw new IOException("The server sent an invalid id field."); + + if (t != Packet.SSH_FXP_STATUS) + throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); + + int errorCode = tr.readUINT32(); + + if (errorCode == ErrorCodes.SSH_FX_OK) + continue; + + String errorMessage = tr.readString(); + + throw new SFTPException(errorMessage, errorCode); + } + } + + /** + * Close a file. + * + * @param handle a SFTPv3FileHandle handle + * @throws IOException the io exception + */ + public void closeFile(SFTPv3FileHandle handle) throws IOException + { + if (handle == null) + throw new IllegalArgumentException("the handle argument may not be null"); + + try + { + if (handle.isClosed == false) + { + closeHandle(handle.fileHandle); + } + } + finally + { + handle.isClosed = true; + } + } + + /** + * Checks if the given path exists. + * + * @param path the path + * @return the boolean + * @throws IOException the io exception + */ + public boolean exists(String path) throws IOException { + return _stat(path)!=null; + } + + /** + * Graceful {@link #stat(String)} that returns null if the path doesn't exist. + * + * @param path the path + * @return the sft pv 3 file attributes + * @throws IOException the io exception + */ + public SFTPv3FileAttributes _stat(String path) throws IOException { + try { + return stat(path); + } catch (SFTPException e) { + int c = e.getServerErrorCode(); + if (c== ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH) + return null; + else + throw e; + } + } + + /** + * Makes sure that the directory exists, by creating it if necessary. + * + * @param path the path + * @param posixPermission the posix permission + * @throws IOException the io exception + */ + public void mkdirs(String path, int posixPermission) throws IOException { + SFTPv3FileAttributes atts = _stat(path); + if (atts!=null && atts.isDirectory()) + return; + + int idx = path.lastIndexOf("/"); + if (idx>0) + mkdirs(path.substring(0,idx), posixPermission); + + try { + mkdir(path, posixPermission); + } catch (IOException e) { + throw (IOException)new IOException("Failed to mkdir "+path).initCause(e); + } + } + + /** + * Creates a new file and writes to it. + * + * @param path the path + * @return the output stream + * @throws IOException the io exception + */ + public OutputStream writeToFile(String path) throws IOException { + final SFTPv3FileHandle h = createFile(path); + return new OutputStream() { + private long offset = 0; + public void write(int b) throws IOException { + write(new byte[]{(byte)b}); + } + + public void write(byte[] b, int off, int len) throws IOException { + SFTPv3Client.this.write(h,offset,b,off,len); + offset += len; + } + + public void close() throws IOException { + closeFile(h); + } + }; + } + + /** + * Read input stream. + * + * @param file the file + * @return the input stream + * @throws IOException the io exception + */ + public InputStream read(String file) throws IOException { + final SFTPv3FileHandle h = openFileRO(file); + return new InputStream() { + private long offset = 0; + + public int read() throws IOException { + byte[] b = new byte[1]; + if(read(b)<0) + return -1; + return b[0]; + } + + public int read(byte[] b, int off, int len) throws IOException { + int r = SFTPv3Client.this.read(h,offset,b,off,len); + if (r<0) return -1; + offset += r; + return r; + } + + public long skip(long n) throws IOException { + offset += n; + return n; + } + + public void close() throws IOException { + closeFile(h); + } + }; + } + + /** + * Chmod. + * + * @param path the path + * @param permissions the permissions + * @throws IOException the io exception + */ + public void chmod(String path, int permissions) throws IOException { + SFTPv3FileAttributes atts = new SFTPv3FileAttributes(); + atts.permissions = Integer.valueOf(permissions); + setstat(path, atts); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/SFTPv3DirectoryEntry.java b/service/src/main/java/com/trilead/ssh2/SFTPv3DirectoryEntry.java new file mode 100644 index 0000000..669ba87 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/SFTPv3DirectoryEntry.java @@ -0,0 +1,38 @@ + +package com.trilead.ssh2; + +/** + * A SFTPv3DirectoryEntry as returned by {@link SFTPv3Client#ls(String)}. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SFTPv3DirectoryEntry.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class SFTPv3DirectoryEntry +{ + /** + * A relative name within the directory, without any path components. + */ + public String filename; + + /** + * An expanded format for the file name, similar to what is returned by + * "ls -l" on Un*x systems. + *

+ * The format of this field is unspecified by the SFTP v3 protocol. + * It MUST be suitable for use in the output of a directory listing + * command (in fact, the recommended operation for a directory listing + * command is to simply display this data). However, clients SHOULD NOT + * attempt to parse the longname field for file attributes; they SHOULD + * use the attrs field instead. + *

+ * The recommended format for the longname field is as follows:
+ * -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer + */ + public String longEntry; + + /** + * The attributes of this entry. + */ + public SFTPv3FileAttributes attributes; +} diff --git a/service/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java b/service/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java new file mode 100644 index 0000000..a6fb6cc --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java @@ -0,0 +1,145 @@ + +package com.trilead.ssh2; + +/** + * A SFTPv3FileAttributes object represents detail information + * about a file on the server. Not all fields may/must be present. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SFTPv3FileAttributes.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ + +public class SFTPv3FileAttributes +{ + /** + * The SIZE attribute. NULL if not present. + */ + public Long size = null; + + /** + * The UID attribute. NULL if not present. + */ + public Integer uid = null; + + /** + * The GID attribute. NULL if not present. + */ + public Integer gid = null; + + /** + * The POSIX permissions. NULL if not present. + *

+ * Here is a list: + * + *

Note: these numbers are all OCTAL.
+	 *  
+	 *  S_IFMT     0170000   bitmask for the file type bitfields
+	 *  S_IFSOCK   0140000   socket
+	 *  S_IFLNK    0120000   symbolic link
+	 *  S_IFREG    0100000   regular file
+	 *  S_IFBLK    0060000   block device
+	 *  S_IFDIR    0040000   directory
+	 *  S_IFCHR    0020000   character device
+	 *  S_IFIFO    0010000   fifo 
+	 *  S_ISUID    0004000   set UID bit
+	 *  S_ISGID    0002000   set GID bit 
+	 *  S_ISVTX    0001000   sticky bit
+	 *  
+	 *  S_IRWXU    00700     mask for file owner permissions
+	 *  S_IRUSR    00400     owner has read permission
+	 *  S_IWUSR    00200     owner has write permission
+	 *  S_IXUSR    00100     owner has execute permission
+	 *  S_IRWXG    00070     mask for group permissions
+	 *  S_IRGRP    00040     group has read permission
+	 *  S_IWGRP    00020     group has write permission
+	 *  S_IXGRP    00010     group has execute permission
+	 *  S_IRWXO    00007     mask for permissions for others (not in group)
+	 *  S_IROTH    00004     others have read permission
+	 *  S_IWOTH    00002     others have write permisson
+	 *  S_IXOTH    00001     others have execute permission
+	 * 
+ */ + public Integer permissions = null; + + /** + * The ATIME attribute. Represented as seconds from Jan 1, 1970 in UTC. + * NULL if not present. + */ + public Long atime = null; + + /** + * The MTIME attribute. Represented as seconds from Jan 1, 1970 in UTC. + * NULL if not present. + */ + public Long mtime = null; + + /** + * Checks if this entry is a directory. + * + * @return Returns true if permissions are available and they indicate + * that this entry represents a directory. + */ + public boolean isDirectory() + { + if (permissions == null) + return false; + + return ((permissions.intValue() & 0040000) != 0); + } + + /** + * Checks if this entry is a regular file. + * + * @return Returns true if permissions are available and they indicate + * that this entry represents a regular file. + */ + public boolean isRegularFile() + { + if (permissions == null) + return false; + + return ((permissions.intValue() & 0100000) != 0); + } + + /** + * Checks if this entry is a a symlink. + * + * @return Returns true if permissions are available and they indicate + * that this entry represents a symlink. + */ + public boolean isSymlink() + { + if (permissions == null) + return false; + + return ((permissions.intValue() & 0120000) != 0); + } + + /** + * Turn the POSIX permissions into a 7 digit octal representation. + * Note: the returned value is first masked with 0177777. + * + * @return NULL if permissions are not available. + */ + public String getOctalPermissions() + { + if (permissions == null) + return null; + + String res = Integer.toString(permissions.intValue() & 0177777, 8); + + StringBuffer sb = new StringBuffer(); + + int leadingZeros = 7 - res.length(); + + while (leadingZeros > 0) + { + sb.append('0'); + leadingZeros--; + } + + sb.append(res); + + return sb.toString(); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/SFTPv3FileHandle.java b/service/src/main/java/com/trilead/ssh2/SFTPv3FileHandle.java new file mode 100644 index 0000000..9b3dbb6 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/SFTPv3FileHandle.java @@ -0,0 +1,45 @@ + +package com.trilead.ssh2; + +/** + * A SFTPv3FileHandle. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SFTPv3FileHandle.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class SFTPv3FileHandle +{ + final SFTPv3Client client; + final byte[] fileHandle; + boolean isClosed = false; + + /* The constructor is NOT public */ + + SFTPv3FileHandle(SFTPv3Client client, byte[] h) + { + this.client = client; + this.fileHandle = h; + } + + /** + * Get the SFTPv3Client instance which created this handle. + * + * @return A SFTPv3Client instance. + */ + public SFTPv3Client getClient() + { + return client; + } + + /** + * Check if this handle was closed with the {@link SFTPv3Client#closeFile(SFTPv3FileHandle)} method + * of the SFTPv3Client instance which created the handle. + * + * @return if the handle is closed. + */ + public boolean isClosed() + { + return isClosed; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/ServerHostKeyVerifier.java b/service/src/main/java/com/trilead/ssh2/ServerHostKeyVerifier.java new file mode 100644 index 0000000..c48be9f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/ServerHostKeyVerifier.java @@ -0,0 +1,31 @@ + +package com.trilead.ssh2; + +/** + * A callback interface used to implement a client specific method of checking + * server host keys. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ServerHostKeyVerifier.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public interface ServerHostKeyVerifier +{ + /** + * The actual verifier method, it will be called by the key exchange code + * on EVERY key exchange - this can happen several times during the lifetime + * of a connection. + *

+ * Note: SSH-2 servers are allowed to change their hostkey at ANY time. + * + * @param hostname the hostname used to create the {@link Connection} object + * @param port the remote TCP port + * @param serverHostKeyAlgorithm the public key algorithm + * @param serverHostKey the server's public key blob + * @return if the client wants to accept the server's host key - if not, the + * connection will be closed. + * @throws Exception Will be wrapped with an IOException, extended version of returning false =) + */ + public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) + throws Exception; +} diff --git a/service/src/main/java/com/trilead/ssh2/Session.java b/service/src/main/java/com/trilead/ssh2/Session.java new file mode 100644 index 0000000..a04be7d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/Session.java @@ -0,0 +1,604 @@ + +package com.trilead.ssh2; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.security.SecureRandom; + +import com.trilead.ssh2.channel.Channel; +import com.trilead.ssh2.channel.ChannelManager; +import com.trilead.ssh2.channel.X11ServerData; +import com.trilead.ssh2.packets.PacketSignal; + + +/** + * A Session is a remote execution of a program. "Program" means + * in this context either a shell, an application or a system command. The + * program may or may not have a tty. Only one single program can be started on + * a session. However, multiple sessions can be active simultaneously. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $ + */ +public class Session +{ + ChannelManager cm; + Channel cn; + + boolean flag_pty_requested = false; + boolean flag_x11_requested = false; + boolean flag_execution_started = false; + boolean flag_closed = false; + + String x11FakeCookie = null; + + final SecureRandom rnd; + + Session(ChannelManager cm, SecureRandom rnd) throws IOException + { + this.cm = cm; + this.cn = cm.openSessionChannel(); + this.rnd = rnd; + } + + /** + * Basically just a wrapper for lazy people - identical to calling + * requestPTY("dumb", 0, 0, 0, 0, null). + * + * @throws IOException the io exception + */ + public void requestDumbPTY() throws IOException + { + requestPTY("dumb", 0, 0, 0, 0, null); + } + + /** + * Basically just another wrapper for lazy people - identical to calling + * requestPTY(term, 0, 0, 0, 0, null). + * + * @param term the term + * @throws IOException the io exception + */ + public void requestPTY(String term) throws IOException + { + requestPTY(term, 0, 0, 0, 0, null); + } + + /** + * Allocate a pseudo-terminal for this session. + *

+ * This method may only be called before a program or shell is started in + * this session. + *

+ * Different aspects can be specified: + * + *

    + *
  • The TERM environment variable value (e.g., vt100)
  • + *
  • The terminal's dimensions.
  • + *
  • The encoded terminal modes.
  • + *
+ * Zero dimension parameters are ignored. The character/row dimensions + * override the pixel dimensions (when nonzero). Pixel dimensions refer to + * the drawable area of the window. The dimension parameters are only + * informational. The encoding of terminal modes (parameter + * terminal_modes) is described in RFC4254. + * + * @param term + * The TERM environment variable value (e.g., vt100) + * @param term_width_characters + * terminal width, characters (e.g., 80) + * @param term_height_characters + * terminal height, rows (e.g., 24) + * @param term_width_pixels + * terminal width, pixels (e.g., 640) + * @param term_height_pixels + * terminal height, pixels (e.g., 480) + * @param terminal_modes + * encoded terminal modes (may be null) + * @throws IOException the io exception + */ + public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels, + int term_height_pixels, byte[] terminal_modes) throws IOException + { + if (term == null) + throw new IllegalArgumentException("TERM cannot be null."); + + if ((terminal_modes != null) && (terminal_modes.length > 0)) + { + if (terminal_modes[terminal_modes.length - 1] != 0) + throw new IOException("Illegal terminal modes description, does not end in zero byte"); + } + else + terminal_modes = new byte[] { 0 }; + + synchronized (this) + { + /* The following is just a nicer error, we would catch it anyway later in the channel code */ + if (flag_closed) + throw new IOException("This session is closed."); + + if (flag_pty_requested) + throw new IOException("A PTY was already requested."); + + if (flag_execution_started) + throw new IOException( + "Cannot request PTY at this stage anymore, a remote execution has already started."); + + flag_pty_requested = true; + } + + cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, + terminal_modes); + } + + /** + * Tells the server that the size of the terminal has changed. + * + * See {@link #requestPTY(String, int, int, int, int, byte[])} for more details about how parameters are interpreted. + * + * @param term_width_characters + * terminal width, characters (e.g., 80) + * @param term_height_characters + * terminal height, rows (e.g., 24) + * @param term_width_pixels + * terminal width, pixels (e.g., 640) + * @param term_height_pixels + * terminal height, pixels (e.g., 480) + * @throws IOException the io exception + */ + public void requestWindowChange(int term_width_characters, int term_height_characters, int term_width_pixels, + int term_height_pixels) throws IOException + { + synchronized (this) + { + /* The following is just a nicer error, we would catch it anyway later in the channel code */ + if (flag_closed) + throw new IOException("This session is closed."); + + if (!flag_pty_requested) + throw new IOException("A PTY was not requested."); + } + + cn.requestWindowChange(term_width_characters, term_height_characters, term_width_pixels, term_height_pixels); + } + + /** + * Sends a signal to the remote process. + * + * @param name the name + * @throws IOException the io exception + */ + public void signal(String name) throws IOException { + synchronized (this) { + /* The following is just a nicer error, we would catch it anyway later in the channel code */ + if (flag_closed) + throw new IOException("This session is closed."); + } + + cn.signal(name); + } + + /** + * Sends a signal to the remote process. + *

+ * For better portability, specify signal by name, not by its number. + * + * @param code the code + * @throws IOException the io exception + */ + public void signal(int code) throws IOException { + String sig = PacketSignal.strsignal(code); + if (sig==null) throw new IllegalArgumentException("Unrecognized signal code: "+code); + signal(sig); + } + + /** + * Request X11 forwarding for the current session. + *

+ * You have to supply the name and port of your X-server. + *

+ * This method may only be called before a program or shell is started in + * this session. + * + * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) + * @param port the port of the real (target) X11 server (e.g., 6010) + * @param cookie if non-null, then present this cookie to the real X11 server + * @param singleConnection if true, then the server is instructed to only forward one single + * connection, no more connections shall be forwarded after first, or after the session + * channel has been closed + * @throws IOException the io exception + */ + public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) + throws IOException + { + if (hostname == null) + throw new IllegalArgumentException("hostname argument may not be null"); + + synchronized (this) + { + /* The following is just a nicer error, we would catch it anyway later in the channel code */ + if (flag_closed) + throw new IOException("This session is closed."); + + if (flag_x11_requested) + throw new IOException("X11 forwarding was already requested."); + + if (flag_execution_started) + throw new IOException( + "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); + + flag_x11_requested = true; + } + + /* X11ServerData - used to store data about the target X11 server */ + + X11ServerData x11data = new X11ServerData(); + + x11data.hostname = hostname; + x11data.port = port; + x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ + + /* Generate fake cookie - this one is used between remote clients and our proxy */ + + byte[] fakeCookie = new byte[16]; + String hexEncodedFakeCookie; + + /* Make sure that this fake cookie is unique for this connection */ + + while (true) + { + rnd.nextBytes(fakeCookie); + + /* Generate also hex representation of fake cookie */ + + StringBuffer tmp = new StringBuffer(32); + for (int i = 0; i < fakeCookie.length; i++) + { + String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); + tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); + } + hexEncodedFakeCookie = tmp.toString(); + + /* Well, yes, chances are low, but we want to be on the safe side */ + + if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) + break; + } + + /* Ask for X11 forwarding */ + + cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); + + /* OK, that went fine, get ready to accept X11 connections... */ + /* ... but only if the user has not called close() in the meantime =) */ + + synchronized (this) + { + if (flag_closed == false) + { + this.x11FakeCookie = hexEncodedFakeCookie; + cm.registerX11Cookie(hexEncodedFakeCookie, x11data); + } + } + + /* Now it is safe to start remote X11 programs */ + } + + /** + * Execute a command on the remote machine. + * + * @param cmd + * The command to execute on the remote host. + * @throws IOException the io exception + */ + public void execCommand(String cmd) throws IOException + { + if (cmd == null) + throw new IllegalArgumentException("cmd argument may not be null"); + + synchronized (this) + { + /* The following is just a nicer error, we would catch it anyway later in the channel code */ + if (flag_closed) + throw new IOException("This session is closed."); + + if (flag_execution_started) + throw new IOException("A remote execution has already started."); + + flag_execution_started = true; + } + + cm.requestExecCommand(cn, cmd); + } + + /** + * Start a shell on the remote machine. + * + * @throws IOException the io exception + */ + public void startShell() throws IOException + { + synchronized (this) + { + /* The following is just a nicer error, we would catch it anyway later in the channel code */ + if (flag_closed) + throw new IOException("This session is closed."); + + if (flag_execution_started) + throw new IOException("A remote execution has already started."); + + flag_execution_started = true; + } + + cm.requestShell(cn); + } + + /** + * Start a subsystem on the remote machine. + * Unless you know what you are doing, you will never need this. + * + * @param name the name of the subsystem. + * @throws IOException the io exception + */ + public void startSubSystem(String name) throws IOException + { + if (name == null) + throw new IllegalArgumentException("name argument may not be null"); + + synchronized (this) + { + /* The following is just a nicer error, we would catch it anyway later in the channel code */ + if (flag_closed) + throw new IOException("This session is closed."); + + if (flag_execution_started) + throw new IOException("A remote execution has already started."); + + flag_execution_started = true; + } + + cm.requestSubSystem(cn, name); + } + + /** + * This method can be used to perform end-to-end session (i.e., SSH channel) + * testing. It sends a 'ping' message to the server and waits for the 'pong' + * from the server. + *

+ * Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request + * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply + * packet. + * + * @throws IOException in case of any problem or when the session is closed + */ + public void ping() throws IOException + { + synchronized (this) + { + /* + * The following is just a nicer error, we would catch it anyway + * later in the channel code + */ + if (flag_closed) + throw new IOException("This session is closed."); + } + + cm.requestChannelTrileadPing(cn); + } + + public InputStream getStdout() + { + return cn.getStdoutStream(); + } + + public InputStream getStderr() + { + return cn.getStderrStream(); + } + + public OutputStream getStdin() + { + return cn.getStdinStream(); + } + + /** + * Write stdout received from the other side to the specified {@link OutputStream}. + * + *

+ * By default, when data arrives from the other side, trilead buffers them and lets + * you read it at your convenience from {@link #getStdout()}. This is normally convenient, + * but if all you are doing is to send the data to another {@link OutputStream} by + * copying a stream, then you'll be end up wasting a thread just for this. + * In such a situation, you can call this method and let the I/O handling thread of trilead + * directly pass the received data to the output stream. This also eliminates the internal + * buffer used for spooling. + * + *

+ * When you do this, beware of a blocking write! If a write blocks, it'll affect + * all the other channels and sessions that are sharing the same SSH connection, + * as there's only one I/O thread. For example, this can happen if you are writing to + * {@link Socket}. + * + *

+ * If any data has already been received and spooled before calling this method, + * the data will be sent to the given stream immediately. + * + *

+ * To signal the end of the stream, when the other side notifies us of EOF or when + * the channel closes, the output stream gets closed. If this is not desirable, + * you must wrap the output stream and ignore the {@link OutputStream#close()} call. + * + * @param os the os + * @throws IOException the io exception + */ + public void pipeStdout(OutputStream os) throws IOException { + cn.pipeStdoutStream(os); + } + + /** + * The same as {@link #pipeStdout(OutputStream)} except for stderr, not for stdout. + * + * @param os the os + * @throws IOException the io exception + */ + public void pipeStderr(OutputStream os) throws IOException { + cn.pipeStderrStream(os); + } + + /** + * This method blocks until there is more data available on either the + * stdout or stderr InputStream of this Session. Very useful + * if you do not want to use two parallel threads for reading from the two + * InputStreams. One can also specify a timeout. NOTE: do NOT call this + * method if you use concurrent threads that operate on either of the two + * InputStreams of this Session (otherwise this method may + * block, even though more data is available). + * + * @param timeout + * The (non-negative) timeout in ms. 0 means no + * timeout, the call may block forever. + * @return + *

    + *
  • 0 if no more data will arrive.
  • + *
  • 1 if more data is available.
  • + *
  • -1 if a timeout occurred.
  • + *
+ * + * @throws IOException the io exception + * @throws InterruptedException the interrupted exception + * @deprecated This method has been replaced with a much more powerful wait-for-condition + * interface and therefore acts only as a wrapper. + * + */ + public int waitUntilDataAvailable(long timeout) throws IOException, InterruptedException { + if (timeout < 0) + throw new IllegalArgumentException("timeout must not be negative!"); + + int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA + | ChannelCondition.EOF); + + if ((conditions & ChannelCondition.TIMEOUT) != 0) + return -1; + + if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0) + return 1; + + /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ + + if ((conditions & ChannelCondition.EOF) != 0) + return 0; + + throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); + } + + /** + * This method blocks until certain conditions hold true on the underlying SSH-2 channel. + *

+ * This method returns as soon as one of the following happens: + *

    + *
  • at least of the specified conditions (see {@link ChannelCondition}) holds true
  • + *
  • timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)
  • + *
  • the underlying channel was closed (CLOSED will be set in result conditions)
  • + *
+ *

+ * In any case, the result value contains ALL current conditions, which may be more + * than the specified condition set (i.e., never use the "==" operator to test for conditions + * in the bitmask, see also comments in {@link ChannelCondition}). + *

+ * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and + * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two + * InputStreams of this Session (otherwise this method may + * block, even though more data is available in the StreamGobblers). + * + * @param condition_set a bitmask based on {@link ChannelCondition} values + * @param timeout non-negative timeout in ms, 0 means no timeout + * @return all bitmask specifying all current conditions that are true + * @throws InterruptedException the interrupted exception + */ + public int waitForCondition(int condition_set, long timeout) throws InterruptedException { + if (timeout < 0) + throw new IllegalArgumentException("timeout must be non-negative!"); + + return cm.waitForCondition(cn, timeout, condition_set); + } + + /** + * Get the exit code/status from the remote command - if available. Be + * careful - not all server implementations return this value. It is + * generally a good idea to call this method only when all data from the + * remote side has been consumed (see also the WaitForCondition method). + * + * @return An Integer holding the exit code, or + * null if no exit code is (yet) available. + */ + public Integer getExitStatus() + { + return cn.getExitStatus(); + } + + /** + * Get the name of the signal by which the process on the remote side was + * stopped - if available and applicable. Be careful - not all server + * implementations return this value. + * + * @return An String holding the name of the signal, or + * null if the process exited normally or is still + * running (or if the server forgot to send this information). + */ + public String getExitSignal() + { + return cn.getExitSignal(); + } + + /** + * Close this session. NEVER forget to call this method to free up resources - + * even if you got an exception from one of the other methods (or when + * getting an Exception on the Input- or OutputStreams). Sometimes these other + * methods may throw an exception, saying that the underlying channel is + * closed (this can happen, e.g., if the other server sent a close message.) + * However, as long as you have not called the close() + * method, you may be wasting (local) resources. + * + */ + public void close() + { + synchronized (this) + { + if (flag_closed) + return; + + flag_closed = true; + + if (x11FakeCookie != null) + cm.unRegisterX11Cookie(x11FakeCookie, true); + + try + { + cm.closeChannel(cn, "Closed due to user request", true); + } + catch (IOException ignored) + { + } + } + } + + /** + * Sets the receive window size. + *

+ * The receive window is a maximum number of bytes that the remote side can send to this channel without + * waiting for us to consume them (AKA "in-flight bytes"). + *

+ * When your connection is over a large-latency/high-bandiwdth network, specifying a bigger value + * allows the network to be efficiently utilized. OTOH, if you don't drain this channel quickly enough + * all those bytes in flight can end up getting buffered. + *

+ * This value can be adjusted at runtime. + * + * @param newSize the new size + */ + public synchronized void setWindowSize(int newSize) { + cn.setWindowSize(newSize); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/StreamGobbler.java b/service/src/main/java/com/trilead/ssh2/StreamGobbler.java new file mode 100644 index 0000000..93c25de --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/StreamGobbler.java @@ -0,0 +1,232 @@ + +package com.trilead.ssh2; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; + +/** + * A StreamGobbler is an InputStream that uses an internal worker + * thread to constantly consume input from another InputStream. It uses a buffer + * to store the consumed data. The buffer size is automatically adjusted, if needed. + *

+ * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR + * InputStreams with instances of this class, then you don't have to bother about + * the shared window of STDOUT and STDERR in the low level SSH-2 protocol, + * since all arriving data will be immediatelly consumed by the worker threads. + * Also, as a side effect, the streams will be buffered (e.g., single byte + * read() operations are faster). + *

+ * Other SSH for Java libraries include this functionality by default in + * their STDOUT and STDERR InputStream implementations, however, please be aware + * that this approach has also a downside: + *

+ * If you do not call the StreamGobbler's read() method often enough + * and the peer is constantly sending huge amounts of data, then you will sooner or later + * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size). + * Joe Average will like this class anyway - a paranoid programmer would never use such an approach. + *

+ * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't", + * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class StreamGobbler extends InputStream +{ + class GobblerThread extends Thread + { + public void run() + { + byte[] buff = new byte[8192]; + + while (true) + { + try + { + int avail = is.read(buff); + + synchronized (synchronizer) + { + if (avail <= 0) + { + isEOF = true; + synchronizer.notifyAll(); + break; + } + + int space_available = buffer.length - write_pos; + + if (space_available < avail) + { + /* compact/resize buffer */ + + int unread_size = write_pos - read_pos; + int need_space = unread_size + avail; + + byte[] new_buffer = buffer; + + if (need_space > buffer.length) + { + int inc = need_space / 3; + inc = (inc < 256) ? 256 : inc; + inc = (inc > 8192) ? 8192 : inc; + new_buffer = new byte[need_space + inc]; + } + + if (unread_size > 0) + System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size); + + buffer = new_buffer; + + read_pos = 0; + write_pos = unread_size; + } + + System.arraycopy(buff, 0, buffer, write_pos, avail); + write_pos += avail; + + synchronizer.notifyAll(); + } + } + catch (IOException e) + { + synchronized (synchronizer) + { + exception = e; + synchronizer.notifyAll(); + break; + } + } + } + } + } + + private InputStream is; + private final GobblerThread t; + + private final Object synchronizer = new Object(); + + private boolean isEOF = false; + private boolean isClosed = false; + private IOException exception = null; + + private byte[] buffer = new byte[2048]; + private int read_pos = 0; + private int write_pos = 0; + + public StreamGobbler(InputStream is) + { + this.is = is; + t = new GobblerThread(); + t.setDaemon(true); + t.start(); + } + + public int read() throws IOException + { + synchronized (synchronizer) + { + if (isClosed) + throw new IOException("This StreamGobbler is closed."); + + while (read_pos == write_pos) + { + if (exception != null) + throw exception; + + if (isEOF) + return -1; + + try + { + synchronizer.wait(); + } + catch (InterruptedException e) + { + throw new InterruptedIOException(); + } + } + + int b = buffer[read_pos++] & 0xff; + + return b; + } + } + + public int available() throws IOException + { + synchronized (synchronizer) + { + if (isClosed) + throw new IOException("This StreamGobbler is closed."); + + return write_pos - read_pos; + } + } + + public int read(byte[] b) throws IOException + { + return read(b, 0, b.length); + } + + public void close() throws IOException + { + synchronized (synchronizer) + { + if (isClosed) + return; + isClosed = true; + isEOF = true; + synchronizer.notifyAll(); + is.close(); + } + } + + public int read(byte[] b, int off, int len) throws IOException + { + if (b == null) + throw new NullPointerException(); + + if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) + throw new IndexOutOfBoundsException(); + + if (len == 0) + return 0; + + synchronized (synchronizer) + { + if (isClosed) + throw new IOException("This StreamGobbler is closed."); + + while (read_pos == write_pos) + { + if (exception != null) + throw exception; + + if (isEOF) + return -1; + + try + { + synchronizer.wait(); + } + catch (InterruptedException e) + { + throw new InterruptedIOException(); + } + } + + int avail = write_pos - read_pos; + + avail = (avail > len) ? len : avail; + + System.arraycopy(buffer, read_pos, b, off, avail); + + read_pos += avail; + + return avail; + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/auth/AgentIdentity.java b/service/src/main/java/com/trilead/ssh2/auth/AgentIdentity.java new file mode 100644 index 0000000..e6c6961 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/auth/AgentIdentity.java @@ -0,0 +1,7 @@ +package com.trilead.ssh2.auth; + +public interface AgentIdentity { + public String getAlgName(); + public byte[] getPublicKeyBlob(); + public byte[] sign(byte[] data); +} diff --git a/service/src/main/java/com/trilead/ssh2/auth/AgentProxy.java b/service/src/main/java/com/trilead/ssh2/auth/AgentProxy.java new file mode 100644 index 0000000..2e66f72 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/auth/AgentProxy.java @@ -0,0 +1,7 @@ +package com.trilead.ssh2.auth; + +import java.util.Collection; + +public interface AgentProxy { + public Collection/**/ getIdentities(); +} diff --git a/service/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java b/service/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java new file mode 100644 index 0000000..09d13f5 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java @@ -0,0 +1,470 @@ +package com.trilead.ssh2.auth; + +import com.trilead.ssh2.InteractiveCallback; +import com.trilead.ssh2.crypto.PEMDecoder; +import com.trilead.ssh2.packets.*; +import com.trilead.ssh2.signature.KeyAlgorithm; +import com.trilead.ssh2.signature.KeyAlgorithmManager; +import com.trilead.ssh2.transport.MessageHandler; +import com.trilead.ssh2.transport.TransportManager; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.util.Collection; +import java.util.Vector; +import java.util.concurrent.TimeUnit; +import com.trilead.ssh2.ConnectionMonitor; + + +/** + * AuthenticationManager. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class AuthenticationManager implements MessageHandler +{ + public static final String PROPERTY_TIMEOUT = AuthenticationManager.class.getName() + ".timeout"; + public static final long TIMEOUT = Long.valueOf(System.getProperty(PROPERTY_TIMEOUT,"120000")); + TransportManager tm; + + Vector packets = new Vector(); + Vector connMonitors = new Vector<>(); + + boolean connectionClosed = false; + + String[] remainingMethods = new String[0]; + boolean isPartialSuccess = false; + + boolean authenticated = false; + boolean initDone = false; + + public AuthenticationManager(TransportManager tm) + { + this.tm = tm; + } + + public void setConnectionMonitors(Vector connMonitors) { + this.connMonitors = (Vector) connMonitors.clone(); + } + + boolean methodPossible(String methName) + { + if (remainingMethods == null) + return false; + + for (String remainingMethod : remainingMethods) { + if (remainingMethod.compareTo(methName) == 0) + return true; + } + return false; + } + + byte[] deQueue() throws IOException + { + synchronized (packets) + { + long waitUntil = System.currentTimeMillis() + TIMEOUT; + long now = System.currentTimeMillis(); + + while (packets.size() == 0 && now < waitUntil) + { + if (connectionClosed) + throw new IOException("The connection is closed.", tm.getReasonClosedCause()); + + try + { + packets.wait(TIMEOUT); + } + catch (InterruptedException ign) + { + throw new InterruptedIOException(ign.getMessage()); + } + now = System.currentTimeMillis(); + } + + if(packets.size()==0){ + throw new IOException("No valid packets after " + TIMEOUT + " milliseconds, " + + "you can increase the timeout by setting the property -D" + PROPERTY_TIMEOUT + "="); + } + /* This sequence works with J2ME */ + byte[] res = (byte[]) packets.firstElement(); + packets.removeElementAt(0); + return res; + } + } + + byte[] getNextMessage() throws IOException + { + while (true) + { + byte[] msg = deQueue(); + + if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER) + return msg; + + PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length); + + String banner = sb.getBanner(); + + if (banner != null) { + for (ConnectionMonitor listener : connMonitors) { + listener.onReceiveInfo(ConnectionMonitor.SERVER_BANNER, banner); + } + } + } + } + + public String[] getRemainingMethods(String user) throws IOException + { + initialize(user); + return remainingMethods; + } + + public boolean getPartialSuccess() + { + return isPartialSuccess; + } + + private boolean initialize(String user) throws IOException + { + if (!initDone) + { + tm.registerMessageHandler(this, 0, 255); + + PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth"); + tm.sendMessage(sr.getPayload()); + + PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user); + tm.sendMessage(urn.getPayload()); + + byte[] msg = getNextMessage(); + new PacketServiceAccept(msg, 0, msg.length); + msg = getNextMessage(); + + initDone = true; + + if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) + { + authenticated = true; + tm.removeMessageHandler(this, 0, 255); + return true; + } + + if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE) + { + PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length); + + remainingMethods = puf.getAuthThatCanContinue(); + isPartialSuccess = puf.isPartialSuccess(); + return false; + } + + throw new IOException("Unexpected SSH message (type " + msg[0] + ")"); + } + return authenticated; + } + + public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException { + initialize(user); + + boolean success; + for (AgentIdentity identity : (Collection) proxy.getIdentities()) { + success = authenticatePublicKey(user, proxy, identity); + if (success) { + return true; + } + } + return false; + } + + boolean authenticatePublicKey(String user, AgentProxy proxy, AgentIdentity identity) throws IOException { + + if (!methodPossible("publickey")) + throw new IOException("Authentication method publickey not supported by the server at this stage."); + + byte[] pubKeyBlob = identity.getPublicKeyBlob(); + if(pubKeyBlob == null) { + return false; + } + + TypesWriter tw = new TypesWriter(); + byte[] H = tm.getSessionIdentifier(); + + tw.writeString(H, 0, H.length); + tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); + tw.writeString(user); + tw.writeString("ssh-connection"); + tw.writeString("publickey"); + tw.writeBoolean(true); + tw.writeString(identity.getAlgName()); + tw.writeString(pubKeyBlob, 0, pubKeyBlob.length); + + byte[] msg = tw.getBytes(); + byte[] response = identity.sign(msg); + + PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey( + "ssh-connection", user, identity.getAlgName(), pubKeyBlob, response); + tm.sendMessage(ua.getPayload()); + + byte[] ar = getNextMessage(); + + if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) + { + authenticated = true; + tm.removeMessageHandler(this, 0, 255); + return true; + } + + if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) + { + PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length); + + remainingMethods = puf.getAuthThatCanContinue(); + isPartialSuccess = puf.isPartialSuccess(); + + return false; + } + + throw new IOException("Unexpected SSH message (type " + ar[0] + ")"); + } + + + public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd) + throws IOException + { + try + { + initialize(user); + + if (!methodPossible("publickey")) + throw new IOException("Authentication method publickey not supported by the server at this stage."); + + KeyPair keyPair = PEMDecoder.decodeKeyPair(PEMPrivateKey, password); + PrivateKey key = keyPair.getPrivate(); + + boolean supportedKey = false; + + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + if (algorithm.supportsKey(key)) { + + byte[] encodedKey = algorithm.encodePublicKey(keyPair.getPublic()); + TypesWriter tw = new TypesWriter(); + + byte[] H = tm.getSessionIdentifier(); + + tw.writeString(H, 0, H.length); + tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); + tw.writeString(user); + tw.writeString("ssh-connection"); + tw.writeString("publickey"); + tw.writeBoolean(true); + tw.writeString(algorithm.getKeyFormat()); + tw.writeString(encodedKey, 0, encodedKey.length); + + byte[] msg = tw.getBytes(); + + byte[] ds = algorithm.generateSignature(msg, keyPair.getPrivate(), rnd); + + byte[] ds_enc = algorithm.encodeSignature(ds); + + PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, + algorithm.getKeyFormat(), encodedKey, ds_enc); + tm.sendMessage(ua.getPayload()); + + supportedKey = true; + break; + } + } + + if (!supportedKey) { + throw new IOException("Unknown private key type returned by the PEM decoder."); + } + + byte[] ar = getNextMessage(); + + if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) + { + authenticated = true; + tm.removeMessageHandler(this, 0, 255); + return true; + } + + if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) + { + PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length); + + remainingMethods = puf.getAuthThatCanContinue(); + isPartialSuccess = puf.isPartialSuccess(); + + return false; + } + + throw new IOException("Unexpected SSH message (type " + ar[0] + ")"); + + } + catch (IOException e) + { + tm.close(e, false); + throw new IOException("Publickey authentication failed.", e); + } + } + + public boolean authenticateNone(String user) throws IOException + { + try + { + initialize(user); + return authenticated; + } + catch (IOException e) + { + tm.close(e, false); + throw new IOException("None authentication failed.", e); + } + } + + public boolean authenticatePassword(String user, String pass) throws IOException + { + try + { + initialize(user); + + if (!methodPossible("password")) + throw new IOException("Authentication method password not supported by the server at this stage."); + + PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass); + tm.sendMessage(ua.getPayload()); + + byte[] ar = getNextMessage(); + + if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) + { + authenticated = true; + tm.removeMessageHandler(this, 0, 255); + return true; + } + + if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) + { + PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length); + + remainingMethods = puf.getAuthThatCanContinue(); + isPartialSuccess = puf.isPartialSuccess(); + + return false; + } + + throw new IOException("Unexpected SSH message (type " + ar[0] + ")"); + + } + catch (IOException e) + { + tm.close(e, false); + throw new IOException("Password authentication failed.", e); + } + } + + public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException + { + try + { + initialize(user); + + if (!methodPossible("keyboard-interactive")) + throw new IOException( + "Authentication method keyboard-interactive not supported by the server at this stage."); + + if (submethods == null) + submethods = new String[0]; + + PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user, + submethods); + + tm.sendMessage(ua.getPayload()); + + while (true) + { + byte[] ar = getNextMessage(); + + if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) + { + authenticated = true; + tm.removeMessageHandler(this, 0, 255); + return true; + } + + if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) + { + PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length); + + remainingMethods = puf.getAuthThatCanContinue(); + isPartialSuccess = puf.isPartialSuccess(); + + return false; + } + + if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST) + { + PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length); + + String[] responses; + + try + { + responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui + .getPrompt(), pui.getEcho()); + } + catch (Exception e) + { + throw new IOException("Exception in callback.", e); + } + + if (responses == null) + throw new IOException("Your callback may not return NULL!"); + + PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses); + tm.sendMessage(puir.getPayload()); + + continue; + } + + throw new IOException("Unexpected SSH message (type " + ar[0] + ")"); + } + } + catch (IOException e) + { + tm.close(e, false); + throw new IOException("Keyboard-interactive authentication failed.", e); + } + } + + public void handleMessage(byte[] msg, int msglen) throws IOException + { + synchronized (packets) + { + byte[] tmp = new byte[msglen]; + System.arraycopy(msg, 0, tmp, 0, msglen); + packets.addElement(tmp); + + packets.notifyAll(); + + if (packets.size() > 5) + { + connectionClosed = true; + throw new IOException("Error, peer is flooding us with authentication packets."); + } + } + } + + public void handleEndMessage(Throwable cause) throws IOException { + synchronized (packets) { + connectionClosed = true; + packets.notifyAll(); + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/Channel.java b/service/src/main/java/com/trilead/ssh2/channel/Channel.java new file mode 100644 index 0000000..9c80492 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/Channel.java @@ -0,0 +1,453 @@ + +package com.trilead.ssh2.channel; + +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.PacketSignal; +import com.trilead.ssh2.packets.PacketWindowChange; +import com.trilead.ssh2.packets.Packets; +import com.trilead.ssh2.transport.TransportManager; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; + +import static com.trilead.ssh2.util.IOUtils.closeQuietly; + +/** + * Channel. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class Channel +{ + /* + * OK. Here is an important part of the JVM Specification: + * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214) + * + * Any association between locks and variables is purely conventional. + * Locking any lock conceptually flushes all variables from a thread's + * working memory, and unlocking any lock forces the writing out to main + * memory of all variables that the thread has assigned. That a lock may be + * associated with a particular object or a class is purely a convention. + * (...) + * + * If a thread uses a particular shared variable only after locking a + * particular lock and before the corresponding unlocking of that same lock, + * then the thread will read the shared value of that variable from main + * memory after the lock operation, if necessary, and will copy back to main + * memory the value most recently assigned to that variable before the + * unlock operation. + * + * This, in conjunction with the mutual exclusion rules for locks, suffices + * to guarantee that values are correctly transmitted from one thread to + * another through shared variables. + * + * ====> Always keep that in mind when modifying the Channel/ChannelManger + * code. + * + */ + + static final int STATE_OPENING = 1; + static final int STATE_OPEN = 2; + static final int STATE_CLOSED = 4; + + private static final int CHANNEL_BUFFER_SIZE = Integer.getInteger( + Channel.class.getName()+".bufferSize", + 1024*1024 + 16*1024).intValue(); + + /** + * This channel's session size. + */ + // @GuarydedBy("this") + int channelBufferSize = CHANNEL_BUFFER_SIZE; + + /* + * To achieve correctness, the following rules have to be respected when + * accessing this object: + */ + + // These fields can always be read + final ChannelManager cm; + final ChannelOutputStream stdinStream; + + /** + * One stream. + * + * Either {@link #stream} and {@link #buffer} is set, or the {@link #sink} is set, but those + * are mutually exclusive. The former is used when we are buffering data and let the application + * read it via {@link InputStream}, and the latter is used when we are passing through the data + * to another {@link OutputStream}. + * + * The synchronization is done by {@link Channel} + */ + class Output { + ChannelInputStream stream; + FifoBuffer buffer = new FifoBuffer(Channel.this, 2048, channelBufferSize); + OutputStream sink; + + public void write(byte[] buf, int start, int len) throws IOException { + if (buffer!=null) { + try { + buffer.write(buf,start,len); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } else { + sink.write(buf,start,len); + freeupWindow(len, true); + } + } + + /** + * How many bytes can be read from the buffer? + */ + public int readable() { + if (buffer!=null) return buffer.readable(); + else return 0; + } + + /** + * See {@link InputStream#available()} + */ + public int available() { + if (buffer==null) + throw new IllegalStateException("Output is being piped to "+sink); + + int sz = buffer.readable(); + if (sz>0) return sz; + return isEOF() ? -1 : 0; + } + + /** + * Read from the buffer. + */ + public int read(byte[] buf, int start, int len) throws InterruptedException { + return buffer.read(buf,start,len); + } + + /** + * Called when there will be no more data arriving to this output any more. + * Not that buffer might still have some more data that needs to be drained. + */ + public void eof() { + if (buffer!=null) + buffer.close(); + else + closeQuietly(sink); + } + + /** + * Instead of spooling data, let our I/O thread write to the given {@link OutputStream}. + */ + public void pipeTo(OutputStream os) throws IOException { + sink = os; + if (buffer.readable()!=0) { + freeupWindow(buffer.writeTo(os)); + } + + buffer = null; + stream = null; + } + } + + final Output stdout = new Output(); + final Output stderr = new Output(); + + // These two fields will only be written while the Channel is in state + // STATE_OPENING. + // The code makes sure that the two fields are written out when the state is + // changing to STATE_OPEN. + // Therefore, if you know that the Channel is in state STATE_OPEN, then you + // can read these two fields without synchronizing on the Channel. However, make + // sure that you get the latest values (e.g., flush caches by synchronizing on any + // object). However, to be on the safe side, you can lock the channel. + + int localID = -1; + int remoteID = -1; + + /* + * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE + * msg. + * + * This is a little bit complicated, but we have to do it in that way, since + * we cannot keep a lock on the Channel during the send operation (this + * would block sometimes the receiver thread, and, in extreme cases, can + * lead to a deadlock on both sides of the connection (senders are blocked + * since the receive buffers on the other side are full, and receiver + * threads wait for the senders to finish). It all depends on the + * implementation on the other side. But we cannot make any assumptions, we + * have to assume the worst case. Confused? Just believe me. + */ + + /* + * If you send a message on a channel, then you have to aquire the + * "channelSendLock" and check the "closeMessageSent" flag (this variable + * may only be accessed while holding the "channelSendLock" !!! + * + * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation + * above. + */ + + final Object channelSendLock = new Object(); + boolean closeMessageSent = false; + + /* + * Stop memory fragmentation by allocating this often used buffer. + * May only be used while holding the channelSendLock + */ + + final byte[] msgWindowAdjust = new byte[9]; + + // If you access (read or write) any of the following fields, then you have + // to synchronize on the channel. + + int state = STATE_OPENING; + + boolean closeMessageRecv = false; + + /* This is a stupid implementation. At the moment we can only wait + * for one pending request per channel. + */ + int successCounter = 0; + int failedCounter = 0; + + int localWindow = 0; /* locally, we use a small window, < 2^31 */ + long remoteWindow = 0; /* long for readable 2^32 - 1 window support */ + + int localMaxPacketSize = -1; + int remoteMaxPacketSize = -1; + + + private boolean eof = false; + + synchronized void eof() { + stdout.eof(); + stderr.eof(); + eof = true; + } + boolean isEOF() { + return eof; + } + + Integer exit_status; + + String exit_signal; + + // we keep the x11 cookie so that this channel can be closed when this + // specific x11 forwarding gets stopped + + String hexX11FakeCookie; + + // reasonClosed is special, since we sometimes need to access it + // while holding the channelSendLock. + // We protect it with a private short term lock. + + private final Object reasonClosedLock = new Object(); + private Throwable reasonClosed = null; + + public Channel(ChannelManager cm) + { + this.cm = cm; + + this.localWindow = channelBufferSize; + this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE - 1024; // leave enough slack + + this.stdinStream = new ChannelOutputStream(this); + this.stdout.stream = new ChannelInputStream(this, false); + this.stderr.stream = new ChannelInputStream(this, true); + } + + /* Methods to allow access from classes outside of this package */ + + public synchronized void setWindowSize(int newSize) { + if (newSize<=0) throw new IllegalArgumentException("Invalid value: "+newSize); + this.channelBufferSize = newSize; + // next time when the other side sends us something, we'll issue SSH_MSG_CHANNEL_WINDOW_ADJUST + } + + public ChannelInputStream getStderrStream() + { + return stderr.stream; + } + + public ChannelOutputStream getStdinStream() + { + return stdinStream; + } + + public ChannelInputStream getStdoutStream() + { + return stdout.stream; + } + + public synchronized void pipeStdoutStream(OutputStream os) throws IOException { + stdout.pipeTo(os); + } + + public synchronized void pipeStderrStream(OutputStream os) throws IOException { + stderr.pipeTo(os); + } + + public String getExitSignal() + { + synchronized (this) + { + return exit_signal; + } + } + + public Integer getExitStatus() + { + synchronized (this) + { + return exit_status; + } + } + + /** + * Gets reason closed. + * + * @return the reason closed + * @deprecated Use {@link #getReasonClosedCause()} + */ + public String getReasonClosed() + { + synchronized (reasonClosedLock) + { + return reasonClosed!=null ? reasonClosed.getMessage() : null; + } + } + + public Throwable getReasonClosedCause() + { + synchronized (reasonClosedLock) + { + return reasonClosed; + } + } + + public void setReasonClosed(String reasonClosed) + { + setReasonClosed(new IOException(reasonClosed)); + } + + public void setReasonClosed(Throwable reasonClosed) { + synchronized (reasonClosedLock) + { + if (this.reasonClosed == null) + this.reasonClosed = reasonClosed; + } + } + + /** + * Update the flow control couner and if necessary, sends ACK to the other end to + * let it send more data. + */ + void freeupWindow(int copylen) throws IOException { + freeupWindow(copylen, false); + } + + /** + * Update the flow control couner and if necessary, sends ACK to the other end to + * let it send more data. + */ + void freeupWindow(int copylen, boolean sendAsync) throws IOException { + if (copylen <= 0) return; + + int increment = 0; + int remoteID; + int localID; + + synchronized (this) { + if (localWindow <= ((channelBufferSize * 3) / 4)) { + // have enough local window been consumed? if so, we'll send Ack + + // the window control is on the combined bytes of stdout & stderr + int space = channelBufferSize - stdout.readable() - stderr.readable(); + + increment = space - localWindow; + if (increment > 0) // increment<0 can't happen, but be defensive + localWindow += increment; + } + + remoteID = this.remoteID; /* read while holding the lock */ + localID = this.localID; /* read while holding the lock */ + + } + + /* + * If a consumer reads stdout and stdin in parallel, we may end up with + * sending two msgWindowAdjust messages. Luckily, it + * does not matter in which order they arrive at the server. + */ + + if (increment > 0) + { + if (log.isEnabled()) + log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")"); + + synchronized (channelSendLock) + { + byte[] msg = msgWindowAdjust; + + msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST; + msg[1] = (byte) (remoteID >> 24); + msg[2] = (byte) (remoteID >> 16); + msg[3] = (byte) (remoteID >> 8); + msg[4] = (byte) (remoteID); + msg[5] = (byte) (increment >> 24); + msg[6] = (byte) (increment >> 16); + msg[7] = (byte) (increment >> 8); + msg[8] = (byte) (increment); + + if (closeMessageSent == false) { + if (sendAsync) { + cm.tm.sendAsynchronousMessage(msg); + } else { + cm.tm.sendMessage(msg); + } + } + } + } + } + + public void requestWindowChange(int term_width_characters, int term_height_characters, + int term_width_pixels, int term_height_pixels) throws IOException { + PacketWindowChange pwc; + + synchronized (this) { + if (state != Channel.STATE_OPEN) + throw (IOException)new IOException("Cannot request window-change on this channel").initCause(getReasonClosedCause()); + + pwc = new PacketWindowChange(remoteID, term_width_characters, term_height_characters, + term_width_pixels, term_height_pixels); + } + + synchronized (channelSendLock) { + if (closeMessageSent) + throw (IOException)new IOException("Cannot request window-change on this channel").initCause(getReasonClosedCause()); + cm.tm.sendMessage(pwc.getPayload()); + } + } + + public void signal(String name) throws IOException { + PacketSignal p; + + synchronized (this) { + if (state != Channel.STATE_OPEN) + throw (IOException)new IOException("Cannot send signal on this channel").initCause(getReasonClosedCause()); + + p = new PacketSignal(remoteID, name); + } + + synchronized (channelSendLock) { + if (closeMessageSent) + throw (IOException)new IOException("Cannot request window-change on this channel").initCause(getReasonClosedCause()); + cm.tm.sendMessage(p.getPayload()); + } + } + + private static final Logger log = Logger.getLogger(Channel.class); +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/ChannelInputStream.java b/service/src/main/java/com/trilead/ssh2/channel/ChannelInputStream.java new file mode 100644 index 0000000..f88522c --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/ChannelInputStream.java @@ -0,0 +1,86 @@ + +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.InputStream; + +/** + * ChannelInputStream. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ChannelInputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public final class ChannelInputStream extends InputStream +{ + Channel c; + + boolean isClosed = false; + boolean isEOF = false; + boolean extendedFlag = false; + + ChannelInputStream(Channel c, boolean isExtended) + { + this.c = c; + this.extendedFlag = isExtended; + } + + public int available() throws IOException + { + if (isEOF) + return 0; + + int avail = c.cm.getAvailable(c, extendedFlag); + + /* We must not return -1 on EOF */ + + return (avail > 0) ? avail : 0; + } + + public void close() throws IOException + { + isClosed = true; + } + + public int read(byte[] b, int off, int len) throws IOException + { + if (b == null) + throw new NullPointerException(); + + if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) + throw new IndexOutOfBoundsException(); + + if (len == 0) + return 0; + + if (isEOF) + return -1; + + int ret = c.cm.getChannelData(c, extendedFlag, b, off, len); + + if (ret == -1) + { + isEOF = true; + } + + return ret; + } + + public int read(byte[] b) throws IOException + { + return read(b, 0, b.length); + } + + public int read() throws IOException + { + /* Yes, this stream is pure and unbuffered, a single byte read() is slow */ + + final byte b[] = new byte[1]; + + int ret = read(b, 0, 1); + + if (ret != 1) + return -1; + + return b[0] & 0xff; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/ChannelManager.java b/service/src/main/java/com/trilead/ssh2/channel/ChannelManager.java new file mode 100644 index 0000000..bb7827b --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/ChannelManager.java @@ -0,0 +1,1591 @@ +package com.trilead.ssh2.channel; + +import android.annotation.SuppressLint; + +import com.trilead.ssh2.ChannelCondition; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.PacketChannelOpenConfirmation; +import com.trilead.ssh2.packets.PacketChannelOpenFailure; +import com.trilead.ssh2.packets.PacketChannelTrileadPing; +import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest; +import com.trilead.ssh2.packets.PacketGlobalForwardRequest; +import com.trilead.ssh2.packets.PacketGlobalTrileadPing; +import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel; +import com.trilead.ssh2.packets.PacketOpenSessionChannel; +import com.trilead.ssh2.packets.PacketSessionExecCommand; +import com.trilead.ssh2.packets.PacketSessionPtyRequest; +import com.trilead.ssh2.packets.PacketSessionStartShell; +import com.trilead.ssh2.packets.PacketSessionSubsystemRequest; +import com.trilead.ssh2.packets.PacketSessionX11Request; +import com.trilead.ssh2.packets.Packets; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.transport.MessageHandler; +import com.trilead.ssh2.transport.TransportManager; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.SocketTimeoutException; +import java.util.HashMap; +import java.util.Vector; + +/** + * ChannelManager. Please read the comments in Channel.java. + *

+ * Besides the crypto part, this is the core of the library. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $ + */ +public class ChannelManager implements MessageHandler +{ + private static final Logger log = Logger.getLogger(ChannelManager.class); + + private HashMap x11_magic_cookies = new HashMap(); + + /*package*/ TransportManager tm; + + private Vector channels = new Vector(); + private int nextLocalChannel = 100; + private boolean shutdown = false; + private int globalSuccessCounter = 0; + private int globalFailedCounter = 0; + + private HashMap remoteForwardings = new HashMap(); + + private Vector listenerThreads = new Vector(); + + private boolean listenerThreadsAllowed = true; + + public ChannelManager(TransportManager tm) + { + this.tm = tm; + tm.registerMessageHandler(this, 80, 100); + } + + private Channel getChannel(int id) + { + synchronized (channels) + { + for (int i = 0; i < channels.size(); i++) + { + Channel c = (Channel) channels.elementAt(i); + if (c.localID == id) + return c; + } + } + return null; + } + + private void removeChannel(int id) + { + synchronized (channels) + { + for (int i = 0; i < channels.size(); i++) + { + Channel c = (Channel) channels.elementAt(i); + if (c.localID == id) + { + channels.removeElementAt(i); + break; + } + } + } + } + + private int addChannel(Channel c) + { + synchronized (channels) + { + channels.addElement(c); + return nextLocalChannel++; + } + } + + private void waitUntilChannelOpen(Channel c) throws IOException + { + synchronized (c) + { + while (c.state == Channel.STATE_OPENING) + { + try + { + c.wait(); + } + catch (InterruptedException ignore) + { + throw new InterruptedIOException(); + } + } + + if (c.state != Channel.STATE_OPEN) + { + removeChannel(c.localID); + + throw ioException("Could not open channel (state:" + c.state + ")", c); + } + } + } + + private final boolean waitForGlobalRequestResult() throws IOException + { + return waitForGlobalRequestResult(0); + } + + /** + * Waits for the reply to a SSH global request. + * + * @param timeoutMillis maximum wait in milliseconds; {@code 0} keeps the + * historical unlimited wait used by non-liveness calls. + */ + private final boolean waitForGlobalRequestResult(long timeoutMillis) throws IOException + { + if (timeoutMillis < 0) + throw new IllegalArgumentException("timeoutMillis must not be negative"); + + final long deadlineNanos = timeoutMillis > 0 + ? System.nanoTime() + (timeoutMillis * 1_000_000L) + : 0L; + + synchronized (channels) + { + while ((globalSuccessCounter == 0) && (globalFailedCounter == 0)) + { + if (shutdown) + { + throw new IOException("The connection is being shutdown"); + } + + try + { + if (timeoutMillis == 0) + { + channels.wait(); + } + else + { + long remainingNanos = deadlineNanos - System.nanoTime(); + if (remainingNanos <= 0) + throw new SocketTimeoutException("SSH global request timed out"); + + long waitMillis = remainingNanos / 1_000_000L; + int waitNanos = (int) (remainingNanos % 1_000_000L); + channels.wait(waitMillis, waitNanos); + } + } + catch (InterruptedException ignore) + { + Thread.currentThread().interrupt(); + throw new InterruptedIOException(); + } + } + + if ((globalFailedCounter == 0) && (globalSuccessCounter == 1)) + return true; + + if ((globalFailedCounter == 1) && (globalSuccessCounter == 0)) + return false; + + throw new IOException("Illegal state. The server sent " + globalSuccessCounter + + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages."); + } + } + + private final boolean waitForChannelRequestResult(Channel c) throws IOException + { + synchronized (c) + { + while ((c.successCounter == 0) && (c.failedCounter == 0)) + { + if (c.state != Channel.STATE_OPEN) + { + throw ioException("This SSH2 channel is not open. state: "+c.state,c); + } + + try + { + c.wait(); + } + catch (InterruptedException ignore) + { + throw new InterruptedIOException(); + } + } + + if ((c.failedCounter == 0) && (c.successCounter == 1)) + return true; + + if ((c.failedCounter == 1) && (c.successCounter == 0)) + return false; + + throw new IOException("Illegal state. The server sent " + c.successCounter + + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages."); + } + } + + public void registerX11Cookie(String hexFakeCookie, X11ServerData data) + { + synchronized (x11_magic_cookies) + { + x11_magic_cookies.put(hexFakeCookie, data); + } + } + + public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels) + { + if (hexFakeCookie == null) + throw new IllegalStateException("hexFakeCookie may not be null"); + + synchronized (x11_magic_cookies) + { + x11_magic_cookies.remove(hexFakeCookie); + } + + if (killChannels == false) + return; + + if (log.isEnabled()) + log.log(50, "Closing all X11 channels for the given fake cookie"); + + Vector channel_copy; + + synchronized (channels) + { + channel_copy = (Vector) channels.clone(); + } + + for (int i = 0; i < channel_copy.size(); i++) + { + Channel c = (Channel) channel_copy.elementAt(i); + + synchronized (c) + { + if (hexFakeCookie.equals(c.hexX11FakeCookie) == false) + continue; + } + + try + { + closeChannel(c, "Closing X11 channel since the corresponding session is closing", true); + } + catch (IOException e) + { + } + } + } + + public X11ServerData checkX11Cookie(String hexFakeCookie) + { + synchronized (x11_magic_cookies) + { + if (hexFakeCookie != null) + return (X11ServerData) x11_magic_cookies.get(hexFakeCookie); + } + return null; + } + + public void closeAllChannels() + { + if (log.isEnabled()) + log.log(50, "Closing all channels"); + + Vector channel_copy; + + synchronized (channels) + { + channel_copy = (Vector) channels.clone(); + } + + for (int i = 0; i < channel_copy.size(); i++) + { + Channel c = (Channel) channel_copy.elementAt(i); + try + { + closeChannel(c, "Closing all channels", true); + } + catch (IOException e) + { + } + } + } + + public void closeChannel(Channel c, String reason, boolean force) throws IOException + { + byte msg[] = new byte[5]; + + synchronized (c) + { + if (force) + { + c.state = Channel.STATE_CLOSED; + c.eof(); + } + + c.setReasonClosed(reason); + + msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE; + msg[1] = (byte) (c.remoteID >> 24); + msg[2] = (byte) (c.remoteID >> 16); + msg[3] = (byte) (c.remoteID >> 8); + msg[4] = (byte) (c.remoteID); + + c.notifyAll(); + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent == true) + return; + tm.sendMessage(msg); + c.closeMessageSent = true; + } + + if (log.isEnabled()) + log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")"); + } + + public void sendEOF(Channel c) throws IOException + { + byte[] msg = new byte[5]; + + synchronized (c) + { + if (c.state != Channel.STATE_OPEN) + return; + + msg[0] = Packets.SSH_MSG_CHANNEL_EOF; + msg[1] = (byte) (c.remoteID >> 24); + msg[2] = (byte) (c.remoteID >> 16); + msg[3] = (byte) (c.remoteID >> 8); + msg[4] = (byte) (c.remoteID); + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent == true) + return; + tm.sendMessage(msg); + } + + if (log.isEnabled()) + log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")"); + } + + public void sendOpenConfirmation(Channel c) throws IOException + { + PacketChannelOpenConfirmation pcoc = null; + + synchronized (c) + { + if (c.state != Channel.STATE_OPENING) + return; + + c.state = Channel.STATE_OPEN; + + pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize); + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent == true) + return; + tm.sendMessage(pcoc.getPayload()); + } + } + + public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException + { + while (len > 0) + { + int thislen = 0; + byte[] msg; + + synchronized (c) + { + while (true) + { + if (c.state == Channel.STATE_CLOSED) + throw ioException("SSH channel is closed",c); + + if (c.state != Channel.STATE_OPEN) + throw new IOException("SSH channel in strange state. (" + c.state + ")"); + + if (c.remoteWindow != 0) + break; + + try + { + c.wait(); + } + catch (InterruptedException ignore) + { + throw new InterruptedIOException(); + } + } + + /* len > 0, no sign extension can happen when comparing */ + + thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow; + + int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9); + + /* The worst case scenario =) a true bottleneck */ + + if (estimatedMaxDataLen <= 0) + { + estimatedMaxDataLen = 1; + } + + if (thislen > estimatedMaxDataLen) + thislen = estimatedMaxDataLen; + + c.remoteWindow -= thislen; + + msg = new byte[1 + 8 + thislen]; + + msg[0] = Packets.SSH_MSG_CHANNEL_DATA; + msg[1] = (byte) (c.remoteID >> 24); + msg[2] = (byte) (c.remoteID >> 16); + msg[3] = (byte) (c.remoteID >> 8); + msg[4] = (byte) (c.remoteID); + msg[5] = (byte) (thislen >> 24); + msg[6] = (byte) (thislen >> 16); + msg[7] = (byte) (thislen >> 8); + msg[8] = (byte) (thislen); + + System.arraycopy(buffer, pos, msg, 9, thislen); + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent == true) + throw ioException("SSH channel is closed",c); + + tm.sendMessage(msg); + } + + pos += thislen; + len -= thislen; + } + } + + public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort) + throws IOException + { + RemoteForwardingData rfd = new RemoteForwardingData(); + + rfd.bindAddress = bindAddress; + rfd.bindPort = bindPort; + rfd.targetAddress = targetAddress; + rfd.targetPort = targetPort; + + synchronized (remoteForwardings) + { + Integer key = new Integer(bindPort); + + if (remoteForwardings.get(key) != null) + { + throw new IOException("There is already a forwarding for remote port " + bindPort); + } + + remoteForwardings.put(key, rfd); + } + + synchronized (channels) + { + globalSuccessCounter = globalFailedCounter = 0; + } + + PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort); + tm.sendMessage(pgf.getPayload()); + + if (log.isEnabled()) + log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")"); + + try + { + if (waitForGlobalRequestResult() == false) + throw new IOException("The server denied the request (did you enable port forwarding?)"); + } + catch (IOException e) + { + synchronized (remoteForwardings) + { + remoteForwardings.remove(rfd); + } + throw e; + } + + return bindPort; + } + + public void requestCancelGlobalForward(int bindPort) throws IOException + { + RemoteForwardingData rfd = null; + + synchronized (remoteForwardings) + { + rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort)); + + if (rfd == null) + throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort); + } + + synchronized (channels) + { + globalSuccessCounter = globalFailedCounter = 0; + } + + PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress, + rfd.bindPort); + tm.sendMessage(pgcf.getPayload()); + + if (log.isEnabled()) + log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")"); + + try + { + if (waitForGlobalRequestResult() == false) + throw new IOException("The server denied the request."); + } + finally + { + synchronized (remoteForwardings) + { + /* Only now we are sure that no more forwarded connections will arrive */ + remoteForwardings.remove(rfd); + } + } + + } + + public void registerThread(IChannelWorkerThread thr) throws IOException + { + synchronized (listenerThreads) + { + if (listenerThreadsAllowed == false) + throw new IOException("Too late, this connection is closed."); + listenerThreads.addElement(thr); + } + } + + public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address, + int originator_port) throws IOException + { + Channel c = new Channel(this); + + synchronized (c) + { + c.localID = addChannel(c); + // end of synchronized block forces writing out to main memory + } + + PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow, + c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port); + + tm.sendMessage(dtc.getPayload()); + + waitUntilChannelOpen(c); + + return c; + } + + public Channel openSessionChannel() throws IOException + { + Channel c = new Channel(this); + + synchronized (c) + { + c.localID = addChannel(c); + // end of synchronized block forces the writing out to main memory + } + + if (log.isEnabled()) + log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")"); + + PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize); + tm.sendMessage(smo.getPayload()); + + waitUntilChannelOpen(c); + + return c; + } + + public long requestGlobalTrileadPing() throws IOException + { + return requestGlobalTrileadPing(0); + } + + public long requestGlobalTrileadPing(long timeoutMillis) throws IOException + { + synchronized (channels) + { + globalSuccessCounter = globalFailedCounter = 0; + } + + PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing(); + + long sentMillis = System.currentTimeMillis(); + tm.sendMessage(pgtp.getPayload()); + + if (log.isEnabled()) + log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'."); + + try + { + if (waitForGlobalRequestResult(timeoutMillis) == true) { + long receivedMillis = System.currentTimeMillis(); + + return (receivedMillis - sentMillis); + + /*throw new IOException("Your server is alive - but buggy. " + + "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not.");*/ + } + + long receivedMillis = System.currentTimeMillis(); + + return (receivedMillis - sentMillis); + } + catch (IOException e) + { + throw (IOException) new IOException("The ping request failed.").initCause(e); + } + } + + public void requestChannelTrileadPing(Channel c) throws IOException + { + PacketChannelTrileadPing pctp; + + synchronized (c) + { + if (c.state != Channel.STATE_OPEN) + throw ioException("Cannot ping this channel",c); + + pctp = new PacketChannelTrileadPing(c.remoteID); + + c.successCounter = c.failedCounter = 0; + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent) + throw ioException("Cannot ping this channel",c); + tm.sendMessage(pctp.getPayload()); + } + + try + { + if (waitForChannelRequestResult(c) == true) + throw new IOException("Your server is alive - but buggy. " + + "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not."); + + } + catch (IOException e) + { + throw (IOException) new IOException("The ping request failed.").initCause(e); + } + } + + public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters, + int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException + { + PacketSessionPtyRequest spr; + + synchronized (c) + { + if (c.state != Channel.STATE_OPEN) + throw ioException("Cannot request PTY on this channel",c); + + spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters, + term_width_pixels, term_height_pixels, terminal_modes); + + c.successCounter = c.failedCounter = 0; + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent) + throw ioException("Cannot request PTY on this channel",c); + tm.sendMessage(spr.getPayload()); + } + + try + { + if (waitForChannelRequestResult(c) == false) + throw new IOException("The server denied the request."); + } + catch (IOException e) + { + throw (IOException) new IOException("PTY request failed").initCause(e); + } + } + + public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol, + String x11AuthenticationCookie, int x11ScreenNumber) throws IOException + { + PacketSessionX11Request psr; + + synchronized (c) + { + if (c.state != Channel.STATE_OPEN) + throw ioException("Cannot request X11 on this channel",c); + + psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol, + x11AuthenticationCookie, x11ScreenNumber); + + c.successCounter = c.failedCounter = 0; + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent) + throw ioException("Cannot request X11 on this channel",c); + tm.sendMessage(psr.getPayload()); + } + + if (log.isEnabled()) + log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")"); + + try + { + if (waitForChannelRequestResult(c) == false) + throw new IOException("The server denied the request."); + } + catch (IOException e) + { + throw (IOException) new IOException("The X11 request failed.").initCause(e); + } + } + + public void requestSubSystem(Channel c, String subSystemName) throws IOException + { + PacketSessionSubsystemRequest ssr; + + synchronized (c) + { + if (c.state != Channel.STATE_OPEN) + throw ioException("Cannot request subsystem on this channel",c); + + ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName); + + c.successCounter = c.failedCounter = 0; + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent) + throw ioException("Cannot request subsystem on this channel",c); + tm.sendMessage(ssr.getPayload()); + } + + try + { + if (waitForChannelRequestResult(c) == false) + throw new IOException("The server denied the request."); + } + catch (IOException e) + { + throw (IOException) new IOException("The subsystem request failed.").initCause(e); + } + } + + public void requestExecCommand(Channel c, String cmd) throws IOException + { + PacketSessionExecCommand sm; + + synchronized (c) + { + if (c.state != Channel.STATE_OPEN) + throw ioException("Cannot execute command on this channel",c); + + sm = new PacketSessionExecCommand(c.remoteID, true, cmd); + + c.successCounter = c.failedCounter = 0; + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent) + throw ioException("Cannot execute command on this channel",c); + tm.sendMessage(sm.getPayload()); + } + + if (log.isEnabled()) + log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')"); + + try + { + if (waitForChannelRequestResult(c) == false) + throw new IOException("The server denied the request."); + } + catch (IOException e) + { + throw (IOException) new IOException("The execute request failed.").initCause(e); + } + } + + public void requestShell(Channel c) throws IOException + { + PacketSessionStartShell sm; + + synchronized (c) + { + if (c.state != Channel.STATE_OPEN) + throw ioException("Cannot start shell on this channel",c); + + sm = new PacketSessionStartShell(c.remoteID, true); + + c.successCounter = c.failedCounter = 0; + } + + synchronized (c.channelSendLock) + { + if (c.closeMessageSent) + throw ioException("Cannot start shell on this channel",c); + tm.sendMessage(sm.getPayload()); + } + + try + { + if (waitForChannelRequestResult(c) == false) + throw new IOException("The server denied the request."); + } + catch (IOException e) + { + throw (IOException) new IOException("The shell request failed.").initCause(e); + } + } + + public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException + { + if (msglen <= 13) + throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")"); + + int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); + int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); + int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id); + + if (dataType != Packets.SSH_EXTENDED_DATA_STDERR) + throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")"); + + if (len != (msglen - 13)) + throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13) + + ", got " + len + ")"); + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")"); + + synchronized (c) + { + if (c.state == Channel.STATE_CLOSED) + return; // ignore + + if (c.state != Channel.STATE_OPEN) + throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state (" + + c.state + ")"); + + if (c.localWindow < len) + throw new IOException("Remote sent too much data, does not fit into window."); + + c.localWindow -= len; + + c.stderr.write(msg,13,len); + } + } + + /** + * Wait until for a condition. + * + * @param c Channel + * @param timeout in ms, 0 means no timeout. + * @param condition_mask minimum event mask + * @return all current events + * @throws InterruptedException the interrupted exception + */ + public int waitForCondition(Channel c, long timeout, int condition_mask) throws InterruptedException { + long end_time = 0; + boolean end_time_set = false; + + synchronized (c) + { + while (true) + { + int current_cond = 0; + + int stdoutAvail = c.stdout.readable(); + int stderrAvail = c.stderr.readable(); + + if (stdoutAvail > 0) + current_cond = current_cond | ChannelCondition.STDOUT_DATA; + + if (stderrAvail > 0) + current_cond = current_cond | ChannelCondition.STDERR_DATA; + + if (c.isEOF()) + current_cond = current_cond | ChannelCondition.EOF; + + if (c.getExitStatus() != null) + current_cond = current_cond | ChannelCondition.EXIT_STATUS; + + if (c.getExitSignal() != null) + current_cond = current_cond | ChannelCondition.EXIT_SIGNAL; + + if (c.state == Channel.STATE_CLOSED) + return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF; + + if ((current_cond & condition_mask) != 0) + return current_cond; + + if (timeout > 0) + { + if (!end_time_set) + { + end_time = System.currentTimeMillis() + timeout; + end_time_set = true; + } + else + { + timeout = end_time - System.currentTimeMillis(); + + if (timeout <= 0) + return current_cond | ChannelCondition.TIMEOUT; + } + } + + if (timeout > 0) + c.wait(timeout); + else + c.wait(); + } + } + } + + public int getAvailable(Channel c, boolean extended) throws IOException + { + synchronized (c) + { + return (extended ? c.stderr : c.stdout ).available(); + } + } + + public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException + { + int copylen; + + synchronized (c) { + try { + copylen = (extended ? c.stderr : c.stdout).read(target, off, len); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + if (copylen<=0) return copylen; + } + + c.freeupWindow(copylen); + + return copylen; + } + + public void msgChannelData(byte[] msg, int msglen) throws IOException + { + if (msglen <= 9) + throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")"); + + int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); + int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id); + + if (len != (msglen - 9)) + throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got " + + len + ")"); + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")"); + + synchronized (c) + { + if (c.state == Channel.STATE_CLOSED) + return; // ignore + + if (c.state != Channel.STATE_OPEN) + throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")"); + + if (c.localWindow < len) + throw new IOException("Remote sent too much data, does not fit into window."); + + c.localWindow -= len; + + c.stdout.write(msg,9,len); + } + } + + public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException + { + if (msglen != 9) + throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")"); + + int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); + int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id); + + synchronized (c) + { + final long huge = 0xFFFFffffL; /* 2^32 - 1 */ + + c.remoteWindow += (windowChange & huge); /* avoid sign extension */ + + /* TODO - is this a good heuristic? */ + + if ((c.remoteWindow > huge)) + c.remoteWindow = huge; + + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")"); + } + + public void msgChannelOpen(byte[] msg, int msglen) throws IOException + { + TypesReader tr = new TypesReader(msg, 0, msglen); + + tr.readByte(); // skip packet type + String channelType = tr.readString(); + int remoteID = tr.readUINT32(); /* sender channel */ + int remoteWindow = tr.readUINT32(); /* initial window size */ + int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */ + + if ("x11".equals(channelType)) + { + synchronized (x11_magic_cookies) + { + /* If we did not request X11 forwarding, then simply ignore this bogus request. */ + + if (x11_magic_cookies.size() == 0) + { + PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, + Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", ""); + + tm.sendAsynchronousMessage(pcof.getPayload()); + + if (log.isEnabled()) + log.log(20, "Unexpected X11 request, denying it!"); + + return; + } + } + + String remoteOriginatorAddress = tr.readString(); + int remoteOriginatorPort = tr.readUINT32(); + + Channel c = new Channel(this); + + synchronized (c) + { + c.remoteID = remoteID; + c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */ + c.remoteMaxPacketSize = remoteMaxPacketSize; + c.localID = addChannel(c); + } + + /* + * The open confirmation message will be sent from another thread + */ + + RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort); + rxat.setDaemon(true); + rxat.start(); + + return; + } + + if ("forwarded-tcpip".equals(channelType)) + { + String remoteConnectedAddress = tr.readString(); /* address that was connected */ + int remoteConnectedPort = tr.readUINT32(); /* port that was connected */ + String remoteOriginatorAddress = tr.readString(); /* originator IP address */ + int remoteOriginatorPort = tr.readUINT32(); /* originator port */ + + RemoteForwardingData rfd = null; + + synchronized (remoteForwardings) + { + rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort)); + } + + if (rfd == null) + { + PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, + Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, + "No thanks, unknown port in forwarded-tcpip request", ""); + + /* Always try to be polite. */ + + tm.sendAsynchronousMessage(pcof.getPayload()); + + if (log.isEnabled()) + log.log(20, "Unexpected forwarded-tcpip request, denying it!"); + + return; + } + + Channel c = new Channel(this); + + synchronized (c) + { + c.remoteID = remoteID; + c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */ + c.remoteMaxPacketSize = remoteMaxPacketSize; + c.localID = addChannel(c); + } + + /* + * The open confirmation message will be sent from another thread. + */ + + RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort, + remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort); + + rat.setDaemon(true); + rat.start(); + + return; + } + + /* Tell the server that we have no idea what it is talking about */ + + PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE, + "Unknown channel type", ""); + + tm.sendAsynchronousMessage(pcof.getPayload()); + + if (log.isEnabled()) + log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")"); + } + + public void msgChannelRequest(byte[] msg, int msglen) throws IOException + { + TypesReader tr = new TypesReader(msg, 0, msglen); + + tr.readByte(); // skip packet type + int id = tr.readUINT32(); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id); + + String type = tr.readString("US-ASCII"); + boolean wantReply = tr.readBoolean(); + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')"); + + if (type.equals("exit-status")) + { + if (wantReply != false) + throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); + + int exit_status = tr.readUINT32(); + + if (tr.remain() != 0) + throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); + + synchronized (c) + { + c.exit_status = new Integer(exit_status); + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")"); + + return; + } + + if (type.equals("exit-signal")) + { + if (wantReply != false) + throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); + + String signame = tr.readString("US-ASCII"); + tr.readBoolean(); + tr.readString(); + tr.readString(); + + if (tr.remain() != 0) + throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); + + synchronized (c) + { + c.exit_signal = signame; + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")"); + + return; + } + + /* We simply ignore unknown channel requests, however, if the server wants a reply, + * then we signal that we have no idea what it is about. + */ + + if (wantReply) + { + byte[] reply = new byte[5]; + + reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE; + reply[1] = (byte) (c.remoteID >> 24); + reply[2] = (byte) (c.remoteID >> 16); + reply[3] = (byte) (c.remoteID >> 8); + reply[4] = (byte) (c.remoteID); + + tm.sendAsynchronousMessage(reply); + } + + if (log.isEnabled()) + log.log(50, "Channel request '" + type + "' is not known, ignoring it"); + } + + @SuppressLint("SuspiciousIndentation") + public void msgChannelEOF(byte[] msg, int msglen) throws IOException + { + if (msglen != 5) + throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")"); + + int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id); + + c.eof(); + + if (log.isEnabled()) + log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")"); + } + + public void msgChannelClose(byte[] msg, int msglen) throws IOException + { + if (msglen != 5) + throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")"); + + int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id); + + synchronized (c) + { + c.eof(); + c.state = Channel.STATE_CLOSED; + c.setReasonClosed("Close requested by remote"); + c.closeMessageRecv = true; + + removeChannel(c.localID); + + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")"); + } + + public void msgChannelSuccess(byte[] msg, int msglen) throws IOException + { + if (msglen != 5) + throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")"); + + int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id); + + synchronized (c) + { + c.successCounter++; + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")"); + } + + public void msgChannelFailure(byte[] msg, int msglen) throws IOException + { + if (msglen != 5) + throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")"); + + int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id); + + synchronized (c) + { + c.failedCounter++; + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")"); + } + + public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException + { + PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen); + + Channel c = getChannel(sm.recipientChannelID); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel " + + sm.recipientChannelID); + + synchronized (c) + { + if (c.state != Channel.STATE_OPENING) + throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel " + + sm.recipientChannelID); + + c.remoteID = sm.senderChannelID; + c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */ + c.remoteMaxPacketSize = sm.maxPacketSize; + c.state = Channel.STATE_OPEN; + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: " + + sm.senderChannelID + ")"); + } + + public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException + { + if (msglen < 5) + throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")"); + + TypesReader tr = new TypesReader(msg, 0, msglen); + + tr.readByte(); // skip packet type + int id = tr.readUINT32(); /* sender channel */ + + Channel c = getChannel(id); + + if (c == null) + throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id); + + int reasonCode = tr.readUINT32(); + String description = tr.readString("UTF-8"); + + String reasonCodeSymbolicName = null; + + switch (reasonCode) + { + case 1: + reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED"; + break; + case 2: + reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED"; + break; + case 3: + reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE"; + break; + case 4: + reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE"; + break; + default: + reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")"; + } + + StringBuffer descriptionBuffer = new StringBuffer(); + descriptionBuffer.append(description); + + for (int i = 0; i < descriptionBuffer.length(); i++) + { + char cc = descriptionBuffer.charAt(i); + + if ((cc >= 32) && (cc <= 126)) + continue; + descriptionBuffer.setCharAt(i, '\uFFFD'); + } + + synchronized (c) + { + c.eof(); + c.state = Channel.STATE_CLOSED; + c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '" + + descriptionBuffer.toString() + "')"); + c.notifyAll(); + } + + if (log.isEnabled()) + log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")"); + } + + public void msgGlobalRequest(byte[] msg, int msglen) throws IOException + { + /* Currently we do not support any kind of global request */ + + TypesReader tr = new TypesReader(msg, 0, msglen); + + tr.readByte(); // skip packet type + String requestName = tr.readString(); + boolean wantReply = tr.readBoolean(); + + if (wantReply) + { + byte[] reply_failure = new byte[1]; + reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE; + + tm.sendAsynchronousMessage(reply_failure); + } + + /* We do not clean up the requestName String - that is OK for debug */ + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")"); + } + + public void msgGlobalSuccess() throws IOException + { + synchronized (channels) + { + globalSuccessCounter++; + channels.notifyAll(); + } + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_REQUEST_SUCCESS"); + } + + public void msgGlobalFailure() throws IOException + { + synchronized (channels) + { + globalFailedCounter++; + channels.notifyAll(); + } + + if (log.isEnabled()) + log.log(80, "Got SSH_MSG_REQUEST_FAILURE"); + } + + public void handleMessage(byte[] msg, int msglen) throws IOException + { + switch (msg[0]) + { + case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + msgChannelOpenConfirmation(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST: + msgChannelWindowAdjust(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_DATA: + msgChannelData(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA: + msgChannelExtendedData(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_REQUEST: + msgChannelRequest(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_EOF: + msgChannelEOF(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_OPEN: + msgChannelOpen(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_CLOSE: + msgChannelClose(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_SUCCESS: + msgChannelSuccess(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_FAILURE: + msgChannelFailure(msg, msglen); + break; + case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE: + msgChannelOpenFailure(msg, msglen); + break; + case Packets.SSH_MSG_GLOBAL_REQUEST: + msgGlobalRequest(msg, msglen); + break; + case Packets.SSH_MSG_REQUEST_SUCCESS: + msgGlobalSuccess(); + break; + case Packets.SSH_MSG_REQUEST_FAILURE: + msgGlobalFailure(); + break; + default: + throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff)); + } + } + + public void handleEndMessage(Throwable cause) throws IOException { + if (log.isEnabled()) + log.log(50, "HandleMessage: got shutdown"); + + synchronized (listenerThreads) + { + for (int i = 0; i < listenerThreads.size(); i++) + { + IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i); + lat.stopWorking(); + } + listenerThreadsAllowed = false; + } + + synchronized (channels) + { + shutdown = true; + + for (int i = 0; i < channels.size(); i++) + { + Channel c = (Channel) channels.elementAt(i); + synchronized (c) + { + c.eof(); + c.state = Channel.STATE_CLOSED; + c.setReasonClosed(new IOException("The connection is being shutdown").initCause(cause)); + c.closeMessageRecv = true; /* + * You never know, perhaps + * we are waiting for a + * pending close message + * from the server... + */ + c.notifyAll(); + } + } + /* Works with J2ME */ + channels.setSize(0); + channels.trimToSize(); + channels.notifyAll(); /* Notify global response waiters */ + } + } + + private IOException ioException(String msg, Channel c) { + return (IOException)new IOException(msg).initCause(c.getReasonClosedCause()); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/ChannelOutputStream.java b/service/src/main/java/com/trilead/ssh2/channel/ChannelOutputStream.java new file mode 100644 index 0000000..7c6f6ec --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/ChannelOutputStream.java @@ -0,0 +1,70 @@ +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * ChannelOutputStream. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ChannelOutputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public final class ChannelOutputStream extends OutputStream +{ + Channel c; + + boolean isClosed = false; + + ChannelOutputStream(Channel c) + { + this.c = c; + } + + public void write(int b) throws IOException + { + byte[] buff = new byte[1]; + + buff[0] = (byte) b; + + write(buff, 0, 1); + } + + public void close() throws IOException + { + if (isClosed == false) + { + isClosed = true; + c.cm.sendEOF(c); + } + } + + public void flush() throws IOException + { + if (isClosed) + throw new IOException("This OutputStream is closed."); + + /* This is a no-op, since this stream is unbuffered */ + } + + public void write(byte[] b, int off, int len) throws IOException + { + if (isClosed) + throw new IOException("This OutputStream is closed."); + + if (b == null) + throw new NullPointerException(); + + if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) + throw new IndexOutOfBoundsException(); + + if (len == 0) + return; + + c.cm.sendData(c, b, off, len); + } + + public void write(byte[] b) throws IOException + { + write(b, 0, b.length); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/DynamicAcceptThread.java b/service/src/main/java/com/trilead/ssh2/channel/DynamicAcceptThread.java new file mode 100644 index 0000000..7f54c59 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/DynamicAcceptThread.java @@ -0,0 +1,345 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NoRouteToHostException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.Semaphore; + +import net.sourceforge.jsocks.Proxy; +import net.sourceforge.jsocks.ProxyMessage; +import net.sourceforge.jsocks.Socks5Message; +import net.sourceforge.jsocks.SocksException; +import net.sourceforge.jsocks.server.ServerAuthenticator; +import net.sourceforge.jsocks.server.ServerAuthenticatorNone; +import android.util.Log; +import com.trilead.ssh2.log.Logger; + +/** + * DynamicAcceptThread. + * + * @author Kenny Root + * @version $Id$ + */ +public class DynamicAcceptThread extends Thread + implements IChannelWorkerThread { + + private static final Logger log = Logger.getLogger(DynamicAcceptThread.class); + + private ChannelManager cm; + private final static int MAX_THREAD_COUNT = 25; + private Semaphore threadBound; + + private ServerSocket ss; + + public DynamicAcceptThread(ChannelManager cm, InetSocketAddress localAddress, int maxThreads) + throws IOException { + this.cm = cm; + + setName("DynamicAcceptThread"); + + ss = new ServerSocket(); + ss.bind(localAddress); + + if (maxThreads < 2) { + maxThreads = MAX_THREAD_COUNT; + } + + threadBound = new Semaphore(maxThreads); + } + + public DynamicAcceptThread(ChannelManager cm, int local_port, int maxThreads) + throws IOException { + this.cm = cm; + + setName("DynamicAcceptThread"); + + ss = new ServerSocket(local_port); + + if (maxThreads < 2) { + maxThreads = MAX_THREAD_COUNT; + } + + threadBound = new Semaphore(maxThreads); + } + + public int getLocalPort() { + return ss.getLocalPort(); + } + + @Override + public void run() { + try { + cm.registerThread(this); + } catch (IOException e) { + stopWorking(); + return; + } + + while (true) { + Socket sock = null; + + try { + sock = ss.accept(); + } catch (IOException e) { + stopWorking(); + return; + } + + threadBound.acquireUninterruptibly(); + + DynamicAcceptRunnable dar = new DynamicAcceptRunnable( + new ServerAuthenticatorNone(), sock); + Thread t = new Thread(dar); + t.setDaemon(true); + t.start(); + } + } + + /* + * (non-Javadoc) + * + * @see com.trilead.ssh2.channel.IChannelWorkerThread#stopWorking() + */ + @Override + public void stopWorking() { + try { + /* This will lead to an IOException in the ss.accept() call */ + ss.close(); + } catch (IOException e) { + } + } + + class DynamicAcceptRunnable implements Runnable { + +private boolean exceptionChainContains(Throwable t, String needle) { + while (t != null) { + String m = t.getMessage(); + if (m != null && m.contains(needle)) + return true; + t = t.getCause(); + } + return false; +} + +private static final int idleTimeout = 60*1000*6; // 6 minutes + + private ServerAuthenticator auth; + private Socket sock; + private InputStream in; + private OutputStream out; + private ProxyMessage msg; + + public DynamicAcceptRunnable(ServerAuthenticator auth, Socket sock) { + this.auth = auth; + this.sock = sock; + + setName("DynamicAcceptRunnable"); + } + + @Override + public void run() { + try { + startSession(); + } catch (IOException ioe) { + int error_code = Proxy.SOCKS_FAILURE; + + if (ioe instanceof SocksException) + error_code = ((SocksException) ioe).errCode; + else if (ioe instanceof NoRouteToHostException) + error_code = Proxy.SOCKS_HOST_UNREACHABLE; + else if (ioe instanceof ConnectException) + error_code = Proxy.SOCKS_CONNECTION_REFUSED; + else if (ioe instanceof InterruptedIOException) + error_code = Proxy.SOCKS_TTL_EXPIRE; + + if (error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED + || error_code < 0) { + error_code = Proxy.SOCKS_FAILURE; + } + + sendErrorMessage(error_code); + } catch (Error e) { + // Force to GC here + System.gc(); + } finally { + if (auth != null) + auth.endSession(); + threadBound.release(); + } + } + + private void handleRequest(ProxyMessage msg) throws IOException { + if (!auth.checkRequest(msg)) + throw new SocksException(Proxy.SOCKS_FAILURE); + + switch (msg.command) { + case Proxy.SOCKS_CMD_CONNECT: + onConnect(msg); + break; + default: + throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED); + } + } + + private void onConnect(ProxyMessage msg) throws IOException { + ProxyMessage response = null; + Channel cn = null; + StreamForwarder r2l = null; + StreamForwarder l2r = null; + + response = new Socks5Message(Proxy.SOCKS_SUCCESS, + (InetAddress) null, 0); + + response.write(out); + + String destHost = msg.host; + if (msg.ip != null) + destHost = msg.ip.getHostAddress(); + + try { + /* + * This may fail, e.g., if the remote port is closed (in + * optimistic terms: not open yet) + */ + + cn = cm.openDirectTCPIPChannel(destHost, msg.port, "127.0.0.1", + 0); + + } catch (IOException e) { + /* + * Some SSH server implementations (commonly Go-based) incorrectly build + * "host:port" for IPv6 literals without brackets, which results in + * "too many colons in address". Retry once with a bracketed IPv6 host. + */ + if (destHost != null && destHost.indexOf(':') >= 0 && destHost.charAt(0) != '[' + && (exceptionChainContains(e, "too many colons in address") + || (exceptionChainContains(e, "dial tcp") && exceptionChainContains(e, "too many colons")))) { + try { + String bracketedHost = "[" + destHost + "]"; + if (log.isEnabled()) + log.log(50, "Retrying DirectTCPIP with bracketed IPv6 host: " + bracketedHost + ":" + msg.port); + cn = cm.openDirectTCPIPChannel(bracketedHost, msg.port, "127.0.0.1", 0); + } catch (IOException retry) { + e = retry; + } + } + + if (cn == null) { + /* + * Simply close the local socket and wait for the next incoming + * connection + */ + try { + sock.close(); + } catch (IOException ignore) { + } + return; + } + } + + try { + r2l = new StreamForwarder(cn, null, null, cn.getStdoutStream(), out, + "RemoteToLocal"); + l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, + "LocalToRemote"); + } catch (IOException e) { + try { + /* + * This message is only visible during debugging, since we + * discard the channel immediatelly + */ + cn.cm.closeChannel(cn, + "Weird error during creation of StreamForwarder (" + + e.getMessage() + ")", true); + } catch (IOException ignore) { + } + + return; + } + + r2l.setDaemon(true); + l2r.setDaemon(true); + r2l.start(); + l2r.start(); + } + + private ProxyMessage readMsg(InputStream in) throws IOException { + PushbackInputStream push_in; + if (in instanceof PushbackInputStream) + push_in = (PushbackInputStream) in; + else + push_in = new PushbackInputStream(in); + + int version = push_in.read(); + push_in.unread(version); + + ProxyMessage msg; + + if (version == 5) { + msg = new Socks5Message(push_in, false); + } else { + throw new SocksException(Proxy.SOCKS_FAILURE); + } + return msg; + } + + private void sendErrorMessage(int error_code) { + ProxyMessage err_msg = new Socks5Message(error_code); + + try { + err_msg.write(out); + } catch (IOException ioe) { + } + } + + private void startSession() throws IOException { + sock.setSoTimeout(idleTimeout); + + try { + auth = auth.startSession(sock); + } catch (IOException ioe) { + log.log(50, "Could not start SOCKS session"); + ioe.printStackTrace(); + auth = null; + return; + } + + if (auth == null) { // Authentication failed + log.log(50, "SOCKS auth failed"); + return; + } + + in = auth.getInputStream(); + out = auth.getOutputStream(); + + msg = readMsg(in); + handleRequest(msg); + } + } +} + diff --git a/service/src/main/java/com/trilead/ssh2/channel/FifoBuffer.java b/service/src/main/java/com/trilead/ssh2/channel/FifoBuffer.java new file mode 100644 index 0000000..332fe15 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/FifoBuffer.java @@ -0,0 +1,244 @@ +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * FIFO buffer for a reader thread and a writer thread to collaborate. + * + * Unlike a ring buffer, which uses a fixed memory regardless of the number of bytes currently in the buffer, + * this implementation uses a single linked list to reduce the memory footprint when the reader + * closely follows the writer, regardless of the capacity limit set in the constructor. + * + * In trilead, the writer puts the data we receive from the network, and the user code acts as a reader. + * A user code normally drains the buffer more quickly than what the network delivers, so this implementation + * saves memory while simultaneously allowing us to advertise a bigger window size for a large latency network. + * + * @author Kohsuke Kawaguchi + */ +class FifoBuffer { + /** + * Unit of buffer, singly linked and lazy created as needed. + */ + static final class Page { + final byte[] buf; + Page next; + + Page(int sz) { + this.buf = new byte[sz]; + } + } + + /** + * Points to a specific byte in a {@link Page}. + */ + class Pointer { + Page p; + /** + * [0,p.buf.size) + */ + int off; + + Pointer(Page p, int off) { + this.p = p; + this.off = off; + } + + /** + * Figure out the number of bytes that can be read/written in one array copy. + */ + private int chunk() { + int sz = pageSize-off; + assert sz>=0; + + if (sz>0) return sz; + + Page q = p.next; + if (q==null) + q = p.next = newPage(); + p = q; + off = 0; + return pageSize; + } + + public void write(byte[] buf, int start, int len) { + while (len>0) { + int chunk = Math.min(len,chunk()); + System.arraycopy(buf,start,p.buf,off,chunk); + + off+=chunk; + len-=chunk; + start+=chunk; + } + } + + public void read(byte[] buf, int start, int len) { + while (len>0) { + int chunk = Math.min(len,chunk()); + System.arraycopy(p.buf,off,buf,start,chunk); + + off+=chunk; + len-=chunk; + start+=chunk; + } + } + } + + private final Object lock; + + /** + * Number of bytes currently in this ring buffer + */ + private int sz; + /** + * Cap to the # of bytes that we can hold. + */ + private int limit; + private final int pageSize; + + /** + * The position at which the next read/write will happen. + */ + private Pointer r,w; + + /** + * Set to true when the writer closes the write end. + */ + private boolean closed; + + FifoBuffer(int pageSize, int limit) { + this(null,pageSize,limit); + } + + FifoBuffer(Object lock, int pageSize, int limit) { + this.lock = lock==null ? this : lock; + this.limit = limit; + this.pageSize = pageSize; + + Page p = newPage(); + r = new Pointer(p,0); + w = new Pointer(p,0); + } + + public void setLimit(int newLimit) { + synchronized (lock) { + limit = newLimit; + } + } + + private Page newPage() { + return new Page(pageSize); + } + + /** + * Number of bytes readable + */ + int readable() { + synchronized (lock) { + return sz; + } + } + + /** + * Number of bytes writable + */ + int writable() { + return Math.max(0,limit-readable()); + } + + public void write(byte[] buf, int start, int len) throws InterruptedException { + while (len>0) { + int chunk; + + synchronized (lock) { + while ((chunk = Math.min(len,writable()))==0) + lock.wait(); + + w.write(buf, start, chunk); + + start += chunk; + len -= chunk; + sz += chunk; + + lock.notifyAll(); + } + } + } + + public void close() { + synchronized (lock) { + if (!closed) { + closed = true; + releaseRing(); + lock.notifyAll(); + } + } + } + + /** + * If the ring is no longer needed, release the buffer. + */ + private void releaseRing() { + if (closed && readable()==0) + r = w = null; + } + + /** + * + * @see InputStream#read(byte[],int,int) + */ + public int read(byte[] buf, int start, int len) throws InterruptedException { + if (len==0) return 0; + + int read = 0; // total # of bytes read + + while (true) { + int chunk; + + synchronized (lock) { + while (true) { + chunk = Math.min(len,readable()); + if (chunk>0) break; + + // there's nothing we can immediately read + + if (read>0) return read; // we've already read some + + if (closed) { + releaseRing(); + return -1; // no more data + } + lock.wait(); // wait until the writer gives us something + } + + r.read(buf,start,chunk); + + start += chunk; + len -= chunk; + read += chunk; + sz -= chunk; + + lock.notifyAll(); + } + } + } + + /** + * Write whatever readable to the specified OutputStream, then return. + */ + public int writeTo(OutputStream out) throws IOException { + try { + int total = 0; + while (readable()>0) { + byte[] buf = new byte[1024]; // most often this method gets called before we have any data, so this is a win + int read = read(buf, 0, buf.length); + out.write(buf,0,read); + total += read; + } + return total; + } catch (InterruptedException e) { + throw new AssertionError(e); // we carefully read only what we can read without blocking + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/IChannelWorkerThread.java b/service/src/main/java/com/trilead/ssh2/channel/IChannelWorkerThread.java new file mode 100644 index 0000000..bce9b1b --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/IChannelWorkerThread.java @@ -0,0 +1,13 @@ + +package com.trilead.ssh2.channel; + +/** + * IChannelWorkerThread. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +interface IChannelWorkerThread +{ + public void stopWorking(); +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java b/service/src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java new file mode 100644 index 0000000..9a8352d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java @@ -0,0 +1,135 @@ + +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * LocalAcceptThread. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class LocalAcceptThread extends Thread implements IChannelWorkerThread +{ + ChannelManager cm; + String host_to_connect; + int port_to_connect; + + final ServerSocket ss; + + public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect) + throws IOException + { + this.cm = cm; + this.host_to_connect = host_to_connect; + this.port_to_connect = port_to_connect; + + ss = new ServerSocket(local_port); + } + + public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect, + int port_to_connect) throws IOException + { + this.cm = cm; + this.host_to_connect = host_to_connect; + this.port_to_connect = port_to_connect; + + ss = new ServerSocket(); + ss.bind(localAddress); + } + + public void run() + { + try + { + cm.registerThread(this); + } + catch (IOException e) + { + stopWorking(); + return; + } + + while (true) + { + Socket s = null; + + try + { + s = ss.accept(); + } + catch (IOException e) + { + stopWorking(); + return; + } + + Channel cn = null; + StreamForwarder r2l = null; + StreamForwarder l2r = null; + + try + { + /* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */ + + cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s + .getPort()); + + } + catch (IOException e) + { + /* Simply close the local socket and wait for the next incoming connection */ + + try + { + s.close(); + } + catch (IOException ignore) + { + } + + continue; + } + + try + { + r2l = new StreamForwarder(cn, null, null, cn.getStdoutStream(), s.getOutputStream(), "RemoteToLocal"); + l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote"); + } + catch (IOException e) + { + try + { + /* This message is only visible during debugging, since we discard the channel immediatelly */ + cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")", + true); + } + catch (IOException ignore) + { + } + + continue; + } + + r2l.setDaemon(true); + l2r.setDaemon(true); + r2l.start(); + l2r.start(); + } + } + + public void stopWorking() + { + try + { + /* This will lead to an IOException in the ss.accept() call */ + ss.close(); + } + catch (IOException e) + { + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java b/service/src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java new file mode 100644 index 0000000..12b1766 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java @@ -0,0 +1,105 @@ + +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.Socket; + +import com.trilead.ssh2.log.Logger; + + +/** + * RemoteAcceptThread. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class RemoteAcceptThread extends Thread +{ + private static final Logger log = Logger.getLogger(RemoteAcceptThread.class); + + Channel c; + + String remoteConnectedAddress; + int remoteConnectedPort; + String remoteOriginatorAddress; + int remoteOriginatorPort; + String targetAddress; + int targetPort; + + Socket s; + + public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort, + String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort) + { + this.c = c; + this.remoteConnectedAddress = remoteConnectedAddress; + this.remoteConnectedPort = remoteConnectedPort; + this.remoteOriginatorAddress = remoteOriginatorAddress; + this.remoteOriginatorPort = remoteOriginatorPort; + this.targetAddress = targetAddress; + this.targetPort = targetPort; + + if (log.isEnabled()) + log.log(30, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: " + + remoteOriginatorAddress + "/" + remoteOriginatorPort); + } + + public void run() + { + try + { + c.cm.sendOpenConfirmation(c); + + s = new Socket(targetAddress, targetPort); + + StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(), + "RemoteToLocal"); + StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(), + "LocalToRemote"); + + /* No need to start two threads, one can be executed in the current thread */ + + r2l.setDaemon(true); + r2l.start(); + l2r.run(); + + while (r2l.isAlive()) + { + try + { + r2l.join(); + } + catch (InterruptedException e) + { + throw new InterruptedIOException(); + } + } + + /* If the channel is already closed, then this is a no-op */ + + c.cm.closeChannel(c, "EOF on both streams reached.", true); + s.close(); + } + catch (IOException e) + { + log.log(50, "IOException in proxy code",e); + + try + { + c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true); + } + catch (IOException e1) + { + } + try + { + if (s != null) + s.close(); + } + catch (IOException e1) + { + } + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/RemoteForwardingData.java b/service/src/main/java/com/trilead/ssh2/channel/RemoteForwardingData.java new file mode 100644 index 0000000..d05378e --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/RemoteForwardingData.java @@ -0,0 +1,17 @@ + +package com.trilead.ssh2.channel; + +/** + * RemoteForwardingData. Data about a requested remote forwarding. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RemoteForwardingData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class RemoteForwardingData +{ + public String bindAddress; + public int bindPort; + + String targetAddress; + int targetPort; +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java b/service/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java new file mode 100644 index 0000000..ee3e087 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java @@ -0,0 +1,241 @@ +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.InterruptedIOException; +import java.net.Socket; + +import com.trilead.ssh2.log.Logger; + + +/** + * RemoteX11AcceptThread. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class RemoteX11AcceptThread extends Thread +{ + private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class); + + Channel c; + + String remoteOriginatorAddress; + int remoteOriginatorPort; + + Socket s; + + public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort) + { + this.c = c; + this.remoteOriginatorAddress = remoteOriginatorAddress; + this.remoteOriginatorPort = remoteOriginatorPort; + } + + public void run() + { + try + { + /* Send Open Confirmation */ + + c.cm.sendOpenConfirmation(c); + + /* Read startup packet from client */ + + OutputStream remote_os = c.getStdinStream(); + InputStream remote_is = c.getStdoutStream(); + + /* The following code is based on the protocol description given in: + * Scheifler/Gettys, + * X Windows System: Core and Extension Protocols: + * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X + */ + + /* + * Client startup: + * + * 1 0X42 MSB first/0x6c lSB first - byteorder + * 1 - unused + * 2 card16 - protocol-major-version + * 2 card16 - protocol-minor-version + * 2 n - lenght of authorization-protocol-name + * 2 d - lenght of authorization-protocol-data + * 2 - unused + * string8 - authorization-protocol-name + * p - unused, p=pad(n) + * string8 - authorization-protocol-data + * q - unused, q=pad(d) + * + * pad(X) = (4 - (X mod 4)) mod 4 + * + * Server response: + * + * 1 (0 failed, 2 authenticate, 1 success) + * ... + * + */ + + /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */ + + byte[] header = new byte[6]; + + if (remote_is.read(header) != 6) + throw new IOException("Unexpected EOF on X11 startup!"); + + if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first + throw new IOException("Unknown endian format in X11 message!"); + + /* Yes, I came up with this myself - shall I file an application for a patent? =) */ + + int idxMSB = (header[0] == 0x42) ? 0 : 1; + + /* Read authorization data header */ + + byte[] auth_buff = new byte[6]; + + if (remote_is.read(auth_buff) != 6) + throw new IOException("Unexpected EOF on X11 startup!"); + + int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff); + int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff); + + if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256)) + throw new IOException("Buggy X11 authorization data"); + + int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4); + int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4); + + byte[] authProtocolName = new byte[authProtocolNameLength]; + byte[] authProtocolData = new byte[authProtocolDataLength]; + + byte[] paddingBuffer = new byte[4]; + + if (remote_is.read(authProtocolName) != authProtocolNameLength) + throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)"); + + if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding) + throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)"); + + if (remote_is.read(authProtocolData) != authProtocolDataLength) + throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)"); + + if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding) + throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)"); + + if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName, "ISO-8859-1")) == false) + throw new IOException("Unknown X11 authorization protocol!"); + + if (authProtocolDataLength != 16) + throw new IOException("Wrong data length for X11 authorization data!"); + + StringBuffer tmp = new StringBuffer(32); + for (int i = 0; i < authProtocolData.length; i++) + { + String digit2 = Integer.toHexString(authProtocolData[i] & 0xff); + tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); + } + String hexEncodedFakeCookie = tmp.toString(); + + /* Order is very important here - it may be that a certain x11 forwarding + * gets disabled right in the moment when we check and register our connection + * */ + + synchronized (c) + { + /* Please read the comment in Channel.java */ + c.hexX11FakeCookie = hexEncodedFakeCookie; + } + + /* Now check our fake cookie directory to see if we produced this cookie */ + + X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie); + + if (sd == null) + throw new IOException("Invalid X11 cookie received."); + + /* If the session which corresponds to this cookie is closed then we will + * detect this: the session's close code will close all channels + * with the session's assigned x11 fake cookie. + */ + + s = new Socket(sd.hostname, sd.port); + + OutputStream x11_os = s.getOutputStream(); + InputStream x11_is = s.getInputStream(); + + /* Now we are sending the startup packet to the real X11 server */ + + x11_os.write(header); + + if (sd.x11_magic_cookie == null) + { + byte[] emptyAuthData = new byte[6]; + /* empty auth data, hopefully you are connecting to localhost =) */ + x11_os.write(emptyAuthData); + } + else + { + if (sd.x11_magic_cookie.length != 16) + throw new IOException("The real X11 cookie has an invalid length!"); + + /* send X11 cookie specified by client */ + x11_os.write(auth_buff); + x11_os.write(authProtocolName); /* re-use */ + x11_os.write(paddingBuffer, 0, authProtocolNamePadding); + x11_os.write(sd.x11_magic_cookie); + x11_os.write(paddingBuffer, 0, authProtocolDataPadding); + } + + x11_os.flush(); + + /* Start forwarding traffic */ + + StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11"); + StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote"); + + /* No need to start two threads, one can be executed in the current thread */ + + r2l.setDaemon(true); + r2l.start(); + l2r.run(); + + while (r2l.isAlive()) + { + try + { + r2l.join(); + } + catch (InterruptedException e) + { + throw new InterruptedIOException(); + } + } + + /* If the channel is already closed, then this is a no-op */ + + c.cm.closeChannel(c, "EOF on both X11 streams reached.", true); + s.close(); + } + catch (IOException e) + { + log.log(50, "IOException in X11 proxy code",e); + + try + { + c.cm.closeChannel(c, "IOException in X11 proxy code (" + e.getMessage() + ")", true); + } + catch (IOException e1) + { + } + try + { + if (s != null) + s.close(); + } + catch (IOException e1) + { + } + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/StreamForwarder.java b/service/src/main/java/com/trilead/ssh2/channel/StreamForwarder.java new file mode 100644 index 0000000..a5d27a2 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/StreamForwarder.java @@ -0,0 +1,114 @@ +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * A StreamForwarder forwards data between two given streams. + * If two StreamForwarder threads are used (one for each direction) + * then one can be configured to shutdown the underlying channel/socket + * if both threads have finished forwarding (EOF). + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class StreamForwarder extends Thread +{ + OutputStream os; + InputStream is; + byte[] buffer; + Channel c; + StreamForwarder sibling; + Socket s; + String mode; + + StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode) + throws IOException + { + this.is = is; + this.os = os; + this.mode = mode; + this.c = c; + this.sibling = sibling; + this.s = s; + // window size is for the other side of the network with some latency. + // we don't need such a big buffer for a copy stream tight loop + this.buffer = new byte[8192/*c.channelBufferSize*/]; + } + + public void run() + { + try + { + while (true) + { + int len = is.read(buffer); + if (len <= 0) + break; + os.write(buffer, 0, len); + os.flush(); + } + } + catch (IOException ignore) + { + try + { + c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): " + + ignore.getMessage(), true); + } + catch (IOException e) + { + } + } + finally + { + try + { + os.close(); + } + catch (IOException e1) + { + } + try + { + is.close(); + } + catch (IOException e2) + { + } + + if (sibling != null) + { + while (sibling.isAlive()) + { + try + { + sibling.join(); + } + catch (InterruptedException e) + { + } + } + + try + { + c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true); + } + catch (IOException e3) + { + } + + try + { + if (s != null) + s.close(); + } + catch (IOException e1) + { + } + } + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/channel/X11ServerData.java b/service/src/main/java/com/trilead/ssh2/channel/X11ServerData.java new file mode 100644 index 0000000..041f9cb --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/channel/X11ServerData.java @@ -0,0 +1,16 @@ + +package com.trilead.ssh2.channel; + +/** + * X11ServerData. Data regarding an x11 forwarding target. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: X11ServerData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + * + */ +public class X11ServerData +{ + public String hostname; + public int port; + public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */ +} diff --git a/service/src/main/java/com/trilead/ssh2/compression/CompressionFactory.java b/service/src/main/java/com/trilead/ssh2/compression/CompressionFactory.java new file mode 100644 index 0000000..0b01b1d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/compression/CompressionFactory.java @@ -0,0 +1,86 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.compression; + +import java.util.Vector; + +/** + * @author Kenny Root + * + */ +public class CompressionFactory { + static class CompressorEntry { + String type; + String compressorClass; + + public CompressorEntry(String type, String compressorClass) { + this.type = type; + this.compressorClass = compressorClass; + } + } + + static Vector compressors = new Vector(); + + static { + /* Higher Priority First */ + + compressors.addElement(new CompressorEntry("zlib", + "com.trilead.ssh2.compression.Zlib")); + compressors.addElement(new CompressorEntry("zlib@openssh.com", + "com.trilead.ssh2.compression.ZlibOpenSSH")); + compressors.addElement(new CompressorEntry("none", "")); + } + + public static void checkCompressorList(String[] compressorCandidates) { + for (int i = 0; i < compressorCandidates.length; i++) + getEntry(compressorCandidates[i]); + } + + public static ICompressor createCompressor(String type) { + try { + CompressorEntry ce = getEntry(type); + if ("".equals(ce.compressorClass)) + return null; + + Class cc = Class.forName(ce.compressorClass); + ICompressor cmp = (ICompressor) cc.newInstance(); + + return cmp; + } catch (Exception e) { + throw new IllegalArgumentException("Cannot instantiate " + type); + } + } + + public static String[] getDefaultCompressorList() { + String list[] = new String[compressors.size()]; + for (int i = 0; i < compressors.size(); i++) { + CompressorEntry ce = compressors.elementAt(i); + list[i] = new String(ce.type); + } + return list; + } + + private static CompressorEntry getEntry(String type) { + for (int i = 0; i < compressors.size(); i++) { + CompressorEntry ce = compressors.elementAt(i); + if (ce.type.equals(type)) + return ce; + } + throw new IllegalArgumentException("Unkown algorithm " + type); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/compression/ICompressor.java b/service/src/main/java/com/trilead/ssh2/compression/ICompressor.java new file mode 100644 index 0000000..e6e1697 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/compression/ICompressor.java @@ -0,0 +1,32 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.compression; + +/** + * @author Kenny Root + * + */ +public interface ICompressor { + boolean canCompressPreauth(); + + int compress(byte[] buf, int start, int len, byte[] output); + + int getBufferSize(); + + byte[] uncompress(byte[] buf, int start, int[] len); +} diff --git a/service/src/main/java/com/trilead/ssh2/compression/Zlib.java b/service/src/main/java/com/trilead/ssh2/compression/Zlib.java new file mode 100644 index 0000000..9eb2eb4 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/compression/Zlib.java @@ -0,0 +1,134 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.compression; + +import com.jcraft.jzlib.JZlib; +import com.jcraft.jzlib.ZStream; + +/** + * @author Kenny Root + * + */ +public class Zlib implements ICompressor { + static private final int DEFAULT_BUF_SIZE = 4096; + static private final int LEVEL = 5; + + private ZStream deflate; + private byte[] deflate_tmpbuf; + + private ZStream inflate; + private byte[] inflate_tmpbuf; + private byte[] inflated_buf; + + public Zlib() { + deflate = new ZStream(); + inflate = new ZStream(); + + deflate.deflateInit(LEVEL); + inflate.inflateInit(); + + deflate_tmpbuf = new byte[DEFAULT_BUF_SIZE]; + inflate_tmpbuf = new byte[DEFAULT_BUF_SIZE]; + inflated_buf = new byte[DEFAULT_BUF_SIZE]; + } + + @Override + public boolean canCompressPreauth() { + return true; + } + + @Override + public int compress(byte[] buf, int start, int len, byte[] output) { + deflate.next_in = buf; + deflate.next_in_index = start; + deflate.avail_in = len - start; + + if ((buf.length + 1024) > deflate_tmpbuf.length) { + deflate_tmpbuf = new byte[buf.length + 1024]; + } + + deflate.next_out = deflate_tmpbuf; + deflate.next_out_index = 0; + deflate.avail_out = output.length; + + if (deflate.deflate(JZlib.Z_PARTIAL_FLUSH) != JZlib.Z_OK) { + System.err.println("compress: compression failure"); + } + + if (deflate.avail_in > 0) { + System.err.println("compress: deflated data too large"); + } + + int outputlen = output.length - deflate.avail_out; + + System.arraycopy(deflate_tmpbuf, 0, output, 0, outputlen); + + return outputlen; + } + + @Override + public int getBufferSize() { + return DEFAULT_BUF_SIZE; + } + + @Override + public byte[] uncompress(byte[] buffer, int start, int[] length) { + int inflated_end = 0; + + inflate.next_in = buffer; + inflate.next_in_index = start; + inflate.avail_in = length[0]; + + while (true) { + inflate.next_out = inflate_tmpbuf; + inflate.next_out_index = 0; + inflate.avail_out = DEFAULT_BUF_SIZE; + int status = inflate.inflate(JZlib.Z_PARTIAL_FLUSH); + switch (status) { + case JZlib.Z_OK: + if (inflated_buf.length < inflated_end + DEFAULT_BUF_SIZE + - inflate.avail_out) { + byte[] foo = new byte[inflated_end + DEFAULT_BUF_SIZE + - inflate.avail_out]; + System.arraycopy(inflated_buf, 0, foo, 0, inflated_end); + inflated_buf = foo; + } + System.arraycopy(inflate_tmpbuf, 0, inflated_buf, inflated_end, + DEFAULT_BUF_SIZE - inflate.avail_out); + inflated_end += (DEFAULT_BUF_SIZE - inflate.avail_out); + length[0] = inflated_end; + break; + case JZlib.Z_BUF_ERROR: + if (inflated_end > buffer.length - start) { + byte[] foo = new byte[inflated_end + start]; + System.arraycopy(buffer, 0, foo, 0, start); + System.arraycopy(inflated_buf, 0, foo, start, inflated_end); + buffer = foo; + } else { + System.arraycopy(inflated_buf, 0, buffer, start, + inflated_end); + } + length[0] = inflated_end; + return buffer; + default: + System.err.println("uncompress: inflate returnd " + status); + return null; + } + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/compression/ZlibOpenSSH.java b/service/src/main/java/com/trilead/ssh2/compression/ZlibOpenSSH.java new file mode 100644 index 0000000..8c0f2f6 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/compression/ZlibOpenSSH.java @@ -0,0 +1,35 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.compression; + +/** + * Defines how zlib@openssh.org compression works. See + * http://www.openssh.org/txt/draft-miller-secsh-compression-delayed-00.txt + * compression is disabled until userauth has occurred. + * + * @author Matt Johnston + * + */ +public class ZlibOpenSSH extends Zlib { + + @Override + public boolean canCompressPreauth() { + return false; + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/Base64.java b/service/src/main/java/com/trilead/ssh2/crypto/Base64.java new file mode 100644 index 0000000..93770ac --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/Base64.java @@ -0,0 +1,148 @@ + +package com.trilead.ssh2.crypto; + +import java.io.CharArrayWriter; +import java.io.IOException; + +/** + * Basic Base64 Support. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Base64.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class Base64 +{ + static final char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + + public static char[] encode(byte[] content) + { + CharArrayWriter cw = new CharArrayWriter((4 * content.length) / 3); + + int idx = 0; + + int x = 0; + + for (int i = 0; i < content.length; i++) + { + if (idx == 0) + x = (content[i] & 0xff) << 16; + else if (idx == 1) + x = x | ((content[i] & 0xff) << 8); + else + x = x | (content[i] & 0xff); + + idx++; + + if (idx == 3) + { + cw.write(alphabet[x >> 18]); + cw.write(alphabet[(x >> 12) & 0x3f]); + cw.write(alphabet[(x >> 6) & 0x3f]); + cw.write(alphabet[x & 0x3f]); + + idx = 0; + } + } + + if (idx == 1) + { + cw.write(alphabet[x >> 18]); + cw.write(alphabet[(x >> 12) & 0x3f]); + cw.write('='); + cw.write('='); + } + + if (idx == 2) + { + cw.write(alphabet[x >> 18]); + cw.write(alphabet[(x >> 12) & 0x3f]); + cw.write(alphabet[(x >> 6) & 0x3f]); + cw.write('='); + } + + return cw.toCharArray(); + } + + public static byte[] decode(char[] message) throws IOException + { + byte buff[] = new byte[4]; + byte dest[] = new byte[message.length]; + + int bpos = 0; + int destpos = 0; + + for (int i = 0; i < message.length; i++) + { + int c = message[i]; + + if ((c == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) + continue; + + if ((c >= 'A') && (c <= 'Z')) + { + buff[bpos++] = (byte) (c - 'A'); + } + else if ((c >= 'a') && (c <= 'z')) + { + buff[bpos++] = (byte) ((c - 'a') + 26); + } + else if ((c >= '0') && (c <= '9')) + { + buff[bpos++] = (byte) ((c - '0') + 52); + } + else if (c == '+') + { + buff[bpos++] = 62; + } + else if (c == '/') + { + buff[bpos++] = 63; + } + else if (c == '=') + { + buff[bpos++] = 64; + } + else + { + throw new IOException("Illegal char in base64 code."); + } + + if (bpos == 4) + { + bpos = 0; + + if (buff[0] == 64) + break; + + if (buff[1] == 64) + throw new IOException("Unexpected '=' in base64 code."); + + if (buff[2] == 64) + { + int v = (((buff[0] & 0x3f) << 6) | ((buff[1] & 0x3f))); + dest[destpos++] = (byte) (v >> 4); + break; + } + else if (buff[3] == 64) + { + int v = (((buff[0] & 0x3f) << 12) | ((buff[1] & 0x3f) << 6) | ((buff[2] & 0x3f))); + dest[destpos++] = (byte) (v >> 10); + dest[destpos++] = (byte) (v >> 2); + break; + } + else + { + int v = (((buff[0] & 0x3f) << 18) | ((buff[1] & 0x3f) << 12) | ((buff[2] & 0x3f) << 6) | ((buff[3] & 0x3f))); + dest[destpos++] = (byte) (v >> 16); + dest[destpos++] = (byte) (v >> 8); + dest[destpos++] = (byte) (v); + } + } + } + + byte[] res = new byte[destpos]; + System.arraycopy(dest, 0, res, 0, destpos); + + return res; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/CertificateDecoder.java b/service/src/main/java/com/trilead/ssh2/crypto/CertificateDecoder.java new file mode 100644 index 0000000..1eebe37 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/CertificateDecoder.java @@ -0,0 +1,20 @@ +package com.trilead.ssh2.crypto; + +import java.io.IOException; +import java.security.KeyPair; + +/** + * @author Michael Clarke + */ +public abstract class CertificateDecoder { + + public abstract String getStartLine(); + + public abstract String getEndLine(); + + public KeyPair createKeyPair(PEMStructure pemStructure, String password) throws IOException { + return createKeyPair(pemStructure); + } + + protected abstract KeyPair createKeyPair(PEMStructure pemStructure) throws IOException; +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java b/service/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java new file mode 100644 index 0000000..06f6539 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java @@ -0,0 +1,28 @@ + +package com.trilead.ssh2.crypto; + +import com.trilead.ssh2.compression.*; +import com.trilead.ssh2.crypto.cipher.*; +import com.trilead.ssh2.crypto.digest.*; +import com.trilead.ssh2.transport.*; + + +/** + * CryptoWishList. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: CryptoWishList.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class CryptoWishList +{ + public String[] kexAlgorithms = KexManager.getDefaultKexAlgorithmList(); + public String[] serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList(); + public String[] c2s_enc_algos = BlockCipherFactory.getDefaultCipherList(); + public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList(); + public String[] c2s_mac_algos = MessageMac.getMacs(); + public String[] s2c_mac_algos = MessageMac.getMacs(); + public String[] c2s_comp_algos = CompressionFactory + .getDefaultCompressorList(); + public String[] s2c_comp_algos = CompressionFactory + .getDefaultCompressorList(); +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java b/service/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java new file mode 100644 index 0000000..c415c3c --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java @@ -0,0 +1,91 @@ + +package com.trilead.ssh2.crypto; + + +import java.math.BigInteger; + +import com.trilead.ssh2.crypto.digest.HashForSSH2Types; + +/** + * Establishes key material for iv/key/mac (both directions). + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: KeyMaterial.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class KeyMaterial +{ + public byte[] initial_iv_client_to_server; + public byte[] initial_iv_server_to_client; + public byte[] enc_key_client_to_server; + public byte[] enc_key_server_to_client; + public byte[] integrity_key_client_to_server; + public byte[] integrity_key_server_to_client; + + private static byte[] calculateKey(HashForSSH2Types sh, BigInteger K, byte[] H, byte type, byte[] SessionID, + int keyLength) + { + byte[] res = new byte[keyLength]; + + int dglen = sh.getDigestLength(); + int numRounds = (keyLength + dglen - 1) / dglen; + + byte[][] tmp = new byte[numRounds][]; + + sh.reset(); + sh.updateBigInt(K); + sh.updateBytes(H); + sh.updateByte(type); + sh.updateBytes(SessionID); + + tmp[0] = sh.getDigest(); + + int off = 0; + int produced = Math.min(dglen, keyLength); + + System.arraycopy(tmp[0], 0, res, off, produced); + + keyLength -= produced; + off += produced; + + for (int i = 1; i < numRounds; i++) + { + sh.updateBigInt(K); + sh.updateBytes(H); + + for (int j = 0; j < i; j++) + sh.updateBytes(tmp[j]); + + tmp[i] = sh.getDigest(); + + produced = Math.min(dglen, keyLength); + System.arraycopy(tmp[i], 0, res, off, produced); + keyLength -= produced; + off += produced; + } + + return res; + } + + public static KeyMaterial create(String hashType, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS, + int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC) + throws IllegalArgumentException + { + KeyMaterial km = new KeyMaterial(); + + HashForSSH2Types sh = new HashForSSH2Types(hashType); + + km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS); + + km.initial_iv_server_to_client = calculateKey(sh, K, H, (byte) 'B', SessionID, blockSizeSC); + + km.enc_key_client_to_server = calculateKey(sh, K, H, (byte) 'C', SessionID, keyLengthCS); + + km.enc_key_server_to_client = calculateKey(sh, K, H, (byte) 'D', SessionID, keyLengthSC); + + km.integrity_key_client_to_server = calculateKey(sh, K, H, (byte) 'E', SessionID, macLengthCS); + + km.integrity_key_server_to_client = calculateKey(sh, K, H, (byte) 'F', SessionID, macLengthSC); + + return km; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java b/service/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java new file mode 100644 index 0000000..e5d1c5c --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java @@ -0,0 +1,503 @@ + +package com.trilead.ssh2.crypto; + +import java.io.BufferedReader; +import java.io.CharArrayReader; +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyPair; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.trilead.ssh2.crypto.cipher.AES; +import com.trilead.ssh2.crypto.cipher.BlockCipher; +import com.trilead.ssh2.crypto.cipher.CBCMode; +import com.trilead.ssh2.crypto.cipher.DES; +import com.trilead.ssh2.crypto.cipher.DESede; +import com.trilead.ssh2.crypto.digest.MD5; +import com.trilead.ssh2.signature.DSAPrivateKey; +import com.trilead.ssh2.signature.KeyAlgorithm; +import com.trilead.ssh2.signature.KeyAlgorithmManager; +import com.trilead.ssh2.signature.RSAPrivateKey; + +/** + * PEM Support. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class PEMDecoder +{ + private static final Logger LOGGER = Logger.getLogger(PEMDecoder.class.getName()); + private static final int PEM_RSA_PRIVATE_KEY = 1; + private static final int PEM_DSA_PRIVATE_KEY = 2; + + private static int hexToInt(char c) + { + if ((c >= 'a') && (c <= 'f')) + { + return (c - 'a') + 10; + } + + if ((c >= 'A') && (c <= 'F')) + { + return (c - 'A') + 10; + } + + if ((c >= '0') && (c <= '9')) + { + return (c - '0'); + } + + throw new IllegalArgumentException("Need hex char"); + } + + private static byte[] hexToByteArray(String hex) + { + if (hex == null) + throw new IllegalArgumentException("null argument"); + + if ((hex.length() % 2) != 0) + throw new IllegalArgumentException("Uneven string length in hex encoding."); + + byte decoded[] = new byte[hex.length() / 2]; + + for (int i = 0; i < decoded.length; i++) + { + int hi = hexToInt(hex.charAt(i * 2)); + int lo = hexToInt(hex.charAt((i * 2) + 1)); + + decoded[i] = (byte) (hi * 16 + lo); + } + + return decoded; + } + + public static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen) + throws IOException + { + if (salt.length < 8) + throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation."); + + MD5 md5 = new MD5(); + + byte[] key = new byte[keyLen]; + byte[] tmp = new byte[md5.getDigestLength()]; + + while (true) + { + md5.update(password, 0, password.length); + md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the + // salt in this step. + // This took me two hours until I got AES-xxx running. + + int copy = (keyLen < tmp.length) ? keyLen : tmp.length; + + md5.digest(tmp, 0); + + System.arraycopy(tmp, 0, key, key.length - keyLen, copy); + + keyLen -= copy; + + if (keyLen == 0) + return key; + + md5.update(tmp, 0, tmp.length); + } + } + + private static byte[] removePadding(byte[] buff, int blockSize) throws IOException + { + /* Removes RFC 1423/PKCS #7 padding */ + + int rfc_1423_padding = buff[buff.length - 1] & 0xff; + + if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize)) + throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?"); + + for (int i = 2; i <= rfc_1423_padding; i++) + { + if (buff[buff.length - i] != rfc_1423_padding) + throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?"); + } + + byte[] tmp = new byte[buff.length - rfc_1423_padding]; + System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding); + return tmp; + } + + private static PEMStructure parsePEM(char[] pem) throws IOException + { + PEMStructure ps = new PEMStructure(); + + String line; + + BufferedReader br = new BufferedReader(new CharArrayReader(pem)); + + String endLine; + + while (true) + { + line = br.readLine(); + + if (line == null) + throw new IOException("Invalid PEM structure, '-----BEGIN...' missing"); + + line = line.trim(); + + if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----")) + { + endLine = "-----END DSA PRIVATE KEY-----"; + ps.pemType = PEM_DSA_PRIVATE_KEY; + break; + } + + if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----")) + { + endLine = "-----END RSA PRIVATE KEY-----"; + ps.pemType = PEM_RSA_PRIVATE_KEY; + break; + } + } + + while (true) + { + line = br.readLine(); + + if (line == null) + throw new IOException("Invalid PEM structure, " + endLine + " missing"); + + line = line.trim(); + + int sem_idx = line.indexOf(':'); + + if (sem_idx == -1) + break; + + String name = line.substring(0, sem_idx + 1); + String value = line.substring(sem_idx + 1); + + String values[] = value.split(","); + + for (int i = 0; i < values.length; i++) + values[i] = values[i].trim(); + + // Proc-Type: 4,ENCRYPTED + // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483 + + if ("Proc-Type:".equals(name)) + { + ps.procType = values; + continue; + } + + if ("DEK-Info:".equals(name)) + { + ps.dekInfo = values; + continue; + } + /* Ignore line */ + } + + StringBuilder keyData = new StringBuilder(); + + while (true) + { + if (line == null) + throw new IOException("Invalid PEM structure, " + endLine + " missing"); + + line = line.trim(); + + if (line.startsWith(endLine)) + break; + + keyData.append(line); + + line = br.readLine(); + } + + char[] pem_chars = new char[keyData.length()]; + keyData.getChars(0, pem_chars.length, pem_chars, 0); + + ps.data = Base64.decode(pem_chars); + + if (ps.data.length == 0) + throw new IOException("Invalid PEM structure, no data available"); + + return ps; + } + + + + private static PEMStructure parsePEM(char[] pem, CertificateDecoder certificateDecoder) throws IOException + { + PEMStructure ps = new PEMStructure(); + + String line; + + BufferedReader br = new BufferedReader(new CharArrayReader(pem)); + + String endLine; + + while (true) + { + line = br.readLine(); + + if (line == null) + throw new IOException("Invalid PEM structure, '-----BEGIN...' missing"); + + line = line.trim(); + + if (line.startsWith(certificateDecoder.getStartLine())) + { + endLine = certificateDecoder.getEndLine(); + break; + } + + } + + while (true) + { + line = br.readLine(); + + if (line == null) + throw new IOException("Invalid PEM structure, " + endLine + " missing"); + + line = line.trim(); + + int sem_idx = line.indexOf(':'); + + if (sem_idx == -1) + break; + + String name = line.substring(0, sem_idx + 1); + String value = line.substring(sem_idx + 1); + + String values[] = value.split(","); + + for (int i = 0; i < values.length; i++) + values[i] = values[i].trim(); + + // Proc-Type: 4,ENCRYPTED + // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483 + + if ("Proc-Type:".equals(name)) + { + ps.procType = values; + continue; + } + + if ("DEK-Info:".equals(name)) + { + ps.dekInfo = values; + continue; + } + /* Ignore line */ + } + + StringBuilder keyData = new StringBuilder(); + + while (true) + { + if (line == null) + throw new IOException("Invalid PEM structure, " + endLine + " missing"); + + line = line.trim(); + + if (line.startsWith(endLine)) + break; + + keyData.append(line); + + line = br.readLine(); + } + + char[] pem_chars = new char[keyData.length()]; + keyData.getChars(0, pem_chars.length, pem_chars, 0); + + ps.data = Base64.decode(pem_chars); + + if (ps.data.length == 0) + throw new IOException("Invalid PEM structure, no data available"); + + return ps; + } + + private static void decryptPEM(PEMStructure ps, byte[] pw) throws IOException + { + if (ps.dekInfo == null) + throw new IOException("Broken PEM, no mode and salt given, but encryption enabled"); + + if (ps.dekInfo.length != 2) + throw new IOException("Broken PEM, DEK-Info is incomplete!"); + + String algo = ps.dekInfo[0]; + byte[] salt = hexToByteArray(ps.dekInfo[1]); + + BlockCipher bc; + + if (algo.equals("DES-EDE3-CBC")) + { + DESede des3 = new DESede(); + des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24)); + bc = new CBCMode(des3, salt, false); + } + else if (algo.equals("DES-CBC")) + { + DES des = new DES(); + des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8)); + bc = new CBCMode(des, salt, false); + } + else if (algo.equals("AES-128-CBC")) + { + AES aes = new AES(); + aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16)); + bc = new CBCMode(aes, salt, false); + } + else if (algo.equals("AES-192-CBC")) + { + AES aes = new AES(); + aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24)); + bc = new CBCMode(aes, salt, false); + } + else if (algo.equals("AES-256-CBC")) + { + AES aes = new AES(); + aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32)); + bc = new CBCMode(aes, salt, false); + } + else + { + throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo); + } + + if ((ps.data.length % bc.getBlockSize()) != 0) + throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of " + + bc.getBlockSize()); + + /* Now decrypt the content */ + + byte[] dz = new byte[ps.data.length]; + + for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++) + { + bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize()); + } + + /* Now check and remove RFC 1423/PKCS #7 padding */ + + dz = removePadding(dz, bc.getBlockSize()); + + ps.data = dz; + ps.dekInfo = null; + ps.procType = null; + } + + public static boolean isPEMEncrypted(PEMStructure ps) throws IOException + { + if (ps.procType == null) + return false; + + if (ps.procType.length != 2) + throw new IOException("Unknown Proc-Type field."); + + if (!"4".equals(ps.procType[0])) + throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")"); + + return ("ENCRYPTED".equals(ps.procType[1])); + } + + @Deprecated + public static Object decode(char[] pem, String password) throws IOException + { + PEMStructure ps = parsePEM(pem); + + if (isPEMEncrypted(ps)) + { + if (password == null) + throw new IOException("PEM is encrypted, but no password was specified"); + + decryptPEM(ps, password.getBytes("ISO-8859-1")); + } + + if (ps.pemType == PEM_DSA_PRIVATE_KEY) + { + SimpleDERReader dr = new SimpleDERReader(ps.data); + + byte[] seq = dr.readSequenceAsByteArray(); + + if (dr.available() != 0) + throw new IOException("Padding in DSA PRIVATE KEY DER stream."); + + dr.resetInput(seq); + + BigInteger version = dr.readInt(); + + if (version.compareTo(BigInteger.ZERO) != 0) + throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream."); + + BigInteger p = dr.readInt(); + BigInteger q = dr.readInt(); + BigInteger g = dr.readInt(); + BigInteger y = dr.readInt(); + BigInteger x = dr.readInt(); + + if (dr.available() != 0) + throw new IOException("Padding in DSA PRIVATE KEY DER stream."); + + return new DSAPrivateKey(p, q, g, y, x); + } + + if (ps.pemType == PEM_RSA_PRIVATE_KEY) + { + SimpleDERReader dr = new SimpleDERReader(ps.data); + + byte[] seq = dr.readSequenceAsByteArray(); + + if (dr.available() != 0) + throw new IOException("Padding in RSA PRIVATE KEY DER stream."); + + dr.resetInput(seq); + + BigInteger version = dr.readInt(); + + if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0)) + throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream."); + + BigInteger n = dr.readInt(); + BigInteger e = dr.readInt(); + BigInteger d = dr.readInt(); + + return new RSAPrivateKey(d, e, n); + } + + throw new IOException("PEM problem: it is of unknown type"); + } + + + public static KeyPair decodeKeyPair(char[] pem, String password) throws IOException + { + + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + for (CertificateDecoder decoder : algorithm.getCertificateDecoders()) { + try { + PEMStructure ps = parsePEM(pem, decoder); + + if (isPEMEncrypted(ps)) { + if (password == null) + throw new IOException("PEM is encrypted, but no password was specified"); + + decryptPEM(ps, password.getBytes("ISO-8859-1")); + } + + return decoder.createKeyPair(ps, password); + } catch (IOException ex) { + LOGGER.log(Level.FINE, "Could not decode PEM Key using current decoder: " + decoder.getClass().getName(), ex); + // we couldn't decode the input, try another decoder + } + } + } + throw new IOException("PEM problem: it is of unknown type"); + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java b/service/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java new file mode 100644 index 0000000..942c3d3 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java @@ -0,0 +1,21 @@ + +package com.trilead.ssh2.crypto; + +/** + * Parsed PEM structure. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class PEMStructure +{ + int pemType; + String dekInfo[]; + String procType[]; + byte[] data; + + public byte[] getData() { + return data; + } +} \ No newline at end of file diff --git a/service/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java b/service/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java new file mode 100644 index 0000000..9592d63 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java @@ -0,0 +1,229 @@ +package com.trilead.ssh2.crypto; + +import java.io.IOException; + +import java.math.BigInteger; + +/** + * SimpleDERReader. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class SimpleDERReader +{ + byte[] buffer; + int pos; + int count; + + public SimpleDERReader(byte[] b) + { + resetInput(b); + } + + public SimpleDERReader(byte[] b, int off, int len) + { + resetInput(b, off, len); + } + + public void resetInput(byte[] b) + { + resetInput(b, 0, b.length); + } + + public void resetInput(byte[] b, int off, int len) + { + buffer = b; + pos = off; + count = len; + } + + private byte readByte() throws IOException + { + if (count <= 0) + throw new IOException("DER byte array: out of data"); + count--; + return buffer[pos++]; + } + + private byte[] readBytes(int len) throws IOException + { + if (len > count) + throw new IOException("DER byte array: out of data"); + + byte[] b = new byte[len]; + + System.arraycopy(buffer, pos, b, 0, len); + + pos += len; + count -= len; + + return b; + } + + public int available() + { + return count; + } + + private int readLength() throws IOException + { + int len = readByte() & 0xff; + + if ((len & 0x80) == 0) + return len; + + int remain = len & 0x7F; + + if (remain == 0 || remain > 4) + return -1; + + len = 0; + + while (remain > 0) + { + len = len << 8; + len = len | (readByte() & 0xff); + remain--; + } + + if (len < 0) + return -1; + + return len; + } + + public int ignoreNextObject() throws IOException + { + int type = readByte() & 0xff; + + int len = readLength(); + + if ((len < 0) || len > available()) + throw new IOException("Illegal len in DER object (" + len + ")"); + + readBytes(len); + + return type; + } + + public BigInteger readInt() throws IOException + { + int type = readByte() & 0xff; + + if (type != 0x02) + throw new IOException("Expected DER Integer, but found type " + type); + + int len = readLength(); + + if ((len < 0) || len > available()) + throw new IOException("Illegal len in DER object (" + len + ")"); + + byte[] b = readBytes(len); + + BigInteger bi = new BigInteger(1, b); + + return bi; + } + + public byte[] readSequenceAsByteArray() throws IOException + { + int type = readByte() & 0xff; + + if (type != 0x30) + throw new IOException("Expected DER Sequence, but found type " + type); + + int len = readLength(); + + if ((len < 0) || len > available()) + throw new IOException("Illegal len in DER object (" + len + ")"); + + byte[] b = readBytes(len); + + return b; + } + + public String readOid() throws IOException { + int type = readByte() & 0xff; + + if (type != 0x06) + throw new IOException("Expected DER OID, but found type " + type); + + int len = readLength(); + + if ((len < 1) || len > available()) + throw new IOException("Illegal len in DER object (" + len + ")"); + + byte[] b = readBytes(len); + + long value = 0; + + StringBuilder sb = new StringBuilder(64); + switch(b[0] / 40) { + case 0: + sb.append('0'); + break; + case 1: + sb.append('1'); + b[0] -= 40; + break; + default: + sb.append('2'); + b[0] -= 80; + break; + } + + for (int i = 0; i < len; i++) { + value = (value << 7) + (b[i] & 0x7F); + if ((b[i] & 0x80) == 0) { + sb.append('.'); + sb.append(value); + value = 0; + } + } + + return sb.toString(); + } + + public SimpleDERReader readConstructed() throws IOException { + int length = readLength(); + if ((length < 0) || length > available()) { + throw new IOException("Illegal length in DER object (" + length + ")"); + } + + SimpleDERReader reader = new SimpleDERReader(buffer, pos, length); + + pos += length; + count -= length; + + return reader; + } + + public int readConstructedType() throws IOException { + int type = readByte() & 0xff; + + if ((type & 0x20) != 0x20) { + throw new IOException("Expected constructed type, but was " + type); + } + + return type & 0x1f; + } + + public byte[] readOctetString() throws IOException + { + int type = readByte() & 0xff; + + if (type != 0x04 && type != 0x03) + throw new IOException("Expected DER Octetstring, but found type " + type); + + int len = readLength(); + + if ((len < 0) || len > available()) + throw new IOException("Illegal len in DER object (" + len + ")"); + + byte[] b = readBytes(len); + + return b; + } + +} \ No newline at end of file diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/AES.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/AES.java new file mode 100644 index 0000000..e89e4a6 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/AES.java @@ -0,0 +1,698 @@ + +package com.trilead.ssh2.crypto.cipher; + +/* + This file was shamelessly taken from the Bouncy Castle Crypto package. + Their licence file states the following: + + Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle + (http://www.bouncycastle.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/** + * An implementation of the AES (Rijndael), from FIPS-197. + *

+ * For further details see: http://csrc.nist.gov/encryption/aes/ + * . + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper + * and C code at http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * + * There are three levels of tradeoff of speed vs memory Because java has no + * preprocessor, they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 + * 256 word tables for encryption and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each, for a + * total of 2Kbytes, adding 12 rotate operations per round to compute the values + * contained in the other tables from the contents of the first + * + * The slowest version uses no static tables at all and computes the values in + * each round + *

+ * This file contains the fast version with 8Kbytes of static tables for round + * precomputation + * + * @author See comments in the source file + * @version $Id: AES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class AES implements BlockCipher +{ + // The S box + private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107, + (byte) 111, (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171, + (byte) 118, (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240, + (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183, + (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165, + (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35, + (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226, + (byte) 235, (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27, + (byte) 110, (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227, + (byte) 47, (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177, + (byte) 91, (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207, + (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69, + (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163, + (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218, + (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236, + (byte) 95, (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100, + (byte) 93, (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42, + (byte) 144, (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11, + (byte) 219, (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92, + (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231, + (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86, + (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37, + (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31, + (byte) 75, (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72, + (byte) 3, (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193, + (byte) 29, (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142, + (byte) 148, (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223, + (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65, + (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, }; + + // The inverse S-box + private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165, + (byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251, + (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52, + (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123, + (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149, + (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102, + (byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109, + (byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104, + (byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182, + (byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218, + (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144, + (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228, + (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30, + (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3, + (byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79, + (byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180, + (byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53, + (byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110, + (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111, + (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86, + (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192, + (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51, + (byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39, + (byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181, + (byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156, + (byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176, + (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23, + (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105, + (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125, }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, + 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + // precomputation tables of calculations for rounds + private static final int[] T0 = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, + 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, + 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, + 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, + 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346, + 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, + 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, + 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, + 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, + 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, + 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, + 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, + 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, + 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, + 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, + 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, + 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, + 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, + 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, + 0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, + 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, + 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, + 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c }; + + private static final int[] T1 = { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, + 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, + 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, + 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, + 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, + 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, 0x23234665, + 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, + 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, + 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, + 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, + 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, + 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, + 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, + 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, + 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, + 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, + 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, + 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, + 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, + 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, + 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, + 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, + 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, + 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, + 0x03030605, 0xf6f6f701, 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, + 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, + 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, + 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a }; + + private static final int[] T2 = { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, + 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, + 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, + 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, + 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, + 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, 0x23466523, + 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, + 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, + 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, + 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, + 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, + 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, + 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, + 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, + 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, + 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, + 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, + 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, + 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, + 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, + 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, + 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, + 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, + 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, + 0x03060503, 0xf6f701f6, 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, + 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, + 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, + 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16 }; + + private static final int[] T3 = { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, + 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, + 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, + 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, + 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, + 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, 0x46652323, + 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, + 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, + 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, + 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, + 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, + 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, + 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, + 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, + 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, + 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, + 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, + 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, + 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, + 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, + 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, + 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, + 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, + 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, + 0x06050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, + 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e, + 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, + 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616 }; + + private static final int[] Tinv0 = { 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, + 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, + 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, + 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, + 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, + 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, + 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, + 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, + 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, + 0x069f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x055dc471, 0x6fd40604, + 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, + 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, + 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, + 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, + 0x1d171b12, 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, + 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, + 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, + 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, + 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, + 0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, + 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, 0xf418596e, + 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, + 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, + 0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, + 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, + 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, + 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, + 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, + 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0 }; + + private static final int[] Tinv1 = { 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, + 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, + 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, + 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, + 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, + 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, + 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, + 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, + 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, + 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, + 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, + 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, + 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, + 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, + 0x171b121d, 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd, + 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, + 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, + 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, + 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, + 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, + 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, + 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4, + 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, + 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, 0xb5d19ee3, + 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, + 0x5610e933, 0x47d66d13, 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, + 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, + 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, + 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042 }; + + private static final int[] Tinv2 = { 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, + 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, + 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, + 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, + 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, + 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, 0xde94876c, + 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, + 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, + 0x0506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, + 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x06046fd4, + 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, + 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff, + 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, + 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, + 0x1b121d17, 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, + 0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, + 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, + 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, + 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, + 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, + 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, + 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, + 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, + 0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, + 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, 0x67b35a1d, 0xdb9252d2, + 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, + 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, + 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, + 0x0c08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257 }; + + private static final int[] Tinv3 = { 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, + 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, + 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, + 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, + 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, + 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, + 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, + 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, + 0x06d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60, + 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x046fd406, + 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8, + 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, + 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, + 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, + 0x121d171b, 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, + 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, + 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, + 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, + 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, + 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, + 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, + 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, + 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, + 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, + 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, + 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, + 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, + 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, + 0x08deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8 }; + + private final int shift(int r, int shift) + { + return (((r >>> shift) | (r << (32 - shift)))); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private static final int m1 = 0x80808080; + private static final int m2 = 0x7f7f7f7f; + private static final int m3 = 0x0000001b; + + private final int FFmulX(int x) + { + return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3)); + } + + /* + * The following defines provide alternative definitions of FFmulX that + * might give improved performance if a fast 32-bit multiply is not + * available. + * + * private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & + * m2) < < 1) ^ ((u >>> 3) | (u >>> 6)); } private static final int m4 = + * 0x1b1b1b1b; private int FFmulX(int x) { int u = x & m1; return ((x & m2) < < + * 1) ^ ((u - (u >>> 7)) & m4); } + * + */ + + private final int inv_mcol(int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24); + } + + private final int subWord(int x) + { + return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24); + } + + /** + * Calculate the necessary round keys The number of calculations depends on + * key size and block size AES specified a fixed block size of 128 bits and + * key sizes 128/192/256 bits This code is written assuming those are the + * only possible values + */ + private final int[][] generateWorkingKey(byte[] key, boolean forEncryption) + { + int KC = key.length / 4; // key length in words + int t; + + if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) + { + throw new IllegalArgumentException("Key length not 128/192/256 bits."); + } + + ROUNDS = KC + 6; // This is not always true for the generalized + // Rijndael that allows larger block sizes + int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.length; t++) + { + W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16) + | (key[i + 3] << 24); + i += 4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + int temp = W[(i - 1) >> 2][(i - 1) & 3]; + if ((i % KC) == 0) + { + temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1]; + } + else if ((KC > 6) && ((i % KC) == 4)) + { + temp = subWord(temp); + } + + W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp; + } + + if (!forEncryption) + { + for (int j = 1; j < ROUNDS; j++) + { + for (int i = 0; i < 4; i++) + { + W[j][i] = inv_mcol(W[j][i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[][] WorkingKey = null; + private int C0, C1, C2, C3; + private boolean doEncrypt; + + private static final int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AES() + { + } + + /** + * initialise an AES cipher. + * + * @param forEncryption + * whether or not we are for encryption. + * @param key + * the key required to set up the cipher. + * @exception IllegalArgumentException + * if the params argument is inappropriate. + */ + + public final void init(boolean forEncryption, byte[] key) + { + WorkingKey = generateWorkingKey(key, forEncryption); + this.doEncrypt = forEncryption; + } + + public final String getAlgorithmName() + { + return "AES"; + } + + public final int getBlockSize() + { + return BLOCK_SIZE; + } + + public final int processBlock(byte[] in, int inOff, byte[] out, int outOff) + { + if (WorkingKey == null) + { + throw new IllegalStateException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > in.length) + { + throw new IllegalArgumentException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > out.length) + { + throw new IllegalArgumentException("output buffer too short"); + } + + if (doEncrypt) + { + unpackBlock(in, inOff); + encryptBlock(WorkingKey); + packBlock(out, outOff); + } + else + { + unpackBlock(in, inOff); + decryptBlock(WorkingKey); + packBlock(out, outOff); + } + + return BLOCK_SIZE; + } + + public final void reset() + { + } + + private final void unpackBlock(byte[] bytes, int off) + { + int index = off; + + C0 = (bytes[index++] & 0xff); + C0 |= (bytes[index++] & 0xff) << 8; + C0 |= (bytes[index++] & 0xff) << 16; + C0 |= bytes[index++] << 24; + + C1 = (bytes[index++] & 0xff); + C1 |= (bytes[index++] & 0xff) << 8; + C1 |= (bytes[index++] & 0xff) << 16; + C1 |= bytes[index++] << 24; + + C2 = (bytes[index++] & 0xff); + C2 |= (bytes[index++] & 0xff) << 8; + C2 |= (bytes[index++] & 0xff) << 16; + C2 |= bytes[index++] << 24; + + C3 = (bytes[index++] & 0xff); + C3 |= (bytes[index++] & 0xff) << 8; + C3 |= (bytes[index++] & 0xff) << 16; + C3 |= bytes[index++] << 24; + } + + private final void packBlock(byte[] bytes, int off) + { + int index = off; + + bytes[index++] = (byte) C0; + bytes[index++] = (byte) (C0 >> 8); + bytes[index++] = (byte) (C0 >> 16); + bytes[index++] = (byte) (C0 >> 24); + + bytes[index++] = (byte) C1; + bytes[index++] = (byte) (C1 >> 8); + bytes[index++] = (byte) (C1 >> 16); + bytes[index++] = (byte) (C1 >> 24); + + bytes[index++] = (byte) C2; + bytes[index++] = (byte) (C2 >> 8); + bytes[index++] = (byte) (C2 >> 16); + bytes[index++] = (byte) (C2 >> 24); + + bytes[index++] = (byte) C3; + bytes[index++] = (byte) (C3 >> 8); + bytes[index++] = (byte) (C3 >> 16); + bytes[index++] = (byte) (C3 >> 24); + } + + private final void encryptBlock(int[][] KW) + { + int r, r0, r1, r2, r3; + + C0 ^= KW[0][0]; + C1 ^= KW[0][1]; + C2 ^= KW[0][2]; + C3 ^= KW[0][3]; + + for (r = 1; r < ROUNDS - 1;) + { + r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0]; + r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1]; + r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2]; + r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3]; + C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0]; + C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1]; + C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2]; + C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3]; + } + + r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0]; + r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1]; + r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2]; + r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3]; + + // the final round's table is a simple function of S so we don't use a + // whole other four tables for it + + C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16) + ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0]; + C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) + ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1]; + C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16) + ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2]; + C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16) + ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3]; + + } + + private final void decryptBlock(int[][] KW) + { + int r, r0, r1, r2, r3; + + C0 ^= KW[ROUNDS][0]; + C1 ^= KW[ROUNDS][1]; + C2 ^= KW[ROUNDS][2]; + C3 ^= KW[ROUNDS][3]; + + for (r = ROUNDS - 1; r > 1;) + { + r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] + ^ KW[r][0]; + r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] + ^ KW[r][1]; + r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] + ^ KW[r][2]; + r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] + ^ KW[r--][3]; + C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255] + ^ KW[r][0]; + C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255] + ^ KW[r][1]; + C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255] + ^ KW[r][2]; + C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255] + ^ KW[r--][3]; + } + + r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0]; + r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1]; + r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2]; + r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3]; + + // the final round's table is a simple function of Si so we don't use a + // whole other four tables for it + + C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16) + ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0]; + C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16) + ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1]; + C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16) + ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2]; + C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16) + ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3]; + } + + public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) + { + processBlock(src, srcoff, dst, dstoff); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipher.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipher.java new file mode 100644 index 0000000..4cc28ab --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipher.java @@ -0,0 +1,16 @@ +package com.trilead.ssh2.crypto.cipher; + +/** + * BlockCipher. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: BlockCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public interface BlockCipher +{ + public void init(boolean forEncryption, byte[] key); + + public int getBlockSize(); + + public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff); +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java new file mode 100644 index 0000000..84bb38a --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java @@ -0,0 +1,115 @@ + +package com.trilead.ssh2.crypto.cipher; + +import java.util.Vector; + +/** + * BlockCipherFactory. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: BlockCipherFactory.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class BlockCipherFactory +{ + static class CipherEntry + { + String type; + int blocksize; + int keysize; + String cipherClass; + + public CipherEntry(String type, int blockSize, int keySize, String cipherClass) + { + this.type = type; + this.blocksize = blockSize; + this.keysize = keySize; + this.cipherClass = cipherClass; + } + } + + static Vector ciphers = new Vector(); + + static + { + /* Higher Priority First */ + + ciphers.addElement(new CipherEntry("aes256-ctr", 16, 32, "com.trilead.ssh2.crypto.cipher.AES")); + ciphers.addElement(new CipherEntry("aes192-ctr", 16, 24, "com.trilead.ssh2.crypto.cipher.AES")); + ciphers.addElement(new CipherEntry("aes128-ctr", 16, 16, "com.trilead.ssh2.crypto.cipher.AES")); + ciphers.addElement(new CipherEntry("blowfish-ctr", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish")); + + ciphers.addElement(new CipherEntry("aes256-cbc", 16, 32, "com.trilead.ssh2.crypto.cipher.AES")); + ciphers.addElement(new CipherEntry("aes192-cbc", 16, 24, "com.trilead.ssh2.crypto.cipher.AES")); + ciphers.addElement(new CipherEntry("aes128-cbc", 16, 16, "com.trilead.ssh2.crypto.cipher.AES")); + ciphers.addElement(new CipherEntry("blowfish-cbc", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish")); + + ciphers.addElement(new CipherEntry("3des-ctr", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede")); + ciphers.addElement(new CipherEntry("3des-cbc", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede")); + } + + public static String[] getDefaultCipherList() + { + String list[] = new String[ciphers.size()]; + for (int i = 0; i < ciphers.size(); i++) + { + CipherEntry ce = (CipherEntry) ciphers.elementAt(i); + list[i] = new String(ce.type); + } + return list; + } + + public static void checkCipherList(String[] cipherCandidates) + { + for (int i = 0; i < cipherCandidates.length; i++) + getEntry(cipherCandidates[i]); + } + + public static BlockCipher createCipher(String type, boolean encrypt, byte[] key, byte[] iv) + { + try + { + CipherEntry ce = getEntry(type); + Class cc = Class.forName(ce.cipherClass); + BlockCipher bc = (BlockCipher) cc.newInstance(); + + if (type.endsWith("-cbc")) + { + bc.init(encrypt, key); + return new CBCMode(bc, iv, encrypt); + } + else if (type.endsWith("-ctr")) + { + bc.init(true, key); + return new CTRMode(bc, iv, encrypt); + } + throw new IllegalArgumentException("Cannot instantiate " + type); + } + catch (Exception e) + { + throw new IllegalArgumentException("Cannot instantiate " + type); + } + } + + private static CipherEntry getEntry(String type) + { + for (int i = 0; i < ciphers.size(); i++) + { + CipherEntry ce = (CipherEntry) ciphers.elementAt(i); + if (ce.type.equals(type)) + return ce; + } + throw new IllegalArgumentException("Unkown algorithm " + type); + } + + public static int getBlockSize(String type) + { + CipherEntry ce = getEntry(type); + return ce.blocksize; + } + + public static int getKeySize(String type) + { + CipherEntry ce = getEntry(type); + return ce.keysize; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlowFish.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlowFish.java new file mode 100644 index 0000000..0b2f295 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/BlowFish.java @@ -0,0 +1,403 @@ + +package com.trilead.ssh2.crypto.cipher; + +/* + This file was shamelessly taken from the Bouncy Castle Crypto package. + Their licence file states the following: + + Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle + (http://www.bouncycastle.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/** + * A class that provides Blowfish key encryption operations, such as encoding + * data and generating keys. All the algorithms herein are from Applied + * Cryptography and implement a simplified cryptography interface. + * + * @author See comments in the source file + * @version $Id: BlowFish.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class BlowFish implements BlockCipher +{ + + private final static int[] KP = { 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, + 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, + 0xB5470917, 0x9216D5D9, 0x8979FB1B }, + + KS0 = { 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947, + 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, + 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, + 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, + 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, + 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, + 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, + 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, + 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2, + 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, + 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, + 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, + 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, + 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, + 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, + 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, + 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB, + 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8, + 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC, + 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, + 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, + 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, + 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, + 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, + 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B, + 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9, + 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, + 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, + 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A }, + + KS1 = { 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, + 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, + 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6, + 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, + 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, + 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, + 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, + 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, + 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718, + 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, + 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6, + 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, + 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, + 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, + 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, + 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, + 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90, + 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, + 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E, + 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, + 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, + 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, + 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, + 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, + 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092, + 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, + 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705, + 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, + 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 }, + + KS2 = { 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, + 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF, + 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, + 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, + 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, + 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, + 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, + 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, + 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, + 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60, + 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, + 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, + 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, + 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, + 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, + 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, + 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, + 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086, + 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, + 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, + 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, + 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, + 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, + 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, + 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, + 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188, + 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, + 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, + 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 }, + + KS3 = { 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D, + 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79, + 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, + 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, + 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, + 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, + 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, + 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, + 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15, + 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5, + 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862, + 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, + 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, + 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, + 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, + 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, + 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1, + 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A, + 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, + 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, + 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, + 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, + 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, + 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, + 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, + 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3, + 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, + 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, + 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 }; + + // ==================================== + // Useful constants + // ==================================== + + private static final int ROUNDS = 16; + private static final int BLOCK_SIZE = 8; // bytes = 64 bits + private static final int SBOX_SK = 256; + private static final int P_SZ = ROUNDS + 2; + + private final int[] S0, S1, S2, S3; // the s-boxes + private final int[] P; // the p-array + + private boolean doEncrypt = false; + + private byte[] workingKey = null; + + public BlowFish() + { + S0 = new int[SBOX_SK]; + S1 = new int[SBOX_SK]; + S2 = new int[SBOX_SK]; + S3 = new int[SBOX_SK]; + P = new int[P_SZ]; + } + + /** + * initialise a Blowfish cipher. + * + * @param encrypting + * whether or not we are for encryption. + * @param key + * the key required to set up the cipher. + * @exception IllegalArgumentException + * if the params argument is inappropriate. + */ + public void init(boolean encrypting, byte[] key) + { + this.doEncrypt = encrypting; + this.workingKey = key; + setKey(this.workingKey); + } + + public String getAlgorithmName() + { + return "Blowfish"; + } + + public final void transformBlock(byte[] in, int inOff, byte[] out, int outOff) + { + if (workingKey == null) + { + throw new IllegalStateException("Blowfish not initialised"); + } + + if (doEncrypt) + { + encryptBlock(in, inOff, out, outOff); + } + else + { + decryptBlock(in, inOff, out, outOff); + } + } + + public void reset() + { + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + // ================================== + // Private Implementation + // ================================== + + private int F(int x) + { + return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff]) ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]); + } + + /** + * apply the encryption cycle to each value pair in the table. + */ + private void processTable(int xl, int xr, int[] table) + { + int size = table.length; + + for (int s = 0; s < size; s += 2) + { + xl ^= P[0]; + + for (int i = 1; i < ROUNDS; i += 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i + 1]; + } + + xr ^= P[ROUNDS + 1]; + + table[s] = xr; + table[s + 1] = xl; + + xr = xl; // end of cycle swap + xl = table[s]; + } + } + + private void setKey(byte[] key) + { + /* + * - comments are from _Applied Crypto_, Schneier, p338 please be + * careful comparing the two, AC numbers the arrays from 1, the enclosed + * code from 0. + * + * (1) Initialise the S-boxes and the P-array, with a fixed string This + * string contains the hexadecimal digits of pi (3.141...) + */ + System.arraycopy(KS0, 0, S0, 0, SBOX_SK); + System.arraycopy(KS1, 0, S1, 0, SBOX_SK); + System.arraycopy(KS2, 0, S2, 0, SBOX_SK); + System.arraycopy(KS3, 0, S3, 0, SBOX_SK); + + System.arraycopy(KP, 0, P, 0, P_SZ); + + /* + * (2) Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with + * the second 32-bits of the key, and so on for all bits of the key (up + * to P[17]). Repeatedly cycle through the key bits until the entire + * P-array has been XOR-ed with the key bits + */ + int keyLength = key.length; + int keyIndex = 0; + + for (int i = 0; i < P_SZ; i++) + { + // get the 32 bits of the key, in 4 * 8 bit chunks + int data = 0x0000000; + for (int j = 0; j < 4; j++) + { + // create a 32 bit block + data = (data << 8) | (key[keyIndex++] & 0xff); + + // wrap when we get to the end of the key + if (keyIndex >= keyLength) + { + keyIndex = 0; + } + } + // XOR the newly created 32 bit chunk onto the P-array + P[i] ^= data; + } + + /* + * (3) Encrypt the all-zero string with the Blowfish algorithm, using + * the subkeys described in (1) and (2) + * + * (4) Replace P1 and P2 with the output of step (3) + * + * (5) Encrypt the output of step(3) using the Blowfish algorithm, with + * the modified subkeys. + * + * (6) Replace P3 and P4 with the output of step (5) + * + * (7) Continue the process, replacing all elements of the P-array and + * then all four S-boxes in order, with the output of the continuously + * changing Blowfish algorithm + */ + + processTable(0, 0, P); + processTable(P[P_SZ - 2], P[P_SZ - 1], S0); + processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1); + processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2); + processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3); + } + + /** + * Encrypt the given input starting at the given offset and place the result + * in the provided buffer starting at the given offset. The input will be an + * exact multiple of our blocksize. + */ + private void encryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) + { + int xl = BytesTo32bits(src, srcIndex); + int xr = BytesTo32bits(src, srcIndex + 4); + + xl ^= P[0]; + + for (int i = 1; i < ROUNDS; i += 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i + 1]; + } + + xr ^= P[ROUNDS + 1]; + + Bits32ToBytes(xr, dst, dstIndex); + Bits32ToBytes(xl, dst, dstIndex + 4); + } + + /** + * Decrypt the given input starting at the given offset and place the result + * in the provided buffer starting at the given offset. The input will be an + * exact multiple of our blocksize. + */ + private void decryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) + { + int xl = BytesTo32bits(src, srcIndex); + int xr = BytesTo32bits(src, srcIndex + 4); + + xl ^= P[ROUNDS + 1]; + + for (int i = ROUNDS; i > 0; i -= 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i - 1]; + } + + xr ^= P[0]; + + Bits32ToBytes(xr, dst, dstIndex); + Bits32ToBytes(xl, dst, dstIndex + 4); + } + + private int BytesTo32bits(byte[] b, int i) + { + return ((b[i] & 0xff) << 24) | ((b[i + 1] & 0xff) << 16) | ((b[i + 2] & 0xff) << 8) | ((b[i + 3] & 0xff)); + } + + private void Bits32ToBytes(int in, byte[] b, int offset) + { + b[offset + 3] = (byte) in; + b[offset + 2] = (byte) (in >> 8); + b[offset + 1] = (byte) (in >> 16); + b[offset] = (byte) (in >> 24); + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/CBCMode.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CBCMode.java new file mode 100644 index 0000000..0ae51b3 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CBCMode.java @@ -0,0 +1,78 @@ +package com.trilead.ssh2.crypto.cipher; + +/** + * CBCMode. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: CBCMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class CBCMode implements BlockCipher +{ + BlockCipher tc; + int blockSize; + boolean doEncrypt; + + byte[] cbc_vector; + byte[] tmp_vector; + + public void init(boolean forEncryption, byte[] key) + { + } + + public CBCMode(BlockCipher tc, byte[] iv, boolean doEncrypt) + throws IllegalArgumentException + { + this.tc = tc; + this.blockSize = tc.getBlockSize(); + this.doEncrypt = doEncrypt; + + if (this.blockSize != iv.length) + throw new IllegalArgumentException("IV must be " + blockSize + + " bytes long! (currently " + iv.length + ")"); + + this.cbc_vector = new byte[blockSize]; + this.tmp_vector = new byte[blockSize]; + System.arraycopy(iv, 0, cbc_vector, 0, blockSize); + } + + public int getBlockSize() + { + return blockSize; + } + + private void encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff) + { + for (int i = 0; i < blockSize; i++) + cbc_vector[i] ^= src[srcoff + i]; + + tc.transformBlock(cbc_vector, 0, dst, dstoff); + + System.arraycopy(dst, dstoff, cbc_vector, 0, blockSize); + } + + private void decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff) + { + /* Assume the worst, src and dst are overlapping... */ + + System.arraycopy(src, srcoff, tmp_vector, 0, blockSize); + + tc.transformBlock(src, srcoff, dst, dstoff); + + for (int i = 0; i < blockSize; i++) + dst[dstoff + i] ^= cbc_vector[i]; + + /* ...that is why we need a tmp buffer. */ + + byte[] swap = cbc_vector; + cbc_vector = tmp_vector; + tmp_vector = swap; + } + + public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) + { + if (doEncrypt) + encryptBlock(src, srcoff, dst, dstoff); + else + decryptBlock(src, srcoff, dst, dstoff); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/CTRMode.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CTRMode.java new file mode 100644 index 0000000..8541c8d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CTRMode.java @@ -0,0 +1,62 @@ + +package com.trilead.ssh2.crypto.cipher; + +/** + * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: CTRMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class CTRMode implements BlockCipher +{ + byte[] X; + byte[] Xenc; + + BlockCipher bc; + int blockSize; + boolean doEncrypt; + + int count = 0; + + public void init(boolean forEncryption, byte[] key) + { + } + + public CTRMode(BlockCipher tc, byte[] iv, boolean doEnc) throws IllegalArgumentException + { + bc = tc; + blockSize = bc.getBlockSize(); + doEncrypt = doEnc; + + if (blockSize != iv.length) + throw new IllegalArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.length + ")"); + + X = new byte[blockSize]; + Xenc = new byte[blockSize]; + + System.arraycopy(iv, 0, X, 0, blockSize); + } + + public final int getBlockSize() + { + return blockSize; + } + + public final void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) + { + bc.transformBlock(X, 0, Xenc, 0); + + for (int i = 0; i < blockSize; i++) + { + dst[dstoff + i] = (byte) (src[srcoff + i] ^ Xenc[i]); + } + + for (int i = (blockSize - 1); i >= 0; i--) + { + X[i]++; + if (X[i] != 0) + break; + + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/CipherInputStream.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CipherInputStream.java new file mode 100644 index 0000000..c9055ab --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CipherInputStream.java @@ -0,0 +1,144 @@ + +package com.trilead.ssh2.crypto.cipher; + +import java.io.IOException; +import java.io.InputStream; + +/** + * CipherInputStream. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: CipherInputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class CipherInputStream +{ + BlockCipher currentCipher; + InputStream bi; + byte[] buffer; + byte[] enc; + int blockSize; + int pos; + + /* + * We cannot use java.io.BufferedInputStream, since that is not available in + * J2ME. Everything could be improved alot here. + */ + + final int BUFF_SIZE = 2048; + byte[] input_buffer = new byte[BUFF_SIZE]; + int input_buffer_pos = 0; + int input_buffer_size = 0; + + public CipherInputStream(BlockCipher tc, InputStream bi) + { + this.bi = bi; + changeCipher(tc); + } + + private int fill_buffer() throws IOException + { + input_buffer_pos = 0; + input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE); + return input_buffer_size; + } + + private int internal_read(byte[] b, int off, int len) throws IOException + { + if (input_buffer_size < 0) + return -1; + + if (input_buffer_pos >= input_buffer_size) + { + if (fill_buffer() <= 0) + return -1; + } + + int avail = input_buffer_size - input_buffer_pos; + int thiscopy = (len > avail) ? avail : len; + + System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy); + input_buffer_pos += thiscopy; + + return thiscopy; + } + + public void changeCipher(BlockCipher bc) + { + this.currentCipher = bc; + blockSize = bc.getBlockSize(); + buffer = new byte[blockSize]; + enc = new byte[blockSize]; + pos = blockSize; + } + + private void getBlock() throws IOException + { + int n = 0; + while (n < blockSize) + { + int len = internal_read(enc, n, blockSize - n); + if (len < 0) + throw new IOException("Cannot read full block, EOF reached."); + n += len; + } + + try + { + currentCipher.transformBlock(enc, 0, buffer, 0); + } + catch (Exception e) + { + throw new IOException("Error while decrypting block."); + } + pos = 0; + } + + public int read(byte[] dst) throws IOException + { + return read(dst, 0, dst.length); + } + + public int read(byte[] dst, int off, int len) throws IOException + { + int count = 0; + + while (len > 0) + { + if (pos >= blockSize) + getBlock(); + + int avail = blockSize - pos; + int copy = Math.min(avail, len); + System.arraycopy(buffer, pos, dst, off, copy); + pos += copy; + off += copy; + len -= copy; + count += copy; + } + return count; + } + + public int read() throws IOException + { + if (pos >= blockSize) + { + getBlock(); + } + return buffer[pos++] & 0xff; + } + + public int readPlain(byte[] b, int off, int len) throws IOException + { + if (pos != blockSize) + throw new IOException("Cannot read plain since crypto buffer is not aligned."); + int n = 0; + while (n < len) + { + int cnt = internal_read(b, off + n, len - n); + if (cnt < 0) + throw new IOException("Cannot fill buffer, EOF reached."); + n += cnt; + } + return n; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java new file mode 100644 index 0000000..cf0db4a --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java @@ -0,0 +1,142 @@ + +package com.trilead.ssh2.crypto.cipher; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * CipherOutputStream. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: CipherOutputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class CipherOutputStream +{ + BlockCipher currentCipher; + OutputStream bo; + byte[] buffer; + byte[] enc; + int blockSize; + int pos; + + /* + * We cannot use java.io.BufferedOutputStream, since that is not available + * in J2ME. Everything could be improved here alot. + */ + + final int BUFF_SIZE = 2048; + byte[] out_buffer = new byte[BUFF_SIZE]; + int out_buffer_pos = 0; + + public CipherOutputStream(BlockCipher tc, OutputStream bo) + { + this.bo = bo; + changeCipher(tc); + } + + private void internal_write(byte[] src, int off, int len) throws IOException + { + while (len > 0) + { + int space = BUFF_SIZE - out_buffer_pos; + int copy = (len > space) ? space : len; + + System.arraycopy(src, off, out_buffer, out_buffer_pos, copy); + + off += copy; + out_buffer_pos += copy; + len -= copy; + + if (out_buffer_pos >= BUFF_SIZE) + { + bo.write(out_buffer, 0, BUFF_SIZE); + out_buffer_pos = 0; + } + } + } + + private void internal_write(int b) throws IOException + { + out_buffer[out_buffer_pos++] = (byte) b; + if (out_buffer_pos >= BUFF_SIZE) + { + bo.write(out_buffer, 0, BUFF_SIZE); + out_buffer_pos = 0; + } + } + + public void flush() throws IOException + { + if (pos != 0) + throw new IOException("FATAL: cannot flush since crypto buffer is not aligned."); + + if (out_buffer_pos > 0) + { + bo.write(out_buffer, 0, out_buffer_pos); + out_buffer_pos = 0; + } + bo.flush(); + } + + public void changeCipher(BlockCipher bc) + { + this.currentCipher = bc; + blockSize = bc.getBlockSize(); + buffer = new byte[blockSize]; + enc = new byte[blockSize]; + pos = 0; + } + + private void writeBlock() throws IOException + { + try + { + currentCipher.transformBlock(buffer, 0, enc, 0); + } + catch (Exception e) + { + throw (IOException) new IOException("Error while decrypting block.").initCause(e); + } + + internal_write(enc, 0, blockSize); + pos = 0; + } + + public void write(byte[] src, int off, int len) throws IOException + { + while (len > 0) + { + int avail = blockSize - pos; + int copy = Math.min(avail, len); + + System.arraycopy(src, off, buffer, pos, copy); + pos += copy; + off += copy; + len -= copy; + + if (pos >= blockSize) + writeBlock(); + } + } + + public void write(int b) throws IOException + { + buffer[pos++] = (byte) b; + if (pos >= blockSize) + writeBlock(); + } + + public void writePlain(int b) throws IOException + { + if (pos != 0) + throw new IOException("Cannot write plain since crypto buffer is not aligned."); + internal_write(b); + } + + public void writePlain(byte[] b, int off, int len) throws IOException + { + if (pos != 0) + throw new IOException("Cannot write plain since crypto buffer is not aligned."); + internal_write(b, off, len); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/DES.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/DES.java new file mode 100644 index 0000000..0d779a0 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/DES.java @@ -0,0 +1,384 @@ + +package com.trilead.ssh2.crypto.cipher; + +/* + This file is based on the 3DES implementation from the Bouncy Castle Crypto package. + Their licence file states the following: + + Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle + (http://www.bouncycastle.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/** + * DES. + * + * @author See comments in the source file + * @version $Id: DES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class DES implements BlockCipher +{ + private int[] workingKey = null; + + /** + * standard constructor. + */ + public DES() + { + } + + /** + * initialise a DES cipher. + * + * @param encrypting + * whether or not we are for encryption. + * @param key + * the parameters required to set up the cipher. + * @exception IllegalArgumentException + * if the params argument is inappropriate. + */ + public void init(boolean encrypting, byte[] key) + { + this.workingKey = generateWorkingKey(encrypting, key, 0); + } + + public String getAlgorithmName() + { + return "DES"; + } + + public int getBlockSize() + { + return 8; + } + + public void transformBlock(byte[] in, int inOff, byte[] out, int outOff) + { + if (workingKey == null) + { + throw new IllegalStateException("DES engine not initialised!"); + } + + desFunc(workingKey, in, inOff, out, outOff); + } + + public void reset() + { + } + + /** + * what follows is mainly taken from "Applied Cryptography", by Bruce + * Schneier, however it also bears great resemblance to Richard + * Outerbridge's D3DES... + */ + + static short[] Df_Key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, + 0x10, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67 }; + + static short[] bytebit = { 0200, 0100, 040, 020, 010, 04, 02, 01 }; + + static int[] bigbyte = { 0x800000, 0x400000, 0x200000, 0x100000, 0x80000, 0x40000, 0x20000, 0x10000, 0x8000, + 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; + + /* + * Use the key schedule specified in the Standard (ANSI X3.92-1981). + */ + + static byte[] pc1 = { 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, + 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, + 4, 27, 19, 11, 3 }; + + static byte[] totrot = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 }; + + static byte[] pc2 = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, + 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + + static int[] SP1 = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, + 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004, + 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404, + 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400, + 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, + 0x00010400, 0x00000000, 0x01010004 }; + + static int[] SP2 = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, + 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020, + 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020, + 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020, + 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, + 0x80100020, 0x80108020, 0x00108000 }; + + static int[] SP3 = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, + 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208, + 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000, + 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208, + 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, + 0x00000008, 0x08020008, 0x00020200 }; + + static int[] SP4 = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, + 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081, + 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001, + 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081, + 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, + 0x00800000, 0x00002000, 0x00802080 }; + + static int[] SP5 = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, + 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000, + 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100, + 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000, + 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, + 0x40080000, 0x02080100, 0x40000100 }; + + static int[] SP6 = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, + 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010, + 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010, + 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000, + 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, + 0x20000000, 0x00400010, 0x20004010 }; + + static int[] SP7 = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, + 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000, + 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800, + 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800, + 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, + 0x04000800, 0x00000800, 0x00200002 }; + + static int[] SP8 = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, + 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040, + 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000, + 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040, + 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, + 0x00040040, 0x10000000, 0x10041000 }; + + /** + * generate an integer based working key based on our secret key and what we + * processing we are planning to do. + *

+ * Acknowledgements for this routine go to James Gillogly & Phil Karn. + * (whoever, and wherever they are!). + * + * @param encrypting the encrypting + * @param key the key + * @param off the off + * @return the int [ ] + */ + protected int[] generateWorkingKey(boolean encrypting, byte[] key, int off) + { + int[] newKey = new int[32]; + boolean[] pc1m = new boolean[56], pcr = new boolean[56]; + + for (int j = 0; j < 56; j++) + { + int l = pc1[j]; + + pc1m[j] = ((key[off + (l >>> 3)] & bytebit[l & 07]) != 0); + } + + for (int i = 0; i < 16; i++) + { + int l, m, n; + + if (encrypting) + { + m = i << 1; + } + else + { + m = (15 - i) << 1; + } + + n = m + 1; + newKey[m] = newKey[n] = 0; + + for (int j = 0; j < 28; j++) + { + l = j + totrot[i]; + if (l < 28) + { + pcr[j] = pc1m[l]; + } + else + { + pcr[j] = pc1m[l - 28]; + } + } + + for (int j = 28; j < 56; j++) + { + l = j + totrot[i]; + if (l < 56) + { + pcr[j] = pc1m[l]; + } + else + { + pcr[j] = pc1m[l - 28]; + } + } + + for (int j = 0; j < 24; j++) + { + if (pcr[pc2[j]]) + { + newKey[m] |= bigbyte[j]; + } + + if (pcr[pc2[j + 24]]) + { + newKey[n] |= bigbyte[j]; + } + } + } + + // + // store the processed key + // + for (int i = 0; i != 32; i += 2) + { + int i1, i2; + + i1 = newKey[i]; + i2 = newKey[i + 1]; + + newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10) | ((i2 & 0x00fc0000) >>> 10) + | ((i2 & 0x00000fc0) >>> 6); + + newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16) | ((i2 & 0x0003f000) >>> 4) + | (i2 & 0x0000003f); + } + + return newKey; + } + + /** + * the DES engine. + * + * @param wKey the w key + * @param in the in + * @param inOff the in off + * @param out the out + * @param outOff the out off + */ + protected void desFunc(int[] wKey, byte[] in, int inOff, byte[] out, int outOff) + { + int work, right, left; + + left = (in[inOff + 0] & 0xff) << 24; + left |= (in[inOff + 1] & 0xff) << 16; + left |= (in[inOff + 2] & 0xff) << 8; + left |= (in[inOff + 3] & 0xff); + + right = (in[inOff + 4] & 0xff) << 24; + right |= (in[inOff + 5] & 0xff) << 16; + right |= (in[inOff + 6] & 0xff) << 8; + right |= (in[inOff + 7] & 0xff); + + work = ((left >>> 4) ^ right) & 0x0f0f0f0f; + right ^= work; + left ^= (work << 4); + work = ((left >>> 16) ^ right) & 0x0000ffff; + right ^= work; + left ^= (work << 16); + work = ((right >>> 2) ^ left) & 0x33333333; + left ^= work; + right ^= (work << 2); + work = ((right >>> 8) ^ left) & 0x00ff00ff; + left ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff; + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff; + + for (int round = 0; round < 8; round++) + { + int fval; + + work = (right << 28) | (right >>> 4); + work ^= wKey[round * 4 + 0]; + fval = SP7[work & 0x3f]; + fval |= SP5[(work >>> 8) & 0x3f]; + fval |= SP3[(work >>> 16) & 0x3f]; + fval |= SP1[(work >>> 24) & 0x3f]; + work = right ^ wKey[round * 4 + 1]; + fval |= SP8[work & 0x3f]; + fval |= SP6[(work >>> 8) & 0x3f]; + fval |= SP4[(work >>> 16) & 0x3f]; + fval |= SP2[(work >>> 24) & 0x3f]; + left ^= fval; + work = (left << 28) | (left >>> 4); + work ^= wKey[round * 4 + 2]; + fval = SP7[work & 0x3f]; + fval |= SP5[(work >>> 8) & 0x3f]; + fval |= SP3[(work >>> 16) & 0x3f]; + fval |= SP1[(work >>> 24) & 0x3f]; + work = left ^ wKey[round * 4 + 3]; + fval |= SP8[work & 0x3f]; + fval |= SP6[(work >>> 8) & 0x3f]; + fval |= SP4[(work >>> 16) & 0x3f]; + fval |= SP2[(work >>> 24) & 0x3f]; + right ^= fval; + } + + right = (right << 31) | (right >>> 1); + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = (left << 31) | (left >>> 1); + work = ((left >>> 8) ^ right) & 0x00ff00ff; + right ^= work; + left ^= (work << 8); + work = ((left >>> 2) ^ right) & 0x33333333; + right ^= work; + left ^= (work << 2); + work = ((right >>> 16) ^ left) & 0x0000ffff; + left ^= work; + right ^= (work << 16); + work = ((right >>> 4) ^ left) & 0x0f0f0f0f; + left ^= work; + right ^= (work << 4); + + out[outOff + 0] = (byte) ((right >>> 24) & 0xff); + out[outOff + 1] = (byte) ((right >>> 16) & 0xff); + out[outOff + 2] = (byte) ((right >>> 8) & 0xff); + out[outOff + 3] = (byte) (right & 0xff); + out[outOff + 4] = (byte) ((left >>> 24) & 0xff); + out[outOff + 5] = (byte) ((left >>> 16) & 0xff); + out[outOff + 6] = (byte) ((left >>> 8) & 0xff); + out[outOff + 7] = (byte) (left & 0xff); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/DESede.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/DESede.java new file mode 100644 index 0000000..f47a636 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/DESede.java @@ -0,0 +1,105 @@ + +package com.trilead.ssh2.crypto.cipher; + +/* + This file was shamelessly taken (and modified) from the Bouncy Castle Crypto package. + Their licence file states the following: + + Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle + (http://www.bouncycastle.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/** + * DESede. + * + * @author See comments in the source file + * @version $Id: DESede.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class DESede extends DES +{ + private int[] key1 = null; + private int[] key2 = null; + private int[] key3 = null; + + private boolean encrypt; + + /** + * standard constructor. + */ + public DESede() + { + } + + /** + * initialise a DES cipher. + * + * @param encrypting + * whether or not we are for encryption. + * @param key + * the parameters required to set up the cipher. + * @exception IllegalArgumentException + * if the params argument is inappropriate. + */ + public void init(boolean encrypting, byte[] key) + { + key1 = generateWorkingKey(encrypting, key, 0); + key2 = generateWorkingKey(!encrypting, key, 8); + key3 = generateWorkingKey(encrypting, key, 16); + + encrypt = encrypting; + } + + public String getAlgorithmName() + { + return "DESede"; + } + + public int getBlockSize() + { + return 8; + } + + public void transformBlock(byte[] in, int inOff, byte[] out, int outOff) + { + if (key1 == null) + { + throw new IllegalStateException("DESede engine not initialised!"); + } + + if (encrypt) + { + desFunc(key1, in, inOff, out, outOff); + desFunc(key2, out, outOff, out, outOff); + desFunc(key3, out, outOff, out, outOff); + } + else + { + desFunc(key3, in, inOff, out, outOff); + desFunc(key2, out, outOff, out, outOff); + desFunc(key1, out, outOff, out, outOff); + } + } + + public void reset() + { + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/cipher/NullCipher.java b/service/src/main/java/com/trilead/ssh2/crypto/cipher/NullCipher.java new file mode 100644 index 0000000..38f8215 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/cipher/NullCipher.java @@ -0,0 +1,35 @@ +package com.trilead.ssh2.crypto.cipher; + +/** + * NullCipher. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: NullCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class NullCipher implements BlockCipher +{ + private int blockSize = 8; + + public NullCipher() + { + } + + public NullCipher(int blockSize) + { + this.blockSize = blockSize; + } + + public void init(boolean forEncryption, byte[] key) + { + } + + public int getBlockSize() + { + return blockSize; + } + + public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) + { + System.arraycopy(src, srcoff, dst, dstoff, blockSize); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java b/service/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java new file mode 100644 index 0000000..7066251 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java @@ -0,0 +1,160 @@ + +package com.trilead.ssh2.crypto.dh; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.SecureRandom; + +import com.trilead.ssh2.crypto.digest.HashForSSH2Types; +import com.trilead.ssh2.log.Logger; + + +/** + * DhExchange. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class DhExchange +{ + private static final Logger log = Logger.getLogger(DhExchange.class); + + /* Given by the standard */ + + static final BigInteger p1, p14; + static final BigInteger g; + + BigInteger p; + + /* Client public and private */ + + BigInteger e; + BigInteger x; + + /* Server public */ + + BigInteger f; + + /* Shared secret */ + + BigInteger k; + + static + { + final String p1_string = "17976931348623159077083915679378745319786029604875" + + "60117064444236841971802161585193689478337958649255415021805654859805036464" + + "40548199239100050792877003355816639229553136239076508735759914822574862575" + + "00742530207744771258955095793777842444242661733472762929938766870920560605" + + "0270810842907692932019128194467627007"; + + final String p14_string = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129" + + "024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0" + + "A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB" + + "6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A" + + "163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208" + + "552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36C" + + "E3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558171" + + "83995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"; + + p1 = new BigInteger(p1_string); + p14 = new BigInteger(p14_string, 16); + g = new BigInteger("2"); + } + + private final String hashAlgorithm; + + @Deprecated + public DhExchange() + { + this("SHA1"); + } + + public DhExchange(String hashAlgorightm) { + super(); + this.hashAlgorithm = hashAlgorightm; + } + + public void init(int group, SecureRandom rnd) + { + k = null; + + if (group == 1) + p = p1; + else if (group == 14) + p = p14; + else + throw new IllegalArgumentException("Unknown DH group " + group); + + x = new BigInteger(p.bitLength() - 1, rnd); + + e = g.modPow(x, p); + } + + /** + * @return Returns the e. + * + */ + public BigInteger getE() + { + if (e == null) + throw new IllegalStateException("DhDsaExchange not initialized!"); + + return e; + } + + /** + * @return Returns the shared secret k. + * + */ + public BigInteger getK() + { + if (k == null) + throw new IllegalStateException("Shared secret not yet known, need f first!"); + + return k; + } + + /** + * @param f f + */ + public void setF(BigInteger f) + { + if (e == null) + throw new IllegalStateException("DhDsaExchange not initialized!"); + + BigInteger zero = BigInteger.valueOf(0); + + if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0) + throw new IllegalArgumentException("Invalid f specified!"); + + this.f = f; + this.k = f.modPow(x, p); + } + + public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload, + byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException + { + HashForSSH2Types hash = new HashForSSH2Types(getHashAlgorithm()); + + if (log.isEnabled()) + { + log.log(90, "Client: '" + new String(clientversion, "ISO-8859-1") + "'"); + log.log(90, "Server: '" + new String(serverversion, "ISO-8859-1") + "'"); + } + + hash.updateByteString(clientversion); + hash.updateByteString(serverversion); + hash.updateByteString(clientKexPayload); + hash.updateByteString(serverKexPayload); + hash.updateByteString(hostKey); + hash.updateBigInt(e); + hash.updateBigInt(f); + hash.updateBigInt(k); + + return hash.getDigest(); + } + + public String getHashAlgorithm() { + return hashAlgorithm; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java b/service/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java new file mode 100644 index 0000000..909eae4 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java @@ -0,0 +1,127 @@ + +package com.trilead.ssh2.crypto.dh; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import com.trilead.ssh2.DHGexParameters; +import com.trilead.ssh2.crypto.digest.HashForSSH2Types; + + +/** + * DhGroupExchange. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class DhGroupExchange +{ + /* Given by the standard */ + + private BigInteger p; + private BigInteger g; + + /* Client public and private */ + + private BigInteger e; + private BigInteger x; + + /* Server public */ + + private BigInteger f; + + /* Shared secret */ + + private BigInteger k; + + private final String hashAlgorithm; + + @Deprecated + public DhGroupExchange(BigInteger p, BigInteger g) { + this("SHA1", p, g); + } + + + public DhGroupExchange(String algorithm, BigInteger p, BigInteger g) + { + this.p = p; + this.g = g; + this.hashAlgorithm = algorithm; + } + + public void init(SecureRandom rnd) + { + k = null; + + x = new BigInteger(p.bitLength() - 1, rnd); + e = g.modPow(x, p); + } + + /** + * @return Returns the e. + */ + public BigInteger getE() + { + if (e == null) + throw new IllegalStateException("Not initialized!"); + + return e; + } + + /** + * @return Returns the shared secret k. + */ + public BigInteger getK() + { + if (k == null) + throw new IllegalStateException("Shared secret not yet known, need f first!"); + + return k; + } + + /** + * Sets f and calculates the shared secret. + * @param f f. + */ + public void setF(BigInteger f) + { + if (e == null) + throw new IllegalStateException("Not initialized!"); + + BigInteger zero = BigInteger.valueOf(0); + + if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0) + throw new IllegalArgumentException("Invalid f specified!"); + + this.f = f; + this.k = f.modPow(x, p); + } + + public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload, + byte[] serverKexPayload, byte[] hostKey, DHGexParameters para) + { + HashForSSH2Types hash = new HashForSSH2Types(getHashAlgorithm()); + + hash.updateByteString(clientversion); + hash.updateByteString(serverversion); + hash.updateByteString(clientKexPayload); + hash.updateByteString(serverKexPayload); + hash.updateByteString(hostKey); + if (para.getMin_group_len() > 0) + hash.updateUINT32(para.getMin_group_len()); + hash.updateUINT32(para.getPref_group_len()); + if (para.getMax_group_len() > 0) + hash.updateUINT32(para.getMax_group_len()); + hash.updateBigInt(p); + hash.updateBigInt(g); + hash.updateBigInt(e); + hash.updateBigInt(f); + hash.updateBigInt(k); + + return hash.getDigest(); + } + + public String getHashAlgorithm() { + return hashAlgorithm; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/Digest.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/Digest.java new file mode 100644 index 0000000..10f8baa --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/Digest.java @@ -0,0 +1,25 @@ + +package com.trilead.ssh2.crypto.digest; + +/** + * Digest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Digest.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public interface Digest +{ + public int getDigestLength(); + + public void update(byte b); + + public void update(byte[] b); + + public void update(byte b[], int off, int len); + + public void reset(); + + public void digest(byte[] out); + + public void digest(byte[] out, int off); +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/HMAC.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/HMAC.java new file mode 100644 index 0000000..818c8d8 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/HMAC.java @@ -0,0 +1,96 @@ + +package com.trilead.ssh2.crypto.digest; + +/** + * HMAC. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: HMAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +@Deprecated +public final class HMAC implements Digest +{ + Digest md; + byte[] k_xor_ipad; + byte[] k_xor_opad; + + byte[] tmp; + + int size; + + public HMAC(Digest md, byte[] key, int size) + { + this.md = md; + this.size = size; + + tmp = new byte[md.getDigestLength()]; + + final int BLOCKSIZE = 64; + + k_xor_ipad = new byte[BLOCKSIZE]; + k_xor_opad = new byte[BLOCKSIZE]; + + if (key.length > BLOCKSIZE) + { + md.reset(); + md.update(key); + md.digest(tmp); + key = tmp; + } + + System.arraycopy(key, 0, k_xor_ipad, 0, key.length); + System.arraycopy(key, 0, k_xor_opad, 0, key.length); + + for (int i = 0; i < BLOCKSIZE; i++) + { + k_xor_ipad[i] ^= 0x36; + k_xor_opad[i] ^= 0x5C; + } + md.update(k_xor_ipad); + } + + public final int getDigestLength() + { + return size; + } + + public final void update(byte b) + { + md.update(b); + } + + public final void update(byte[] b) + { + md.update(b); + } + + public final void update(byte[] b, int off, int len) + { + md.update(b, off, len); + } + + public final void reset() + { + md.reset(); + md.update(k_xor_ipad); + } + + public final void digest(byte[] out) + { + digest(out, 0); + } + + public final void digest(byte[] out, int off) + { + md.digest(tmp); + + md.update(k_xor_opad); + md.update(tmp); + + md.digest(tmp); + + System.arraycopy(tmp, 0, out, off, size); + + md.update(k_xor_ipad); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java new file mode 100644 index 0000000..91230dc --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java @@ -0,0 +1,107 @@ + +package com.trilead.ssh2.crypto.digest; + +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; + +/** + * HashForSSH2Types. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class HashForSSH2Types +{ + + /** + * Overwriting this value will not cause the result of the + * digest to change + * @deprecated the actual message digest is held in a private field + */ + @Deprecated + Digest md; + + private final Digest messageDigest; + + + + public HashForSSH2Types(Digest md) + { + super(); + this.md = md; + this.messageDigest = md; + } + + public HashForSSH2Types(String type) + { + this(new JreMessageDigestWrapper(createMessageDigest(type))); + } + + private static MessageDigest createMessageDigest(String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (GeneralSecurityException ex) { + throw new IllegalArgumentException("Could not get Message digest instance", ex); + } + } + + public void updateByte(byte b) + { + /* HACK - to test it with J2ME */ + byte[] tmp = new byte[1]; + tmp[0] = b; + messageDigest.update(tmp); + } + + public void updateBytes(byte[] b) + { + messageDigest.update(b); + } + + public void updateUINT32(int v) + { + messageDigest.update((byte) (v >> 24)); + messageDigest.update((byte) (v >> 16)); + messageDigest.update((byte) (v >> 8)); + messageDigest.update((byte) (v)); + } + + public void updateByteString(byte[] b) + { + updateUINT32(b.length); + updateBytes(b); + } + + public void updateBigInt(BigInteger b) + { + updateByteString(b.toByteArray()); + } + + public void reset() + { + messageDigest.reset(); + } + + public int getDigestLength() + { + return messageDigest.getDigestLength(); + } + + public byte[] getDigest() + { + byte[] tmp = new byte[messageDigest.getDigestLength()]; + getDigest(tmp); + return tmp; + } + + public void getDigest(byte[] out) + { + getDigest(out, 0); + } + + public void getDigest(byte[] out, int off) + { + messageDigest.digest(out, off); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/JreMessageDigestWrapper.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/JreMessageDigestWrapper.java new file mode 100644 index 0000000..935f6ce --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/JreMessageDigestWrapper.java @@ -0,0 +1,47 @@ +package com.trilead.ssh2.crypto.digest; + +import java.security.MessageDigest; + +/** + * @author Michael Clarke + */ +public class JreMessageDigestWrapper implements Digest { + + private final MessageDigest digest; + + public JreMessageDigestWrapper(MessageDigest digest) { + super(); + this.digest = digest; + } + + public int getDigestLength() { + return digest.getDigestLength(); + } + + public void update(byte b) { + digest.update(b); + } + + public void update(byte[] b) { + digest.update(b); + } + + public void update(byte[] b, int off, int len) { + digest.update(b, off, len); + } + + public void reset() { + digest.reset(); + } + + public void digest(byte[] out) { + byte[] output = digest.digest(); + System.arraycopy(output, 0, out, 0, out.length); + } + + public void digest(byte[] out, int off) { + byte[] output = digest.digest(); + System.arraycopy(output, 0, out, off, out.length); + + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/MAC.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/MAC.java new file mode 100644 index 0000000..bb11d10 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/MAC.java @@ -0,0 +1,130 @@ + +package com.trilead.ssh2.crypto.digest; + +/** + * MAC. Replace by {@link MessageMac to support enchanced Mac algorithms} + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +/* This class is technically deprecated, but isn't marked as such since it's +* just the implementation with the public fields that's deprecated, rather than +* any of the methods in it +*/ +public class MAC +{ + /** + * @deprecated May be null if a newer Mac algorithm is used + */ + @Deprecated + Digest mac; + + /** + * @deprecated May be null if a newer Mac algorithm is used + */ + @Deprecated + int size; + + /** + * Get mac list string [ ]. + * + * @return the string [ ] + * @deprecated Use {@link MessageMac#getMacs()} + */ + @Deprecated + public static String[] getMacList() + { + /* Higher Priority First */ + + return new String[] { "hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5" }; + } + + + /** + * Check mac list. + * + * @param macs the macs + * @deprecated Use {@link MessageMac#checkMacs(String[])} + */ + @Deprecated + public static void checkMacList(String[] macs) + { + for (int i = 0; i < macs.length; i++) + getKeyLen(macs[i]); + } + + + /** + * Gets key len. + * + * @param type the type + * @return the key len + * @deprecated Use {@link MessageMac#getKeyLength(String)} + */ + @Deprecated + public static int getKeyLen(String type) + { + if (type.equals("hmac-sha1")) + return 20; + if (type.equals("hmac-sha1-96")) + return 20; + if (type.equals("hmac-md5")) + return 16; + if (type.equals("hmac-md5-96")) + return 16; + throw new IllegalArgumentException("Unkown algorithm " + type); + } + + /** + * @param type the MAC algorithm to use + * @param key the key to use in the MAC + * @deprecated use {@link MessageMac#MessageMac(String, byte[])} + */ + public MAC(String type, byte[] key) + { + if (type.equals("hmac-sha1")) + { + mac = new HMAC(new SHA1(), key, 20); + } + else if (type.equals("hmac-sha1-96")) + { + mac = new HMAC(new SHA1(), key, 12); + } + else if (type.equals("hmac-md5")) + { + mac = new HMAC(new MD5(), key, 16); + } + else if (type.equals("hmac-md5-96")) + { + mac = new HMAC(new MD5(), key, 12); + } + else + return; + + size = mac.getDigestLength(); + } + + public void initMac(int seq) + { + mac.reset(); + mac.update((byte) (seq >> 24)); + mac.update((byte) (seq >> 16)); + mac.update((byte) (seq >> 8)); + mac.update((byte) (seq)); + } + + public void update(byte[] packetdata, int off, int len) + { + mac.update(packetdata, off, len); + } + + public void getMac(byte[] out, int off) + { + mac.digest(out, off); + } + + public int size() + { + return size; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/MD5.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/MD5.java new file mode 100644 index 0000000..b3b424c --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/MD5.java @@ -0,0 +1,268 @@ + +package com.trilead.ssh2.crypto.digest; + +/** + * MD5. Based on the example code in RFC 1321. Optimized (...a little). + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: MD5.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ + +/* + * The following disclaimer has been copied from RFC 1321: + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights + * reserved. + * + * License to copy and use this software is granted provided that it is + * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in + * all material mentioning or referencing this software or this function. + * + * License is also granted to make and use derivative works provided that such + * works are identified as "derived from the RSA Data Security, Inc. MD5 + * Message-Digest Algorithm" in all material mentioning or referencing the + * derived work. + * + * RSA Data Security, Inc. makes no representations concerning either the + * merchantability of this software or the suitability of this software for any + * particular purpose. It is provided "as is" without express or implied + * warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + * @deprecated Use java.security.MessageDigest.getInstance("MD5"); + */ +@Deprecated +public final class MD5 implements Digest +{ + private int state0, state1, state2, state3; + private long count; + private final byte[] block = new byte[64]; + private final int x[] = new int[16]; + + private static final byte[] padding = new byte[] { (byte) 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + public MD5() + { + reset(); + } + + private static final int FF(int a, int b, int c, int d, int x, int s, int ac) + { + a += ((b & c) | ((~b) & d)) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static final int GG(int a, int b, int c, int d, int x, int s, int ac) + { + a += ((b & d) | (c & (~d))) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static final int HH(int a, int b, int c, int d, int x, int s, int ac) + { + a += (b ^ c ^ d) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static final int II(int a, int b, int c, int d, int x, int s, int ac) + { + a += (c ^ (b | (~d))) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static final void encode(byte[] dst, int dstoff, int word) + { + dst[dstoff] = (byte) (word); + dst[dstoff + 1] = (byte) (word >> 8); + dst[dstoff + 2] = (byte) (word >> 16); + dst[dstoff + 3] = (byte) (word >> 24); + } + + private final void transform(byte[] src, int pos) + { + int a = state0; + int b = state1; + int c = state2; + int d = state3; + + for (int i = 0; i < 16; i++, pos += 4) + { + x[i] = (src[pos] & 0xff) | ((src[pos + 1] & 0xff) << 8) | ((src[pos + 2] & 0xff) << 16) + | ((src[pos + 3] & 0xff) << 24); + } + + /* Round 1 */ + + a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */ + d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */ + c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */ + b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */ + a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */ + d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */ + c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */ + b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */ + a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */ + d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */ + c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */ + b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */ + a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */ + d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */ + c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */ + b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */ + d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */ + c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */ + b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */ + a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */ + d = GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */ + c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */ + b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */ + a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */ + d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */ + c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */ + b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */ + a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */ + d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */ + c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */ + b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */ + d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */ + c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */ + b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */ + a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */ + d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */ + c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */ + b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */ + a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */ + d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */ + c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */ + b = HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */ + a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */ + d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */ + c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */ + b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */ + d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */ + c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */ + b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */ + a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */ + d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */ + c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */ + b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */ + a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */ + d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */ + c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */ + b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */ + a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */ + d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */ + c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */ + b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */ + + state0 += a; + state1 += b; + state2 += c; + state3 += d; + } + + public final void reset() + { + count = 0; + + state0 = 0x67452301; + state1 = 0xefcdab89; + state2 = 0x98badcfe; + state3 = 0x10325476; + + /* Clear traces in memory... */ + + for (int i = 0; i < 16; i++) + x[i] = 0; + } + + public final void update(byte b) + { + final int space = 64 - ((int) (count & 0x3f)); + + count++; + + block[64 - space] = b; + + if (space == 1) + transform(block, 0); + } + + public final void update(byte[] buff, int pos, int len) + { + int space = 64 - ((int) (count & 0x3f)); + + count += len; + + while (len > 0) + { + if (len < space) + { + System.arraycopy(buff, pos, block, 64 - space, len); + break; + } + + if (space == 64) + { + transform(buff, pos); + } + else + { + System.arraycopy(buff, pos, block, 64 - space, space); + transform(block, 0); + } + + pos += space; + len -= space; + space = 64; + } + } + + public final void update(byte[] b) + { + update(b, 0, b.length); + } + + public final void digest(byte[] dst, int pos) + { + byte[] bits = new byte[8]; + + encode(bits, 0, (int) (count << 3)); + encode(bits, 4, (int) (count >> 29)); + + int idx = (int) count & 0x3f; + int padLen = (idx < 56) ? (56 - idx) : (120 - idx); + + update(padding, 0, padLen); + update(bits, 0, 8); + + encode(dst, pos, state0); + encode(dst, pos + 4, state1); + encode(dst, pos + 8, state2); + encode(dst, pos + 12, state3); + + reset(); + } + + public final void digest(byte[] dst) + { + digest(dst, 0); + } + + public final int getDigestLength() + { + return 16; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/MessageMac.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/MessageMac.java new file mode 100644 index 0000000..8d95971 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/MessageMac.java @@ -0,0 +1,107 @@ + +package com.trilead.ssh2.crypto.digest; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.List; + +public final class MessageMac extends MAC { + + private final Mac messageMac; + + public MessageMac(String type, byte[] key) { + super(type, key); + + try { + messageMac = Mac.getInstance(Hmac.getHmac(type).getAlgorithm()); + messageMac.init(new SecretKeySpec(key, type)); + } catch (GeneralSecurityException ex) { + throw new IllegalArgumentException("Could not create Mac", ex); + } + } + + + public static String[] getMacs() { + List macList = new ArrayList(); + for (Hmac hmac : Hmac.values()) { + macList.add(0, hmac.getType()); + } + return macList.toArray(new String[macList.size()]); + } + + public static void checkMacs(String[] macs) { + for (String mac : macs) { + Hmac.getHmac(mac); + } + } + + public static int getKeyLength(String type) { + return Hmac.getHmac(type).getLength(); + } + + public final void initMac(int seq) { + messageMac.reset(); + messageMac.update((byte) (seq >> 24)); + messageMac.update((byte) (seq >> 16)); + messageMac.update((byte) (seq >> 8)); + messageMac.update((byte) (seq)); + } + + public final void update(byte[] packetdata, int off, int len) + { + messageMac.update(packetdata, off, len); + } + + public final void getMac(byte[] out, int off) { + byte[] mac = messageMac.doFinal(); + System.arraycopy(mac, off, out, 0, mac.length - off); + } + + public final int size() + { + return messageMac.getMacLength(); + } + + + private enum Hmac { + HMAC_MD5_96("hmac-md5-96", "HmacMD5", 16), + HMAC_MD5("hmac-md5", "HmacMD5", 16), + HMAC_SHA1_96("hmac-sha1-96", "HmacSHA1", 20), + HMAC_SHA1("hmac-sha1", "HmacSHA1", 20), + HMAC_SHA2_256("hmac-sha2-256", "HmacSHA256", 32), + HMAC_SHA2_512("hmac-sha2-512", "HmacSHA512", 64); + + private String type; + private String algorithm; + private int length; + + Hmac(String type, String algorithm, int length) { + this.type = type; + this.algorithm = algorithm; + this.length = length; + } + + public String getType() { + return type; + } + + public String getAlgorithm() { + return algorithm; + } + + public int getLength() { + return length; + } + + private static Hmac getHmac(String type) { + for (Hmac hmac : values()) { + if (hmac.getType().equals(type)) { + return hmac; + } + } + throw new IllegalArgumentException("Invalid HMAC type: " + type); + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/crypto/digest/SHA1.java b/service/src/main/java/com/trilead/ssh2/crypto/digest/SHA1.java new file mode 100644 index 0000000..4ead6e7 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/crypto/digest/SHA1.java @@ -0,0 +1,611 @@ + +package com.trilead.ssh2.crypto.digest; + +/** + * SHA-1 implementation based on FIPS PUB 180-1. + * Highly optimized. + *

+ * (http://www.itl.nist.gov/fipspubs/fip180-1.htm) + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: SHA1.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated Use java.security.MessageDigest.getInstance("SHA1"); + */ +@Deprecated +public final class SHA1 implements Digest +{ + private int H0, H1, H2, H3, H4; + + private final int[] w = new int[80]; + private int currentPos; + private long currentLen; + + public SHA1() + { + reset(); + } + + public final int getDigestLength() + { + return 20; + } + + public final void reset() + { + H0 = 0x67452301; + H1 = 0xEFCDAB89; + H2 = 0x98BADCFE; + H3 = 0x10325476; + H4 = 0xC3D2E1F0; + + currentPos = 0; + currentLen = 0; + + /* In case of complete paranoia, we should also wipe out the + * information contained in the w[] array */ + } + + public final void update(byte b[]) + { + update(b, 0, b.length); + } + + public final void update(byte b[], int off, int len) + { + if (len >= 4) + { + int idx = currentPos >> 2; + + switch (currentPos & 3) + { + case 0: + w[idx] = (((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff)); + len -= 4; + currentPos += 4; + currentLen += 32; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + case 1: + w[idx] = (w[idx] << 24) | (((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff)); + len -= 3; + currentPos += 3; + currentLen += 24; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + case 2: + w[idx] = (w[idx] << 16) | (((b[off++] & 0xff) << 8) | (b[off++] & 0xff)); + len -= 2; + currentPos += 2; + currentLen += 16; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + case 3: + w[idx] = (w[idx] << 8) | (b[off++] & 0xff); + len--; + currentPos++; + currentLen += 8; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + } + + /* Now currentPos is a multiple of 4 - this is the place to be...*/ + + while (len >= 8) + { + w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) + | (b[off++] & 0xff); + currentPos += 4; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + + w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) + | (b[off++] & 0xff); + + currentPos += 4; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + + currentLen += 64; + len -= 8; + } + + while (len < 0) //(len >= 4) + { + w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) + | (b[off++] & 0xff); + len -= 4; + currentPos += 4; + currentLen += 32; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + } + } + + /* Remaining bytes (1-3) */ + + while (len > 0) + { + /* Here is room for further improvements */ + int idx = currentPos >> 2; + w[idx] = (w[idx] << 8) | (b[off++] & 0xff); + + currentLen += 8; + currentPos++; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + len--; + } + } + + public final void update(byte b) + { + int idx = currentPos >> 2; + w[idx] = (w[idx] << 8) | (b & 0xff); + + currentLen += 8; + currentPos++; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + } + + private final void putInt(byte[] b, int pos, int val) + { + b[pos] = (byte) (val >> 24); + b[pos + 1] = (byte) (val >> 16); + b[pos + 2] = (byte) (val >> 8); + b[pos + 3] = (byte) val; + } + + public final void digest(byte[] out) + { + digest(out, 0); + } + + public final void digest(byte[] out, int off) + { + /* Pad with a '1' and 7-31 zero bits... */ + + int idx = currentPos >> 2; + w[idx] = ((w[idx] << 8) | (0x80)) << ((3 - (currentPos & 3)) << 3); + + currentPos = (currentPos & ~3) + 4; + + if (currentPos == 64) + { + currentPos = 0; + perform(); + } + else if (currentPos == 60) + { + currentPos = 0; + w[15] = 0; + perform(); + } + + /* Now currentPos is a multiple of 4 and we can do the remaining + * padding much more efficiently, furthermore we are sure + * that currentPos <= 56. + */ + + for (int i = currentPos >> 2; i < 14; i++) + w[i] = 0; + + w[14] = (int) (currentLen >> 32); + w[15] = (int) currentLen; + + perform(); + + putInt(out, off, H0); + putInt(out, off + 4, H1); + putInt(out, off + 8, H2); + putInt(out, off + 12, H3); + putInt(out, off + 16, H4); + + reset(); + } + + private final void perform() + { + for (int t = 16; t < 80; t++) + { + int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; + w[t] = ((x << 1) | (x >>> 31)); + } + + int A = H0; + int B = H1; + int C = H2; + int D = H3; + int E = H4; + + /* Here we use variable substitution and loop unrolling + * + * === Original step: + * + * T = s5(A) + f(B,C,D) + E + w[0] + K; + * E = D; D = C; C = s30(B); B = A; A = T; + * + * === Rewritten step: + * + * T = s5(A + f(B,C,D) + E + w[0] + K; + * B = s30(B); + * E = D; D = C; C = B; B = A; A = T; + * + * === Let's rewrite things, introducing new variables: + * + * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A; + * + * T = s5(A0) + f(B0,C0,D0) + E0 + w[0] + K; + * B0 = s30(B0); + * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = T; + * + * T = s5(A1) + f(B1,C1,D1) + E1 + w[1] + K; + * B1 = s30(B1); + * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = T; + * + * E = E2; D = E2; C = C2; B = B2; A = A2; + * + * === No need for 'T', we can write into 'Ex' instead since + * after the calculation of 'T' nobody is interested + * in 'Ex' anymore. + * + * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A; + * + * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K; + * B0 = s30(B0); + * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0; + * + * E1 = E1 + s5(A1) + f(B1,C1,D1) + w[1] + K; + * B1 = s30(B1); + * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = E1; + * + * E = Ex; D = Ex; C = Cx; B = Bx; A = Ax; + * + * === Further optimization: get rid of the swap operations + * Idea: instead of swapping the variables, swap the names of + * the used variables in the next step: + * + * E0 = E; D0 = d; C0 = C; B0 = B; A0 = A; + * + * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K; + * B0 = s30(B0); + * // E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0; + * + * D0 = D0 + s5(E0) + f(A0,B0,C0) + w[1] + K; + * A0 = s30(A0); + * E2 = C0; D2 = B0; C2 = A0; B2 = E0; A2 = D0; + * + * E = E2; D = D2; C = C2; B = B2; A = A2; + * + * === OK, let's do this several times, also, directly + * use A (instead of A0) and B,C,D,E. + * + * E = E + s5(A) + f(B,C,D) + w[0] + K; + * B = s30(B); + * // E1 = D; D1 = C; C1 = B; B1 = A; A1 = E; + * + * D = D + s5(E) + f(A,B,C) + w[1] + K; + * A = s30(A); + * // E2 = C; D2 = B; C2 = A; B2 = E; A2 = D; + * + * C = C + s5(D) + f(E,A,B) + w[2] + K; + * E = s30(E); + * // E3 = B; D3 = A; C3 = E; B3 = D; A3 = C; + * + * B = B + s5(C) + f(D,E,A) + w[3] + K; + * D = s30(D); + * // E4 = A; D4 = E; C4 = D; B4 = C; A4 = B; + * + * A = A + s5(B) + f(C,D,E) + w[4] + K; + * C = s30(C); + * // E5 = E; D5 = D; C5 = C; B5 = B; A5 = A; + * + * //E = E5; D = D5; C = C5; B = B5; A = A5; + * + * === Very nice, after 5 steps each variable + * has the same contents as after 5 steps with + * the original algorithm! + * + * We therefore can easily unroll each interval, + * as the number of steps in each interval is a + * multiple of 5 (20 steps per interval). + */ + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[0] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[1] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[2] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[3] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[4] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[5] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[6] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[7] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[8] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[9] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[10] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[11] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[12] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[13] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[14] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[15] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[16] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[17] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[18] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[19] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[40] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[41] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[42] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[43] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[44] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[45] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[46] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[47] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[48] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[49] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[50] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[51] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[52] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[53] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[54] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E = E + ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[55] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[56] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[57] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[58] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[59] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + H0 += A; + H1 += B; + H2 += C; + H3 += D; + H4 += E; + + // debug(80, H0, H1, H2, H3, H4); + } + + private static final String toHexString(byte[] b) + { + final String hexChar = "0123456789ABCDEF"; + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < b.length; i++) + { + sb.append(hexChar.charAt((b[i] >> 4) & 0x0f)); + sb.append(hexChar.charAt(b[i] & 0x0f)); + } + return sb.toString(); + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/jenkins/SFTPClient.java b/service/src/main/java/com/trilead/ssh2/jenkins/SFTPClient.java new file mode 100644 index 0000000..01c8e5d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/jenkins/SFTPClient.java @@ -0,0 +1,189 @@ +/* + * The MIT License + * + * Copyright (c) 2004-, all the contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.trilead.ssh2.jenkins; + +import com.trilead.ssh2.SFTPv3Client; +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.SFTPv3FileHandle; +import com.trilead.ssh2.SFTPv3FileAttributes; +import com.trilead.ssh2.SFTPException; +import com.trilead.ssh2.sftp.ErrorCodes; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.InputStream; + +/** + * This Class adds file manage capabilities to the SFTPv3Client class. + * @author Kohsuke Kawaguchi + */ +public class SFTPClient extends SFTPv3Client { + public SFTPClient(Connection conn) throws IOException { + super(conn); + } + + /** + * Checks if the given path exists. + * @param path directory or file path. + * @return true if it exists. + * @throws IOException if it is not possible to access to the directory or file . + */ + public boolean exists(String path) throws IOException { + return _stat(path)!=null; + } + + /** + * Graceful {@link #stat(String)} that returns null if the path doesn't exist. + * @param path directory path. + * @throws IOException if it is not possible to access to the directory. + */ + public SFTPv3FileAttributes _stat(String path) throws IOException { + try { + return stat(path); + } catch (SFTPException e) { + int c = e.getServerErrorCode(); + if (c==ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH) + return null; + else + throw e; + } + } + + /** + * Makes sure that the directory exists, by creating it if necessary. + * @param path directory path. + * @param posixPermission POSIX permissions. + * @throws IOException if it is not possible to access to the directory. + */ + public void mkdirs(String path, int posixPermission) throws IOException { + SFTPv3FileAttributes atts = _stat(path); + if (atts!=null && atts.isDirectory()) + return; + + int idx = path.lastIndexOf('/'); + if (idx>0) + mkdirs(path.substring(0,idx), posixPermission); + + try { + mkdir(path, posixPermission); + } catch (IOException e) { + throw new IOException("Failed to mkdir "+path,e); + } + } + + /** + * + * @param path file path. + * @return Creates a new file and return an OutputStream to writes to it. + * @throws IOException if it is not possible to access to the file. + */ + public OutputStream writeToFile(String path) throws IOException { + final SFTPv3FileHandle h = createFile(path); + return new SFTPOutputStream(h); + } + + /** + * + * @param file file path. + * @return return an InputStream to the file. + * @throws IOException if it is not possible to access to the file. + */ + public InputStream read(String file) throws IOException { + final SFTPv3FileHandle h = openFileRO(file); + return new SFTPInputStream(h); + } + + /** + * Change file or directory permissions. + * @param path file or directory path. + * @param permissions POSIX permissions. + * @throws IOException in case of error. + */ + public void chmod(String path, int permissions) throws IOException { + SFTPv3FileAttributes atts = new SFTPv3FileAttributes(); + atts.permissions = permissions; + setstat(path, atts); + } + + private class SFTPOutputStream extends OutputStream { + private final SFTPv3FileHandle h; + private long offset; + + public SFTPOutputStream(SFTPv3FileHandle h) { + this.h = h; + offset = 0; + } + + public void write(int b) throws IOException { + write(new byte[]{(byte)b}); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + SFTPClient.this.write(h,offset,b,off,len); + offset += len; + } + + @Override + public void close() throws IOException { + closeFile(h); + } + } + + private class SFTPInputStream extends InputStream { + private final SFTPv3FileHandle h; + private long offset; + + public SFTPInputStream(SFTPv3FileHandle h) { + this.h = h; + offset = 0; + } + + public int read() throws IOException { + byte[] b = new byte[1]; + if(read(b)<0) + return -1; + return b[0]; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int r = SFTPClient.this.read(h,offset,b,off,len); + if (r<0) return -1; + offset += r; + return r; + } + + @Override + public long skip(long n) throws IOException { + offset += n; + return n; + } + + @Override + public void close() throws IOException { + closeFile(h); + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/log/Logger.java b/service/src/main/java/com/trilead/ssh2/log/Logger.java new file mode 100644 index 0000000..65e7a5d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/log/Logger.java @@ -0,0 +1,61 @@ + +package com.trilead.ssh2.log; + +import com.trilead.ssh2.DebugLogger; + +import java.util.logging.Level; + +/** + * Logger - a very simple logger, mainly used during development. + * Is not based on log4j (to reduce external dependencies). + * However, if needed, something like log4j could easily be + * hooked in. + *

+ * For speed reasons, the static variables are not protected + * with semaphores. In other words, if you dynamicaly change the + * logging settings, then some threads may still use the old setting. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Logger.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $ + */ + +public class Logger +{ + public static boolean enabled = false; + public static DebugLogger logger = null; + + private String className; + + public final static Logger getLogger(Class x) + { + return new Logger(x); + } + + public Logger(Class x) + { + this.className = x.getName(); + } + + public final boolean isEnabled() + { + return enabled; + } + + public final void log(int lv, String message, Throwable cause) + { + log(lv, message + ", " + cause); + } + + public final void log(int level, String message) + { + if (!enabled) + return; + + DebugLogger target = logger; + + if (target == null) + return; + + target.log(level, className, message); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java new file mode 100644 index 0000000..bd2ea3f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java @@ -0,0 +1,66 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketChannelOpenConfirmation. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketChannelOpenConfirmation.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketChannelOpenConfirmation +{ + byte[] payload; + + public int recipientChannelID; + public int senderChannelID; + public int initialWindowSize; + public int maxPacketSize; + + public PacketChannelOpenConfirmation(int recipientChannelID, int senderChannelID, int initialWindowSize, + int maxPacketSize) + { + this.recipientChannelID = recipientChannelID; + this.senderChannelID = senderChannelID; + this.initialWindowSize = initialWindowSize; + this.maxPacketSize = maxPacketSize; + } + + public PacketChannelOpenConfirmation(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) + throw new IOException( + "This is not a SSH_MSG_CHANNEL_OPEN_CONFIRMATION! (" + + packet_type + ")"); + + recipientChannelID = tr.readUINT32(); + senderChannelID = tr.readUINT32(); + initialWindowSize = tr.readUINT32(); + maxPacketSize = tr.readUINT32(); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + tw.writeUINT32(recipientChannelID); + tw.writeUINT32(senderChannelID); + tw.writeUINT32(initialWindowSize); + tw.writeUINT32(maxPacketSize); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenFailure.java b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenFailure.java new file mode 100644 index 0000000..1370355 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenFailure.java @@ -0,0 +1,66 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketChannelOpenFailure. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketChannelOpenFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketChannelOpenFailure +{ + byte[] payload; + + public int recipientChannelID; + public int reasonCode; + public String description; + public String languageTag; + + public PacketChannelOpenFailure(int recipientChannelID, int reasonCode, String description, + String languageTag) + { + this.recipientChannelID = recipientChannelID; + this.reasonCode = reasonCode; + this.description = description; + this.languageTag = languageTag; + } + + public PacketChannelOpenFailure(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_FAILURE) + throw new IOException( + "This is not a SSH_MSG_CHANNEL_OPEN_FAILURE! (" + + packet_type + ")"); + + recipientChannelID = tr.readUINT32(); + reasonCode = tr.readUINT32(); + description = tr.readString(); + languageTag = tr.readString(); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_FAILURE packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_FAILURE); + tw.writeUINT32(recipientChannelID); + tw.writeUINT32(reasonCode); + tw.writeString(description); + tw.writeString(languageTag); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java new file mode 100644 index 0000000..c337930 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java @@ -0,0 +1,35 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketChannelTrileadPing. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketChannelTrileadPing.java,v 1.1 2008/03/03 07:01:36 + * cplattne Exp $ + */ +public class PacketChannelTrileadPing +{ + byte[] payload; + + public int recipientChannelID; + + public PacketChannelTrileadPing(int recipientChannelID) + { + this.recipientChannelID = recipientChannelID; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("trilead-ping"); + tw.writeBoolean(true); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java new file mode 100644 index 0000000..37ec081 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java @@ -0,0 +1,57 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketChannelWindowAdjust. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketChannelWindowAdjust.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketChannelWindowAdjust +{ + byte[] payload; + + public int recipientChannelID; + public int windowChange; + + public PacketChannelWindowAdjust(int recipientChannelID, int windowChange) + { + this.recipientChannelID = recipientChannelID; + this.windowChange = windowChange; + } + + public PacketChannelWindowAdjust(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST) + throw new IOException( + "This is not a SSH_MSG_CHANNEL_WINDOW_ADJUST! (" + + packet_type + ")"); + + recipientChannelID = tr.readUINT32(); + windowChange = tr.readUINT32(); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_CHANNEL_WINDOW_ADJUST packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST); + tw.writeUINT32(recipientChannelID); + tw.writeUINT32(windowChange); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java b/service/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java new file mode 100644 index 0000000..50d6ec2 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java @@ -0,0 +1,57 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketDisconnect. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketDisconnect.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class PacketDisconnect +{ + byte[] payload; + + int reason; + String desc; + String lang; + + public PacketDisconnect(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_DISCONNECT) + throw new IOException("This is not a Disconnect Packet! (" + packet_type + ")"); + + reason = tr.readUINT32(); + desc = tr.readString(); + lang = tr.readString(); + } + + public PacketDisconnect(int reason, String desc, String lang) + { + this.reason = reason; + this.desc = desc; + this.lang = lang; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_DISCONNECT); + tw.writeUINT32(reason); + tw.writeString(desc); + tw.writeString(lang); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java b/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java new file mode 100644 index 0000000..20bd558 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java @@ -0,0 +1,42 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketGlobalCancelForwardRequest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketGlobalCancelForwardRequest.java,v 1.1 2007/10/15 12:49:55 + * cplattne Exp $ + */ +public class PacketGlobalCancelForwardRequest +{ + byte[] payload; + + public boolean wantReply; + public String bindAddress; + public int bindPort; + + public PacketGlobalCancelForwardRequest(boolean wantReply, String bindAddress, int bindPort) + { + this.wantReply = wantReply; + this.bindAddress = bindAddress; + this.bindPort = bindPort; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST); + + tw.writeString("cancel-tcpip-forward"); + tw.writeBoolean(wantReply); + tw.writeString(bindAddress); + tw.writeUINT32(bindPort); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java b/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java new file mode 100644 index 0000000..55257e9 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java @@ -0,0 +1,41 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketGlobalForwardRequest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketGlobalForwardRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketGlobalForwardRequest +{ + byte[] payload; + + public boolean wantReply; + public String bindAddress; + public int bindPort; + + public PacketGlobalForwardRequest(boolean wantReply, String bindAddress, int bindPort) + { + this.wantReply = wantReply; + this.bindAddress = bindAddress; + this.bindPort = bindPort; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST); + + tw.writeString("tcpip-forward"); + tw.writeBoolean(wantReply); + tw.writeString(bindAddress); + tw.writeUINT32(bindPort); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java b/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java new file mode 100644 index 0000000..3d8930e --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java @@ -0,0 +1,32 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketGlobalTrileadPing. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketGlobalTrileadPing.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $ + */ +public class PacketGlobalTrileadPing +{ + byte[] payload; + + public PacketGlobalTrileadPing() + { + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST); + + tw.writeString("trilead-ping"); + tw.writeBoolean(true); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketIgnore.java b/service/src/main/java/com/trilead/ssh2/packets/PacketIgnore.java new file mode 100644 index 0000000..2b4d917 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketIgnore.java @@ -0,0 +1,59 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketIgnore. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketIgnore.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketIgnore +{ + byte[] payload; + + byte[] data; + + public void setData(byte[] data) + { + this.data = data; + payload = null; + } + + public PacketIgnore() + { + } + + public PacketIgnore(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_IGNORE) + throw new IOException("This is not a SSH_MSG_IGNORE packet! (" + packet_type + ")"); + + /* Could parse String body */ + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_IGNORE); + + if (data != null) + tw.writeString(data, 0, data.length); + else + tw.writeString(""); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java new file mode 100644 index 0000000..cf39e33 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java @@ -0,0 +1,33 @@ +package com.trilead.ssh2.packets; + +import java.math.BigInteger; + +/** + * PacketKexDHInit. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexDHInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexDHInit +{ + byte[] payload; + + BigInteger e; + + public PacketKexDHInit(BigInteger e) + { + this.e = e; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_KEXDH_INIT); + tw.writeMPInt(e); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java new file mode 100644 index 0000000..27f9f91 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java @@ -0,0 +1,55 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +import java.math.BigInteger; + +/** + * PacketKexDHReply. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexDHReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexDHReply +{ + byte[] payload; + + byte[] hostKey; + BigInteger f; + byte[] signature; + + public PacketKexDHReply(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_KEXDH_REPLY) + throw new IOException("This is not a SSH_MSG_KEXDH_REPLY! (" + + packet_type + ")"); + + hostKey = tr.readByteString(); + f = tr.readMPINT(); + signature = tr.readByteString(); + + if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!"); + } + + public BigInteger getF() + { + return f; + } + + public byte[] getHostKey() + { + return hostKey; + } + + public byte[] getSignature() + { + return signature; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexGroup.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexGroup.java new file mode 100644 index 0000000..db85b61 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexGroup.java @@ -0,0 +1,50 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +import java.math.BigInteger; + +/** + * PacketKexDhGexGroup. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexDhGexGroup.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexDhGexGroup +{ + byte[] payload; + + BigInteger p; + BigInteger g; + + public PacketKexDhGexGroup(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_GROUP) + throw new IllegalArgumentException( + "This is not a SSH_MSG_KEX_DH_GEX_GROUP! (" + packet_type + + ")"); + + p = tr.readMPINT(); + g = tr.readMPINT(); + + if (tr.remain() != 0) + throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_GROUP!"); + } + + public BigInteger getG() + { + return g; + } + + public BigInteger getP() + { + return p; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexInit.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexInit.java new file mode 100644 index 0000000..8b34230 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexInit.java @@ -0,0 +1,33 @@ +package com.trilead.ssh2.packets; + +import java.math.BigInteger; + +/** + * PacketKexDhGexInit. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexDhGexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexDhGexInit +{ + byte[] payload; + + BigInteger e; + + public PacketKexDhGexInit(BigInteger e) + { + this.e = e; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_INIT); + tw.writeMPInt(e); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexReply.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexReply.java new file mode 100644 index 0000000..382b3b7 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexReply.java @@ -0,0 +1,56 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; + +import java.math.BigInteger; + +/** + * PacketKexDhGexReply. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexDhGexReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexDhGexReply +{ + byte[] payload; + + byte[] hostKey; + BigInteger f; + byte[] signature; + + public PacketKexDhGexReply(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_REPLY) + throw new IOException("This is not a SSH_MSG_KEX_DH_GEX_REPLY! (" + packet_type + ")"); + + hostKey = tr.readByteString(); + f = tr.readMPINT(); + signature = tr.readByteString(); + + if (tr.remain() != 0) + throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_REPLY!"); + } + + public BigInteger getF() + { + return f; + } + + public byte[] getHostKey() + { + return hostKey; + } + + public byte[] getSignature() + { + return signature; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequest.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequest.java new file mode 100644 index 0000000..50369df --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequest.java @@ -0,0 +1,39 @@ +package com.trilead.ssh2.packets; + +import com.trilead.ssh2.DHGexParameters; + +/** + * PacketKexDhGexRequest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexDhGexRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexDhGexRequest +{ + byte[] payload; + + int min; + int n; + int max; + + public PacketKexDhGexRequest(DHGexParameters para) + { + this.min = para.getMin_group_len(); + this.n = para.getPref_group_len(); + this.max = para.getMax_group_len(); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST); + tw.writeUINT32(min); + tw.writeUINT32(n); + tw.writeUINT32(max); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java new file mode 100644 index 0000000..327f379 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java @@ -0,0 +1,34 @@ + +package com.trilead.ssh2.packets; + +import com.trilead.ssh2.DHGexParameters; + +/** + * PacketKexDhGexRequestOld. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexDhGexRequestOld.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexDhGexRequestOld +{ + byte[] payload; + + int n; + + public PacketKexDhGexRequestOld(DHGexParameters para) + { + this.n = para.getPref_group_len(); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST_OLD); + tw.writeUINT32(n); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java b/service/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java new file mode 100644 index 0000000..fbd09ef --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java @@ -0,0 +1,165 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; +import java.security.SecureRandom; + +import com.trilead.ssh2.crypto.CryptoWishList; +import com.trilead.ssh2.transport.KexParameters; + + +/** + * PacketKexInit. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketKexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketKexInit +{ + byte[] payload; + + KexParameters kp = new KexParameters(); + + public PacketKexInit(CryptoWishList cwl, SecureRandom rnd) + { + kp.cookie = new byte[16]; + rnd.nextBytes(kp.cookie); + + kp.kex_algorithms = cwl.kexAlgorithms; + kp.server_host_key_algorithms = cwl.serverHostKeyAlgorithms; + kp.encryption_algorithms_client_to_server = cwl.c2s_enc_algos; + kp.encryption_algorithms_server_to_client = cwl.s2c_enc_algos; + kp.mac_algorithms_client_to_server = cwl.c2s_mac_algos; + kp.mac_algorithms_server_to_client = cwl.s2c_mac_algos; + kp.compression_algorithms_client_to_server = new String[] { "none" }; + kp.compression_algorithms_server_to_client = new String[] { "none" }; + kp.languages_client_to_server = new String[] {}; + kp.languages_server_to_client = new String[] {}; + kp.first_kex_packet_follows = false; + kp.reserved_field1 = 0; + } + + public PacketKexInit(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_KEXINIT) + throw new IOException("This is not a KexInitPacket! (" + packet_type + ")"); + + kp.cookie = tr.readBytes(16); + kp.kex_algorithms = tr.readNameList(); + kp.server_host_key_algorithms = tr.readNameList(); + kp.encryption_algorithms_client_to_server = tr.readNameList(); + kp.encryption_algorithms_server_to_client = tr.readNameList(); + kp.mac_algorithms_client_to_server = tr.readNameList(); + kp.mac_algorithms_server_to_client = tr.readNameList(); + kp.compression_algorithms_client_to_server = tr.readNameList(); + kp.compression_algorithms_server_to_client = tr.readNameList(); + kp.languages_client_to_server = tr.readNameList(); + kp.languages_server_to_client = tr.readNameList(); + kp.first_kex_packet_follows = tr.readBoolean(); + kp.reserved_field1 = tr.readUINT32(); + + if (tr.remain() != 0) + throw new IOException("Padding in KexInitPacket!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_KEXINIT); + tw.writeBytes(kp.cookie, 0, 16); + tw.writeNameList(kp.kex_algorithms); + tw.writeNameList(kp.server_host_key_algorithms); + tw.writeNameList(kp.encryption_algorithms_client_to_server); + tw.writeNameList(kp.encryption_algorithms_server_to_client); + tw.writeNameList(kp.mac_algorithms_client_to_server); + tw.writeNameList(kp.mac_algorithms_server_to_client); + tw.writeNameList(kp.compression_algorithms_client_to_server); + tw.writeNameList(kp.compression_algorithms_server_to_client); + tw.writeNameList(kp.languages_client_to_server); + tw.writeNameList(kp.languages_server_to_client); + tw.writeBoolean(kp.first_kex_packet_follows); + tw.writeUINT32(kp.reserved_field1); + payload = tw.getBytes(); + } + return payload; + } + + public KexParameters getKexParameters() + { + return kp; + } + + public String[] getCompression_algorithms_client_to_server() + { + return kp.compression_algorithms_client_to_server; + } + + public String[] getCompression_algorithms_server_to_client() + { + return kp.compression_algorithms_server_to_client; + } + + public byte[] getCookie() + { + return kp.cookie; + } + + public String[] getEncryption_algorithms_client_to_server() + { + return kp.encryption_algorithms_client_to_server; + } + + public String[] getEncryption_algorithms_server_to_client() + { + return kp.encryption_algorithms_server_to_client; + } + + public boolean isFirst_kex_packet_follows() + { + return kp.first_kex_packet_follows; + } + + public String[] getKex_algorithms() + { + return kp.kex_algorithms; + } + + public String[] getLanguages_client_to_server() + { + return kp.languages_client_to_server; + } + + public String[] getLanguages_server_to_client() + { + return kp.languages_server_to_client; + } + + public String[] getMac_algorithms_client_to_server() + { + return kp.mac_algorithms_client_to_server; + } + + public String[] getMac_algorithms_server_to_client() + { + return kp.mac_algorithms_server_to_client; + } + + public int getReserved_field1() + { + return kp.reserved_field1; + } + + public String[] getServer_host_key_algorithms() + { + return kp.server_host_key_algorithms; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketNewKeys.java b/service/src/main/java/com/trilead/ssh2/packets/PacketNewKeys.java new file mode 100644 index 0000000..3ca6503 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketNewKeys.java @@ -0,0 +1,46 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketNewKeys. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketNewKeys.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketNewKeys +{ + byte[] payload; + + public PacketNewKeys() + { + } + + public PacketNewKeys(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_NEWKEYS) + throw new IOException("This is not a SSH_MSG_NEWKEYS! (" + + packet_type + ")"); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_NEWKEYS packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_NEWKEYS); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java b/service/src/main/java/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java new file mode 100644 index 0000000..da6cbef --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java @@ -0,0 +1,56 @@ +package com.trilead.ssh2.packets; + + +/** + * PacketOpenDirectTCPIPChannel. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketOpenDirectTCPIPChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketOpenDirectTCPIPChannel +{ + byte[] payload; + + int channelID; + int initialWindowSize; + int maxPacketSize; + + String host_to_connect; + int port_to_connect; + String originator_IP_address; + int originator_port; + + public PacketOpenDirectTCPIPChannel(int channelID, int initialWindowSize, int maxPacketSize, + String host_to_connect, int port_to_connect, String originator_IP_address, + int originator_port) + { + this.channelID = channelID; + this.initialWindowSize = initialWindowSize; + this.maxPacketSize = maxPacketSize; + this.host_to_connect = host_to_connect; + this.port_to_connect = port_to_connect; + this.originator_IP_address = originator_IP_address; + this.originator_port = originator_port; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + + tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN); + tw.writeString("direct-tcpip"); + tw.writeUINT32(channelID); + tw.writeUINT32(initialWindowSize); + tw.writeUINT32(maxPacketSize); + tw.writeString(host_to_connect); + tw.writeUINT32(port_to_connect); + tw.writeString(originator_IP_address); + tw.writeUINT32(originator_port); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketOpenSessionChannel.java b/service/src/main/java/com/trilead/ssh2/packets/PacketOpenSessionChannel.java new file mode 100644 index 0000000..a75ea63 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketOpenSessionChannel.java @@ -0,0 +1,62 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketOpenSessionChannel. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketOpenSessionChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketOpenSessionChannel +{ + byte[] payload; + + int channelID; + int initialWindowSize; + int maxPacketSize; + + public PacketOpenSessionChannel(int channelID, int initialWindowSize, + int maxPacketSize) + { + this.channelID = channelID; + this.initialWindowSize = initialWindowSize; + this.maxPacketSize = maxPacketSize; + } + + public PacketOpenSessionChannel(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN) + throw new IOException("This is not a SSH_MSG_CHANNEL_OPEN! (" + + packet_type + ")"); + + channelID = tr.readUINT32(); + initialWindowSize = tr.readUINT32(); + maxPacketSize = tr.readUINT32(); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN); + tw.writeString("session"); + tw.writeUINT32(channelID); + tw.writeUINT32(initialWindowSize); + tw.writeUINT32(maxPacketSize); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java b/service/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java new file mode 100644 index 0000000..d5c9a90 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java @@ -0,0 +1,61 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketServiceAccept. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketServiceAccept.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class PacketServiceAccept +{ + byte[] payload; + + String serviceName; + + public PacketServiceAccept(String serviceName) + { + this.serviceName = serviceName; + } + + public PacketServiceAccept(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_SERVICE_ACCEPT) + throw new IOException("This is not a SSH_MSG_SERVICE_ACCEPT! (" + packet_type + ")"); + + /* Be clever in case the server is not. Some servers seem to violate RFC4253 */ + + if (tr.remain() > 0) + { + serviceName = tr.readString(); + } + else + { + serviceName = ""; + } + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_SERVICE_ACCEPT packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_SERVICE_ACCEPT); + tw.writeString(serviceName); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketServiceRequest.java b/service/src/main/java/com/trilead/ssh2/packets/PacketServiceRequest.java new file mode 100644 index 0000000..c2d2065 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketServiceRequest.java @@ -0,0 +1,52 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketServiceRequest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketServiceRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketServiceRequest +{ + byte[] payload; + + String serviceName; + + public PacketServiceRequest(String serviceName) + { + this.serviceName = serviceName; + } + + public PacketServiceRequest(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_SERVICE_REQUEST) + throw new IOException("This is not a SSH_MSG_SERVICE_REQUEST! (" + + packet_type + ")"); + + serviceName = tr.readString(); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_SERVICE_REQUEST packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_SERVICE_REQUEST); + tw.writeString(serviceName); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketSessionExecCommand.java b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionExecCommand.java new file mode 100644 index 0000000..84efa5d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionExecCommand.java @@ -0,0 +1,39 @@ +package com.trilead.ssh2.packets; + + +/** + * PacketSessionExecCommand. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketSessionExecCommand.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketSessionExecCommand +{ + byte[] payload; + + public int recipientChannelID; + public boolean wantReply; + public String command; + + public PacketSessionExecCommand(int recipientChannelID, boolean wantReply, String command) + { + this.recipientChannelID = recipientChannelID; + this.wantReply = wantReply; + this.command = command; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("exec"); + tw.writeBoolean(wantReply); + tw.writeString(command); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketSessionPtyRequest.java b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionPtyRequest.java new file mode 100644 index 0000000..d9c3d59 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionPtyRequest.java @@ -0,0 +1,57 @@ +package com.trilead.ssh2.packets; + + +/** + * PacketSessionPtyRequest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketSessionPtyRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketSessionPtyRequest +{ + byte[] payload; + + public int recipientChannelID; + public boolean wantReply; + public String term; + public int character_width; + public int character_height; + public int pixel_width; + public int pixel_height; + public byte[] terminal_modes; + + public PacketSessionPtyRequest(int recipientChannelID, boolean wantReply, String term, + int character_width, int character_height, int pixel_width, int pixel_height, + byte[] terminal_modes) + { + this.recipientChannelID = recipientChannelID; + this.wantReply = wantReply; + this.term = term; + this.character_width = character_width; + this.character_height = character_height; + this.pixel_width = pixel_width; + this.pixel_height = pixel_height; + this.terminal_modes = terminal_modes; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("pty-req"); + tw.writeBoolean(wantReply); + tw.writeString(term); + tw.writeUINT32(character_width); + tw.writeUINT32(character_height); + tw.writeUINT32(pixel_width); + tw.writeUINT32(pixel_height); + tw.writeString(terminal_modes, 0, terminal_modes.length); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketSessionStartShell.java b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionStartShell.java new file mode 100644 index 0000000..e5add01 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionStartShell.java @@ -0,0 +1,36 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketSessionStartShell. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketSessionStartShell.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketSessionStartShell +{ + byte[] payload; + + public int recipientChannelID; + public boolean wantReply; + + public PacketSessionStartShell(int recipientChannelID, boolean wantReply) + { + this.recipientChannelID = recipientChannelID; + this.wantReply = wantReply; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("shell"); + tw.writeBoolean(wantReply); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java new file mode 100644 index 0000000..cdc3a8c --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java @@ -0,0 +1,40 @@ +package com.trilead.ssh2.packets; + + +/** + * PacketSessionSubsystemRequest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketSessionSubsystemRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketSessionSubsystemRequest +{ + byte[] payload; + + public int recipientChannelID; + public boolean wantReply; + public String subsystem; + + public PacketSessionSubsystemRequest(int recipientChannelID, boolean wantReply, String subsystem) + { + this.recipientChannelID = recipientChannelID; + this.wantReply = wantReply; + this.subsystem = subsystem; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("subsystem"); + tw.writeBoolean(wantReply); + tw.writeString(subsystem); + payload = tw.getBytes(); + tw.getBytes(payload); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketSessionX11Request.java b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionX11Request.java new file mode 100644 index 0000000..26479c7 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketSessionX11Request.java @@ -0,0 +1,53 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketSessionX11Request. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketSessionX11Request.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketSessionX11Request +{ + byte[] payload; + + public int recipientChannelID; + public boolean wantReply; + + public boolean singleConnection; + String x11AuthenticationProtocol; + String x11AuthenticationCookie; + int x11ScreenNumber; + + public PacketSessionX11Request(int recipientChannelID, boolean wantReply, boolean singleConnection, + String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber) + { + this.recipientChannelID = recipientChannelID; + this.wantReply = wantReply; + + this.singleConnection = singleConnection; + this.x11AuthenticationProtocol = x11AuthenticationProtocol; + this.x11AuthenticationCookie = x11AuthenticationCookie; + this.x11ScreenNumber = x11ScreenNumber; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("x11-req"); + tw.writeBoolean(wantReply); + + tw.writeBoolean(singleConnection); + tw.writeString(x11AuthenticationProtocol); + tw.writeString(x11AuthenticationCookie); + tw.writeUINT32(x11ScreenNumber); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketSignal.java b/service/src/main/java/com/trilead/ssh2/packets/PacketSignal.java new file mode 100644 index 0000000..da01eaa --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketSignal.java @@ -0,0 +1,62 @@ +package com.trilead.ssh2.packets; + +import java.util.HashMap; +import java.util.Map; + +/** + * Delivers a signal from client to server. + * + * See section 6.9 of RFC 4254. + * + * @author Kohsuke Kawaguchi + */ +public class PacketSignal { + byte[] payload; + + public int recipientChannelID; + public String signalName; + + public PacketSignal(int recipientChannelID, String signalName) { + this.recipientChannelID = recipientChannelID; + + if (signalName.startsWith("SIG")) signalName=signalName.substring(3); + this.signalName = signalName; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("signal"); + tw.writeBoolean(false); + tw.writeString(signalName); + + payload = tw.getBytes(); + } + return payload; + } + + private static final Map SIGNALS = new HashMap(); + + public static String strsignal(int i) { + return SIGNALS.get(i); + } + + static { + SIGNALS.put(14,"ALRM"); + SIGNALS.put( 1,"HUP"); + SIGNALS.put( 2,"INT"); + SIGNALS.put( 9,"KILL"); + SIGNALS.put(13,"PIPE"); + SIGNALS.put(15,"TERM"); + SIGNALS.put( 6,"ABRT"); + SIGNALS.put( 8,"FPE"); + SIGNALS.put( 4,"ILL"); + SIGNALS.put( 3,"QUIT"); + SIGNALS.put(11,"SEGV"); + SIGNALS.put( 5,"TRAP"); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthBanner.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthBanner.java new file mode 100644 index 0000000..8ad8c3b --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthBanner.java @@ -0,0 +1,60 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketUserauthBanner. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthBanner.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthBanner +{ + byte[] payload; + + String message; + String language; + + public PacketUserauthBanner(String message, String language) + { + this.message = message; + this.language = language; + } + + public String getBanner() + { + return message; + } + + public PacketUserauthBanner(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_USERAUTH_BANNER) + throw new IOException("This is not a SSH_MSG_USERAUTH_BANNER! (" + packet_type + ")"); + + message = tr.readString("UTF-8"); + language = tr.readString(); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_USERAUTH_BANNER); + tw.writeString(message); + tw.writeString(language); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthFailure.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthFailure.java new file mode 100644 index 0000000..fd4a726 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthFailure.java @@ -0,0 +1,53 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketUserauthBanner. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthFailure +{ + byte[] payload; + + String[] authThatCanContinue; + boolean partialSuccess; + + public PacketUserauthFailure(String[] authThatCanContinue, boolean partialSuccess) + { + this.authThatCanContinue = authThatCanContinue; + this.partialSuccess = partialSuccess; + } + + public PacketUserauthFailure(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_USERAUTH_FAILURE) + throw new IOException("This is not a SSH_MSG_USERAUTH_FAILURE! (" + packet_type + ")"); + + authThatCanContinue = tr.readNameList(); + partialSuccess = tr.readBoolean(); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_USERAUTH_FAILURE packet!"); + } + + public String[] getAuthThatCanContinue() + { + return authThatCanContinue; + } + + public boolean isPartialSuccess() + { + return partialSuccess; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java new file mode 100644 index 0000000..e1606d1 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java @@ -0,0 +1,84 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketUserauthInfoRequest. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthInfoRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthInfoRequest +{ + byte[] payload; + + String name; + String instruction; + String languageTag; + int numPrompts; + + String prompt[]; + boolean echo[]; + + public PacketUserauthInfoRequest(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_USERAUTH_INFO_REQUEST) + throw new IOException("This is not a SSH_MSG_USERAUTH_INFO_REQUEST! (" + packet_type + ")"); + + name = tr.readString(); + instruction = tr.readString(); + languageTag = tr.readString(); + + numPrompts = tr.readUINT32(); + + prompt = new String[numPrompts]; + echo = new boolean[numPrompts]; + + for (int i = 0; i < numPrompts; i++) + { + prompt[i] = tr.readString(); + echo[i] = tr.readBoolean(); + } + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_USERAUTH_INFO_REQUEST packet!"); + } + + public boolean[] getEcho() + { + return echo; + } + + public String getInstruction() + { + return instruction; + } + + public String getLanguageTag() + { + return languageTag; + } + + public String getName() + { + return name; + } + + public int getNumPrompts() + { + return numPrompts; + } + + public String[] getPrompt() + { + return prompt; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java new file mode 100644 index 0000000..e8795d4 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java @@ -0,0 +1,35 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketUserauthInfoResponse. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthInfoResponse.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthInfoResponse +{ + byte[] payload; + + String[] responses; + + public PacketUserauthInfoResponse(String[] responses) + { + this.responses = responses; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_USERAUTH_INFO_RESPONSE); + tw.writeUINT32(responses.length); + for (int i = 0; i < responses.length; i++) + tw.writeString(responses[i]); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java new file mode 100644 index 0000000..83e9f49 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java @@ -0,0 +1,42 @@ + +package com.trilead.ssh2.packets; + +/** + * PacketUserauthRequestInteractive. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthRequestInteractive.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthRequestInteractive +{ + byte[] payload; + + String userName; + String serviceName; + String[] submethods; + + public PacketUserauthRequestInteractive(String serviceName, String user, String[] submethods) + { + this.serviceName = serviceName; + this.userName = user; + this.submethods = submethods; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); + tw.writeString(userName); + tw.writeString(serviceName); + tw.writeString("keyboard-interactive"); + tw.writeString(""); // draft-ietf-secsh-newmodes-04.txt says that + // the language tag should be empty. + tw.writeNameList(submethods); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestNone.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestNone.java new file mode 100644 index 0000000..d786003 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestNone.java @@ -0,0 +1,61 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketUserauthRequestPassword. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthRequestNone.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthRequestNone +{ + byte[] payload; + + String userName; + String serviceName; + + public PacketUserauthRequestNone(String serviceName, String user) + { + this.serviceName = serviceName; + this.userName = user; + } + + public PacketUserauthRequestNone(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) + throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")"); + + userName = tr.readString(); + serviceName = tr.readString(); + + String method = tr.readString(); + + if (method.equals("none") == false) + throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type none!"); + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); + tw.writeString(userName); + tw.writeString(serviceName); + tw.writeString("none"); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java new file mode 100644 index 0000000..83047dd --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java @@ -0,0 +1,67 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketUserauthRequestPassword. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthRequestPassword.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthRequestPassword +{ + byte[] payload; + + String userName; + String serviceName; + String password; + + public PacketUserauthRequestPassword(String serviceName, String user, String pass) + { + this.serviceName = serviceName; + this.userName = user; + this.password = pass; + } + + public PacketUserauthRequestPassword(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) + throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")"); + + userName = tr.readString(); + serviceName = tr.readString(); + + String method = tr.readString(); + + if (method.equals("password") == false) + throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type password!"); + + /* ... */ + + if (tr.remain() != 0) + throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); + tw.writeString(userName); + tw.writeString(serviceName); + tw.writeString("password"); + tw.writeBoolean(false); + tw.writeString(password); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java new file mode 100644 index 0000000..6462864 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java @@ -0,0 +1,65 @@ +package com.trilead.ssh2.packets; + +import java.io.IOException; + +/** + * PacketUserauthRequestPublicKey. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: PacketUserauthRequestPublicKey.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class PacketUserauthRequestPublicKey +{ + byte[] payload; + + String userName; + String serviceName; + String password; + String pkAlgoName; + byte[] pk; + byte[] sig; + + public PacketUserauthRequestPublicKey(String serviceName, String user, + String pkAlgorithmName, byte[] pk, byte[] sig) + { + this.serviceName = serviceName; + this.userName = user; + this.pkAlgoName = pkAlgorithmName; + this.pk = pk; + this.sig = sig; + } + + public PacketUserauthRequestPublicKey(byte payload[], int off, int len) throws IOException + { + this.payload = new byte[len]; + System.arraycopy(payload, off, this.payload, 0, len); + + TypesReader tr = new TypesReader(payload, off, len); + + int packet_type = tr.readByte(); + + if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) + throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + + packet_type + ")"); + + throw new IOException("Not implemented!"); + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); + tw.writeString(userName); + tw.writeString(serviceName); + tw.writeString("publickey"); + tw.writeBoolean(true); + tw.writeString(pkAlgoName); + tw.writeString(pk, 0, pk.length); + tw.writeString(sig, 0, sig.length); + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/PacketWindowChange.java b/service/src/main/java/com/trilead/ssh2/packets/PacketWindowChange.java new file mode 100644 index 0000000..8cdbcf3 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/PacketWindowChange.java @@ -0,0 +1,47 @@ +package com.trilead.ssh2.packets; + +/** + * Indicates that that size of the terminal (window) size has changed on the client side. + * + * See section 6.7 of RFC 4254. + * + * @author Kohsuke Kawaguchi + */ +public class PacketWindowChange { + byte[] payload; + + public int recipientChannelID; + public int character_width; + public int character_height; + public int pixel_width; + public int pixel_height; + + public PacketWindowChange(int recipientChannelID, + int character_width, int character_height, int pixel_width, int pixel_height) + { + this.recipientChannelID = recipientChannelID; + this.character_width = character_width; + this.character_height = character_height; + this.pixel_width = pixel_width; + this.pixel_height = pixel_height; + } + + public byte[] getPayload() + { + if (payload == null) + { + TypesWriter tw = new TypesWriter(); + tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST); + tw.writeUINT32(recipientChannelID); + tw.writeString("window-change"); + tw.writeBoolean(false); + tw.writeUINT32(character_width); + tw.writeUINT32(character_height); + tw.writeUINT32(pixel_width); + tw.writeUINT32(pixel_height); + + payload = tw.getBytes(); + } + return payload; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/Packets.java b/service/src/main/java/com/trilead/ssh2/packets/Packets.java new file mode 100644 index 0000000..6989286 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/Packets.java @@ -0,0 +1,149 @@ + +package com.trilead.ssh2.packets; + +/** + * Packets. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Packets.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class Packets +{ + public static final int SSH_MSG_DISCONNECT = 1; + public static final int SSH_MSG_IGNORE = 2; + public static final int SSH_MSG_UNIMPLEMENTED = 3; + public static final int SSH_MSG_DEBUG = 4; + public static final int SSH_MSG_SERVICE_REQUEST = 5; + public static final int SSH_MSG_SERVICE_ACCEPT = 6; + + public static final int SSH_MSG_KEXINIT = 20; + public static final int SSH_MSG_NEWKEYS = 21; + + public static final int SSH_MSG_KEXDH_INIT = 30; + public static final int SSH_MSG_KEXDH_REPLY = 31; + + public static final int SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30; + public static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34; + public static final int SSH_MSG_KEX_DH_GEX_GROUP = 31; + public static final int SSH_MSG_KEX_DH_GEX_INIT = 32; + public static final int SSH_MSG_KEX_DH_GEX_REPLY = 33; + + public static final int SSH_MSG_USERAUTH_REQUEST = 50; + public static final int SSH_MSG_USERAUTH_FAILURE = 51; + public static final int SSH_MSG_USERAUTH_SUCCESS = 52; + public static final int SSH_MSG_USERAUTH_BANNER = 53; + public static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60; + public static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61; + + public static final int SSH_MSG_GLOBAL_REQUEST = 80; + public static final int SSH_MSG_REQUEST_SUCCESS = 81; + public static final int SSH_MSG_REQUEST_FAILURE = 82; + + public static final int SSH_MSG_CHANNEL_OPEN = 90; + public static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91; + public static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92; + public static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93; + public static final int SSH_MSG_CHANNEL_DATA = 94; + public static final int SSH_MSG_CHANNEL_EXTENDED_DATA = 95; + public static final int SSH_MSG_CHANNEL_EOF = 96; + public static final int SSH_MSG_CHANNEL_CLOSE = 97; + public static final int SSH_MSG_CHANNEL_REQUEST = 98; + public static final int SSH_MSG_CHANNEL_SUCCESS = 99; + public static final int SSH_MSG_CHANNEL_FAILURE = 100; + + public static final int SSH_EXTENDED_DATA_STDERR = 1; + + public static final int SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1; + public static final int SSH_DISCONNECT_PROTOCOL_ERROR = 2; + public static final int SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3; + public static final int SSH_DISCONNECT_RESERVED = 4; + public static final int SSH_DISCONNECT_MAC_ERROR = 5; + public static final int SSH_DISCONNECT_COMPRESSION_ERROR = 6; + public static final int SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7; + public static final int SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8; + public static final int SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9; + public static final int SSH_DISCONNECT_CONNECTION_LOST = 10; + public static final int SSH_DISCONNECT_BY_APPLICATION = 11; + public static final int SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12; + public static final int SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13; + public static final int SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14; + public static final int SSH_DISCONNECT_ILLEGAL_USER_NAME = 15; + + public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1; + public static final int SSH_OPEN_CONNECT_FAILED = 2; + public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3; + public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4; + + private static final String[] reverseNames = new String[101]; + + static + { + reverseNames[1] = "SSH_MSG_DISCONNECT"; + reverseNames[2] = "SSH_MSG_IGNORE"; + reverseNames[3] = "SSH_MSG_UNIMPLEMENTED"; + reverseNames[4] = "SSH_MSG_DEBUG"; + reverseNames[5] = "SSH_MSG_SERVICE_REQUEST"; + reverseNames[6] = "SSH_MSG_SERVICE_ACCEPT"; + + reverseNames[20] = "SSH_MSG_KEXINIT"; + reverseNames[21] = "SSH_MSG_NEWKEYS"; + + reverseNames[30] = "SSH_MSG_KEXDH_INIT"; + reverseNames[31] = "SSH_MSG_KEXDH_REPLY/SSH_MSG_KEX_DH_GEX_GROUP"; + reverseNames[32] = "SSH_MSG_KEX_DH_GEX_INIT"; + reverseNames[33] = "SSH_MSG_KEX_DH_GEX_REPLY"; + reverseNames[34] = "SSH_MSG_KEX_DH_GEX_REQUEST"; + + reverseNames[50] = "SSH_MSG_USERAUTH_REQUEST"; + reverseNames[51] = "SSH_MSG_USERAUTH_FAILURE"; + reverseNames[52] = "SSH_MSG_USERAUTH_SUCCESS"; + reverseNames[53] = "SSH_MSG_USERAUTH_BANNER"; + + reverseNames[60] = "SSH_MSG_USERAUTH_INFO_REQUEST"; + reverseNames[61] = "SSH_MSG_USERAUTH_INFO_RESPONSE"; + + reverseNames[80] = "SSH_MSG_GLOBAL_REQUEST"; + reverseNames[81] = "SSH_MSG_REQUEST_SUCCESS"; + reverseNames[82] = "SSH_MSG_REQUEST_FAILURE"; + + reverseNames[90] = "SSH_MSG_CHANNEL_OPEN"; + reverseNames[91] = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION"; + reverseNames[92] = "SSH_MSG_CHANNEL_OPEN_FAILURE"; + reverseNames[93] = "SSH_MSG_CHANNEL_WINDOW_ADJUST"; + reverseNames[94] = "SSH_MSG_CHANNEL_DATA"; + reverseNames[95] = "SSH_MSG_CHANNEL_EXTENDED_DATA"; + reverseNames[96] = "SSH_MSG_CHANNEL_EOF"; + reverseNames[97] = "SSH_MSG_CHANNEL_CLOSE"; + reverseNames[98] = "SSH_MSG_CHANNEL_REQUEST"; + reverseNames[99] = "SSH_MSG_CHANNEL_SUCCESS"; + reverseNames[100] = "SSH_MSG_CHANNEL_FAILURE"; + } + + public static final String getMessageName(int type) + { + String res = null; + + if ((type >= 0) && (type < reverseNames.length)) + { + res = reverseNames[type]; + } + + return (res == null) ? ("UNKNOWN MSG " + type) : res; + } + + // public static final void debug(String tag, byte[] msg) + // { + // System.err.println(tag + " Type: " + msg[0] + ", LEN: " + msg.length); + // + // for (int i = 0; i < msg.length; i++) + // { + // if (((msg[i] >= 'a') && (msg[i] <= 'z')) || ((msg[i] >= 'A') && (msg[i] <= 'Z')) + // || ((msg[i] >= '0') && (msg[i] <= '9')) || (msg[i] == ' ')) + // System.err.print((char) msg[i]); + // else + // System.err.print("."); + // } + // System.err.println(); + // System.err.flush(); + // } +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/TypesReader.java b/service/src/main/java/com/trilead/ssh2/packets/TypesReader.java new file mode 100644 index 0000000..28f5363 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/TypesReader.java @@ -0,0 +1,177 @@ + +package com.trilead.ssh2.packets; + +import java.io.IOException; +import java.math.BigInteger; + +import com.trilead.ssh2.util.Tokenizer; + + +/** + * TypesReader. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: TypesReader.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class TypesReader +{ + byte[] arr; + int pos = 0; + int max = 0; + + public TypesReader(byte[] arr) + { + this.arr = arr; + pos = 0; + max = arr.length; + } + + public TypesReader(byte[] arr, int off) + { + this.arr = arr; + this.pos = off; + this.max = arr.length; + + if ((pos < 0) || (pos > arr.length)) + throw new IllegalArgumentException("Illegal offset."); + } + + public TypesReader(byte[] arr, int off, int len) + { + this.arr = arr; + this.pos = off; + this.max = off + len; + + if ((pos < 0) || (pos > arr.length)) + throw new IllegalArgumentException("Illegal offset."); + + if ((max < 0) || (max > arr.length)) + throw new IllegalArgumentException("Illegal length."); + } + + public int readByte() throws IOException + { + if (pos >= max) + throw new IOException("Packet too short."); + + return (arr[pos++] & 0xff); + } + + public byte[] readBytes(int len) throws IOException + { + if ((pos + len) > max) + throw new IOException("Packet too short."); + + byte[] res = new byte[len]; + + System.arraycopy(arr, pos, res, 0, len); + pos += len; + + return res; + } + + public void readBytes(byte[] dst, int off, int len) throws IOException + { + if ((pos + len) > max) + throw new IOException("Packet too short."); + + System.arraycopy(arr, pos, dst, off, len); + pos += len; + } + + public boolean readBoolean() throws IOException + { + if (pos >= max) + throw new IOException("Packet too short."); + + return (arr[pos++] != 0); + } + + public int readUINT32() throws IOException + { + if ((pos + 4) > max) + throw new IOException("Packet too short."); + + return ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8) + | (arr[pos++] & 0xff); + } + + public long readUINT64() throws IOException + { + if ((pos + 8) > max) + throw new IOException("Packet too short."); + + long high = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8) + | (arr[pos++] & 0xff); /* sign extension may take place - will be shifted away =) */ + + long low = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8) + | (arr[pos++] & 0xff); /* sign extension may take place - handle below */ + + return (high << 32) | (low & 0xffffffffl); /* see Java language spec (15.22.1, 5.6.2) */ + } + + public BigInteger readMPINT() throws IOException + { + BigInteger b; + + byte raw[] = readByteString(); + + if (raw.length == 0) + b = BigInteger.ZERO; + else + b = new BigInteger(raw); + + return b; + } + + public byte[] readByteString() throws IOException + { + int len = readUINT32(); + + if ((len + pos) > max) + throw new IOException("Malformed SSH byte string."); + + byte[] res = new byte[len]; + System.arraycopy(arr, pos, res, 0, len); + pos += len; + return res; + } + + public String readString(String charsetName) throws IOException + { + int len = readUINT32(); + + if ((len + pos) > max) + throw new IOException("Malformed SSH string."); + + String res = (charsetName == null) ? new String(arr, pos, len) : new String(arr, pos, len, charsetName); + pos += len; + + return res; + } + + public String readString() throws IOException + { + int len = readUINT32(); + + if ((len + pos) > max) + throw new IOException("Malformed SSH string."); + + String res = new String(arr, pos, len, "ISO-8859-1"); + + pos += len; + + return res; + } + + public String[] readNameList() throws IOException + { + return Tokenizer.parseTokens(readString(), ','); + } + + public int remain() + { + return max - pos; + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/packets/TypesWriter.java b/service/src/main/java/com/trilead/ssh2/packets/TypesWriter.java new file mode 100644 index 0000000..9f7336b --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/packets/TypesWriter.java @@ -0,0 +1,169 @@ + +package com.trilead.ssh2.packets; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; + +/** + * TypesWriter. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: TypesWriter.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class TypesWriter +{ + byte arr[]; + int pos; + + public TypesWriter() + { + arr = new byte[256]; + pos = 0; + } + + private void resize(int len) + { + byte new_arr[] = new byte[len]; + System.arraycopy(arr, 0, new_arr, 0, arr.length); + arr = new_arr; + } + + public int length() + { + return pos; + } + + public byte[] getBytes() + { + byte[] dst = new byte[pos]; + System.arraycopy(arr, 0, dst, 0, pos); + return dst; + } + + public void getBytes(byte dst[]) + { + System.arraycopy(arr, 0, dst, 0, pos); + } + + public void writeUINT32(int val, int off) + { + if ((off + 4) > arr.length) + resize(off + 32); + + arr[off++] = (byte) (val >> 24); + arr[off++] = (byte) (val >> 16); + arr[off++] = (byte) (val >> 8); + arr[off++] = (byte) val; + } + + public void writeUINT32(int val) + { + writeUINT32(val, pos); + pos += 4; + } + + public void writeUINT64(long val) + { + if ((pos + 8) > arr.length) + resize(arr.length + 32); + + arr[pos++] = (byte) (val >> 56); + arr[pos++] = (byte) (val >> 48); + arr[pos++] = (byte) (val >> 40); + arr[pos++] = (byte) (val >> 32); + arr[pos++] = (byte) (val >> 24); + arr[pos++] = (byte) (val >> 16); + arr[pos++] = (byte) (val >> 8); + arr[pos++] = (byte) val; + } + + public void writeBoolean(boolean v) + { + if ((pos + 1) > arr.length) + resize(arr.length + 32); + + arr[pos++] = v ? (byte) 1 : (byte) 0; + } + + public void writeByte(int v, int off) + { + if ((off + 1) > arr.length) + resize(off + 32); + + arr[off] = (byte) v; + } + + public void writeByte(int v) + { + writeByte(v, pos); + pos++; + } + + public void writeMPInt(BigInteger b) + { + byte raw[] = b.toByteArray(); + + if ((raw.length == 1) && (raw[0] == 0)) + writeUINT32(0); /* String with zero bytes of data */ + else + writeString(raw, 0, raw.length); + } + + public void writeBytes(byte[] buff) + { + writeBytes(buff, 0, buff.length); + } + + public void writeBytes(byte[] buff, int off, int len) + { + if ((pos + len) > arr.length) + resize(arr.length + len + 32); + + System.arraycopy(buff, off, arr, pos, len); + pos += len; + } + + public void writeString(byte[] buff, int off, int len) + { + writeUINT32(len); + writeBytes(buff, off, len); + } + + public void writeString(String v) + { + byte[] b; + + try + { + /* All Java JVMs must support ISO-8859-1 */ + b = v.getBytes("ISO-8859-1"); + } + catch (UnsupportedEncodingException ignore) + { + b = v.getBytes(); + } + + writeUINT32(b.length); + writeBytes(b, 0, b.length); + } + + public void writeString(String v, String charsetName) throws UnsupportedEncodingException + { + byte[] b = (charsetName == null) ? v.getBytes() : v.getBytes(charsetName); + + writeUINT32(b.length); + writeBytes(b, 0, b.length); + } + + public void writeNameList(String v[]) + { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < v.length; i++) + { + if (i > 0) + sb.append(','); + sb.append(v[i]); + } + writeString(sb.toString()); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/AttrTextHints.java b/service/src/main/java/com/trilead/ssh2/sftp/AttrTextHints.java new file mode 100644 index 0000000..19f0525 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/AttrTextHints.java @@ -0,0 +1,38 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * Values for the 'text-hint' field in the SFTP ATTRS data type. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: AttrTextHints.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class AttrTextHints +{ + /** + * The server knows the file is a text file, and should be opened + * using the SSH_FXF_ACCESS_TEXT_MODE flag. + */ + public static final int SSH_FILEXFER_ATTR_KNOWN_TEXT = 0x00; + + /** + * The server has applied a heuristic or other mechanism and + * believes that the file should be opened with the + * SSH_FXF_ACCESS_TEXT_MODE flag. + */ + public static final int SSH_FILEXFER_ATTR_GUESSED_TEXT = 0x01; + + /** + * The server knows the file has binary content. + */ + public static final int SSH_FILEXFER_ATTR_KNOWN_BINARY = 0x02; + + /** + * The server has applied a heuristic or other mechanism and + * believes has binary content, and should not be opened with the + * SSH_FXF_ACCESS_TEXT_MODE flag. + */ + public static final int SSH_FILEXFER_ATTR_GUESSED_BINARY = 0x03; +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/AttribBits.java b/service/src/main/java/com/trilead/ssh2/sftp/AttribBits.java new file mode 100644 index 0000000..b143613 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/AttribBits.java @@ -0,0 +1,129 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * SFTP Attribute Bits for the "attrib-bits" and "attrib-bits-valid" fields + * of the SFTP ATTR data type. + *

+ * Yes, these are the "attrib-bits", even though they have "_FLAGS_" in + * their name. Don't ask - I did not invent it. + *

+ * "These fields, taken together, reflect various attributes of the file + * or directory, on the server. Bits not set in 'attrib-bits-valid' MUST be + * ignored in the 'attrib-bits' field. This allows both the server and the + * client to communicate only the bits it knows about without inadvertently + * twiddling bits they don't understand." + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: AttribBits.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class AttribBits +{ + /** + * Advisory, read-only bit. This bit is not part of the access + * control information on the file, but is rather an advisory field + * indicating that the file should not be written. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001; + + /** + * The file is part of the operating system. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002; + + /** + * File SHOULD NOT be shown to user unless specifically requested. + * For example, most UNIX systems SHOULD set this bit if the filename + * begins with a 'period'. This bit may be read-only (see section 5.4 of + * the SFTP standard draft). Most UNIX systems will not allow this to be + * changed. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004; + + /** + * This attribute applies only to directories. This attribute is + * always read-only, and cannot be modified. This attribute means + * that files and directory names in this directory should be compared + * without regard to case. + *

+ * It is recommended that where possible, the server's filesystem be + * allowed to do comparisons. For example, if a client wished to prompt + * a user before overwriting a file, it should not compare the new name + * with the previously retrieved list of names in the directory. Rather, + * it should first try to create the new file by specifying + * SSH_FXF_CREATE_NEW flag. Then, if this fails and returns + * SSH_FX_FILE_ALREADY_EXISTS, it should prompt the user and then retry + * the create specifying SSH_FXF_CREATE_TRUNCATE. + *

+ * Unless otherwise specified, filenames are assumed to be case sensitive. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008; + + /** + * The file should be included in backup / archive operations. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010; + + /** + * The file is stored on disk using file-system level transparent + * encryption. This flag does not affect the file data on the wire + * (for either READ or WRITE requests.) + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020; + + /** + * The file is stored on disk using file-system level transparent + * compression. This flag does not affect the file data on the wire. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040; + + /** + * The file is a sparse file; this means that file blocks that have + * not been explicitly written are not stored on disk. For example, if + * a client writes a buffer at 10 M from the beginning of the file, + * the blocks between the previous EOF marker and the 10 M offset would + * not consume physical disk space. + *

+ * Some servers may store all files as sparse files, in which case + * this bit will be unconditionally set. Other servers may not have + * a mechanism for determining if the file is sparse, and so the file + * MAY be stored sparse even if this flag is not set. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080; + + /** + * Opening the file without either the SSH_FXF_ACCESS_APPEND_DATA or + * the SSH_FXF_ACCESS_APPEND_DATA_ATOMIC flag (see section 8.1.1.3 + * of the SFTP standard draft) MUST result in an + * SSH_FX_INVALID_PARAMETER error. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100; + + /** + * The file cannot be deleted or renamed, no hard link can be created + * to this file, and no data can be written to the file. + *

+ * This bit implies a stronger level of protection than + * SSH_FILEXFER_ATTR_FLAGS_READONLY, the file permission mask or ACLs. + * Typically even the superuser cannot write to immutable files, and + * only the superuser can set or remove the bit. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200; + + /** + * When the file is modified, the changes are written synchronously + * to the disk. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400; + + /** + * The server MAY include this bit in a directory listing or realpath + * response. It indicates there was a failure in the translation to UTF-8. + * If this flag is included, the server SHOULD also include the + * UNTRANSLATED_NAME attribute. + */ + public static final int SSH_FILEXFER_ATTR_FLAGS_TRANSLATION_ERR = 0x00000800; + +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/AttribFlags.java b/service/src/main/java/com/trilead/ssh2/sftp/AttribFlags.java new file mode 100644 index 0000000..ea27871 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/AttribFlags.java @@ -0,0 +1,112 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * Attribute Flags. The 'valid-attribute-flags' field in + * the SFTP ATTRS data type specifies which of the fields are actually present. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: AttribFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class AttribFlags +{ + /** + * Indicates that the 'allocation-size' field is present. + */ + public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001; + + /** Protocol version 6: + * 0x00000002 was used in a previous version of this protocol. + * It is now a reserved value and MUST NOT appear in the mask. + * Some future version of this protocol may reuse this value. + */ + public static final int SSH_FILEXFER_ATTR_V3_UIDGID = 0x00000002; + + /** + * Indicates that the 'permissions' field is present. + */ + public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004; + + /** + * Indicates that the 'atime' and 'mtime' field are present + * (protocol v3). + */ + public static final int SSH_FILEXFER_ATTR_V3_ACMODTIME = 0x00000008; + + /** + * Indicates that the 'atime' field is present. + */ + public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008; + + /** + * Indicates that the 'createtime' field is present. + */ + public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010; + + /** + * Indicates that the 'mtime' field is present. + */ + public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020; + + /** + * Indicates that the 'acl' field is present. + */ + public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040; + + /** + * Indicates that the 'owner' and 'group' fields are present. + */ + public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080; + + /** + * Indicates that additionally to the 'atime', 'createtime', + * 'mtime' and 'ctime' fields (if present), there is also + * 'atime-nseconds', 'createtime-nseconds', 'mtime-nseconds' + * and 'ctime-nseconds'. + */ + public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100; + + /** + * Indicates that the 'attrib-bits' and 'attrib-bits-valid' + * fields are present. + */ + public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200; + + /** + * Indicates that the 'allocation-size' field is present. + */ + public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400; + + /** + * Indicates that the 'text-hint' field is present. + */ + public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800; + + /** + * Indicates that the 'mime-type' field is present. + */ + public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000; + + /** + * Indicates that the 'link-count' field is present. + */ + public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000; + + /** + * Indicates that the 'untranslated-name' field is present. + */ + public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000; + + /** + * Indicates that the 'ctime' field is present. + */ + public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000; + + /** + * Indicates that the 'extended-count' field (and probablby some + * 'extensions') is present. + */ + public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000; +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/AttribPermissions.java b/service/src/main/java/com/trilead/ssh2/sftp/AttribPermissions.java new file mode 100644 index 0000000..558aa6f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/AttribPermissions.java @@ -0,0 +1,32 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * Permissions for the 'permissions' field in the SFTP ATTRS data type. + *

+ * "The 'permissions' field contains a bit mask specifying file permissions. + * These permissions correspond to the st_mode field of the stat structure + * defined by POSIX [IEEE.1003-1.1996]." + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: AttribPermissions.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class AttribPermissions +{ + /* Octal values! */ + + public static final int S_IRUSR = 0400; + public static final int S_IWUSR = 0200; + public static final int S_IXUSR = 0100; + public static final int S_IRGRP = 0040; + public static final int S_IWGRP = 0020; + public static final int S_IXGRP = 0010; + public static final int S_IROTH = 0004; + public static final int S_IWOTH = 0002; + public static final int S_IXOTH = 0001; + public static final int S_ISUID = 04000; + public static final int S_ISGID = 02000; + public static final int S_ISVTX = 01000; +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/AttribTypes.java b/service/src/main/java/com/trilead/ssh2/sftp/AttribTypes.java new file mode 100644 index 0000000..e2f4169 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/AttribTypes.java @@ -0,0 +1,28 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * Types for the 'type' field in the SFTP ATTRS data type. + *

+ * "On a POSIX system, these values would be derived from the mode field + * of the stat structure. SPECIAL should be used for files that are of + * a known type which cannot be expressed in the protocol. UNKNOWN + * should be used if the type is not known." + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: AttribTypes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class AttribTypes +{ + public static final int SSH_FILEXFER_TYPE_REGULAR = 1; + public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2; + public static final int SSH_FILEXFER_TYPE_SYMLINK = 3; + public static final int SSH_FILEXFER_TYPE_SPECIAL = 4; + public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5; + public static final int SSH_FILEXFER_TYPE_SOCKET = 6; + public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7; + public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8; + public static final int SSH_FILEXFER_TYPE_FIFO = 9; +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/ErrorCodes.java b/service/src/main/java/com/trilead/ssh2/sftp/ErrorCodes.java new file mode 100644 index 0000000..7317a00 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/ErrorCodes.java @@ -0,0 +1,104 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * SFTP Error Codes + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ErrorCodes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class ErrorCodes +{ + public static final int SSH_FX_OK = 0; + public static final int SSH_FX_EOF = 1; + public static final int SSH_FX_NO_SUCH_FILE = 2; + public static final int SSH_FX_PERMISSION_DENIED = 3; + public static final int SSH_FX_FAILURE = 4; + public static final int SSH_FX_BAD_MESSAGE = 5; + public static final int SSH_FX_NO_CONNECTION = 6; + public static final int SSH_FX_CONNECTION_LOST = 7; + public static final int SSH_FX_OP_UNSUPPORTED = 8; + public static final int SSH_FX_INVALID_HANDLE = 9; + public static final int SSH_FX_NO_SUCH_PATH = 10; + public static final int SSH_FX_FILE_ALREADY_EXISTS = 11; + public static final int SSH_FX_WRITE_PROTECT = 12; + public static final int SSH_FX_NO_MEDIA = 13; + public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14; + public static final int SSH_FX_QUOTA_EXCEEDED = 15; + public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16; + public static final int SSH_FX_LOCK_CONFLICT = 17; + public static final int SSH_FX_DIR_NOT_EMPTY = 18; + public static final int SSH_FX_NOT_A_DIRECTORY = 19; + public static final int SSH_FX_INVALID_FILENAME = 20; + public static final int SSH_FX_LINK_LOOP = 21; + public static final int SSH_FX_CANNOT_DELETE = 22; + public static final int SSH_FX_INVALID_PARAMETER = 23; + public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24; + public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25; + public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26; + public static final int SSH_FX_DELETE_PENDING = 27; + public static final int SSH_FX_FILE_CORRUPT = 28; + public static final int SSH_FX_OWNER_INVALID = 29; + public static final int SSH_FX_GROUP_INVALID = 30; + public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31; + + private static final String[][] messages = { + + { "SSH_FX_OK", "Indicates successful completion of the operation." }, + { "SSH_FX_EOF", + "An attempt to read past the end-of-file was made; or, there are no more directory entries to return." }, + { "SSH_FX_NO_SUCH_FILE", "A reference was made to a file which does not exist." }, + { "SSH_FX_PERMISSION_DENIED", "The user does not have sufficient permissions to perform the operation." }, + { "SSH_FX_FAILURE", "An error occurred, but no specific error code exists to describe the failure." }, + { "SSH_FX_BAD_MESSAGE", "A badly formatted packet or other SFTP protocol incompatibility was detected." }, + { "SSH_FX_NO_CONNECTION", "There is no connection to the server." }, + { "SSH_FX_CONNECTION_LOST", "The connection to the server was lost." }, + { "SSH_FX_OP_UNSUPPORTED", + "An attempted operation could not be completed by the server because the server does not support the operation." }, + { "SSH_FX_INVALID_HANDLE", "The handle value was invalid." }, + { "SSH_FX_NO_SUCH_PATH", "The file path does not exist or is invalid." }, + { "SSH_FX_FILE_ALREADY_EXISTS", "The file already exists." }, + { "SSH_FX_WRITE_PROTECT", "The file is on read-only media, or the media is write protected." }, + { "SSH_FX_NO_MEDIA", + "The requested operation cannot be completed because there is no media available in the drive." }, + { "SSH_FX_NO_SPACE_ON_FILESYSTEM", + "The requested operation cannot be completed because there is insufficient free space on the filesystem." }, + { "SSH_FX_QUOTA_EXCEEDED", + "The operation cannot be completed because it would exceed the user's storage quota." }, + { + "SSH_FX_UNKNOWN_PRINCIPAL", + "A principal referenced by the request (either the 'owner', 'group', or 'who' field of an ACL), was unknown. The error specific data contains the problematic names." }, + { "SSH_FX_LOCK_CONFLICT", "The file could not be opened because it is locked by another process." }, + { "SSH_FX_DIR_NOT_EMPTY", "The directory is not empty." }, + { "SSH_FX_NOT_A_DIRECTORY", "The specified file is not a directory." }, + { "SSH_FX_INVALID_FILENAME", "The filename is not valid." }, + { "SSH_FX_LINK_LOOP", + "Too many symbolic links encountered or, an SSH_FXF_NOFOLLOW open encountered a symbolic link as the final component." }, + { "SSH_FX_CANNOT_DELETE", + "The file cannot be deleted. One possible reason is that the advisory READONLY attribute-bit is set." }, + { "SSH_FX_INVALID_PARAMETER", + "One of the parameters was out of range, or the parameters specified cannot be used together." }, + { "SSH_FX_FILE_IS_A_DIRECTORY", + "The specified file was a directory in a context where a directory cannot be used." }, + { "SSH_FX_BYTE_RANGE_LOCK_CONFLICT", + " A read or write operation failed because another process's mandatory byte-range lock overlaps with the request." }, + { "SSH_FX_BYTE_RANGE_LOCK_REFUSED", "A request for a byte range lock was refused." }, + { "SSH_FX_DELETE_PENDING", "An operation was attempted on a file for which a delete operation is pending." }, + { "SSH_FX_FILE_CORRUPT", "The file is corrupt; an filesystem integrity check should be run." }, + { "SSH_FX_OWNER_INVALID", "The principal specified can not be assigned as an owner of a file." }, + { "SSH_FX_GROUP_INVALID", "The principal specified can not be assigned as the primary group of a file." }, + { "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK", + "The requested operation could not be completed because the specifed byte range lock has not been granted." }, + + }; + + public static final String[] getDescription(int errorCode) + { + if ((errorCode < 0) || (errorCode >= messages.length)) + return null; + + return messages[errorCode]; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/OpenFlags.java b/service/src/main/java/com/trilead/ssh2/sftp/OpenFlags.java new file mode 100644 index 0000000..d3018ba --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/OpenFlags.java @@ -0,0 +1,223 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * SFTP Open Flags. + * + * The following table is provided to assist in mapping POSIX semantics + * to equivalent SFTP file open parameters: + *

+ * TODO: This comment should be moved to the open method. + * + *

    + *
  • O_RDONLY + *
    • desired-access = READ_DATA | READ_ATTRIBUTES
    + *
  • + *
+ *
    + *
  • O_WRONLY + *
    • desired-access = WRITE_DATA | WRITE_ATTRIBUTES
    + *
  • + *
+ *
    + *
  • O_RDWR + *
    • desired-access = READ_DATA | READ_ATTRIBUTES | WRITE_DATA | WRITE_ATTRIBUTES
    + *
  • + *
+ *
    + *
  • O_APPEND + *
      + *
    • desired-access = WRITE_DATA | WRITE_ATTRIBUTES | APPEND_DATA
    • + *
    • flags = SSH_FXF_ACCESS_APPEND_DATA and or SSH_FXF_ACCESS_APPEND_DATA_ATOMIC
    • + *
    + *
  • + *
+ *
    + *
  • O_CREAT + *
      + *
    • flags = SSH_FXF_OPEN_OR_CREATE
    • + *
    + *
  • + *
+ *
    + *
  • O_TRUNC + *
      + *
    • flags = SSH_FXF_TRUNCATE_EXISTING
    • + *
    + *
  • + *
+ *
    + *
  • O_TRUNC|O_CREATE + *
      + *
    • flags = SSH_FXF_CREATE_TRUNCATE
    • + *
    + *
  • + *
+ * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: OpenFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + */ +public class OpenFlags +{ + /** + * Disposition is a 3 bit field that controls how the file is opened. + * The server MUST support these bits (possible enumaration values: + * SSH_FXF_CREATE_NEW, SSH_FXF_CREATE_TRUNCATE, SSH_FXF_OPEN_EXISTING, + * SSH_FXF_OPEN_OR_CREATE or SSH_FXF_TRUNCATE_EXISTING). + */ + public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007; + + /** + * A new file is created; if the file already exists, the server + * MUST return status SSH_FX_FILE_ALREADY_EXISTS. + */ + public static final int SSH_FXF_CREATE_NEW = 0x00000000; + + /** + * A new file is created; if the file already exists, it is opened + * and truncated. + */ + public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001; + + /** + * An existing file is opened. If the file does not exist, the + * server MUST return SSH_FX_NO_SUCH_FILE. If a directory in the + * path does not exist, the server SHOULD return + * SSH_FX_NO_SUCH_PATH. It is also acceptable if the server + * returns SSH_FX_NO_SUCH_FILE in this case. + */ + public static final int SSH_FXF_OPEN_EXISTING = 0x00000002; + + /** + * If the file exists, it is opened. If the file does not exist, + * it is created. + */ + public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003; + + /** + * An existing file is opened and truncated. If the file does not + * exist, the server MUST return the same error codes as defined + * for SSH_FXF_OPEN_EXISTING. + */ + public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004; + + /** + * Data is always written at the end of the file. The offset field + * of the SSH_FXP_WRITE requests are ignored. + *

+ * Data is not required to be appended atomically. This means that + * if multiple writers attempt to append data simultaneously, data + * from the first may be lost. However, data MAY be appended + * atomically. + */ + public static final int SSH_FXF_ACCESS_APPEND_DATA = 0x00000008; + + /** + * Data is always written at the end of the file. The offset field + * of the SSH_FXP_WRITE requests are ignored. + *

+ * Data MUST be written atomically so that there is no chance that + * multiple appenders can collide and result in data being lost. + *

+ * If both append flags are specified, the server SHOULD use atomic + * append if it is available, but SHOULD use non-atomic appends + * otherwise. The server SHOULD NOT fail the request in this case. + */ + public static final int SSH_FXF_ACCESS_APPEND_DATA_ATOMIC = 0x00000010; + + /** + * Indicates that the server should treat the file as text and + * convert it to the canonical newline convention in use. + * (See Determining Server Newline Convention in section 5.3 in the + * SFTP standard draft). + *

+ * When a file is opened with this flag, the offset field in the read + * and write functions is ignored. + *

+ * Servers MUST process multiple, parallel reads and writes correctly + * in this mode. Naturally, it is permissible for them to do this by + * serializing the requests. + *

+ * Clients SHOULD use the SSH_FXF_ACCESS_APPEND_DATA flag to append + * data to a text file rather then using write with a calculated offset. + */ + public static final int SSH_FXF_ACCESS_TEXT_MODE = 0x00000020; + + /** + * The server MUST guarantee that no other handle has been opened + * with ACE4_READ_DATA access, and that no other handle will be + * opened with ACE4_READ_DATA access until the client closes the + * handle. (This MUST apply both to other clients and to other + * processes on the server.) + *

+ * If there is a conflicting lock the server MUST return + * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking + * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED. + *

+ * Other handles MAY be opened for ACE4_WRITE_DATA or any other + * combination of accesses, as long as ACE4_READ_DATA is not included + * in the mask. + */ + public static final int SSH_FXF_ACCESS_BLOCK_READ = 0x00000040; + + /** + * The server MUST guarantee that no other handle has been opened + * with ACE4_WRITE_DATA or ACE4_APPEND_DATA access, and that no other + * handle will be opened with ACE4_WRITE_DATA or ACE4_APPEND_DATA + * access until the client closes the handle. (This MUST apply both + * to other clients and to other processes on the server.) + *

+ * If there is a conflicting lock the server MUST return + * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking + * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED. + *

+ * Other handles MAY be opened for ACE4_READ_DATA or any other + * combination of accesses, as long as neither ACE4_WRITE_DATA nor + * ACE4_APPEND_DATA are included in the mask. + */ + public static final int SSH_FXF_ACCESS_BLOCK_WRITE = 0x00000080; + + /** + * The server MUST guarantee that no other handle has been opened + * with ACE4_DELETE access, opened with the + * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that no other handle + * will be opened with ACE4_DELETE access or with the + * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that the file itself + * is not deleted in any other way until the client closes the handle. + *

+ * If there is a conflicting lock the server MUST return + * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking + * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED. + */ + public static final int SSH_FXF_ACCESS_BLOCK_DELETE = 0x00000100; + + /** + * If this bit is set, the above BLOCK modes are advisory. In advisory + * mode, only other accesses that specify a BLOCK mode need be + * considered when determining whether the BLOCK can be granted, + * and the server need not prevent I/O operations that violate the + * block mode. + *

+ * The server MAY perform mandatory locking even if the BLOCK_ADVISORY + * bit is set. + */ + public static final int SSH_FXF_ACCESS_BLOCK_ADVISORY = 0x00000200; + + /** + * If the final component of the path is a symlink, then the open + * MUST fail, and the error SSH_FX_LINK_LOOP MUST be returned. + */ + public static final int SSH_FXF_ACCESS_NOFOLLOW = 0x00000400; + + /** + * The file should be deleted when the last handle to it is closed. + * (The last handle may not be an sftp-handle.) This MAY be emulated + * by a server if the OS doesn't support it by deleting the file when + * this handle is closed. + *

+ * It is implementation specific whether the directory entry is + * removed immediately or when the handle is closed. + */ + public static final int SSH_FXF_ACCESS_DELETE_ON_CLOSE = 0x00000800; +} diff --git a/service/src/main/java/com/trilead/ssh2/sftp/Packet.java b/service/src/main/java/com/trilead/ssh2/sftp/Packet.java new file mode 100644 index 0000000..444af90 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/sftp/Packet.java @@ -0,0 +1,43 @@ + +package com.trilead.ssh2.sftp; + +/** + * + * SFTP Paket Types + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Packet.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $ + * + */ +public class Packet +{ + public static final int SSH_FXP_INIT = 1; + public static final int SSH_FXP_VERSION = 2; + public static final int SSH_FXP_OPEN = 3; + public static final int SSH_FXP_CLOSE = 4; + public static final int SSH_FXP_READ = 5; + public static final int SSH_FXP_WRITE = 6; + public static final int SSH_FXP_LSTAT = 7; + public static final int SSH_FXP_FSTAT = 8; + public static final int SSH_FXP_SETSTAT = 9; + public static final int SSH_FXP_FSETSTAT = 10; + public static final int SSH_FXP_OPENDIR = 11; + public static final int SSH_FXP_READDIR = 12; + public static final int SSH_FXP_REMOVE = 13; + public static final int SSH_FXP_MKDIR = 14; + public static final int SSH_FXP_RMDIR = 15; + public static final int SSH_FXP_REALPATH = 16; + public static final int SSH_FXP_STAT = 17; + public static final int SSH_FXP_RENAME = 18; + public static final int SSH_FXP_READLINK = 19; + public static final int SSH_FXP_SYMLINK = 20; + + public static final int SSH_FXP_STATUS = 101; + public static final int SSH_FXP_HANDLE = 102; + public static final int SSH_FXP_DATA = 103; + public static final int SSH_FXP_NAME = 104; + public static final int SSH_FXP_ATTRS = 105; + + public static final int SSH_FXP_EXTENDED = 200; + public static final int SSH_FXP_EXTENDED_REPLY = 201; +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/DSAKeyAlgorithm.java b/service/src/main/java/com/trilead/ssh2/signature/DSAKeyAlgorithm.java new file mode 100644 index 0000000..8beb352 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/DSAKeyAlgorithm.java @@ -0,0 +1,253 @@ +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.IOWarningException; +import com.trilead.ssh2.crypto.CertificateDecoder; +import com.trilead.ssh2.crypto.PEMStructure; +import com.trilead.ssh2.crypto.SimpleDERReader; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import java.util.Arrays; +import java.util.List; + +/** + * @author Michael Clarke + */ +public class DSAKeyAlgorithm extends KeyAlgorithm { + + public DSAKeyAlgorithm() { + super("SHA1WithDSA", "ssh-dss", DSAPrivateKey.class); + } + + @Override + public byte[] encodeSignature(byte[] signature) throws IOException { + TypesWriter tw = new TypesWriter(); + + tw.writeString(getKeyFormat()); + + int index = 3; + int len = signature[index++] & 0xff; + byte[] r = new byte[len]; + System.arraycopy(signature, index, r, 0, r.length); + + index += len + 1; + len = signature[index++] & 0xff; + byte[] s = new byte[len]; + System.arraycopy(signature, index, s, 0, s.length); + + + byte[] a40 = new byte[40]; + + /* Patch (unsigned) r and s into the target array. */ + + int r_copylen = (r.length < 20) ? r.length : 20; + int s_copylen = (s.length < 20) ? s.length : 20; + + System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen); + System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen); + + tw.writeString(a40, 0, 40); + + return tw.getBytes(); + } + + @Override + public byte[] decodeSignature(byte[] encodedSignature) throws IOException { + byte[] rsArray; + + if (encodedSignature.length == 40) + { + /* OK, another broken SSH server. */ + rsArray = encodedSignature; + } + else + { + /* Hopefully a server obeying the standard... */ + TypesReader tr = new TypesReader(encodedSignature); + + String sig_format = tr.readString(); + + if (!sig_format.equals(getKeyFormat())) + throw new IOException("Peer sent wrong signature format"); + + rsArray = tr.readByteString(); + + if (rsArray.length != 40) + throw new IOException("Peer sent corrupt signature"); + + if (tr.remain() != 0) + throw new IOException("Padding in DSA signature!"); + } + + /* Remember, s and r are unsigned ints. */ + + int i = 0; + + if (rsArray[0] == 0 && rsArray[1] == 0 && rsArray[2] == 0) { + int j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000) + | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff); + i += j; + j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000) + | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff); + byte[] tmp = new byte[j]; + System.arraycopy(rsArray, i, tmp, 0, j); + rsArray = tmp; + } + + int first = ((rsArray[0] & 0x80) != 0 ? 1 : 0); + int second = ((rsArray[20] & 0x80) != 0 ? 1 : 0); + int length = rsArray.length + 6 + first + second; + byte[] tmp = new byte[length]; + + tmp[0] = (byte) 0x30; + + if (rsArray.length != 40) { + throw new IOException("Peer sent corrupt signature"); + } + + tmp[1] = (byte) 0x2c; + tmp[1] += first; + tmp[1] += second; + + tmp[2] = (byte) 0x02; + tmp[3] = (byte) 0x14; + tmp[3] += first; + + System.arraycopy(rsArray, 0, tmp, 4 + first, 20); + + tmp[4 + tmp[3]] = (byte) 0x02; + tmp[5 + tmp[3]] = (byte) 0x14; + tmp[5 + tmp[3]] += second; + + System.arraycopy(rsArray, 20, tmp, 6 + tmp[3] + second, 20); + + return tmp; + } + + @Override + public byte[] encodePublicKey(DSAPublicKey publicKey) throws IOException { + DSAParams params = publicKey.getParams(); + + TypesWriter tw = new TypesWriter(); + + tw.writeString(getKeyFormat()); + tw.writeMPInt(params.getP()); + tw.writeMPInt(params.getQ()); + tw.writeMPInt(params.getG()); + tw.writeMPInt(publicKey.getY()); + + return tw.getBytes(); + } + + @Override + public DSAPublicKey decodePublicKey(byte[] encodedPublicKey) throws IOException { + TypesReader tr = new TypesReader(encodedPublicKey); + + String key_format = tr.readString(); + if (!key_format.equals(getKeyFormat())) { + throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting " + getKeyFormat()); + } + + final BigInteger p = tr.readMPINT(); + final BigInteger q = tr.readMPINT(); + final BigInteger g = tr.readMPINT(); + final BigInteger y = tr.readMPINT(); + + if (tr.remain() != 0) { + throw new IOException("Padding in DSA public key!"); + } + + try { + KeyFactory generator = KeyFactory.getInstance("DSA"); + return (DSAPublicKey) generator.generatePublic(new DSAPublicKeySpec(y, p, q, g)); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not generate DSA Key", ex); + } + } + + @Override + public List getCertificateDecoders() { + return Arrays.asList(new DsaCertificateDecoder(), new OpenSshCertificateDecoder(getKeyFormat()) { + @Override + KeyPair generateKeyPair(TypesReader typesReader) throws GeneralSecurityException, IOException { + BigInteger p = typesReader.readMPINT(); + BigInteger q = typesReader.readMPINT(); + BigInteger g = typesReader.readMPINT(); + BigInteger y = typesReader.readMPINT(); + BigInteger x = typesReader.readMPINT(); + + DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g); + DSAPublicKeySpec publicKeySpec = new DSAPublicKeySpec(y, p, q, g); + + + KeyFactory factory = KeyFactory.getInstance("DSA"); + PrivateKey privateKey = factory.generatePrivate(privateKeySpec); + PublicKey publicKey = factory.generatePublic(publicKeySpec); + return new KeyPair(publicKey, privateKey); + } + }); + } + + private static class DsaCertificateDecoder extends CertificateDecoder { + + @Override + public String getStartLine() { + return "-----BEGIN DSA PRIVATE KEY-----"; + } + + @Override + public String getEndLine() { + return "-----END DSA PRIVATE KEY-----"; + } + + @Override + protected KeyPair createKeyPair(PEMStructure pemStructure) throws IOException { + SimpleDERReader dr = new SimpleDERReader(pemStructure.getData()); + + byte[] seq = dr.readSequenceAsByteArray(); + + if (dr.available() != 0) + throw new IOException("Padding in DSA PRIVATE KEY DER stream."); + + dr.resetInput(seq); + + BigInteger version = dr.readInt(); + + if (version.compareTo(BigInteger.ZERO) != 0) + throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream."); + + BigInteger p = dr.readInt(); + BigInteger q = dr.readInt(); + BigInteger g = dr.readInt(); + BigInteger y = dr.readInt(); + BigInteger x = dr.readInt(); + + if (dr.available() != 0) + throw new IOException("Padding in DSA PRIVATE KEY DER stream."); + + try { + DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g); + DSAPublicKeySpec publicKeySpec = new DSAPublicKeySpec(y, p, q, g); + KeyFactory factory = KeyFactory.getInstance("DSA"); + PrivateKey privateKey = factory.generatePrivate(privateKeySpec); + PublicKey publicKey = factory.generatePublic(publicKeySpec); + return new KeyPair(publicKey, privateKey); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not decode DSA key pair"); + } + + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/DSAPrivateKey.java b/service/src/main/java/com/trilead/ssh2/signature/DSAPrivateKey.java new file mode 100644 index 0000000..60c7e0b --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/DSAPrivateKey.java @@ -0,0 +1,61 @@ +package com.trilead.ssh2.signature; + +import java.math.BigInteger; + +/** + * DSAPrivateKey. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated use {@link java.security.interfaces.DSAPrivateKey} + * @see java.security.interfaces.DSAPrivateKey + */ +@Deprecated +public class DSAPrivateKey +{ + private BigInteger p; + private BigInteger q; + private BigInteger g; + private BigInteger x; + private BigInteger y; + + public DSAPrivateKey(BigInteger p, BigInteger q, BigInteger g, + BigInteger y, BigInteger x) + { + this.p = p; + this.q = q; + this.g = g; + this.y = y; + this.x = x; + } + + public BigInteger getP() + { + return p; + } + + public BigInteger getQ() + { + return q; + } + + public BigInteger getG() + { + return g; + } + + public BigInteger getY() + { + return y; + } + + public BigInteger getX() + { + return x; + } + + public DSAPublicKey getPublicKey() + { + return new DSAPublicKey(p, q, g, y); + } +} \ No newline at end of file diff --git a/service/src/main/java/com/trilead/ssh2/signature/DSAPublicKey.java b/service/src/main/java/com/trilead/ssh2/signature/DSAPublicKey.java new file mode 100644 index 0000000..f9de761 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/DSAPublicKey.java @@ -0,0 +1,47 @@ +package com.trilead.ssh2.signature; + +import java.math.BigInteger; + +/** + * DSAPublicKey. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated use {@link java.security.interfaces.DSAPublicKey} + * @see java.security.interfaces.DSAPublicKey + */ +public class DSAPublicKey +{ + private BigInteger p; + private BigInteger q; + private BigInteger g; + private BigInteger y; + + public DSAPublicKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y) + { + this.p = p; + this.q = q; + this.g = g; + this.y = y; + } + + public BigInteger getP() + { + return p; + } + + public BigInteger getQ() + { + return q; + } + + public BigInteger getG() + { + return g; + } + + public BigInteger getY() + { + return y; + } +} \ No newline at end of file diff --git a/service/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java b/service/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java new file mode 100644 index 0000000..b227b35 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java @@ -0,0 +1,220 @@ + +package com.trilead.ssh2.signature; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; + +import com.trilead.ssh2.IOWarningException; +import com.trilead.ssh2.crypto.digest.SHA1; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + + +/** + * DSASHA1Verify. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + * @deprecated user {@link DSAKeyAlgorithm} + */ +@Deprecated +public class DSASHA1Verify +{ + private static final Logger log = Logger.getLogger(DSASHA1Verify.class); + + + @Deprecated + public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException { + final TypesReader tr = new TypesReader(key); + + final String key_format = tr.readString(); + if (!key_format.equals("ssh-dss")) { + throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-dss"); + } + + final BigInteger p = tr.readMPINT(); + final BigInteger q = tr.readMPINT(); + final BigInteger g = tr.readMPINT(); + final BigInteger y = tr.readMPINT(); + + if (tr.remain() != 0) { + throw new IOException("Padding in DSA public key!"); + } + + return new DSAPublicKey(p, q, g, y); + } + + @Deprecated + public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-dss"); + tw.writeMPInt(pk.getP()); + tw.writeMPInt(pk.getQ()); + tw.writeMPInt(pk.getG()); + tw.writeMPInt(pk.getY()); + + return tw.getBytes(); + } + + @Deprecated + public static byte[] encodeSSHDSASignature(DSASignature ds) + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-dss"); + + byte[] r = ds.getR().toByteArray(); + byte[] s = ds.getS().toByteArray(); + + byte[] a40 = new byte[40]; + + /* Patch (unsigned) r and s into the target array. */ + + int r_copylen = (r.length < 20) ? r.length : 20; + int s_copylen = (s.length < 20) ? s.length : 20; + + System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen); + System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen); + + tw.writeString(a40, 0, 40); + + return tw.getBytes(); + } + + @Deprecated + public static DSASignature decodeSSHDSASignature(byte[] sig) throws IOException + { + byte[] rsArray = null; + + if (sig.length == 40) + { + /* OK, another broken SSH server. */ + rsArray = sig; + } + else + { + /* Hopefully a server obeing the standard... */ + TypesReader tr = new TypesReader(sig); + + String sig_format = tr.readString(); + + if (!sig_format.equals("ssh-dss")) + throw new IOException("Peer sent wrong signature format"); + + rsArray = tr.readByteString(); + + if (rsArray.length != 40) + throw new IOException("Peer sent corrupt signature"); + + if (tr.remain() != 0) + throw new IOException("Padding in DSA signature!"); + } + + /* Remember, s and r are unsigned ints. */ + + byte[] tmp = new byte[20]; + + System.arraycopy(rsArray, 0, tmp, 0, 20); + BigInteger r = new BigInteger(1, tmp); + + System.arraycopy(rsArray, 20, tmp, 0, 20); + BigInteger s = new BigInteger(1, tmp); + + if (log.isEnabled()) + { + log.log(30, "decoded ssh-dss signature: first bytes r(" + ((rsArray[0]) & 0xff) + "), s(" + + ((rsArray[20]) & 0xff) + ")"); + } + + return new DSASignature(r, s); + } + + @Deprecated + public static boolean verifySignature(byte[] message, DSASignature ds, DSAPublicKey dpk) throws IOException + { + /* Inspired by Bouncycastle's DSASigner class */ + + SHA1 md = new SHA1(); + md.update(message); + byte[] sha_message = new byte[md.getDigestLength()]; + md.digest(sha_message); + + BigInteger m = new BigInteger(1, sha_message); + + BigInteger r = ds.getR(); + BigInteger s = ds.getS(); + + BigInteger g = dpk.getG(); + BigInteger p = dpk.getP(); + BigInteger q = dpk.getQ(); + BigInteger y = dpk.getY(); + + BigInteger zero = BigInteger.ZERO; + + if (log.isEnabled()) + { + log.log(60, "ssh-dss signature: m: " + m.toString(16)); + log.log(60, "ssh-dss signature: r: " + r.toString(16)); + log.log(60, "ssh-dss signature: s: " + s.toString(16)); + log.log(60, "ssh-dss signature: g: " + g.toString(16)); + log.log(60, "ssh-dss signature: p: " + p.toString(16)); + log.log(60, "ssh-dss signature: q: " + q.toString(16)); + log.log(60, "ssh-dss signature: y: " + y.toString(16)); + } + + if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0) + { + log.log(20, "ssh-dss signature: zero.compareTo(r) >= 0 || q.compareTo(r) <= 0"); + return false; + } + + if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0) + { + log.log(20, "ssh-dss signature: zero.compareTo(s) >= 0 || q.compareTo(s) <= 0"); + return false; + } + + BigInteger w = s.modInverse(q); + + BigInteger u1 = m.multiply(w).mod(q); + BigInteger u2 = r.multiply(w).mod(q); + + u1 = g.modPow(u1, p); + u2 = y.modPow(u2, p); + + BigInteger v = u1.multiply(u2).mod(p).mod(q); + + return v.equals(r); + } + + @Deprecated + public static DSASignature generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd) + { + SHA1 md = new SHA1(); + md.update(message); + byte[] sha_message = new byte[md.getDigestLength()]; + md.digest(sha_message); + + BigInteger m = new BigInteger(1, sha_message); + BigInteger k; + int qBitLength = pk.getQ().bitLength(); + + do + { + k = new BigInteger(qBitLength, rnd); + } + while (k.compareTo(pk.getQ()) >= 0); + + BigInteger r = pk.getG().modPow(k, pk.getP()).mod(pk.getQ()); + + k = k.modInverse(pk.getQ()).multiply(m.add((pk).getX().multiply(r))); + + BigInteger s = k.mod(pk.getQ()); + + return new DSASignature(r, s); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/DSASignature.java b/service/src/main/java/com/trilead/ssh2/signature/DSASignature.java new file mode 100644 index 0000000..5f58477 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/DSASignature.java @@ -0,0 +1,33 @@ +package com.trilead.ssh2.signature; + +import java.math.BigInteger; + +/** + * DSASignature. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated signatures are now stored in ray byte[] form. + */ +@Deprecated +public class DSASignature +{ + private BigInteger r; + private BigInteger s; + + public DSASignature(BigInteger r, BigInteger s) + { + this.r = r; + this.s = s; + } + + public BigInteger getR() + { + return r; + } + + public BigInteger getS() + { + return s; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/ECDSAKeyAlgorithm.java b/service/src/main/java/com/trilead/ssh2/signature/ECDSAKeyAlgorithm.java new file mode 100644 index 0000000..101e396 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/ECDSAKeyAlgorithm.java @@ -0,0 +1,447 @@ +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.crypto.CertificateDecoder; +import com.trilead.ssh2.crypto.PEMStructure; +import com.trilead.ssh2.crypto.SimpleDERReader; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; +import java.security.spec.KeySpec; +import java.util.Arrays; +import java.util.List; + +/** + * @author Michael Clarke + */ +public abstract class ECDSAKeyAlgorithm extends KeyAlgorithm { + + private static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-"; + + private static final byte ANS1_INTEGER = 0x02; + private static final byte ANS1_ZERO = 0x00; + + private final String curveName; + private final ECParameterSpec ecParameterSpec; + + private ECDSAKeyAlgorithm(String signatureAlgorithm, String curveName, ECParameterSpec ecParameterSpec) { + super(signatureAlgorithm, ECDSA_SHA2_PREFIX + curveName, ECPrivateKey.class); + this.curveName = curveName; + this.ecParameterSpec = ecParameterSpec; + } + + /*package*/ String getCurveName() { + return curveName; + } + + /*package*/ ECParameterSpec getEcParameterSpec() { + return ecParameterSpec; + } + + @Override + public ECPublicKey decodePublicKey(byte[] key) throws IOException + { + TypesReader tr = new TypesReader(key); + + String keyFormat = tr.readString(); + if (!keyFormat.equals(getKeyFormat())) { + throw new IOException("Invalid key format"); + } + + /* + We need to read the next block, but don't do anything with it: + the curve name is part of the key format which we've already checked above + */ + /*String curveName = */tr.readString(); + byte[] groupBytes = tr.readByteString(); + + if (tr.remain() != 0) { + throw new IOException("Unexpected adding in ECDSA public key"); + } + + ECParameterSpec params = getEcParameterSpec(); + ECPoint group = decodePoint(groupBytes, params.getCurve()); + if (null == group) { + throw new IOException("Invalid ECDSA group"); + } + + + try { + KeySpec keySpec = new ECPublicKeySpec(group, params); + KeyFactory kf = KeyFactory.getInstance("EC"); + return (ECPublicKey) kf.generatePublic(keySpec); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not decode ECDSA key", ex); + } + } + + @Override + public byte[] encodePublicKey(ECPublicKey key) throws IOException { + + byte[] encodedPoint = encodePoint(key.getW(), key.getParams().getCurve()); + + TypesWriter tw = new TypesWriter(); + tw.writeString(getKeyFormat()); + tw.writeString(getCurveName()); + tw.writeString(encodedPoint, 0, encodedPoint.length); + + return tw.getBytes(); + } + + + @Override + public byte[] decodeSignature(byte[] encodedSignature) throws IOException { + + TypesReader typesReader = new TypesReader(encodedSignature); + + String signatureFormat = typesReader.readString(); + if (!signatureFormat.equals(getKeyFormat())) { + throw new IOException("Unsupported signature format: " + signatureFormat); + } + + byte[] rAndS = typesReader.readByteString(); + + if (typesReader.remain() != 0) { + throw new IOException("Unexpected padding in ECDSA signature"); + } + + TypesReader rsReader = new TypesReader(rAndS); + byte[] r = rsReader.readMPINT().toByteArray(); + byte[] s = rsReader.readMPINT().toByteArray(); + + int rLength = r.length; + int sLength = s.length; + + if ((r[0] & 0x80) != 0) { + rLength++; + } + + if ((s[0] & 0x80) != 0) { + sLength++; + } + + int totalLength = 6 + rLength + sLength; + ByteArrayOutputStream os = new ByteArrayOutputStream(totalLength); + + os.write(0x30); + + writeLength(totalLength - 2, os); + + os.write(ANS1_INTEGER); + writeLength(rLength, os); + if (rLength != r.length) { + os.write(ANS1_ZERO); + } + os.write(r); + + os.write(ANS1_INTEGER); + writeLength(sLength, os); + if (sLength != s.length) { + os.write(ANS1_ZERO); + } + os.write(s); + + return os.toByteArray(); + } + + private static void writeLength(int length, OutputStream os) throws IOException { + if (length <= 0x7F) { + os.write(length); + return; + } + + int numOctets = 0; + int lenCopy = length; + while (lenCopy != 0) { + lenCopy >>>= 8; + numOctets++; + } + + os.write(0x80 | numOctets); + + for (int i = (numOctets - 1) * 8; i >= 0; i -= 8) { + os.write((byte) (length >> i)); + } + } + + @Override + public byte[] encodeSignature(byte[] sig) throws IOException { + SimpleDERReader reader = new SimpleDERReader(new SimpleDERReader(sig).readSequenceAsByteArray()); + BigInteger r = reader.readInt(); + BigInteger s = reader.readInt(); + + TypesWriter rAndSWriter = new TypesWriter(); + rAndSWriter.writeMPInt(r); + rAndSWriter.writeMPInt(s); + + byte[] encoded = rAndSWriter.getBytes(); + + TypesWriter typesWriter = new TypesWriter(); + typesWriter.writeString(getKeyFormat()); + typesWriter.writeString(encoded, 0, encoded.length); + return typesWriter.getBytes(); + } + + @Override + public boolean supportsKey(PrivateKey originalKey) { + if (!(originalKey instanceof ECPrivateKey)) { + return false; + } + ECPrivateKey key = (ECPrivateKey) originalKey; + return super.supportsKey(key) && key.getParams().getCurve().getField().getFieldSize() == getEcParameterSpec().getCurve().getField().getFieldSize(); + } + + private static ECPoint decodePoint(byte[] encodedPoint, EllipticCurve curve) { + int elementSize = (curve.getField().getFieldSize() + 7) / 8; + if (encodedPoint.length != 2 * elementSize + 1 || encodedPoint[0] != 0x04 || encodedPoint.length == 0) { + return null; + } + + byte[] xPoint = new byte[elementSize]; + System.arraycopy(encodedPoint, 1, xPoint, 0, elementSize); + byte[] yPoint = new byte[elementSize]; + System.arraycopy(encodedPoint, 1 + elementSize, yPoint, 0, elementSize); + + return new ECPoint(new BigInteger(1, xPoint), new BigInteger(1, yPoint)); + } + + private static byte[] encodePoint(ECPoint group, EllipticCurve curve) { + int elementSize = (curve.getField().getFieldSize() + 7) / 8; + byte[] encodedPoint = new byte[2 * elementSize + 1]; + + encodedPoint[0] = 0x04; + + byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray()); + System.arraycopy(affineX, 0, encodedPoint, 1 + elementSize - affineX.length, affineX.length); + byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray()); + System.arraycopy(affineY, 0, encodedPoint, 1 + elementSize + elementSize - affineY.length, affineY.length); + + return encodedPoint; + } + + private static byte[] removeLeadingZeroes(byte[] input) { + if (input[0] != ANS1_ZERO) { + return input; + } + + int pos = 1; + while (pos < input.length - 1 && input[pos] == ANS1_ZERO) { + pos++; + } + + byte[] output = new byte[input.length - pos]; + System.arraycopy(input, pos, output, 0, output.length); + return output; + } + + + public static class ECDSASha2Nistp256 extends ECDSAKeyAlgorithm { + + public ECDSASha2Nistp256() { + super("SHA256withECDSA", "nistp256", + new ECParameterSpec( + new EllipticCurve( + new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)), + new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16) + ), + new ECPoint( + new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16), + new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16) + ), + new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16), + 1) + ); + } + + @Override + public List getCertificateDecoders() { + return Arrays.asList(new EcdsaCertificateDecoder("1.2.840.10045.3.1.7", getEcParameterSpec()), + new OpenSshEcdsaCertificateDecoder(getKeyFormat(), getCurveName(), getEcParameterSpec())); + } + } + + public static class ECDSASha2Nistp384 extends ECDSAKeyAlgorithm { + + public ECDSASha2Nistp384() { + super("SHA384withECDSA", "nistp384", + new ECParameterSpec( + new EllipticCurve( + new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)), + new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16), + new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16) + ), + new ECPoint( + new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16), + new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16) + ), + new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16), + 1) + ); + } + + @Override + public List getCertificateDecoders() { + return Arrays.asList(new EcdsaCertificateDecoder("1.3.132.0.34", getEcParameterSpec()), + new OpenSshEcdsaCertificateDecoder(getKeyFormat(), getCurveName(), getEcParameterSpec())); + } + } + + public static class ECDSASha2Nistp521 extends ECDSAKeyAlgorithm { + + public ECDSASha2Nistp521() { + super("SHA512withECDSA", "nistp521", + new ECParameterSpec( + new EllipticCurve( + new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)), + new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16), + new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16) + ), + new ECPoint( + new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16), + new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16) + ), + new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16), + 1) + ); + } + + @Override + public List getCertificateDecoders() { + return Arrays.asList(new EcdsaCertificateDecoder("1.3.132.0.35", getEcParameterSpec()), + new OpenSshEcdsaCertificateDecoder(getKeyFormat(), getCurveName(), getEcParameterSpec())); + } + + } + + + private static class EcdsaCertificateDecoder extends CertificateDecoder { + + private final String oid; + private final ECParameterSpec ecParameterSpec; + + private EcdsaCertificateDecoder(String oid, ECParameterSpec ecParameterSpec) { + super(); + this.oid = oid; + this.ecParameterSpec = ecParameterSpec; + } + + @Override + public String getStartLine() { + return "-----BEGIN EC PRIVATE KEY-----"; + } + + @Override + public String getEndLine() { + return "-----END EC PRIVATE KEY-----"; + } + + @Override + protected KeyPair createKeyPair(PEMStructure pemStructure) throws IOException { + SimpleDERReader DERderReader = new SimpleDERReader(pemStructure.getData()); + + byte[] sequence = DERderReader.readSequenceAsByteArray(); + + if (DERderReader.available() != 0) { + throw new IOException("Unexpected padding in EC private key"); + } + + SimpleDERReader sequenceReader = new SimpleDERReader(sequence); + + BigInteger version = sequenceReader.readInt(); + if ((version.compareTo(BigInteger.ONE) != 0)) { + throw new IOException("Unexpected version number in EC private key: " + version); + } + + byte[] privateBytes = sequenceReader.readOctetString(); + + String curveOid = null; + byte[] publicBytes = null; + while (sequenceReader.available() > 0) { + int type = sequenceReader.readConstructedType(); + SimpleDERReader fieldReader = sequenceReader.readConstructed(); + switch (type) { + case 0: + curveOid = fieldReader.readOid(); + break; + case 1: + publicBytes = fieldReader.readOctetString(); + break; + } + } + + if (!oid.equals(curveOid)) { + throw new IOException("Incorrect OID for current curve"); + } + + BigInteger s = new BigInteger(1, privateBytes); + byte[] publicBytesSlice = new byte[publicBytes.length - 1]; + System.arraycopy(publicBytes, 1, publicBytesSlice, 0, publicBytesSlice.length); + ECPoint w = ECDSAKeyAlgorithm.decodePoint(publicBytesSlice, ecParameterSpec.getCurve()); + + ECPrivateKeySpec privSpec = new ECPrivateKeySpec(s, ecParameterSpec); + ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, ecParameterSpec); + + try { + KeyFactory factory = KeyFactory.getInstance("EC"); + PublicKey ecPublicKey = factory.generatePublic(pubSpec); + PrivateKey ecPrivateKey = factory.generatePrivate(privSpec); + return new KeyPair(ecPublicKey, ecPrivateKey); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not generate EC key pair"); + } + } + } + + + private static class OpenSshEcdsaCertificateDecoder extends OpenSshCertificateDecoder { + + private final String curveName; + private final ECParameterSpec ecParameterSpec; + + OpenSshEcdsaCertificateDecoder(String keyAlgorithm, String curveName, ECParameterSpec ecParameterSpec) { + super(keyAlgorithm); + this.curveName = curveName; + this.ecParameterSpec = ecParameterSpec; + } + + @Override + KeyPair generateKeyPair(TypesReader tr) throws GeneralSecurityException, IOException { + String curveName = tr.readString(); + if (!curveName.equals(this.curveName)) { + throw new IOException("Incorrect curve name: " + curveName); + } + byte[] groupBytes = tr.readByteString(); + BigInteger privateKey = tr.readMPINT(); + + ECPoint group = decodePoint(groupBytes, ecParameterSpec.getCurve()); + if (null == group) { + throw new IOException("Invalid ECDSA group"); + } + + + KeySpec keySpec = new ECPublicKeySpec(group, ecParameterSpec); + ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(privateKey, ecParameterSpec); + KeyFactory kf = KeyFactory.getInstance("EC"); + return new KeyPair(kf.generatePublic(keySpec), kf.generatePrivate(privateKeySpec)); + + } + } + +} \ No newline at end of file diff --git a/service/src/main/java/com/trilead/ssh2/signature/ED25519KeyAlgorithm.java b/service/src/main/java/com/trilead/ssh2/signature/ED25519KeyAlgorithm.java new file mode 100644 index 0000000..7643f4f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/ED25519KeyAlgorithm.java @@ -0,0 +1,112 @@ +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.crypto.CertificateDecoder; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; +import net.i2p.crypto.eddsa.EdDSAPrivateKey; +import net.i2p.crypto.eddsa.EdDSAPublicKey; +import net.i2p.crypto.eddsa.EdDSASecurityProvider; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; +import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec; +import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; +import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Michael Clarke + */ +public class ED25519KeyAlgorithm extends KeyAlgorithm { + + private static final String ED25519_KEY_NAME = "ssh-ed25519"; + private static final String ED25519_CURVE_NAME = "Ed25519"; + + protected ED25519KeyAlgorithm() { + /*Whilst the signature is 'NoneWith', it actually uses a digest from the key's parameter specification + * so is really SHA512WithEdDSA, but has to be looked up using what's in the Provider implementation. + */ + super("NoneWithEdDSA", ED25519_KEY_NAME, EdDSAPrivateKey.class, new EdDSASecurityProvider()); + } + + @Override + public byte[] encodeSignature(byte[] signature) throws IOException { + TypesWriter signatureWriter = new TypesWriter(); + signatureWriter.writeString(ED25519_KEY_NAME); + signatureWriter.writeString(signature, 0, signature.length); + return signatureWriter.getBytes(); + } + + @Override + public byte[] decodeSignature(byte[] encodedSignature) throws IOException { + TypesReader typesReader = new TypesReader(encodedSignature); + + String signatureFormat = typesReader.readString(); + if (!signatureFormat.equals(ED25519_KEY_NAME)) { + throw new IOException("Invalid signature format"); + } + + byte[] signature = typesReader.readByteString(); + if (typesReader.remain() != 0) { + throw new IOException("Unexpected padding in signature"); + } + + return signature; + } + + @Override + public byte[] encodePublicKey(EdDSAPublicKey publicKey) throws IOException { + byte[] encoded = publicKey.getAbyte(); + + TypesWriter typesWriter = new TypesWriter(); + typesWriter.writeString(ED25519_KEY_NAME); + typesWriter.writeString(encoded, 0, encoded.length); + return typesWriter.getBytes(); + } + + @Override + public EdDSAPublicKey decodePublicKey(byte[] encodedPublicKey) throws IOException { + TypesReader typesReader = new TypesReader(encodedPublicKey); + + String keyFormat = typesReader.readString(); + if (!keyFormat.equals(ED25519_KEY_NAME)) { + throw new IOException("Invalid key type"); + } + + byte[] keyBytes = typesReader.readByteString(); + if (0 != typesReader.remain()) { + throw new IOException("Unexpected padding in public key"); + } + + return new EdDSAPublicKey(new EdDSAPublicKeySpec(keyBytes, EdDSANamedCurveTable.getByName(ED25519_CURVE_NAME))); + } + + @Override + public List getCertificateDecoders() { + return Collections.singletonList((CertificateDecoder) new OpenSshCertificateDecoder(ED25519KeyAlgorithm.ED25519_KEY_NAME) { + @Override + KeyPair generateKeyPair(TypesReader reader) throws GeneralSecurityException, IOException { + EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(ED25519KeyAlgorithm.ED25519_CURVE_NAME); + + byte[] publicKeyBytes = reader.readByteString(); + byte[] privateKeyBytes = reader.readByteString(); + + EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(publicKeyBytes, spec); + EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(Arrays.copyOfRange(privateKeyBytes, 0, 32), spec); + + KeyFactory factory = KeyFactory.getInstance("EdDSA", new EdDSASecurityProvider()); + PublicKey publicKey = factory.generatePublic(publicKeySpec); + PrivateKey privateKey = factory.generatePrivate(privateKeySpec); + return new KeyPair(publicKey, privateKey); + } + }); + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/KeyAlgorithm.java b/service/src/main/java/com/trilead/ssh2/signature/KeyAlgorithm.java new file mode 100644 index 0000000..158fa64 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/KeyAlgorithm.java @@ -0,0 +1,76 @@ +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.crypto.CertificateDecoder; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.util.List; + +/** + * @author Michael Clarke + */ +public abstract class KeyAlgorithm { + + private final String signatureAlgorithm; + private final String keyFormat; + private final Class keyType; + private final Provider provider; + + protected KeyAlgorithm(String signatureAlgorithm, String keyFormat, Class keyType) { + this(signatureAlgorithm, keyFormat, keyType, null); + } + + protected KeyAlgorithm(String signatureAlgorithm, String keyFormat, Class keyType, Provider provider) { + super(); + this.signatureAlgorithm = signatureAlgorithm; + this.keyFormat = keyFormat; + this.keyType = keyType; + this.provider = provider; + } + + public byte[] generateSignature(byte[] message, R pk, SecureRandom rnd) throws IOException { + try { + Signature signature = (null == provider ? Signature.getInstance(signatureAlgorithm) : Signature.getInstance(signatureAlgorithm, provider)); + signature.initSign(pk, rnd); + signature.update(message); + return signature.sign(); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not generate signature", ex); + } + } + + public boolean verifySignature(byte[] message, byte[] ds, U dpk) throws IOException { + try { + Signature signature = (null == provider ? Signature.getInstance(signatureAlgorithm) : Signature.getInstance(signatureAlgorithm, provider)); + signature.initVerify(dpk); + signature.update(message); + return signature.verify(ds); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not verify signature", ex); + } + } + + public String getKeyFormat() { + return keyFormat; + } + + public abstract byte[] encodeSignature(byte[] signature) throws IOException; + + public abstract byte[] decodeSignature(byte[] encodedSignature) throws IOException; + + public abstract byte[] encodePublicKey(U publicKey) throws IOException; + + public abstract U decodePublicKey(byte[] encodedPublicKey) throws IOException; + + public abstract List getCertificateDecoders(); + + public boolean supportsKey(PrivateKey key) { + return keyType.isAssignableFrom(key.getClass()); + } + +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/KeyAlgorithmManager.java b/service/src/main/java/com/trilead/ssh2/signature/KeyAlgorithmManager.java new file mode 100644 index 0000000..8d2d69f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/KeyAlgorithmManager.java @@ -0,0 +1,47 @@ +package com.trilead.ssh2.signature; + +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author Michael Clarke + */ +public final class KeyAlgorithmManager { + + private static final Collection> supportedAlgorithms = buildSupportAlgorithmsList(); + + private KeyAlgorithmManager() { + super(); + // static access only + } + + public static Collection> getSupportedAlgorithms() { + return supportedAlgorithms; + } + + private static Collection> buildSupportAlgorithmsList() { + List> algorithms = new ArrayList<>(); + + + try { + KeyFactory.getInstance("EC"); + algorithms.add(new ECDSAKeyAlgorithm.ECDSASha2Nistp521()); + algorithms.add(new ECDSAKeyAlgorithm.ECDSASha2Nistp384()); + algorithms.add(new ECDSAKeyAlgorithm.ECDSASha2Nistp256()); + } catch (GeneralSecurityException ex) { + // we don't use ECDSA algorithms in this case + } + + + algorithms.add(new RSAKeyAlgorithm()); + algorithms.add(new DSAKeyAlgorithm()); + + return (Collection) Collections.unmodifiableCollection(algorithms); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/OpenSshCertificateDecoder.java b/service/src/main/java/com/trilead/ssh2/signature/OpenSshCertificateDecoder.java new file mode 100644 index 0000000..bf367d5 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/OpenSshCertificateDecoder.java @@ -0,0 +1,219 @@ +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.crypto.CertificateDecoder; +import com.trilead.ssh2.crypto.PEMStructure; +import com.trilead.ssh2.crypto.cipher.BlockCipher; +import com.trilead.ssh2.crypto.cipher.BlockCipherFactory; +import com.trilead.ssh2.crypto.cipher.CBCMode; +import com.trilead.ssh2.crypto.cipher.DES; +import com.trilead.ssh2.packets.TypesReader; +import org.mindrot.jbcrypt.BCrypt; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyPair; + +/** + * An decoder that can read keys written in the 'new' OpenSSH format, generally identified with the header + * 'BEGIN OPENSSH PRIVATE KEY'. + * @author Michael Clarke + */ +abstract class OpenSshCertificateDecoder extends CertificateDecoder { + + private final String keyAlgorithm; + + OpenSshCertificateDecoder(String keyAlgorithm) { + super(); + this.keyAlgorithm = keyAlgorithm; + } + + @Override + public String getStartLine() { + return "-----BEGIN OPENSSH PRIVATE KEY-----"; + } + + @Override + public String getEndLine() { + return "-----END OPENSSH PRIVATE KEY-----"; + } + + @Override + public KeyPair createKeyPair(PEMStructure pemStructure) { + return null; + } + + @Override + public KeyPair createKeyPair(PEMStructure pemStructure, String password) throws IOException { + TypesReader pemReader = new TypesReader(pemStructure.getData()); + + byte[] header = pemReader.readBytes(15); + if (!"openssh-key-v1".equals(new String(header, StandardCharsets.UTF_8).trim())) { + throw new IOException("Could not find openssh header in key"); + } + + String cipher = pemReader.readString(); + String kdf = pemReader.readString(); + byte[] kdfOptions = pemReader.readByteString(); + int keyCount = pemReader.readUINT32(); + + // I can't actually find any test cases for multiple keys to know how they're used. OpenSSH doesn't even support + // this case, so I'm not going to look any further for now. + if (keyCount != 1) { + throw new IOException("Only single OpenSSH keys are supported"); + } + + /*byte[] publicKeys = */pemReader.readByteString(); //public keys can be parsed from a private key + byte[] privateKeys = pemReader.readByteString(); + + if ("bcrypt".equals(kdf)) { + if (password == null) { + throw new IOException("PEM is encrypted but password has not been specified"); + } + + TypesReader kdfReader = new TypesReader(kdfOptions); + byte[] salt = kdfReader.readByteString(); + int rounds = kdfReader.readUINT32(); + SshCipher sshCipher = SshCipher.getInstance(cipher); + privateKeys = decryptData(privateKeys, generateKayAndIvPbkdf2(password.getBytes(StandardCharsets.UTF_8), salt, rounds, sshCipher.getKeyLength(), sshCipher.getBlockSize()), sshCipher); + } else if (!"none".equals(cipher) || !"none".equals(kdf)) { + throw new IOException("Unexpected encryption method for key"); + } + + TypesReader privateKeyTypeReader = new TypesReader(privateKeys); + int checkNumber1 = privateKeyTypeReader.readUINT32(); + int checkNumber2 = privateKeyTypeReader.readUINT32(); + + if (checkNumber1 != checkNumber2) { + throw new IOException("Check integers didn't match"); + } + + String keyType = privateKeyTypeReader.readString(); + if (!keyType.equals(keyAlgorithm)) { + throw new IOException("Invalid key type: " + keyType); + } + + try { + KeyPair keyPair = generateKeyPair(privateKeyTypeReader); + + /*byte[] comment = */privateKeyTypeReader.readByteString(); // we don't need the key name/comment + + for (int i = 0; i < pemReader.remain(); i++) { + if (i + 1 != pemReader.readByte()) { + throw new IOException("Incorrect padding on private keys"); + } + } + + return keyPair; + } catch (GeneralSecurityException ex) { + throw new IOException("Could not create key pair", ex); + } + } + + abstract KeyPair generateKeyPair(TypesReader typesReader) throws GeneralSecurityException, IOException; + + private static byte[] decryptData(byte[] encryptedData, byte[] keyAndIv, SshCipher sshCipher) { + byte[] key = new byte[sshCipher.getKeyLength()]; + byte[] iv = new byte[sshCipher.getBlockSize()]; + + System.arraycopy(keyAndIv, 0, key, 0, key.length); + System.arraycopy(keyAndIv, key.length, iv, 0, iv.length); + + BlockCipher cipher = sshCipher.createBlockCipher(key, iv, false); + + byte[] decrypted = new byte[encryptedData.length]; + for (int i = 0; i < encryptedData.length / cipher.getBlockSize(); i++) { + cipher.transformBlock(encryptedData, i * cipher.getBlockSize(), decrypted, i * cipher.getBlockSize()); + } + + return decrypted; + + } + + private static byte[] generateKayAndIvPbkdf2(byte[] password, byte[] salt, int rounds, int keyLength, int ivLength) { + byte[] keyAndIV = new byte[keyLength + ivLength]; + new BCrypt().pbkdf(password, salt, rounds, keyAndIV); + return keyAndIV; + } + + private enum SshCipher { + + DESEDE_CBC(24, 8, "des-ede3-cbc") { + @Override + BlockCipher createBlockCipher(byte[] key, byte[] iv, boolean encrypt) { + return BlockCipherFactory.createCipher("3des-cbc", encrypt, key, iv); + } + }, + DES_CBC(8, 8, "des-cbc") { + @Override + BlockCipher createBlockCipher(byte[] key, byte[] iv, boolean encrypt) { + DES des = new DES(); + des.init(encrypt, key); + return new CBCMode(des, iv, encrypt); + } + }, + AES128_CBC(16, 16, "aes-128-cbc", "aes128-cbc") { + @Override + BlockCipher createBlockCipher(byte[] key, byte[] iv, boolean encrypt) { + return BlockCipherFactory.createCipher("aes128-cbc", encrypt, key, iv); + } + }, + AES192_CBC(24, 16, "aes-192-cbc", "aes192-cbc") { + @Override + BlockCipher createBlockCipher(byte[] key, byte[] iv, boolean encrypt) { + return BlockCipherFactory.createCipher("aes192-cbc", encrypt, key, iv); + } + }, + AES256_CBC(32, 16, "aes-256-cbc", "aes256-cbc") { + @Override + BlockCipher createBlockCipher(byte[] key, byte[] iv, boolean encrypt) { + return BlockCipherFactory.createCipher("aes256-cbc", encrypt, key, iv); + } + }, + AES256_CTR(32, 16, "aes-256-ctr", "aes256-ctr") { + @Override + BlockCipher createBlockCipher(byte[] key, byte[] iv, boolean encrypt) { + return BlockCipherFactory.createCipher("aes256-ctr", encrypt, key, iv); + } + }; + + private final String[] sshCipherNames; + private final int keyLength; + private final int blockSize; + + SshCipher(int keyLength, int blockSize, String cipherName, String... cipherAliases) { + this.keyLength = keyLength; + this.blockSize = blockSize; + String[] sshCipherNames = new String[1 + (null == cipherAliases ? 0 : cipherAliases.length)]; + sshCipherNames[0] = cipherName; + if (null != cipherAliases) { + System.arraycopy(cipherAliases, 0, sshCipherNames, 1, cipherAliases.length); + } + this.sshCipherNames = sshCipherNames; + } + + abstract BlockCipher createBlockCipher(byte[] key, byte[] iv, boolean encrypt); + + public int getBlockSize() { + return blockSize; + } + + public int getKeyLength() { + return keyLength; + } + + public static SshCipher getInstance(String cipher) { + for (SshCipher instance : values()) { + for (String name : instance.sshCipherNames) { + if (name.equalsIgnoreCase(cipher)) { + return instance; + } + } + } + throw new IllegalArgumentException("Unknown Cipher: " + cipher); + } + + } + + +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/RSAKeyAlgorithm.java b/service/src/main/java/com/trilead/ssh2/signature/RSAKeyAlgorithm.java new file mode 100644 index 0000000..771f788 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/RSAKeyAlgorithm.java @@ -0,0 +1,201 @@ +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.IOWarningException; +import com.trilead.ssh2.crypto.CertificateDecoder; +import com.trilead.ssh2.crypto.PEMStructure; +import com.trilead.ssh2.crypto.SimpleDERReader; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.RSAPrivateCrtKeySpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.Arrays; +import java.util.List; + +/** + * @author Michael Clarke + */ +public class RSAKeyAlgorithm extends KeyAlgorithm { + + public RSAKeyAlgorithm() { + super("SHA1WithRSA", "ssh-rsa", RSAPrivateKey.class); + } + + @Override + public byte[] encodeSignature(byte[] signature) throws IOException { + final TypesWriter tw = new TypesWriter(); + + tw.writeString(getKeyFormat()); + + /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string + * containing s (which is an integer, without lengths or padding, unsigned and in + * network byte order)." + */ + + + /* Remove first zero sign byte, if present */ + + if ((signature.length > 1) && (signature[0] == 0x00)) { + tw.writeString(signature, 1, signature.length - 1); + } else { + tw.writeString(signature, 0, signature.length); + } + + return tw.getBytes(); + } + + @Override + public byte[] decodeSignature(byte[] encodedSignature) throws IOException { + final TypesReader tr = new TypesReader(encodedSignature); + + final String sig_format = tr.readString(); + + if (!sig_format.equals(getKeyFormat())) { + throw new IOException("Peer sent wrong signature format"); + } + + /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string + * containing s (which is an integer, without lengths or padding, unsigned and in + * network byte order)." See also below. + */ + + final byte[] s = tr.readByteString(); + + if (s.length == 0) { + throw new IOException("Error in RSA signature, S is empty."); + } + + if (tr.remain() != 0) { + throw new IOException("Padding in RSA signature!"); + } + + return s; + } + + @Override + public byte[] encodePublicKey(RSAPublicKey publicKey) throws IOException { + final TypesWriter tw = new TypesWriter(); + + tw.writeString(getKeyFormat()); + tw.writeMPInt(publicKey.getPublicExponent()); + tw.writeMPInt(publicKey.getModulus()); + + return tw.getBytes(); + } + + @Override + public RSAPublicKey decodePublicKey(byte[] encodedPublicKey) throws IOException { + final TypesReader tr = new TypesReader(encodedPublicKey); + + final String key_format = tr.readString(); + if (!key_format.equals(getKeyFormat())) { + throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting " + getKeyFormat()); + } + + final BigInteger e = tr.readMPINT(); + final BigInteger n = tr.readMPINT(); + + if (tr.remain() != 0) { + throw new IOException("Padding in RSA public key!"); + } + + try { + final KeyFactory generator = KeyFactory.getInstance("RSA"); + return (RSAPublicKey) generator.generatePublic(new RSAPublicKeySpec(n, e)); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not generate RSA key", ex); + } + } + + @Override + public List getCertificateDecoders() { + return Arrays.asList(new RSACertificateDecoder(), new OpenSshCertificateDecoder("ssh-rsa") { + @Override + KeyPair generateKeyPair(TypesReader typesReader) throws GeneralSecurityException, IOException { + BigInteger n = typesReader.readMPINT(); + BigInteger e = typesReader.readMPINT(); + BigInteger d = typesReader.readMPINT(); + + BigInteger c = typesReader.readMPINT(); + BigInteger p = typesReader.readMPINT(); + + + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n, e); + RSAPrivateKeySpec privateKeySpec; + + if (null == p || null == c) { + privateKeySpec = new RSAPrivateKeySpec(n, d); + } else { + BigInteger q = c.modInverse(p); + BigInteger pE = d.mod(p.subtract(BigInteger.ONE)); + BigInteger qE = d.mod(q.subtract(BigInteger.ONE)); + privateKeySpec = new RSAPrivateCrtKeySpec(n, e, d, p, q, pE, qE, c); + } + + KeyFactory factory = KeyFactory.getInstance("RSA"); + return new KeyPair(factory.generatePublic(publicKeySpec), factory.generatePrivate(privateKeySpec)); + } + }); + } + + + private static class RSACertificateDecoder extends CertificateDecoder { + + @Override + public String getStartLine() { + return "-----BEGIN RSA PRIVATE KEY-----"; + } + + @Override + public String getEndLine() { + return "-----END RSA PRIVATE KEY-----"; + } + + @Override + protected KeyPair createKeyPair(PEMStructure pemStructure) throws IOException { + SimpleDERReader dr = new SimpleDERReader(pemStructure.getData()); + + byte[] seq = dr.readSequenceAsByteArray(); + + if (dr.available() != 0) + throw new IOException("Padding in RSA PRIVATE KEY DER stream."); + + dr.resetInput(seq); + + BigInteger version = dr.readInt(); + + if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0)) + throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream."); + + BigInteger n = dr.readInt(); + BigInteger e = dr.readInt(); + BigInteger d = dr.readInt(); + BigInteger p = dr.readInt(); + BigInteger q = dr.readInt(); + BigInteger pE = dr.readInt(); + BigInteger qE = dr.readInt(); + BigInteger c = dr.readInt(); + + try { + RSAPrivateKeySpec privateKeySpec = new RSAPrivateCrtKeySpec(n, e, d, p, q, pE, qE, c); + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n, e); + KeyFactory factory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = factory.generatePrivate(privateKeySpec); + PublicKey publicKey = factory.generatePublic(publicKeySpec); + return new KeyPair(publicKey, privateKey); + } catch (GeneralSecurityException ex) { + throw new IOException("Could not decode RSA Key Pair"); + } + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java b/service/src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java new file mode 100644 index 0000000..1e01db5 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java @@ -0,0 +1,65 @@ +package com.trilead.ssh2.signature; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; +import java.security.spec.RSAPublicKeySpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.InvalidKeySpecException; + +/** + * RSAPrivateKey. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated use {@link java.security.interfaces.RSAPrivateKey} + * @see java.security.interfaces.RSAPrivateKey + */ +public class RSAPrivateKey +{ + private BigInteger d; + private BigInteger e; + private BigInteger n; + + public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n) + { + this.d = d; + this.e = e; + this.n = n; + } + + public BigInteger getD() + { + return d; + } + + public BigInteger getE() + { + return e; + } + + public BigInteger getN() + { + return n; + } + + public RSAPublicKey getPublicKey() + { + return new RSAPublicKey(e, n); + } + + /** + * Converts this to a JCE API representation of the RSA key pair. + * + * @return the key pair + * @throws GeneralSecurityException the general security exception + */ + public KeyPair toJCEKeyPair() throws GeneralSecurityException { + KeyFactory kf = KeyFactory.getInstance("RSA"); + return new KeyPair( + kf.generatePublic(new RSAPublicKeySpec(getN(), getE())), + kf.generatePrivate(new RSAPrivateKeySpec(getN(), getD()))); + } +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/RSAPublicKey.java b/service/src/main/java/com/trilead/ssh2/signature/RSAPublicKey.java new file mode 100644 index 0000000..7b9a77e --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/RSAPublicKey.java @@ -0,0 +1,33 @@ +package com.trilead.ssh2.signature; + +import java.math.BigInteger; + +/** + * RSAPublicKey. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated use {@link java.security.interfaces.RSAPublicKey} + * @see java.security.interfaces.RSAPublicKey + */ +public class RSAPublicKey +{ + BigInteger e; + BigInteger n; + + public RSAPublicKey(BigInteger e, BigInteger n) + { + this.e = e; + this.n = n; + } + + public BigInteger getE() + { + return e; + } + + public BigInteger getN() + { + return n; + } +} \ No newline at end of file diff --git a/service/src/main/java/com/trilead/ssh2/signature/RSASHA1Verify.java b/service/src/main/java/com/trilead/ssh2/signature/RSASHA1Verify.java new file mode 100644 index 0000000..401e073 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/RSASHA1Verify.java @@ -0,0 +1,295 @@ + +package com.trilead.ssh2.signature; + +import java.io.IOException; +import java.math.BigInteger; + +import com.trilead.ssh2.IOWarningException; +import com.trilead.ssh2.crypto.SimpleDERReader; +import com.trilead.ssh2.crypto.digest.SHA1; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + + +/** + * RSASHA1Verify. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated Use {@link RSAKeyAlgorithm} + */ +@Deprecated +public class RSASHA1Verify +{ + private static final Logger log = Logger.getLogger(RSASHA1Verify.class); + + @Deprecated + public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException { + final TypesReader tr = new TypesReader(key); + + final String key_format = tr.readString(); + if (!key_format.equals("ssh-rsa")) { + throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-rsa"); + } + + final BigInteger e = tr.readMPINT(); + final BigInteger n = tr.readMPINT(); + + if (tr.remain() != 0) { + throw new IOException("Padding in RSA public key!"); + } + + return new RSAPublicKey(e, n); + } + + + @Deprecated + public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-rsa"); + tw.writeMPInt(pk.getE()); + tw.writeMPInt(pk.getN()); + + return tw.getBytes(); + } + + @Deprecated + public static RSASignature decodeSSHRSASignature(byte[] sig) throws IOException + { + TypesReader tr = new TypesReader(sig); + + String sig_format = tr.readString(); + + if (!sig_format.equals("ssh-rsa")) + throw new IOException("Peer sent wrong signature format"); + + /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string + * containing s (which is an integer, without lengths or padding, unsigned and in + * network byte order)." See also below. + */ + + byte[] s = tr.readByteString(); + + if (s.length == 0) + throw new IOException("Error in RSA signature, S is empty."); + + if (log.isEnabled()) + { + log.log(80, "Decoding ssh-rsa signature string (length: " + s.length + ")"); + } + + if (tr.remain() != 0) + throw new IOException("Padding in RSA signature!"); + + return new RSASignature(new BigInteger(1, s)); + } + + @Deprecated + public static byte[] encodeSSHRSASignature(RSASignature sig) throws IOException + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-rsa"); + + /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string + * containing s (which is an integer, without lengths or padding, unsigned and in + * network byte order)." + */ + + byte[] s = sig.getS().toByteArray(); + + /* Remove first zero sign byte, if present */ + + if ((s.length > 1) && (s[0] == 0x00)) + tw.writeString(s, 1, s.length - 1); + else + tw.writeString(s, 0, s.length); + + return tw.getBytes(); + } + + @Deprecated + public static RSASignature generateSignature(byte[] message, RSAPrivateKey pk) throws IOException + { + SHA1 md = new SHA1(); + md.update(message); + byte[] sha_message = new byte[md.getDigestLength()]; + md.digest(sha_message); + + byte[] der_header = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, + 0x04, 0x14 }; + + int rsa_block_len = (pk.getN().bitLength() + 7) / 8; + + int num_pad = rsa_block_len - (2 + der_header.length + sha_message.length) - 1; + + if (num_pad < 8) + throw new IOException("Cannot sign with RSA, message too long"); + + byte[] sig = new byte[der_header.length + sha_message.length + 2 + num_pad]; + + sig[0] = 0x01; + + for (int i = 0; i < num_pad; i++) + { + sig[i + 1] = (byte) 0xff; + } + + sig[num_pad + 1] = 0x00; + + System.arraycopy(der_header, 0, sig, 2 + num_pad, der_header.length); + System.arraycopy(sha_message, 0, sig, 2 + num_pad + der_header.length, sha_message.length); + + BigInteger m = new BigInteger(1, sig); + + BigInteger s = m.modPow(pk.getD(), pk.getN()); + + return new RSASignature(s); + } + + @Deprecated + public static boolean verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk) throws IOException + { + SHA1 md = new SHA1(); + md.update(message); + byte[] sha_message = new byte[md.getDigestLength()]; + md.digest(sha_message); + + BigInteger n = dpk.getN(); + BigInteger e = dpk.getE(); + BigInteger s = ds.getS(); + + if (n.compareTo(s) <= 0) + { + log.log(20, "ssh-rsa signature: n.compareTo(s) <= 0"); + return false; + } + + int rsa_block_len = (n.bitLength() + 7) / 8; + + /* And now the show begins */ + + if (rsa_block_len < 1) + { + log.log(20, "ssh-rsa signature: rsa_block_len < 1"); + return false; + } + + byte[] v = s.modPow(e, n).toByteArray(); + + int startpos = 0; + + if ((v.length > 0) && (v[0] == 0x00)) + startpos++; + + if ((v.length - startpos) != (rsa_block_len - 1)) + { + log.log(20, "ssh-rsa signature: (v.length - startpos) != (rsa_block_len - 1)"); + return false; + } + + if (v[startpos] != 0x01) + { + log.log(20, "ssh-rsa signature: v[startpos] != 0x01"); + return false; + } + + int pos = startpos + 1; + + while (true) + { + if (pos >= v.length) + { + log.log(20, "ssh-rsa signature: pos >= v.length"); + return false; + } + if (v[pos] == 0x00) + break; + if (v[pos] != (byte) 0xff) + { + log.log(20, "ssh-rsa signature: v[pos] != (byte) 0xff"); + return false; + } + pos++; + } + + int num_pad = pos - (startpos + 1); + + if (num_pad < 8) + { + log.log(20, "ssh-rsa signature: num_pad < 8"); + return false; + } + + pos++; + + if (pos >= v.length) + { + log.log(20, "ssh-rsa signature: pos >= v.length"); + return false; + } + + SimpleDERReader dr = new SimpleDERReader(v, pos, v.length - pos); + + byte[] seq = dr.readSequenceAsByteArray(); + + if (dr.available() != 0) + { + log.log(20, "ssh-rsa signature: dr.available() != 0"); + return false; + } + + dr.resetInput(seq); + + /* Read digestAlgorithm */ + + byte digestAlgorithm[] = dr.readSequenceAsByteArray(); + + /* Inspired by RFC 3347, however, ignoring the comment regarding old BER based implementations */ + + if ((digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)) + { + log.log(20, "ssh-rsa signature: (digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)"); + return false; + } + + byte[] digestAlgorithm_sha1 = new byte[] { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00 }; + + for (int i = 0; i < digestAlgorithm.length; i++) + { + if (digestAlgorithm[i] != digestAlgorithm_sha1[i]) + { + log.log(20, "ssh-rsa signature: digestAlgorithm[i] != digestAlgorithm_sha1[i]"); + return false; + } + } + + byte[] digest = dr.readOctetString(); + + if (dr.available() != 0) + { + log.log(20, "ssh-rsa signature: dr.available() != 0 (II)"); + return false; + } + + if (digest.length != sha_message.length) + { + log.log(20, "ssh-rsa signature: digest.length != sha_message.length"); + return false; + } + + for (int i = 0; i < sha_message.length; i++) + { + if (sha_message[i] != digest[i]) + { + log.log(20, "ssh-rsa signature: sha_message[i] != digest[i]"); + return false; + } + } + + return true; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/signature/RSASignature.java b/service/src/main/java/com/trilead/ssh2/signature/RSASignature.java new file mode 100644 index 0000000..348381d --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/signature/RSASignature.java @@ -0,0 +1,28 @@ + +package com.trilead.ssh2.signature; + +import java.math.BigInteger; + + +/** + * RSASignature. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + * @deprecated signatures are now stored as raw byte arrays + */ +@Deprecated +public class RSASignature +{ + BigInteger s; + + public BigInteger getS() + { + return s; + } + + public RSASignature(BigInteger s) + { + this.s = s; + } +} \ No newline at end of file diff --git a/service/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java b/service/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java new file mode 100644 index 0000000..71847a2 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java @@ -0,0 +1,125 @@ + +package com.trilead.ssh2.transport; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import com.trilead.ssh2.Connection; + +/** + * ClientServerHello. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: ClientServerHello.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class ClientServerHello +{ + String server_line; + String client_line; + + String server_versioncomment; + + public final static int readLineRN(InputStream is, byte[] buffer) throws IOException + { + int pos = 0; + boolean need10 = false; + int len = 0; + while (true) + { + int c = is.read(); + if (c == -1) + throw new IOException("Premature connection close"); + + buffer[pos++] = (byte) c; + + if (c == 13) + { + need10 = true; + continue; + } + + if (c == 10) + break; + + if (need10 == true) + throw new IOException("Malformed line sent by the server, the line does not end correctly."); + + len++; + if (pos >= buffer.length) + throw new IOException("The server sent a too long line: "+new String(buffer, "ISO-8859-1")); + } + + return len; + } + + public ClientServerHello(InputStream bi, OutputStream bo) throws IOException + { + client_line = "SSH-2.0-" + Connection.identification; + + bo.write((client_line + "\r\n").getBytes("ISO-8859-1")); + bo.flush(); + + byte[] serverVersion = new byte[512]; + + for (int i = 0; i < 50; i++) + { + int len = readLineRN(bi, serverVersion); + + server_line = new String(serverVersion, 0, len, "ISO-8859-1"); + + if (server_line.startsWith("SSH-")) + break; + } + + if (server_line.startsWith("SSH-") == false) + throw new IOException( + "Malformed server identification string. There was no line starting with 'SSH-' amongst the first 50 lines."); + + if (server_line.startsWith("SSH-1.99-")) + server_versioncomment = server_line.substring(9); + else if (server_line.startsWith("SSH-2.0-")) + server_versioncomment = server_line.substring(8); + else + throw new IOException("Server uses incompatible protocol, it is not SSH-2 compatible."); + } + + /** + * @return Returns the client_versioncomment. + */ + public byte[] getClientString() + { + byte[] result; + + try + { + result = client_line.getBytes("ISO-8859-1"); + } + catch (UnsupportedEncodingException ign) + { + result = client_line.getBytes(); + } + + return result; + } + + /** + * @return Returns the server_versioncomment. + */ + public byte[] getServerString() + { + byte[] result; + + try + { + result = server_line.getBytes("ISO-8859-1"); + } + catch (UnsupportedEncodingException ign) + { + result = server_line.getBytes(); + } + + return result; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/KexManager.java b/service/src/main/java/com/trilead/ssh2/transport/KexManager.java new file mode 100644 index 0000000..6961fe4 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/KexManager.java @@ -0,0 +1,641 @@ + +package com.trilead.ssh2.transport; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; + +import com.trilead.ssh2.ConnectionInfo; +import com.trilead.ssh2.DHGexParameters; +import com.trilead.ssh2.ServerHostKeyVerifier; +import com.trilead.ssh2.crypto.CryptoWishList; +import com.trilead.ssh2.crypto.KeyMaterial; +import com.trilead.ssh2.crypto.cipher.BlockCipher; +import com.trilead.ssh2.crypto.cipher.BlockCipherFactory; +import com.trilead.ssh2.crypto.dh.DhExchange; +import com.trilead.ssh2.crypto.dh.DhGroupExchange; +import com.trilead.ssh2.crypto.digest.MessageMac; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.PacketKexDHInit; +import com.trilead.ssh2.packets.PacketKexDHReply; +import com.trilead.ssh2.packets.PacketKexDhGexGroup; +import com.trilead.ssh2.packets.PacketKexDhGexInit; +import com.trilead.ssh2.packets.PacketKexDhGexReply; +import com.trilead.ssh2.packets.PacketKexDhGexRequest; +import com.trilead.ssh2.packets.PacketKexDhGexRequestOld; +import com.trilead.ssh2.packets.PacketKexInit; +import com.trilead.ssh2.packets.PacketNewKeys; +import com.trilead.ssh2.packets.Packets; +import com.trilead.ssh2.signature.KeyAlgorithm; +import com.trilead.ssh2.signature.KeyAlgorithmManager; + + +/** + * KexManager. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class KexManager implements MessageHandler +{ + private static final Logger log = Logger.getLogger(KexManager.class); + + private static final List DEFAULT_KEY_ALGORITHMS = buildDefaultKeyAlgorithms(); + + KexState kxs; + int kexCount = 0; + KeyMaterial km; + byte[] sessionId; + ClientServerHello csh; + + final Object accessLock = new Object(); + ConnectionInfo lastConnInfo = null; + + boolean connectionClosed = false; + + boolean ignore_next_kex_packet = false; + + final TransportManager tm; + + CryptoWishList nextKEXcryptoWishList; + DHGexParameters nextKEXdhgexParameters; + + ServerHostKeyVerifier verifier; + final String hostname; + final int port; + final SecureRandom rnd; + + public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port, + ServerHostKeyVerifier keyVerifier, SecureRandom rnd) + { + this.tm = tm; + this.csh = csh; + this.nextKEXcryptoWishList = initialCwl; + this.nextKEXdhgexParameters = new DHGexParameters(); + this.hostname = hostname; + this.port = port; + this.verifier = keyVerifier; + this.rnd = rnd; + } + + public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException + { + synchronized (accessLock) + { + while (true) + { + if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount)) + return lastConnInfo; + + if (connectionClosed) + throw new IOException("Key exchange was not finished, connection is closed.", tm.getReasonClosedCause()); + + try + { + accessLock.wait(); + } + catch (InterruptedException e) + { + throw new InterruptedIOException(); + } + } + } + } + + private String getFirstMatch(String[] client, String[] server) throws NegotiateException + { + if (client == null || server == null) + throw new IllegalArgumentException(); + + if (client.length == 0) + return null; + + for (String aClient : client) { + for (String aServer : server) { + if (aClient.equals(aServer)) + return aClient; + } + } + throw new NegotiateException(); + } + + private boolean compareFirstOfNameList(String[] a, String[] b) + { + if (a == null || b == null) + throw new IllegalArgumentException(); + + if ((a.length == 0) && (b.length == 0)) + return true; + + if ((a.length == 0) || (b.length == 0)) + return false; + + return (a[0].equals(b[0])); + } + + private boolean isGuessOK(KexParameters cpar, KexParameters spar) + { + if (cpar == null || spar == null) + throw new IllegalArgumentException(); + + if (!compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms)) + { + return false; + } + + if (!compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms)) + { + return false; + } + + /* + * We do NOT check here if the other algorithms can be agreed on, this + * is just a check if kex_algorithms and server_host_key_algorithms were + * guessed right! + */ + + return true; + } + + private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server) + { + NegotiatedParameters np = new NegotiatedParameters(); + + try + { + np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms); + + log.log(30, "kex_algo=" + np.kex_algo); + + np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms, + server.server_host_key_algorithms); + + log.log(30, "server_host_key_algo=" + np.server_host_key_algo); + + np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server, + server.encryption_algorithms_client_to_server); + np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client, + server.encryption_algorithms_server_to_client); + + log.log(30, "enc_algo_client_to_server=" + np.enc_algo_client_to_server); + log.log(30, "enc_algo_server_to_client=" + np.enc_algo_server_to_client); + + np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server, + server.mac_algorithms_client_to_server); + np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client, + server.mac_algorithms_server_to_client); + + log.log(30, "mac_algo_client_to_server=" + np.mac_algo_client_to_server); + log.log(30, "mac_algo_server_to_client=" + np.mac_algo_server_to_client); + + np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server, + server.compression_algorithms_client_to_server); + np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client, + server.compression_algorithms_server_to_client); + + log.log(30, "comp_algo_client_to_server=" + np.comp_algo_client_to_server); + log.log(30, "comp_algo_server_to_client=" + np.comp_algo_server_to_client); + + } + catch (NegotiateException e) + { + return null; + } + + try + { + np.lang_client_to_server = getFirstMatch(client.languages_client_to_server, + server.languages_client_to_server); + } + catch (NegotiateException e1) + { + np.lang_client_to_server = null; + } + + try + { + np.lang_server_to_client = getFirstMatch(client.languages_server_to_client, + server.languages_server_to_client); + } + catch (NegotiateException e2) + { + np.lang_server_to_client = null; + } + + if (isGuessOK(client, server)) + np.guessOK = true; + + return np; + } + + public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException + { + nextKEXcryptoWishList = cwl; + nextKEXdhgexParameters = dhgex; + + if (kxs == null) + { + kxs = new KexState(); + + kxs.dhgexParameters = nextKEXdhgexParameters; + PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd); + kxs.localKEX = kp; + tm.sendKexMessage(kp.getPayload()); + } + } + + private boolean establishKeyMaterial() + { + try + { + int mac_cs_key_len = MessageMac.getKeyLength(kxs.np.mac_algo_client_to_server); + int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server); + int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server); + + int mac_sc_key_len = MessageMac.getKeyLength(kxs.np.mac_algo_server_to_client); + int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client); + int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client); + + km = KeyMaterial.create(kxs.getHashAlgorithm(), kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len, + enc_sc_key_len, enc_sc_block_len, mac_sc_key_len); + } + catch (IllegalArgumentException e) + { + return false; + } + return true; + } + + private void finishKex() throws IOException + { + if (sessionId == null) + sessionId = kxs.H; + + establishKeyMaterial(); + + /* Tell the other side that we start using the new material */ + + PacketNewKeys ign = new PacketNewKeys(); + tm.sendKexMessage(ign.getPayload()); + + BlockCipher cbc; + MessageMac mac; + + try + { + cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server, + km.initial_iv_client_to_server); + + mac = new MessageMac(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server); + + } + catch (IllegalArgumentException e1) + { + throw new IOException("Fatal error during MAC startup!"); + } + + tm.changeSendCipher(cbc, mac); + tm.kexFinished(); + } + + public static String[] getDefaultServerHostkeyAlgorithmList() + { + return DEFAULT_KEY_ALGORITHMS.toArray(new String[DEFAULT_KEY_ALGORITHMS.size()]); + } + + private static List buildDefaultKeyAlgorithms() { + List algorithms = new ArrayList<>(); + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + algorithms.add(algorithms.size(), algorithm.getKeyFormat()); + } + return algorithms; + } + + public static void checkServerHostkeyAlgorithmsList(String[] algos) + { + for (String algo : algos) { + boolean matched = false; + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + if (algorithm.getKeyFormat().equals(algo)) { + matched = true; + break; + } + } + if (!matched) { + throw new IllegalArgumentException("Unknown server host key algorithm '" + algo + "'"); + } + } + } + + public static String[] getDefaultKexAlgorithmList() + { + return new String[] { "diffie-hellman-group-exchange-sha256", "diffie-hellman-group-exchange-sha1", + "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1" }; + } + + public static void checkKexAlgorithmList(String[] algos) + { + for (String algo : algos) { + if ("diffie-hellman-group-exchange-sha1".equals(algo)) + continue; + + if ("diffie-hellman-group14-sha1".equals(algo)) + continue; + + if ("diffie-hellman-group1-sha1".equals(algo)) + continue; + + if ("diffie-hellman-group-exchange-sha256".equals(algo)) + continue; + + throw new IllegalArgumentException("Unknown kex algorithm '" + algo + "'"); + } + } + + private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException + { + for (KeyAlgorithm algorithm : KeyAlgorithmManager.getSupportedAlgorithms()) { + if (algorithm.getKeyFormat().equals(kxs.np.server_host_key_algo)) { + PublicKey publicKey = algorithm.decodePublicKey(hostkey); + byte[] signature = algorithm.decodeSignature(sig); + return algorithm.verifySignature(kxs.H, signature, publicKey); + } + } + throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'"); + } + + public synchronized void handleMessage(byte[] msg, int msglen) throws IOException + { + PacketKexInit kip; + + if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT)) + throw new IOException("Unexpected KEX message (type " + msg[0] + ")"); + + if (ignore_next_kex_packet) + { + ignore_next_kex_packet = false; + return; + } + + if (msg[0] == Packets.SSH_MSG_KEXINIT) + { + if ((kxs != null) && (kxs.state != 0)) + throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!"); + + if (kxs == null) + { + /* + * Ah, OK, peer wants to do KEX. Let's be nice and play + * together. + */ + kxs = new KexState(); + kxs.dhgexParameters = nextKEXdhgexParameters; + kip = new PacketKexInit(nextKEXcryptoWishList, rnd); + kxs.localKEX = kip; + tm.sendKexMessage(kip.getPayload()); + } + + kip = new PacketKexInit(msg, 0, msglen); + kxs.remoteKEX = kip; + + kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters()); + + if (kxs.np == null) + throw new IOException("Cannot negotiate, proposals do not match."); + + if (kxs.remoteKEX.isFirst_kex_packet_follows() && !kxs.np.guessOK) + { + /* + * Guess was wrong, we need to ignore the next kex packet. + */ + + ignore_next_kex_packet = true; + } + + if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1") + || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) + { + if (kxs.dhgexParameters.getMin_group_len() == 0) + { + PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters); + tm.sendKexMessage(dhgexreq.getPayload()); + + } + else + { + PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters); + tm.sendKexMessage(dhgexreq.getPayload()); + } + kxs.state = 1; + + if (kxs.np.kex_algo.endsWith("sha1")) { + kxs.setHashAlgorithm("SHA1"); + } else { + kxs.setHashAlgorithm("SHA-256"); + } + + return; + } + + if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1") + || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")) + { + kxs.dhx = new DhExchange("SHA1"); + + if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")) + kxs.dhx.init(1, rnd); + else + kxs.dhx.init(14, rnd); + + PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE()); + tm.sendKexMessage(kp.getPayload()); + kxs.state = 1; + kxs.setHashAlgorithm(kxs.dhx.getHashAlgorithm()); + return; + } + + throw new IllegalStateException("Unkown KEX method!"); + } + + if (msg[0] == Packets.SSH_MSG_NEWKEYS) + { + if (km == null) + throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!"); + + BlockCipher cbc; + MessageMac mac; + + try + { + cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false, + km.enc_key_server_to_client, km.initial_iv_server_to_client); + + mac = new MessageMac(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client); + + } + catch (IllegalArgumentException e1) + { + throw new IOException("Fatal error during MAC startup!"); + } + + tm.changeRecvCipher(cbc, mac); + + ConnectionInfo sci = new ConnectionInfo(); + + kexCount++; + + sci.keyExchangeAlgorithm = kxs.np.kex_algo; + sci.keyExchangeCounter = kexCount; + sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server; + sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client; + sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server; + sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client; + sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo; + sci.serverHostKey = kxs.hostkey; + + synchronized (accessLock) + { + lastConnInfo = sci; + accessLock.notifyAll(); + } + + kxs = null; + return; + } + + if ((kxs == null) || (kxs.state == 0)) + throw new IOException("Unexpected Kex submessage!"); + + if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1") + || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) + { + if (kxs.state == 1) + { + PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen); + kxs.dhgx = new DhGroupExchange(kxs.getHashAlgorithm(), dhgexgrp.getP(), dhgexgrp.getG()); + kxs.dhgx.init(rnd); + PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE()); + tm.sendKexMessage(dhgexinit.getPayload()); + kxs.state = 2; + return; + } + + if (kxs.state == 2) + { + PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen); + + kxs.hostkey = dhgexrpl.getHostKey(); + + if (verifier != null) + { + boolean vres = false; + + try + { + vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey); + } + catch (Exception e) + { + throw new IOException( + "The server hostkey was not accepted by the verifier callback.", e); + } + + if (!vres) + throw new IOException("The server hostkey was not accepted by the verifier callback"); + } + + kxs.dhgx.setF(dhgexrpl.getF()); + + try + { + kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(), + kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(), + kxs.dhgexParameters); + } + catch (IllegalArgumentException e) + { + throw new IOException("KEX error.", e); + } + + boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey); + + if (!res) + throw new IOException("Hostkey signature sent by remote is wrong!"); + + kxs.K = kxs.dhgx.getK(); + + finishKex(); + kxs.state = -1; + return; + } + + throw new IllegalStateException("Illegal State in KEX Exchange!"); + } + + if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1") + || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")) + { + if (kxs.state == 1) + { + + PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen); + + kxs.hostkey = dhr.getHostKey(); + + if (verifier != null) + { + boolean vres = false; + + try + { + vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey); + } + catch (Exception e) + { + throw new IOException( + "The server hostkey was not accepted by the verifier callback.", e); + } + + if (!vres) + throw new IOException("The server hostkey was not accepted by the verifier callback"); + } + + kxs.dhx.setF(dhr.getF()); + + try + { + kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(), + kxs.remoteKEX.getPayload(), dhr.getHostKey()); + } + catch (IllegalArgumentException e) + { + throw new IOException("KEX error.", e); + } + + boolean res = verifySignature(dhr.getSignature(), kxs.hostkey); + + if (!res) + throw new IOException("Hostkey signature sent by remote is wrong!"); + + kxs.K = kxs.dhx.getK(); + + finishKex(); + kxs.state = -1; + return; + } + } + + throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")"); + } + + public void handleEndMessage(Throwable cause) throws IOException { + synchronized (accessLock) { + connectionClosed = true; + accessLock.notifyAll(); + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/KexParameters.java b/service/src/main/java/com/trilead/ssh2/transport/KexParameters.java new file mode 100644 index 0000000..70bcf3e --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/KexParameters.java @@ -0,0 +1,24 @@ +package com.trilead.ssh2.transport; + +/** + * KexParameters. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: KexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class KexParameters +{ + public byte[] cookie; + public String[] kex_algorithms; + public String[] server_host_key_algorithms; + public String[] encryption_algorithms_client_to_server; + public String[] encryption_algorithms_server_to_client; + public String[] mac_algorithms_client_to_server; + public String[] mac_algorithms_server_to_client; + public String[] compression_algorithms_client_to_server; + public String[] compression_algorithms_server_to_client; + public String[] languages_client_to_server; + public String[] languages_server_to_client; + public boolean first_kex_packet_follows; + public int reserved_field1; +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/KexState.java b/service/src/main/java/com/trilead/ssh2/transport/KexState.java new file mode 100644 index 0000000..2bbabd1 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/KexState.java @@ -0,0 +1,41 @@ +package com.trilead.ssh2.transport; + + +import java.math.BigInteger; + +import com.trilead.ssh2.DHGexParameters; +import com.trilead.ssh2.crypto.dh.DhExchange; +import com.trilead.ssh2.crypto.dh.DhGroupExchange; +import com.trilead.ssh2.packets.PacketKexInit; + +/** + * KexState. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: KexState.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class KexState +{ + public PacketKexInit localKEX; + public PacketKexInit remoteKEX; + public NegotiatedParameters np; + public int state = 0; + + public BigInteger K; + public byte[] H; + + public byte[] hostkey; + + public DhExchange dhx; + public DhGroupExchange dhgx; + public DHGexParameters dhgexParameters; + private String hashAlgorithm; + + public void setHashAlgorithm(String hashAlgorithm) { + this.hashAlgorithm = hashAlgorithm; + } + + public String getHashAlgorithm() { + return hashAlgorithm; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/MessageHandler.java b/service/src/main/java/com/trilead/ssh2/transport/MessageHandler.java new file mode 100644 index 0000000..ed6ca80 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/MessageHandler.java @@ -0,0 +1,29 @@ +package com.trilead.ssh2.transport; + +import java.io.IOException; + +/** + * MessageHandler. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id : MessageHandler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public interface MessageHandler +{ + /** + * Handle message. + * + * @param msg the msg + * @param msglen the msglen + * @throws IOException the io exception + */ + public void handleMessage(byte[] msg, int msglen) throws IOException; + + /** + * Called to inform that no more messages will be delivered. + * + * @param cause For diagnosis, the reason that caused the transport to close down. + * @throws IOException the io exception + */ + public void handleEndMessage(Throwable cause) throws IOException; +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/NegotiateException.java b/service/src/main/java/com/trilead/ssh2/transport/NegotiateException.java new file mode 100644 index 0000000..ff53097 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/NegotiateException.java @@ -0,0 +1,12 @@ +package com.trilead.ssh2.transport; + +/** + * NegotiateException. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: NegotiateException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class NegotiateException extends Exception +{ + private static final long serialVersionUID = 3689910669428143157L; +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/NegotiatedParameters.java b/service/src/main/java/com/trilead/ssh2/transport/NegotiatedParameters.java new file mode 100644 index 0000000..e9f3a0a --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/NegotiatedParameters.java @@ -0,0 +1,22 @@ +package com.trilead.ssh2.transport; + +/** + * NegotiatedParameters. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: NegotiatedParameters.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class NegotiatedParameters +{ + public boolean guessOK; + public String kex_algo; + public String server_host_key_algo; + public String enc_algo_client_to_server; + public String enc_algo_server_to_client; + public String mac_algo_client_to_server; + public String mac_algo_server_to_client; + public String comp_algo_client_to_server; + public String comp_algo_server_to_client; + public String lang_client_to_server; + public String lang_server_to_client; +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/TransportConnection.java b/service/src/main/java/com/trilead/ssh2/transport/TransportConnection.java new file mode 100644 index 0000000..e5857bd --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/TransportConnection.java @@ -0,0 +1,266 @@ + +package com.trilead.ssh2.transport; + +import com.dragonssh.xhttpdemo.core.StatisticGraphData; +import com.trilead.ssh2.crypto.cipher.BlockCipher; +import com.trilead.ssh2.crypto.cipher.CipherInputStream; +import com.trilead.ssh2.crypto.cipher.CipherOutputStream; +import com.trilead.ssh2.crypto.cipher.NullCipher; +import com.trilead.ssh2.crypto.digest.MAC; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.Packets; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; + + +/** + * TransportConnection. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ +public class TransportConnection +{ + private static final Logger log = Logger.getLogger(TransportConnection.class); + int send_seq_number = 0; + int recv_seq_number = 0; + CipherInputStream cis; + CipherOutputStream cos; + boolean useRandomPadding = false; + /* Depends on current MAC and CIPHER */ + MAC send_mac; + byte[] send_mac_buffer; + int send_padd_blocksize = 8; + MAC recv_mac; + byte[] recv_mac_buffer; + byte[] recv_mac_buffer_cmp; + int recv_padd_blocksize = 8; + /* won't change */ + final byte[] send_padding_buffer = new byte[256]; + final byte[] send_packet_header_buffer = new byte[5]; + final byte[] recv_padding_buffer = new byte[256]; + final byte[] recv_packet_header_buffer = new byte[5]; + boolean recv_packet_header_present = false; + ClientServerHello csh; + final SecureRandom rnd; + StatisticGraphData.DataTransferStats dataTransferStats; + + public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd) + { + this.cis = new CipherInputStream(new NullCipher(), is); + this.cos = new CipherOutputStream(new NullCipher(), os); + this.dataTransferStats = StatisticGraphData.getStatisticData().getDataTransferStats(); + this.rnd = rnd; + } + + public void changeRecvCipher(BlockCipher bc, MAC mac) + { + cis.changeCipher(bc); + recv_mac = mac; + recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null; + recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null; + recv_padd_blocksize = bc.getBlockSize(); + if (recv_padd_blocksize < 8) + recv_padd_blocksize = 8; + } + + public void changeSendCipher(BlockCipher bc, MAC mac) + { + if ((bc instanceof NullCipher) == false) + { + /* Only use zero byte padding for the first few packets */ + useRandomPadding = true; + /* Once we start encrypting, there is no way back */ + } + + cos.changeCipher(bc); + send_mac = mac; + send_mac_buffer = (mac != null) ? new byte[mac.size()] : null; + send_padd_blocksize = bc.getBlockSize(); + if (send_padd_blocksize < 8) + send_padd_blocksize = 8; + } + + public void sendMessage(byte[] message) throws IOException + { + sendMessage(message, 0, message.length, 0); + } + + public void sendMessage(byte[] message, int off, int len) throws IOException + { + sendMessage(message, off, len, 0); + } + + public int getPacketOverheadEstimate() + { + // return an estimate for the paket overhead (for send operations) + return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length; + } + + public void sendMessage(byte[] message, int off, int len, int padd) throws IOException + { + if (padd < 4) + padd = 4; + else if (padd > 64) + padd = 64; + int bytesSent = len; + int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */ + + int slack = packet_len % send_padd_blocksize; + + if (slack != 0) + { + packet_len += (send_padd_blocksize - slack); + } + + if (packet_len < 16) + packet_len = 16; + + int padd_len = packet_len - (5 + len); + + if (useRandomPadding) + { + for (int i = 0; i < padd_len; i = i + 4) + { + /* + * don't waste calls to rnd.nextInt() (by using only 8bit of the + * output). just believe me: even though we may write here up to 3 + * bytes which won't be used, there is no "buffer overflow" (i.e., + * arrayindexoutofbounds). the padding buffer is big enough =) (256 + * bytes, and that is bigger than any current cipher block size + 64). + */ + + int r = rnd.nextInt(); + send_padding_buffer[i] = (byte) r; + send_padding_buffer[i + 1] = (byte) (r >> 8); + send_padding_buffer[i + 2] = (byte) (r >> 16); + send_padding_buffer[i + 3] = (byte) (r >> 24); + } + } + else + { + /* use zero padding for unencrypted traffic */ + for (int i = 0; i < padd_len; i++) + send_padding_buffer[i] = 0; + /* Actually this code is paranoid: we never filled any + * bytes into the padding buffer so far, therefore it should + * consist of zeros only. + */ + } + + send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24); + send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16); + send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8); + send_packet_header_buffer[3] = (byte) ((packet_len - 4)); + send_packet_header_buffer[4] = (byte) padd_len; + + cos.write(send_packet_header_buffer, 0, 5); + cos.write(message, off, len); + cos.write(send_padding_buffer, 0, padd_len); + + if (send_mac != null) + { + send_mac.initMac(send_seq_number); + send_mac.update(send_packet_header_buffer, 0, 5); + send_mac.update(message, off, len); + send_mac.update(send_padding_buffer, 0, padd_len); + + send_mac.getMac(send_mac_buffer, 0); + cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length); + } + + cos.flush(); + this.dataTransferStats.addBytesSent(bytesSent); + + if (log.isEnabled()) + { + log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload"); + } + + send_seq_number++; + } + + public int peekNextMessageLength() throws IOException + { + if (recv_packet_header_present == false) + { + cis.read(recv_packet_header_buffer, 0, 5); + recv_packet_header_present = true; + } + + int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24) + | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8) + | ((recv_packet_header_buffer[3] & 0xff)); + + int padding_length = recv_packet_header_buffer[4] & 0xff; + + if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) + throw new IOException("Illegal packet size! (" + packet_length + ")"); + + int payload_length = packet_length - padding_length - 1; + + if (payload_length < 0) + throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")"); + + return payload_length; + } + + public int receiveMessage(byte buffer[], int off, int len) throws IOException + { + if (recv_packet_header_present == false) + { + cis.read(recv_packet_header_buffer, 0, 5); + } + else + recv_packet_header_present = false; + + int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24) + | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8) + | ((recv_packet_header_buffer[3] & 0xff)); + + int padding_length = recv_packet_header_buffer[4] & 0xff; + + if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) + throw new IOException("Illegal packet size! (" + packet_length + ")"); + + int payload_length = packet_length - padding_length - 1; + + if (payload_length < 0) + throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")"); + + if (payload_length >= len) + throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")"); + + cis.read(buffer, off, payload_length); + cis.read(recv_padding_buffer, 0, padding_length); + + if (recv_mac != null) + { + cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length); + + recv_mac.initMac(recv_seq_number); + recv_mac.update(recv_packet_header_buffer, 0, 5); + recv_mac.update(buffer, off, payload_length); + recv_mac.update(recv_padding_buffer, 0, padding_length); + recv_mac.getMac(recv_mac_buffer_cmp, 0); + + for (int i = 0; i < recv_mac_buffer.length; i++) + { + if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i]) + throw new IOException("Remote sent corrupt MAC."); + } + } + + recv_seq_number++; + + if (log.isEnabled()) + { + log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length + + " bytes payload"); + } + this.dataTransferStats.addBytesReceived(payload_length); + return payload_length; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/transport/TransportManager.java b/service/src/main/java/com/trilead/ssh2/transport/TransportManager.java new file mode 100644 index 0000000..0d0cdee --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/transport/TransportManager.java @@ -0,0 +1,745 @@ +package com.trilead.ssh2.transport; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.SecureRandom; +import java.util.Vector; + +import com.dragonssh.xhttpdemo.core.tunnel.vpn.SocketProtector; + +import com.trilead.ssh2.ConnectionInfo; +import com.trilead.ssh2.ConnectionMonitor; +import com.trilead.ssh2.DHGexParameters; +import com.trilead.ssh2.HTTPProxyData; +import com.trilead.ssh2.HTTPProxyException; +import com.trilead.ssh2.ProxyData; +import com.trilead.ssh2.ServerHostKeyVerifier; +import com.trilead.ssh2.crypto.Base64; +import com.trilead.ssh2.crypto.CryptoWishList; +import com.trilead.ssh2.crypto.cipher.BlockCipher; +import com.trilead.ssh2.crypto.digest.MAC; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.PacketDisconnect; +import com.trilead.ssh2.packets.Packets; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.util.Tokenizer; + + +/* + * Yes, the "standard" is a big mess. On one side, the say that arbitary channel + * packets are allowed during kex exchange, on the other side we need to blindly + * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that + * the next packet is not a channel data packet? Yes, we could check if it is in + * the KEX range. But the standard says nothing about this. The OpenSSH guys + * block local "normal" traffic during KEX. That's fine - however, they assume + * that the other side is doing the same. During re-key, if they receive traffic + * other than KEX, they become horribly irritated and kill the connection. Since + * we are very likely going to communicate with OpenSSH servers, we have to play + * the same game - even though we could do better. + * + * btw: having stdout and stderr on the same channel, with a shared window, is + * also a VERY good idea... =( + */ + +/** + * TransportManager. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: TransportManager.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class TransportManager +{ + private static final Logger log = Logger.getLogger(TransportManager.class); + + class HandlerEntry + { + MessageHandler mh; + int low; + int high; + } + + private final Vector asynchronousQueue = new Vector(); + private Thread asynchronousThread = null; + + class AsynchronousWorker extends Thread + { + public void run() + { + while (true) + { + byte[] msg = null; + + synchronized (asynchronousQueue) + { + if (asynchronousQueue.size() == 0) + { + /* After the queue is empty for about 2 seconds, stop this thread */ + + try + { + asynchronousQueue.wait(2000); + } + catch (InterruptedException e) + { + /* OKOK, if somebody interrupts us, then we may die earlier. */ + } + + if (asynchronousQueue.size() == 0) + { + asynchronousThread = null; + return; + } + } + + msg = (byte[]) asynchronousQueue.remove(0); + } + + /* The following invocation may throw an IOException. + * There is no point in handling it - it simply means + * that the connection has a problem and we should stop + * sending asynchronously messages. We do not need to signal that + * we have exited (asynchronousThread = null): further + * messages in the queue cannot be sent by this or any + * other thread. + * Other threads will sooner or later (when receiving or + * sending the next message) get the same IOException and + * get to the same conclusion. + */ + + try + { + sendMessage(msg); + } + catch (IOException e) + { + return; + } + } + } + } + + final private String sourceAddress; + String hostname; + int port; + Socket sock; + ProxyData proxyData; + + final Object connectionSemaphore = new Object(); + + boolean flagKexOngoing = false; + + Throwable reasonClosedCause = null; + + TransportConnection tc; + KexManager km; + + Vector messageHandlers = new Vector(); + + Thread receiveThread; + + Vector connectionMonitors = new Vector(); + boolean monitorsWereInformed = false; + private ClientServerHello versions; + + /** + * There were reports that there are JDKs which use + * the resolver even though one supplies a dotted IP + * address in the Socket constructor. That is why we + * try to generate the InetAdress "by hand". + * + * @param host + * @return the InetAddress + * @throws UnknownHostException + */ + public static InetAddress createInetAddress(String host) throws UnknownHostException + { + /* Check if it is a dotted IP4 address */ + + InetAddress addr = parseIPv4Address(host); + + if (addr != null) + return addr; + + return InetAddress.getByName(host); + } + + private static InetAddress parseIPv4Address(String host) throws UnknownHostException + { + if (host == null) + return null; + + String[] quad = Tokenizer.parseTokens(host, '.'); + + if ((quad == null) || (quad.length != 4)) + return null; + + byte[] addr = new byte[4]; + + for (int i = 0; i < 4; i++) + { + int part = 0; + + if ((quad[i].length() == 0) || (quad[i].length() > 3)) + return null; + + for (int k = 0; k < quad[i].length(); k++) + { + char c = quad[i].charAt(k); + + /* No, Character.isDigit is not the same */ + if ((c < '0') || (c > '9')) + return null; + + part = part * 10 + (c - '0'); + } + + if (part > 255) /* 300.1.2.3 is invalid =) */ + return null; + + addr[i] = (byte) part; + } + + return InetAddress.getByAddress(host, addr); + } + + public TransportManager(String host, int port) throws IOException + { + this(host, port, null); + } + + public TransportManager(String host, int port, String sourceAddress) throws IOException + { + this.hostname = host; + this.port = port; + this.sourceAddress = sourceAddress; + } + + public int getPacketOverheadEstimate() + { + return tc.getPacketOverheadEstimate(); + } + + public void setTcpNoDelay(boolean state) throws IOException + { + sock.setTcpNoDelay(state); + } + + public void setSoTimeout(int timeout) throws IOException + { + sock.setSoTimeout(timeout); + } + + public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException + { + return km.getOrWaitForConnectionInfo(kexNumber); + } + + public ClientServerHello getVersionInfo() { + return versions; + } + + /** + * If the socket connection is lost (either by this side closing down or the other side closing down), + * @return a non-null object indicating the cause of the connection loss. + */ + public Throwable getReasonClosedCause() + { + synchronized (connectionSemaphore) + { + return reasonClosedCause; + } + } + + public boolean isConnectionClosed() { + return getReasonClosedCause()!=null; + } + + public byte[] getSessionIdentifier() + { + return km.sessionId; + } + + public void close(Throwable cause, boolean useDisconnectPacket) + { + if (log.isEnabled()) + log.log(50, "Closing all conections"); + + if (useDisconnectPacket == false) + { + /* OK, hard shutdown - do not aquire the semaphore, + * perhaps somebody is inside (and waits until the remote + * side is ready to accept new data). */ + + try + { + if (proxyData != null) + proxyData.close(); + + if (sock != null) + sock.close(); + } + catch (IOException ignore) + { + } + + /* OK, whoever tried to send data, should now agree that + * there is no point in further waiting =) + * It is safe now to aquire the semaphore. + */ + } + + synchronized (connectionSemaphore) + { + if (reasonClosedCause==null) + { + if (useDisconnectPacket == true) + { + try + { + byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "") + .getPayload(); + if (tc != null) + tc.sendMessage(msg); + } + catch (IOException ignore) + { + } + + try + { + if (proxyData != null) + proxyData.close(); + + if (sock != null) + sock.close(); + } + catch (IOException ignore) + { + } + } + + if (cause==null) + cause = new Exception("Unknown cause"); + reasonClosedCause = cause; + } + connectionSemaphore.notifyAll(); + } + + /* No check if we need to inform the monitors */ + + Vector monitors = null; + + synchronized (this) + { + /* Short term lock to protect "connectionMonitors" + * and "monitorsWereInformed" + * (they may be modified concurrently) + */ + + if (monitorsWereInformed == false) + { + monitorsWereInformed = true; + monitors = (Vector) connectionMonitors.clone(); + } + } + + if (monitors != null) + { + for (int i = 0; i < monitors.size(); i++) + { + try + { + ConnectionMonitor cmon = (ConnectionMonitor) monitors.elementAt(i); + cmon.connectionLost(reasonClosedCause); + } + catch (Exception ignore) + { + } + } + } + } + + private void establishConnection(ProxyData proxyData, int connectTimeout, int readTimeout) + throws IOException + { + if (proxyData == null) + sock = connectDirect(hostname, port, connectTimeout, readTimeout); + else { + sock = proxyData.openConnection(hostname, port, connectTimeout, readTimeout); + } + } + + private Socket connectDirect(String hostname, int port, int connectTimeout, int readTimeout) + throws IOException + { + Socket sock = new Socket(); + if (sourceAddress != null) + { + InetAddress sourceaddr = createInetAddress(this.sourceAddress); + sock.bind(new InetSocketAddress(sourceaddr,0)); + } + // Ensure the control socket bypasses the VPN to prevent routing loops. + SocketProtector.protect(sock); + InetAddress addr = createInetAddress(hostname); + sock.connect(new InetSocketAddress(addr, port), connectTimeout); + sock.setSoTimeout(readTimeout); + return sock; + } + + public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex, + int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException { + initialize(cwl, verifier, dhgex, connectTimeout, 0, rnd, proxyData); + } + + public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex, + int connectTimeout, int readTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException + { + /* First, establish the TCP connection to the SSH-2 server */ + + this.proxyData = proxyData; + establishConnection(proxyData, connectTimeout, readTimeout); + + /* Parse the server line and say hello - important: this information is later needed for the + * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object + * for later use. + */ + + ClientServerHello csh = new ClientServerHello(sock.getInputStream(), sock.getOutputStream()); + versions = csh; + + tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd); + + km = new KexManager(this, csh, cwl, hostname, port, verifier, rnd); + km.initiateKEX(cwl, dhgex); + + receiveThread = new Thread(new Runnable() + { + public void run() + { + Throwable cause; + try + { + receiveLoop(); + cause = new AssertionError(); // receiveLoop never returns normally + } + catch (IOException e) + { + if (log.isEnabled() && !isConnectionClosed()) + log.log(10, "Receive thread: error in receiveLoop",e); + + cause = e; + close(e, false); + } + + if (log.isEnabled()) + log.log(50, "Receive thread: back from receiveLoop"); + + /* Tell all handlers that it is time to say goodbye */ + + if (km != null) + { + try + { + km.handleEndMessage(cause); + } + catch (IOException e) + { + } + } + + for (int i = 0; i < messageHandlers.size(); i++) + { + HandlerEntry he = (HandlerEntry) messageHandlers.elementAt(i); + try + { + he.mh.handleEndMessage(cause); + } + catch (Exception ignore) + { + } + } + } + }); + + receiveThread.setDaemon(true); + receiveThread.start(); + } + + public void registerMessageHandler(MessageHandler mh, int low, int high) + { + HandlerEntry he = new HandlerEntry(); + he.mh = mh; + he.low = low; + he.high = high; + + synchronized (messageHandlers) + { + messageHandlers.addElement(he); + } + } + + public void removeMessageHandler(MessageHandler mh, int low, int high) + { + synchronized (messageHandlers) + { + for (int i = 0; i < messageHandlers.size(); i++) + { + HandlerEntry he = (HandlerEntry) messageHandlers.elementAt(i); + if ((he.mh == mh) && (he.low == low) && (he.high == high)) + { + messageHandlers.removeElementAt(i); + break; + } + } + } + } + + public void sendKexMessage(byte[] msg) throws IOException + { + synchronized (connectionSemaphore) + { + ensureConnected(); + + flagKexOngoing = true; + + try + { + tc.sendMessage(msg); + } + catch (IOException e) + { + close(e, false); + throw e; + } + } + } + + private void ensureConnected() throws IOException { + if (reasonClosedCause!=null) + { + throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause); + } + } + + public void kexFinished() throws IOException + { + synchronized (connectionSemaphore) + { + flagKexOngoing = false; + connectionSemaphore.notifyAll(); + } + } + + public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex) throws IOException + { + km.initiateKEX(cwl, dhgex); + } + + public void changeRecvCipher(BlockCipher bc, MAC mac) + { + tc.changeRecvCipher(bc, mac); + } + + public void changeSendCipher(BlockCipher bc, MAC mac) + { + tc.changeSendCipher(bc, mac); + } + + public void sendAsynchronousMessage(byte[] msg) throws IOException + { + synchronized (asynchronousQueue) + { + asynchronousQueue.addElement(msg); + + /* This limit should be flexible enough. We need this, otherwise the peer + * can flood us with global requests (and other stuff where we have to reply + * with an asynchronous message) and (if the server just sends data and does not + * read what we send) this will probably put us in a low memory situation + * (our send queue would grow and grow and...) */ + + if (asynchronousQueue.size() > 100) + throw new IOException("Error: the peer is not consuming our asynchronous replies."); + + /* Check if we have an asynchronous sending thread */ + + if (asynchronousThread == null) + { + asynchronousThread = new AsynchronousWorker(); + asynchronousThread.setDaemon(true); + asynchronousThread.start(); + + /* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */ + } + } + } + + public void setConnectionMonitors(Vector monitors) + { + synchronized (this) + { + connectionMonitors = (Vector) monitors.clone(); + } + } + + public void sendMessage(byte[] msg) throws IOException + { + if (Thread.currentThread() == receiveThread) + throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!"); + + synchronized (connectionSemaphore) + { + while (true) + { + ensureConnected(); + + if (flagKexOngoing == false) + break; + + try + { + connectionSemaphore.wait(); + } + catch (InterruptedException e) + { + throw new InterruptedIOException(); + } + } + + try + { + tc.sendMessage(msg); + } + catch (IOException e) + { + close(e, false); + throw e; + } + } + } + + public void receiveLoop() throws IOException + { + byte[] msg = new byte[MAX_PACKET_SIZE]; + + while (true) + { + int msglen = tc.receiveMessage(msg, 0, msg.length); + + int type = msg[0] & 0xff; + + if (type == Packets.SSH_MSG_IGNORE) + continue; + + if (type == Packets.SSH_MSG_DEBUG) + { + if (log.isEnabled()) + { + TypesReader tr = new TypesReader(msg, 0, msglen); + tr.readByte(); + tr.readBoolean(); + StringBuffer debugMessageBuffer = new StringBuffer(); + debugMessageBuffer.append(tr.readString("UTF-8")); + + for (int i = 0; i < debugMessageBuffer.length(); i++) + { + char c = debugMessageBuffer.charAt(i); + + if ((c >= 32) && (c <= 126)) + continue; + debugMessageBuffer.setCharAt(i, '\uFFFD'); + } + + log.log(50, "DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'"); + } + continue; + } + + if (type == Packets.SSH_MSG_UNIMPLEMENTED) + { + throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen."); + } + + if (type == Packets.SSH_MSG_DISCONNECT) + { + TypesReader tr = new TypesReader(msg, 0, msglen); + tr.readByte(); + int reason_code = tr.readUINT32(); + StringBuffer reasonBuffer = new StringBuffer(); + reasonBuffer.append(tr.readString("UTF-8")); + + /* + * Do not get fooled by servers that send abnormal long error + * messages + */ + + if (reasonBuffer.length() > 255) + { + reasonBuffer.setLength(255); + reasonBuffer.setCharAt(254, '.'); + reasonBuffer.setCharAt(253, '.'); + reasonBuffer.setCharAt(252, '.'); + } + + /* + * Also, check that the server did not send charcaters that may + * screw up the receiver -> restrict to reasonable US-ASCII + * subset -> "printable characters" (ASCII 32 - 126). Replace + * all others with 0xFFFD (UNICODE replacement character). + */ + + for (int i = 0; i < reasonBuffer.length(); i++) + { + char c = reasonBuffer.charAt(i); + + if ((c >= 32) && (c <= 126)) + continue; + reasonBuffer.setCharAt(i, '\uFFFD'); + } + + throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): " + + reasonBuffer.toString()); + } + + /* + * Is it a KEX Packet? + */ + + if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS) + || ((type >= 30) && (type <= 49))) + { + km.handleMessage(msg, msglen); + continue; + } + + MessageHandler mh = null; + + for (int i = 0; i < messageHandlers.size(); i++) + { + HandlerEntry he = (HandlerEntry) messageHandlers.elementAt(i); + if ((he.low <= type) && (type <= he.high)) + { + mh = he.mh; + break; + } + } + + if (mh == null) + throw new IOException("Unexpected SSH message (type " + type + ")"); + + mh.handleMessage(msg, msglen); + } + } + + /** + * Advertised maximum SSH packet size that the other side can send to us. + */ + public static final int MAX_PACKET_SIZE = Integer.getInteger( + TransportManager.class.getName()+".maxPacketSize", + 64*1024); +} diff --git a/service/src/main/java/com/trilead/ssh2/util/IOUtils.java b/service/src/main/java/com/trilead/ssh2/util/IOUtils.java new file mode 100644 index 0000000..3d837f2 --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/util/IOUtils.java @@ -0,0 +1,17 @@ +package com.trilead.ssh2.util; + +import java.io.Closeable; +import java.io.IOException; + +/** + * @author Kohsuke Kawaguchi + */ +public class IOUtils { + public static void closeQuietly(Closeable c) { + try { + c.close(); + } catch (IOException e) { + // ignore error + } + } +} diff --git a/service/src/main/java/com/trilead/ssh2/util/TimeoutService.java b/service/src/main/java/com/trilead/ssh2/util/TimeoutService.java new file mode 100644 index 0000000..13a047f --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/util/TimeoutService.java @@ -0,0 +1,76 @@ + +package com.trilead.ssh2.util; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * TimeoutService (beta). Here you can register a timeout. + *

+ * Implemented having large scale programs in mind: if you open many concurrent SSH connections + * that rely on timeouts, then there will be only one timeout thread. Once all timeouts + * have expired/are cancelled, the thread will (sooner or later) exit. + * Only after new timeouts arrive a new thread (singleton) will be instantiated. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: TimeoutService.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class TimeoutService { + + private final static ThreadFactory threadFactory = new ThreadFactory() { + + private AtomicInteger count = new AtomicInteger(); + + public Thread newThread(Runnable r) { + int threadNumber = count.incrementAndGet(); + String threadName = "TimeoutService-" + threadNumber; + Thread thread = new Thread(r, threadName); + thread.setDaemon(true); + return thread; + } + }; + + private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(20, threadFactory); + + public static class TimeoutToken implements Runnable { + private Runnable handler; + private boolean cancelled = false; + + public void run() { + if (!cancelled) { + handler.run(); + } + } + } + + /** + * It is assumed that the passed handler will not execute for a long time. + * + * @param runTime runTime + * @param handler handler + * @return a TimeoutToken that can be used to cancel the timeout. + */ + public static final TimeoutToken addTimeoutHandler(long runTime, Runnable handler) { + TimeoutToken token = new TimeoutToken(); + token.handler = handler; + long delay = runTime - System.currentTimeMillis(); + if (delay < 0) { + delay = 0; + } + scheduler.schedule(token, delay, TimeUnit.MILLISECONDS); + return token; + } + + /** + * Cancel the timeout callback for the specified token. + * + * @param token token to be canceled. + */ + public static final void cancelTimeoutHandler(TimeoutToken token) { + token.cancelled = true; + } +} diff --git a/service/src/main/java/com/trilead/ssh2/util/Tokenizer.java b/service/src/main/java/com/trilead/ssh2/util/Tokenizer.java new file mode 100644 index 0000000..bbd289b --- /dev/null +++ b/service/src/main/java/com/trilead/ssh2/util/Tokenizer.java @@ -0,0 +1,51 @@ + +package com.trilead.ssh2.util; + +/** + * Tokenizer. Why? Because StringTokenizer is not available in J2ME. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: Tokenizer.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class Tokenizer +{ + /** + * Exists because StringTokenizer is not available in J2ME. + * Returns an array with at least 1 entry. + * + * @param source must be non-null + * @param delimiter delimiter + * @return an array of Strings + */ + public static String[] parseTokens(String source, char delimiter) + { + int numtoken = 1; + + for (int i = 0; i < source.length(); i++) + { + if (source.charAt(i) == delimiter) + numtoken++; + } + + String list[] = new String[numtoken]; + int nextfield = 0; + + for (int i = 0; i < numtoken; i++) + { + if (nextfield >= source.length()) + { + list[i] = ""; + } + else + { + int idx = source.indexOf(delimiter, nextfield); + if (idx == -1) + idx = source.length(); + list[i] = source.substring(nextfield, idx); + nextfield = idx + 1; + } + } + + return list; + } +} diff --git a/service/src/main/java/net/sourceforge/jsocks/Proxy.java b/service/src/main/java/net/sourceforge/jsocks/Proxy.java new file mode 100644 index 0000000..32a2d98 --- /dev/null +++ b/service/src/main/java/net/sourceforge/jsocks/Proxy.java @@ -0,0 +1,311 @@ +package net.sourceforge.jsocks; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import com.dragonssh.xhttpdemo.core.tunnel.vpn.SocketProtector; + +/** + * Abstract class Proxy, base for classes Socks4Proxy and Socks5Proxy. Defines + * methods for specifying default proxy, to be used by all classes of this + * package. + */ + +public abstract class Proxy { + + // Data members + // protected InetRange directHosts = new InetRange(); + + public static final int SOCKS_SUCCESS = 0; + public static final int SOCKS_FAILURE = 1; + public static final int SOCKS_BADCONNECT = 2; + public static final int SOCKS_BADNETWORK = 3; + + public static final int SOCKS_HOST_UNREACHABLE = 4; + public static final int SOCKS_CONNECTION_REFUSED = 5; + + public static final int SOCKS_TTL_EXPIRE = 6; + + public static final int SOCKS_CMD_NOT_SUPPORTED = 7; + + public static final int SOCKS_ADDR_NOT_SUPPORTED = 8; + + public static final int SOCKS_NO_PROXY = 1 << 16; + + public static final int SOCKS_PROXY_NO_CONNECT = 2 << 16; + + public static final int SOCKS_PROXY_IO_ERROR = 3 << 16; + + public static final int SOCKS_AUTH_NOT_SUPPORTED = 4 << 16; + + // Public instance methods + // ======================== + + public static final int SOCKS_AUTH_FAILURE = 5 << 16; + + public static final int SOCKS_JUST_ERROR = 6 << 16; + + public static final int SOCKS_DIRECT_FAILED = 7 << 16; + + // Public Static(Class) Methods + // ============================== + + public static final int SOCKS_METHOD_NOTSUPPORTED = 8 << 16; + + public static final int SOCKS_CMD_CONNECT = 0x1; + + static final int SOCKS_CMD_BIND = 0x2; + + static final int SOCKS_CMD_UDP_ASSOCIATE = 0x3; + + /** + * Get current default proxy. + * + * @return Current default proxy, or null if none is set. + */ + public static Proxy getDefaultProxy() { + return defaultProxy; + } + + + /** + * Sets default proxy. + * + * @param p + * Proxy to use as default proxy. + */ + public static void setDefaultProxy(Proxy p) { + defaultProxy = p; + } + + protected InetAddress proxyIP = null; + + protected String proxyHost = null; + + protected int proxyPort; + + protected Socket proxySocket = null; + + protected InputStream in; + + protected OutputStream out; + + protected int version; + + protected Proxy chainProxy = null; + + // Protected static/class variables + protected static Proxy defaultProxy = null; + + Proxy(InetAddress proxyIP, int proxyPort) { + this.proxyIP = proxyIP; + this.proxyPort = proxyPort; + } + + Proxy(Proxy p) { + this.proxyIP = p.proxyIP; + this.proxyPort = p.proxyPort; + this.version = p.version; + } + + Proxy(Proxy chainProxy, InetAddress proxyIP, int proxyPort) { + this.chainProxy = chainProxy; + this.proxyIP = proxyIP; + this.proxyPort = proxyPort; + } + + // Private methods + // =============== + + // Constants + + // Constructors + // ==================== + Proxy(String proxyHost, int proxyPort) throws UnknownHostException { + this.proxyHost = proxyHost; + this.proxyIP = InetAddress.getByName(proxyHost); + this.proxyPort = proxyPort; + } + protected ProxyMessage accept() throws IOException, SocksException { + ProxyMessage msg; + try { + msg = formMessage(in); + } catch (InterruptedIOException iioe) { + throw iioe; + } catch (IOException io_ex) { + endSession(); + throw new SocksException(SOCKS_PROXY_IO_ERROR, + "While Trying accept:" + io_ex); + } + return msg; + } + protected ProxyMessage bind(InetAddress ip, int port) throws SocksException { + try { + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_BIND, ip, port); + return exchange(request); + } catch (SocksException se) { + endSession(); + throw se; + } + } + protected ProxyMessage bind(String host, int port) + throws UnknownHostException, SocksException { + try { + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_BIND, host, port); + return exchange(request); + } catch (SocksException se) { + endSession(); + throw se; + } + } + protected ProxyMessage connect(InetAddress ip, int port) + throws SocksException { + try { + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, ip, port); + return exchange(request); + } catch (SocksException se) { + endSession(); + throw se; + } + } + protected ProxyMessage connect(String host, int port) + throws UnknownHostException, SocksException { + try { + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, host, port); + return exchange(request); + } catch (SocksException se) { + endSession(); + throw se; + } + } + protected abstract Proxy copy(); + protected void endSession() { + try { + if (proxySocket != null) + proxySocket.close(); + proxySocket = null; + } catch (IOException io_ex) { + } + } + /** + * Sends the request reads reply and returns it throws exception if + * something wrong with IO or the reply code is not zero + */ + protected ProxyMessage exchange(ProxyMessage request) throws SocksException { + ProxyMessage reply; + try { + request.write(out); + reply = formMessage(in); + } catch (SocksException s_ex) { + throw s_ex; + } catch (IOException ioe) { + throw (new SocksException(SOCKS_PROXY_IO_ERROR, "" + ioe)); + } + return reply; + } + + protected abstract ProxyMessage formMessage(InputStream in) + throws SocksException, IOException; + protected abstract ProxyMessage formMessage(int cmd, InetAddress ip, + int port); + protected abstract ProxyMessage formMessage(int cmd, String host, int port) + throws UnknownHostException; + /** + * Get the ip address of the proxy server host. + * + * @return Proxy InetAddress. + */ + public InetAddress getInetAddress() { + return proxyIP; + } + /** + * Get the port on which proxy server is running. + * + * @return Proxy port. + */ + public int getPort() { + return proxyPort; + } + /** + * Reads the reply from the SOCKS server + */ + protected ProxyMessage readMsg() throws SocksException, IOException { + return formMessage(in); + } + + /** + * Sends the request to SOCKS server + */ + protected void sendMsg(ProxyMessage msg) throws SocksException, IOException { + msg.write(out); + } + protected void startSession() throws SocksException { + try { + proxySocket = new Socket(); + // Ensure the SOCKS control socket bypasses the VPN to prevent routing loops. + SocketProtector.protect(proxySocket); + proxySocket.connect(new InetSocketAddress(proxyIP, proxyPort)); + in = proxySocket.getInputStream(); + out = proxySocket.getOutputStream(); + } catch (SocksException se) { + throw se; + } catch (IOException io_ex) { + throw new SocksException(SOCKS_PROXY_IO_ERROR, "" + io_ex); + } + } + + /** + * Get string representation of this proxy. + * + * @returns string in the form:proxyHost:proxyPort \t Version versionNumber + */ + @Override + public String toString() { + return ("" + proxyIP.getHostName() + ":" + proxyPort + "\tVersion " + version); + } + protected ProxyMessage udpAssociate(InetAddress ip, int port) + throws SocksException { + try { + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE, ip, + port); + if (request != null) + return exchange(request); + } catch (SocksException se) { + endSession(); + throw se; + } + // Only get here if request was null + endSession(); + throw new SocksException(SOCKS_METHOD_NOTSUPPORTED, + "This version of proxy does not support UDP associate, use version 5"); + } + protected ProxyMessage udpAssociate(String host, int port) + throws UnknownHostException, SocksException { + try { + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE, host, + port); + if (request != null) + return exchange(request); + } catch (SocksException se) { + endSession(); + throw se; + } + // Only get here if request was null + endSession(); + throw new SocksException(SOCKS_METHOD_NOTSUPPORTED, + "This version of proxy does not support UDP associate, use version 5"); + } + +} diff --git a/service/src/main/java/net/sourceforge/jsocks/ProxyMessage.java b/service/src/main/java/net/sourceforge/jsocks/ProxyMessage.java new file mode 100644 index 0000000..f7fead5 --- /dev/null +++ b/service/src/main/java/net/sourceforge/jsocks/ProxyMessage.java @@ -0,0 +1,128 @@ +package net.sourceforge.jsocks; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Abstract class which describes SOCKS4/5 response/request. + */ +public abstract class ProxyMessage { + static final String bytes2IPV4(byte[] addr, int offset) { + String hostName = "" + (addr[offset] & 0xFF); + for (int i = offset + 1; i < offset + 4; ++i) + hostName += "." + (addr[i] & 0xFF); + return hostName; + } + static final String bytes2IPV6(byte[] addr, int offset) { + try { + // SOCKS5 IPv6 address is 16 bytes starting at offset. + // Convert raw bytes into a standard textual IPv6 literal. + byte[] ipBytes = new byte[16]; + System.arraycopy(addr, offset, ipBytes, 0, 16); + return java.net.InetAddress.getByAddress(ipBytes).getHostAddress(); + } catch (Exception e) { + // Fallback: represent as hex groups if parsing fails for any reason. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 16; i += 2) { + int hi = addr[offset + i] & 0xFF; + int lo = addr[offset + i + 1] & 0xFF; + int group = (hi << 8) | lo; + sb.append(Integer.toHexString(group)); + if (i < 14) sb.append(':'); + } + return sb.toString(); + } + } + /** Host as an IP address */ + public InetAddress ip = null; + /** SOCKS version, or version of the response for SOCKS4 */ + public int version; + /** Port field of the request/response */ + public int port; + /** Request/response code as an int */ + public int command; + + /** Host as string. */ + public String host = null; + + /** User field for SOCKS4 request messages */ + public String user = null; + + ProxyMessage() { + } + + ProxyMessage(int command, InetAddress ip, int port) { + this.command = command; + this.ip = ip; + this.port = port; + } + + /** + * Get the Address field of this message as InetAddress object. + * + * @return Host address or null, if one can't be determined. + */ + public InetAddress getInetAddress() throws UnknownHostException { + return ip; + } + + /** + * Initialises Message from the stream. Reads server response from given + * stream. + * + * @param in + * Input stream to read response from. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0), or if any + * error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public abstract void read(InputStream in) throws SocksException, + IOException; + + /** + * Initialises Message from the stream. Reads server response or client + * request from given stream. + * + * @param in + * Input stream to read response from. + * @param clinetMode + * If true read server response, else read client request. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0) and reading + * in client mode, or if any error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public abstract void read(InputStream in, boolean client_mode) + throws SocksException, IOException; + + // Package methods + // //////////////// + + /** + * Get string representaion of this message. + * + * @return string representation of this message. + */ + @Override + public String toString() { + return "Proxy Message:\n" + "Version:" + version + "\n" + "Command:" + + command + "\n" + "IP: " + ip + "\n" + "Port: " + port + + "\n" + "User: " + user + "\n"; + } + + /** + * Writes the message to the stream. + * + * @param out + * Output stream to which message should be written. + */ + public abstract void write(OutputStream out) throws SocksException, + IOException; + +} diff --git a/service/src/main/java/net/sourceforge/jsocks/Socks5Message.java b/service/src/main/java/net/sourceforge/jsocks/Socks5Message.java new file mode 100644 index 0000000..bd7f6ce --- /dev/null +++ b/service/src/main/java/net/sourceforge/jsocks/Socks5Message.java @@ -0,0 +1,313 @@ +package net.sourceforge.jsocks; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * SOCKS5 request/response message. + */ + +public class Socks5Message extends ProxyMessage { + /** Address type of given message */ + public int addrType; + + byte[] data; + + // SOCKS5 constants + public static final int SOCKS_VERSION = 5; + + public static final int SOCKS_ATYP_IPV4 = 0x1; // Where is 2?? + + public static final int SOCKS_ATYP_DOMAINNAME = 0x3; // !!!!rfc1928 + + public static final int SOCKS_ATYP_IPV6 = 0x4; + + public static final int SOCKS_IPV6_LENGTH = 16; + + static boolean doResolveIP = true; + + /** + * Wether to resolve hostIP returned from SOCKS server that is wether to + * create InetAddress object from the hostName string + */ + static public boolean resolveIP() { + return doResolveIP; + } + + /** + * Wether to resolve hostIP returned from SOCKS server that is wether to + * create InetAddress object from the hostName string + * + * @param doResolve + * Wether to resolve hostIP from SOCKS server. + * @return Previous value. + */ + static public boolean resolveIP(boolean doResolve) { + boolean old = doResolveIP; + doResolveIP = doResolve; + return old; + } + + /** + * Initialises Message from the stream. Reads server response from given + * stream. + * + * @param in + * Input stream to read response from. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0), or if any + * error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public Socks5Message(InputStream in) throws SocksException, IOException { + this(in, true); + } + + /** + * Initialises Message from the stream. Reads server response or client + * request from given stream. + * + * @param in + * Input stream to read response from. + * @param clinetMode + * If true read server response, else read client request. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0) and reading + * in client mode, or if any error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public Socks5Message(InputStream in, boolean clientMode) + throws SocksException, IOException { + read(in, clientMode); + } + + /** + * Server error response. + * + * @param cmd + * Error code. + */ + public Socks5Message(int cmd) { + super(cmd, null, 0); + data = new byte[3]; + data[0] = SOCKS_VERSION; // Version. + data[1] = (byte) cmd; // Reply code for some kind of failure. + data[2] = 0; // Reserved byte. + } + + /** + * Construct client request or server response. + * + * @param cmd + * - Request/Response code. + * @param ip + * - IP field. + * @paarm port - port field. + */ + public Socks5Message(int cmd, InetAddress ip, int port) { + super(cmd, ip, port); + this.host = ip == null ? "0.0.0.0" : ip.getHostName(); + this.version = SOCKS_VERSION; + + byte[] addr; + + if (ip == null) { + addr = new byte[4]; + addr[0] = addr[1] = addr[2] = addr[3] = 0; + } else + addr = ip.getAddress(); + + addrType = addr.length == 4 ? SOCKS_ATYP_IPV4 : SOCKS_ATYP_IPV6; + + data = new byte[6 + addr.length]; + data[0] = (byte) SOCKS_VERSION; // Version + data[1] = (byte) command; // Command + data[2] = (byte) 0; // Reserved byte + data[3] = (byte) addrType; // Address type + + // Put Address + System.arraycopy(addr, 0, data, 4, addr.length); + // Put port + data[data.length - 2] = (byte) (port >> 8); + data[data.length - 1] = (byte) (port); + } + + /* + * private static final void debug(String s){ if(DEBUG) System.out.print(s); + * } private static final boolean DEBUG = false; + */ + + /** + * Construct client request or server response. + * + * @param cmd + * - Request/Response code. + * @param hostName + * - IP field as hostName, uses ADDR_TYPE of HOSTNAME. + * @paarm port - port field. + */ + public Socks5Message(int cmd, String hostName, int port) { + super(cmd, null, port); + this.host = hostName; + this.version = SOCKS_VERSION; + + // System.out.println("Doing ATYP_DOMAINNAME"); + + addrType = SOCKS_ATYP_DOMAINNAME; + byte addr[] = hostName.getBytes(); + + data = new byte[7 + addr.length]; + data[0] = (byte) SOCKS_VERSION; // Version + data[1] = (byte) command; // Command + data[2] = (byte) 0; // Reserved byte + data[3] = (byte) SOCKS_ATYP_DOMAINNAME; // Address type + data[4] = (byte) addr.length; // Length of the address + + // Put Address + System.arraycopy(addr, 0, data, 5, addr.length); + // Put port + data[data.length - 2] = (byte) (port >> 8); + data[data.length - 1] = (byte) (port); + } + + /** + * Returns IP field of the message as IP, if the message was created with + * ATYP of HOSTNAME, it will attempt to resolve the hostname, which might + * fail. + * + * @throws UnknownHostException + * if host can't be resolved. + */ + @Override + public InetAddress getInetAddress() throws UnknownHostException { + if (ip != null) + return ip; + + return (ip = InetAddress.getByName(host)); + } + /** + * Initialises Message from the stream. Reads server response from given + * stream. + * + * @param in + * Input stream to read response from. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0), or if any + * error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + @Override + public void read(InputStream in) throws SocksException, IOException { + read(in, true); + } + /** + * Initialises Message from the stream. Reads server response or client + * request from given stream. + * + * @param in + * Input stream to read response from. + * @param clinetMode + * If true read server response, else read client request. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0) and reading + * in client mode, or if any error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + @Override + public void read(InputStream in, boolean clientMode) throws SocksException, + IOException { + data = null; + ip = null; + + DataInputStream di = new DataInputStream(in); + + version = di.readUnsignedByte(); + command = di.readUnsignedByte(); + if (clientMode && command != 0) + throw new SocksException(command); + + @SuppressWarnings("unused") + int reserved = di.readUnsignedByte(); + addrType = di.readUnsignedByte(); + + byte addr[]; + + switch (addrType) { + case SOCKS_ATYP_IPV4: + addr = new byte[4]; + di.readFully(addr); + host = bytes2IPV4(addr, 0); + break; + case SOCKS_ATYP_IPV6: + addr = new byte[SOCKS_IPV6_LENGTH];// I believe it is 16 bytes,huge! + di.readFully(addr); + host = bytes2IPV6(addr, 0); + break; + case SOCKS_ATYP_DOMAINNAME: + // System.out.println("Reading ATYP_DOMAINNAME"); + addr = new byte[di.readUnsignedByte()];// Next byte shows the length + di.readFully(addr); + host = new String(addr); + break; + default: + throw (new SocksException(Proxy.SOCKS_JUST_ERROR)); + } + + port = di.readUnsignedShort(); + + if (addrType != SOCKS_ATYP_DOMAINNAME && doResolveIP) { + try { + ip = InetAddress.getByName(host); + } catch (UnknownHostException uh_ex) { + } + } + } + + /** + * Returns string representation of the message. + */ + @Override + public String toString() { + String s = "Socks5Message:" + "\n" + "VN " + version + "\n" + "CMD " + + command + "\n" + "ATYP " + addrType + "\n" + "ADDR " + host + + "\n" + "PORT " + port + "\n"; + return s; + } + + /** + * Writes the message to the stream. + * + * @param out + * Output stream to which message should be written. + */ + @Override + public void write(OutputStream out) throws SocksException, IOException { + if (data == null) { + Socks5Message msg; + + if (addrType == SOCKS_ATYP_DOMAINNAME) + msg = new Socks5Message(command, host, port); + else { + if (ip == null) { + try { + ip = InetAddress.getByName(host); + } catch (UnknownHostException uh_ex) { + throw new SocksException(Proxy.SOCKS_JUST_ERROR); + } + } + msg = new Socks5Message(command, ip, port); + } + data = msg.data; + } + out.write(data); + } + +} diff --git a/service/src/main/java/net/sourceforge/jsocks/SocksException.java b/service/src/main/java/net/sourceforge/jsocks/SocksException.java new file mode 100644 index 0000000..5d67034 --- /dev/null +++ b/service/src/main/java/net/sourceforge/jsocks/SocksException.java @@ -0,0 +1,80 @@ +package net.sourceforge.jsocks; + +/** + * Exception thrown by various socks classes to indicate errors with protocol or + * unsuccessful server responses. + */ +public class SocksException extends java.io.IOException { + private static final long serialVersionUID = 6141184566248512277L; + + static final String UNASSIGNED_ERROR_MESSAGE = "Unknown error message"; + + static final String serverReplyMessage[] = { "Succeeded", + "General SOCKS server failure", + "Connection not allowed by ruleset", "Network unreachable", + "Host unreachable", "Connection refused", "TTL expired", + "Command not supported", "Address type not supported" }; + + static final String localErrorMessage[] = { "SOCKS server not specified", + "Unable to contact SOCKS server", "IO error", + "None of Authentication methods are supported", + "Authentication failed", "General SOCKS fault" }; + + String errString; + + public int errCode; + /** + * Construct a SocksException with given error code. + *

+ * Tries to look up message which corresponds to this error code. + * + * @param errCode + * Error code for this exception. + */ + public SocksException(int errCode) { + this.errCode = errCode; + if ((errCode >> 16) == 0) { + // Server reply error message + errString = errCode <= serverReplyMessage.length ? serverReplyMessage[errCode] + : UNASSIGNED_ERROR_MESSAGE; + } else { + // Local error + errCode = (errCode >> 16) - 1; + errString = errCode <= localErrorMessage.length ? localErrorMessage[errCode] + : UNASSIGNED_ERROR_MESSAGE; + } + } + + /** + * Constructs a SocksException with given error code and message. + * + * @param errCode + * Error code. + * @param errString + * Error Message. + */ + public SocksException(int errCode, String errString) { + this.errCode = errCode; + this.errString = errString; + } + + /** + * Get the error code associated with this exception. + * + * @return Error code associated with this exception. + */ + public int getErrorCode() { + return errCode; + } + /** + * Get human readable representation of this exception. + * + * @return String represntation of this exception. + */ + @Override + public String toString() { + return errString; + } + +}// End of SocksException class + diff --git a/service/src/main/java/net/sourceforge/jsocks/server/ServerAuthenticator.java b/service/src/main/java/net/sourceforge/jsocks/server/ServerAuthenticator.java new file mode 100644 index 0000000..4be118d --- /dev/null +++ b/service/src/main/java/net/sourceforge/jsocks/server/ServerAuthenticator.java @@ -0,0 +1,112 @@ +package net.sourceforge.jsocks.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.DatagramPacket; +import java.net.Socket; + +import net.sourceforge.jsocks.ProxyMessage; + +/** + * Classes implementing this interface should provide socks server with + * authentication and authorization of users. + **/ +public interface ServerAuthenticator { + + /** + * This method is called when datagram is received by the server. + *

+ * Implementaions should decide wether it should be forwarded or dropped. It + * is expecteed that implementation will use datagram address and port + * information to make a decision, as well as anything else. Address and + * port of the datagram are always correspond to remote machine. It is + * either destination or source address. If out is true address is + * destination address, else it is a source address, address of the machine + * from which datagram have been received for the client. + *

+ * Implementaions should return true if the datagram is to be forwarded, and + * false if the datagram should be dropped. + *

+ * This method is called on the object returned from the startSession + * function. + * + * @param out + * If true the datagram is being send out(from the client), + * otherwise it is an incoming datagram. + * @return True to forward datagram false drop it silently. + */ + boolean checkRequest(DatagramPacket dp, boolean out); + + /** + * This method is called when a request have been read. + *

+ * Implementation should decide wether to grant request or not. Returning + * true implies granting the request, false means request should be + * rejected. + *

+ * This method is called on the object returned from the startSession + * function. + * + * @param msg + * Request message. + * @return true to grant request, false to reject it. + */ + boolean checkRequest(ProxyMessage msg); + + /** + * This method is called when session is completed. Either due to normal + * termination or due to any error condition. + *

+ * This method is called on the object returned from the startSession + * function. + */ + void endSession(); + + /** + * This method should return input stream which should be used on the + * accepted socket. + *

+ * SOCKSv5 allows to have multiple authentication methods, and these methods + * might require some kind of transformations being made on the data. + *

+ * This method is called on the object returned from the startSession + * function. + */ + InputStream getInputStream(); + + /** + * This method should return output stream to use to write to the accepted + * socket. + *

+ * SOCKSv5 allows to have multiple authentication methods, and these methods + * might require some kind of transformations being made on the data. + *

+ * This method is called on the object returned from the startSession + * function. + */ + OutputStream getOutputStream(); + + /** + * This method is called when a new connection accepted by the server. + *

+ * At this point no data have been extracted from the connection. It is + * responsibility of this method to ensure that the next byte in the stream + * after this method have been called is the first byte of the socks request + * message. For SOCKSv4 there is no authentication data and the first byte + * in the stream is part of the request. With SOCKSv5 however there is an + * authentication data first. It is expected that implementaions will + * process this authentication data. + *

+ * If authentication was successful an instance of ServerAuthentication + * should be returned, it later will be used by the server to perform + * authorization and some other things. If authentication fails null should + * be returned, or an exception may be thrown. + * + * @param s + * Accepted Socket. + * @return An instance of ServerAuthenticator to be used for this connection + * or null + */ + ServerAuthenticator startSession(Socket s) throws IOException; +} diff --git a/service/src/main/java/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java b/service/src/main/java/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java new file mode 100644 index 0000000..e54dd9a --- /dev/null +++ b/service/src/main/java/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java @@ -0,0 +1,174 @@ +package net.sourceforge.jsocks.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.net.Socket; + +import net.sourceforge.jsocks.ProxyMessage; + +/** + * An implementation of ServerAuthenticator, which does not do any + * authentication. + *

+ * Warning!!
+ * Should not be used on machines which are not behind the firewall. + *

+ * It is only provided to make implementing other authentication schemes easier. + *
+ * For Example:

+   class MyAuth extends socks.server.ServerAuthenticator{
+    ...
+    public ServerAuthenticator startSession(java.net.Socket s){
+      if(!checkHost(s.getInetAddress()) return null;
+      return super.startSession(s);
+    }
+
+    boolean checkHost(java.net.Inetaddress addr){
+      boolean allow;
+      //Do it somehow
+      return allow;
+    }
+   }
+
+ */ +public class ServerAuthenticatorNone implements ServerAuthenticator { + + static final byte[] socks5response = { 5, 0 }; + + /** + * Convinience routine for selecting SOCKSv5 authentication. + *

+ * This method reads in authentication methods that client supports, checks + * wether it supports given method. If it does, the notification method is + * written back to client, that this method have been chosen for + * authentication. If given method was not found, authentication failure + * message is send to client ([5,FF]). + * + * @param in + * Input stream, version byte should be removed from the stream + * before calling this method. + * @param out + * Output stream. + * @param methodId + * Method which should be selected. + * @return true if methodId was found, false otherwise. + */ + static public boolean selectSocks5Authentication(InputStream in, + OutputStream out, int methodId) throws IOException { + + int num_methods = in.read(); + if (num_methods <= 0) + return false; + byte method_ids[] = new byte[num_methods]; + byte response[] = new byte[2]; + boolean found = false; + + response[0] = (byte) 5; // SOCKS version + response[1] = (byte) 0xFF; // Not found, we are pessimistic + + int bread = 0; // bytes read so far + while (bread < num_methods) + bread += in.read(method_ids, bread, num_methods - bread); + + for (int i = 0; i < num_methods; ++i) + if (method_ids[i] == methodId) { + found = true; + response[1] = (byte) methodId; + break; + } + + out.write(response); + return found; + } + InputStream in; + + OutputStream out; + + /** + * Creates new instance of the ServerAuthenticatorNone. + */ + public ServerAuthenticatorNone() { + this.in = null; + this.out = null; + } + + /** + * Constructs new ServerAuthenticatorNone object suitable for returning from + * the startSession function. + * + * @param in + * Input stream to return from getInputStream method. + * @param out + * Output stream to return from getOutputStream method. + */ + public ServerAuthenticatorNone(InputStream in, OutputStream out) { + this.in = in; + this.out = out; + } + + /** + * Allways returns true. + */ + @Override + public boolean checkRequest(java.net.DatagramPacket dp, boolean out) { + return true; + } + + /** + * Allways returns true. + */ + @Override + public boolean checkRequest(ProxyMessage msg) { + return true; + } + + /** + * Does nothing. + */ + @Override + public void endSession() { + } + + /** + * Get input stream. + * + * @return Input stream speciefied in the constructor. + */ + @Override + public InputStream getInputStream() { + return in; + } + + /** + * Get output stream. + * + * @return Output stream speciefied in the constructor. + */ + @Override + public OutputStream getOutputStream() { + return out; + } + + /** + * Grants access to everyone.Removes authentication related bytes from the + * stream, when a SOCKS5 connection is being made, selects an authentication + * NONE. + */ + @Override + public ServerAuthenticator startSession(Socket s) throws IOException { + + PushbackInputStream in = new PushbackInputStream(s.getInputStream()); + OutputStream out = s.getOutputStream(); + + int version = in.read(); + if (version == 5) { + if (!selectSocks5Authentication(in, out, 0)) + return null; + } else + return null; + + return new ServerAuthenticatorNone(in, out); + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/CipherParameters.java b/service/src/main/java/org/spongycastle/crypto/CipherParameters.java new file mode 100644 index 0000000..5a9576b --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/CipherParameters.java @@ -0,0 +1,8 @@ +package org.spongycastle.crypto; + +/** + * all parameter classes implement this. + */ +public interface CipherParameters +{ +} diff --git a/service/src/main/java/org/spongycastle/crypto/DataLengthException.java b/service/src/main/java/org/spongycastle/crypto/DataLengthException.java new file mode 100644 index 0000000..c89e8bf --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/DataLengthException.java @@ -0,0 +1,29 @@ +package org.spongycastle.crypto; + +/** + * this exception is thrown if a buffer that is meant to have output + * copied into it turns out to be too short, or if we've been given + * insufficient input. In general this exception will get thrown rather + * than an ArrayOutOfBounds exception. + */ +public class DataLengthException + extends RuntimeCryptoException +{ + /** + * base constructor. + */ + public DataLengthException() + { + } + + /** + * create a DataLengthException with the given message. + * + * @param message the message to be carried with the exception. + */ + public DataLengthException( + String message) + { + super(message); + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/Digest.java b/service/src/main/java/org/spongycastle/crypto/Digest.java new file mode 100644 index 0000000..33103b2 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/Digest.java @@ -0,0 +1,51 @@ +package org.spongycastle.crypto; + +/** + * interface that a message digest conforms to. + */ +public interface Digest +{ + /** + * return the algorithm name + * + * @return the algorithm name + */ + public String getAlgorithmName(); + + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + public int getDigestSize(); + + /** + * update the message digest with a single byte. + * + * @param in the input byte to be entered. + */ + public void update(byte in); + + /** + * update the message digest with a block of bytes. + * + * @param in the byte array containing the data. + * @param inOff the offset into the byte array where the data starts. + * @param len the length of the data. + */ + public void update(byte[] in, int inOff, int len); + + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param out the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + public int doFinal(byte[] out, int outOff); + + /** + * reset the digest back to it's initial state. + */ + public void reset(); +} diff --git a/service/src/main/java/org/spongycastle/crypto/ExtendedDigest.java b/service/src/main/java/org/spongycastle/crypto/ExtendedDigest.java new file mode 100644 index 0000000..7cc339f --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/ExtendedDigest.java @@ -0,0 +1,13 @@ +package org.spongycastle.crypto; + +public interface ExtendedDigest + extends Digest +{ + /** + * Return the size in bytes of the internal buffer the digest applies it's compression + * function to. + * + * @return byte length of the digests internal buffer. + */ + public int getByteLength(); +} diff --git a/service/src/main/java/org/spongycastle/crypto/Mac.java b/service/src/main/java/org/spongycastle/crypto/Mac.java new file mode 100644 index 0000000..8be6114 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/Mac.java @@ -0,0 +1,71 @@ +package org.spongycastle.crypto; + + +/** + * The base interface for implementations of message authentication codes (MACs). + */ +public interface Mac +{ + /** + * Initialise the MAC. + * + * @param params the key and other data required by the MAC. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the MAC implements. + * + * @return the name of the algorithm the MAC implements. + */ + public String getAlgorithmName(); + + /** + * Return the block size for this MAC (in bytes). + * + * @return the block size for this MAC in bytes. + */ + public int getMacSize(); + + /** + * add a single byte to the mac for processing. + * + * @param in the byte to be processed. + * @exception IllegalStateException if the MAC is not initialised. + */ + public void update(byte in) + throws IllegalStateException; + + /** + * @param in the array containing the input. + * @param inOff the index in the array the data begins at. + * @param len the length of the input starting at inOff. + * @exception IllegalStateException if the MAC is not initialised. + * @exception DataLengthException if there isn't enough data in in. + */ + public void update(byte[] in, int inOff, int len) + throws DataLengthException, IllegalStateException; + + /** + * Compute the final stage of the MAC writing the output to the out + * parameter. + *

+ * doFinal leaves the MAC in the same state it was after the last init. + * + * @param out the array the MAC is to be output to. + * @param outOff the offset into the out buffer the output is to start at. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the MAC is not initialised. + */ + public int doFinal(byte[] out, int outOff) + throws DataLengthException, IllegalStateException; + + /** + * Reset the MAC. At the end of resetting the MAC should be in the + * in the same state it was after the last init (if there was one). + */ + public void reset(); +} diff --git a/service/src/main/java/org/spongycastle/crypto/PBEParametersGenerator.java b/service/src/main/java/org/spongycastle/crypto/PBEParametersGenerator.java new file mode 100644 index 0000000..6788d14 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/PBEParametersGenerator.java @@ -0,0 +1,171 @@ +package org.spongycastle.crypto; + +import org.spongycastle.util.Strings; + +/** + * super class for all Password Based Encryption (PBE) parameter generator classes. + */ +public abstract class PBEParametersGenerator +{ + protected byte[] password; + protected byte[] salt; + protected int iterationCount; + + /** + * base constructor. + */ + protected PBEParametersGenerator() + { + } + + /** + * initialise the PBE generator. + * + * @param password the password converted into bytes (see below). + * @param salt the salt to be mixed with the password. + * @param iterationCount the number of iterations the "mixing" function + * is to be applied for. + */ + public void init( + byte[] password, + byte[] salt, + int iterationCount) + { + this.password = password; + this.salt = salt; + this.iterationCount = iterationCount; + } + + /** + * return the password byte array. + * + * @return the password byte array. + */ + public byte[] getPassword() + { + return password; + } + + /** + * return the salt byte array. + * + * @return the salt byte array. + */ + public byte[] getSalt() + { + return salt; + } + + /** + * return the iteration count. + * + * @return the iteration count. + */ + public int getIterationCount() + { + return iterationCount; + } + + /** + * generate derived parameters for a key of length keySize. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + public abstract CipherParameters generateDerivedParameters(int keySize); + + /** + * generate derived parameters for a key of length keySize, and + * an initialisation vector (IV) of length ivSize. + * + * @param keySize the length, in bits, of the key required. + * @param ivSize the length, in bits, of the iv required. + * @return a parameters object representing a key and an IV. + */ + public abstract CipherParameters generateDerivedParameters(int keySize, int ivSize); + + /** + * generate derived parameters for a key of length keySize, specifically + * for use with a MAC. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + public abstract CipherParameters generateDerivedMacParameters(int keySize); + + /** + * converts a password to a byte array according to the scheme in + * PKCS5 (ascii, no padding) + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS5PasswordToBytes( + char[] password) + { + if (password != null) + { + byte[] bytes = new byte[password.length]; + + for (int i = 0; i != bytes.length; i++) + { + bytes[i] = (byte)password[i]; + } + + return bytes; + } + else + { + return new byte[0]; + } + } + + /** + * converts a password to a byte array according to the scheme in + * PKCS5 (UTF-8, no padding) + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS5PasswordToUTF8Bytes( + char[] password) + { + if (password != null) + { + return Strings.toUTF8ByteArray(password); + } + else + { + return new byte[0]; + } + } + + /** + * converts a password to a byte array according to the scheme in + * PKCS12 (unicode, big endian, 2 zero pad bytes at the end). + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS12PasswordToBytes( + char[] password) + { + if (password != null && password.length > 0) + { + // +1 for extra 2 pad bytes. + byte[] bytes = new byte[(password.length + 1) * 2]; + + for (int i = 0; i != password.length; i ++) + { + bytes[i * 2] = (byte)(password[i] >>> 8); + bytes[i * 2 + 1] = (byte)password[i]; + } + + return bytes; + } + else + { + return new byte[0]; + } + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/RuntimeCryptoException.java b/service/src/main/java/org/spongycastle/crypto/RuntimeCryptoException.java new file mode 100644 index 0000000..4eeb18f --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/RuntimeCryptoException.java @@ -0,0 +1,26 @@ +package org.spongycastle.crypto; + +/** + * the foundation class for the exceptions thrown by the crypto packages. + */ +public class RuntimeCryptoException + extends RuntimeException +{ + /** + * base constructor. + */ + public RuntimeCryptoException() + { + } + + /** + * create a RuntimeCryptoException with the given message. + * + * @param message the message to be carried with the exception. + */ + public RuntimeCryptoException( + String message) + { + super(message); + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/EncodableDigest.java b/service/src/main/java/org/spongycastle/crypto/digests/EncodableDigest.java new file mode 100644 index 0000000..643df2f --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/EncodableDigest.java @@ -0,0 +1,17 @@ +package org.spongycastle.crypto.digests; + +/** + * Encodable digests allow you to download an encoded copy of their internal state. This is useful for the situation where + * you need to generate a signature on an external device and it allows for "sign with last round", so a copy of the + * internal state of the digest, plus the last few blocks of the message are all that needs to be sent, rather than the + * entire message. + */ +public interface EncodableDigest +{ + /** + * Return an encoded byte array for the digest's internal state + * + * @return an encoding of the digests internal state. + */ + byte[] getEncodedState(); +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/GeneralDigest.java b/service/src/main/java/org/spongycastle/crypto/digests/GeneralDigest.java new file mode 100644 index 0000000..2cb24d9 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/GeneralDigest.java @@ -0,0 +1,160 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + +/** + * base implementation of MD4 family style digest as outlined in + * "Handbook of Applied Cryptography", pages 344 - 347. + */ +public abstract class GeneralDigest + implements ExtendedDigest, Memoable +{ + private static final int BYTE_LENGTH = 64; + + private final byte[] xBuf = new byte[4]; + private int xBufOff; + + private long byteCount; + + /** + * Standard constructor + */ + protected GeneralDigest() + { + xBufOff = 0; + } + + /** + * Copy constructor. We are using copy constructors in place + * of the Object.clone() interface as this interface is not + * supported by J2ME. + */ + protected GeneralDigest(GeneralDigest t) + { + copyIn(t); + } + + protected GeneralDigest(byte[] encodedState) + { + System.arraycopy(encodedState, 0, xBuf, 0, xBuf.length); + xBufOff = Pack.bigEndianToInt(encodedState, 4); + byteCount = Pack.bigEndianToLong(encodedState, 8); + } + + protected void copyIn(GeneralDigest t) + { + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void update( + byte in) + { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) + { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void update( + byte[] in, + int inOff, + int len) + { + len = Math.max(0, len); + + // + // fill the current word + // + int i = 0; + if (xBufOff != 0) + { + while (i < len) + { + xBuf[xBufOff++] = in[inOff + i++]; + if (xBufOff == 4) + { + processWord(xBuf, 0); + xBufOff = 0; + break; + } + } + } + + // + // process whole words. + // + int limit = ((len - i) & ~3) + i; + for (; i < limit; i += 4) + { + processWord(in, inOff + i); + } + + // + // load in the remainder. + // + while (i < len) + { + xBuf[xBufOff++] = in[inOff + i++]; + } + + byteCount += len; + } + + public void finish() + { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + update((byte)128); + + while (xBufOff != 0) + { + update((byte)0); + } + + processLength(bitLength); + + processBlock(); + } + + public void reset() + { + byteCount = 0; + + xBufOff = 0; + for (int i = 0; i < xBuf.length; i++) + { + xBuf[i] = 0; + } + } + + protected void populateState(byte[] state) + { + System.arraycopy(xBuf, 0, state, 0, xBufOff); + Pack.intToBigEndian(xBufOff, state, 4); + Pack.longToBigEndian(byteCount, state, 8); + } + + public int getByteLength() + { + return BYTE_LENGTH; + } + + protected abstract void processWord(byte[] in, int inOff); + + protected abstract void processLength(long bitLength); + + protected abstract void processBlock(); +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/KeccakDigest.java b/service/src/main/java/org/spongycastle/crypto/digests/KeccakDigest.java new file mode 100644 index 0000000..d5c0547 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/KeccakDigest.java @@ -0,0 +1,489 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Pack; + +/** + * implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + *

+ * Following the naming conventions used in the C source code to enable easy review of the implementation. + */ +public class KeccakDigest + implements ExtendedDigest +{ + private static long[] KeccakRoundConstants = keccakInitializeRoundConstants(); + + private static int[] KeccakRhoOffsets = keccakInitializeRhoOffsets(); + + private static long[] keccakInitializeRoundConstants() + { + long[] keccakRoundConstants = new long[24]; + byte[] LFSRstate = new byte[1]; + + LFSRstate[0] = 0x01; + int i, j, bitPosition; + + for (i = 0; i < 24; i++) + { + keccakRoundConstants[i] = 0; + for (j = 0; j < 7; j++) + { + bitPosition = (1 << j) - 1; + if (LFSR86540(LFSRstate)) + { + keccakRoundConstants[i] ^= 1L << bitPosition; + } + } + } + + return keccakRoundConstants; + } + + private static boolean LFSR86540(byte[] LFSR) + { + boolean result = (((LFSR[0]) & 0x01) != 0); + if (((LFSR[0]) & 0x80) != 0) + { + LFSR[0] = (byte)(((LFSR[0]) << 1) ^ 0x71); + } + else + { + LFSR[0] <<= 1; + } + + return result; + } + + private static int[] keccakInitializeRhoOffsets() + { + int[] keccakRhoOffsets = new int[25]; + int x, y, t, newX, newY; + + keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = 0; + x = 1; + y = 0; + for (t = 0; t < 24; t++) + { + keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = ((t + 1) * (t + 2) / 2) % 64; + newX = (0 * x + 1 * y) % 5; + newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + return keccakRhoOffsets; + } + + protected long[] state = new long[(1600 / 64)]; + protected byte[] dataQueue = new byte[(1536 / 8)]; + protected int rate; + protected int bitsInQueue; + protected int fixedOutputLength; + protected boolean squeezing; + + public KeccakDigest() + { + this(288); + } + + public KeccakDigest(int bitLength) + { + init(bitLength); + } + + public KeccakDigest(KeccakDigest source) + { + System.arraycopy(source.state, 0, this.state, 0, source.state.length); + System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length); + this.rate = source.rate; + this.bitsInQueue = source.bitsInQueue; + this.fixedOutputLength = source.fixedOutputLength; + this.squeezing = source.squeezing; + } + + public String getAlgorithmName() + { + return "Keccak-" + fixedOutputLength; + } + + public int getDigestSize() + { + return fixedOutputLength / 8; + } + + public void update(byte in) + { + absorb(new byte[]{ in }, 0, 1); + } + + public void update(byte[] in, int inOff, int len) + { + absorb(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + squeeze(out, outOff, fixedOutputLength); + + reset(); + + return getDigestSize(); + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits) + { + if (partialBits > 0) + { + absorbBits(partialByte, partialBits); + } + + squeeze(out, outOff, fixedOutputLength); + + reset(); + + return getDigestSize(); + } + + public void reset() + { + init(fixedOutputLength); + } + + /** + * Return the size of block that the compression function is applied to in bytes. + * + * @return internal byte length of a block. + */ + public int getByteLength() + { + return rate / 8; + } + + private void init(int bitLength) + { + switch (bitLength) + { + case 128: + case 224: + case 256: + case 288: + case 384: + case 512: + initSponge(1600 - (bitLength << 1)); + break; + default: + throw new IllegalArgumentException("bitLength must be one of 128, 224, 256, 288, 384, or 512."); + } + } + + private void initSponge(int rate) + { + if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0)) + { + throw new IllegalStateException("invalid rate value"); + } + + this.rate = rate; + for (int i = 0; i < state.length; ++i) + { + state[i] = 0L; + } + Arrays.fill(this.dataQueue, (byte)0); + this.bitsInQueue = 0; + this.squeezing = false; + this.fixedOutputLength = (1600 - rate) / 2; + } + + protected void absorb(byte[] data, int off, int len) + { + if ((bitsInQueue % 8) != 0) + { + throw new IllegalStateException("attempt to absorb with odd length queue"); + } + if (squeezing) + { + throw new IllegalStateException("attempt to absorb while squeezing"); + } + + int bytesInQueue = bitsInQueue >> 3; + int rateBytes = rate >> 3; + + int count = 0; + while (count < len) + { + if (bytesInQueue == 0 && count <= (len - rateBytes)) + { + do + { + KeccakAbsorb(data, off + count); + count += rateBytes; + } + while (count <= (len - rateBytes)); + } + else + { + int partialBlock = Math.min(rateBytes - bytesInQueue, len - count); + System.arraycopy(data, off + count, dataQueue, bytesInQueue, partialBlock); + + bytesInQueue += partialBlock; + count += partialBlock; + + if (bytesInQueue == rateBytes) + { + KeccakAbsorb(dataQueue, 0); + bytesInQueue = 0; + } + } + } + + bitsInQueue = bytesInQueue << 3; + } + + protected void absorbBits(int data, int bits) + { + if (bits < 1 || bits > 7) + { + throw new IllegalArgumentException("'bits' must be in the range 1 to 7"); + } + if ((bitsInQueue % 8) != 0) + { + throw new IllegalStateException("attempt to absorb with odd length queue"); + } + if (squeezing) + { + throw new IllegalStateException("attempt to absorb while squeezing"); + } + + int mask = (1 << bits) - 1; + dataQueue[bitsInQueue >> 3] = (byte)(data & mask); + + // NOTE: After this, bitsInQueue is no longer a multiple of 8, so no more absorbs will work + bitsInQueue += bits; + } + + private void padAndSwitchToSqueezingPhase() + { + dataQueue[bitsInQueue >> 3] |= (byte)(1L << (bitsInQueue & 7)); + + if (++bitsInQueue == rate) + { + KeccakAbsorb(dataQueue, 0); + bitsInQueue = 0; + } + + { + int full = bitsInQueue >> 6, partial = bitsInQueue & 63; + int off = 0; + for (int i = 0; i < full; ++i) + { + state[i] ^= Pack.littleEndianToLong(dataQueue, off); + off += 8; + } + if (partial > 0) + { + long mask = (1L << partial) - 1L; + state[full] ^= Pack.littleEndianToLong(dataQueue, off) & mask; + } + state[(rate - 1) >> 6] ^= (1L << 63); + } + + KeccakPermutation(); + +// displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---"); + KeccakExtract(); + bitsInQueue = rate; + +// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsInQueue / 8); + squeezing = true; + } + + protected void squeeze(byte[] output, int offset, long outputLength) + { + if (!squeezing) + { + padAndSwitchToSqueezingPhase(); + } + if ((outputLength % 8) != 0) + { + throw new IllegalStateException("outputLength not a multiple of 8"); + } + + long i = 0; + while (i < outputLength) + { + if (bitsInQueue == 0) + { + KeccakPermutation(); + KeccakExtract(); + bitsInQueue = rate; +// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8); + } + int partialBlock = (int)Math.min((long)bitsInQueue, outputLength - i); + System.arraycopy(dataQueue, (rate - bitsInQueue) / 8, output, offset + (int)(i / 8), partialBlock / 8); + bitsInQueue -= partialBlock; + i += partialBlock; + } + } + + private void KeccakAbsorb(byte[] data, int off) + { + int count = rate >> 6; + for (int i = 0; i < count; ++i) + { + state[i] ^= Pack.littleEndianToLong(data, off); + off += 8; + } + + KeccakPermutation(); + } + + private void KeccakExtract() + { + Pack.longToLittleEndian(state, 0, rate >> 6, dataQueue, 0); + } + + private void KeccakPermutation() + { +// displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state); + + for (int i = 0; i < 24; i++) + { +// displayIntermediateValues.displayRoundNumber(3, i); + + theta(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state); + + rho(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state); + + pi(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state); + + chi(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state); + + iota(state, i); +// displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state); + } + } + + private static long leftRotate(long v, int r) + { + return (v << r) | (v >>> -r); + } + + private static void theta(long[] A) + { + long C0 = A[0 + 0] ^ A[0 + 5] ^ A[0 + 10] ^ A[0 + 15] ^ A[0 + 20]; + long C1 = A[1 + 0] ^ A[1 + 5] ^ A[1 + 10] ^ A[1 + 15] ^ A[1 + 20]; + long C2 = A[2 + 0] ^ A[2 + 5] ^ A[2 + 10] ^ A[2 + 15] ^ A[2 + 20]; + long C3 = A[3 + 0] ^ A[3 + 5] ^ A[3 + 10] ^ A[3 + 15] ^ A[3 + 20]; + long C4 = A[4 + 0] ^ A[4 + 5] ^ A[4 + 10] ^ A[4 + 15] ^ A[4 + 20]; + + long dX = leftRotate(C1, 1) ^ C4; + + A[0] ^= dX; + A[5] ^= dX; + A[10] ^= dX; + A[15] ^= dX; + A[20] ^= dX; + + dX = leftRotate(C2, 1) ^ C0; + + A[1] ^= dX; + A[6] ^= dX; + A[11] ^= dX; + A[16] ^= dX; + A[21] ^= dX; + + dX = leftRotate(C3, 1) ^ C1; + + A[2] ^= dX; + A[7] ^= dX; + A[12] ^= dX; + A[17] ^= dX; + A[22] ^= dX; + + dX = leftRotate(C4, 1) ^ C2; + + A[3] ^= dX; + A[8] ^= dX; + A[13] ^= dX; + A[18] ^= dX; + A[23] ^= dX; + + dX = leftRotate(C0, 1) ^ C3; + + A[4] ^= dX; + A[9] ^= dX; + A[14] ^= dX; + A[19] ^= dX; + A[24] ^= dX; + } + + private static void rho(long[] A) + { + // KeccakRhoOffsets[0] == 0 + for (int x = 1; x < 25; x++) + { + A[x] = leftRotate(A[x], KeccakRhoOffsets[x]); + } + } + + private static void pi(long[] A) + { + long a1 = A[1]; + A[1] = A[6]; + A[6] = A[9]; + A[9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[2]; + A[2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[4]; + A[4] = A[24]; + A[24] = A[21]; + A[21] = A[8]; + A[8] = A[16]; + A[16] = A[5]; + A[5] = A[3]; + A[3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[7]; + A[7] = A[10]; + A[10] = a1; + } + + private static void chi(long[] A) + { + long chiC0, chiC1, chiC2, chiC3, chiC4; + + for (int yBy5 = 0; yBy5 < 25; yBy5 += 5) + { + chiC0 = A[0 + yBy5] ^ ((~A[(((0 + 1) % 5) + yBy5)]) & A[(((0 + 2) % 5) + yBy5)]); + chiC1 = A[1 + yBy5] ^ ((~A[(((1 + 1) % 5) + yBy5)]) & A[(((1 + 2) % 5) + yBy5)]); + chiC2 = A[2 + yBy5] ^ ((~A[(((2 + 1) % 5) + yBy5)]) & A[(((2 + 2) % 5) + yBy5)]); + chiC3 = A[3 + yBy5] ^ ((~A[(((3 + 1) % 5) + yBy5)]) & A[(((3 + 2) % 5) + yBy5)]); + chiC4 = A[4 + yBy5] ^ ((~A[(((4 + 1) % 5) + yBy5)]) & A[(((4 + 2) % 5) + yBy5)]); + + A[0 + yBy5] = chiC0; + A[1 + yBy5] = chiC1; + A[2 + yBy5] = chiC2; + A[3 + yBy5] = chiC3; + A[4 + yBy5] = chiC4; + } + } + + private static void iota(long[] A, int indexRound) + { + A[0] ^= KeccakRoundConstants[indexRound]; + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/LongDigest.java b/service/src/main/java/org/spongycastle/crypto/digests/LongDigest.java new file mode 100644 index 0000000..50a9b66 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/LongDigest.java @@ -0,0 +1,409 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + +/** + * Base class for SHA-384 and SHA-512. + */ +public abstract class LongDigest + implements ExtendedDigest, Memoable, EncodableDigest +{ + private static final int BYTE_LENGTH = 128; + + private byte[] xBuf = new byte[8]; + private int xBufOff; + + private long byteCount1; + private long byteCount2; + + protected long H1, H2, H3, H4, H5, H6, H7, H8; + + private long[] W = new long[80]; + private int wOff; + + /** + * Constructor for variable length word + */ + protected LongDigest() + { + xBufOff = 0; + + reset(); + } + + /** + * Copy constructor. We are using copy constructors in place + * of the Object.clone() interface as this interface is not + * supported by J2ME. + */ + protected LongDigest(LongDigest t) + { + copyIn(t); + } + + protected void copyIn(LongDigest t) + { + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount1 = t.byteCount1; + byteCount2 = t.byteCount2; + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + System.arraycopy(t.W, 0, W, 0, t.W.length); + wOff = t.wOff; + } + + protected void populateState(byte[] state) + { + System.arraycopy(xBuf, 0, state, 0, xBufOff); + Pack.intToBigEndian(xBufOff, state, 8); + Pack.longToBigEndian(byteCount1, state, 12); + Pack.longToBigEndian(byteCount2, state, 20); + Pack.longToBigEndian(H1, state, 28); + Pack.longToBigEndian(H2, state, 36); + Pack.longToBigEndian(H3, state, 44); + Pack.longToBigEndian(H4, state, 52); + Pack.longToBigEndian(H5, state, 60); + Pack.longToBigEndian(H6, state, 68); + Pack.longToBigEndian(H7, state, 76); + Pack.longToBigEndian(H8, state, 84); + + Pack.intToBigEndian(wOff, state, 92); + for (int i = 0; i < wOff; i++) + { + Pack.longToBigEndian(W[i], state, 96 + (i * 8)); + } + } + + protected void restoreState(byte[] encodedState) + { + xBufOff = Pack.bigEndianToInt(encodedState, 8); + System.arraycopy(encodedState, 0, xBuf, 0, xBufOff); + byteCount1 = Pack.bigEndianToLong(encodedState, 12); + byteCount2 = Pack.bigEndianToLong(encodedState, 20); + + H1 = Pack.bigEndianToLong(encodedState, 28); + H2 = Pack.bigEndianToLong(encodedState, 36); + H3 = Pack.bigEndianToLong(encodedState, 44); + H4 = Pack.bigEndianToLong(encodedState, 52); + H5 = Pack.bigEndianToLong(encodedState, 60); + H6 = Pack.bigEndianToLong(encodedState, 68); + H7 = Pack.bigEndianToLong(encodedState, 76); + H8 = Pack.bigEndianToLong(encodedState, 84); + + wOff = Pack.bigEndianToInt(encodedState, 92); + for (int i = 0; i < wOff; i++) + { + W[i] = Pack.bigEndianToLong(encodedState, 96 + (i * 8)); + } + } + + protected int getEncodedStateSize() + { + return 96 + (wOff * 8); + } + + public void update( + byte in) + { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) + { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount1++; + } + + public void update( + byte[] in, + int inOff, + int len) + { + // + // fill the current word + // + while ((xBufOff != 0) && (len > 0)) + { + update(in[inOff]); + + inOff++; + len--; + } + + // + // process whole words. + // + while (len > xBuf.length) + { + processWord(in, inOff); + + inOff += xBuf.length; + len -= xBuf.length; + byteCount1 += xBuf.length; + } + + // + // load in the remainder. + // + while (len > 0) + { + update(in[inOff]); + + inOff++; + len--; + } + } + + public void finish() + { + adjustByteCounts(); + + long lowBitLength = byteCount1 << 3; + long hiBitLength = byteCount2; + + // + // add the pad bytes. + // + update((byte)128); + + while (xBufOff != 0) + { + update((byte)0); + } + + processLength(lowBitLength, hiBitLength); + + processBlock(); + } + + public void reset() + { + byteCount1 = 0; + byteCount2 = 0; + + xBufOff = 0; + for (int i = 0; i < xBuf.length; i++) + { + xBuf[i] = 0; + } + + wOff = 0; + for (int i = 0; i != W.length; i++) + { + W[i] = 0; + } + } + + public int getByteLength() + { + return BYTE_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + W[wOff] = Pack.bigEndianToLong(in, inOff); + + if (++wOff == 16) + { + processBlock(); + } + } + + /** + * adjust the byte counts so that byteCount2 represents the + * upper long (less 3 bits) word of the byte count. + */ + private void adjustByteCounts() + { + if (byteCount1 > 0x1fffffffffffffffL) + { + byteCount2 += (byteCount1 >>> 61); + byteCount1 &= 0x1fffffffffffffffL; + } + } + + protected void processLength( + long lowW, + long hiW) + { + if (wOff > 14) + { + processBlock(); + } + + W[14] = hiW; + W[15] = lowW; + } + + protected void processBlock() + { + adjustByteCounts(); + + // + // expand 16 word block into 80 word blocks. + // + for (int t = 16; t <= 79; t++) + { + W[t] = Sigma1(W[t - 2]) + W[t - 7] + Sigma0(W[t - 15]) + W[t - 16]; + } + + // + // set up working variables. + // + long a = H1; + long b = H2; + long c = H3; + long d = H4; + long e = H5; + long f = H6; + long g = H7; + long h = H8; + + int t = 0; + for(int i = 0; i < 10; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++]; + e += a; + a += Sum0(b) + Maj(b, c, d); + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + wOff = 0; + for (int i = 0; i < 16; i++) + { + W[i] = 0; + } + } + + /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ + private long Ch( + long x, + long y, + long z) + { + return ((x & y) ^ ((~x) & z)); + } + + private long Maj( + long x, + long y, + long z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private long Sum0( + long x) + { + return ((x << 36)|(x >>> 28)) ^ ((x << 30)|(x >>> 34)) ^ ((x << 25)|(x >>> 39)); + } + + private long Sum1( + long x) + { + return ((x << 50)|(x >>> 14)) ^ ((x << 46)|(x >>> 18)) ^ ((x << 23)|(x >>> 41)); + } + + private long Sigma0( + long x) + { + return ((x << 63)|(x >>> 1)) ^ ((x << 56)|(x >>> 8)) ^ (x >>> 7); + } + + private long Sigma1( + long x) + { + return ((x << 45)|(x >>> 19)) ^ ((x << 3)|(x >>> 61)) ^ (x >>> 6); + } + + /* SHA-384 and SHA-512 Constants + * (represent the first 64 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + static final long K[] = { +0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL, 0xe9b5dba58189dbbcL, +0x3956c25bf348b538L, 0x59f111f1b605d019L, 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L, +0xd807aa98a3030242L, 0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L, +0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L, 0xc19bf174cf692694L, +0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L, 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L, +0x2de92c6f592b0275L, 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L, +0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL, 0xbf597fc7beef0ee4L, +0xc6e00bf33da88fc2L, 0xd5a79147930aa725L, 0x06ca6351e003826fL, 0x142929670a0e6e70L, +0x27b70a8546d22ffcL, 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL, +0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L, 0x92722c851482353bL, +0xa2bfe8a14cf10364L, 0xa81a664bbc423001L, 0xc24b8b70d0f89791L, 0xc76c51a30654be30L, +0xd192e819d6ef5218L, 0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L, +0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L, 0x34b0bcb5e19b48a8L, +0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL, 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L, +0x748f82ee5defb2fcL, 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL, +0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L, 0xc67178f2e372532bL, +0xca273eceea26619cL, 0xd186b8c721c0c207L, 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L, +0x06f067aa72176fbaL, 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL, +0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL, +0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L + }; + +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/MD5Digest.java b/service/src/main/java/org/spongycastle/crypto/digests/MD5Digest.java new file mode 100644 index 0000000..b81b41d --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/MD5Digest.java @@ -0,0 +1,361 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + +/** + * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. + */ +public class MD5Digest + extends GeneralDigest + implements EncodableDigest +{ + private static final int DIGEST_LENGTH = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public MD5Digest() + { + reset(); + } + + public MD5Digest(byte[] encodedState) + { + super(encodedState); + + H1 = Pack.bigEndianToInt(encodedState, 16); + H2 = Pack.bigEndianToInt(encodedState, 20); + H3 = Pack.bigEndianToInt(encodedState, 24); + H4 = Pack.bigEndianToInt(encodedState, 28); + + xOff = Pack.bigEndianToInt(encodedState, 32); + for (int i = 0; i != xOff; i++) + { + X[i] = Pack.bigEndianToInt(encodedState, 36 + (i * 4)); + } + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD5Digest(MD5Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(MD5Digest t) + { + super.copyIn(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "MD5"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H1, out, outOff); + unpackWord(H2, out, outOff + 4); + unpackWord(H3, out, outOff + 8); + unpackWord(H4, out, outOff + 12); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private static final int S11 = 7; + private static final int S12 = 12; + private static final int S13 = 17; + private static final int S14 = 22; + + // + // round 2 left rotates + // + private static final int S21 = 5; + private static final int S22 = 9; + private static final int S23 = 14; + private static final int S24 = 20; + + // + // round 3 left rotates + // + private static final int S31 = 4; + private static final int S32 = 11; + private static final int S33 = 16; + private static final int S34 = 23; + + // + // round 4 left rotates + // + private static final int S41 = 6; + private static final int S42 = 10; + private static final int S43 = 15; + private static final int S44 = 21; + + /* + * rotate int x left n bits. + */ + private int rotateLeft( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * F, G, H and I are the basic MD5 functions. + */ + private int F( + int u, + int v, + int w) + { + return (u & v) | (~u & w); + } + + private int G( + int u, + int v, + int w) + { + return (u & w) | (v & ~w); + } + + private int H( + int u, + int v, + int w) + { + return u ^ v ^ w; + } + + private int K( + int u, + int v, + int w) + { + return v ^ (u | ~w); + } + + protected void processBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = rotateLeft(a + F(b, c, d) + X[ 0] + 0xd76aa478, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 1] + 0xe8c7b756, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[ 2] + 0x242070db, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[ 3] + 0xc1bdceee, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[ 4] + 0xf57c0faf, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 5] + 0x4787c62a, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[ 6] + 0xa8304613, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[ 7] + 0xfd469501, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[ 8] + 0x698098d8, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 9] + 0x8b44f7af, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[10] + 0xffff5bb1, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[11] + 0x895cd7be, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[12] + 0x6b901122, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[13] + 0xfd987193, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[14] + 0xa679438e, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[15] + 0x49b40821, S14) + c; + + // + // Round 2 - G cycle, 16 times. + // + a = rotateLeft(a + G(b, c, d) + X[ 1] + 0xf61e2562, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[ 6] + 0xc040b340, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[11] + 0x265e5a51, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 0] + 0xe9b6c7aa, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[ 5] + 0xd62f105d, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[10] + 0x02441453, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[15] + 0xd8a1e681, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 4] + 0xe7d3fbc8, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[ 9] + 0x21e1cde6, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[14] + 0xc33707d6, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[ 3] + 0xf4d50d87, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 8] + 0x455a14ed, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[13] + 0xa9e3e905, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[ 2] + 0xfcefa3f8, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[ 7] + 0x676f02d9, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[12] + 0x8d2a4c8a, S24) + c; + + // + // Round 3 - H cycle, 16 times. + // + a = rotateLeft(a + H(b, c, d) + X[ 5] + 0xfffa3942, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 8] + 0x8771f681, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[11] + 0x6d9d6122, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[14] + 0xfde5380c, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[ 1] + 0xa4beea44, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 4] + 0x4bdecfa9, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[ 7] + 0xf6bb4b60, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[10] + 0xbebfbc70, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[13] + 0x289b7ec6, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 0] + 0xeaa127fa, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[ 3] + 0xd4ef3085, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[ 6] + 0x04881d05, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[ 9] + 0xd9d4d039, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[12] + 0xe6db99e5, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[15] + 0x1fa27cf8, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[ 2] + 0xc4ac5665, S34) + c; + + // + // Round 4 - K cycle, 16 times. + // + a = rotateLeft(a + K(b, c, d) + X[ 0] + 0xf4292244, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[ 7] + 0x432aff97, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[14] + 0xab9423a7, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 5] + 0xfc93a039, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[12] + 0x655b59c3, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[ 3] + 0x8f0ccc92, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[10] + 0xffeff47d, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 1] + 0x85845dd1, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[ 8] + 0x6fa87e4f, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[15] + 0xfe2ce6e0, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[ 6] + 0xa3014314, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[13] + 0x4e0811a1, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[ 4] + 0xf7537e82, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[11] + 0xbd3af235, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[ 2] + 0x2ad7d2bb, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 9] + 0xeb86d391, S44) + c; + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new MD5Digest(this); + } + + public void reset(Memoable other) + { + MD5Digest d = (MD5Digest)other; + + copyIn(d); + } + + public byte[] getEncodedState() + { + byte[] state = new byte[36 + xOff * 4]; + + super.populateState(state); + + Pack.intToBigEndian(H1, state, 16); + Pack.intToBigEndian(H2, state, 20); + Pack.intToBigEndian(H3, state, 24); + Pack.intToBigEndian(H4, state, 28); + Pack.intToBigEndian(xOff, state, 32); + + for (int i = 0; i != xOff; i++) + { + Pack.intToBigEndian(X[i], state, 36 + (i * 4)); + } + + return state; + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/SHA1Digest.java b/service/src/main/java/org/spongycastle/crypto/digests/SHA1Digest.java new file mode 100644 index 0000000..fda1584 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/SHA1Digest.java @@ -0,0 +1,353 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + +/** + * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349. + * + * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5 + * is the "endianness" of the word processing! + */ +public class SHA1Digest + extends GeneralDigest + implements EncodableDigest +{ + private static final int DIGEST_LENGTH = 20; + + private int H1, H2, H3, H4, H5; + + private int[] X = new int[80]; + private int xOff; + + /** + * Standard constructor + */ + public SHA1Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA1Digest(SHA1Digest t) + { + super(t); + + copyIn(t); + } + + /** + * State constructor - create a digest initialised with the state of a previous one. + * + * @param encodedState the encoded state from the originating digest. + */ + public SHA1Digest(byte[] encodedState) + { + super(encodedState); + + H1 = Pack.bigEndianToInt(encodedState, 16); + H2 = Pack.bigEndianToInt(encodedState, 20); + H3 = Pack.bigEndianToInt(encodedState, 24); + H4 = Pack.bigEndianToInt(encodedState, 28); + H5 = Pack.bigEndianToInt(encodedState, 32); + + xOff = Pack.bigEndianToInt(encodedState, 36); + for (int i = 0; i != xOff; i++) + { + X[i] = Pack.bigEndianToInt(encodedState, 40 + (i * 4)); + } + } + + private void copyIn(SHA1Digest t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-1"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[ inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + H5 = 0xc3d2e1f0; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // Additive constants + // + private static final int Y1 = 0x5a827999; + private static final int Y2 = 0x6ed9eba1; + private static final int Y3 = 0x8f1bbcdc; + private static final int Y4 = 0xca62c1d6; + + private int f( + int u, + int v, + int w) + { + return ((u & v) | ((~u) & w)); + } + + private int h( + int u, + int v, + int w) + { + return (u ^ v ^ w); + } + + private int g( + int u, + int v, + int w) + { + return ((u & v) | (u & w) | (v & w)); + } + + protected void processBlock() + { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i < 80; i++) + { + int t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]; + X[i] = t << 1 | t >>> 31; + } + + // + // set up working variables. + // + int A = H1; + int B = H2; + int C = H3; + int D = H4; + int E = H5; + + // + // round 1 + // + int idx = 0; + + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + f(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + f(B, C, D) + X[idx++] + Y1; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + f(A, B, C) + X[idx++] + Y1; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + f(E, A, B) + X[idx++] + Y1; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + f(D, E, A) + X[idx++] + Y1; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + f(C, D, E) + X[idx++] + Y1; + C = C << 30 | C >>> 2; + } + + // + // round 2 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y2; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y2; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y2; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y2; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y2; + C = C << 30 | C >>> 2; + } + + // + // round 3 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + g(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + g(B, C, D) + X[idx++] + Y3; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + g(A, B, C) + X[idx++] + Y3; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + g(E, A, B) + X[idx++] + Y3; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + g(D, E, A) + X[idx++] + Y3; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + g(C, D, E) + X[idx++] + Y3; + C = C << 30 | C >>> 2; + } + + // + // round 4 + // + for (int j = 0; j <= 3; j++) + { + // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y4; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y4; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y4; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y4; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y4; + C = C << 30 | C >>> 2; + } + + + H1 += A; + H2 += B; + H3 += C; + H4 += D; + H5 += E; + + // + // reset start of the buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new SHA1Digest(this); + } + + public void reset(Memoable other) + { + SHA1Digest d = (SHA1Digest)other; + + super.copyIn(d); + copyIn(d); + } + + public byte[] getEncodedState() + { + byte[] state = new byte[40 + xOff * 4]; + + super.populateState(state); + + Pack.intToBigEndian(H1, state, 16); + Pack.intToBigEndian(H2, state, 20); + Pack.intToBigEndian(H3, state, 24); + Pack.intToBigEndian(H4, state, 28); + Pack.intToBigEndian(H5, state, 32); + Pack.intToBigEndian(xOff, state, 36); + + for (int i = 0; i != xOff; i++) + { + Pack.intToBigEndian(X[i], state, 40 + (i * 4)); + } + + return state; + } +} + + + + diff --git a/service/src/main/java/org/spongycastle/crypto/digests/SHA224Digest.java b/service/src/main/java/org/spongycastle/crypto/digests/SHA224Digest.java new file mode 100644 index 0000000..e9f83d9 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/SHA224Digest.java @@ -0,0 +1,361 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + + +/** + * SHA-224 as described in RFC 3874 + *

+ *         block  word  digest
+ * SHA-1   512    32    160
+ * SHA-224 512    32    224
+ * SHA-256 512    32    256
+ * SHA-384 1024   64    384
+ * SHA-512 1024   64    512
+ * 
+ */ +public class SHA224Digest + extends GeneralDigest + implements EncodableDigest +{ + private static final int DIGEST_LENGTH = 28; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + /** + * Standard constructor + */ + public SHA224Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA224Digest(SHA224Digest t) + { + super(t); + + doCopy(t); + } + + private void doCopy(SHA224Digest t) + { + super.copyIn(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + /** + * State constructor - create a digest initialised with the state of a previous one. + * + * @param encodedState the encoded state from the originating digest. + */ + public SHA224Digest(byte[] encodedState) + { + super(encodedState); + + H1 = Pack.bigEndianToInt(encodedState, 16); + H2 = Pack.bigEndianToInt(encodedState, 20); + H3 = Pack.bigEndianToInt(encodedState, 24); + H4 = Pack.bigEndianToInt(encodedState, 28); + H5 = Pack.bigEndianToInt(encodedState, 32); + H6 = Pack.bigEndianToInt(encodedState, 36); + H7 = Pack.bigEndianToInt(encodedState, 40); + H8 = Pack.bigEndianToInt(encodedState, 44); + + xOff = Pack.bigEndianToInt(encodedState, 48); + for (int i = 0; i != xOff; i++) + { + X[i] = Pack.bigEndianToInt(encodedState, 52 + (i * 4)); + } + } + + public String getAlgorithmName() + { + return "SHA-224"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[ inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + Pack.intToBigEndian(H6, out, outOff + 20); + Pack.intToBigEndian(H7, out, outOff + 24); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-224 initial hash value + */ + + H1 = 0xc1059ed8; + H2 = 0x367cd507; + H3 = 0x3070dd17; + H4 = 0xf70e5939; + H5 = 0xffc00b31; + H6 = 0x68581511; + H7 = 0x64f98fa7; + H8 = 0xbefa4fa4; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + protected void processBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int t = 16; t <= 63; t++) + { + X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0(a) + Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0(h) + Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0(g) + Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0(f) + Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0(e) + Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0(d) + Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0(c) + Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0(b) + Maj(b, c, d); + ++t; + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + /* SHA-224 functions */ + private int Ch( + int x, + int y, + int z) + { + return ((x & y) ^ ((~x) & z)); + } + + private int Maj( + int x, + int y, + int z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private int Sum0( + int x) + { + return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10)); + } + + private int Sum1( + int x) + { + return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7)); + } + + private int Theta0( + int x) + { + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); + } + + private int Theta1( + int x) + { + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10); + } + + /* SHA-224 Constants + * (represent the first 32 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + static final int K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + public Memoable copy() + { + return new SHA224Digest(this); + } + + public void reset(Memoable other) + { + SHA224Digest d = (SHA224Digest)other; + + doCopy(d); + } + + public byte[] getEncodedState() + { + byte[] state = new byte[52 + xOff * 4]; + + super.populateState(state); + + Pack.intToBigEndian(H1, state, 16); + Pack.intToBigEndian(H2, state, 20); + Pack.intToBigEndian(H3, state, 24); + Pack.intToBigEndian(H4, state, 28); + Pack.intToBigEndian(H5, state, 32); + Pack.intToBigEndian(H6, state, 36); + Pack.intToBigEndian(H7, state, 40); + Pack.intToBigEndian(H8, state, 44); + Pack.intToBigEndian(xOff, state, 48); + + for (int i = 0; i != xOff; i++) + { + Pack.intToBigEndian(X[i], state, 52 + (i * 4)); + } + + return state; + } +} + diff --git a/service/src/main/java/org/spongycastle/crypto/digests/SHA256Digest.java b/service/src/main/java/org/spongycastle/crypto/digests/SHA256Digest.java new file mode 100644 index 0000000..c34c527 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/SHA256Digest.java @@ -0,0 +1,365 @@ +package org.spongycastle.crypto.digests; + + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + + +/** + * FIPS 180-2 implementation of SHA-256. + * + *
+ *         block  word  digest
+ * SHA-1   512    32    160
+ * SHA-256 512    32    256
+ * SHA-384 1024   64    384
+ * SHA-512 1024   64    512
+ * 
+ */ +public class SHA256Digest + extends GeneralDigest + implements EncodableDigest +{ + private static final int DIGEST_LENGTH = 32; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + /** + * Standard constructor + */ + public SHA256Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA256Digest(SHA256Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(SHA256Digest t) + { + super.copyIn(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + /** + * State constructor - create a digest initialised with the state of a previous one. + * + * @param encodedState the encoded state from the originating digest. + */ + public SHA256Digest(byte[] encodedState) + { + super(encodedState); + + H1 = Pack.bigEndianToInt(encodedState, 16); + H2 = Pack.bigEndianToInt(encodedState, 20); + H3 = Pack.bigEndianToInt(encodedState, 24); + H4 = Pack.bigEndianToInt(encodedState, 28); + H5 = Pack.bigEndianToInt(encodedState, 32); + H6 = Pack.bigEndianToInt(encodedState, 36); + H7 = Pack.bigEndianToInt(encodedState, 40); + H8 = Pack.bigEndianToInt(encodedState, 44); + + xOff = Pack.bigEndianToInt(encodedState, 48); + for (int i = 0; i != xOff; i++) + { + X[i] = Pack.bigEndianToInt(encodedState, 52 + (i * 4)); + } + } + + + public String getAlgorithmName() + { + return "SHA-256"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + Pack.intToBigEndian(H6, out, outOff + 20); + Pack.intToBigEndian(H7, out, outOff + 24); + Pack.intToBigEndian(H8, out, outOff + 28); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-256 initial hash value + * The first 32 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + + H1 = 0x6a09e667; + H2 = 0xbb67ae85; + H3 = 0x3c6ef372; + H4 = 0xa54ff53a; + H5 = 0x510e527f; + H6 = 0x9b05688c; + H7 = 0x1f83d9ab; + H8 = 0x5be0cd19; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + protected void processBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int t = 16; t <= 63; t++) + { + X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0(a) + Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0(h) + Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0(g) + Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0(f) + Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0(e) + Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0(d) + Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0(c) + Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0(b) + Maj(b, c, d); + ++t; + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + /* SHA-256 functions */ + private int Ch( + int x, + int y, + int z) + { + return (x & y) ^ ((~x) & z); + } + + private int Maj( + int x, + int y, + int z) + { + return (x & y) ^ (x & z) ^ (y & z); + } + + private int Sum0( + int x) + { + return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10)); + } + + private int Sum1( + int x) + { + return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7)); + } + + private int Theta0( + int x) + { + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); + } + + private int Theta1( + int x) + { + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10); + } + + /* SHA-256 Constants + * (represent the first 32 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + static final int K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + public Memoable copy() + { + return new SHA256Digest(this); + } + + public void reset(Memoable other) + { + SHA256Digest d = (SHA256Digest)other; + + copyIn(d); + } + + public byte[] getEncodedState() + { + byte[] state = new byte[52 + xOff * 4]; + + super.populateState(state); + + Pack.intToBigEndian(H1, state, 16); + Pack.intToBigEndian(H2, state, 20); + Pack.intToBigEndian(H3, state, 24); + Pack.intToBigEndian(H4, state, 28); + Pack.intToBigEndian(H5, state, 32); + Pack.intToBigEndian(H6, state, 36); + Pack.intToBigEndian(H7, state, 40); + Pack.intToBigEndian(H8, state, 44); + Pack.intToBigEndian(xOff, state, 48); + + for (int i = 0; i != xOff; i++) + { + Pack.intToBigEndian(X[i], state, 52 + (i * 4)); + } + + return state; + } +} + diff --git a/service/src/main/java/org/spongycastle/crypto/digests/SHA384Digest.java b/service/src/main/java/org/spongycastle/crypto/digests/SHA384Digest.java new file mode 100644 index 0000000..2224544 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/SHA384Digest.java @@ -0,0 +1,116 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + + +/** + * FIPS 180-2 implementation of SHA-384. + * + *
+ *         block  word  digest
+ * SHA-1   512    32    160
+ * SHA-256 512    32    256
+ * SHA-384 1024   64    384
+ * SHA-512 1024   64    512
+ * 
+ */ +public class SHA384Digest + extends LongDigest +{ + private static final int DIGEST_LENGTH = 48; + + /** + * Standard constructor + */ + public SHA384Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA384Digest(SHA384Digest t) + { + super(t); + } + + /** + * State constructor - create a digest initialised with the state of a previous one. + * + * @param encodedState the encoded state from the originating digest. + */ + public SHA384Digest(byte[] encodedState) + { + restoreState(encodedState); + } + + public String getAlgorithmName() + { + return "SHA-384"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.longToBigEndian(H1, out, outOff); + Pack.longToBigEndian(H2, out, outOff + 8); + Pack.longToBigEndian(H3, out, outOff + 16); + Pack.longToBigEndian(H4, out, outOff + 24); + Pack.longToBigEndian(H5, out, outOff + 32); + Pack.longToBigEndian(H6, out, outOff + 40); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-384 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the 9th through 16th prime numbers + */ + H1 = 0xcbbb9d5dc1059ed8l; + H2 = 0x629a292a367cd507l; + H3 = 0x9159015a3070dd17l; + H4 = 0x152fecd8f70e5939l; + H5 = 0x67332667ffc00b31l; + H6 = 0x8eb44a8768581511l; + H7 = 0xdb0c2e0d64f98fa7l; + H8 = 0x47b5481dbefa4fa4l; + } + + public Memoable copy() + { + return new SHA384Digest(this); + } + + public void reset(Memoable other) + { + SHA384Digest d = (SHA384Digest)other; + + super.copyIn(d); + } + + public byte[] getEncodedState() + { + byte[] encoded = new byte[getEncodedStateSize()]; + super.populateState(encoded); + return encoded; + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/SHA3Digest.java b/service/src/main/java/org/spongycastle/crypto/digests/SHA3Digest.java new file mode 100644 index 0000000..5547c80 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/SHA3Digest.java @@ -0,0 +1,74 @@ +package org.spongycastle.crypto.digests; + + +/** + * implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + *

+ * Following the naming conventions used in the C source code to enable easy review of the implementation. + */ +public class SHA3Digest + extends KeccakDigest +{ + private static int checkBitLength(int bitLength) + { + switch (bitLength) + { + case 224: + case 256: + case 384: + case 512: + return bitLength; + default: + throw new IllegalArgumentException("'bitLength' " + bitLength + " not supported for SHA-3"); + } + } + + public SHA3Digest() + { + this(256); + } + + public SHA3Digest(int bitLength) + { + super(checkBitLength(bitLength)); + } + + public SHA3Digest(SHA3Digest source) { + super(source); + } + + public String getAlgorithmName() + { + return "SHA3-" + fixedOutputLength; + } + + public int doFinal(byte[] out, int outOff) + { + absorbBits(0x02, 2); + + return super.doFinal(out, outOff); + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits) + { + if (partialBits < 0 || partialBits > 7) + { + throw new IllegalArgumentException("'partialBits' must be in the range [0,7]"); + } + + int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x02 << partialBits); + int finalBits = partialBits + 2; + + if (finalBits >= 8) + { + absorb(new byte[]{ (byte)finalInput }, 0, 1); + finalBits -= 8; + finalInput >>>= 8; + } + + return super.doFinal(out, outOff, (byte)finalInput, finalBits); + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/digests/SHA512Digest.java b/service/src/main/java/org/spongycastle/crypto/digests/SHA512Digest.java new file mode 100644 index 0000000..f6ea0f1 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/SHA512Digest.java @@ -0,0 +1,119 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.Pack; + + +/** + * FIPS 180-2 implementation of SHA-512. + * + *

+ *         block  word  digest
+ * SHA-1   512    32    160
+ * SHA-256 512    32    256
+ * SHA-384 1024   64    384
+ * SHA-512 1024   64    512
+ * 
+ */ +public class SHA512Digest + extends LongDigest +{ + private static final int DIGEST_LENGTH = 64; + + /** + * Standard constructor + */ + public SHA512Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA512Digest(SHA512Digest t) + { + super(t); + } + + /** + * State constructor - create a digest initialised with the state of a previous one. + * + * @param encodedState the encoded state from the originating digest. + */ + public SHA512Digest(byte[] encodedState) + { + restoreState(encodedState); + } + + public String getAlgorithmName() + { + return "SHA-512"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.longToBigEndian(H1, out, outOff); + Pack.longToBigEndian(H2, out, outOff + 8); + Pack.longToBigEndian(H3, out, outOff + 16); + Pack.longToBigEndian(H4, out, outOff + 24); + Pack.longToBigEndian(H5, out, outOff + 32); + Pack.longToBigEndian(H6, out, outOff + 40); + Pack.longToBigEndian(H7, out, outOff + 48); + Pack.longToBigEndian(H8, out, outOff + 56); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-512 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + H1 = 0x6a09e667f3bcc908L; + H2 = 0xbb67ae8584caa73bL; + H3 = 0x3c6ef372fe94f82bL; + H4 = 0xa54ff53a5f1d36f1L; + H5 = 0x510e527fade682d1L; + H6 = 0x9b05688c2b3e6c1fL; + H7 = 0x1f83d9abfb41bd6bL; + H8 = 0x5be0cd19137e2179L; + } + + public Memoable copy() + { + return new SHA512Digest(this); + } + + public void reset(Memoable other) + { + SHA512Digest d = (SHA512Digest)other; + + copyIn(d); + } + + public byte[] getEncodedState() + { + byte[] encoded = new byte[getEncodedStateSize()]; + super.populateState(encoded); + return encoded; + } +} + diff --git a/service/src/main/java/org/spongycastle/crypto/digests/SHA512tDigest.java b/service/src/main/java/org/spongycastle/crypto/digests/SHA512tDigest.java new file mode 100644 index 0000000..fc05781 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/digests/SHA512tDigest.java @@ -0,0 +1,227 @@ +package org.spongycastle.crypto.digests; + +import org.spongycastle.util.Memoable; +import org.spongycastle.util.MemoableResetException; +import org.spongycastle.util.Pack; + +/** + * FIPS 180-4 implementation of SHA-512/t + */ +public class SHA512tDigest + extends LongDigest +{ + private int digestLength; // non-final due to old flow analyser. + + private long H1t, H2t, H3t, H4t, H5t, H6t, H7t, H8t; + + /** + * Standard constructor + */ + public SHA512tDigest(int bitLength) + { + if (bitLength >= 512) + { + throw new IllegalArgumentException("bitLength cannot be >= 512"); + } + + if (bitLength % 8 != 0) + { + throw new IllegalArgumentException("bitLength needs to be a multiple of 8"); + } + + if (bitLength == 384) + { + throw new IllegalArgumentException("bitLength cannot be 384 use SHA384 instead"); + } + + this.digestLength = bitLength / 8; + + tIvGenerate(digestLength * 8); + + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA512tDigest(SHA512tDigest t) + { + super(t); + + this.digestLength = t.digestLength; + + reset(t); + } + + public SHA512tDigest(byte[] encodedState) + { + this(readDigestLength(encodedState)); + restoreState(encodedState); + } + + private static int readDigestLength(byte[] encodedState) + { + return Pack.bigEndianToInt(encodedState, encodedState.length - 4); + } + + public String getAlgorithmName() + { + return "SHA-512/" + Integer.toString(digestLength * 8); + } + + public int getDigestSize() + { + return digestLength; + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + longToBigEndian(H1, out, outOff, digestLength); + longToBigEndian(H2, out, outOff + 8, digestLength - 8); + longToBigEndian(H3, out, outOff + 16, digestLength - 16); + longToBigEndian(H4, out, outOff + 24, digestLength - 24); + longToBigEndian(H5, out, outOff + 32, digestLength - 32); + longToBigEndian(H6, out, outOff + 40, digestLength - 40); + longToBigEndian(H7, out, outOff + 48, digestLength - 48); + longToBigEndian(H8, out, outOff + 56, digestLength - 56); + + reset(); + + return digestLength; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* + * initial hash values use the iv generation algorithm for t. + */ + H1 = H1t; + H2 = H2t; + H3 = H3t; + H4 = H4t; + H5 = H5t; + H6 = H6t; + H7 = H7t; + H8 = H8t; + } + + private void tIvGenerate(int bitLength) + { + H1 = 0x6a09e667f3bcc908L ^ 0xa5a5a5a5a5a5a5a5L; + H2 = 0xbb67ae8584caa73bL ^ 0xa5a5a5a5a5a5a5a5L; + H3 = 0x3c6ef372fe94f82bL ^ 0xa5a5a5a5a5a5a5a5L; + H4 = 0xa54ff53a5f1d36f1L ^ 0xa5a5a5a5a5a5a5a5L; + H5 = 0x510e527fade682d1L ^ 0xa5a5a5a5a5a5a5a5L; + H6 = 0x9b05688c2b3e6c1fL ^ 0xa5a5a5a5a5a5a5a5L; + H7 = 0x1f83d9abfb41bd6bL ^ 0xa5a5a5a5a5a5a5a5L; + H8 = 0x5be0cd19137e2179L ^ 0xa5a5a5a5a5a5a5a5L; + + update((byte)0x53); + update((byte)0x48); + update((byte)0x41); + update((byte)0x2D); + update((byte)0x35); + update((byte)0x31); + update((byte)0x32); + update((byte)0x2F); + + if (bitLength > 100) + { + update((byte)(bitLength / 100 + 0x30)); + bitLength = bitLength % 100; + update((byte)(bitLength / 10 + 0x30)); + bitLength = bitLength % 10; + update((byte)(bitLength + 0x30)); + } + else if (bitLength > 10) + { + update((byte)(bitLength / 10 + 0x30)); + bitLength = bitLength % 10; + update((byte)(bitLength + 0x30)); + } + else + { + update((byte)(bitLength + 0x30)); + } + + finish(); + + H1t = H1; + H2t = H2; + H3t = H3; + H4t = H4; + H5t = H5; + H6t = H6; + H7t = H7; + H8t = H8; + } + + private static void longToBigEndian(long n, byte[] bs, int off, int max) + { + if (max > 0) + { + intToBigEndian((int)(n >>> 32), bs, off, max); + + if (max > 4) + { + intToBigEndian((int)(n & 0xffffffffL), bs, off + 4, max - 4); + } + } + } + + private static void intToBigEndian(int n, byte[] bs, int off, int max) + { + int num = Math.min(4, max); + while (--num >= 0) + { + int shift = 8 * (3 - num); + bs[off + num] = (byte)(n >>> shift); + } + } + + public Memoable copy() + { + return new SHA512tDigest(this); + } + + public void reset(Memoable other) + { + SHA512tDigest t = (SHA512tDigest)other; + + if (this.digestLength != t.digestLength) + { + throw new MemoableResetException("digestLength inappropriate in other"); + } + + super.copyIn(t); + + this.H1t = t.H1t; + this.H2t = t.H2t; + this.H3t = t.H3t; + this.H4t = t.H4t; + this.H5t = t.H5t; + this.H6t = t.H6t; + this.H7t = t.H7t; + this.H8t = t.H8t; + } + + public byte[] getEncodedState() + { + final int baseSize = getEncodedStateSize(); + byte[] encoded = new byte[baseSize + 4]; + populateState(encoded); + Pack.intToBigEndian(digestLength * 8, encoded, baseSize); + return encoded; + } + +} diff --git a/service/src/main/java/org/spongycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/service/src/main/java/org/spongycastle/crypto/generators/PKCS5S2ParametersGenerator.java new file mode 100644 index 0000000..8a4675b --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/generators/PKCS5S2ParametersGenerator.java @@ -0,0 +1,154 @@ +package org.spongycastle.crypto.generators; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.PBEParametersGenerator; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.util.DigestFactory; +import org.spongycastle.util.Arrays; + +/** + * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 2. + * This generator uses a SHA-1 HMac as the calculation function. + *

+ * The document this implementation is based on can be found at + * + * RSA's PKCS5 Page + */ +public class PKCS5S2ParametersGenerator + extends PBEParametersGenerator +{ + private Mac hMac; + private byte[] state; + + /** + * construct a PKCS5 Scheme 2 Parameters generator. + */ + public PKCS5S2ParametersGenerator() + { + this(DigestFactory.createSHA1()); + } + + public PKCS5S2ParametersGenerator(Digest digest) + { + hMac = new HMac(digest); + state = new byte[hMac.getMacSize()]; + } + + private void F( + byte[] S, + int c, + byte[] iBuf, + byte[] out, + int outOff) + { + if (c == 0) + { + throw new IllegalArgumentException("iteration count must be at least 1."); + } + + if (S != null) + { + hMac.update(S, 0, S.length); + } + + hMac.update(iBuf, 0, iBuf.length); + hMac.doFinal(state, 0); + + System.arraycopy(state, 0, out, outOff, state.length); + + for (int count = 1; count < c; count++) + { + hMac.update(state, 0, state.length); + hMac.doFinal(state, 0); + + for (int j = 0; j != state.length; j++) + { + out[outOff + j] ^= state[j]; + } + } + } + + private byte[] generateDerivedKey( + int dkLen) + { + int hLen = hMac.getMacSize(); + int l = (dkLen + hLen - 1) / hLen; + byte[] iBuf = new byte[4]; + byte[] outBytes = new byte[l * hLen]; + int outPos = 0; + + CipherParameters param = new KeyParameter(password); + + hMac.init(param); + + for (int i = 1; i <= l; i++) + { + // Increment the value in 'iBuf' + int pos = 3; + while (++iBuf[pos] == 0) + { + --pos; + } + + F(salt, iterationCount, iBuf, outBytes, outPos); + outPos += hLen; + } + + return outBytes; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = Arrays.copyOfRange(generateDerivedKey(keySize), 0, keySize); + + return new KeyParameter(dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + */ + public CipherParameters generateDerivedParameters( + int keySize, + int ivSize) + { + keySize = keySize / 8; + ivSize = ivSize / 8; + + byte[] dKey = generateDerivedKey(keySize + ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedMacParameters( + int keySize) + { + return generateDerivedParameters(keySize); + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/macs/HMac.java b/service/src/main/java/org/spongycastle/crypto/macs/HMac.java new file mode 100644 index 0000000..e7c0fe5 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/macs/HMac.java @@ -0,0 +1,231 @@ +package org.spongycastle.crypto.macs; + +import java.util.Hashtable; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.ExtendedDigest; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.util.Integers; +import org.spongycastle.util.Memoable; + +/** + * HMAC implementation based on RFC2104 + * + * H(K XOR opad, H(K XOR ipad, text)) + */ +public class HMac + implements Mac +{ + private final static byte IPAD = (byte)0x36; + private final static byte OPAD = (byte)0x5C; + + private Digest digest; + private int digestSize; + private int blockLength; + private Memoable ipadState; + private Memoable opadState; + + private byte[] inputPad; + private byte[] outputBuf; + + private static Hashtable blockLengths; + + static + { + blockLengths = new Hashtable(); + + blockLengths.put("GOST3411", Integers.valueOf(32)); + + blockLengths.put("MD2", Integers.valueOf(16)); + blockLengths.put("MD4", Integers.valueOf(64)); + blockLengths.put("MD5", Integers.valueOf(64)); + + blockLengths.put("RIPEMD128", Integers.valueOf(64)); + blockLengths.put("RIPEMD160", Integers.valueOf(64)); + + blockLengths.put("SHA-1", Integers.valueOf(64)); + blockLengths.put("SHA-224", Integers.valueOf(64)); + blockLengths.put("SHA-256", Integers.valueOf(64)); + blockLengths.put("SHA-384", Integers.valueOf(128)); + blockLengths.put("SHA-512", Integers.valueOf(128)); + + blockLengths.put("Tiger", Integers.valueOf(64)); + blockLengths.put("Whirlpool", Integers.valueOf(64)); + } + + private static int getByteLength( + Digest digest) + { + if (digest instanceof ExtendedDigest) + { + return ((ExtendedDigest)digest).getByteLength(); + } + + Integer b = (Integer)blockLengths.get(digest.getAlgorithmName()); + + if (b == null) + { + throw new IllegalArgumentException("unknown digest passed: " + digest.getAlgorithmName()); + } + + return b.intValue(); + } + + /** + * Base constructor for one of the standard digest algorithms that the + * byteLength of the algorithm is know for. + * + * @param digest the digest. + */ + public HMac( + Digest digest) + { + this(digest, getByteLength(digest)); + } + + private HMac( + Digest digest, + int byteLength) + { + this.digest = digest; + this.digestSize = digest.getDigestSize(); + this.blockLength = byteLength; + this.inputPad = new byte[blockLength]; + this.outputBuf = new byte[blockLength + digestSize]; + } + + public String getAlgorithmName() + { + return digest.getAlgorithmName() + "/HMAC"; + } + + public Digest getUnderlyingDigest() + { + return digest; + } + + public void init( + CipherParameters params) + { + digest.reset(); + + byte[] key = ((KeyParameter)params).getKey(); + int keyLength = key.length; + + if (keyLength > blockLength) + { + digest.update(key, 0, keyLength); + digest.doFinal(inputPad, 0); + + keyLength = digestSize; + } + else + { + System.arraycopy(key, 0, inputPad, 0, keyLength); + } + + for (int i = keyLength; i < inputPad.length; i++) + { + inputPad[i] = 0; + } + + System.arraycopy(inputPad, 0, outputBuf, 0, blockLength); + + xorPad(inputPad, blockLength, IPAD); + xorPad(outputBuf, blockLength, OPAD); + + if (digest instanceof Memoable) + { + opadState = ((Memoable)digest).copy(); + + ((Digest)opadState).update(outputBuf, 0, blockLength); + } + + digest.update(inputPad, 0, inputPad.length); + + if (digest instanceof Memoable) + { + ipadState = ((Memoable)digest).copy(); + } + } + + public int getMacSize() + { + return digestSize; + } + + public void update( + byte in) + { + digest.update(in); + } + + public void update( + byte[] in, + int inOff, + int len) + { + digest.update(in, inOff, len); + } + + public int doFinal( + byte[] out, + int outOff) + { + digest.doFinal(outputBuf, blockLength); + + if (opadState != null) + { + ((Memoable)digest).reset(opadState); + digest.update(outputBuf, blockLength, digest.getDigestSize()); + } + else + { + digest.update(outputBuf, 0, outputBuf.length); + } + + int len = digest.doFinal(out, outOff); + + for (int i = blockLength; i < outputBuf.length; i++) + { + outputBuf[i] = 0; + } + + if (ipadState != null) + { + ((Memoable)digest).reset(ipadState); + } + else + { + digest.update(inputPad, 0, inputPad.length); + } + + return len; + } + + /** + * Reset the mac generator. + */ + public void reset() + { + /* + * reset the underlying digest. + */ + digest.reset(); + + /* + * reinitialize the digest. + */ + digest.update(inputPad, 0, inputPad.length); + } + + private static void xorPad(byte[] pad, int len, byte n) + { + for (int i = 0; i < len; ++i) + { + pad[i] ^= n; + } + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/params/KeyParameter.java b/service/src/main/java/org/spongycastle/crypto/params/KeyParameter.java new file mode 100644 index 0000000..079e136 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/params/KeyParameter.java @@ -0,0 +1,30 @@ +package org.spongycastle.crypto.params; + +import org.spongycastle.crypto.CipherParameters; + +public class KeyParameter + implements CipherParameters +{ + private byte[] key; + + public KeyParameter( + byte[] key) + { + this(key, 0, key.length); + } + + public KeyParameter( + byte[] key, + int keyOff, + int keyLen) + { + this.key = new byte[keyLen]; + + System.arraycopy(key, keyOff, this.key, 0, keyLen); + } + + public byte[] getKey() + { + return key; + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/params/ParametersWithIV.java b/service/src/main/java/org/spongycastle/crypto/params/ParametersWithIV.java new file mode 100644 index 0000000..7fb39b7 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/params/ParametersWithIV.java @@ -0,0 +1,39 @@ +package org.spongycastle.crypto.params; + +import org.spongycastle.crypto.CipherParameters; + +public class ParametersWithIV + implements CipherParameters +{ + private byte[] iv; + private CipherParameters parameters; + + public ParametersWithIV( + CipherParameters parameters, + byte[] iv) + { + this(parameters, iv, 0, iv.length); + } + + public ParametersWithIV( + CipherParameters parameters, + byte[] iv, + int ivOff, + int ivLen) + { + this.iv = new byte[ivLen]; + this.parameters = parameters; + + System.arraycopy(iv, ivOff, this.iv, 0, ivLen); + } + + public byte[] getIV() + { + return iv; + } + + public CipherParameters getParameters() + { + return parameters; + } +} diff --git a/service/src/main/java/org/spongycastle/crypto/util/DigestFactory.java b/service/src/main/java/org/spongycastle/crypto/util/DigestFactory.java new file mode 100644 index 0000000..0d4dbb5 --- /dev/null +++ b/service/src/main/java/org/spongycastle/crypto/util/DigestFactory.java @@ -0,0 +1,77 @@ +package org.spongycastle.crypto.util; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA3Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.digests.SHA512tDigest; + +/** + * Basic factory class for message digests. + */ +public final class DigestFactory +{ + public static Digest createMD5() + { + return new MD5Digest(); + } + + public static Digest createSHA1() + { + return new SHA1Digest(); + } + + public static Digest createSHA224() + { + return new SHA224Digest(); + } + + public static Digest createSHA256() + { + return new SHA256Digest(); + } + + public static Digest createSHA384() + { + return new SHA384Digest(); + } + + public static Digest createSHA512() + { + return new SHA512Digest(); + } + + public static Digest createSHA512_224() + { + return new SHA512tDigest(224); + } + + public static Digest createSHA512_256() + { + return new SHA512tDigest(256); + } + + public static Digest createSHA3_224() + { + return new SHA3Digest(224); + } + + public static Digest createSHA3_256() + { + return new SHA3Digest(256); + } + + public static Digest createSHA3_384() + { + return new SHA3Digest(384); + } + + public static Digest createSHA3_512() + { + return new SHA3Digest(512); + } +} diff --git a/service/src/main/java/org/spongycastle/util/Arrays.java b/service/src/main/java/org/spongycastle/util/Arrays.java new file mode 100644 index 0000000..1c1441f --- /dev/null +++ b/service/src/main/java/org/spongycastle/util/Arrays.java @@ -0,0 +1,1200 @@ +package org.spongycastle.util; + +import java.math.BigInteger; +import java.util.NoSuchElementException; + +/** + * General array utilities. + */ +public final class Arrays +{ + private Arrays() + { + // static class, hide constructor + } + + public static boolean areEqual( + boolean[] a, + boolean[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + public static boolean areEqual( + char[] a, + char[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + public static boolean areEqual( + byte[] a, + byte[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + public static boolean areEqual( + short[] a, + short[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + /** + * A constant time equals comparison - does not terminate early if + * test will fail. For best results always pass the expected value + * as the first parameter. + * + * @param expected first array + * @param supplied second array + * @return true if arrays equal, false otherwise. + */ + public static boolean constantTimeAreEqual( + byte[] expected, + byte[] supplied) + { + if (expected == supplied) + { + return true; + } + + if (expected == null || supplied == null) + { + return false; + } + + if (expected.length != supplied.length) + { + return !Arrays.constantTimeAreEqual(expected, expected); + } + + int nonEqual = 0; + + for (int i = 0; i != expected.length; i++) + { + nonEqual |= (expected[i] ^ supplied[i]); + } + + return nonEqual == 0; + } + + public static boolean areEqual( + int[] a, + int[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + public static boolean areEqual( + long[] a, + long[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + public static boolean areEqual(Object[] a, Object[] b) + { + if (a == b) + { + return true; + } + if (a == null || b == null) + { + return false; + } + if (a.length != b.length) + { + return false; + } + for (int i = 0; i != a.length; i++) + { + Object objA = a[i], objB = b[i]; + if (objA == null) + { + if (objB != null) + { + return false; + } + } + else if (!objA.equals(objB)) + { + return false; + } + } + return true; + } + + public static int compareUnsigned(byte[] a, byte[] b) + { + if (a == b) + { + return 0; + } + if (a == null) + { + return -1; + } + if (b == null) + { + return 1; + } + int minLen = Math.min(a.length, b.length); + for (int i = 0; i < minLen; ++i) + { + int aVal = a[i] & 0xFF, bVal = b[i] & 0xFF; + if (aVal < bVal) + { + return -1; + } + if (aVal > bVal) + { + return 1; + } + } + if (a.length < b.length) + { + return -1; + } + if (a.length > b.length) + { + return 1; + } + return 0; + } + + public static boolean contains(short[] a, short n) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == n) + { + return true; + } + } + return false; + } + + public static boolean contains(int[] a, int n) + { + for (int i = 0; i < a.length; ++i) + { + if (a[i] == n) + { + return true; + } + } + return false; + } + + public static void fill( + byte[] array, + byte value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static void fill( + char[] array, + char value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static void fill( + long[] array, + long value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static void fill( + short[] array, + short value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static void fill( + int[] array, + int value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static int hashCode(byte[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static int hashCode(byte[] data, int off, int len) + { + if (data == null) + { + return 0; + } + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[off + i]; + } + + return hc; + } + + public static int hashCode(char[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static int hashCode(int[][] ints) + { + int hc = 0; + + for (int i = 0; i != ints.length; i++) + { + hc = hc * 257 + hashCode(ints[i]); + } + + return hc; + } + + public static int hashCode(int[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static int hashCode(int[] data, int off, int len) + { + if (data == null) + { + return 0; + } + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[off + i]; + } + + return hc; + } + + public static int hashCode(long[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + long di = data[i]; + hc *= 257; + hc ^= (int)di; + hc *= 257; + hc ^= (int)(di >>> 32); + } + + return hc; + } + + public static int hashCode(long[] data, int off, int len) + { + if (data == null) + { + return 0; + } + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + long di = data[off + i]; + hc *= 257; + hc ^= (int)di; + hc *= 257; + hc ^= (int)(di >>> 32); + } + + return hc; + } + + public static int hashCode(short[][][] shorts) + { + int hc = 0; + + for (int i = 0; i != shorts.length; i++) + { + hc = hc * 257 + hashCode(shorts[i]); + } + + return hc; + } + + public static int hashCode(short[][] shorts) + { + int hc = 0; + + for (int i = 0; i != shorts.length; i++) + { + hc = hc * 257 + hashCode(shorts[i]); + } + + return hc; + } + + public static int hashCode(short[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= (data[i] & 0xff); + } + + return hc; + } + + public static int hashCode(Object[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i].hashCode(); + } + + return hc; + } + + public static byte[] clone(byte[] data) + { + if (data == null) + { + return null; + } + byte[] copy = new byte[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static char[] clone(char[] data) + { + if (data == null) + { + return null; + } + char[] copy = new char[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static byte[] clone(byte[] data, byte[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.length != data.length)) + { + return clone(data); + } + System.arraycopy(data, 0, existing, 0, existing.length); + return existing; + } + + public static byte[][] clone(byte[][] data) + { + if (data == null) + { + return null; + } + + byte[][] copy = new byte[data.length][]; + + for (int i = 0; i != copy.length; i++) + { + copy[i] = clone(data[i]); + } + + return copy; + } + + public static byte[][][] clone(byte[][][] data) + { + if (data == null) + { + return null; + } + + byte[][][] copy = new byte[data.length][][]; + + for (int i = 0; i != copy.length; i++) + { + copy[i] = clone(data[i]); + } + + return copy; + } + + public static int[] clone(int[] data) + { + if (data == null) + { + return null; + } + int[] copy = new int[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static long[] clone(long[] data) + { + if (data == null) + { + return null; + } + long[] copy = new long[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static long[] clone(long[] data, long[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.length != data.length)) + { + return clone(data); + } + System.arraycopy(data, 0, existing, 0, existing.length); + return existing; + } + + public static short[] clone(short[] data) + { + if (data == null) + { + return null; + } + short[] copy = new short[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static BigInteger[] clone(BigInteger[] data) + { + if (data == null) + { + return null; + } + BigInteger[] copy = new BigInteger[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static byte[] copyOf(byte[] data, int newLength) + { + byte[] tmp = new byte[newLength]; + + if (newLength < data.length) + { + System.arraycopy(data, 0, tmp, 0, newLength); + } + else + { + System.arraycopy(data, 0, tmp, 0, data.length); + } + + return tmp; + } + + public static char[] copyOf(char[] data, int newLength) + { + char[] tmp = new char[newLength]; + + if (newLength < data.length) + { + System.arraycopy(data, 0, tmp, 0, newLength); + } + else + { + System.arraycopy(data, 0, tmp, 0, data.length); + } + + return tmp; + } + + public static int[] copyOf(int[] data, int newLength) + { + int[] tmp = new int[newLength]; + + if (newLength < data.length) + { + System.arraycopy(data, 0, tmp, 0, newLength); + } + else + { + System.arraycopy(data, 0, tmp, 0, data.length); + } + + return tmp; + } + + public static long[] copyOf(long[] data, int newLength) + { + long[] tmp = new long[newLength]; + + if (newLength < data.length) + { + System.arraycopy(data, 0, tmp, 0, newLength); + } + else + { + System.arraycopy(data, 0, tmp, 0, data.length); + } + + return tmp; + } + + public static BigInteger[] copyOf(BigInteger[] data, int newLength) + { + BigInteger[] tmp = new BigInteger[newLength]; + + if (newLength < data.length) + { + System.arraycopy(data, 0, tmp, 0, newLength); + } + else + { + System.arraycopy(data, 0, tmp, 0, data.length); + } + + return tmp; + } + + /** + * Make a copy of a range of bytes from the passed in data array. The range can + * extend beyond the end of the input array, in which case the return array will + * be padded with zeroes. + * + * @param data the array from which the data is to be copied. + * @param from the start index at which the copying should take place. + * @param to the final index of the range (exclusive). + * + * @return a new byte array containing the range given. + */ + public static byte[] copyOfRange(byte[] data, int from, int to) + { + int newLength = getLength(from, to); + + byte[] tmp = new byte[newLength]; + + if (data.length - from < newLength) + { + System.arraycopy(data, from, tmp, 0, data.length - from); + } + else + { + System.arraycopy(data, from, tmp, 0, newLength); + } + + return tmp; + } + + public static int[] copyOfRange(int[] data, int from, int to) + { + int newLength = getLength(from, to); + + int[] tmp = new int[newLength]; + + if (data.length - from < newLength) + { + System.arraycopy(data, from, tmp, 0, data.length - from); + } + else + { + System.arraycopy(data, from, tmp, 0, newLength); + } + + return tmp; + } + + public static long[] copyOfRange(long[] data, int from, int to) + { + int newLength = getLength(from, to); + + long[] tmp = new long[newLength]; + + if (data.length - from < newLength) + { + System.arraycopy(data, from, tmp, 0, data.length - from); + } + else + { + System.arraycopy(data, from, tmp, 0, newLength); + } + + return tmp; + } + + public static BigInteger[] copyOfRange(BigInteger[] data, int from, int to) + { + int newLength = getLength(from, to); + + BigInteger[] tmp = new BigInteger[newLength]; + + if (data.length - from < newLength) + { + System.arraycopy(data, from, tmp, 0, data.length - from); + } + else + { + System.arraycopy(data, from, tmp, 0, newLength); + } + + return tmp; + } + + private static int getLength(int from, int to) + { + int newLength = to - from; + if (newLength < 0) + { + StringBuffer sb = new StringBuffer(from); + sb.append(" > ").append(to); + throw new IllegalArgumentException(sb.toString()); + } + return newLength; + } + + public static byte[] append(byte[] a, byte b) + { + if (a == null) + { + return new byte[]{ b }; + } + + int length = a.length; + byte[] result = new byte[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static short[] append(short[] a, short b) + { + if (a == null) + { + return new short[]{ b }; + } + + int length = a.length; + short[] result = new short[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static int[] append(int[] a, int b) + { + if (a == null) + { + return new int[]{ b }; + } + + int length = a.length; + int[] result = new int[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static String[] append(String[] a, String b) + { + if (a == null) + { + return new String[]{ b }; + } + + int length = a.length; + String[] result = new String[length + 1]; + System.arraycopy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + + + public static byte[] concatenate(byte[] a, byte[] b) + { + if (a != null && b != null) + { + byte[] rv = new byte[a.length + b.length]; + + System.arraycopy(a, 0, rv, 0, a.length); + System.arraycopy(b, 0, rv, a.length, b.length); + + return rv; + } + else if (b != null) + { + return clone(b); + } + else + { + return clone(a); + } + } + + public static byte[] concatenate(byte[] a, byte[] b, byte[] c) + { + if (a != null && b != null && c != null) + { + byte[] rv = new byte[a.length + b.length + c.length]; + + System.arraycopy(a, 0, rv, 0, a.length); + System.arraycopy(b, 0, rv, a.length, b.length); + System.arraycopy(c, 0, rv, a.length + b.length, c.length); + + return rv; + } + else if (a == null) + { + return concatenate(b, c); + } + else if (b == null) + { + return concatenate(a, c); + } + else + { + return concatenate(a, b); + } + } + + public static byte[] concatenate(byte[] a, byte[] b, byte[] c, byte[] d) + { + if (a != null && b != null && c != null && d != null) + { + byte[] rv = new byte[a.length + b.length + c.length + d.length]; + + System.arraycopy(a, 0, rv, 0, a.length); + System.arraycopy(b, 0, rv, a.length, b.length); + System.arraycopy(c, 0, rv, a.length + b.length, c.length); + System.arraycopy(d, 0, rv, a.length + b.length + c.length, d.length); + + return rv; + } + else if (d == null) + { + return concatenate(a, b, c); + } + else if (c == null) + { + return concatenate(a, b, d); + } + else if (b == null) + { + return concatenate(a, c, d); + } + else + { + return concatenate(b, c, d); + } + } + + public static byte[] concatenate(byte[][] arrays) + { + int size = 0; + for (int i = 0; i != arrays.length; i++) + { + size += arrays[i].length; + } + + byte[] rv = new byte[size]; + + int offSet = 0; + for (int i = 0; i != arrays.length; i++) + { + System.arraycopy(arrays[i], 0, rv, offSet, arrays[i].length); + offSet += arrays[i].length; + } + + return rv; + } + + public static int[] concatenate(int[] a, int[] b) + { + if (a == null) + { + return clone(b); + } + if (b == null) + { + return clone(a); + } + + int[] c = new int[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + + public static byte[] prepend(byte[] a, byte b) + { + if (a == null) + { + return new byte[]{ b }; + } + + int length = a.length; + byte[] result = new byte[length + 1]; + System.arraycopy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static short[] prepend(short[] a, short b) + { + if (a == null) + { + return new short[]{ b }; + } + + int length = a.length; + short[] result = new short[length + 1]; + System.arraycopy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static int[] prepend(int[] a, int b) + { + if (a == null) + { + return new int[]{ b }; + } + + int length = a.length; + int[] result = new int[length + 1]; + System.arraycopy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static byte[] reverse(byte[] a) + { + if (a == null) + { + return null; + } + + int p1 = 0, p2 = a.length; + byte[] result = new byte[p2]; + + while (--p2 >= 0) + { + result[p2] = a[p1++]; + } + + return result; + } + + public static int[] reverse(int[] a) + { + if (a == null) + { + return null; + } + + int p1 = 0, p2 = a.length; + int[] result = new int[p2]; + + while (--p2 >= 0) + { + result[p2] = a[p1++]; + } + + return result; + } + + /** + * Iterator backed by a specific array. + */ + public static class Iterator + implements java.util.Iterator + { + private final T[] dataArray; + + private int position = 0; + + /** + * Base constructor. + *

+ * Note: the array is not cloned, changes to it will affect the values returned by next(). + *

+ * + * @param dataArray array backing the iterator. + */ + public Iterator(T[] dataArray) + { + this.dataArray = dataArray; + } + + public boolean hasNext() + { + return position < dataArray.length; + } + + public T next() + { + if (position == dataArray.length) + { + throw new NoSuchElementException("Out of elements: " + position); + } + + return dataArray[position++]; + } + + public void remove() + { + throw new UnsupportedOperationException("Cannot remove element from an Array."); + } + } +} diff --git a/service/src/main/java/org/spongycastle/util/Integers.java b/service/src/main/java/org/spongycastle/util/Integers.java new file mode 100644 index 0000000..3b6dc35 --- /dev/null +++ b/service/src/main/java/org/spongycastle/util/Integers.java @@ -0,0 +1,22 @@ +package org.spongycastle.util; + +/** + * Utility methods for ints. + */ +public class Integers +{ + public static int rotateLeft(int i, int distance) + { + return Integer.rotateLeft(i, distance); + } + + public static int rotateRight(int i, int distance) + { + return Integer.rotateRight(i, distance); + } + + public static Integer valueOf(int value) + { + return Integer.valueOf(value); + } +} diff --git a/service/src/main/java/org/spongycastle/util/Memoable.java b/service/src/main/java/org/spongycastle/util/Memoable.java new file mode 100644 index 0000000..3d0113d --- /dev/null +++ b/service/src/main/java/org/spongycastle/util/Memoable.java @@ -0,0 +1,27 @@ +package org.spongycastle.util; + +/** + * Interface for Memoable objects. Memoable objects allow the taking of a snapshot of their internal state + * via the copy() method and then reseting the object back to that state later using the reset() method. + */ +public interface Memoable +{ + /** + * Produce a copy of this object with its configuration and in its current state. + *

+ * The returned object may be used simply to store the state, or may be used as a similar object + * starting from the copied state. + */ + Memoable copy(); + + /** + * Restore a copied object state into this object. + *

+ * Implementations of this method should try to avoid or minimise memory allocation to perform the reset. + * + * @param other an object originally {@link #copy() copied} from an object of the same type as this instance. + * @throws ClassCastException if the provided object is not of the correct type. + * @throws MemoableResetException if the other parameter is in some other way invalid. + */ + void reset(Memoable other); +} diff --git a/service/src/main/java/org/spongycastle/util/MemoableResetException.java b/service/src/main/java/org/spongycastle/util/MemoableResetException.java new file mode 100644 index 0000000..6d579ed --- /dev/null +++ b/service/src/main/java/org/spongycastle/util/MemoableResetException.java @@ -0,0 +1,22 @@ +package org.spongycastle.util; + +/** + * Exception to be thrown on a failure to reset an object implementing Memoable. + *

+ * The exception extends ClassCastException to enable users to have a single handling case, + * only introducing specific handling of this one if required. + *

+ */ +public class MemoableResetException + extends ClassCastException +{ + /** + * Basic Constructor. + * + * @param msg message to be associated with this exception. + */ + public MemoableResetException(String msg) + { + super(msg); + } +} diff --git a/service/src/main/java/org/spongycastle/util/Pack.java b/service/src/main/java/org/spongycastle/util/Pack.java new file mode 100644 index 0000000..bc68f11 --- /dev/null +++ b/service/src/main/java/org/spongycastle/util/Pack.java @@ -0,0 +1,260 @@ +package org.spongycastle.util; + +/** + * Utility methods for converting byte arrays into ints and longs, and back again. + */ +public abstract class Pack +{ + public static short bigEndianToShort(byte[] bs, int off) + { + int n = (bs[ off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return (short)n; + } + + public static int bigEndianToInt(byte[] bs, int off) + { + int n = bs[ off] << 24; + n |= (bs[++off] & 0xff) << 16; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return n; + } + + public static void bigEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToInt(bs, off); + off += 4; + } + } + + public static byte[] intToBigEndian(int n) + { + byte[] bs = new byte[4]; + intToBigEndian(n, bs, 0); + return bs; + } + + public static void intToBigEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n >>> 24); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n ); + } + + public static byte[] intToBigEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToBigEndian(ns, bs, 0); + return bs; + } + + public static void intToBigEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToBigEndian(ns[i], bs, off); + off += 4; + } + } + + public static long bigEndianToLong(byte[] bs, int off) + { + int hi = bigEndianToInt(bs, off); + int lo = bigEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void bigEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToLong(bs, off); + off += 8; + } + } + + public static byte[] longToBigEndian(long n) + { + byte[] bs = new byte[8]; + longToBigEndian(n, bs, 0); + return bs; + } + + public static void longToBigEndian(long n, byte[] bs, int off) + { + intToBigEndian((int)(n >>> 32), bs, off); + intToBigEndian((int)(n & 0xffffffffL), bs, off + 4); + } + + public static byte[] longToBigEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToBigEndian(ns, bs, 0); + return bs; + } + + public static void longToBigEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToBigEndian(ns[i], bs, off); + off += 8; + } + } + + public static short littleEndianToShort(byte[] bs, int off) + { + int n = bs[ off] & 0xff; + n |= (bs[++off] & 0xff) << 8; + return (short)n; + } + + public static int littleEndianToInt(byte[] bs, int off) + { + int n = bs[ off] & 0xff; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff) << 16; + n |= bs[++off] << 24; + return n; + } + + public static void littleEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToInt(bs, off); + off += 4; + } + } + + public static void littleEndianToInt(byte[] bs, int bOff, int[] ns, int nOff, int count) + { + for (int i = 0; i < count; ++i) + { + ns[nOff + i] = littleEndianToInt(bs, bOff); + bOff += 4; + } + } + + public static int[] littleEndianToInt(byte[] bs, int off, int count) + { + int[] ns = new int[count]; + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToInt(bs, off); + off += 4; + } + return ns; + } + + public static byte[] shortToLittleEndian(short n) + { + byte[] bs = new byte[2]; + shortToLittleEndian(n, bs, 0); + return bs; + } + + public static void shortToLittleEndian(short n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >>> 8); + } + + public static byte[] intToLittleEndian(int n) + { + byte[] bs = new byte[4]; + intToLittleEndian(n, bs, 0); + return bs; + } + + public static void intToLittleEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 24); + } + + public static byte[] intToLittleEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToLittleEndian(ns, bs, 0); + return bs; + } + + public static void intToLittleEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToLittleEndian(ns[i], bs, off); + off += 4; + } + } + + public static long littleEndianToLong(byte[] bs, int off) + { + int lo = littleEndianToInt(bs, off); + int hi = littleEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void littleEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToLong(bs, off); + off += 8; + } + } + + public static void littleEndianToLong(byte[] bs, int bsOff, long[] ns, int nsOff, int nsLen) + { + for (int i = 0; i < nsLen; ++i) + { + ns[nsOff + i] = littleEndianToLong(bs, bsOff); + bsOff += 8; + } + } + + public static byte[] longToLittleEndian(long n) + { + byte[] bs = new byte[8]; + longToLittleEndian(n, bs, 0); + return bs; + } + + public static void longToLittleEndian(long n, byte[] bs, int off) + { + intToLittleEndian((int)(n & 0xffffffffL), bs, off); + intToLittleEndian((int)(n >>> 32), bs, off + 4); + } + + public static byte[] longToLittleEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToLittleEndian(ns, bs, 0); + return bs; + } + + public static void longToLittleEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToLittleEndian(ns[i], bs, off); + off += 8; + } + } + + public static void longToLittleEndian(long[] ns, int nsOff, int nsLen, byte[] bs, int bsOff) + { + for (int i = 0; i < nsLen; ++i) + { + longToLittleEndian(ns[nsOff + i], bs, bsOff); + bsOff += 8; + } + } +} diff --git a/service/src/main/java/org/spongycastle/util/StringList.java b/service/src/main/java/org/spongycastle/util/StringList.java new file mode 100644 index 0000000..3955370 --- /dev/null +++ b/service/src/main/java/org/spongycastle/util/StringList.java @@ -0,0 +1,42 @@ +package org.spongycastle.util; + +/** + * An interface defining a list of strings. + */ +public interface StringList + extends Iterable +{ + /** + * Add a String to the list. + * + * @param s the String to add. + * @return true + */ + boolean add(String s); + + /** + * Get the string at index index. + * + * @param index the index position of the String of interest. + * @return the String at position index. + */ + String get(int index); + + int size(); + + /** + * Return the contents of the list as an array. + * + * @return an array of String. + */ + String[] toStringArray(); + + /** + * Return a section of the contents of the list. If the list is too short the array is filled with nulls. + * + * @param from the initial index of the range to be copied, inclusive + * @param to the final index of the range to be copied, exclusive. + * @return an array of length to - from + */ + String[] toStringArray(int from, int to); +} diff --git a/service/src/main/java/org/spongycastle/util/Strings.java b/service/src/main/java/org/spongycastle/util/Strings.java new file mode 100644 index 0000000..8cbeed4 --- /dev/null +++ b/service/src/main/java/org/spongycastle/util/Strings.java @@ -0,0 +1,406 @@ +package org.spongycastle.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Vector; +import java.util.stream.Stream; + +/** + * String utilities. + */ +public final class Strings +{ + private static String LINE_SEPARATOR; + + static + { + try + { + LINE_SEPARATOR = AccessController.doPrivileged(new PrivilegedAction() + { + public String run() + { + // the easy way + return System.getProperty("line.separator"); + } + }); + + } + catch (Exception e) + { + try + { + // the harder way + LINE_SEPARATOR = String.format("%n"); + } + catch (Exception ef) + { + LINE_SEPARATOR = "\n"; // we're desperate use this... + } + } + } + + public static String fromUTF8ByteArray(byte[] bytes) + { + int i = 0; + int length = 0; + + while (i < bytes.length) + { + length++; + if ((bytes[i] & 0xf0) == 0xf0) + { + // surrogate pair + length++; + i += 4; + } + else if ((bytes[i] & 0xe0) == 0xe0) + { + i += 3; + } + else if ((bytes[i] & 0xc0) == 0xc0) + { + i += 2; + } + else + { + i += 1; + } + } + + char[] cs = new char[length]; + + i = 0; + length = 0; + + while (i < bytes.length) + { + char ch; + + if ((bytes[i] & 0xf0) == 0xf0) + { + int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i + 1] & 0x3F) << 12) | ((bytes[i + 2] & 0x3F) << 6) | (bytes[i + 3] & 0x3F); + int U = codePoint - 0x10000; + char W1 = (char)(0xD800 | (U >> 10)); + char W2 = (char)(0xDC00 | (U & 0x3FF)); + cs[length++] = W1; + ch = W2; + i += 4; + } + else if ((bytes[i] & 0xe0) == 0xe0) + { + ch = (char)(((bytes[i] & 0x0f) << 12) + | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)); + i += 3; + } + else if ((bytes[i] & 0xd0) == 0xd0) + { + ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f)); + i += 2; + } + else if ((bytes[i] & 0xc0) == 0xc0) + { + ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f)); + i += 2; + } + else + { + ch = (char)(bytes[i] & 0xff); + i += 1; + } + + cs[length++] = ch; + } + + return new String(cs); + } + + public static byte[] toUTF8ByteArray(String string) + { + return toUTF8ByteArray(string.toCharArray()); + } + + public static byte[] toUTF8ByteArray(char[] string) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + toUTF8ByteArray(string, bOut); + } + catch (IOException e) + { + throw new IllegalStateException("cannot encode string to byte array!"); + } + + return bOut.toByteArray(); + } + + public static void toUTF8ByteArray(char[] string, OutputStream sOut) + throws IOException + { + char[] c = string; + int i = 0; + + while (i < c.length) + { + char ch = c[i]; + + if (ch < 0x0080) + { + sOut.write(ch); + } + else if (ch < 0x0800) + { + sOut.write(0xc0 | (ch >> 6)); + sOut.write(0x80 | (ch & 0x3f)); + } + // surrogate pair + else if (ch >= 0xD800 && ch <= 0xDFFF) + { + // in error - can only happen, if the Java String class has a + // bug. + if (i + 1 >= c.length) + { + throw new IllegalStateException("invalid UTF-16 codepoint"); + } + char W1 = ch; + ch = c[++i]; + char W2 = ch; + // in error - can only happen, if the Java String class has a + // bug. + if (W1 > 0xDBFF) + { + throw new IllegalStateException("invalid UTF-16 codepoint"); + } + int codePoint = (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)) + 0x10000; + sOut.write(0xf0 | (codePoint >> 18)); + sOut.write(0x80 | ((codePoint >> 12) & 0x3F)); + sOut.write(0x80 | ((codePoint >> 6) & 0x3F)); + sOut.write(0x80 | (codePoint & 0x3F)); + } + else + { + sOut.write(0xe0 | (ch >> 12)); + sOut.write(0x80 | ((ch >> 6) & 0x3F)); + sOut.write(0x80 | (ch & 0x3F)); + } + + i++; + } + } + + /** + * A locale independent version of toUpperCase. + * + * @param string input to be converted + * @return a US Ascii uppercase version + */ + public static String toUpperCase(String string) + { + boolean changed = false; + char[] chars = string.toCharArray(); + + for (int i = 0; i != chars.length; i++) + { + char ch = chars[i]; + if ('a' <= ch && 'z' >= ch) + { + changed = true; + chars[i] = (char)(ch - 'a' + 'A'); + } + } + + if (changed) + { + return new String(chars); + } + + return string; + } + + /** + * A locale independent version of toLowerCase. + * + * @param string input to be converted + * @return a US ASCII lowercase version + */ + public static String toLowerCase(String string) + { + boolean changed = false; + char[] chars = string.toCharArray(); + + for (int i = 0; i != chars.length; i++) + { + char ch = chars[i]; + if ('A' <= ch && 'Z' >= ch) + { + changed = true; + chars[i] = (char)(ch - 'A' + 'a'); + } + } + + if (changed) + { + return new String(chars); + } + + return string; + } + + public static byte[] toByteArray(char[] chars) + { + byte[] bytes = new byte[chars.length]; + + for (int i = 0; i != bytes.length; i++) + { + bytes[i] = (byte)chars[i]; + } + + return bytes; + } + + public static byte[] toByteArray(String string) + { + byte[] bytes = new byte[string.length()]; + + for (int i = 0; i != bytes.length; i++) + { + char ch = string.charAt(i); + + bytes[i] = (byte)ch; + } + + return bytes; + } + + public static int toByteArray(String s, byte[] buf, int off) + { + int count = s.length(); + for (int i = 0; i < count; ++i) + { + char c = s.charAt(i); + buf[off + i] = (byte)c; + } + return count; + } + + /** + * Convert an array of 8 bit characters into a string. + * + * @param bytes 8 bit characters. + * @return resulting String. + */ + public static String fromByteArray(byte[] bytes) + { + return new String(asCharArray(bytes)); + } + + /** + * Do a simple conversion of an array of 8 bit characters into a string. + * + * @param bytes 8 bit characters. + * @return resulting String. + */ + public static char[] asCharArray(byte[] bytes) + { + char[] chars = new char[bytes.length]; + + for (int i = 0; i != chars.length; i++) + { + chars[i] = (char)(bytes[i] & 0xff); + } + + return chars; + } + + public static String[] split(String input, char delimiter) + { + Vector v = new Vector(); + boolean moreTokens = true; + String subString; + + while (moreTokens) + { + int tokenLocation = input.indexOf(delimiter); + if (tokenLocation > 0) + { + subString = input.substring(0, tokenLocation); + v.addElement(subString); + input = input.substring(tokenLocation + 1); + } + else + { + moreTokens = false; + v.addElement(input); + } + } + + String[] res = new String[v.size()]; + + for (int i = 0; i != res.length; i++) + { + res[i] = (String)v.elementAt(i); + } + return res; + } + + public static StringList newList() + { + return new StringListImpl(); + } + + public static String lineSeparator() + { + return LINE_SEPARATOR; + } + + private static class StringListImpl + extends ArrayList + implements StringList + { + public boolean add(String s) + { + return super.add(s); + } + + public String set(int index, String element) + { + return super.set(index, element); + } + + public void add(int index, String element) + { + super.add(index, element); + } + + public String[] toStringArray() + { + String[] strs = new String[this.size()]; + + for (int i = 0; i != strs.length; i++) + { + strs[i] = this.get(i); + } + + return strs; + } + + public String[] toStringArray(int from, int to) + { + String[] strs = new String[to - from]; + + for (int i = from; i != this.size() && i != to; i++) + { + strs[i - from] = this.get(i); + } + + return strs; + } + + } +} diff --git a/service/src/main/jni/Android.mk b/service/src/main/jni/Android.mk new file mode 100644 index 0000000..b910fa9 --- /dev/null +++ b/service/src/main/jni/Android.mk @@ -0,0 +1,73 @@ +LOCAL_PATH := $(call my-dir) +ROOT_PATH := $(LOCAL_PATH) +BUILD_SHARED_EXECUTABLE := $(LOCAL_PATH)/build-shared-executable.mk + +######################################################## +## pdnsd (local DNS forwarder for the VPN DNS path) +## Produces libpdnsd.so in nativeLibraryDir; loaded by Pdnsd.java. +######################################################## +include $(CLEAR_VARS) +PDNSD_SOURCES := $(wildcard $(LOCAL_PATH)/pdnsd/src/*.c) +LOCAL_MODULE := pdnsd +LOCAL_SRC_FILES := $(PDNSD_SOURCES:$(LOCAL_PATH)/%=%) +LOCAL_CFLAGS := -Wall -O2 -I$(LOCAL_PATH)/pdnsd -DHAVE_STPCPY +include $(BUILD_SHARED_EXECUTABLE) + +include $(CLEAR_VARS) +ANCILLARY_SOURCE := fd_recv.c fd_send.c +LOCAL_MODULE := libancillary +LOCAL_CFLAGS := -O2 -I$(LOCAL_PATH)/libancillary +LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE)) +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE:= system +LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libancillary +LOCAL_SRC_FILES:= system.cpp +LOCAL_LDLIBS := -ldl -llog +LOCAL_STATIC_LIBRARIES := cpufeatures libancillary +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_CFLAGS := -std=gnu99 +LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE +LOCAL_CFLAGS += -DBADVPN_USE_SELFPIPE -DBADVPN_USE_EPOLL +LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE -DNDEBUG -DANDROID +LOCAL_STATIC_LIBRARIES := libancillary +LOCAL_C_INCLUDES:= \ + $(LOCAL_PATH)/libancillary \ + $(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \ + $(LOCAL_PATH)/badvpn/lwip/src/include/ipv6 \ + $(LOCAL_PATH)/badvpn/lwip/src/include \ + $(LOCAL_PATH)/badvpn/lwip/custom \ + $(LOCAL_PATH)/badvpn/ +TUN2SOCKS_SOURCES := \ + base/BLog_syslog.c system/BReactor_badvpn.c system/BSignal.c \ + system/BConnection_unix.c system/BTime.c system/BUnixSignal.c system/BNetwork.c \ + flow/StreamRecvInterface.c flow/PacketRecvInterface.c flow/PacketPassInterface.c \ + flow/StreamPassInterface.c flow/SinglePacketBuffer.c flow/BufferWriter.c \ + flow/PacketBuffer.c flow/PacketStreamSender.c flow/PacketPassConnector.c \ + flow/PacketProtoFlow.c flow/PacketPassFairQueue.c flow/PacketProtoEncoder.c \ + flow/PacketProtoDecoder.c socksclient/BSocksClient.c tuntap/BTap.c \ + lwip/src/core/timers.c lwip/src/core/udp.c lwip/src/core/memp.c \ + lwip/src/core/init.c lwip/src/core/pbuf.c lwip/src/core/tcp.c \ + lwip/src/core/tcp_out.c lwip/src/core/netif.c lwip/src/core/def.c \ + lwip/src/core/mem.c lwip/src/core/tcp_in.c lwip/src/core/stats.c \ + lwip/src/core/inet_chksum.c lwip/src/core/ipv4/icmp.c \ + lwip/src/core/ipv4/igmp.c lwip/src/core/ipv4/ip4_addr.c \ + lwip/src/core/ipv4/ip_frag.c lwip/src/core/ipv4/ip4.c \ + lwip/src/core/ipv4/autoip.c lwip/src/core/ipv6/ethip6.c \ + lwip/src/core/ipv6/inet6.c lwip/src/core/ipv6/ip6_addr.c \ + lwip/src/core/ipv6/mld6.c lwip/src/core/ipv6/dhcp6.c \ + lwip/src/core/ipv6/icmp6.c lwip/src/core/ipv6/ip6.c \ + lwip/src/core/ipv6/ip6_frag.c lwip/src/core/ipv6/nd6.c \ + lwip/custom/sys.c tun2socks/tun2socks.c base/DebugObject.c \ + base/BLog.c base/BPending.c system/BDatagram_unix.c \ + flowextra/PacketPassInactivityMonitor.c tun2socks/SocksUdpGwClient.c \ + udpgw_client/UdpGwClient.c +LOCAL_MODULE := tun2socks +LOCAL_LDLIBS := -ldl -llog +LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES)) +include $(BUILD_SHARED_EXECUTABLE) + +$(call import-module,android/cpufeatures) diff --git a/service/src/main/jni/Application.mk b/service/src/main/jni/Application.mk new file mode 100644 index 0000000..a3799fa --- /dev/null +++ b/service/src/main/jni/Application.mk @@ -0,0 +1,4 @@ +APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 +APP_PLATFORM := android-26 +APP_STL := c++_static +APP_LDFLAGS += -Wl,-z,max-page-size=16384 diff --git a/service/src/main/jni/badvpn/COPYING b/service/src/main/jni/badvpn/COPYING new file mode 100644 index 0000000..f973347 --- /dev/null +++ b/service/src/main/jni/badvpn/COPYING @@ -0,0 +1,24 @@ +Copyright (c) 2009, Ambroz Bizjak +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/service/src/main/jni/badvpn/base/BLog.c b/service/src/main/jni/badvpn/base/BLog.c new file mode 100644 index 0000000..e77073a --- /dev/null +++ b/service/src/main/jni/badvpn/base/BLog.c @@ -0,0 +1,88 @@ +/** + * @file BLog.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "BLog.h" + +#ifdef ANDROID +#include +#endif + +#ifndef BADVPN_PLUGIN + +struct _BLog_channel blog_channel_list[] = { +#include +}; + +struct _BLog_global blog_global = { + #ifndef NDEBUG + 0 + #endif +}; + +#endif + +// keep in sync with level numbers in BLog.h! +static char *level_names[] = { NULL, "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG" }; + +static void stdout_log (int channel, int level, const char *msg) +{ +#ifndef ANDROID + fprintf(stdout, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#else + __android_log_print(ANDROID_LOG_DEBUG, "tun2socks", + "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#endif +} + +static void stderr_log (int channel, int level, const char *msg) +{ +#ifndef ANDROID + fprintf(stderr, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#else + __android_log_print(ANDROID_LOG_ERROR, "tun2socks", + "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#endif +} + +static void stdout_stderr_free (void) +{ +} + +void BLog_InitStdout (void) +{ + BLog_Init(stdout_log, stdout_stderr_free); +} + +void BLog_InitStderr (void) +{ + BLog_Init(stderr_log, stdout_stderr_free); +} diff --git a/service/src/main/jni/badvpn/base/BLog.h b/service/src/main/jni/badvpn/base/BLog.h new file mode 100644 index 0000000..d7234ca --- /dev/null +++ b/service/src/main/jni/badvpn/base/BLog.h @@ -0,0 +1,399 @@ +/** + * @file BLog.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A global object for logging. + */ + +#ifndef BADVPN_BLOG_H +#define BADVPN_BLOG_H + +#include +#include + +#include +#include + +// auto-generated channel numbers and number of channels +#include + +// keep in sync with level names in BLog.c! +#define BLOG_ERROR 1 +#define BLOG_WARNING 2 +#define BLOG_NOTICE 3 +#define BLOG_INFO 4 +#define BLOG_DEBUG 5 + +#define BLog(...) BLog_LogToChannel(BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define BContextLog(context, ...) BLog_ContextLog((context), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define BLOG_CCCC(context) BLog_MakeChannelContext((context), BLOG_CURRENT_CHANNEL) + +typedef void (*_BLog_log_func) (int channel, int level, const char *msg); +typedef void (*_BLog_free_func) (void); + +struct _BLog_channel { + const char *name; + int loglevel; +}; + +struct _BLog_global { + #ifndef NDEBUG + int initialized; // initialized statically + #endif + struct _BLog_channel channels[BLOG_NUM_CHANNELS]; + _BLog_log_func log_func; + _BLog_free_func free_func; + BMutex mutex; +#ifndef NDEBUG + int logging; +#endif + char logbuf[2048]; + int logbuf_pos; +}; + +extern struct _BLog_channel blog_channel_list[]; +extern struct _BLog_global blog_global; + +typedef void (*BLog_logfunc) (void *); + +typedef struct { + BLog_logfunc logfunc; + void *logfunc_user; +} BLogContext; + +typedef struct { + BLogContext context; + int channel; +} BLogChannelContext; + +static int BLogGlobal_GetChannelByName (const char *channel_name); + +static void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func); +static void BLog_Free (void); +static void BLog_SetChannelLoglevel (int channel, int loglevel); +static int BLog_WouldLog (int channel, int level); +static void BLog_Begin (void); +static void BLog_AppendVarArg (const char *fmt, va_list vl); +static void BLog_Append (const char *fmt, ...); +static void BLog_AppendBytes (const char *data, size_t len); +static void BLog_Finish (int channel, int level); +static void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl); +static void BLog_LogToChannel (int channel, int level, const char *fmt, ...); +static void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl); +static void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...); +static BLogContext BLog_RootContext (void); +static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user); +static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl); +static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...); +static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel); +static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl); +static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...); + +void BLog_InitStdout (void); +void BLog_InitStderr (void); + +int BLogGlobal_GetChannelByName (const char *channel_name) +{ + int i; + for (i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (!strcmp(blog_channel_list[i].name, channel_name)) { + return i; + } + } + + return -1; +} + +void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func) +{ + ASSERT(!blog_global.initialized) + + #ifndef NDEBUG + blog_global.initialized = 1; + #endif + + // initialize channels + memcpy(blog_global.channels, blog_channel_list, BLOG_NUM_CHANNELS * sizeof(struct _BLog_channel)); + + blog_global.log_func = log_func; + blog_global.free_func = free_func; +#ifndef NDEBUG + blog_global.logging = 0; +#endif + blog_global.logbuf_pos = 0; + blog_global.logbuf[0] = '\0'; + + ASSERT_FORCE(BMutex_Init(&blog_global.mutex)) +} + +void BLog_Free (void) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(!blog_global.logging) +#endif + + BMutex_Free(&blog_global.mutex); + + #ifndef NDEBUG + blog_global.initialized = 0; + #endif + + blog_global.free_func(); +} + +void BLog_SetChannelLoglevel (int channel, int loglevel) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(loglevel >= 0 && loglevel <= BLOG_DEBUG) + + blog_global.channels[channel].loglevel = loglevel; +} + +int BLog_WouldLog (int channel, int level) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + return (level <= blog_global.channels[channel].loglevel); +} + +void BLog_Begin (void) +{ + ASSERT(blog_global.initialized) + + BMutex_Lock(&blog_global.mutex); + +#ifndef NDEBUG + ASSERT(!blog_global.logging) + blog_global.logging = 1; +#endif +} + +void BLog_AppendVarArg (const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + + int w = vsnprintf(blog_global.logbuf + blog_global.logbuf_pos, sizeof(blog_global.logbuf) - blog_global.logbuf_pos, fmt, vl); + + if (w >= sizeof(blog_global.logbuf) - blog_global.logbuf_pos) { + blog_global.logbuf_pos = sizeof(blog_global.logbuf) - 1; + } else { + blog_global.logbuf_pos += w; + } +} + +void BLog_Append (const char *fmt, ...) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + + va_list vl; + va_start(vl, fmt); + BLog_AppendVarArg(fmt, vl); + va_end(vl); +} + +void BLog_AppendBytes (const char *data, size_t len) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + + size_t avail = (sizeof(blog_global.logbuf) - 1) - blog_global.logbuf_pos; + len = (len > avail ? avail : len); + + memcpy(blog_global.logbuf + blog_global.logbuf_pos, data, len); + blog_global.logbuf_pos += len; + blog_global.logbuf[blog_global.logbuf_pos] = '\0'; +} + +void BLog_Finish (int channel, int level) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + ASSERT(BLog_WouldLog(channel, level)) + + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + ASSERT(blog_global.logbuf[blog_global.logbuf_pos] == '\0') + + blog_global.log_func(channel, level, blog_global.logbuf); + +#ifndef NDEBUG + blog_global.logging = 0; +#endif + blog_global.logbuf_pos = 0; + blog_global.logbuf[0] = '\0'; + + BMutex_Unlock(&blog_global.mutex); +} + +void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + BLog_Begin(); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); +} + +void BLog_LogToChannel (int channel, int level, const char *fmt, ...) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + va_list vl; + va_start(vl, fmt); + + BLog_Begin(); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); + + va_end(vl); +} + +void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + BLog_Begin(); + func(arg); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); +} + +void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + va_list vl; + va_start(vl, fmt); + + BLog_Begin(); + func(arg); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); + + va_end(vl); +} + +static void BLog__root_logfunc (void *unused) +{ +} + +static BLogContext BLog_RootContext (void) +{ + return BLog_MakeContext(BLog__root_logfunc, NULL); +} + +static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user) +{ + ASSERT(logfunc) + + BLogContext context; + context.logfunc = logfunc; + context.logfunc_user = logfunc_user; + return context; +} + +static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl) +{ + BLog_LogViaFuncVarArg(context.logfunc, context.logfunc_user, channel, level, fmt, vl); +} + +static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_ContextLogVarArg(context, channel, level, fmt, vl); + va_end(vl); +} + +static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel) +{ + BLogChannelContext ccontext; + ccontext.context = context; + ccontext.channel = channel; + return ccontext; +} + +static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl) +{ + BLog_ContextLogVarArg(ccontext.context, ccontext.channel, level, fmt, vl); +} + +static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_ChannelContextLogVarArg(ccontext, level, fmt, vl); + va_end(vl); +} + +#endif diff --git a/service/src/main/jni/badvpn/base/BLog_syslog.c b/service/src/main/jni/badvpn/base/BLog_syslog.c new file mode 100644 index 0000000..d7a954b --- /dev/null +++ b/service/src/main/jni/badvpn/base/BLog_syslog.c @@ -0,0 +1,150 @@ +/** + * @file BLog_syslog.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include "BLog_syslog.h" + +static int resolve_facility (char *str, int *out) +{ + if (!strcmp(str, "authpriv")) { + *out = LOG_AUTHPRIV; + } + else if (!strcmp(str, "cron")) { + *out = LOG_CRON; + } + else if (!strcmp(str, "daemon")) { + *out = LOG_DAEMON; + } + else if (!strcmp(str, "ftp")) { + *out = LOG_FTP; + } + else if (!strcmp(str, "local0")) { + *out = LOG_LOCAL0; + } + else if (!strcmp(str, "local1")) { + *out = LOG_LOCAL1; + } + else if (!strcmp(str, "local2")) { + *out = LOG_LOCAL2; + } + else if (!strcmp(str, "local3")) { + *out = LOG_LOCAL3; + } + else if (!strcmp(str, "local4")) { + *out = LOG_LOCAL4; + } + else if (!strcmp(str, "local5")) { + *out = LOG_LOCAL5; + } + else if (!strcmp(str, "local6")) { + *out = LOG_LOCAL6; + } + else if (!strcmp(str, "local7")) { + *out = LOG_LOCAL7; + } + else if (!strcmp(str, "lpr")) { + *out = LOG_LPR; + } + else if (!strcmp(str, "mail")) { + *out = LOG_MAIL; + } + else if (!strcmp(str, "news")) { + *out = LOG_NEWS; + } + else if (!strcmp(str, "syslog")) { + *out = LOG_SYSLOG; + } + else if (!strcmp(str, "user")) { + *out = LOG_USER; + } + else if (!strcmp(str, "uucp")) { + *out = LOG_UUCP; + } + else { + return 0; + } + + return 1; +} + +static int convert_level (int level) +{ + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + switch (level) { + case BLOG_ERROR: + return LOG_ERR; + case BLOG_WARNING: + return LOG_WARNING; + case BLOG_NOTICE: + return LOG_NOTICE; + case BLOG_INFO: + return LOG_INFO; + case BLOG_DEBUG: + return LOG_DEBUG; + default: + ASSERT(0) + return 0; + } +} + +static struct { + char ident[200]; +} syslog_global; + +static void syslog_log (int channel, int level, const char *msg) +{ + syslog(convert_level(level), "%s: %s", blog_global.channels[channel].name, msg); +} + +static void syslog_free (void) +{ + closelog(); +} + +int BLog_InitSyslog (char *ident, char *facility_str) +{ + int facility; + if (!resolve_facility(facility_str, &facility)) { + return 0; + } + + snprintf(syslog_global.ident, sizeof(syslog_global.ident), "%s", ident); + + openlog(syslog_global.ident, 0, facility); + + BLog_Init(syslog_log, syslog_free); + + return 1; +} diff --git a/service/src/main/jni/badvpn/base/BLog_syslog.h b/service/src/main/jni/badvpn/base/BLog_syslog.h new file mode 100644 index 0000000..1adf04e --- /dev/null +++ b/service/src/main/jni/badvpn/base/BLog_syslog.h @@ -0,0 +1,42 @@ +/** + * @file BLog_syslog.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * BLog syslog backend. + */ + +#ifndef BADVPN_BLOG_SYSLOG_H +#define BADVPN_BLOG_SYSLOG_H + +#include +#include + +int BLog_InitSyslog (char *ident, char *facility) WARN_UNUSED; + +#endif diff --git a/service/src/main/jni/badvpn/base/BMutex.h b/service/src/main/jni/badvpn/base/BMutex.h new file mode 100644 index 0000000..fbcbd05 --- /dev/null +++ b/service/src/main/jni/badvpn/base/BMutex.h @@ -0,0 +1,101 @@ +/** + * @file BMutex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BMUTEX_H +#define BADVPN_BMUTEX_H + +#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1) +#error BADVPN_THREAD_SAFE is not defined or incorrect +#endif + +#if BADVPN_THREAD_SAFE +#include +#endif + +#include +#include + +typedef struct { +#if BADVPN_THREAD_SAFE + pthread_mutex_t pthread_mutex; +#endif + DebugObject d_obj; +} BMutex; + +static int BMutex_Init (BMutex *o) WARN_UNUSED; +static void BMutex_Free (BMutex *o); +static void BMutex_Lock (BMutex *o); +static void BMutex_Unlock (BMutex *o); + +static int BMutex_Init (BMutex *o) +{ +#if BADVPN_THREAD_SAFE + if (pthread_mutex_init(&o->pthread_mutex, NULL) != 0) { + return 0; + } +#endif + + DebugObject_Init(&o->d_obj); + return 1; +} + +static void BMutex_Free (BMutex *o) +{ + DebugObject_Free(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_destroy(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +static void BMutex_Lock (BMutex *o) +{ + DebugObject_Access(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_lock(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +static void BMutex_Unlock (BMutex *o) +{ + DebugObject_Access(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_unlock(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +#endif diff --git a/service/src/main/jni/badvpn/base/BPending.c b/service/src/main/jni/badvpn/base/BPending.c new file mode 100644 index 0000000..6d05163 --- /dev/null +++ b/service/src/main/jni/badvpn/base/BPending.c @@ -0,0 +1,205 @@ +/** + * @file BPending.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "BPending.h" + +#include "BPending_list.h" +#include + +void BPendingGroup_Init (BPendingGroup *g) +{ + // init jobs list + BPending__List_Init(&g->jobs); + + // init pending counter + DebugCounter_Init(&g->pending_ctr); + + // init debug object + DebugObject_Init(&g->d_obj); +} + +void BPendingGroup_Free (BPendingGroup *g) +{ + DebugCounter_Free(&g->pending_ctr); + ASSERT(BPending__List_IsEmpty(&g->jobs)) + DebugObject_Free(&g->d_obj); +} + +int BPendingGroup_HasJobs (BPendingGroup *g) +{ + DebugObject_Access(&g->d_obj); + + return !BPending__List_IsEmpty(&g->jobs); +} + +void BPendingGroup_ExecuteJob (BPendingGroup *g) +{ + ASSERT(!BPending__List_IsEmpty(&g->jobs)) + DebugObject_Access(&g->d_obj); + + // get a job + BSmallPending *p = BPending__List_First(&g->jobs); + ASSERT(p->pending_node.next != p) + ASSERT(p->pending) + + // remove from jobs list + BPending__List_Remove(&g->jobs, p); + + // set not pending + p->pending_node.next = p; +#ifndef NDEBUG + p->pending = 0; +#endif + + // execute job + p->handler(p->user); + return; +} + +BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g) +{ + DebugObject_Access(&g->d_obj); + + return BPending__List_First(&g->jobs); +} + +void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user) +{ + // init arguments + o->handler = handler; + o->user = user; + + // set not pending + o->pending_node.next = o; +#ifndef NDEBUG + o->pending = 0; +#endif + + // increment pending counter + DebugCounter_Increment(&g->pending_ctr); + + // init debug object + DebugObject_Init(&o->d_obj); +} + +void BSmallPending_Free (BSmallPending *o, BPendingGroup *g) +{ + DebugCounter_Decrement(&g->pending_ctr); + DebugObject_Free(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + // remove from jobs list + if (o->pending_node.next != o) { + BPending__List_Remove(&g->jobs, o); + } +} + +void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + // set handler + o->handler = handler; + o->user = user; +} + +void BSmallPending_Set (BSmallPending *o, BPendingGroup *g) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + // remove from jobs list + if (o->pending_node.next != o) { + BPending__List_Remove(&g->jobs, o); + } + + // insert to jobs list + BPending__List_Prepend(&g->jobs, o); + + // set pending +#ifndef NDEBUG + o->pending = 1; +#endif +} + +void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + if (o->pending_node.next != o) { + // remove from jobs list + BPending__List_Remove(&g->jobs, o); + + // set not pending + o->pending_node.next = o; +#ifndef NDEBUG + o->pending = 0; +#endif + } +} + +int BSmallPending_IsSet (BSmallPending *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == (o->pending_node.next != o)) + + return (o->pending_node.next != o); +} + +void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user) +{ + BSmallPending_Init(&o->base, g, handler, user); + o->g = g; +} + +void BPending_Free (BPending *o) +{ + BSmallPending_Free(&o->base, o->g); +} + +void BPending_Set (BPending *o) +{ + BSmallPending_Set(&o->base, o->g); +} + +void BPending_Unset (BPending *o) +{ + BSmallPending_Unset(&o->base, o->g); +} + +int BPending_IsSet (BPending *o) +{ + return BSmallPending_IsSet(&o->base); +} diff --git a/service/src/main/jni/badvpn/base/BPending.h b/service/src/main/jni/badvpn/base/BPending.h new file mode 100644 index 0000000..07644be --- /dev/null +++ b/service/src/main/jni/badvpn/base/BPending.h @@ -0,0 +1,250 @@ +/** + * @file BPending.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module for managing a queue of jobs pending execution. + */ + +#ifndef BADVPN_BPENDING_H +#define BADVPN_BPENDING_H + +#include + +#include +#include +#include + +struct BSmallPending_s; + +#include "BPending_list.h" +#include + +/** + * Job execution handler. + * It is guaranteed that the associated {@link BSmallPending} object was + * in set state. + * The {@link BSmallPending} object enters not set state before the handler + * is called. + * + * @param user as in {@link BSmallPending_Init} + */ +typedef void (*BSmallPending_handler) (void *user); + +/** + * Job execution handler. + * It is guaranteed that the associated {@link BPending} object was + * in set state. + * The {@link BPending} object enters not set state before the handler + * is called. + * + * @param user as in {@link BPending_Init} + */ +typedef void (*BPending_handler) (void *user); + +/** + * Object that contains a list of jobs pending execution. + */ +typedef struct { + BPending__List jobs; + DebugCounter pending_ctr; + DebugObject d_obj; +} BPendingGroup; + +/** + * Object for queuing a job for execution. + */ +typedef struct BSmallPending_s { + BPending_handler handler; + void *user; + BPending__ListNode pending_node; // optimization: if not pending, .next is this +#ifndef NDEBUG + uint8_t pending; +#endif + DebugObject d_obj; +} BSmallPending; + +/** + * Object for queuing a job for execution. This is a convenience wrapper + * around {@link BSmallPending} with an extra field to remember the + * {@link BPendingGroup} being used. + */ +typedef struct { + BSmallPending base; + BPendingGroup *g; +} BPending; + +/** + * Initializes the object. + * + * @param g the object + */ +void BPendingGroup_Init (BPendingGroup *g); + +/** + * Frees the object. + * There must be no {@link BPending} or {@link BSmallPending} objects using + * this group. + * + * @param g the object + */ +void BPendingGroup_Free (BPendingGroup *g); + +/** + * Checks if there is at least one job in the queue. + * + * @param g the object + * @return 1 if there is at least one job, 0 if not + */ +int BPendingGroup_HasJobs (BPendingGroup *g); + +/** + * Executes the top job on the job list. + * The job is removed from the list and enters + * not set state before being executed. + * There must be at least one job in job list. + * + * @param g the object + */ +void BPendingGroup_ExecuteJob (BPendingGroup *g); + +/** + * Returns the top job on the job list, or NULL if there are none. + * + * @param g the object + * @return the top job if there is at least one job, NULL if not + */ +BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g); + +/** + * Initializes the object. + * The object is initialized in not set state. + * + * @param o the object + * @param g pending group to use + * @param handler job execution handler + * @param user value to pass to handler + */ +void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user); + +/** + * Frees the object. + * The execution handler will not be called after the object + * is freed. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Free (BSmallPending *o, BPendingGroup *g); + +/** + * Changes the job execution handler. + * + * @param o the object + * @param handler job execution handler + * @param user value to pass to handler + */ +void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user); + +/** + * Enables the job, pushing it to the top of the job list. + * If the object was already in set state, the job is removed from its + * current position in the list before being pushed. + * The object enters set state. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Set (BSmallPending *o, BPendingGroup *g); + +/** + * Disables the job, removing it from the job list. + * If the object was not in set state, nothing is done. + * The object enters not set state. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g); + +/** + * Checks if the job is in set state. + * + * @param o the object + * @return 1 if in set state, 0 if not + */ +int BSmallPending_IsSet (BSmallPending *o); + +/** + * Initializes the object. + * The object is initialized in not set state. + * + * @param o the object + * @param g pending group to use + * @param handler job execution handler + * @param user value to pass to handler + */ +void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user); + +/** + * Frees the object. + * The execution handler will not be called after the object + * is freed. + * + * @param o the object + */ +void BPending_Free (BPending *o); + +/** + * Enables the job, pushing it to the top of the job list. + * If the object was already in set state, the job is removed from its + * current position in the list before being pushed. + * The object enters set state. + * + * @param o the object + */ +void BPending_Set (BPending *o); + +/** + * Disables the job, removing it from the job list. + * If the object was not in set state, nothing is done. + * The object enters not set state. + * + * @param o the object + */ +void BPending_Unset (BPending *o); + +/** + * Checks if the job is in set state. + * + * @param o the object + * @return 1 if in set state, 0 if not + */ +int BPending_IsSet (BPending *o); + +#endif diff --git a/service/src/main/jni/badvpn/base/BPending_list.h b/service/src/main/jni/badvpn/base/BPending_list.h new file mode 100644 index 0000000..eadac61 --- /dev/null +++ b/service/src/main/jni/badvpn/base/BPending_list.h @@ -0,0 +1,4 @@ +#define SLINKEDLIST_PARAM_NAME BPending__List +#define SLINKEDLIST_PARAM_FEATURE_LAST 0 +#define SLINKEDLIST_PARAM_TYPE_ENTRY struct BSmallPending_s +#define SLINKEDLIST_PARAM_MEMBER_NODE pending_node diff --git a/service/src/main/jni/badvpn/base/DebugObject.c b/service/src/main/jni/badvpn/base/DebugObject.c new file mode 100644 index 0000000..e694617 --- /dev/null +++ b/service/src/main/jni/badvpn/base/DebugObject.c @@ -0,0 +1,39 @@ +/** + * @file DebugObject.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DebugObject.h" + +#ifndef BADVPN_PLUGIN +#ifndef NDEBUG +DebugCounter debugobject_counter = DEBUGCOUNTER_STATIC; +#if BADVPN_THREAD_SAFE +pthread_mutex_t debugobject_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +#endif +#endif diff --git a/service/src/main/jni/badvpn/base/DebugObject.h b/service/src/main/jni/badvpn/base/DebugObject.h new file mode 100644 index 0000000..b8db287 --- /dev/null +++ b/service/src/main/jni/badvpn/base/DebugObject.h @@ -0,0 +1,147 @@ +/** + * @file DebugObject.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used for detecting leaks. + */ + +#ifndef BADVPN_DEBUGOBJECT_H +#define BADVPN_DEBUGOBJECT_H + +#include + +#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1) +#error BADVPN_THREAD_SAFE is not defined or incorrect +#endif + +#if BADVPN_THREAD_SAFE +#include +#endif + +#include +#include + +#define DEBUGOBJECT_VALID UINT32_C(0x31415926) + +/** + * Object used for detecting leaks. + */ +typedef struct { + #ifndef NDEBUG + uint32_t c; + #endif +} DebugObject; + +/** + * Initializes the object. + * + * @param obj the object + */ +static void DebugObject_Init (DebugObject *obj); + +/** + * Frees the object. + * + * @param obj the object + */ +static void DebugObject_Free (DebugObject *obj); + +/** + * Does nothing. + * + * @param obj the object + */ +static void DebugObject_Access (const DebugObject *obj); + +/** + * Does nothing. + * There must be no {@link DebugObject}'s initialized. + */ +static void DebugObjectGlobal_Finish (void); + +#ifndef NDEBUG +extern DebugCounter debugobject_counter; +#if BADVPN_THREAD_SAFE +extern pthread_mutex_t debugobject_mutex; +#endif +#endif + +void DebugObject_Init (DebugObject *obj) +{ + #ifndef NDEBUG + + obj->c = DEBUGOBJECT_VALID; + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0) + #endif + + DebugCounter_Increment(&debugobject_counter); + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0) + #endif + + #endif +} + +void DebugObject_Free (DebugObject *obj) +{ + ASSERT(obj->c == DEBUGOBJECT_VALID) + + #ifndef NDEBUG + + obj->c = 0; + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0) + #endif + + DebugCounter_Decrement(&debugobject_counter); + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0) + #endif + + #endif +} + +void DebugObject_Access (const DebugObject *obj) +{ + ASSERT(obj->c == DEBUGOBJECT_VALID) +} + +void DebugObjectGlobal_Finish (void) +{ + #ifndef NDEBUG + DebugCounter_Free(&debugobject_counter); + #endif +} + +#endif diff --git a/service/src/main/jni/badvpn/flow/BufferWriter.c b/service/src/main/jni/badvpn/flow/BufferWriter.c new file mode 100644 index 0000000..b0e4129 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/BufferWriter.c @@ -0,0 +1,112 @@ +/** + * @file BufferWriter.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void output_handler_recv (BufferWriter *o, uint8_t *data) +{ + ASSERT(!o->out_have) + + // set output packet + o->out_have = 1; + o->out = data; +} + +void BufferWriter_Init (BufferWriter *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init output + PacketRecvInterface_Init(&o->recv_interface, mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // set no output packet + o->out_have = 0; + + DebugObject_Init(&o->d_obj); + #ifndef NDEBUG + o->d_mtu = mtu; + o->d_writing = 0; + #endif +} + +void BufferWriter_Free (BufferWriter *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->recv_interface); +} + +PacketRecvInterface * BufferWriter_GetOutput (BufferWriter *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->recv_interface; +} + +int BufferWriter_StartPacket (BufferWriter *o, uint8_t **buf) +{ + ASSERT(!o->d_writing) + DebugObject_Access(&o->d_obj); + + if (!o->out_have) { + return 0; + } + + if (buf) { + *buf = o->out; + } + + #ifndef NDEBUG + o->d_writing = 1; + #endif + + return 1; +} + +void BufferWriter_EndPacket (BufferWriter *o, int len) +{ + ASSERT(len >= 0) + ASSERT(len <= o->d_mtu) + ASSERT(o->out_have) + ASSERT(o->d_writing) + DebugObject_Access(&o->d_obj); + + // set no output packet + o->out_have = 0; + + // finish packet + PacketRecvInterface_Done(&o->recv_interface, len); + + #ifndef NDEBUG + o->d_writing = 0; + #endif +} diff --git a/service/src/main/jni/badvpn/flow/BufferWriter.h b/service/src/main/jni/badvpn/flow/BufferWriter.h new file mode 100644 index 0000000..6b6a9c4 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/BufferWriter.h @@ -0,0 +1,107 @@ +/** + * @file BufferWriter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for writing packets to a {@link PacketRecvInterface} client + * in a best-effort fashion. + */ + +#ifndef BADVPN_FLOW_BUFFERWRITER_H +#define BADVPN_FLOW_BUFFERWRITER_H + +#include + +#include +#include +#include + +/** + * Object for writing packets to a {@link PacketRecvInterface} client + * in a best-effort fashion. + */ +typedef struct { + PacketRecvInterface recv_interface; + int out_have; + uint8_t *out; + DebugObject d_obj; + #ifndef NDEBUG + int d_mtu; + int d_writing; + #endif +} BufferWriter; + +/** + * Initializes the object. + * The object is initialized in not writing state. + * + * @param o the object + * @param mtu maximum input packet length + * @param pg pending group + */ +void BufferWriter_Init (BufferWriter *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void BufferWriter_Free (BufferWriter *o); + +/** + * Returns the output interface. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * BufferWriter_GetOutput (BufferWriter *o); + +/** + * Attempts to provide a memory location for writing a packet. + * The object must be in not writing state. + * On success, the object enters writing state. + * + * @param o the object + * @param buf if not NULL, on success, the memory location will be stored here. + * It will have space for MTU bytes. + * @return 1 on success, 0 on failure + */ +int BufferWriter_StartPacket (BufferWriter *o, uint8_t **buf) WARN_UNUSED; + +/** + * Submits a packet written to the buffer. + * The object must be in writing state. + * Yhe object enters not writing state. + * + * @param o the object + * @param len length of the packet that was written. Must be >=0 and + * <=MTU. + */ +void BufferWriter_EndPacket (BufferWriter *o, int len); + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketBuffer.c b/service/src/main/jni/badvpn/flow/PacketBuffer.c new file mode 100644 index 0000000..30afef6 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketBuffer.c @@ -0,0 +1,131 @@ +/** + * @file PacketBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +static void input_handler_done (PacketBuffer *buf, int in_len); +static void output_handler_done (PacketBuffer *buf); + +void input_handler_done (PacketBuffer *buf, int in_len) +{ + ASSERT(in_len >= 0) + ASSERT(in_len <= buf->input_mtu) + DebugObject_Access(&buf->d_obj); + + // remember if buffer is empty + int was_empty = (buf->buf.output_avail < 0); + + // submit packet to buffer + ChunkBuffer2_SubmitPacket(&buf->buf, in_len); + + // if there is space, schedule receive + if (buf->buf.input_avail >= buf->input_mtu) { + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + } + + // if buffer was empty, schedule send + if (was_empty) { + PacketPassInterface_Sender_Send(buf->output, buf->buf.output_dest, buf->buf.output_avail); + } +} + +void output_handler_done (PacketBuffer *buf) +{ + DebugObject_Access(&buf->d_obj); + + // remember if buffer is full + int was_full = (buf->buf.input_avail < buf->input_mtu); + + // remove packet from buffer + ChunkBuffer2_ConsumePacket(&buf->buf); + + // if buffer was full and there is space, schedule receive + if (was_full && buf->buf.input_avail >= buf->input_mtu) { + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + } + + // if there is more data, schedule send + if (buf->buf.output_avail >= 0) { + PacketPassInterface_Sender_Send(buf->output, buf->buf.output_dest, buf->buf.output_avail); + } +} + +int PacketBuffer_Init (PacketBuffer *buf, PacketRecvInterface *input, PacketPassInterface *output, int num_packets, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) >= PacketRecvInterface_GetMTU(input)) + ASSERT(num_packets > 0) + + // init arguments + buf->input = input; + buf->output = output; + + // init input + PacketRecvInterface_Receiver_Init(buf->input, (PacketRecvInterface_handler_done)input_handler_done, buf); + + // set input MTU + buf->input_mtu = PacketRecvInterface_GetMTU(buf->input); + + // init output + PacketPassInterface_Sender_Init(buf->output, (PacketPassInterface_handler_done)output_handler_done, buf); + + // allocate buffer + int num_blocks = ChunkBuffer2_calc_blocks(buf->input_mtu, num_packets); + if (num_blocks < 0) { + goto fail0; + } + if (!(buf->buf_data = (struct ChunkBuffer2_block *)BAllocArray(num_blocks, sizeof(buf->buf_data[0])))) { + goto fail0; + } + + // init buffer + ChunkBuffer2_Init(&buf->buf, buf->buf_data, num_blocks, buf->input_mtu); + + // schedule receive + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + + DebugObject_Init(&buf->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketBuffer_Free (PacketBuffer *buf) +{ + DebugObject_Free(&buf->d_obj); + + // free buffer + BFree(buf->buf_data); +} diff --git a/service/src/main/jni/badvpn/flow/PacketBuffer.h b/service/src/main/jni/badvpn/flow/PacketBuffer.h new file mode 100644 index 0000000..6daab69 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketBuffer.h @@ -0,0 +1,77 @@ +/** + * @file PacketBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output. + */ + +#ifndef BADVPN_FLOW_PACKETBUFFER_H +#define BADVPN_FLOW_PACKETBUFFER_H + +#include + +#include +#include +#include +#include +#include + +/** + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface *input; + int input_mtu; + PacketPassInterface *output; + struct ChunkBuffer2_block *buf_data; + ChunkBuffer2 buf; +} PacketBuffer; + +/** + * Initializes the buffer. + * Output MTU must be >= input MTU. + * + * @param buf the object + * @param input input interface + * @param output output interface + * @param num_packets minimum number of packets the buffer must hold. Must be >0. + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketBuffer_Init (PacketBuffer *buf, PacketRecvInterface *input, PacketPassInterface *output, int num_packets, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the buffer. + * + * @param buf the object + */ +void PacketBuffer_Free (PacketBuffer *buf); + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketPassConnector.c b/service/src/main/jni/badvpn/flow/PacketPassConnector.c new file mode 100644 index 0000000..1a10326 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketPassConnector.c @@ -0,0 +1,125 @@ +/** + * @file PacketPassConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void input_handler_send (PacketPassConnector *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->input_mtu) + ASSERT(o->in_len == -1) + DebugObject_Access(&o->d_obj); + + // remember input packet + o->in_len = data_len; + o->in = data; + + if (o->output) { + // schedule send + PacketPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +static void output_handler_done (PacketPassConnector *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // have no input packet + o->in_len = -1; + + // allow input to send more packets + PacketPassInterface_Done(&o->input); +} + +void PacketPassConnector_Init (PacketPassConnector *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + o->input_mtu = mtu; + + // init input + PacketPassInterface_Init(&o->input, o->input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + + // have no input packet + o->in_len = -1; + + // have no output + o->output = NULL; + + DebugObject_Init(&o->d_obj); +} + +void PacketPassConnector_Free (PacketPassConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassConnector_GetInput (PacketPassConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassConnector_ConnectOutput (PacketPassConnector *o, PacketPassInterface *output) +{ + ASSERT(!o->output) + ASSERT(PacketPassInterface_GetMTU(output) >= o->input_mtu) + DebugObject_Access(&o->d_obj); + + // set output + o->output = output; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // if we have an input packet, schedule send + if (o->in_len >= 0) { + PacketPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +void PacketPassConnector_DisconnectOutput (PacketPassConnector *o) +{ + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // set no output + o->output = NULL; +} diff --git a/service/src/main/jni/badvpn/flow/PacketPassConnector.h b/service/src/main/jni/badvpn/flow/PacketPassConnector.h new file mode 100644 index 0000000..1a2cc6c --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketPassConnector.h @@ -0,0 +1,102 @@ +/** + * @file PacketPassConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_PACKETPASSCONNECTOR_H +#define BADVPN_FLOW_PACKETPASSCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link PacketPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ +typedef struct { + PacketPassInterface input; + int input_mtu; + int in_len; + uint8_t *in; + PacketPassInterface *output; + DebugObject d_obj; +} PacketPassConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param mtu maximum input packet size. Must be >=0. + * @param pg pending group + */ +void PacketPassConnector_Init (PacketPassConnector *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassConnector_Free (PacketPassConnector *o); + +/** + * Returns the input interface. + * The MTU of the interface will be as in {@link PacketPassConnector_Init}. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassConnector_GetInput (PacketPassConnector *o); + +/** + * Connects output. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output output to connect. Its MTU must be >= MTU specified in + * {@link PacketPassConnector_Init}. + */ +void PacketPassConnector_ConnectOutput (PacketPassConnector *o, PacketPassInterface *output); + +/** + * Disconnects output. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void PacketPassConnector_DisconnectOutput (PacketPassConnector *o); + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketPassFairQueue.c b/service/src/main/jni/badvpn/flow/PacketPassFairQueue.c new file mode 100644 index 0000000..bbfafe0 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketPassFairQueue.c @@ -0,0 +1,405 @@ +/** + * @file PacketPassFairQueue.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +#include + +static int compare_flows (PacketPassFairQueueFlow *f1, PacketPassFairQueueFlow *f2) +{ + int cmp = B_COMPARE(f1->time, f2->time); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)f1, (uintptr_t)f2); +} + +#include "PacketPassFairQueue_tree.h" +#include + +static uint64_t get_current_time (PacketPassFairQueue *m) +{ + if (m->sending_flow) { + return m->sending_flow->time; + } + + uint64_t time = 0; // to remove warning + int have = 0; + + PacketPassFairQueueFlow *first_flow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + if (first_flow) { + ASSERT(first_flow->is_queued) + + time = first_flow->time; + have = 1; + } + + if (m->previous_flow) { + if (!have || m->previous_flow->time < time) { + time = m->previous_flow->time; + have = 1; + } + } + + return (have ? time : 0); +} + +static void increment_sent_flow (PacketPassFairQueueFlow *flow, uint64_t amount) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(amount <= FAIRQUEUE_MAX_TIME) + ASSERT(!flow->is_queued) + ASSERT(!m->sending_flow) + + // does time overflow? + if (amount > FAIRQUEUE_MAX_TIME - flow->time) { + // get time to subtract + uint64_t subtract; + PacketPassFairQueueFlow *first_flow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + if (!first_flow) { + subtract = flow->time; + } else { + ASSERT(first_flow->is_queued) + subtract = first_flow->time; + } + + // subtract time from all flows + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&m->flows_list); list_node; list_node = LinkedList1Node_Next(list_node)) { + PacketPassFairQueueFlow *someflow = UPPER_OBJECT(list_node, PacketPassFairQueueFlow, list_node); + + // don't subtract more time than there is, except for the just finished flow, + // where we allow time to underflow and then overflow to the correct value after adding to it + if (subtract > someflow->time && someflow != flow) { + ASSERT(!someflow->is_queued) + someflow->time = 0; + } else { + someflow->time -= subtract; + } + } + } + + // add time to flow + flow->time += amount; +} + +static void schedule (PacketPassFairQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->previous_flow) + ASSERT(!m->freeing) + ASSERT(!PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) + + // get first queued flow + PacketPassFairQueueFlow *qflow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + ASSERT(qflow->is_queued) + + // remove flow from queue + PacketPassFairQueue__Tree_Remove(&m->queued_tree, 0, qflow); + qflow->is_queued = 0; + + // schedule send + PacketPassInterface_Sender_Send(m->output, qflow->queued.data, qflow->queued.data_len); + m->sending_flow = qflow; + m->sending_len = qflow->queued.data_len; +} + +static void schedule_job_handler (PacketPassFairQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // remove previous flow + m->previous_flow = NULL; + + if (!PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) { + schedule(m); + } +} + +static void input_handler_send (PacketPassFairQueueFlow *flow, uint8_t *data, int data_len) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(flow != m->sending_flow) + ASSERT(!flow->is_queued) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + if (flow == m->previous_flow) { + // remove from previous flow + m->previous_flow = NULL; + } else { + // raise time + flow->time = bmax_uint64(flow->time, get_current_time(m)); + } + + // queue flow + flow->queued.data = data; + flow->queued.data_len = data_len; + int res = PacketPassFairQueue__Tree_Insert(&m->queued_tree, 0, flow, NULL); + ASSERT_EXECUTE(res) + flow->is_queued = 1; + + if (!m->sending_flow && !BPending_IsSet(&m->schedule_job)) { + schedule(m); + } +} + +static void output_handler_done (PacketPassFairQueue *m) +{ + ASSERT(m->sending_flow) + ASSERT(!m->previous_flow) + ASSERT(!BPending_IsSet(&m->schedule_job)) + ASSERT(!m->freeing) + ASSERT(!m->sending_flow->is_queued) + + PacketPassFairQueueFlow *flow = m->sending_flow; + + // sending finished + m->sending_flow = NULL; + + // remember this flow so the schedule job can remove its time if it didn's send + m->previous_flow = flow; + + // update flow time by packet size + increment_sent_flow(flow, (uint64_t)m->packet_weight + m->sending_len); + + // schedule schedule + BPending_Set(&m->schedule_job); + + // finish flow packet + PacketPassInterface_Done(&flow->input); + + // call busy handler if set + if (flow->handler_busy) { + // handler is one-shot, unset it before calling + PacketPassFairQueue_handler_busy handler = flow->handler_busy; + flow->handler_busy = NULL; + + // call handler + handler(flow->user); + return; + } +} + +int PacketPassFairQueue_Init (PacketPassFairQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel, int packet_weight) +{ + ASSERT(packet_weight > 0) + ASSERT(use_cancel == 0 || use_cancel == 1) + ASSERT(!use_cancel || PacketPassInterface_HasCancel(output)) + + // init arguments + m->output = output; + m->pg = pg; + m->use_cancel = use_cancel; + m->packet_weight = packet_weight; + + // make sure that (output MTU + packet_weight <= FAIRQUEUE_MAX_TIME) + if (!( + (PacketPassInterface_GetMTU(output) <= FAIRQUEUE_MAX_TIME) && + (packet_weight <= FAIRQUEUE_MAX_TIME - PacketPassInterface_GetMTU(output)) + )) { + goto fail0; + } + + // init output + PacketPassInterface_Sender_Init(m->output, (PacketPassInterface_handler_done)output_handler_done, m); + + // not sending + m->sending_flow = NULL; + + // no previous flow + m->previous_flow = NULL; + + // init queued tree + PacketPassFairQueue__Tree_Init(&m->queued_tree); + + // init flows list + LinkedList1_Init(&m->flows_list); + + // not freeing + m->freeing = 0; + + // init schedule job + BPending_Init(&m->schedule_job, m->pg, (BPending_handler)schedule_job_handler, m); + + DebugObject_Init(&m->d_obj); + DebugCounter_Init(&m->d_ctr); + return 1; + +fail0: + return 0; +} + +void PacketPassFairQueue_Free (PacketPassFairQueue *m) +{ + ASSERT(LinkedList1_IsEmpty(&m->flows_list)) + ASSERT(PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) + ASSERT(!m->previous_flow) + ASSERT(!m->sending_flow) + DebugCounter_Free(&m->d_ctr); + DebugObject_Free(&m->d_obj); + + // free schedule job + BPending_Free(&m->schedule_job); +} + +void PacketPassFairQueue_PrepareFree (PacketPassFairQueue *m) +{ + DebugObject_Access(&m->d_obj); + + // set freeing + m->freeing = 1; +} + +int PacketPassFairQueue_GetMTU (PacketPassFairQueue *m) +{ + DebugObject_Access(&m->d_obj); + + return PacketPassInterface_GetMTU(m->output); +} + +void PacketPassFairQueueFlow_Init (PacketPassFairQueueFlow *flow, PacketPassFairQueue *m) +{ + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // init arguments + flow->m = m; + + // have no canfree handler + flow->handler_busy = NULL; + + // init input + PacketPassInterface_Init(&flow->input, PacketPassInterface_GetMTU(flow->m->output), (PacketPassInterface_handler_send)input_handler_send, flow, m->pg); + + // set time + flow->time = 0; + + // add to flows list + LinkedList1_Append(&m->flows_list, &flow->list_node); + + // is not queued + flow->is_queued = 0; + + DebugObject_Init(&flow->d_obj); + DebugCounter_Increment(&m->d_ctr); +} + +void PacketPassFairQueueFlow_Free (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(m->freeing || flow != m->sending_flow) + DebugCounter_Decrement(&m->d_ctr); + DebugObject_Free(&flow->d_obj); + + // remove from current flow + if (flow == m->sending_flow) { + m->sending_flow = NULL; + } + + // remove from previous flow + if (flow == m->previous_flow) { + m->previous_flow = NULL; + } + + // remove from queue + if (flow->is_queued) { + PacketPassFairQueue__Tree_Remove(&m->queued_tree, 0, flow); + } + + // remove from flows list + LinkedList1_Remove(&m->flows_list, &flow->list_node); + + // free input + PacketPassInterface_Free(&flow->input); +} + +void PacketPassFairQueueFlow_AssertFree (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + B_USE(m) + + ASSERT(m->freeing || flow != m->sending_flow) + DebugObject_Access(&flow->d_obj); +} + +int PacketPassFairQueueFlow_IsBusy (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + return (flow == m->sending_flow); +} + +void PacketPassFairQueueFlow_RequestCancel (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(flow == m->sending_flow) + ASSERT(m->use_cancel) + ASSERT(!m->freeing) + ASSERT(!BPending_IsSet(&m->schedule_job)) + DebugObject_Access(&flow->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(m->output); +} + +void PacketPassFairQueueFlow_SetBusyHandler (PacketPassFairQueueFlow *flow, PacketPassFairQueue_handler_busy handler, void *user) +{ + PacketPassFairQueue *m = flow->m; + B_USE(m) + + ASSERT(flow == m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + // set handler + flow->handler_busy = handler; + flow->user = user; +} + +PacketPassInterface * PacketPassFairQueueFlow_GetInput (PacketPassFairQueueFlow *flow) +{ + DebugObject_Access(&flow->d_obj); + + return &flow->input; +} diff --git a/service/src/main/jni/badvpn/flow/PacketPassFairQueue.h b/service/src/main/jni/badvpn/flow/PacketPassFairQueue.h new file mode 100644 index 0000000..f846596 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketPassFairQueue.h @@ -0,0 +1,204 @@ +/** + * @file PacketPassFairQueue.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Fair queue using {@link PacketPassInterface}. + */ + +#ifndef BADVPN_FLOW_PACKETPASSFAIRQUEUE_H +#define BADVPN_FLOW_PACKETPASSFAIRQUEUE_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +// reduce this to test time overflow handling +#define FAIRQUEUE_MAX_TIME UINT64_MAX + +typedef void (*PacketPassFairQueue_handler_busy) (void *user); + +struct PacketPassFairQueueFlow_s; + +#include "PacketPassFairQueue_tree.h" +#include + +typedef struct PacketPassFairQueueFlow_s { + struct PacketPassFairQueue_s *m; + PacketPassFairQueue_handler_busy handler_busy; + void *user; + PacketPassInterface input; + uint64_t time; + LinkedList1Node list_node; + int is_queued; + struct { + PacketPassFairQueue__TreeNode tree_node; + uint8_t *data; + int data_len; + } queued; + DebugObject d_obj; +} PacketPassFairQueueFlow; + +/** + * Fair queue using {@link PacketPassInterface}. + */ +typedef struct PacketPassFairQueue_s { + PacketPassInterface *output; + BPendingGroup *pg; + int use_cancel; + int packet_weight; + struct PacketPassFairQueueFlow_s *sending_flow; + int sending_len; + struct PacketPassFairQueueFlow_s *previous_flow; + PacketPassFairQueue__Tree queued_tree; + LinkedList1 flows_list; + int freeing; + BPending schedule_job; + DebugObject d_obj; + DebugCounter d_ctr; +} PacketPassFairQueue; + +/** + * Initializes the queue. + * + * @param m the object + * @param output output interface + * @param pg pending group + * @param use_cancel whether cancel functionality is required. Must be 0 or 1. + * If 1, output must support cancel functionality. + * @param packet_weight additional weight a packet bears. Must be >0, to keep + * the queue fair for zero size packets. + * @return 1 on success, 0 on failure (because output MTU is too large) + */ +int PacketPassFairQueue_Init (PacketPassFairQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel, int packet_weight) WARN_UNUSED; + +/** + * Frees the queue. + * All flows must have been freed. + * + * @param m the object + */ +void PacketPassFairQueue_Free (PacketPassFairQueue *m); + +/** + * Prepares for freeing the entire queue. Must be called to allow freeing + * the flows in the process of freeing the entire queue. + * After this function is called, flows and the queue must be freed + * before any further I/O. + * May be called multiple times. + * The queue enters freeing state. + * + * @param m the object + */ +void PacketPassFairQueue_PrepareFree (PacketPassFairQueue *m); + +/** + * Returns the MTU of the queue. + * + * @param m the object + */ +int PacketPassFairQueue_GetMTU (PacketPassFairQueue *m); + +/** + * Initializes a queue flow. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param m queue to attach to + */ +void PacketPassFairQueueFlow_Init (PacketPassFairQueueFlow *flow, PacketPassFairQueue *m); + +/** + * Frees a queue flow. + * Unless the queue is in freeing state: + * - The flow must not be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * - Must not be called from queue calls to output. + * + * @param flow the object + */ +void PacketPassFairQueueFlow_Free (PacketPassFairQueueFlow *flow); + +/** + * Does nothing. + * It must be possible to free the flow (see {@link PacketPassFairQueueFlow_Free}). + * + * @param flow the object + */ +void PacketPassFairQueueFlow_AssertFree (PacketPassFairQueueFlow *flow); + +/** + * Determines if the flow is busy. If the flow is considered busy, it must not + * be freed. At any given time, at most one flow will be indicated as busy. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @return 0 if not busy, 1 is busy + */ +int PacketPassFairQueueFlow_IsBusy (PacketPassFairQueueFlow *flow); + +/** + * Requests the output to stop processing the current packet as soon as possible. + * Cancel functionality must be enabled for the queue. + * The flow must be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * + * @param flow the object + */ +void PacketPassFairQueueFlow_RequestCancel (PacketPassFairQueueFlow *flow); + +/** + * Sets up a callback to be called when the flow is no longer busy. + * The handler will be called as soon as the flow is no longer busy, i.e. it is not + * possible that this flow is no longer busy before the handler is called. + * The flow must be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param handler callback function. NULL to disable. + * @param user value passed to callback function. Ignored if handler is NULL. + */ +void PacketPassFairQueueFlow_SetBusyHandler (PacketPassFairQueueFlow *flow, PacketPassFairQueue_handler_busy handler, void *user); + +/** + * Returns the input interface of the flow. + * + * @param flow the object + * @return input interface + */ +PacketPassInterface * PacketPassFairQueueFlow_GetInput (PacketPassFairQueueFlow *flow); + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketPassFairQueue_tree.h b/service/src/main/jni/badvpn/flow/PacketPassFairQueue_tree.h new file mode 100644 index 0000000..5dd0a7d --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketPassFairQueue_tree.h @@ -0,0 +1,7 @@ +#define SAVL_PARAM_NAME PacketPassFairQueue__Tree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 1 +#define SAVL_PARAM_TYPE_ENTRY struct PacketPassFairQueueFlow_s +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_flows((entry1), (entry2)) +#define SAVL_PARAM_MEMBER_NODE queued.tree_node diff --git a/service/src/main/jni/badvpn/flow/PacketPassInterface.c b/service/src/main/jni/badvpn/flow/PacketPassInterface.c new file mode 100644 index 0000000..dfa6ed5 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketPassInterface.c @@ -0,0 +1,68 @@ +/** + * @file PacketPassInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _PacketPassInterface_job_operation (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PPI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _PacketPassInterface_job_requestcancel (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_BUSY) + ASSERT(i->cancel_requested) + ASSERT(i->handler_requestcancel) + DebugObject_Access(&i->d_obj); + + // call handler + i->handler_requestcancel(i->user_provider); + return; +} + +void _PacketPassInterface_job_done (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PPI_STATE_NONE; + + // call handler + i->handler_done(i->user_user); + return; +} diff --git a/service/src/main/jni/badvpn/flow/PacketPassInterface.h b/service/src/main/jni/badvpn/flow/PacketPassInterface.h new file mode 100644 index 0000000..0cc2274 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketPassInterface.h @@ -0,0 +1,236 @@ +/** + * @file PacketPassInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a packet sender to pass data packets to a packet receiver. + */ + +#ifndef BADVPN_FLOW_PACKETPASSINTERFACE_H +#define BADVPN_FLOW_PACKETPASSINTERFACE_H + +#include +#include + +#include +#include +#include + +#define PPI_STATE_NONE 1 +#define PPI_STATE_OPERATION_PENDING 2 +#define PPI_STATE_BUSY 3 +#define PPI_STATE_DONE_PENDING 4 + +typedef void (*PacketPassInterface_handler_send) (void *user, uint8_t *data, int data_len); + +typedef void (*PacketPassInterface_handler_requestcancel) (void *user); + +typedef void (*PacketPassInterface_handler_done) (void *user); + +typedef struct { + // provider data + int mtu; + PacketPassInterface_handler_send handler_operation; + PacketPassInterface_handler_requestcancel handler_requestcancel; + void *user_provider; + + // user data + PacketPassInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // requestcancel job + BPending job_requestcancel; + + // done job + BPending job_done; + + // state + int state; + int cancel_requested; + + DebugObject d_obj; +} PacketPassInterface; + +static void PacketPassInterface_Init (PacketPassInterface *i, int mtu, PacketPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg); + +static void PacketPassInterface_Free (PacketPassInterface *i); + +static void PacketPassInterface_EnableCancel (PacketPassInterface *i, PacketPassInterface_handler_requestcancel handler_requestcancel); + +static void PacketPassInterface_Done (PacketPassInterface *i); + +static int PacketPassInterface_GetMTU (PacketPassInterface *i); + +static void PacketPassInterface_Sender_Init (PacketPassInterface *i, PacketPassInterface_handler_done handler_done, void *user); + +static void PacketPassInterface_Sender_Send (PacketPassInterface *i, uint8_t *data, int data_len); + +static void PacketPassInterface_Sender_RequestCancel (PacketPassInterface *i); + +static int PacketPassInterface_HasCancel (PacketPassInterface *i); + +void _PacketPassInterface_job_operation (PacketPassInterface *i); +void _PacketPassInterface_job_requestcancel (PacketPassInterface *i); +void _PacketPassInterface_job_done (PacketPassInterface *i); + +void PacketPassInterface_Init (PacketPassInterface *i, int mtu, PacketPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + i->mtu = mtu; + i->handler_operation = handler_operation; + i->handler_requestcancel = NULL; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_PacketPassInterface_job_operation, i); + BPending_Init(&i->job_requestcancel, pg, (BPending_handler)_PacketPassInterface_job_requestcancel, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_PacketPassInterface_job_done, i); + + // set state + i->state = PPI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void PacketPassInterface_Free (PacketPassInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_requestcancel); + BPending_Free(&i->job_operation); +} + +void PacketPassInterface_EnableCancel (PacketPassInterface *i, PacketPassInterface_handler_requestcancel handler_requestcancel) +{ + ASSERT(!i->handler_requestcancel) + ASSERT(!i->handler_done) + ASSERT(handler_requestcancel) + + i->handler_requestcancel = handler_requestcancel; +} + +void PacketPassInterface_Done (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_BUSY) + DebugObject_Access(&i->d_obj); + + // unset requestcancel job + BPending_Unset(&i->job_requestcancel); + + // schedule done + BPending_Set(&i->job_done); + + // set state + i->state = PPI_STATE_DONE_PENDING; +} + +int PacketPassInterface_GetMTU (PacketPassInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return i->mtu; +} + +void PacketPassInterface_Sender_Init (PacketPassInterface *i, PacketPassInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void PacketPassInterface_Sender_Send (PacketPassInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= i->mtu) + ASSERT(!(data_len > 0) || data) + ASSERT(i->state == PPI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = PPI_STATE_OPERATION_PENDING; + i->cancel_requested = 0; +} + +void PacketPassInterface_Sender_RequestCancel (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_OPERATION_PENDING || i->state == PPI_STATE_BUSY || i->state == PPI_STATE_DONE_PENDING) + ASSERT(i->handler_requestcancel) + DebugObject_Access(&i->d_obj); + + // ignore multiple cancel requests + if (i->cancel_requested) { + return; + } + + // remember we requested cancel + i->cancel_requested = 1; + + if (i->state == PPI_STATE_OPERATION_PENDING) { + // unset operation job + BPending_Unset(&i->job_operation); + + // set done job + BPending_Set(&i->job_done); + + // set state + i->state = PPI_STATE_DONE_PENDING; + } else if (i->state == PPI_STATE_BUSY) { + // set requestcancel job + BPending_Set(&i->job_requestcancel); + } +} + +int PacketPassInterface_HasCancel (PacketPassInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return !!i->handler_requestcancel; +} + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketProtoDecoder.c b/service/src/main/jni/badvpn/flow/PacketProtoDecoder.c new file mode 100644 index 0000000..68d26c5 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketProtoDecoder.c @@ -0,0 +1,182 @@ +/** + * @file PacketProtoDecoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static void process_data (PacketProtoDecoder *enc); +static void input_handler_done (PacketProtoDecoder *enc, int data_len); +static void output_handler_done (PacketProtoDecoder *enc); + +void process_data (PacketProtoDecoder *enc) +{ + int was_error = 0; + + do { + uint8_t *data = enc->buf + enc->buf_start; + int left = enc->buf_used; + + // check if header was received + if (left < sizeof(struct packetproto_header)) { + break; + } + struct packetproto_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(struct packetproto_header); + left -= sizeof(struct packetproto_header); + int data_len = ltoh16(header.len); + + // check data length + if (data_len > enc->output_mtu) { + BLog(BLOG_NOTICE, "error: packet too large"); + was_error = 1; + break; + } + + // check if whole packet was received + if (left < data_len) { + break; + } + + // update buffer + enc->buf_start += sizeof(struct packetproto_header) + data_len; + enc->buf_used -= sizeof(struct packetproto_header) + data_len; + + // submit packet + PacketPassInterface_Sender_Send(enc->output, data, data_len); + return; + } while (0); + + if (was_error) { + // reset buffer + enc->buf_start = 0; + enc->buf_used = 0; + } else { + // if we reached the end of the buffer, wrap around to allow more data to be received + if (enc->buf_start + enc->buf_used == enc->buf_size) { + memmove(enc->buf, enc->buf + enc->buf_start, enc->buf_used); + enc->buf_start = 0; + } + } + + // receive data + StreamRecvInterface_Receiver_Recv(enc->input, enc->buf + (enc->buf_start + enc->buf_used), enc->buf_size - (enc->buf_start + enc->buf_used)); + + // if we had error, report it + if (was_error) { + enc->handler_error(enc->user); + return; + } +} + +static void input_handler_done (PacketProtoDecoder *enc, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= enc->buf_size - (enc->buf_start + enc->buf_used)) + DebugObject_Access(&enc->d_obj); + + // update buffer + enc->buf_used += data_len; + + // process data + process_data(enc); + return; +} + +void output_handler_done (PacketProtoDecoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + // process data + process_data(enc); + return; +} + +int PacketProtoDecoder_Init (PacketProtoDecoder *enc, StreamRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg, void *user, PacketProtoDecoder_handler_error handler_error) +{ + // init arguments + enc->input = input; + enc->output = output; + enc->user = user; + enc->handler_error = handler_error; + + // init input + StreamRecvInterface_Receiver_Init(enc->input, (StreamRecvInterface_handler_done)input_handler_done, enc); + + // init output + PacketPassInterface_Sender_Init(enc->output, (PacketPassInterface_handler_done)output_handler_done, enc); + + // set output MTU, limit by maximum payload size + enc->output_mtu = bmin_int(PacketPassInterface_GetMTU(enc->output), PACKETPROTO_MAXPAYLOAD); + + // init buffer state + enc->buf_size = PACKETPROTO_ENCLEN(enc->output_mtu); + enc->buf_start = 0; + enc->buf_used = 0; + + // allocate buffer + if (!(enc->buf = (uint8_t *)malloc(enc->buf_size))) { + goto fail0; + } + + // start receiving + StreamRecvInterface_Receiver_Recv(enc->input, enc->buf, enc->buf_size); + + DebugObject_Init(&enc->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketProtoDecoder_Free (PacketProtoDecoder *enc) +{ + DebugObject_Free(&enc->d_obj); + + // free buffer + free(enc->buf); +} + +void PacketProtoDecoder_Reset (PacketProtoDecoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + enc->buf_start += enc->buf_used; + enc->buf_used = 0; +} diff --git a/service/src/main/jni/badvpn/flow/PacketProtoDecoder.h b/service/src/main/jni/badvpn/flow/PacketProtoDecoder.h new file mode 100644 index 0000000..2c20694 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketProtoDecoder.h @@ -0,0 +1,96 @@ +/** + * @file PacketProtoDecoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which decodes a stream according to PacketProto. + */ + +#ifndef BADVPN_FLOW_PACKETPROTODECODER_H +#define BADVPN_FLOW_PACKETPROTODECODER_H + +#include + +#include +#include +#include +#include +#include + +/** + * Handler called when a protocol error occurs. + * When an error occurs, the decoder is reset to the initial state. + * + * @param user as in {@link PacketProtoDecoder_Init} + */ +typedef void (*PacketProtoDecoder_handler_error) (void *user); + +typedef struct { + StreamRecvInterface *input; + PacketPassInterface *output; + void *user; + PacketProtoDecoder_handler_error handler_error; + int output_mtu; + int buf_size; + int buf_start; + int buf_used; + uint8_t *buf; + DebugObject d_obj; +} PacketProtoDecoder; + +/** + * Initializes the object. + * + * @param enc the object + * @param input input interface. The decoder will accept packets with payload size up to its MTU + * (but the payload can never be more than PACKETPROTO_MAXPAYLOAD). + * @param output output interface + * @param pg pending group + * @param user argument to handlers + * @param handler_error error handler + * @return 1 on success, 0 on failure + */ +int PacketProtoDecoder_Init (PacketProtoDecoder *enc, StreamRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg, void *user, PacketProtoDecoder_handler_error handler_error) WARN_UNUSED; + +/** + * Frees the object. + * + * @param enc the object + */ +void PacketProtoDecoder_Free (PacketProtoDecoder *enc); + +/** + * Clears the internal buffer. + * The next data received from the input will be treated as a new + * PacketProto stream. + * + * @param enc the object + */ +void PacketProtoDecoder_Reset (PacketProtoDecoder *enc); + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketProtoEncoder.c b/service/src/main/jni/badvpn/flow/PacketProtoEncoder.c new file mode 100644 index 0000000..00aaa95 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketProtoEncoder.c @@ -0,0 +1,101 @@ +/** + * @file PacketProtoEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +static void output_handler_recv (PacketProtoEncoder *enc, uint8_t *data) +{ + ASSERT(!enc->output_packet) + ASSERT(data) + DebugObject_Access(&enc->d_obj); + + // schedule receive + enc->output_packet = data; + PacketRecvInterface_Receiver_Recv(enc->input, enc->output_packet + sizeof(struct packetproto_header)); +} + +static void input_handler_done (PacketProtoEncoder *enc, int in_len) +{ + ASSERT(enc->output_packet) + DebugObject_Access(&enc->d_obj); + + // write length + struct packetproto_header pp; + pp.len = htol16(in_len); + memcpy(enc->output_packet, &pp, sizeof(pp)); + + // finish output packet + enc->output_packet = NULL; + PacketRecvInterface_Done(&enc->output, PACKETPROTO_ENCLEN(in_len)); +} + +void PacketProtoEncoder_Init (PacketProtoEncoder *enc, PacketRecvInterface *input, BPendingGroup *pg) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= PACKETPROTO_MAXPAYLOAD) + + // init arguments + enc->input = input; + + // init input + PacketRecvInterface_Receiver_Init(enc->input, (PacketRecvInterface_handler_done)input_handler_done, enc); + + // init output + PacketRecvInterface_Init( + &enc->output, PACKETPROTO_ENCLEN(PacketRecvInterface_GetMTU(enc->input)), + (PacketRecvInterface_handler_recv)output_handler_recv, enc, pg + ); + + // set no output packet + enc->output_packet = NULL; + + DebugObject_Init(&enc->d_obj); +} + +void PacketProtoEncoder_Free (PacketProtoEncoder *enc) +{ + DebugObject_Free(&enc->d_obj); + + // free input + PacketRecvInterface_Free(&enc->output); +} + +PacketRecvInterface * PacketProtoEncoder_GetOutput (PacketProtoEncoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + return &enc->output; +} diff --git a/service/src/main/jni/badvpn/flow/PacketProtoEncoder.h b/service/src/main/jni/badvpn/flow/PacketProtoEncoder.h new file mode 100644 index 0000000..022aa00 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketProtoEncoder.h @@ -0,0 +1,80 @@ +/** + * @file PacketProtoEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which encodes packets according to PacketProto. + */ + +#ifndef BADVPN_FLOW_PACKETPROTOENCODER_H +#define BADVPN_FLOW_PACKETPROTOENCODER_H + +#include + +#include +#include + +/** + * Object which encodes packets according to PacketProto. + * + * Input is with {@link PacketRecvInterface}. + * Output is with {@link PacketRecvInterface}. + */ +typedef struct { + PacketRecvInterface *input; + PacketRecvInterface output; + uint8_t *output_packet; + DebugObject d_obj; +} PacketProtoEncoder; + +/** + * Initializes the object. + * + * @param enc the object + * @param input input interface. Its MTU must be <=PACKETPROTO_MAXPAYLOAD. + * @param pg pending group + */ +void PacketProtoEncoder_Init (PacketProtoEncoder *enc, PacketRecvInterface *input, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param enc the object + */ +void PacketProtoEncoder_Free (PacketProtoEncoder *enc); + +/** + * Returns the output interface. + * The MTU of the output interface is PACKETPROTO_ENCLEN(MTU of input interface). + * + * @param enc the object + * @return output interface + */ +PacketRecvInterface * PacketProtoEncoder_GetOutput (PacketProtoEncoder *enc); + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketProtoFlow.c b/service/src/main/jni/badvpn/flow/PacketProtoFlow.c new file mode 100644 index 0000000..8fad8e2 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketProtoFlow.c @@ -0,0 +1,82 @@ +/** + * @file PacketProtoFlow.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +int PacketProtoFlow_Init (PacketProtoFlow *o, int input_mtu, int num_packets, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(input_mtu >= 0) + ASSERT(input_mtu <= PACKETPROTO_MAXPAYLOAD) + ASSERT(num_packets > 0) + ASSERT(PacketPassInterface_GetMTU(output) >= PACKETPROTO_ENCLEN(input_mtu)) + + // init async input + BufferWriter_Init(&o->ainput, input_mtu, pg); + + // init encoder + PacketProtoEncoder_Init(&o->encoder, BufferWriter_GetOutput(&o->ainput), pg); + + // init buffer + if (!PacketBuffer_Init(&o->buffer, PacketProtoEncoder_GetOutput(&o->encoder), output, num_packets, pg)) { + goto fail0; + } + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + PacketProtoEncoder_Free(&o->encoder); + BufferWriter_Free(&o->ainput); + return 0; +} + +void PacketProtoFlow_Free (PacketProtoFlow *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + PacketBuffer_Free(&o->buffer); + + // free encoder + PacketProtoEncoder_Free(&o->encoder); + + // free async input + BufferWriter_Free(&o->ainput); +} + +BufferWriter * PacketProtoFlow_GetInput (PacketProtoFlow *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->ainput; +} diff --git a/service/src/main/jni/badvpn/flow/PacketProtoFlow.h b/service/src/main/jni/badvpn/flow/PacketProtoFlow.h new file mode 100644 index 0000000..05e1233 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketProtoFlow.h @@ -0,0 +1,83 @@ +/** + * @file PacketProtoFlow.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Buffer which encodes packets with PacketProto, with {@link BufferWriter} + * input and {@link PacketPassInterface} output. + */ + +#ifndef BADVPN_FLOW_PACKETPROTOFLOW_H +#define BADVPN_FLOW_PACKETPROTOFLOW_H + +#include + +#include +#include +#include +#include + +/** + * Buffer which encodes packets with PacketProto, with {@link BufferWriter} + * input and {@link PacketPassInterface} output. + */ +typedef struct { + BufferWriter ainput; + PacketProtoEncoder encoder; + PacketBuffer buffer; + DebugObject d_obj; +} PacketProtoFlow; + +/** + * Initializes the object. + * + * @param o the object + * @param input_mtu maximum input packet size. Must be >=0 and <=PACKETPROTO_MAXPAYLOAD. + * @param num_packets minimum number of packets the buffer should hold. Must be >0. + * @param output output interface. Its MTU must be >=PACKETPROTO_ENCLEN(input_mtu). + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketProtoFlow_Init (PacketProtoFlow *o, int input_mtu, int num_packets, PacketPassInterface *output, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void PacketProtoFlow_Free (PacketProtoFlow *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +BufferWriter * PacketProtoFlow_GetInput (PacketProtoFlow *o); + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketRecvInterface.c b/service/src/main/jni/badvpn/flow/PacketRecvInterface.c new file mode 100644 index 0000000..40bb8c6 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketRecvInterface.c @@ -0,0 +1,56 @@ +/** + * @file PacketRecvInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _PacketRecvInterface_job_operation (PacketRecvInterface *i) +{ + ASSERT(i->state == PRI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PRI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data); + return; +} + +void _PacketRecvInterface_job_done (PacketRecvInterface *i) +{ + ASSERT(i->state == PRI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PRI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/service/src/main/jni/badvpn/flow/PacketRecvInterface.h b/service/src/main/jni/badvpn/flow/PacketRecvInterface.h new file mode 100644 index 0000000..6350ed2 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketRecvInterface.h @@ -0,0 +1,170 @@ +/** + * @file PacketRecvInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a packet receiver to receive data packets from a packet sender. + */ + +#ifndef BADVPN_FLOW_PACKETRECVINTERFACE_H +#define BADVPN_FLOW_PACKETRECVINTERFACE_H + +#include +#include + +#include +#include +#include + +#define PRI_STATE_NONE 1 +#define PRI_STATE_OPERATION_PENDING 2 +#define PRI_STATE_BUSY 3 +#define PRI_STATE_DONE_PENDING 4 + +typedef void (*PacketRecvInterface_handler_recv) (void *user, uint8_t *data); + +typedef void (*PacketRecvInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + int mtu; + PacketRecvInterface_handler_recv handler_operation; + void *user_provider; + + // user data + PacketRecvInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} PacketRecvInterface; + +static void PacketRecvInterface_Init (PacketRecvInterface *i, int mtu, PacketRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg); + +static void PacketRecvInterface_Free (PacketRecvInterface *i); + +static void PacketRecvInterface_Done (PacketRecvInterface *i, int data_len); + +static int PacketRecvInterface_GetMTU (PacketRecvInterface *i); + +static void PacketRecvInterface_Receiver_Init (PacketRecvInterface *i, PacketRecvInterface_handler_done handler_done, void *user); + +static void PacketRecvInterface_Receiver_Recv (PacketRecvInterface *i, uint8_t *data); + +void _PacketRecvInterface_job_operation (PacketRecvInterface *i); +void _PacketRecvInterface_job_done (PacketRecvInterface *i); + +void PacketRecvInterface_Init (PacketRecvInterface *i, int mtu, PacketRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + i->mtu = mtu; + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_PacketRecvInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_PacketRecvInterface_job_done, i); + + // set state + i->state = PRI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void PacketRecvInterface_Free (PacketRecvInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void PacketRecvInterface_Done (PacketRecvInterface *i, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= i->mtu) + ASSERT(i->state == PRI_STATE_BUSY) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = PRI_STATE_DONE_PENDING; +} + +int PacketRecvInterface_GetMTU (PacketRecvInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return i->mtu; +} + +void PacketRecvInterface_Receiver_Init (PacketRecvInterface *i, PacketRecvInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void PacketRecvInterface_Receiver_Recv (PacketRecvInterface *i, uint8_t *data) +{ + ASSERT(!(i->mtu > 0) || data) + ASSERT(i->state == PRI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + BPending_Set(&i->job_operation); + + // set state + i->state = PRI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/service/src/main/jni/badvpn/flow/PacketStreamSender.c b/service/src/main/jni/badvpn/flow/PacketStreamSender.c new file mode 100644 index 0000000..153a72b --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketStreamSender.c @@ -0,0 +1,111 @@ +/** + * @file PacketStreamSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void send_data (PacketStreamSender *s) +{ + ASSERT(s->in_len >= 0) + + if (s->in_used < s->in_len) { + // send more data + StreamPassInterface_Sender_Send(s->output, s->in + s->in_used, s->in_len - s->in_used); + } else { + // finish input packet + s->in_len = -1; + PacketPassInterface_Done(&s->input); + } +} + +static void input_handler_send (PacketStreamSender *s, uint8_t *data, int data_len) +{ + ASSERT(s->in_len == -1) + ASSERT(data_len >= 0) + DebugObject_Access(&s->d_obj); + + // set input packet + s->in_len = data_len; + s->in = data; + s->in_used = 0; + + // send + send_data(s); +} + +static void output_handler_done (PacketStreamSender *s, int data_len) +{ + ASSERT(s->in_len >= 0) + ASSERT(data_len > 0) + ASSERT(data_len <= s->in_len - s->in_used) + DebugObject_Access(&s->d_obj); + + // update number of bytes sent + s->in_used += data_len; + + // send + send_data(s); +} + +void PacketStreamSender_Init (PacketStreamSender *s, StreamPassInterface *output, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + s->output = output; + + // init input + PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)input_handler_send, s, pg); + + // init output + StreamPassInterface_Sender_Init(s->output, (StreamPassInterface_handler_done)output_handler_done, s); + + // have no input packet + s->in_len = -1; + + DebugObject_Init(&s->d_obj); +} + +void PacketStreamSender_Free (PacketStreamSender *s) +{ + DebugObject_Free(&s->d_obj); + + // free input + PacketPassInterface_Free(&s->input); +} + +PacketPassInterface * PacketStreamSender_GetInput (PacketStreamSender *s) +{ + DebugObject_Access(&s->d_obj); + + return &s->input; +} diff --git a/service/src/main/jni/badvpn/flow/PacketStreamSender.h b/service/src/main/jni/badvpn/flow/PacketStreamSender.h new file mode 100644 index 0000000..735360c --- /dev/null +++ b/service/src/main/jni/badvpn/flow/PacketStreamSender.h @@ -0,0 +1,83 @@ +/** + * @file PacketStreamSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which forwards packets obtained with {@link PacketPassInterface} + * as a stream with {@link StreamPassInterface} (i.e. it concatenates them). + */ + +#ifndef BADVPN_FLOW_PACKETSTREAMSENDER_H +#define BADVPN_FLOW_PACKETSTREAMSENDER_H + +#include + +#include +#include +#include + +/** + * Object which forwards packets obtained with {@link PacketPassInterface} + * as a stream with {@link StreamPassInterface} (i.e. it concatenates them). + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface input; + StreamPassInterface *output; + int in_len; + uint8_t *in; + int in_used; +} PacketStreamSender; + +/** + * Initializes the object. + * + * @param s the object + * @param output output interface + * @param mtu input MTU. Must be >=0. + * @param pg pending group + */ +void PacketStreamSender_Init (PacketStreamSender *s, StreamPassInterface *output, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param s the object + */ +void PacketStreamSender_Free (PacketStreamSender *s); + +/** + * Returns the input interface. + * Its MTU will be as in {@link PacketStreamSender_Init}. + * + * @param s the object + * @return input interface + */ +PacketPassInterface * PacketStreamSender_GetInput (PacketStreamSender *s); + +#endif diff --git a/service/src/main/jni/badvpn/flow/SinglePacketBuffer.c b/service/src/main/jni/badvpn/flow/SinglePacketBuffer.c new file mode 100644 index 0000000..bbc72ae --- /dev/null +++ b/service/src/main/jni/badvpn/flow/SinglePacketBuffer.c @@ -0,0 +1,87 @@ +/** + * @file SinglePacketBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +static void input_handler_done (SinglePacketBuffer *o, int in_len) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Sender_Send(o->output, o->buf, in_len); +} + +static void output_handler_done (SinglePacketBuffer *o) +{ + DebugObject_Access(&o->d_obj); + + PacketRecvInterface_Receiver_Recv(o->input, o->buf); +} + +int SinglePacketBuffer_Init (SinglePacketBuffer *o, PacketRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) >= PacketRecvInterface_GetMTU(input)) + + // init arguments + o->input = input; + o->output = output; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init buffer + if (!(o->buf = (uint8_t *)BAlloc(PacketRecvInterface_GetMTU(o->input)))) { + goto fail1; + } + + // schedule receive + PacketRecvInterface_Receiver_Recv(o->input, o->buf); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + return 0; +} + +void SinglePacketBuffer_Free (SinglePacketBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + BFree(o->buf); +} diff --git a/service/src/main/jni/badvpn/flow/SinglePacketBuffer.h b/service/src/main/jni/badvpn/flow/SinglePacketBuffer.h new file mode 100644 index 0000000..87314a5 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/SinglePacketBuffer.h @@ -0,0 +1,75 @@ +/** + * @file SinglePacketBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output + * than can store only a single packet. + */ + +#ifndef BADVPN_FLOW_SINGLEPACKETBUFFER_H +#define BADVPN_FLOW_SINGLEPACKETBUFFER_H + +#include + +#include +#include +#include +#include + +/** + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output + * than can store only a single packet. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface *input; + PacketPassInterface *output; + uint8_t *buf; +} SinglePacketBuffer; + +/** + * Initializes the object. + * Output MTU must be >= input MTU. + * + * @param o the object + * @param input input interface + * @param output output interface + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int SinglePacketBuffer_Init (SinglePacketBuffer *o, PacketRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object + * + * @param o the object + */ +void SinglePacketBuffer_Free (SinglePacketBuffer *o); + +#endif diff --git a/service/src/main/jni/badvpn/flow/StreamPassInterface.c b/service/src/main/jni/badvpn/flow/StreamPassInterface.c new file mode 100644 index 0000000..f0dc547 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/StreamPassInterface.c @@ -0,0 +1,56 @@ +/** + * @file StreamPassInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _StreamPassInterface_job_operation (StreamPassInterface *i) +{ + ASSERT(i->state == SPI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SPI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _StreamPassInterface_job_done (StreamPassInterface *i) +{ + ASSERT(i->state == SPI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SPI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/service/src/main/jni/badvpn/flow/StreamPassInterface.h b/service/src/main/jni/badvpn/flow/StreamPassInterface.h new file mode 100644 index 0000000..1fe650e --- /dev/null +++ b/service/src/main/jni/badvpn/flow/StreamPassInterface.h @@ -0,0 +1,165 @@ +/** + * @file StreamPassInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a stream sender to pass stream data to a stream receiver. + * + * Note that this interface behaves exactly the same and has the same code as + * {@link StreamRecvInterface} if names and its external semantics are disregarded. + * If you modify this file, you should probably modify {@link StreamRecvInterface} + * too. + */ + +#ifndef BADVPN_FLOW_STREAMPASSINTERFACE_H +#define BADVPN_FLOW_STREAMPASSINTERFACE_H + +#include +#include + +#include +#include +#include + +#define SPI_STATE_NONE 1 +#define SPI_STATE_OPERATION_PENDING 2 +#define SPI_STATE_BUSY 3 +#define SPI_STATE_DONE_PENDING 4 + +typedef void (*StreamPassInterface_handler_send) (void *user, uint8_t *data, int data_len); + +typedef void (*StreamPassInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + StreamPassInterface_handler_send handler_operation; + void *user_provider; + + // user data + StreamPassInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} StreamPassInterface; + +static void StreamPassInterface_Init (StreamPassInterface *i, StreamPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg); + +static void StreamPassInterface_Free (StreamPassInterface *i); + +static void StreamPassInterface_Done (StreamPassInterface *i, int data_len); + +static void StreamPassInterface_Sender_Init (StreamPassInterface *i, StreamPassInterface_handler_done handler_done, void *user); + +static void StreamPassInterface_Sender_Send (StreamPassInterface *i, uint8_t *data, int data_len); + +void _StreamPassInterface_job_operation (StreamPassInterface *i); +void _StreamPassInterface_job_done (StreamPassInterface *i); + +void StreamPassInterface_Init (StreamPassInterface *i, StreamPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg) +{ + // init arguments + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_StreamPassInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_StreamPassInterface_job_done, i); + + // set state + i->state = SPI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void StreamPassInterface_Free (StreamPassInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void StreamPassInterface_Done (StreamPassInterface *i, int data_len) +{ + ASSERT(i->state == SPI_STATE_BUSY) + ASSERT(data_len > 0) + ASSERT(data_len <= i->job_operation_len) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = SPI_STATE_DONE_PENDING; +} + +void StreamPassInterface_Sender_Init (StreamPassInterface *i, StreamPassInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void StreamPassInterface_Sender_Send (StreamPassInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data) + ASSERT(i->state == SPI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = SPI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/service/src/main/jni/badvpn/flow/StreamRecvInterface.c b/service/src/main/jni/badvpn/flow/StreamRecvInterface.c new file mode 100644 index 0000000..697e1ae --- /dev/null +++ b/service/src/main/jni/badvpn/flow/StreamRecvInterface.c @@ -0,0 +1,56 @@ +/** + * @file StreamRecvInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _StreamRecvInterface_job_operation (StreamRecvInterface *i) +{ + ASSERT(i->state == SRI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SRI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _StreamRecvInterface_job_done (StreamRecvInterface *i) +{ + ASSERT(i->state == SRI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SRI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/service/src/main/jni/badvpn/flow/StreamRecvInterface.h b/service/src/main/jni/badvpn/flow/StreamRecvInterface.h new file mode 100644 index 0000000..cd4a181 --- /dev/null +++ b/service/src/main/jni/badvpn/flow/StreamRecvInterface.h @@ -0,0 +1,165 @@ +/** + * @file StreamRecvInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a stream receiver to receive stream data from a stream sender. + * + * Note that this interface behaves exactly the same and has the same code as + * {@link StreamPassInterface} if names and its external semantics are disregarded. + * If you modify this file, you should probably modify {@link StreamPassInterface} + * too. + */ + +#ifndef BADVPN_FLOW_STREAMRECVINTERFACE_H +#define BADVPN_FLOW_STREAMRECVINTERFACE_H + +#include +#include + +#include +#include +#include + +#define SRI_STATE_NONE 1 +#define SRI_STATE_OPERATION_PENDING 2 +#define SRI_STATE_BUSY 3 +#define SRI_STATE_DONE_PENDING 4 + +typedef void (*StreamRecvInterface_handler_recv) (void *user, uint8_t *data, int data_len); + +typedef void (*StreamRecvInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + StreamRecvInterface_handler_recv handler_operation; + void *user_provider; + + // user data + StreamRecvInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} StreamRecvInterface; + +static void StreamRecvInterface_Init (StreamRecvInterface *i, StreamRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg); + +static void StreamRecvInterface_Free (StreamRecvInterface *i); + +static void StreamRecvInterface_Done (StreamRecvInterface *i, int data_len); + +static void StreamRecvInterface_Receiver_Init (StreamRecvInterface *i, StreamRecvInterface_handler_done handler_done, void *user); + +static void StreamRecvInterface_Receiver_Recv (StreamRecvInterface *i, uint8_t *data, int data_len); + +void _StreamRecvInterface_job_operation (StreamRecvInterface *i); +void _StreamRecvInterface_job_done (StreamRecvInterface *i); + +void StreamRecvInterface_Init (StreamRecvInterface *i, StreamRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg) +{ + // init arguments + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_StreamRecvInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_StreamRecvInterface_job_done, i); + + // set state + i->state = SRI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void StreamRecvInterface_Free (StreamRecvInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void StreamRecvInterface_Done (StreamRecvInterface *i, int data_len) +{ + ASSERT(i->state == SRI_STATE_BUSY) + ASSERT(data_len > 0) + ASSERT(data_len <= i->job_operation_len) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = SRI_STATE_DONE_PENDING; +} + +void StreamRecvInterface_Receiver_Init (StreamRecvInterface *i, StreamRecvInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void StreamRecvInterface_Receiver_Recv (StreamRecvInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data) + ASSERT(i->state == SRI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = SRI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/service/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.c b/service/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.c new file mode 100644 index 0000000..6531fe0 --- /dev/null +++ b/service/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.c @@ -0,0 +1,131 @@ +/** + * @file PacketPassInactivityMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "PacketPassInactivityMonitor.h" + +static void input_handler_send (PacketPassInactivityMonitor *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // schedule send + PacketPassInterface_Sender_Send(o->output, data, data_len); + + // stop timer + BReactor_RemoveTimer(o->reactor, &o->timer); +} + +static void input_handler_requestcancel (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(o->output); +} + +static void output_handler_done (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // output no longer busy, restart timer + BReactor_SetTimer(o->reactor, &o->timer); + + // call done + PacketPassInterface_Done(&o->input); +} + +static void timer_handler (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // restart timer + BReactor_SetTimer(o->reactor, &o->timer); + + // call handler + if (o->handler) { + o->handler(o->user); + return; + } +} + +void PacketPassInactivityMonitor_Init (PacketPassInactivityMonitor *o, PacketPassInterface *output, BReactor *reactor, btime_t interval, PacketPassInactivityMonitor_handler handler, void *user) +{ + // init arguments + o->output = output; + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // init input + PacketPassInterface_Init(&o->input, PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(o->reactor)); + if (PacketPassInterface_HasCancel(o->output)) { + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + } + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init timer + BTimer_Init(&o->timer, interval, (BTimer_handler)timer_handler, o); + BReactor_SetTimer(o->reactor, &o->timer); + + DebugObject_Init(&o->d_obj); +} + +void PacketPassInactivityMonitor_Free (PacketPassInactivityMonitor *o) +{ + DebugObject_Free(&o->d_obj); + + // free timer + BReactor_RemoveTimer(o->reactor, &o->timer); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassInactivityMonitor_GetInput (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassInactivityMonitor_SetHandler (PacketPassInactivityMonitor *o, PacketPassInactivityMonitor_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + o->handler = handler; + o->user = user; +} + +void PacketPassInactivityMonitor_Force (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + BReactor_SetTimerAfter(o->reactor, &o->timer, 0); +} diff --git a/service/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.h b/service/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.h new file mode 100644 index 0000000..5857618 --- /dev/null +++ b/service/src/main/jni/badvpn/flowextra/PacketPassInactivityMonitor.h @@ -0,0 +1,124 @@ +/** + * @file PacketPassInactivityMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer for detecting inactivity. + */ + +#ifndef BADVPN_PACKETPASSINACTIVITYMONITOR_H +#define BADVPN_PACKETPASSINACTIVITYMONITOR_H + +#include +#include +#include + +/** + * Handler function invoked when inactivity is detected. + * It is guaranteed that the interfaces are in not sending state. + * + * @param user value given to {@link PacketPassInactivityMonitor_Init} + */ +typedef void (*PacketPassInactivityMonitor_handler) (void *user); + +/** + * A {@link PacketPassInterface} layer for detecting inactivity. + * It reports inactivity to a user provided handler function. + * + * The object behaves like that: + * ("timer set" means started with the given timeout whether if was running or not, + * "timer unset" means stopped if it was running) + * - There is a timer. + * - The timer is set when the object is initialized. + * - When the input calls Send, the call is passed on to the output. + * If the output accepted the packet, the timer is set. If the output + * blocked the packet, the timer is unset. + * - When the output calls Done, the timer is set, and the call is + * passed on to the input. + * - When the input calls Cancel, the timer is set, and the call is + * passed on to the output. + * - When the timer expires, the timer is set, ant the user's handler + * function is invoked. + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface *output; + BReactor *reactor; + PacketPassInactivityMonitor_handler handler; + void *user; + PacketPassInterface input; + BTimer timer; +} PacketPassInactivityMonitor; + +/** + * Initializes the object. + * See {@link PacketPassInactivityMonitor} for details. + * + * @param o the object + * @param output output interface + * @param reactor reactor we live in + * @param interval timer value in milliseconds + * @param handler handler function for reporting inactivity, or NULL to disable + * @param user value passed to handler functions + */ +void PacketPassInactivityMonitor_Init (PacketPassInactivityMonitor *o, PacketPassInterface *output, BReactor *reactor, btime_t interval, PacketPassInactivityMonitor_handler handler, void *user); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassInactivityMonitor_Free (PacketPassInactivityMonitor *o); + +/** + * Returns the input interface. + * The MTU of the interface will be the same as of the output interface. + * The interface supports cancel functionality if the output interface supports it. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassInactivityMonitor_GetInput (PacketPassInactivityMonitor *o); + +/** + * Sets or removes the inactivity handler. + * + * @param o the object + * @param handler handler function for reporting inactivity, or NULL to disable + * @param user value passed to handler functions + */ +void PacketPassInactivityMonitor_SetHandler (PacketPassInactivityMonitor *o, PacketPassInactivityMonitor_handler handler, void *user); + +/** + * Sets the timer to expire immediately in order to force an inactivity report. + * + * @param o the object + */ +void PacketPassInactivityMonitor_Force (PacketPassInactivityMonitor *o); + +#endif diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BConnection.h b/service/src/main/jni/badvpn/generated/blog_channel_BConnection.h new file mode 100644 index 0000000..8447db0 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BConnection.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BConnection diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BDatagram.h b/service/src/main/jni/badvpn/generated/blog_channel_BDatagram.h new file mode 100644 index 0000000..d95cf24 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BDatagram.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDatagram diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BNetwork.h b/service/src/main/jni/badvpn/generated/blog_channel_BNetwork.h new file mode 100644 index 0000000..c5e3bc1 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BNetwork.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BNetwork diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BReactor.h b/service/src/main/jni/badvpn/generated/blog_channel_BReactor.h new file mode 100644 index 0000000..d111dc7 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BReactor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BReactor diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BSignal.h b/service/src/main/jni/badvpn/generated/blog_channel_BSignal.h new file mode 100644 index 0000000..2820ebc --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSignal diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BSocksClient.h b/service/src/main/jni/badvpn/generated/blog_channel_BSocksClient.h new file mode 100644 index 0000000..72086fa --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BSocksClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSocksClient diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BTap.h b/service/src/main/jni/badvpn/generated/blog_channel_BTap.h new file mode 100644 index 0000000..ab3e951 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BTap.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BTap diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BTime.h b/service/src/main/jni/badvpn/generated/blog_channel_BTime.h new file mode 100644 index 0000000..b323ee7 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BTime.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BTime diff --git a/service/src/main/jni/badvpn/generated/blog_channel_BUnixSignal.h b/service/src/main/jni/badvpn/generated/blog_channel_BUnixSignal.h new file mode 100644 index 0000000..914b21b --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_BUnixSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BUnixSignal diff --git a/service/src/main/jni/badvpn/generated/blog_channel_PacketProtoDecoder.h b/service/src/main/jni/badvpn/generated/blog_channel_PacketProtoDecoder.h new file mode 100644 index 0000000..fbfa5d8 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_PacketProtoDecoder.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PacketProtoDecoder diff --git a/service/src/main/jni/badvpn/generated/blog_channel_SocksUdpGwClient.h b/service/src/main/jni/badvpn/generated/blog_channel_SocksUdpGwClient.h new file mode 100644 index 0000000..6ba39ae --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_SocksUdpGwClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_SocksUdpGwClient diff --git a/service/src/main/jni/badvpn/generated/blog_channel_UdpGwClient.h b/service/src/main/jni/badvpn/generated/blog_channel_UdpGwClient.h new file mode 100644 index 0000000..8530376 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_UdpGwClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_UdpGwClient diff --git a/service/src/main/jni/badvpn/generated/blog_channel_tun2socks.h b/service/src/main/jni/badvpn/generated/blog_channel_tun2socks.h new file mode 100644 index 0000000..21c1ce2 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channel_tun2socks.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_tun2socks diff --git a/service/src/main/jni/badvpn/generated/blog_channels_defines.h b/service/src/main/jni/badvpn/generated/blog_channels_defines.h new file mode 100644 index 0000000..d89dc86 --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channels_defines.h @@ -0,0 +1,146 @@ +#define BLOG_CHANNEL_server 0 +#define BLOG_CHANNEL_client 1 +#define BLOG_CHANNEL_flooder 2 +#define BLOG_CHANNEL_tun2socks 3 +#define BLOG_CHANNEL_ncd 4 +#define BLOG_CHANNEL_ncd_var 5 +#define BLOG_CHANNEL_ncd_list 6 +#define BLOG_CHANNEL_ncd_depend 7 +#define BLOG_CHANNEL_ncd_multidepend 8 +#define BLOG_CHANNEL_ncd_dynamic_depend 9 +#define BLOG_CHANNEL_ncd_concat 10 +#define BLOG_CHANNEL_ncd_if 11 +#define BLOG_CHANNEL_ncd_strcmp 12 +#define BLOG_CHANNEL_ncd_regex_match 13 +#define BLOG_CHANNEL_ncd_logical 14 +#define BLOG_CHANNEL_ncd_sleep 15 +#define BLOG_CHANNEL_ncd_print 16 +#define BLOG_CHANNEL_ncd_blocker 17 +#define BLOG_CHANNEL_ncd_run 18 +#define BLOG_CHANNEL_ncd_runonce 19 +#define BLOG_CHANNEL_ncd_daemon 20 +#define BLOG_CHANNEL_ncd_spawn 21 +#define BLOG_CHANNEL_ncd_imperative 22 +#define BLOG_CHANNEL_ncd_ref 23 +#define BLOG_CHANNEL_ncd_index 24 +#define BLOG_CHANNEL_ncd_alias 25 +#define BLOG_CHANNEL_ncd_process_manager 26 +#define BLOG_CHANNEL_ncd_ondemand 27 +#define BLOG_CHANNEL_ncd_foreach 28 +#define BLOG_CHANNEL_ncd_choose 29 +#define BLOG_CHANNEL_ncd_net_backend_waitdevice 30 +#define BLOG_CHANNEL_ncd_net_backend_waitlink 31 +#define BLOG_CHANNEL_ncd_net_backend_badvpn 32 +#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 33 +#define BLOG_CHANNEL_ncd_net_backend_rfkill 34 +#define BLOG_CHANNEL_ncd_net_up 35 +#define BLOG_CHANNEL_ncd_net_dns 36 +#define BLOG_CHANNEL_ncd_net_iptables 37 +#define BLOG_CHANNEL_ncd_net_ipv4_addr 38 +#define BLOG_CHANNEL_ncd_net_ipv4_route 39 +#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 40 +#define BLOG_CHANNEL_ncd_net_ipv4_arp_probe 41 +#define BLOG_CHANNEL_ncd_net_watch_interfaces 42 +#define BLOG_CHANNEL_ncd_sys_watch_input 43 +#define BLOG_CHANNEL_ncd_sys_watch_usb 44 +#define BLOG_CHANNEL_ncd_sys_evdev 45 +#define BLOG_CHANNEL_ncd_sys_watch_directory 46 +#define BLOG_CHANNEL_StreamPeerIO 47 +#define BLOG_CHANNEL_DatagramPeerIO 48 +#define BLOG_CHANNEL_BReactor 49 +#define BLOG_CHANNEL_BSignal 50 +#define BLOG_CHANNEL_FragmentProtoAssembler 51 +#define BLOG_CHANNEL_BPredicate 52 +#define BLOG_CHANNEL_ServerConnection 53 +#define BLOG_CHANNEL_Listener 54 +#define BLOG_CHANNEL_DataProto 55 +#define BLOG_CHANNEL_FrameDecider 56 +#define BLOG_CHANNEL_BSocksClient 57 +#define BLOG_CHANNEL_BDHCPClientCore 58 +#define BLOG_CHANNEL_BDHCPClient 59 +#define BLOG_CHANNEL_NCDIfConfig 60 +#define BLOG_CHANNEL_BUnixSignal 61 +#define BLOG_CHANNEL_BProcess 62 +#define BLOG_CHANNEL_PRStreamSink 63 +#define BLOG_CHANNEL_PRStreamSource 64 +#define BLOG_CHANNEL_PacketProtoDecoder 65 +#define BLOG_CHANNEL_DPRelay 66 +#define BLOG_CHANNEL_BThreadWork 67 +#define BLOG_CHANNEL_DPReceive 68 +#define BLOG_CHANNEL_BInputProcess 69 +#define BLOG_CHANNEL_NCDUdevMonitorParser 70 +#define BLOG_CHANNEL_NCDUdevMonitor 71 +#define BLOG_CHANNEL_NCDUdevCache 72 +#define BLOG_CHANNEL_NCDUdevManager 73 +#define BLOG_CHANNEL_BTime 74 +#define BLOG_CHANNEL_BEncryption 75 +#define BLOG_CHANNEL_SPProtoDecoder 76 +#define BLOG_CHANNEL_LineBuffer 77 +#define BLOG_CHANNEL_BTap 78 +#define BLOG_CHANNEL_lwip 79 +#define BLOG_CHANNEL_NCDConfigTokenizer 80 +#define BLOG_CHANNEL_NCDConfigParser 81 +#define BLOG_CHANNEL_NCDValParser 82 +#define BLOG_CHANNEL_nsskey 83 +#define BLOG_CHANNEL_addr 84 +#define BLOG_CHANNEL_PasswordListener 85 +#define BLOG_CHANNEL_NCDInterfaceMonitor 86 +#define BLOG_CHANNEL_NCDRfkillMonitor 87 +#define BLOG_CHANNEL_udpgw 88 +#define BLOG_CHANNEL_UdpGwClient 89 +#define BLOG_CHANNEL_SocksUdpGwClient 90 +#define BLOG_CHANNEL_BNetwork 91 +#define BLOG_CHANNEL_BConnection 92 +#define BLOG_CHANNEL_BSSLConnection 93 +#define BLOG_CHANNEL_BDatagram 94 +#define BLOG_CHANNEL_PeerChat 95 +#define BLOG_CHANNEL_BArpProbe 96 +#define BLOG_CHANNEL_NCDModuleIndex 97 +#define BLOG_CHANNEL_NCDModuleProcess 98 +#define BLOG_CHANNEL_NCDValGenerator 99 +#define BLOG_CHANNEL_ncd_from_string 100 +#define BLOG_CHANNEL_ncd_to_string 101 +#define BLOG_CHANNEL_ncd_value 102 +#define BLOG_CHANNEL_ncd_try 103 +#define BLOG_CHANNEL_ncd_sys_request_server 104 +#define BLOG_CHANNEL_NCDRequest 105 +#define BLOG_CHANNEL_ncd_net_ipv6_wait_dynamic_addr 106 +#define BLOG_CHANNEL_NCDRequestClient 107 +#define BLOG_CHANNEL_ncd_request 108 +#define BLOG_CHANNEL_ncd_sys_request_client 109 +#define BLOG_CHANNEL_ncd_exit 110 +#define BLOG_CHANNEL_ncd_getargs 111 +#define BLOG_CHANNEL_ncd_arithmetic 112 +#define BLOG_CHANNEL_ncd_parse 113 +#define BLOG_CHANNEL_ncd_valuemetic 114 +#define BLOG_CHANNEL_ncd_file 115 +#define BLOG_CHANNEL_ncd_netmask 116 +#define BLOG_CHANNEL_ncd_implode 117 +#define BLOG_CHANNEL_ncd_call2 118 +#define BLOG_CHANNEL_ncd_assert 119 +#define BLOG_CHANNEL_ncd_reboot 120 +#define BLOG_CHANNEL_ncd_explode 121 +#define BLOG_CHANNEL_NCDPlaceholderDb 122 +#define BLOG_CHANNEL_NCDVal 123 +#define BLOG_CHANNEL_ncd_net_ipv6_addr 124 +#define BLOG_CHANNEL_ncd_net_ipv6_route 125 +#define BLOG_CHANNEL_ncd_net_ipv4_addr_in_network 126 +#define BLOG_CHANNEL_ncd_net_ipv6_addr_in_network 127 +#define BLOG_CHANNEL_dostest_server 128 +#define BLOG_CHANNEL_dostest_attacker 129 +#define BLOG_CHANNEL_ncd_timer 130 +#define BLOG_CHANNEL_ncd_file_open 131 +#define BLOG_CHANNEL_ncd_backtrack 132 +#define BLOG_CHANNEL_ncd_socket 133 +#define BLOG_CHANNEL_ncd_depend_scope 134 +#define BLOG_CHANNEL_ncd_substr 135 +#define BLOG_CHANNEL_ncd_sys_start_process 136 +#define BLOG_CHANNEL_NCDBuildProgram 137 +#define BLOG_CHANNEL_ncd_log 138 +#define BLOG_CHANNEL_ncd_log_msg 139 +#define BLOG_CHANNEL_ncd_buffer 140 +#define BLOG_CHANNEL_ncd_getenv 141 +#define BLOG_CHANNEL_BThreadSignal 142 +#define BLOG_CHANNEL_BLockReactor 143 +#define BLOG_CHANNEL_ncd_load_module 144 +#define BLOG_NUM_CHANNELS 145 diff --git a/service/src/main/jni/badvpn/generated/blog_channels_list.h b/service/src/main/jni/badvpn/generated/blog_channels_list.h new file mode 100644 index 0000000..c903c0f --- /dev/null +++ b/service/src/main/jni/badvpn/generated/blog_channels_list.h @@ -0,0 +1,145 @@ +{"server", 4}, +{"client", 4}, +{"flooder", 4}, +{"tun2socks", 4}, +{"ncd", 4}, +{"ncd_var", 4}, +{"ncd_list", 4}, +{"ncd_depend", 4}, +{"ncd_multidepend", 4}, +{"ncd_dynamic_depend", 4}, +{"ncd_concat", 4}, +{"ncd_if", 4}, +{"ncd_strcmp", 4}, +{"ncd_regex_match", 4}, +{"ncd_logical", 4}, +{"ncd_sleep", 4}, +{"ncd_print", 4}, +{"ncd_blocker", 4}, +{"ncd_run", 4}, +{"ncd_runonce", 4}, +{"ncd_daemon", 4}, +{"ncd_spawn", 4}, +{"ncd_imperative", 4}, +{"ncd_ref", 4}, +{"ncd_index", 4}, +{"ncd_alias", 4}, +{"ncd_process_manager", 4}, +{"ncd_ondemand", 4}, +{"ncd_foreach", 4}, +{"ncd_choose", 4}, +{"ncd_net_backend_waitdevice", 4}, +{"ncd_net_backend_waitlink", 4}, +{"ncd_net_backend_badvpn", 4}, +{"ncd_net_backend_wpa_supplicant", 4}, +{"ncd_net_backend_rfkill", 4}, +{"ncd_net_up", 4}, +{"ncd_net_dns", 4}, +{"ncd_net_iptables", 4}, +{"ncd_net_ipv4_addr", 4}, +{"ncd_net_ipv4_route", 4}, +{"ncd_net_ipv4_dhcp", 4}, +{"ncd_net_ipv4_arp_probe", 4}, +{"ncd_net_watch_interfaces", 4}, +{"ncd_sys_watch_input", 4}, +{"ncd_sys_watch_usb", 4}, +{"ncd_sys_evdev", 4}, +{"ncd_sys_watch_directory", 4}, +{"StreamPeerIO", 4}, +{"DatagramPeerIO", 4}, +{"BReactor", 3}, +{"BSignal", 3}, +{"FragmentProtoAssembler", 4}, +{"BPredicate", 3}, +{"ServerConnection", 4}, +{"Listener", 4}, +{"DataProto", 4}, +{"FrameDecider", 4}, +{"BSocksClient", 4}, +{"BDHCPClientCore", 4}, +{"BDHCPClient", 4}, +{"NCDIfConfig", 4}, +{"BUnixSignal", 4}, +{"BProcess", 4}, +{"PRStreamSink", 4}, +{"PRStreamSource", 4}, +{"PacketProtoDecoder", 4}, +{"DPRelay", 4}, +{"BThreadWork", 4}, +{"DPReceive", 4}, +{"BInputProcess", 4}, +{"NCDUdevMonitorParser", 4}, +{"NCDUdevMonitor", 4}, +{"NCDUdevCache", 4}, +{"NCDUdevManager", 4}, +{"BTime", 4}, +{"BEncryption", 4}, +{"SPProtoDecoder", 4}, +{"LineBuffer", 4}, +{"BTap", 4}, +{"lwip", 4}, +{"NCDConfigTokenizer", 4}, +{"NCDConfigParser", 4}, +{"NCDValParser", 4}, +{"nsskey", 4}, +{"addr", 4}, +{"PasswordListener", 4}, +{"NCDInterfaceMonitor", 4}, +{"NCDRfkillMonitor", 4}, +{"udpgw", 4}, +{"UdpGwClient", 4}, +{"SocksUdpGwClient", 4}, +{"BNetwork", 4}, +{"BConnection", 4}, +{"BSSLConnection", 4}, +{"BDatagram", 4}, +{"PeerChat", 4}, +{"BArpProbe", 4}, +{"NCDModuleIndex", 4}, +{"NCDModuleProcess", 4}, +{"NCDValGenerator", 4}, +{"ncd_from_string", 4}, +{"ncd_to_string", 4}, +{"ncd_value", 4}, +{"ncd_try", 4}, +{"ncd_sys_request_server", 4}, +{"NCDRequest", 4}, +{"ncd_net_ipv6_wait_dynamic_addr", 4}, +{"NCDRequestClient", 4}, +{"ncd_request", 4}, +{"ncd_sys_request_client", 4}, +{"ncd_exit", 4}, +{"ncd_getargs", 4}, +{"ncd_arithmetic", 4}, +{"ncd_parse", 4}, +{"ncd_valuemetic", 4}, +{"ncd_file", 4}, +{"ncd_netmask", 4}, +{"ncd_implode", 4}, +{"ncd_call2", 4}, +{"ncd_assert", 4}, +{"ncd_reboot", 4}, +{"ncd_explode", 4}, +{"NCDPlaceholderDb", 4}, +{"NCDVal", 4}, +{"ncd_net_ipv6_addr", 4}, +{"ncd_net_ipv6_route", 4}, +{"ncd_net_ipv4_addr_in_network", 4}, +{"ncd_net_ipv6_addr_in_network", 4}, +{"dostest_server", 4}, +{"dostest_attacker", 4}, +{"ncd_timer", 4}, +{"ncd_file_open", 4}, +{"ncd_backtrack", 4}, +{"ncd_socket", 4}, +{"ncd_depend_scope", 4}, +{"ncd_substr", 4}, +{"ncd_sys_start_process", 4}, +{"NCDBuildProgram", 4}, +{"ncd_log", 4}, +{"ncd_log_msg", 4}, +{"ncd_buffer", 4}, +{"ncd_getenv", 4}, +{"BThreadSignal", 4}, +{"BLockReactor", 4}, +{"ncd_load_module", 4}, diff --git a/service/src/main/jni/badvpn/lwip/COPYING b/service/src/main/jni/badvpn/lwip/COPYING new file mode 100644 index 0000000..e23898b --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/COPYING @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2001, 2002 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + diff --git a/service/src/main/jni/badvpn/lwip/custom/arch/cc.h b/service/src/main/jni/badvpn/lwip/custom/arch/cc.h new file mode 100644 index 0000000..653a2e2 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/custom/arch/cc.h @@ -0,0 +1,96 @@ +/** + * @file cc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_CC_H +#define LWIP_CUSTOM_CC_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define u8_t uint8_t +#define s8_t int8_t +#define u16_t uint16_t +#define s16_t int16_t +#define u32_t uint32_t +#define s32_t int32_t +#define mem_ptr_t uintptr_t + +#define PACK_STRUCT_BEGIN B_START_PACKED +#define PACK_STRUCT_END B_END_PACKED +#define PACK_STRUCT_STRUCT B_PACKED + +#define LWIP_PLATFORM_DIAG(x) { if (BLog_WouldLog(BLOG_CHANNEL_lwip, BLOG_INFO)) { BLog_Begin(); BLog_Append x; BLog_Finish(BLOG_CHANNEL_lwip, BLOG_INFO); } } +#define LWIP_PLATFORM_ASSERT(x) { fprintf(stderr, "%s: lwip assertion failure: %s\n", __FUNCTION__, (x)); abort(); } + +#define U16_F PRIu16 +#define S16_F PRId16 +#define X16_F PRIx16 +#define U32_F PRIu32 +#define S32_F PRId32 +#define X32_F PRIx32 +#define SZT_F "zu" + +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) hton16(x) +#define LWIP_PLATFORM_HTONL(x) hton32(x) + +#define LWIP_RAND() ( \ + (((uint32_t)(rand() & 0xFF)) << 24) | \ + (((uint32_t)(rand() & 0xFF)) << 16) | \ + (((uint32_t)(rand() & 0xFF)) << 8) | \ + (((uint32_t)(rand() & 0xFF)) << 0) \ +) + +// for BYTE_ORDER +#if defined(BADVPN_USE_WINAPI) && !defined(_MSC_VER) + #include +#elif defined(BADVPN_LINUX) + #include +#elif defined(BADVPN_FREEBSD) + #include +#else + #define LITTLE_ENDIAN 1234 + #define BIG_ENDIAN 4321 + #if defined(BADVPN_LITTLE_ENDIAN) + #define BYTE_ORDER LITTLE_ENDIAN + #else + #define BYTE_ORDER BIG_ENDIAN + #endif +#endif + +#endif diff --git a/service/src/main/jni/badvpn/lwip/custom/arch/perf.h b/service/src/main/jni/badvpn/lwip/custom/arch/perf.h new file mode 100644 index 0000000..09c9d47 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/custom/arch/perf.h @@ -0,0 +1,36 @@ +/** + * @file perf.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_PERF_H +#define LWIP_CUSTOM_PERF_H + +#define PERF_START +#define PERF_STOP(x) + +#endif diff --git a/service/src/main/jni/badvpn/lwip/custom/lwipopts.h b/service/src/main/jni/badvpn/lwip/custom/lwipopts.h new file mode 100644 index 0000000..64e03ec --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/custom/lwipopts.h @@ -0,0 +1,70 @@ +/** + * @file lwipopts.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_LWIPOPTS_H +#define LWIP_CUSTOM_LWIPOPTS_H + +#define NO_SYS 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_ARP 0 +#define ARP_QUEUEING 0 +#define IP_FORWARD 0 +#define LWIP_ICMP 1 +#define LWIP_RAW 0 +#define LWIP_DHCP 0 +#define LWIP_AUTOIP 0 +#define LWIP_SNMP 0 +#define LWIP_IGMP 0 +#define LWIP_DNS 0 +#define LWIP_UDP 0 +#define LWIP_UDPLITE 0 +#define LWIP_TCP 1 +#define LWIP_CALLBACK_API 1 +#define LWIP_NETIF_API 0 +#define LWIP_NETIF_LOOPBACK 0 +#define LWIP_HAVE_LOOPIF 0 +#define LWIP_HAVE_SLIPIF 0 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define PPP_SUPPORT 0 +#define LWIP_IPV6 1 +#define LWIP_IPV6_MLD 0 +#define LWIP_IPV6_AUTOCONFIG 0 + +#define MEMP_NUM_TCP_PCB_LISTEN 16 +#define MEMP_NUM_TCP_PCB 1024 +#define TCP_MSS 1460 +#define TCP_SND_BUF 16384 +#define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF)/(TCP_MSS)) + +#define MEM_LIBC_MALLOC 1 +#define MEMP_MEM_MALLOC 1 + +#endif diff --git a/service/src/main/jni/badvpn/lwip/custom/sys.c b/service/src/main/jni/badvpn/lwip/custom/sys.c new file mode 100644 index 0000000..efd1455 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/custom/sys.c @@ -0,0 +1,37 @@ +/** + * @file sys.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +u32_t sys_now (void) +{ + return btime_gettime(); +} diff --git a/service/src/main/jni/badvpn/lwip/src/core/def.c b/service/src/main/jni/badvpn/lwip/src/core/def.c new file mode 100644 index 0000000..352b552 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/def.c @@ -0,0 +1,108 @@ +/** + * @file + * Common functions used throughout the stack. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" +#include "lwip/def.h" + +/** + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * #define LWIP_PLATFORM_BYTESWAP 1 + * #define LWIP_PLATFORM_HTONS(x) + * #define LWIP_PLATFORM_HTONL(x) + * + * Note ntohs() and ntohl() are merely references to the htonx counterparts. + */ + +#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) + +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +lwip_htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +/** + * Convert an u16_t from network- to host byte order. + * + * @param n u16_t in network byte order + * @return n in host byte order + */ +u16_t +lwip_ntohs(u16_t n) +{ + return lwip_htons(n); +} + +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +lwip_htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); +} + +/** + * Convert an u32_t from network- to host byte order. + * + * @param n u32_t in network byte order + * @return n in host byte order + */ +u32_t +lwip_ntohl(u32_t n) +{ + return lwip_htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/inet_chksum.c b/service/src/main/jni/badvpn/lwip/src/core/inet_chksum.c new file mode 100644 index 0000000..8bc42c1 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/inet_chksum.c @@ -0,0 +1,545 @@ +/** + * @file + * Incluse internet checksum functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/def.h" + +#include +#include + +/* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * #define LWIP_CHKSUM + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 2 +# endif +u16_t lwip_standard_chksum(void *dataptr, int len); +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +u16_t +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ + +u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((mem_ptr_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)(void *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((mem_ptr_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((mem_ptr_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#endif /* LWIP_IPV6 */ + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + u16_t chklen; + + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo_partial: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} + +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. Will only compute for a + * portion of the payload. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @param chksum_len number of payload bytes used to compute chksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} +#endif /* LWIP_IPV6 */ + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + return ~LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} + +/* These are some implementations for LWIP_CHKSUM_COPY, which copies data + * like MEMCPY but generates a checksum at the same time. Since this is a + * performance-sensitive function, you might want to create your own version + * in assembly targeted at your hardware by defining it in lwipopts.h: + * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len) + */ + +#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */ +/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM. + * For architectures with big caches, data might still be in cache when + * generating the checksum after copying. + */ +u16_t +lwip_chksum_copy(void *dst, const void *src, u16_t len) +{ + MEMCPY(dst, src, len); + return LWIP_CHKSUM(dst, len); +} +#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/init.c b/service/src/main/jni/badvpn/lwip/src/core/init.c new file mode 100644 index 0000000..c24c027 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/init.c @@ -0,0 +1,345 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp_msg.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/timers.h" +#include "netif/etharp.h" +#include "lwip/ip6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/api.h" + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) + #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_IGMP) + #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) + #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#endif /* !MEMP_MEM_MALLOC */ +#if (LWIP_TCP && (TCP_WND > 0xffff)) + #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) + #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" +#endif +#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) + #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if (!LWIP_NETCONN && LWIP_SOCKET) + #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0)) + #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif +#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) + #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" +#endif +#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) + #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" +#endif +#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT + #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on" +#endif +#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT) + #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT" +#endif +#if (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND) + #error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value" +#endif +#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING + #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" +#endif +#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE + #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets" +#endif +#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF + #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues" +#endif +#if LWIP_NETCONN && LWIP_TCP +#if NETCONN_COPY != TCP_WRITE_FLAG_COPY + #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" +#endif +#if NETCONN_MORE != TCP_WRITE_FLAG_MORE + #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" +#endif +#endif /* LWIP_NETCONN && LWIP_TCP */ +#if LWIP_SOCKET +/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ +#if SO_ACCEPTCONN != SOF_ACCEPTCONN + #error "SO_ACCEPTCONN != SOF_ACCEPTCONN" +#endif +#if SO_REUSEADDR != SOF_REUSEADDR + #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" +#endif +#if SO_KEEPALIVE != SOF_KEEPALIVE + #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" +#endif +#if SO_BROADCAST != SOF_BROADCAST + #error "WARNING: SO_BROADCAST != SOF_BROADCAST" +#endif +#if SO_LINGER != SOF_LINGER + #error "WARNING: SO_LINGER != SOF_LINGER" +#endif +#endif /* LWIP_SOCKET */ + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef MEMP_NUM_API_MSG + #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif + +#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS +#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 +#endif +#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS +#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 +#endif + +/* MEMP sanity checks */ +#if !LWIP_DISABLE_MEMP_SANITY_CHECKS +#if LWIP_NETCONN +#if MEMP_MEM_MALLOC +#if !MEMP_NUM_NETCONN && LWIP_SOCKET +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" +#endif +#else /* MEMP_MEM_MALLOC */ +#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* MEMP_MEM_MALLOC */ +#endif /* LWIP_NETCONN */ +#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ + +/* TCP sanity checks */ +#if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if LWIP_TCP +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_BUF < (2 * TCP_MSS) + #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= TCP_SND_BUF + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN + #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) + #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_WND < TCP_MSS + #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* LWIP_TCP */ +#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); +#if LWIP_SOCKET + lwip_socket_init(); +#endif /* LWIP_SOCKET */ + ip_init(); +#if LWIP_ARP + etharp_init(); +#endif /* LWIP_ARP */ +#if LWIP_RAW + raw_init(); +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); +#endif /* LWIP_UDP */ +#if LWIP_TCP + tcp_init(); +#endif /* LWIP_TCP */ +#if LWIP_SNMP + snmp_init(); +#endif /* LWIP_SNMP */ +#if LWIP_AUTOIP + autoip_init(); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + igmp_init(); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + ip6_init(); + nd6_init(); +#if LWIP_IPV6_MLD + mld6_init(); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if LWIP_TIMERS + sys_timeouts_init(); +#endif /* LWIP_TIMERS */ +} diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv4/autoip.c b/service/src/main/jni/badvpn/lwip/src/core/ipv4/autoip.c new file mode 100644 index 0000000..b122da2 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv4/autoip.c @@ -0,0 +1,528 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +/******************************************************************************* + * USAGE: + * + * define LWIP_AUTOIP 1 in your lwipopts.h + * + * If you don't use tcpip.c (so, don't call, you don't call tcpip_init): + * - First, call autoip_init(). + * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces, + * that should be defined in autoip.h. + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#include +#include + +/* 169.254.0.0 */ +#define AUTOIP_NET 0xA9FE0000 +/* 169.254.1.0 */ +#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100) +/* 169.254.254.255 */ +#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF) + + +/** Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif->autoip?netif->autoip->tried_llipaddr:0)) +#endif /* LWIP_AUTOIP_RAND */ + +/** + * Macro that generates the initial IP address to be tried by AUTOIP. + * If you want to override this, define it to something else in lwipopts.h. + */ +#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR +#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ + htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \ + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) +#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ + +/* static functions */ +static void autoip_handle_arp_conflict(struct netif *netif); + +/* creates a pseudo random LL IP-Address for a network interface */ +static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr); + +/* sends an ARP probe */ +static err_t autoip_arp_probe(struct netif *netif); + +/* sends an ARP announce */ +static err_t autoip_arp_announce(struct netif *netif); + +/* configure interface for use with current LL IP-Address */ +static err_t autoip_bind(struct netif *netif); + +/* start sending probes for llipaddr */ +static void autoip_start_probing(struct netif *netif); + + +/** Set a statically allocated struct autoip to work with. + * Using this prevents autoip_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct autoip + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +autoip_set_struct(struct netif *netif, struct autoip *autoip) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("autoip != NULL", autoip != NULL); + LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL); + + /* clear data structure */ + memset(autoip, 0, sizeof(struct autoip)); + /* autoip->state = AUTOIP_STATE_OFF; */ + netif->autoip = autoip; +} + +/** Restart AutoIP client and check the next address (conflict detected) + * + * @param netif The netif under AutoIP control + */ +static void +autoip_restart(struct netif *netif) +{ + netif->autoip->tried_llipaddr++; + autoip_start(netif); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + /* Somehow detect if we are defending or retreating */ + unsigned char defend = 1; /* tbd */ + + if (defend) { + if (netif->autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last + * DEFEND_INTERVAL seconds + */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we do not defend, retreating\n")); + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param ipaddr ip address to initialize + */ +static void +autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities */ + + u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); + addr += netif->autoip->tried_llipaddr; + addr = AUTOIP_NET | (addr & 0xffff); + /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ + + if (addr < AUTOIP_RANGE_START) { + addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + if (addr > AUTOIP_RANGE_END) { + addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && + (addr <= AUTOIP_RANGE_END)); + ip4_addr_set_u32(ipaddr, htonl(addr)); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), + ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); +} + +/** + * Sends an ARP probe from a network interface + * + * @param netif network interface used to send the probe + */ +static err_t +autoip_arp_probe(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + ip_addr_t sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_ipaddr(netif, &autoip->llipaddr); + netif_set_netmask(netif, &sn_mask); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + return ERR_OK; +} + +/** + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + err_t result = ERR_OK; + + if (netif_is_up(netif)) { + netif_set_down(netif); + } + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if (autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = (struct autoip *)mem_malloc(sizeof(struct autoip)); + if (autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + memset(autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif->autoip = autoip; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + ip_addr_set_zero(&autoip->llipaddr); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip_start_probing(netif); + + return result; +} + +static void +autoip_start_probing(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + + /* time to wait to first probe, this is randomly + * choosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * accquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + if (autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Handle a possible change in the network configuration. + * + * If there is an AutoIP address configured, take the interface down + * and begin probing with the same address. + */ +void +autoip_network_changed(struct netif *netif) +{ + if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) { + netif_set_down(netif); + autoip_start_probing(netif); + } +} + +/** + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + netif->autoip->state = AUTOIP_STATE_OFF; + netif_set_down(netif); + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on AutoIP configured interfaces */ + if (netif->autoip != NULL) { + if (netif->autoip->lastconflict > 0) { + netif->autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(netif->autoip->state), netif->autoip->ttw)); + + switch(netif->autoip->state) { + case AUTOIP_STATE_PROBING: + if (netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if (netif->autoip->sent_num >= PROBE_NUM) { + netif->autoip->state = AUTOIP_STATE_ANNOUNCING; + netif->autoip->sent_num = 0; + netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } else { + autoip_arp_probe(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() PROBING Sent Probe\n")); + netif->autoip->sent_num++; + /* calculate time to wait to next probe */ + netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if (netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if (netif->autoip->sent_num == 0) { + /* We are here the first time, so we waited ANNOUNCE_WAIT seconds + * Now we can bind to an IP address and use it. + * + * autoip_bind calls netif_set_up. This triggers a gratuitous ARP + * which counts as an announcement. + */ + autoip_bind(netif); + } else { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() ANNOUNCING Sent Announce\n")); + } + netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + netif->autoip->sent_num++; + + if (netif->autoip->sent_num >= ANNOUNCE_NUM) { + netif->autoip->state = AUTOIP_STATE_BOUND; + netif->autoip->sent_num = 0; + netif->autoip->ttw = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } + } + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_arp_input. + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n")); + if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + ip_addr_t sipaddr, dipaddr; + struct eth_addr netifaddr; + ETHADDR16_COPY(netifaddr.addr, netif->hwaddr); + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && + (netif->autoip->sent_num == 0))) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) || + (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_restart(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +#endif /* LWIP_AUTOIP */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv4/icmp.c b/service/src/main/jni/badvpn/lwip/src/core/ipv4/icmp.c new file mode 100644 index 0000000..af47153 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv4/icmp.c @@ -0,0 +1,338 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be + * used to modify and send a response packet (and to 1 if this is not the case, + * e.g. when link header is stripped of when receiving) */ +#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the icmp header + * @param inp the netif on which this packet was received + */ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; +#ifdef LWIP_DEBUG + u8_t code; +#endif /* LWIP_DEBUG */ + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + s16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + iphdr = (struct ip_hdr *)ip_current_header(); + hlen = IPH_HL(iphdr) * 4; + if (p->len < sizeof(u16_t)*2) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + goto lenerr; + } + + type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG + code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ + switch (type) { + case ICMP_ER: + /* This is OK, echo reply might have been parsed by a raw PCB + (as obviously, an echo request has been sent, too). */ + break; + case ICMP_ECHO: +#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING + { + int accepted = 1; +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip_addr_ismulticast(ip_current_dest_addr())) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { + accepted = 0; + } +#endif /* LWIP_BROADCAST_PING */ + /* broadcast or multicast destination address not acceptd? */ + if (!accepted) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } + } +#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + goto lenerr; + } + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + snmp_inc_icmpinerrors(); + return; + } +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* switch p->payload to ip header */ + if (pbuf_header(p, hlen)) { + LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); + goto memerr; + } + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", + (r->len >= hlen + sizeof(struct icmp_echo_hdr))); + /* copy the whole packet including ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); + goto memerr; + } + iphdr = (struct ip_hdr *)r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header */ + if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + } +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = (struct icmp_echo_hdr *)p->payload; + ip_addr_copy(iphdr->src, *ip_current_dest_addr()); + ip_addr_copy(iphdr->dest, *ip_current_src_addr()); + ICMPH_TYPE_SET(iecho, ICMP_ER); +#if CHECKSUM_GEN_ICMP + /* adjust the checksum */ + if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } +#else /* CHECKSUM_GEN_ICMP */ + iecho->chksum = 0; +#endif /* CHECKSUM_GEN_ICMP */ + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of echo replies attempted to send */ + snmp_inc_icmpoutechoreps(); + + if(pbuf_header(p, hlen)) { + LWIP_ASSERT("Can't move over header in packet", 0); + } else { + err_t ret; + /* send an ICMP packet, src addr is the dest addr of the curren packet */ + ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret)); + } + } + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +memerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + snmp_inc_icmpinerrors(); + return; +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ +} + +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + icmp_send_response(p, ICMP_DUR, t); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + icmp_send_response(p, ICMP_TE, t); +} + +#endif /* IP_FORWARD || IP_REASSEMBLY */ + +/** + * Send an icmp packet in response to an incoming packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param type Type of the ICMP header + * @param code Code of the ICMP header + */ +static void +icmp_send_response(struct pbuf *p, u8_t type, u8_t code) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + /* we can use the echo header here */ + struct icmp_echo_hdr *icmphdr; + ip_addr_t iphdr_src; + + /* ICMP header + IP header + 8 bytes of data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = (struct ip_hdr *)p->payload; + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + icmphdr = (struct icmp_echo_hdr *)q->payload; + icmphdr->type = type; + icmphdr->code = code; + icmphdr->id = 0; + icmphdr->seqno = 0; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + /* calculate checksum */ + icmphdr->chksum = 0; + icmphdr->chksum = inet_chksum(icmphdr, q->len); + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpouttimeexcds(); + ip_addr_copy(iphdr_src, iphdr->src); + ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv4/igmp.c b/service/src/main/jni/badvpn/lwip/src/core/ipv4/igmp.c new file mode 100644 index 0000000..bd52744 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv4/igmp.c @@ -0,0 +1,805 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#include "string.h" + +/* + * IGMP constants + */ +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404U +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/** + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FIELD(u8_t igmp_msgtype); + PACK_STRUCT_FIELD(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr); +static err_t igmp_remove_group(struct igmp_group *group); +static void igmp_timeout( struct igmp_group *group); +static void igmp_start_timer(struct igmp_group *group, u8_t max_time); +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); +static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); +static void igmp_send(struct igmp_group *group, u8_t type); + + +static struct igmp_group* igmp_group_list; +static ip_addr_t allsystems; +static ip_addr_t allrouters; + + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +#ifdef LWIP_DEBUG +/** + * Dump global IGMP groups list + */ +void +igmp_dump_group_list() +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + group = group->next; + } + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); +} +#else +#define igmp_dump_group_list() +#endif /* LWIP_DEBUG */ + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, &allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + struct igmp_group *prev = NULL; + struct igmp_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == igmp_group_list) { + igmp_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); + + while (group != NULL) { + if (group->netif == netif) { + igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + } + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +struct igmp_group * +igmp_lookup_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->netif = ifp; + ip_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = igmp_group_list; + + igmp_group_list = group; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +igmp_remove_group(struct igmp_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (igmp_group_list == group) { + igmp_group_list = group->next; + } else { + /* look for group further down the list */ + struct igmp_group *tmpGroup; + for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the igmp header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) +{ + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + IGMP_STATS_INC(igmp.recv); + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + if (p->len < IGMP_MINLEN) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + IGMP_STATS_INC(igmp.drop); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: { + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.rx_v1); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } else { + IGMP_STATS_INC(igmp.rx_general); + } + + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + igmp_delaying_member(groupref, igmp->igmp_maxresp); + } + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (!ip_addr_isany(&igmp->igmp_group_address)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); + if (ip_addr_cmp(dest, &allsystems)) { + ip_addr_t groupaddr; + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-look for the group since we used dest last time */ + ip_addr_copy(groupaddr, igmp->igmp_group_address); + group = igmp_lookfor_group(inp, &groupaddr); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.rx_group); + igmp_delaying_member(group, igmp->igmp_maxresp); + } else { + IGMP_STATS_INC(igmp.drop); + } + } else { + IGMP_STATS_INC(igmp.proterr); + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + IGMP_STATS_INC(igmp.rx_report); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + } + default: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", + igmp->igmp_msgtype, group->group_state, &group, group->netif)); + IGMP_STATS_INC(igmp.proterr); + break; + } + } + + pbuf_free(p); + return; +} + +/** + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.tx_join); + igmp_send(group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + err = ERR_OK; + } else { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); + return ERR_MEM; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); + IGMP_STATS_INC(igmp.tx_leave); + igmp_send(group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* Free the group */ + igmp_remove_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } else { + /* It's not a fatal error on "leavegroup" */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + igmp_timeout(group); + } + } + group = group->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +static void +igmp_timeout(struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + + IGMP_STATS_INC(igmp.tx_report); + igmp_send(group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +static void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /* ensure the input value is > 0 */ + if (max_time == 0) { + max_time = 1; + } +#ifdef LWIP_RAND + /* ensure the random value is > 0 */ + group->timer = (LWIP_RAND() % (max_time - 1)) + 1; +#endif /* LWIP_RAND */ +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +static void +igmp_delaying_member(struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || + ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + igmp_start_timer(group, maxresp); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +static err_t +igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = PP_HTONS(ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + IGMP_STATS_INC(igmp.xmit); + return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +static void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + ip_addr_t src = *IP_ADDR_ANY; + ip_addr_t* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = (struct igmp_msg *)p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_copy(src, group->netif->ip_addr); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + ip_addr_copy(igmp->igmp_group_address, group->group_address); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip_addr_copy(igmp->igmp_group_address, group->group_address); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); + + igmp_ip_output_if(p, &src, dest, group->netif); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + IGMP_STATS_INC(igmp.memerr); + } +} + +#endif /* LWIP_IGMP */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip4.c b/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip4.c new file mode 100644 index 0000000..1acc255 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip4.c @@ -0,0 +1,924 @@ +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip_frag.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/igmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +/** Set this to 0 in the rare case of wanting to call an extra function to + * generate the IP checksum (in contrast to calculating it on-the-fly). */ +#ifndef LWIP_INLINE_IP_CHKSUM +#define LWIP_INLINE_IP_CHKSUM 1 +#endif +#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP_INLINE 1 +#else +#define CHECKSUM_GEN_IP_INLINE 0 +#endif + +#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT) +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1 + +/** Some defines for DHCP to let link-layer-addressed packets through while the + * netif is down. + * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT + * to return 1 if the port is accepted and 0 if the port is not accepted. + */ +#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) +/* accept DHCP client port and custom port */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \ + || (LWIP_IP_ACCEPT_UDP_PORT(port))) +#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept custom port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port)) +#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept DHCP client port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT)) +#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ + +#else /* LWIP_DHCP */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0 +#endif /* LWIP_DHCP */ + +/** Global data for both IPv4 and IPv6 */ +struct ip_globals ip_data; + +/** The IP header ID of the next outgoing IP packet */ +static u16_t ip_id; + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip_route(ip_addr_t *dest) +{ + struct netif *netif; + +#ifdef LWIP_HOOK_IP4_ROUTE + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif + + /* iterate through netifs */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + return netif_default; +} + +#if IP_FORWARD +/** + * Determine whether an IP address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + * @param p the packet to forward + * @param dest the destination IP address + * @return 1: can forward 0: discard + */ +static int +ip_canforward(struct pbuf *p) +{ + u32_t addr = htonl(ip4_addr_get_u32(ip_current_dest_addr())); + + if (p->flags & PBUF_FLAG_LLBCAST) { + /* don't route link-layer broadcasts */ + return 0; + } + if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { + /* don't route link-layer multicasts unless the destination address is an IP + multicast address */ + return 0; + } + if (IP_EXPERIMENTAL(addr)) { + return 0; + } + if (IP_CLASSA(addr)) { + u32_t net = addr & IP_CLASSA_NET; + if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { + /* don't route loopback packets */ + return 0; + } + } + return 1; +} + +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + + if (!ip_canforward(p)) { + goto return_noroute; + } + + /* RFC3927 2.7: do not forward link-local addresses */ + if (ip_addr_islinklocal(ip_current_dest_addr())) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + goto return_noroute; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip_route(ip_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + /* @todo: send ICMP_DUR_NET? */ + goto return_noroute; + } +#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + goto return_noroute; + } +#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + snmp_inc_ipinhdrerrors(); +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + return; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { +#if IP_FRAG + ip_frag(p, netif, ip_current_dest_addr()); +#else /* IP_FRAG */ + /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */ +#endif /* IP_FRAG */ + } else { + /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */ + icmp_dest_unreach(p, ICMP_DUR_FRAG); + } + return; + } + /* transmit pbuf on chosen interface */ + netif->output(netif, p, ip_current_dest_addr()); + return; +return_noroute: + snmp_inc_ipoutnoroutes(); +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = (struct ip_hdr *)p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } + +#ifdef LWIP_HOOK_IP4_INPUT + if (LWIP_HOOK_IP4_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + + /* obtain IP header length in number of 32-bit words */ + iphdr_hlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = ntohs(IPH_LEN(iphdr)); + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { + if (iphdr_hlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + } + if (iphdr_len > p->tot_len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_len, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + if (inet_chksum(iphdr, iphdr_hlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } +#endif + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, iphdr_len); + + /* copy IP addresses to aligned ip_addr_t */ + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest); + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(ip_current_dest_addr())) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) { + netif = inp; + } else { + netif = NULL; + } + } else +#endif /* LWIP_IGMP */ + { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr), + ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { + /* unicast to this interface address? */ + if (ip_addr_cmp(ip_current_dest_addr(), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(ip_current_dest_addr(), netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#if LWIP_AUTOIP + /* connections to link-local addresses must persist after changing + the netif's address (RFC3927 ch. 1.9) */ + if ((netif->autoip != NULL) && + ip_addr_cmp(ip_current_dest_addr(), &(netif->autoip->llipaddr))) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#endif /* LWIP_AUTOIP */ + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); + } + +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + * + * If you want to accept private broadcast communication while a netif is down, + * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.: + * + * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345)) + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(udphdr->dest))); + if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ + if (check_ip_src && !ip_addr_isany(ip_current_src_addr())) +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) || + (ip_addr_ismulticast(ip_current_src_addr()))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + return ERR_OK; + } + } + + /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it + isn't for a local address */ + if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IPH_PROTO(iphdr) == IP_PROTO_TCP) { + netif = inp; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } else +#endif /* IP_FORWARD */ + { + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", + ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); + /* reassemble the packet*/ + p = ip_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = (struct ip_hdr *)p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; + } +#endif /* IP_OPTIONS_ALLOWED == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + + ip_data.current_netif = inp; + ip_data.current_ip4_header = iphdr; + ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p, inp, ip_current_dest_addr()); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) && + !ip_addr_ismulticast(ip_current_dest_addr())) { + pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */ + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinunknownprotos(); + } + } + + /* @todo: this is not really necessary... */ + ip_data.current_netif = NULL; + ip_data.current_ip4_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip_addr_set_any(ip_current_src_addr()); + ip_addr_set_any(ip_current_dest_addr()); + + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + * If the destination IP address is IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" + */ +err_t +ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ +#if IP_OPTIONS_SEND + return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if() but with the possibility to include IP options: + * + * @ param ip_options pointer to the IP options, copied into the IP header + * @ param optlen length of ip_options + */ +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ + struct ip_hdr *iphdr; + ip_addr_t dest_addr; +#if CHECKSUM_GEN_IP_INLINE + u32_t chk_sum = 0; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + snmp_inc_ipoutrequests(); + + /* Should the IP header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + u16_t ip_hlen = IP_HLEN; +#if IP_OPTIONS_SEND + u16_t optlen_aligned = 0; + if (optlen != 0) { +#if CHECKSUM_GEN_IP_INLINE + int i; +#endif /* CHECKSUM_GEN_IP_INLINE */ + /* round up to a multiple of 4 */ + optlen_aligned = ((optlen + 3) & ~3); + ip_hlen += optlen_aligned; + /* First write in the IP options */ + if (pbuf_header(p, optlen_aligned)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n")); + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + MEMCPY(p->payload, ip_options, optlen); + if (optlen < optlen_aligned) { + /* zero the remaining bytes */ + memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + } +#if CHECKSUM_GEN_IP_INLINE + for (i = 0; i < optlen_aligned/2; i++) { + chk_sum += ((u16_t*)p->payload)[i]; + } +#endif /* CHECKSUM_GEN_IP_INLINE */ + } +#endif /* IP_OPTIONS_SEND */ + /* generate IP header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + + iphdr = (struct ip_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(proto, ttl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* dest cannot be NULL here */ + ip_addr_copy(iphdr->dest, *dest); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + IPH_VHL_SET(iphdr, 4, ip_hlen / 4); + IPH_TOS_SET(iphdr, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_LEN_SET(iphdr, htons(p->tot_len)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_len; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_id; +#endif /* CHECKSUM_GEN_IP_INLINE */ + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_copy(iphdr->src, netif->ip_addr); + } else { + /* src cannot be NULL here */ + ip_addr_copy(iphdr->src, *src); + } + +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16; + chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF); + chk_sum = (chk_sum >> 16) + chk_sum; + chk_sum = ~chk_sum; + iphdr->_chksum = chk_sum; /* network order */ +#else /* CHECKSUM_GEN_IP_INLINE */ + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); +#endif +#endif /* CHECKSUM_GEN_IP_INLINE */ + } else { + /* IP header already included in p */ + iphdr = (struct ip_hdr *)p->payload; + ip_addr_copy(dest_addr, iphdr->dest); + dest = &dest_addr; + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + +#if ENABLE_LOOPBACK + if (ip_addr_cmp(dest, &netif->ip_addr)) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + return netif_loop_output(netif, p, dest); + } +#if LWIP_IGMP + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p, dest); + } +#endif /* LWIP_IGMP */ +#endif /* ENABLE_LOOPBACK */ +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + return ip_frag(p, netif, dest); + } +#endif /* IP_FRAG */ + + LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); + return netif->output(netif, p, dest); +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1_16(&iphdr->src), + ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), + ip4_addr4_16(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1_16(&iphdr->dest), + ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), + ip4_addr4_16(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip4_addr.c b/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip4_addr.c new file mode 100644 index 0000000..8f633ff --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip4_addr.c @@ -0,0 +1,312 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const ip_addr_t ip_addr_any = { IPADDR_ANY }; +const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST }; + +/** + * Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + */ +u8_t +ip4_addr_isbroadcast(u32_t addr, const struct netif *netif) +{ + ip_addr_t ipaddr; + ip4_addr_set_u32(&ipaddr, addr); + + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr == IPADDR_ANY) || + (addr == IPADDR_ANY)) { + return 1; + /* no broadcast support on this network interface? */ + } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) { + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) { + return 0; + /* on the same (sub) network... */ + } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr & ~ip4_addr_get_u32(&netif->netmask)) == + (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) { + /* => network broadcast address */ + return 1; + } else { + return 0; + } +} + +/** Checks if a netmask is valid (starting with ones, then only zeros) + * + * @param netmask the IPv4 netmask to check (in network byte order!) + * @return 1 if the netmask is valid, 0 if it is not + */ +u8_t +ip4_addr_netmask_valid(u32_t netmask) +{ + u32_t mask; + u32_t nm_hostorder = lwip_htonl(netmask); + + /* first, check for the first zero */ + for (mask = 1UL << 31 ; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) == 0) { + break; + } + } + /* then check that there is no one */ + for (; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) != 0) { + /* there is a one after the first zero -> invalid */ + return 0; + } + } + /* no one after the first zero -> valid */ + return 1; +} + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +ipaddr_addr(const char *cp) +{ + ip_addr_t val; + + if (ipaddr_aton(cp, &val)) { + return ip4_addr_get_u32(&val); + } + return (IPADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ipaddr_aton(const char *cp, ip_addr_t *addr) +{ + u32_t val; + u8_t base; + char c; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else + base = 8; + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (int)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) { + return (0); + } + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) { + return (0); + } + /* + * Concoct the address according to + * the number of parts specified. + */ + switch (pp - parts + 1) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffUL) { + return (0); + } + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + default: + LWIP_ASSERT("unhandled", 0); + break; + } + if (addr) { + ip4_addr_set_u32(addr, htonl(val)); + } + return (1); +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ipaddr_ntoa(const ip_addr_t *addr) +{ + static char str[16]; + return ipaddr_ntoa_r(addr, str, 16); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen) +{ + u32_t s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + int len = 0; + + s_addr = ip4_addr_get_u32(addr); + + rp = buf; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) { + if (len++ >= buflen) { + return NULL; + } + *rp++ = inv[i]; + } + if (len++ >= buflen) { + return NULL; + } + *rp++ = '.'; + ap++; + } + *--rp = 0; + return buf; +} diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip_frag.c b/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip_frag.c new file mode 100644 index 0000000..8d18434 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv4/ip_frag.c @@ -0,0 +1,863 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_frag.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IP header, since it replaces + * the IP header in memory in incoming fragments (after copying it) to keep + * track of the various fragments. (-> If the IP header doesn't need packing, + * this struct doesn't need packing, too.) + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + snmp_inc_ipreasmfails(); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reass struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no wholes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no wholes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { + valid = 0; + } else { + /* and check that there are no wholes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len; + u8_t clen; + struct ip_reassdata *ipr_prev = NULL; + + IPFRAG_STATS_INC(ip_frag.recv); + snmp_inc_ipreasmreqds(); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if(ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set calculate the correct checksum? */ + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while(r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; +#else /* IP_FRAG_USES_STATIC_BUF */ + +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ipfrag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; +#endif + struct ip_hdr *original_iphdr; +#endif + struct ip_hdr *iphdr; + u16_t nfb; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + + /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); + return ERR_MEM; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = (struct ip_hdr *)rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = (struct ip_hdr *)p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + nfb = (mtu - IP_HLEN) / 8; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) { + tmp = tmp | IP_MF; + } + + /* Fill this fragment */ + cop = last ? left : nfb * 8; + +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, rambuf->payload, cop, poff); + /* make room for the IP header */ + if(pbuf_header(rambuf, IP_HLEN)) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = rambuf->payload; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr *)rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + +#if IP_FRAG_USES_STATIC_BUF + if (last) { + pbuf_realloc(rambuf, left + IP_HLEN); + } + + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it.A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + snmp_inc_ipfragcreates(); + pbuf_free(header); + } else { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + return ERR_MEM; + } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + left -= cop; + ofo += nfb; + } +#if IP_FRAG_USES_STATIC_BUF + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + snmp_inc_ipfragoks(); + return ERR_OK; +} +#endif /* IP_FRAG */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/dhcp6.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/dhcp6.c new file mode 100644 index 0000000..9656c3b --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/dhcp6.c @@ -0,0 +1,50 @@ +/** + * @file + * + * DHCPv6. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + + +#endif /* LWIP_IPV6_DHCP6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/ethip6.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ethip6.c new file mode 100644 index 0000000..ab9783a --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ethip6.c @@ -0,0 +1,193 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET + +#include "lwip/ethip6.h" +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" + +#include + +#define ETHTYPE_IPV6 0x86DD + +/** The ethernet address */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[6]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Ethernet header */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +/** + * Send an IPv6 packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +ethip6_send(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be 6 for ethip6!", + (netif->hwaddr_len == 6)); + SMEMCPY(ðhdr->dest, dst, 6); + SMEMCPY(ðhdr->src, src, 6); + ethhdr->type = PP_HTONS(ETHTYPE_IPV6); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IPv6 packet. + * + * For IPv6 multicast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, ... + * + * @TODO anycast addresses + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ip6addr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr) +{ + struct eth_addr dest; + s8_t i; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + return ERR_BUF; + } + + /* multicast destination IP address? */ + if (ip6_addr_ismulticast(ip6addr)) { + /* Hash IP multicast address to MAC address.*/ + dest.addr[0] = 0x33; + dest.addr[1] = 0x33; + dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0]; + dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1]; + dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2]; + dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3]; + + /* Send out. */ + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We have a unicast destination IP address */ + /* TODO anycast? */ + /* Get next hop record. */ + i = nd6_get_next_hop_entry(ip6addr, netif); + if (i < 0) { + /* failed to get a next hop neighbor record. */ + return ERR_MEM; + } + + /* Now that we have a destination record, send or queue the packet. */ + if (neighbor_cache[i].state == ND6_STALE) { + /* Switch to delay state. */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + /* TODO should we send or queue if PROBE? send for now, to let unicast NS pass. */ + if ((neighbor_cache[i].state == ND6_REACHABLE) || + (neighbor_cache[i].state == ND6_DELAY) || + (neighbor_cache[i].state == ND6_PROBE)) { + + /* Send out. */ + SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6); + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We should queue packet on this interface. */ + pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR); + return nd6_queue_packet(i, q); +} + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/icmp6.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/icmp6.c new file mode 100644 index 0000000..ea82682 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/icmp6.c @@ -0,0 +1,337 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif +#if LWIP_ICMP6_DATASIZE == 0 +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/* Forward declarations */ +static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); + + +/** + * Process an input ICMPv6 message. Called by ip6_input. + * + * Will generate a reply for echo requests. Other messages are forwarded + * to nd6_input, or mld6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +icmp6_input(struct pbuf *p, struct netif *inp) +{ + struct icmp6_hdr *icmp6hdr; + struct pbuf * r; + ip6_addr_t * reply_src; + + ICMP6_STATS_INC(icmp6.recv); + + /* Check that ICMPv6 header fits in payload */ + if (p->len < sizeof(struct icmp6_hdr)) { + /* drop short packets */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.lenerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + +#if LWIP_ICMP6_CHECKSUM_CHECK + if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), + ip6_current_dest_addr()) != 0) { + /* Checksum failed */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.chkerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_ICMP6_CHECKSUM_CHECK */ + + switch (icmp6hdr->type) { + case ICMP6_TYPE_NA: /* Neighbor advertisement */ + case ICMP6_TYPE_NS: /* Neighbor solicitation */ + case ICMP6_TYPE_RA: /* Router advertisement */ + case ICMP6_TYPE_RD: /* Redirect */ + case ICMP6_TYPE_PTB: /* Packet too big */ + nd6_input(p, inp); + return; + break; + case ICMP6_TYPE_RS: +#if LWIP_IPV6_FORWARD + /* TODO implement router functionality */ +#endif + break; +#if LWIP_IPV6_MLD + case ICMP6_TYPE_MLQ: + case ICMP6_TYPE_MLR: + case ICMP6_TYPE_MLD: + mld6_input(p, inp); + return; + break; +#endif + case ICMP6_TYPE_EREQ: +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_MULTICAST_PING */ + + /* Allocate reply. */ + r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); + if (r == NULL) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + + /* Copy echo request. */ + if (pbuf_copy(r, p) != ERR_OK) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.err); + return; + } + + /* Determine reply source IPv6 address. */ +#if LWIP_MULTICAST_PING + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + reply_src = ip6_select_source_address(inp, ip6_current_src_addr()); + if (reply_src == NULL) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else +#endif /* LWIP_MULTICAST_PING */ + { + reply_src = ip6_current_dest_addr(); + } + + /* Set fields in reply. */ + ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, + IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); + + /* Send reply. */ + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(r, reply_src, ip6_current_src_addr(), + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); + pbuf_free(r); + + break; + default: + ICMP6_STATS_INC(icmp6.proterr); + ICMP6_STATS_INC(icmp6.drop); + break; + } + + pbuf_free(p); +} + + +/** + * Send an icmpv6 'destination unreachable' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the unreachable type + */ +void +icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); +} + +/** + * Send an icmpv6 'packet too big' packet. + * + * @param p the input packet for which the 'packet too big' should be sent, + * p->payload pointing to the IPv6 header + * @param mtu the maximum mtu that we can accept + */ +void +icmp6_packet_too_big(struct pbuf *p, u32_t mtu) +{ + icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); +} + +/** + * Send an icmpv6 'time exceeded' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the time exceeded type + */ +void +icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); +} + +/** + * Send an icmpv6 'parameter problem' packet. + * + * @param p the input packet for which the 'param problem' should be sent, + * p->payload pointing to the IP header + * @param c ICMPv6 code for the param problem type + * @param pointer the pointer to the byte where the parameter is found + */ +void +icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) +{ + icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); +} + +/** + * Send an ICMPv6 packet in response to an incoming packet. + * + * @param p the input packet for which the response should be sent, + * p->payload pointing to the IPv6 header + * @param code Code of the ICMPv6 header + * @param data Additional 32-bit parameter in the ICMPv6 header + * @param type Type of the ICMPv6 header + */ +static void +icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) +{ + struct pbuf *q; + struct icmp6_hdr *icmp6hdr; + ip6_addr_t *reply_src, *reply_dest; + ip6_addr_t reply_src_local, reply_dest_local; + struct ip6_hdr *ip6hdr; + struct netif *netif; + + /* ICMPv6 header + IPv6 header + data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp 6message", + (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); + + icmp6hdr = (struct icmp6_hdr *)q->payload; + icmp6hdr->type = type; + icmp6hdr->code = code; + icmp6hdr->data = data; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, + IP6_HLEN + LWIP_ICMP6_DATASIZE); + + /* Get the destination address and netif for this ICMP message. */ + if ((ip_current_netif() == NULL) || + ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { + /* Special case, as ip6_current_xxx is either NULL, or points + * to a different packet than the one that expired. + * We must use the addresses that are stored in the expired packet. */ + ip6hdr = (struct ip6_hdr *)p->payload; + /* copy from packed address to aligned address */ + ip6_addr_copy(reply_dest_local, ip6hdr->src); + ip6_addr_copy(reply_src_local, ip6hdr->dest); + reply_dest = &reply_dest_local; + reply_src = &reply_src_local; + netif = ip6_route(reply_src, reply_dest); + if (netif == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else { + netif = ip_current_netif(); + reply_dest = ip6_current_src_addr(); + + /* Select an address to use as source. */ + reply_src = ip6_select_source_address(netif, reply_dest); + if (reply_src == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + + /* calculate checksum */ + icmp6hdr->chksum = 0; + icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, + reply_src, reply_dest); + + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(q); +} + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/inet6.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/inet6.c new file mode 100644 index 0000000..bdf4ff4 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/inet6.c @@ -0,0 +1,51 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/inet6.h" + +/** @see ip6_addr.c for implementation of functions. */ + +#endif /* LWIP_IPV6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6.c new file mode 100644 index 0000000..1eb91f9 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6.c @@ -0,0 +1,1034 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_frag.h" +#include "lwip/icmp6.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/dhcp6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + + +/** + * Finds the appropriate network interface for a given IPv6 address. It tries to select + * a netif following a sequence of heuristics: + * 1) if there is only 1 netif, return it + * 2) if the destination is a link-local address, try to match the src address to a netif. + * this is a tricky case because with multiple netifs, link-local addresses only have + * meaning within a particular subnet/link. + * 3) tries to match the destination subnet to a configured address + * 4) tries to find a router + * 5) tries to match the source address to the netif + * 6) returns the default netif, if configured + * + * @param src the source IPv6 address, if known + * @param dest the destination IPv6 address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip6_route(struct ip6_addr *src, struct ip6_addr *dest) +{ + struct netif *netif; + s8_t i; + + /* If single netif configuration, fast return. */ + if ((netif_list != NULL) && (netif_list->next == NULL)) { + return netif_list; + } + + /* Special processing for link-local addresses. */ + if (ip6_addr_islinklocal(dest)) { + if (ip6_addr_isany(src)) { + /* Use default netif. */ + return netif_default; + } + + /* Try to find the netif for the source address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* netif not found, use default netif */ + return netif_default; + } + + /* See if the destination subnet matches a configured address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* Get the netif for a suitable router. */ + i = nd6_select_router(dest, NULL); + if (i >= 0) { + if (default_router_list[i].neighbor_entry != NULL) { + if (default_router_list[i].neighbor_entry->netif != NULL) { + return default_router_list[i].neighbor_entry->netif; + } + } + } + + /* try with the netif that matches the source address. */ + if (!ip6_addr_isany(src)) { + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + } + + /* no matching netif found, use default netif */ + return netif_default; +} + +/** + * Select the best IPv6 source address for a given destination + * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior + * is assumed. + * + * @param netif the netif on which to send a packet + * @param dest the destination we are trying to reach + * @return the most suitable source address to use, or NULL if no suitable + * source address is found + */ +ip6_addr_t * +ip6_select_source_address(struct netif *netif, ip6_addr_t * dest) +{ + ip6_addr_t * src = NULL; + u8_t i; + + /* If dest is link-local, choose a link-local source. */ + if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a site-local with matching prefix. */ + if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a unique-local with matching prefix. */ + if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a global with best matching prefix. */ + if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isglobal(netif_ip6_addr(netif, i))) { + if (src == NULL) { + src = netif_ip6_addr(netif, i); + } + else { + /* Replace src only if we find a prefix match. */ + /* TODO find longest matching prefix. */ + if ((!(ip6_addr_netcmp(src, dest))) && + ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { + src = netif_ip6_addr(netif, i); + } + } + } + } + if (src != NULL) { + return src; + } + } + + /* Last resort: see if arbitrary prefix matches. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + + return NULL; +} + +#if LWIP_IPV6_FORWARD +/** + * Forwards an IPv6 packet. It finds an appropriate route for the + * packet, decrements the HL value of the packet, and outputs + * the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IPv6 header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + /* do not forward link-local addresses */ + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip6_route(IP6_ADDR_ANY, ip6_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* decrement HL */ + IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); + /* send ICMP6 if HL == 0 */ + if (IP6H_HOPLIM(iphdr) == 0) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_time_exceeded(p, ICMP6_TE_HL); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + if (netif->mtu && (p->tot_len > netif->mtu)) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_packet_too_big(p, netif->mtu); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); + + /* transmit pbuf on chosen interface */ + netif->output_ip6(netif, p, ip6_current_dest_addr()); + IP6_STATS_INC(ip6.fw); + IP6_STATS_INC(ip6.xmit); + return; +} +#endif /* LWIP_IPV6_FORWARD */ + + +/** + * This function is called by the network interface device driver when + * an IPv6 packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip6_forward). + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IPv6 packet (p->payload points to IPv6 header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip6_input(struct pbuf *p, struct netif *inp) +{ + struct ip6_hdr *ip6hdr; + struct netif *netif; + u8_t nexth; + u16_t hlen; /* the current header length */ + u8_t i; +#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ + @todo + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP6_STATS_INC(ip6.recv); + + /* identify the IP header */ + ip6hdr = (struct ip6_hdr *)p->payload; + if (IP6H_V(ip6hdr) != 6) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n", + IP6H_V(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { + if (IP6_HLEN > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + IP6_HLEN, p->len)); + } + if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr)); + + /* copy IP addresses to aligned ip6_addr_t */ + ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest); + ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src); + + /* current header pointer. */ + ip_data.current_ip6_header = ip6hdr; + + /* In netif, used in case we need to send ICMPv6 packets back. */ + ip_data.current_netif = inp; + + /* match packet against an interface, i.e. is this packet for us? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* Always joined to multicast if-local and link-local all-nodes group. */ + if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || + ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { + netif = inp; + } +#if LWIP_IPV6_MLD + else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { + netif = inp; + } +#else /* LWIP_IPV6_MLD */ + else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { + /* Filter solicited node packets when MLD is not enabled + * (for Neighbor discovery). */ + netif = NULL; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + netif = inp; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + break; + } + } + } +#endif /* LWIP_IPV6_MLD */ + else { + netif = NULL; + } + } + else { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + /* interface is up? */ + if (netif_is_up(netif)) { + /* unicast to this interface address? address configured? */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { + /* exit outer loop */ + goto netif_found; + } + } + } + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + /* Do not match link-local addresses to other netifs. */ + netif = NULL; + break; + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); +netif_found: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", + netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); + } + + /* "::" packet source address? (used in duplicate address detection) */ + if (ip6_addr_isany(ip6_current_src_addr()) && + (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { + /* packet source is not valid */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it + isn't for a local address */ + if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IP6H_NEXTH(ip6hdr) == IP6_NEXTH_TCP) { + netif = inp; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); +#if LWIP_IPV6_FORWARD + /* non-multicast packet? */ + if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* try to forward IP packet on (other) interfaces */ + ip6_forward(p, ip6hdr, inp); + } +#endif /* LWIP_IPV6_FORWARD */ + pbuf_free(p); + goto ip6_input_cleanup; + } + + /* current netif pointer. */ + ip_data.current_netif = netif; + + /* Save next header type. */ + nexth = IP6H_NEXTH(ip6hdr); + + /* Init header length. */ + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + + /* Move to payload. */ + pbuf_header(p, -IP6_HLEN); + + /* Process known option extension headers, if present. */ + while (nexth != IP6_NEXTH_NONE) + { + switch (nexth) { + case IP6_NEXTH_HOPBYHOP: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_DESTOPTS: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_ROUTING: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + + case IP6_NEXTH_FRAGMENT: + { + struct ip6_frag_hdr * frag_hdr; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); + + frag_hdr = (struct ip6_frag_hdr *)p->payload; + + /* Get next header type. */ + nexth = frag_hdr->_nexth; + + /* Fragment Header length. */ + hlen = 8; + ip_data.current_ip_header_tot_len += hlen; + + /* Make sure this header fits in current pbuf. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_FRAG_STATS_INC(ip6_frag.lenerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto ip6_input_cleanup; + } + + /* Offset == 0 and more_fragments == 0? */ + if (((frag_hdr->_fragment_offset & IP6_FRAG_OFFSET_MASK) == 0) && + ((frag_hdr->_fragment_offset & IP6_FRAG_MORE_FLAG) == 0)) { + + /* This is a 1-fragment packet, usually a packet that we have + * already reassembled. Skip this header anc continue. */ + pbuf_header(p, -hlen); + } + else { +#if LWIP_IPV6_REASS + + /* reassemble the packet */ + p = ip6_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + goto ip6_input_cleanup; + } + + /* Returned p point to IPv6 header. + * Update all our variables and pointers and continue. */ + ip6hdr = (struct ip6_hdr *)p->payload; + nexth = IP6H_NEXTH(ip6hdr); + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + pbuf_header(p, -IP6_HLEN); + +#else /* LWIP_IPV6_REASS */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.opterr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; +#endif /* LWIP_IPV6_REASS */ + } + break; + } + default: + goto options_done; + break; + } + } +options_done: + + /* p points to IPv6 header again. */ + pbuf_header(p, ip_data.current_ip_header_tot_len); + + /* send to upper layers */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); + ip6_debug_print(p); + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + switch (nexth) { + case IP6_NEXTH_NONE: + pbuf_free(p); + break; +#if LWIP_UDP + case IP6_NEXTH_UDP: +#if LWIP_UDPLITE + case IP6_NEXTH_UDPLITE: +#endif /* LWIP_UDPLITE */ + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP6_NEXTH_TCP: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP6 + case IP6_NEXTH_ICMP6: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + icmp6_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP6 + /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ + if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && + (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { + icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); + } +#endif /* LWIP_ICMP */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.proterr); + IP6_STATS_INC(ip6.drop); + break; + } + } + +ip6_input_cleanup: + ip_data.current_netif = NULL; + ip_data.current_ip6_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip6_addr_set_any(&ip_data.current_iphdr_src.ip6); + ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6); + + return ERR_OK; +} + + +/** + * Sends an IPv6 packet on a network interface. This function constructs + * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is + * used as source (usually during network startup). If the source IPv6 address it + * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network + * interface is filled in as source address. If the destination IPv6 address is + * IP_HDRINCL, p is assumed to already include an IPv6 header and p->payload points + * to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IPv6/LINK headers + * returns errors returned by netif->output + */ +err_t +ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + struct ip6_hdr *ip6hdr; + ip6_addr_t dest_addr; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + /* Should the IPv6 header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + /* generate IPv6 header */ + if (pbuf_header(p, IP6_HLEN)) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + ip6hdr = (struct ip6_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", + (p->len >= sizeof(struct ip6_hdr))); + + IP6H_HOPLIM_SET(ip6hdr, hl); + IP6H_NEXTH_SET(ip6hdr, nexth); + + /* dest cannot be NULL here */ + ip6_addr_copy(ip6hdr->dest, *dest); + + IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); + IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); + + if (src == NULL) { + src = IP6_ADDR_ANY; + } + else if (ip6_addr_isany(src)) { + src = ip6_select_source_address(netif, dest); + if ((src == NULL) || ip6_addr_isany(src)) { + /* No appropriate source address was found for this packet. */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + } + /* src cannot be NULL here */ + ip6_addr_copy(ip6hdr->src, *src); + + } else { + /* IP header already included in p */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(dest_addr, ip6hdr->dest); + dest = &dest_addr; + } + + IP6_STATS_INC(ip6.xmit); + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip6_debug_print(p); + +#if ENABLE_LOOPBACK + /* TODO implement loopback for v6 + if (ip6_addr_cmp(dest, netif_ip6_addr(0))) { + return netif_loop_output(netif, p, dest); + }*/ +#endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV6_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { + return ip6_frag(p, netif, dest); + } +#endif /* LWIP_IPV6_FRAG */ + + LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()")); + return netif->output_ip6(netif, p, dest); +} + +/** + * Simple interface to ip6_output_if. It finds the outgoing network + * interface and calls upon ip6_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + + /* pbufs passed to IPv6 must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + return ip6_output_if(p, src, dest, hl, tc, nexth, netif); +} + + +#if LWIP_NETIF_HWADDRHINT +/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip6_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if LWIP_IPV6_MLD +/** + * Add a hop-by-hop options header with a router alert option and padding. + * + * Used by MLD when sending a Multicast listener report/done message. + * + * @param p the packet to which we will prepend the options header + * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6) + * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD) + * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise + */ +err_t +ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value) +{ + struct ip6_hbh_hdr * hbh_hdr; + + /* Move pointer to make room for hop-by-hop options header. */ + if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + hbh_hdr = (struct ip6_hbh_hdr *)p->payload; + + /* Set fields. */ + hbh_hdr->_nexth = nexth; + hbh_hdr->_hlen = 0; + hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION; + hbh_hdr->_ra_opt_dlen = 2; + hbh_hdr->_ra_opt_data = value; + hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION; + hbh_hdr->_padn_opt_dlen = 0; + + return ERR_OK; +} +#endif /* LWIP_IPV6_MLD */ + +#if IP6_DEBUG +/* Print an IPv6 header by using LWIP_DEBUGF + * @param p an IPv6 packet, p->payload pointing to the IPv6 header + */ +void +ip6_debug_print(struct pbuf *p) +{ + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + + LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n")); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n", + IP6H_V(ip6hdr), + IP6H_TC(ip6hdr), + IP6H_FL(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n", + IP6H_PLEN(ip6hdr), + IP6H_NEXTH(ip6hdr), + IP6H_HOPLIM(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->src)), + IP6_ADDR_BLOCK2(&(ip6hdr->src)), + IP6_ADDR_BLOCK3(&(ip6hdr->src)), + IP6_ADDR_BLOCK4(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->src)), + IP6_ADDR_BLOCK6(&(ip6hdr->src)), + IP6_ADDR_BLOCK7(&(ip6hdr->src)), + IP6_ADDR_BLOCK8(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->dest)), + IP6_ADDR_BLOCK2(&(ip6hdr->dest)), + IP6_ADDR_BLOCK3(&(ip6hdr->dest)), + IP6_ADDR_BLOCK4(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->dest)), + IP6_ADDR_BLOCK6(&(ip6hdr->dest)), + IP6_ADDR_BLOCK7(&(ip6hdr->dest)), + IP6_ADDR_BLOCK8(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP6_DEBUG */ + +#endif /* LWIP_IPV6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_addr.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_addr.c new file mode 100644 index 0000000..65d2798 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_addr.c @@ -0,0 +1,251 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Functions for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +/* used by IP6_ADDR_ANY in ip6_addr.h */ +const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } }; + +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) +#endif + +/** + * Check whether "cp" is a valid ascii representation + * of an IPv6 address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * + * @param cp IPv6 address in ascii represenation (e.g. "FF01::1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ip6addr_aton(const char *cp, ip6_addr_t *addr) +{ + u32_t addr_index, zero_blocks, current_block_index, current_block_value; + const char * s; + + /* Count the number of colons, to count the number of blocks in a "::" sequence + zero_blocks may be 1 even if there are no :: sequences */ + zero_blocks = 8; + for (s = cp; *s != 0; s++) { + if (*s == ':') + zero_blocks--; + else if (!isxdigit(*s)) + break; + } + + /* parse each block */ + addr_index = 0; + current_block_index = 0; + current_block_value = 0; + for (s = cp; *s != 0; s++) { + if (*s == ':') { + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + current_block_index++; + current_block_value = 0; + if (current_block_index > 7) { + /* address too long! */ + return 0; + } if (s[1] == ':') { + s++; + /* "::" found, set zeros */ + while (zero_blocks-- > 0) { + if (current_block_index & 0x1) { + addr_index++; + } + else { + if (addr) { + addr->addr[addr_index] = 0; + } + } + current_block_index++; + } + } + } else if (isxdigit(*s)) { + /* add current digit */ + current_block_value = (current_block_value << 4) + + (isdigit(*s) ? *s - '0' : + 10 + (islower(*s) ? *s - 'a' : *s - 'A')); + } else { + /* unexpected digit, space? CRLF? */ + break; + } + } + + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + + /* convert to network byte order. */ + if (addr) { + for (addr_index = 0; addr_index < 4; addr_index++) { + addr->addr[addr_index] = htonl(addr->addr[addr_index]); + } + } + + if (current_block_index != 7) { + return 0; + } + + return 1; +} + +/** + * Convert numeric IPv6 address into ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip6 address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ip6addr_ntoa(const ip6_addr_t *addr) +{ + static char str[40]; + return ip6addr_ntoa_r(addr, str, 40); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip6 address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char * +ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) +{ + u32_t current_block_index, current_block_value; + s32_t zero_flag, i; + + i = 0; + zero_flag = 0; /* used to indicate a zero chain for "::' */ + + for (current_block_index = 0; current_block_index < 8; current_block_index++) { + /* get the current 16-bit block */ + current_block_value = htonl(addr->addr[current_block_index >> 1]); + if ((current_block_index & 0x1) == 0) { + current_block_value = current_block_value >> 16; + } + current_block_value &= 0xffff; + + if (current_block_value == 0) { + /* generate empty block "::" */ + if (!zero_flag) { + if (current_block_index > 0) { + zero_flag = 1; + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + } + } + else { + if (current_block_index > 0) { + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + + if ((current_block_value & 0xf000) == 0) { + zero_flag = 1; + } + else { + buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf00) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf0) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + buf[i++] = xchar((current_block_value & 0xf)); + if (i >= buflen) return NULL; + + zero_flag = 0; + } + } + + buf[i] = 0; + + return buf; +} +#endif /* LWIP_IPV6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_frag.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_frag.c new file mode 100644 index 0000000..a43fd61 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/ip6_frag.c @@ -0,0 +1,697 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" +#include "lwip/ip6_frag.h" +#include "lwip/ip6.h" +#include "lwip/icmp6.h" +#include "lwip/nd6.h" + +#include "lwip/pbuf.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IPv6 header, since it replaces + * the Fragment Header in memory in incoming fragments to keep + * track of the various fragments. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* static variables */ +static struct ip6_reassdata *reassdatagrams; +static u16_t ip6_reass_pbufcount; + +/* Forward declarations. */ +static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr); +#if IP_REASS_FREE_OLDEST +static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed); +#endif /* IP_REASS_FREE_OLDEST */ + +void +ip6_reass_tmr(void) +{ + struct ip6_reassdata *r, *tmp; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + r = r->next; + } else { + /* reassembly timed out */ + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip6_reass_free_complete_datagram(tmp); + } + } +} + +/** + * Free a datagram (struct ip6_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), + * sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + */ +static void +ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) +{ + struct ip6_reassdata *prev; + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip6_reass_helper *iprh; + +#if LWIP_ICMP6 + iprh = (struct ip6_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, move back to the original header (we are now pointing to Fragment header). */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); + } + else { + icmp6_time_exceeded(p, ICMP6_TE_FRAG); + } + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP6 */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip6_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + + /* Then, unchain the struct ip6_reassdata from the list and free it. */ + if (ipr == reassdatagrams) { + reassdatagrams = ipr->next; + } else { + prev = reassdatagrams; + while (prev != NULL) { + if (prev->next == ipr) { + break; + } + prev = prev->next; + } + if (prev != NULL) { + prev->next = ipr->next; + } + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* Finally, update number of pbufs in reassembly queue */ + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); + ip6_reass_pbufcount -= pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram ipr is not freed! + * + * @param ipr ip6_reassdata for the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + */ +static void +ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed) +{ + struct ip6_reassdata *r, *oldest; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the current datagram! */ + do { + r = oldest = reassdatagrams; + while (r != NULL) { + if (r != ipr) { + if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + r = r->next; + } + if (oldest != NULL) { + ip6_reass_free_complete_datagram(oldest); + } + } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL)); +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Reassembles incoming IPv6 fragments into an IPv6 datagram. + * + * @param p points to the IPv6 Fragment Header + * @param len the length of the payload (after Fragment Header) + * @return NULL if reassembly is incomplete, pbuf pointing to + * IPv6 Header if reassembly is complete + */ +struct pbuf * +ip6_reass(struct pbuf *p) +{ + struct ip6_reassdata *ipr, *ipr_prev; + struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct ip6_frag_hdr * frag_hdr; + u16_t offset, len; + u8_t clen, valid = 1; + struct pbuf *q; + + IP6_FRAG_STATS_INC(ip6_frag.recv); + + frag_hdr = (struct ip6_frag_hdr *) p->payload; + + clen = pbuf_clen(p); + + offset = ntohs(frag_hdr->_fragment_offset); + + /* Calculate fragment length from IPv6 payload length. + * Adjust for headers before Fragment Header. + * And finally adjust by Fragment Header length. */ + len = ntohs(ip6_current_header()->_plen); + len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; + len -= IP6_FRAG_HLEN; + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if ((frag_hdr->_identification == ipr->identification) && + ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) && + ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) { + IP6_FRAG_STATS_INC(ip6_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + /* Make room and try again. */ + ip6_reass_remove_oldest_datagram(ipr, clen); + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + memset(ipr, 0, sizeof(struct ip6_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + + /* Use the current IPv6 header for src/dest address reference. + * Eventually, we will replace it when we get the first fragment + * (it might be this one, in any case, it is done later). */ + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + + /* copy the fragmented packet id. */ + ipr->identification = frag_hdr->_identification; + + /* copy the nexth field */ + ipr->nexth = frag_hdr->_nexth; + } + + /* Check if we are allowed to enqueue more datagrams. */ + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + ip6_reass_remove_oldest_datagram(ipr, clen); + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* @todo: send ICMPv6 time exceeded here? */ + /* drop this pbuf */ + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + /* Overwrite Fragment Header with our own helper struct. */ + iprh = (struct ip6_reass_helper *)p->payload; + iprh->next_pbuf = NULL; + iprh->start = (offset & IP6_FRAG_OFFSET_MASK); + iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; + + /* find the right place to insert this pbuf */ + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip6_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { +#if IP_REASS_CHECK_OVERLAP + if (iprh->end > iprh_tmp->start) { + /* fragment overlaps with following, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + if (iprh_prev != NULL) { + if (iprh->start < iprh_prev->end) { + /* fragment overlaps with previous, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } +#endif /* IP_REASS_CHECK_OVERLAP */ + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ + iprh_prev->next_pbuf = p; + } else { + /* fragment with the lowest offset */ + ipr->p = p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no gaps. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = p; + } + } + + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip6_reass_pbufcount += clen; + + /* Remember IPv6 header if this is the first fragment. */ + if (iprh->start == 0) { + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + } + + /* If this is the last fragment, calculate total packet length. */ + if ((offset & IP6_FRAG_MORE_FLAG) == 0) { + ipr->datagram_len = iprh->end; + } + + /* Additional validity tests: we have received first and last fragment. */ + iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; + if (iprh_tmp->start != 0) { + valid = 0; + } + if (ipr->datagram_len == 0) { + valid = 0; + } + + /* Final validity test: no gaps between current and last fragment. */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while ((q != NULL) && valid) { + iprh = (struct ip6_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + + if (valid) { + /* All fragments have been received */ + + /* chain together the pbufs contained within the ip6_reassdata list. */ + iprh = (struct ip6_reass_helper*) ipr->p->payload; + while(iprh != NULL) { + + if (iprh->next_pbuf != NULL) { + /* Save next helper struct (will be hidden in next step). */ + iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload; + + /* hide the fragment header for every succeding fragment */ + pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN); + pbuf_cat(ipr->p, iprh->next_pbuf); + } + else { + iprh_tmp = NULL; + } + + iprh = iprh_tmp; + } + + /* Adjust datagram length by adding header lengths. */ + ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr) + + IP6_FRAG_HLEN + - IP6_HLEN ; + + /* Set payload length in ip header. */ + ipr->iphdr->_plen = htons(ipr->datagram_len); + + /* Get the furst pbuf. */ + p = ipr->p; + + /* Restore Fragment Header in first pbuf. Mark as "single fragment" + * packet. Restore nexth. */ + frag_hdr = (struct ip6_frag_hdr *) p->payload; + frag_hdr->_nexth = ipr->nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = 0; + frag_hdr->_identification = 0; + + /* release the sources allocate for the fragment queue entry */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); + ipr_prev->next = ipr->next; + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* adjust the number of pbufs currently queued for reassembly. */ + ip6_reass_pbufcount -= pbuf_clen(p); + + /* Move pbuf back to IPv6 header. */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); + pbuf_free(p); + return NULL; + } + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + return NULL; + +nullreturn: + pbuf_free(p); + return NULL; +} + +#endif /* LWIP_IPV6 ^^ LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG + +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip6_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ip6_frag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip6_frag_free_pbuf_custom_ref(pcr); +} + +/** + * Fragment an IPv6 datagram if too large for the netif or path MTU. + * + * Chop the datagram in MTU sized chunks and send them in order + * by pointing PBUF_REFs into p + * + * @param p ipv6 packet to send + * @param netif the netif on which to send + * @param dest destination ipv6 address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest) +{ + struct ip6_hdr *original_ip6hdr; + struct ip6_hdr *ip6hdr; + struct ip6_frag_hdr * frag_hdr; + struct pbuf *rambuf; + struct pbuf *newpbuf; + static u32_t identification; + u16_t nfb; + u16_t left, cop; + u16_t mtu; + u16_t fragment_offset = 0; + u16_t last; + u16_t poff = IP6_HLEN; + u16_t newpbuflen = 0; + u16_t left_to_copy; + + identification++; + + original_ip6hdr = (struct ip6_hdr *)p->payload; + + mtu = nd6_get_destination_mtu(dest, netif); + + /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */ + left = p->tot_len - IP6_HLEN; + + nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK; + + while (left) { + last = (left <= nfb); + + /* Fill this fragment */ + cop = last ? left : nfb; + + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM); + if (rambuf == NULL) { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP6_HLEN + IP6_FRAG_HLEN))); + SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN); + ip6hdr = (struct ip6_hdr *)rambuf->payload; + frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN); + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + p->tot_len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip6_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip6_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; + + /* Set headers */ + frag_hdr->_nexth = original_ip6hdr->_nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)); + frag_hdr->_identification = htonl(identification); + + IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); + IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN); + + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + IP6_FRAG_STATS_INC(ip6_frag.xmit); + netif->output_ip6(netif, rambuf, dest); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); + left -= cop; + fragment_offset += cop; + } + return ERR_OK; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/mld6.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/mld6.c new file mode 100644 index 0000000..1cb2dd9 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/mld6.c @@ -0,0 +1,580 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +/* Based on igmp.c implementation of igmp v2 protocol */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mld6.h" +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + + +/* + * MLD constants + */ +#define MLD6_HL 1 +#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) + +#define MLD6_GROUP_NON_MEMBER 0 +#define MLD6_GROUP_DELAYING_MEMBER 1 +#define MLD6_GROUP_IDLE_MEMBER 2 + + +/* The list of joined groups. */ +static struct mld_group* mld_group_list; + + +/* Forward declarations. */ +static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr); +static err_t mld6_free_group(struct mld_group *group); +static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); +static void mld6_send(struct mld_group *group, u8_t type); + + +/** + * Stop MLD processing on interface + * + * @param netif network interface on which stop MLD processing + */ +err_t +mld6_stop(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + struct mld_group *prev = NULL; + struct mld_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == mld_group_list) { + mld_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report MLD memberships for this interface + * + * @param netif network interface on which report MLD memberships + */ +void +mld6_report_groups(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->netif == netif) { + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + group = group->next; + } +} + +/** + * Search for a group that is joined on a netif + * + * @param ifp the network interface for which to look + * @param addr the group ipv6 address to search for + * @return a struct mld_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct mld_group * +mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + return NULL; +} + + +/** + * create a new group + * + * @param ifp the network interface for which to create + * @param addr the new group ipv6 + * @return a struct mld_group*, + * NULL on memory error. + */ +static struct mld_group * +mld6_new_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group; + + group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); + if (group != NULL) { + group->netif = ifp; + ip6_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = mld_group_list; + + mld_group_list = group; + } + + return group; +} + +/** + * Remove a group in the mld_group_list and free + * + * @param group the group to remove + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +mld6_free_group(struct mld_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (mld_group_list == group) { + mld_group_list = group->next; + } else { + /* look for group further down the list */ + struct mld_group *tmpGroup; + for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not find group */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + + return err; +} + + +/** + * Process an input MLD message. Called by icmp6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +mld6_input(struct pbuf *p, struct netif *inp) +{ + struct mld_header * mld_hdr; + struct mld_group* group; + + MLD6_STATS_INC(mld6.recv); + + /* Check that mld header fits in packet. */ + if (p->len < sizeof(struct mld_header)) { + /* TODO debug message */ + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + MLD6_STATS_INC(mld6.drop); + return; + } + + mld_hdr = (struct mld_header *)p->payload; + + switch (mld_hdr->type) { + case ICMP6_TYPE_MLQ: /* Multicast listener query. */ + { + /* Is it a general query? */ + if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && + ip6_addr_isany(&(mld_hdr->multicast_address))) { + MLD6_STATS_INC(mld6.rx_general); + /* Report all groups, except all nodes group, and if-local groups. */ + group = mld_group_list; + while (group != NULL) { + if ((group->netif == inp) && + (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && + (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + group = group->next; + } + } + else { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_group); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* Schedule a report. */ + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + } + break; /* ICMP6_TYPE_MLQ */ + } + case ICMP6_TYPE_MLR: /* Multicast listener report. */ + { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_report); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* If we are waiting to report, cancel it. */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + group->timer = 0; /* stopped */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + } + break; /* ICMP6_TYPE_MLR */ + } + case ICMP6_TYPE_MLD: /* Multicast listener done. */ + { + /* Do nothing, router will query us. */ + break; /* ICMP6_TYPE_MLD */ + } + default: + MLD6_STATS_INC(mld6.proterr); + MLD6_STATS_INC(mld6.drop); + break; + } + + pbuf_free(p); +} + +/** + * Join a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * join a new group. If IP6_ADDR_ANY, join on all netifs + * @param groupaddr the ipv6 address of the group to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group or create a new one if not found */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group == NULL) { + /* Joining a new group. Create a new group entry. */ + group = mld6_new_group(netif, groupaddr); + if (group == NULL) { + return ERR_MEM; + } + + /* Activate this address on the MAC layer. */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER); + } + + /* Report our membership. */ + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + + /* Increment group use */ + group->use++; + err = ERR_OK; + } + + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * leave the group. If IP6_ISANY, leave on all netifs + * @param groupaddr the ipv6 address of the group to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Leave if there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + MLD6_STATS_INC(mld6.tx_leave); + mld6_send(group, ICMP6_TYPE_MLD); + } + + /* Disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER); + } + + /* Free the group */ + mld6_free_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + + +/** + * Periodic timer for mld processing. Must be called every + * MLD6_TMR_INTERVAL milliseconds (100). + * + * When a delaying member expires, a membership report is sent. + */ +void +mld6_tmr(void) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + group->group_state = MLD6_GROUP_IDLE_MEMBER; + } + } + } + group = group->next; + } +} + +/** + * Schedule a delayed membership report for a group + * + * @param group the mld_group for which "delaying" membership report + * should be sent + * @param maxresp the max resp delay provided in the query + */ +static void +mld6_delayed_report(struct mld_group *group, u16_t maxresp) +{ + /* Convert maxresp from milliseconds to tmr ticks */ + maxresp = maxresp / MLD6_TMR_INTERVAL; + if (maxresp == 0) { + maxresp = 1; + } + +#ifdef LWIP_RAND + /* Randomize maxresp. (if LWIP_RAND is supported) */ + maxresp = (LWIP_RAND() % (maxresp - 1)) + 1; +#endif /* LWIP_RAND */ + + /* Apply timer value if no report has been scheduled already. */ + if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || + ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + group->timer = maxresp; + group->group_state = MLD6_GROUP_DELAYING_MEMBER; + } +} + +/** + * Send a MLD message (report or done). + * + * An IPv6 hop-by-hop options header with a router alert option + * is prepended. + * + * @param group the group to report or quit + * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) + */ +static void +mld6_send(struct mld_group *group, u8_t type) +{ + struct mld_header * mld_hdr; + struct pbuf * p; + ip6_addr_t * src_addr; + + /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) { + /* We couldn't allocate a suitable pbuf. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + MLD6_STATS_INC(mld6.memerr); + return; + } + + /* Move to make room for Hop-by-hop options header. */ + if (pbuf_header(p, -IP6_HBH_HLEN)) { + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + return; + } + + /* Select our source address. */ + if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) { + /* This is a special case, when we are performing duplicate address detection. + * We must join the multicast group, but we don't have a valid address yet. */ + src_addr = IP6_ADDR_ANY; + } else { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(group->netif, 0); + } + + /* MLD message header pointer. */ + mld_hdr = (struct mld_header *)p->payload; + + /* Set fields. */ + mld_hdr->type = type; + mld_hdr->code = 0; + mld_hdr->chksum = 0; + mld_hdr->max_resp_delay = 0; + mld_hdr->reserved = 0; + ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); + + mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, + src_addr, &(group->group_address)); + + /* Add hop-by-hop headers options: router alert with MLD value. */ + ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); + + /* Send the packet out. */ + MLD6_STATS_INC(mld6.xmit); + ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), + MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif); + pbuf_free(p); +} + + + +#endif /* LWIP_IPV6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/ipv6/nd6.c b/service/src/main/jni/badvpn/lwip/src/core/ipv6/nd6.c new file mode 100644 index 0000000..480638e --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/ipv6/nd6.c @@ -0,0 +1,1787 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + + +/* Router tables. */ +struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; +struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; +struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; +struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; + +/* Default values, can be updated by a RA message. */ +u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; +u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* TODO implement this value in timer */ + +/* Index for cache entries. */ +static u8_t nd6_cached_neighbor_index; +static u8_t nd6_cached_destination_index; + +/* Multicast address holder. */ +static ip6_addr_t multicast_address; + +/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */ +static u8_t nd6_ra_buffer[sizeof(struct prefix_option)]; + +/* Forward declarations. */ +static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_neighbor_cache_entry(void); +static void nd6_free_neighbor_cache_entry(s8_t i); +static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_destination_cache_entry(void); +static s8_t nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif); +static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); +static s8_t nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); + +#define ND6_SEND_FLAG_MULTICAST_DEST 0x01 +#define ND6_SEND_FLAG_ALLNODES_DEST 0x02 +static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +static void nd6_send_rs(struct netif * netif); +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +#if LWIP_ND6_QUEUEING +static void nd6_free_q(struct nd6_q_entry *q); +#else /* LWIP_ND6_QUEUEING */ +#define nd6_free_q(q) pbuf_free(q) +#endif /* LWIP_ND6_QUEUEING */ +static void nd6_send_q(s8_t i); + + +/** + * Process an incoming neighbor discovery message + * + * @param p the nd packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +nd6_input(struct pbuf *p, struct netif *inp) +{ + u8_t msg_type; + s8_t i; + + ND6_STATS_INC(nd6.recv); + + msg_type = *((u8_t *)p->payload); + switch (msg_type) { + case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ + { + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that na header fits in packet. */ + if (p->len < (sizeof(struct na_header))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + na_hdr = (struct na_header *)p->payload; + + /* Unsolicited NA?*/ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* This is an unsolicited NA. + * link-layer changed? + * part of DAD mechanism? */ + + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* If the target address matches this netif, it is a DAD response. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + /* We are using a duplicate address. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + +#if LWIP_IPV6_MLD + /* Leave solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]); + mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address); +#endif /* LWIP_IPV6_MLD */ + + + + +#if LWIP_IPV6_AUTOCONFIG + /* Check to see if this address was autoconfigured. */ + if (!ip6_addr_islinklocal(ip6_current_dest_addr())) { + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i >= 0) { + /* Mark this prefix as duplicate, so that we don't use it + * to generate this address again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE; + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + + pbuf_free(p); + return; + } + } +#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ + + /* This is an unsolicited NA, most likely there was a LLADDR change. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i >= 0) { + if (na_hdr->flags & ND6_FLAG_OVERRIDE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + } + } + else { + /* This is a solicited NA. + * neighbor address resolution response? + * neighbor unreachability detection response? */ + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + + /* Find the cache entry corresponding to this na. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i < 0) { + /* We no longer care about this target address. drop it. */ + pbuf_free(p); + return; + } + + /* Update cache entry. */ + neighbor_cache[i].netif = inp; + neighbor_cache[i].counter.reachable_time = reachable_time; + if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || + (neighbor_cache[i].state == ND6_INCOMPLETE)) { + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + neighbor_cache[i].state = ND6_REACHABLE; + + /* Send queued packets, if any. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + } + + break; /* ICMP6_TYPE_NA */ + } + case ICMP6_TYPE_NS: /* Neighbor solicitation. */ + { + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + u8_t accepted; + + /* Check that ns header fits in packet. */ + if (p->len < sizeof(struct ns_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ns_hdr = (struct ns_header *)p->payload; + + /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ + lladdr_opt = NULL; + if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + } + + /* Check if the target address is configured on the receiving netif. */ + accepted = 0; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || + (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && + ip6_addr_isany(ip6_current_src_addr()))) && + ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + accepted = 1; + break; + } + } + + /* NS not for us? */ + if (!accepted) { + pbuf_free(p); + return; + } + + /* Check for ANY address in src (DAD algorithm). */ + if (ip6_addr_isany(ip6_current_src_addr())) { + /* Sender is validating this address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + /* Send a NA back so that the sender does not use this address. */ + nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); + if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { + /* We shouldn't use this address either. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + } + } + } + } + else { + /* Sender is trying to resolve our address. */ + /* Verify that they included their own link-layer address. */ + if (lladdr_opt == NULL) { + /* Not a valid message. */ + pbuf_free(p); + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + return; + } + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if ( i>= 0) { + /* We already have a record for the solicitor. */ + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + + /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + else + { + /* Add their IPv6 address and link-layer address to neighbor cache. + * We will need it at least to send a unicast NA message, but most + * likely we will also be communicating with this node soon. */ + i = nd6_new_neighbor_cache_entry(); + if (i < 0) { + /* We couldn't assign a cache entry for this neighbor. + * we won't be able to reply. drop it. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address)); + + /* Send back a NA for us. Allocate the reply pbuf. */ + nd6_send_na(inp, ip6_current_dest_addr(), ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); + } + + break; /* ICMP6_TYPE_NS */ + } + case ICMP6_TYPE_RA: /* Router Advertisement. */ + { + struct ra_header * ra_hdr; + u8_t * buffer; /* Used to copy options. */ + u16_t offset; + + /* Check that RA header fits in packet. */ + if (p->len < sizeof(struct ra_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ra_hdr = (struct ra_header *)p->payload; + + /* If we are sending RS messages, stop. */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + inp->rs_count = 0; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + /* Get the matching default router entry. */ + i = nd6_get_router(ip6_current_src_addr(), inp); + if (i < 0) { + /* Create a new router entry. */ + i = nd6_new_router(ip6_current_src_addr(), inp); + } + + if (i < 0) { + /* Could not create a new router entry. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Re-set invalidation timer. */ + default_router_list[i].invalidation_timer = ra_hdr->router_lifetime; + + /* Re-set default timer values. */ +#if LWIP_ND6_ALLOW_RA_UPDATES + if (ra_hdr->retrans_timer > 0) { + retrans_timer = ra_hdr->retrans_timer; + } + if (ra_hdr->reachable_time > 0) { + reachable_time = ra_hdr->reachable_time; + } +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + + /* TODO set default hop limit... */ + /* ra_hdr->current_hop_limit;*/ + + /* Update flags in local entry (incl. preference). */ + default_router_list[i].flags = ra_hdr->flags; + + /* Offset to options. */ + offset = sizeof(struct ra_header); + + /* Process each option. */ + while ((p->tot_len - offset) > 0) { + if (p->len == p->tot_len) { + /* no need to copy from contiguous pbuf */ + buffer = &((u8_t*)p->payload)[offset]; + } else { + buffer = nd6_ra_buffer; + pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset); + } + switch (buffer[0]) { + case ND6_OPTION_TYPE_SOURCE_LLADDR: + { + struct lladdr_option * lladdr_opt; + lladdr_opt = (struct lladdr_option *)buffer; + if ((default_router_list[i].neighbor_entry != NULL) && + (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { + SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); + default_router_list[i].neighbor_entry->state = ND6_REACHABLE; + default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; + } + break; + } + case ND6_OPTION_TYPE_MTU: + { + struct mtu_option * mtu_opt; + mtu_opt = (struct mtu_option *)buffer; + if (mtu_opt->mtu >= 1280) { +#if LWIP_ND6_ALLOW_RA_UPDATES + inp->mtu = mtu_opt->mtu; +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + } + break; + } + case ND6_OPTION_TYPE_PREFIX_INFO: + { + struct prefix_option * prefix_opt; + prefix_opt = (struct prefix_option *)buffer; + + if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) { + /* Add to on-link prefix list. */ + + /* Get a memory-aligned copy of the prefix. */ + ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix)); + + /* find cache entry for this prefix. */ + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i < 0) { + /* Create a new cache entry. */ + i = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp); + } + if (i >= 0) { + prefix_list[i].invalidation_timer = prefix_opt->valid_lifetime; + +#if LWIP_IPV6_AUTOCONFIG + if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { + /* Mark prefix as autonomous, so that address autoconfiguration can take place. + * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS; + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + break; + } + case ND6_OPTION_TYPE_ROUTE_INFO: + { + /* TODO implement preferred routes. + struct route_option * route_opt; + route_opt = (struct route_option *)buffer;*/ + + break; + } + default: + /* Unrecognized option, abort. */ + ND6_STATS_INC(nd6.proterr); + break; + } + offset += 8 * ((u16_t)buffer[1]); + } + + break; /* ICMP6_TYPE_RA */ + } + case ICMP6_TYPE_RD: /* Redirect */ + { + struct redirect_header * redir_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that Redir header fits in packet. */ + if (p->len < sizeof(struct redirect_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + redir_hdr = (struct redirect_header *)p->payload; + + lladdr_opt = NULL; + if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); + } + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address)); + + /* Find dest address in cache */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Set the new target address. */ + ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); + + /* If Link-layer address of other router is given, try to add to neighbor cache. */ + if (lladdr_opt != NULL) { + if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { + /* Copy target address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address)); + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if (i < 0) { + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + if (i >= 0) { + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + } + } + break; /* ICMP6_TYPE_RD */ + } + case ICMP6_TYPE_PTB: /* Packet too big */ + { + struct icmp6_hdr *icmp6hdr; /* Packet too big message */ + struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */ + + /* Check that ICMPv6 header + IPv6 header fit in payload */ + if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { + /* drop short packets */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest)); + + /* Look for entry in destination cache. */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Change the Path MTU. */ + destination_cache[i].pmtu = icmp6hdr->data; + + break; /* ICMP6_TYPE_PTB */ + } + + default: + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + break; /* default */ + } + + pbuf_free(p); +} + + +/** + * Periodic timer for Neighbor discovery functions: + * + * - Update neighbor reachability states + * - Update destination cache entries age + * - Update invalidation timers of default routers and on-link prefixes + * - Perform duplicate address detection (DAD) for our addresses + * - Send router solicitations + */ +void +nd6_tmr(void) +{ + s8_t i, j; + struct netif * netif; + + /* Process neighbor entries. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + switch (neighbor_cache[i].state) { + case ND6_INCOMPLETE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST); + } + break; + case ND6_REACHABLE: + /* Send queued packets, if any are left. Should have been sent already. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { + /* Change to stale state. */ + neighbor_cache[i].state = ND6_STALE; + neighbor_cache[i].counter.stale_time = 0; + } + else { + neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_STALE: + neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL; + break; + case ND6_DELAY: + if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) { + /* Change to PROBE state. */ + neighbor_cache[i].state = ND6_PROBE; + neighbor_cache[i].counter.probes_sent = 0; + } + else { + neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_PROBE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0); + } + break; + case ND6_NO_ENTRY: + default: + /* Do nothing. */ + break; + } + } + + /* Process destination entries. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + destination_cache[i].age++; + } + + /* Process router entries. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (default_router_list[i].neighbor_entry != NULL) { + /* Active entry. */ + if (default_router_list[i].invalidation_timer > 0) { + default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + } + if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + /* Less than 1 second remainig. Clear this entry. */ + default_router_list[i].neighbor_entry->isrouter = 0; + default_router_list[i].neighbor_entry = NULL; + default_router_list[i].invalidation_timer = 0; + default_router_list[i].flags = 0; + } + } + } + + /* Process prefix entries. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + prefix_list[i].invalidation_timer = 0; + } + if ((prefix_list[i].invalidation_timer > 0) && + (prefix_list[i].netif != NULL)) { + prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + +#if LWIP_IPV6_AUTOCONFIG + /* Initiate address autoconfiguration for this prefix, if conditions are met. */ + if (prefix_list[i].netif->ip6_autoconfig_enabled && + (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) && + !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) { + /* Try to get an address on this netif that is invalid. + * Skip 0 index (link-local address) */ + for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { + if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDRESS_STATE_INVALID) { + /* Generate an address using this prefix and interface ID from link-local address. */ + prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0]; + prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1]; + prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2]; + prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3]; + + /* Mark it as tentative (DAD will be performed if configured). */ + netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE); + + /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED; + + /* Exit loop. */ + break; + } + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + + /* Process our own addresses, if DAD configured. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_istentative(netif->ip6_addr_state[i])) { + if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { + /* No NA received in response. Mark address as valid. */ + netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED; + /* TODO implement preferred and valid lifetimes. */ + } + else if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_IPV6_MLD + if ((netif->ip6_addr_state[i] & 0x07) == 0) { + /* Join solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]); + mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address); + } +#endif /* LWIP_IPV6_MLD */ + /* Send a NS for this address. */ + nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST); + (netif->ip6_addr_state[i])++; + /* TODO send max 1 NS per tmr call? enable return*/ + /*return;*/ + } + } + } + } + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send router solicitation messages, if necessary. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) { + nd6_send_rs(netif); + netif->rs_count--; + } + } +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +} + +/** + * Send a neighbor solicitation message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + + if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(netif, 0); + } else { + src_addr = IP6_ADDR_ANY; + } + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + ns_hdr = (struct ns_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + + ns_hdr->type = ICMP6_TYPE_NS; + ns_hdr->code = 0; + ns_hdr->chksum = 0; + ns_hdr->reserved = 0; + ip6_addr_set(&(ns_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + target_addr = &multicast_address; + } + + ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + target_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, (src_addr == IP6_ADDR_ANY) ? NULL : src_addr, target_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +/** + * Send a neighbor advertisement message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + ip6_addr_t * dest_addr; + + /* Use link-local address as source address. */ + /* src_addr = &(netif->ip6_addr[0]); */ + /* Use target address as source address. */ + src_addr = target_addr; + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + na_hdr = (struct na_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + na_hdr->type = ICMP6_TYPE_NA; + na_hdr->code = 0; + na_hdr->chksum = 0; + na_hdr->flags = flags & 0xf0; + na_hdr->reserved[0] = 0; + na_hdr->reserved[1] = 0; + na_hdr->reserved[2] = 0; + ip6_addr_set(&(na_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + dest_addr = &multicast_address; + } + else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { + ip6_addr_set_allnodes_linklocal(&multicast_address); + dest_addr = &multicast_address; + } + else { + dest_addr = ip6_current_src_addr(); + } + + na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + dest_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, dest_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +/** + * Send a router solicitation message + * + * @param netif the netif on which to send the message + */ +static void +nd6_send_rs(struct netif * netif) +{ + struct rs_header * rs_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + u16_t packet_len; + + /* Link-local source address, or unspecified address? */ + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { + src_addr = netif_ip6_addr(netif, 0); + } + else { + src_addr = IP6_ADDR_ANY; + } + + /* Generate the all routers target address. */ + ip6_addr_set_allrouters_linklocal(&multicast_address); + + /* Allocate a packet. */ + packet_len = sizeof(struct rs_header); + if (src_addr != IP6_ADDR_ANY) { + packet_len += sizeof(struct lladdr_option); + } + p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM); + if ((p == NULL) || (p->len < packet_len)) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + rs_hdr = (struct rs_header *)p->payload; + + rs_hdr->type = ICMP6_TYPE_RS; + rs_hdr->code = 0; + rs_hdr->chksum = 0; + rs_hdr->reserved = 0; + + if (src_addr != IP6_ADDR_ANY) { + /* Include our hw address. */ + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + } + + rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + &multicast_address); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, &multicast_address, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +/** + * Search for a neighbor cache entry + * + * @param ip6addr the IPv6 address of the neighbor + * @return The neighbor cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { + return i; + } + } + return -1; +} + +/** + * Create a new neighbor cache entry. + * + * If no unused entry is found, will try to recycle an old entry + * according to ad-hoc "age" heuristic. + * + * @return The neighbor cache entry index that was created, -1 if no + * entry could be created + */ +static s8_t +nd6_new_neighbor_cache_entry(void) +{ + s8_t i; + s8_t j; + u32_t time; + + + /* First, try to find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (neighbor_cache[i].state == ND6_NO_ENTRY) { + return i; + } + } + + /* We need to recycle an entry. in general, do not recycle if it is a router. */ + + /* Next, try to find a Stale entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_STALE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Probe entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_PROBE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Delayed entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_DELAY) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find the oldest reachable entry. */ + time = 0xfffffffful; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_REACHABLE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.reachable_time < time) { + j = i; + time = neighbor_cache[i].counter.reachable_time; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry without queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ( + (neighbor_cache[i].q == NULL) && + (neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry with queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* No more entries to try. */ + return -1; +} + +/** + * Will free any resources associated with a neighbor cache + * entry, and will mark it as unused. + * + * @param i the neighbor cache entry index to free + */ +static void +nd6_free_neighbor_cache_entry(s8_t i) +{ + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + + /* Free any queued packets. */ + if (neighbor_cache[i].q != NULL) { + nd6_free_q(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } + + neighbor_cache[i].state = ND6_NO_ENTRY; + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = NULL; + neighbor_cache[i].counter.reachable_time = 0; + ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); +} + +/** + * Search for a destination cache entry + * + * @param ip6addr the IPv6 address of the destination + * @return The destination cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_destination_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { + return i; + } + } + return -1; +} + +/** + * Create a new destination cache entry. If no unused entry is found, + * will recycle oldest entry. + * + * @return The destination cache entry index that was created, -1 if no + * entry was created + */ +static s8_t +nd6_new_destination_cache_entry(void) +{ + s8_t i, j; + u32_t age; + + /* Find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { + return i; + } + } + + /* Find oldest entry. */ + age = 0; + j = LWIP_ND6_NUM_DESTINATIONS - 1; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (destination_cache[i].age > age) { + j = i; + } + } + + return j; +} + +/** + * Determine whether an address matches an on-link prefix. + * + * @param ip6addr the IPv6 address to match + * @return 1 if the address is on-link, 0 otherwise + */ +static s8_t +nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if ((prefix_list[i].netif == netif) && + (prefix_list[i].invalidation_timer > 0) && + ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { + return 1; + } + } + /* Check to see if address prefix matches a (manually?) configured address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { + return 1; + } + } + return 0; +} + +/** + * Select a default router for a destination. + * + * @param ip6addr the destination address + * @param netif the netif for the outgoing packet, if known + * @return the default router entry index, or -1 if no suitable + * router is found + */ +s8_t +nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + /* last_router is used for round-robin router selection (as recommended + * in RFC). This is more robust in case one router is not reachable, + * we are not stuck trying to resolve it. */ + static s8_t last_router; + (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ + + /* TODO: implement default router preference */ + + /* Look for reachable routers. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0) && + (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) { + return i; + } + } + + /* Look for router in other reachability states, but still valid according to timer. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0)) { + return i; + } + } + + /* Look for any router for which we have any information at all. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if (default_router_list[i].neighbor_entry != NULL && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) { + return i; + } + } + + /* no suitable router found. */ + return -1; +} + +/** + * Find an entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is found, if known + * @return the index of the router entry, or -1 if not found + */ +static s8_t +nd6_get_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t i; + + /* Look for router. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if ((default_router_list[i].neighbor_entry != NULL) && + ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && + ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { + return i; + } + } + + /* router not found. */ + return -1; +} + +/** + * Create a new entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is connected, if known + * @return the index on the router table, or -1 if could not be created + */ +static s8_t +nd6_new_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t router_index; + s8_t neighbor_index; + + /* Do we have a neighbor entry for this router? */ + neighbor_index = nd6_find_neighbor_cache_entry(router_addr); + if (neighbor_index < 0) { + /* Create a neighbor entry for this router. */ + neighbor_index = nd6_new_neighbor_cache_entry(); + if (neighbor_index < 0) { + /* Could not create neighbor entry for this router. */ + return -1; + } + ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); + neighbor_cache[neighbor_index].netif = netif; + neighbor_cache[neighbor_index].q = NULL; + neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; + neighbor_cache[neighbor_index].counter.probes_sent = 0; + } + + /* Mark neighbor as router. */ + neighbor_cache[neighbor_index].isrouter = 1; + + /* Look for empty entry. */ + for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) { + if (default_router_list[router_index].neighbor_entry == NULL) { + default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]); + return router_index; + } + } + + /* Could not create a router entry. */ + + /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ + neighbor_cache[neighbor_index].isrouter = 0; + + /* router not found. */ + return -1; +} + +/** + * Find the cached entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not found + */ +static s8_t +nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Look for prefix in list. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && + (prefix_list[i].netif == netif)) { + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Creates a new entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not created + */ +static s8_t +nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Create new entry. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((prefix_list[i].netif == NULL) || + (prefix_list[i].invalidation_timer == 0)) { + /* Found empty prefix entry. */ + prefix_list[i].netif = netif; + ip6_addr_set(&(prefix_list[i].prefix), prefix); +#if LWIP_IPV6_AUTOCONFIG + prefix_list[i].flags = 0; +#endif + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Determine the next hop for a destination. Will determine if the + * destination is on-link, else a suitable on-link router is selected. + * + * The last entry index is cached for fast entry search. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the neighbor cache entry for the next hop, ERR_RTE if no + * suitable next hop was found, ERR_MEM if no cache entry + * could be created + */ +s8_t +nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t addr_hint = *(netif->addr_hint); + if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { + nd6_cached_destination_index = addr_hint; + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look for ip6addr in destination cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + /* the cached entry index is the right one! */ + /* do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + /* Search destination cache. */ + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + /* found destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } + else { + /* Not found. Create a new destination entry. */ + i = nd6_new_destination_cache_entry(); + if (i >= 0) { + /* got new destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } else { + /* Could not create a destination cache entry. */ + return ERR_MEM; + } + + /* Copy dest address to destination cache. */ + ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); + + /* Now find the next hop. is it a neighbor? */ + if (ip6_addr_islinklocal(ip6addr) || + nd6_is_prefix_in_netif(ip6addr, netif)) { + /* Destination in local link. */ + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr); + } + else { + /* We need to select a router. */ + i = nd6_select_router(ip6addr, netif); + if (i < 0) { + /* No router found. */ + ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); + return ERR_RTE; + } + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); + } + } + } + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + *(netif->addr_hint) = nd6_cached_destination_index; + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look in neighbor cache for the next-hop address. */ + if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), + &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + /* Cache hit. */ + /* Do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); + if (i >= 0) { + /* Found a matching record, make it new cached entry. */ + nd6_cached_neighbor_index = i; + } + else { + /* Neighbor not in cache. Make a new entry. */ + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + /* got new neighbor entry. make it our new cached index. */ + nd6_cached_neighbor_index = i; + } else { + /* Could not create a neighbor cache entry. */ + return ERR_MEM; + } + + /* Initialize fields. */ + ip6_addr_copy(neighbor_cache[i].next_hop_address, + destination_cache[nd6_cached_destination_index].next_hop_addr); + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = netif; + neighbor_cache[i].state = ND6_INCOMPLETE; + neighbor_cache[i].counter.probes_sent = 0; + } + } + + /* Reset this destination's age. */ + destination_cache[nd6_cached_destination_index].age = 0; + + return nd6_cached_neighbor_index; +} + +/** + * Queue a packet for a neighbor. + * + * @param neighbor_index the index in the neighbor cache table + * @param q packet to be queued + * @return ERR_OK if succeeded, ERR_MEM if out of memory + */ +err_t +nd6_queue_packet(s8_t neighbor_index, struct pbuf * q) +{ + err_t result = ERR_MEM; + struct pbuf *p; + int copy_needed = 0; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *new_entry, *r; +#endif /* LWIP_ND6_QUEUEING */ + + if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { + return ERR_ARG; + } + + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ +#if LWIP_ND6_QUEUEING + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); +#else /* LWIP_ND6_QUEUEING */ + pbuf_free(neighbor_cache[neighbor_index].q); + neighbor_cache[neighbor_index].q = NULL; +#endif /* LWIP_ND6_QUEUEING */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + } + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet was copied/ref'd? */ + if (p != NULL) { + /* queue packet ... */ +#if LWIP_ND6_QUEUEING + /* allocate a new nd6 queue entry */ + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + } + if (new_entry != NULL) { + new_entry->next = NULL; + new_entry->p = p; + if(neighbor_cache[neighbor_index].q != NULL) { + /* queue was already existent, append the new entry to the end */ + r = neighbor_cache[neighbor_index].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + neighbor_cache[neighbor_index].q = new_entry; + } + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; + } else { + /* the pool MEMP_ND6_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); + /* { result == ERR_MEM } through initialization */ + } +#else /* LWIP_ND6_QUEUEING */ + /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ + if (neighbor_cache[neighbor_index].q != NULL) { + pbuf_free(neighbor_cache[neighbor_index].q); + } + neighbor_cache[neighbor_index].q = p; + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; +#endif /* LWIP_ND6_QUEUEING */ + } else { + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } + + return result; +} + +#if LWIP_ND6_QUEUEING +/** + * Free a complete queue of nd6 q entries + * + * @param q a queue of nd6_q_entry to free + */ +static void +nd6_free_q(struct nd6_q_entry *q) +{ + struct nd6_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ND6_QUEUE, r); + } +} +#endif /* LWIP_ND6_QUEUEING */ + +/** + * Send queued packets for a neighbor + * + * @param i the neighbor to send packets to + */ +static void +nd6_send_q(s8_t i) +{ + struct ip6_hdr *ip6hdr; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *q; +#endif /* LWIP_ND6_QUEUEING */ + + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + +#if LWIP_ND6_QUEUEING + while (neighbor_cache[i].q != NULL) { + /* remember first in queue */ + q = neighbor_cache[i].q; + /* pop first item off the queue */ + neighbor_cache[i].q = q->next; + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(q->p->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(q->p); + /* now queue entry can be freed */ + memp_free(MEMP_ND6_QUEUE, q); + } +#else /* LWIP_ND6_QUEUEING */ + if (neighbor_cache[i].q != NULL) { + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } +#endif /* LWIP_ND6_QUEUEING */ +} + + +/** + * Get the Path MTU for a destination. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the Path MTU, if known, or the netif default MTU + */ +u16_t +nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + if (destination_cache[i].pmtu > 0) { + return destination_cache[i].pmtu; + } + } + + if (netif != NULL) { + return netif->mtu; + } + + return 1280; /* Minimum MTU */ +} + + +#if LWIP_ND6_TCP_REACHABILITY_HINTS +/** + * Provide the Neighbor discovery process with a hint that a + * destination is reachable. Called by tcp_receive when ACKs are + * received or sent (as per RFC). This is useful to avoid sending + * NS messages every 30 seconds. + * + * @param ip6addr the destination address which is know to be reachable + * by an upper layer protocol (TCP) + */ +void +nd6_reachability_hint(ip6_addr_t * ip6addr) +{ + s8_t i; + + /* Find destination in cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + i = nd6_cached_destination_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_destination_cache_entry(ip6addr); + } + if (i < 0) { + return; + } + + /* Find next hop neighbor in cache. */ + if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + i = nd6_cached_neighbor_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); + } + if (i < 0) { + return; + } + + /* Set reachability state. */ + neighbor_cache[i].state = ND6_REACHABLE; + neighbor_cache[i].counter.reachable_time = reachable_time; +} +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#endif /* LWIP_IPV6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/mem.c b/service/src/main/jni/badvpn/lwip/src/core/mem.c new file mode 100644 index 0000000..1659a2c --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/mem.c @@ -0,0 +1,659 @@ +/** + * @file + * Dynamic memory manager + * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/err.h" + +#include + +#if MEM_USE_POOLS +/* lwIP head implemented with different sized pools */ + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + void *ret; + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { +#if MEM_USE_POOLS_TRY_BIGGER_POOL +again: +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + /* is this pool big enough to hold an element of the required size + plus a struct memp_malloc_helper that saves the pool this element came from? */ + if (required_size <= memp_sizes[poolnr]) { + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + return NULL; + } + element = (struct memp_malloc_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already + taken care of in memp.c */ +#if MEM_USE_POOLS_TRY_BIGGER_POOL + /** Try a bigger pool if this one is empty! */ + if (poolnr < MEMP_POOL_LAST) { + poolnr++; + goto again; + } +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + return NULL; + } + + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct memp_malloc_helper */ + ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + return ret; +} + +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct memp_malloc_helper *hmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + + /* get the original struct memp_malloc_helper */ + hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. + */ +struct mem { + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[prev]) of the previous struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; +}; + +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** If you want to relocate the heap to external memory, simply define + * LWIP_RAM_HEAP_POINTER as a void-pointer to that location. + * If so, make sure the memory at that location is big enough (see below on + * how that space is calculated). */ +#ifndef LWIP_RAM_HEAP_POINTER +/** the heap. we need one struct mem at the end and some room for alignment */ +u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +#define LWIP_RAM_HEAP_POINTER ram_heap +#endif /* LWIP_RAM_HEAP_POINTER */ + +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ +static struct mem *ram_end; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; + +/** concurrent access protection */ +#if !NO_SYS +static sys_mutex_t mem_mutex; +#endif + +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex) +#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_trim() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + + nmem = (struct mem *)(void *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram); + } + + /* plug hole backward */ + pmem = (struct mem *)(void *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram); + } +} + +/** + * Zero the heap and initialize start, end and lowest-free + */ +void +mem_init(void) +{ + struct mem *mem; + + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER); + /* initialize the start of the heap */ + mem = (struct mem *)(void *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)(void *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); + + if(sys_mutex_new(&mem_mutex) != ERR_OK) { + LWIP_ASSERT("failed to create mem_mutex", 0); + } +} + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n")); + return; + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return; + } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + LWIP_ASSERT("mem_free: mem->used", mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + + MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram))); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); +} + +/** + * Shrink memory returned by mem_malloc(). + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ +void * +mem_trim(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if(newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; + } + + if (newsize > MEM_SIZE_ALIGNED) { + return NULL; + } + + LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (mem_size_t)((u8_t *)mem - ram); + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + mem2 = (struct mem *)(void *)&ram[mem->next]; + if(mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)(void *)&ram[ptr2]; + } + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)(void *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* the original mem->next is used, so no need to plug holes! */ + } + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); + return rmem; +} + +/** + * Adam's mem_malloc() plus solution for bug #17922 + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + size = LWIP_MEM_ALIGN_SIZE(size); + + if(size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; + } + + if (size > MEM_SIZE_ALIGNED) { + return NULL; + } + + /* protect the heap from concurrent access */ + sys_mutex_lock(&mem_mutex); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc or mem_trim */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)(void *)&ram[ptr])->next) { + mem = (struct mem *)(void *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free or mem_trim to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem. */ + local_mem_free_count = 1; + break; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +mem_malloc_adjust_lfree: +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + if (mem == lfree) { + struct mem *cur = lfree; + /* Find next free block after mem and update lowest free pointer */ + while (cur->used && cur != ram_end) { +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem or lfree. */ + goto mem_malloc_adjust_lfree; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + cur = (struct mem *)(void *)&ram[cur->next]; + } + lfree = cur; + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while(local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + return NULL; +} + +#endif /* MEM_USE_POOLS */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + memset(p, 0, count * size); + } + return p; +} + +#endif /* !MEM_LIBC_MALLOC */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/memp.c b/service/src/main/jni/badvpn/lwip/src/core/memp.c new file mode 100644 index 0000000..1323463 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/memp.c @@ -0,0 +1,485 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/tcp_impl.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.h" +#include "lwip/snmp_structs.h" +#include "lwip/snmp_msg.h" +#include "lwip/dns.h" +#include "netif/ppp_oe.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" + +#include + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +struct memp { + struct memp *next; +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ +}; + +#if MEMP_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ + +/** This array holds the first free element of each pool. + * Elements form a linked list. */ +static struct memp *memp_tab[MEMP_MAX]; + +#else /* MEMP_MEM_MALLOC */ + +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_MEM_MALLOC */ + +/** This array holds the element sizes of each pool. */ +#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC +static +#endif +const u16_t memp_sizes[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +/** This array holds the number of elements in each pool. */ +static const u16_t memp_num[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (num), +#include "lwip/memp_std.h" +}; + +/** This array holds a textual description of each pool. */ +#ifdef LWIP_DEBUG +static const char *memp_desc[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (desc), +#include "lwip/memp_std.h" +}; +#endif /* LWIP_DEBUG */ + +#if MEMP_SEPARATE_POOLS + +/** This creates each memory pool. These are named memp_memory_XXX_base (where + * XXX is the name of the pool defined in memp_std.h). + * To relocate a pool, declare it as extern in cc.h. Example for GCC: + * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[]; + */ +#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \ + [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; +#include "lwip/memp_std.h" + +/** This array holds the base of each memory pool. */ +static u8_t *const memp_bases[] = { +#define LWIP_MEMPOOL(name,num,size,desc) memp_memory_ ## name ## _base, +#include "lwip/memp_std.h" +}; + +#else /* MEMP_SEPARATE_POOLS */ + +/** This is the actual memory used by the pools (all pools in one big block). */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; + +#endif /* MEMP_SEPARATE_POOLS */ + +#if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm". + */ +static int +memp_sanity(void) +{ + s16_t i; + struct memp *t, *h; + + for (i = 0; i < MEMP_MAX; i++) { + t = memp_tab[i]; + if(t != NULL) { + for (h = t->next; (t != NULL) && (h != NULL); t = t->next, + h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) { + if (t == h) { + return 0; + } + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +#if defined(LWIP_DEBUG) && MEMP_STATS +static const char * memp_overflow_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) "/"desc, +#include "lwip/memp_std.h" + }; +#endif + +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void +memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type]; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp overflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Check if a memp element was victim of an underflow + * (e.g. the restricted area before it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void +memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp underflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_overflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_underflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} + +/** + * Initialize the restricted areas of all memp elements in every pool. + */ +static void +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[i]; + memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize this module. + * + * Carves out memp_memory into linked lists for each pool-type. + */ +void +memp_init(void) +{ + struct memp *memp; + u16_t i, j; + + for (i = 0; i < MEMP_MAX; ++i) { + MEMP_STATS_AVAIL(used, i, 0); + MEMP_STATS_AVAIL(max, i, 0); + MEMP_STATS_AVAIL(err, i, 0); + MEMP_STATS_AVAIL(avail, i, memp_num[i]); + } + +#if !MEMP_SEPARATE_POOLS + memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; +#if MEMP_SEPARATE_POOLS + memp = (struct memp*)memp_bases[i]; +#endif /* MEMP_SEPARATE_POOLS */ + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = memp_tab[i]; + memp_tab[i] = memp; + memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i] +#if MEMP_OVERFLOW_CHECK + + MEMP_SANITY_REGION_AFTER_ALIGNED +#endif + ); + } + } +#if MEMP_OVERFLOW_CHECK + memp_overflow_init(); + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK */ +} + +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * the debug version has two more parameters: + * @param file file name calling this function + * @param line number of line where this function is called + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + + memp = memp_tab[type]; + + if (memp != NULL) { + memp_tab[type] = memp->next; +#if MEMP_OVERFLOW_CHECK + memp->next = NULL; + memp->file = file; + memp->line = line; +#endif /* MEMP_OVERFLOW_CHECK */ + MEMP_STATS_INC_USED(used, type); + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + MEMP_STATS_INC(err, type); + } + + SYS_ARCH_UNPROTECT(old_level); + + return memp; +} + +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + if (mem == NULL) { + return; + } + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#else + memp_overflow_check_element_overflow(memp, type); + memp_overflow_check_element_underflow(memp, type); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ + + MEMP_STATS_DEC(used, type); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity()); +#endif /* MEMP_SANITY_CHECK */ + + SYS_ARCH_UNPROTECT(old_level); +} + +#endif /* MEMP_MEM_MALLOC */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/netif.c b/service/src/main/jni/badvpn/lwip/src/core/netif.c new file mode 100644 index 0000000..44b7d3f --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/netif.c @@ -0,0 +1,912 @@ +/** + * @file + * lwIP network interface abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" +#if ENABLE_LOOPBACK +#include "lwip/sys.h" +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_AUTOIP +#include "lwip/autoip.h" +#endif /* LWIP_AUTOIP */ +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_IPV6_MLD +#include "lwip/mld6.h" +#endif /* LWIP_IPV6_MLD */ + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0) +#else +#define NETIF_STATUS_CALLBACK(n) +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0) +#else +#define NETIF_LINK_CALLBACK(n) +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +static u8_t netif_num; + +#if LWIP_IPV6 +static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ + +#if LWIP_HAVE_LOOPIF +static struct netif loop_netif; + +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ +static err_t +netif_loopif_init(struct netif *netif) +{ + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0); + + netif->name[0] = 'l'; + netif->name[1] = 'o'; + netif->output = netif_loop_output; + return ERR_OK; +} +#endif /* LWIP_HAVE_LOOPIF */ + +void +netif_init(void) +{ +#if LWIP_HAVE_LOOPIF + ip_addr_t loop_ipaddr, loop_netmask, loop_gw; + IP4_ADDR(&loop_gw, 127,0,0,1); + IP4_ADDR(&loop_ipaddr, 127,0,0,1); + IP4_ADDR(&loop_netmask, 255,0,0,0); + +#if NO_SYS + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input); +#else /* NO_SYS */ + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input); +#endif /* NO_SYS */ + netif_set_up(&loop_netif); + +#endif /* LWIP_HAVE_LOOPIF */ +} + +/** + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack. + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input) +{ +#if LWIP_IPV6 + u32_t i; +#endif + + LWIP_ASSERT("No init function given", init != NULL); + + /* reset new interface configuration state */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + ip6_addr_set_zero(&netif->ip6_addr[i]); + netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); + } + netif->output_ip6 = netif_null_output_ip6; +#endif /* LWIP_IPV6 */ + netif->flags = 0; +#if LWIP_DHCP + /* netif not under DHCP control by default */ + netif->dhcp = NULL; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /* netif not under AutoIP control by default */ + netif->autoip = NULL; +#endif /* LWIP_AUTOIP */ +#if LWIP_IPV6_AUTOCONFIG + /* IPv6 address autoconfiguration not enabled by default */ + netif->ip6_autoconfig_enabled = 0; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /* netif not under DHCPv6 control by default */ + netif->dhcp6 = NULL; +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = NULL; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; + netif->num = netif_num++; + netif->input = input; + NETIF_SET_HWADDRHINT(netif, NULL); +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + + netif_set_addr(netif, ipaddr, netmask, gw); + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + snmp_inc_iflist(); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start(netif); + } +#endif /* LWIP_IGMP */ + + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", + netif->name[0], netif->name[1])); + ip_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip_addr_debug_print(NETIF_DEBUG, gw); + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +/** + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw) +{ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); +} + +/** + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ +void +netif_remove(struct netif *netif) +{ + if (netif == NULL) { + return; + } + +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop(netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* stop MLD processing */ + mld6_stop(netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + if (netif_is_up(netif)) { + /* set netif down before removing (call callback function) */ + netif_set_down(netif); + } + + snmp_delete_ipaddridx_tree(netif); + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + } else { + /* look for netif further down the list */ + struct netif * tmpNetif; + for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { + if (tmpNetif->next == netif) { + tmpNetif->next = netif->next; + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + snmp_dec_iflist(); + /* this netif is default? */ + if (netif_default == netif) { + /* reset default netif */ + netif_set_default(NULL); + } +#if LWIP_NETIF_REMOVE_CALLBACK + if (netif->remove_callback) { + netif->remove_callback(netif); + } +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +/** + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ +struct netif * +netif_find(char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +int netif_is_named (struct netif *netif, const char name[3]) +{ + u8_t num = name[2] - '0'; + + return (!memcmp(netif->name, name, 2) && netif->num == num); +} + +/** + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ +void +netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* TODO: Handling of obsolete pcbs */ + /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ +#if LWIP_TCP + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb; + + /* address is actually being changed? */ + if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { + /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); + pcb = tcp_active_pcbs; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr)) +#if LWIP_AUTOIP + /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ + && !ip_addr_islinklocal(ipX_2_ip(&pcb->local_ip)) +#endif /* LWIP_AUTOIP */ + ) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + /* PCB bound to current local interface address? */ + if ((!(ip_addr_isany(ipX_2_ip(&lpcb->local_ip)))) && + (ip_addr_cmp(ipX_2_ip(&lpcb->local_ip), &(netif->ip_addr)))) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_set(ipX_2_ip(&lpcb->local_ip), ipaddr); + } + } + } +#endif + snmp_delete_ipaddridx_tree(netif); + snmp_delete_iprteidx_tree(0,netif); + /* set new IP address to netif */ + ip_addr_set(&(netif->ip_addr), ipaddr); + snmp_insert_ipaddridx_tree(netif); + snmp_insert_iprteidx_tree(0,netif); + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->ip_addr), + ip4_addr2_16(&netif->ip_addr), + ip4_addr3_16(&netif->ip_addr), + ip4_addr4_16(&netif->ip_addr))); +} + +/** + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ +void +netif_set_gw(struct netif *netif, ip_addr_t *gw) +{ + ip_addr_set(&(netif->gw), gw); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->gw), + ip4_addr2_16(&netif->gw), + ip4_addr3_16(&netif->gw), + ip4_addr4_16(&netif->gw))); +} + +void netif_set_pretend_tcp (struct netif *netif, u8_t pretend) +{ + if (pretend) { + netif->flags |= NETIF_FLAG_PRETEND_TCP; + } else { + netif->flags &= ~NETIF_FLAG_PRETEND_TCP; + } +} + +/** + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ +void +netif_set_netmask(struct netif *netif, ip_addr_t *netmask) +{ + snmp_delete_iprteidx_tree(0, netif); + /* set new netmask to netif */ + ip_addr_set(&(netif->netmask), netmask); + snmp_insert_iprteidx_tree(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->netmask), + ip4_addr2_16(&netif->netmask), + ip4_addr3_16(&netif->netmask), + ip4_addr4_16(&netif->netmask))); +} + +/** + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ +void +netif_set_default(struct netif *netif) +{ + if (netif == NULL) { + /* remove default route */ + snmp_delete_iprteidx_tree(1, netif); + } else { + /* install default route */ + snmp_insert_iprteidx_tree(1, netif); + } + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * Bring an interface up, available for processing + * traffic. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_up(struct netif *netif) +{ + if (!(netif->flags & NETIF_FLAG_UP)) { + netif->flags |= NETIF_FLAG_UP; + +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif /* LWIP_SNMP */ + + NETIF_STATUS_CALLBACK(netif); + + if (netif->flags & NETIF_FLAG_LINK_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & (NETIF_FLAG_ETHARP)) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send Router Solicitation messages. */ + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + } + } +} + +/** + * Bring an interface down, disabling any traffic processing. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_down(struct netif *netif) +{ + if (netif->flags & NETIF_FLAG_UP) { + netif->flags &= ~NETIF_FLAG_UP; +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif + +#if LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_cleanup_netif(netif); + } +#endif /* LWIP_ARP */ + NETIF_STATUS_CALLBACK(netif); + } +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback) +{ + if (netif) { + netif->status_callback = status_callback; + } +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_REMOVE_CALLBACK +/** + * Set callback to be called when the interface has been removed + */ +void +netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) +{ + if (netif) { + netif->remove_callback = remove_callback; + } +} +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + if (!(netif->flags & NETIF_FLAG_LINK_UP)) { + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_DHCP + if (netif->dhcp) { + dhcp_network_changed(netif); + } +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP + if (netif->autoip) { + autoip_network_changed(netif); + } +#endif /* LWIP_AUTOIP */ + + if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + } + NETIF_LINK_CALLBACK(netif); + } +} + +/** + * Called by a driver when its link goes down + */ +void netif_set_link_down(struct netif *netif ) +{ + if (netif->flags & NETIF_FLAG_LINK_UP) { + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); + } +} + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @param ipaddr the ip address to send the packet to (not used) + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ipaddr); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next); + + SYS_ARCH_PROTECT(lev); + if(netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + + LINK_STATS_INC(link.xmit); + snmp_add_ifoutoctets(stats_if, p->tot_len); + snmp_inc_ifoutucastpkts(stats_if); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback((tcpip_callback_fn)netif_poll, netif); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + struct pbuf *in; + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + + do { + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + in = netif->loop_first; + if (in != NULL) { + struct pbuf *in_end = in; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = pbuf_clen(in); + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + while (in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; + } + /* 'in_end' now points to the last pbuf from 'in' */ + if (in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + } + SYS_ARCH_UNPROTECT(lev); + + if (in != NULL) { + LINK_STATS_INC(link.recv); + snmp_add_ifinoctets(stats_if, in->tot_len); + snmp_inc_ifinucastpkts(stats_if); + /* loopback packets are always IP packets! */ + if (ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + /* Don't reference the packet any more! */ + in = NULL; + } + /* go on while there is a packet on the list */ + } while (netif->loop_first != NULL); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +s8_t +netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { + return i; + } + } + return -1; +} + +void +netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit) +{ + u8_t i, addr_index; + + /* Link-local prefix. */ + netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul); + netif->ip6_addr[0].addr[1] = 0; + + /* Generate interface ID. */ + if (from_mac_48bit) { + /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ + netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | + ((u32_t)(netif->hwaddr[1]) << 16) | + ((u32_t)(netif->hwaddr[2]) << 8) | + (0xff)); + netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) | + ((u32_t)(netif->hwaddr[3]) << 16) | + ((u32_t)(netif->hwaddr[4]) << 8) | + (netif->hwaddr[5])); + } + else { + /* Use hwaddr directly as interface ID. */ + netif->ip6_addr[0].addr[2] = 0; + netif->ip6_addr[0].addr[3] = 0; + + addr_index = 3; + for (i = 0; i < 8; i++) { + if (i == 4) { + addr_index--; + } + netif->ip6_addr[0].addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); + } + } + + /* Set address state. */ +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* Will perform duplicate address detection (DAD). */ + netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE; +#else + /* Consider address valid. */ + netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED; +#endif /* LWIP_IPV6_AUTOCONFIG */ +} + +static err_t +netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + (void)netif; + (void)p; + (void)ipaddr; + + return ERR_IF; +} +#endif /* LWIP_IPV6 */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/pbuf.c b/service/src/main/jni/badvpn/lwip/src/core/pbuf.c new file mode 100644 index 0000000..1e5e53b --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/pbuf.c @@ -0,0 +1,1179 @@ +/** + * @file + * Packet buffer management + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "arch/perf.h" +#if LWIP_TCP && TCP_QUEUE_OOSEQ +#include "lwip/tcp_impl.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) + +#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +#if !NO_SYS +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL +#include "lwip/tcpip.h" +#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ + if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ + SYS_ARCH_PROTECT(old_level); \ + pbuf_free_ooseq_pending = 0; \ + SYS_ARCH_UNPROTECT(old_level); \ + } } while(0) +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +#endif /* !NO_SYS */ + +volatile u8_t pbuf_free_ooseq_pending; +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() + +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + * + * This must be done in the correct thread context therefore this function + * can only be used with NO_SYS=0 and through tcpip_callback. + */ +#if !NO_SYS +static +#endif /* !NO_SYS */ +void +pbuf_free_ooseq(void) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 0; + SYS_ARCH_UNPROTECT(old_level); + + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + if (NULL != pcb->ooseq) { + /** Free the ooseq pbufs of one PCB only */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return; + } + } +} + +#if !NO_SYS +/** + * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq(). + */ +static void +pbuf_free_ooseq_callback(void *arg) +{ + LWIP_UNUSED_ARG(arg); + pbuf_free_ooseq(); +} +#endif /* !NO_SYS */ + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void +pbuf_pool_is_empty(void) +{ +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); +#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_pending; + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); + + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); + } +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +} +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +/** + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (type) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { + PBUF_POOL_IS_EMPTY(); + return NULL; + } + p->type = type; + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", + (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + if (q == NULL) { + PBUF_POOL_IS_EMPTY(); + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccesfully */ + return NULL; + } + q->type = type; + q->flags = 0; + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + return p; +} + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Initialize a custom pbuf (already allocated). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type type of the pbuf (only used to treat the pbuf accordingly, as + * this function allocates no memory) + * @param p pointer to the custom pbuf to initialize (already allocated) + * @param payload_mem pointer to the buffer that is used for payload and headers, + * must be at least big enough to hold 'length' plus the header size, + * may be NULL if set later. + * ATTENTION: The caller is responsible for correct alignment of this buffer!! + * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least + * big enough to hold 'length' plus the header size + */ +struct pbuf* +pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, + void *payload_mem, u16_t payload_mem_len) +{ + u16_t offset; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); + return NULL; + } + + if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); + return NULL; + } + + p->pbuf.next = NULL; + if (payload_mem != NULL) { + p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); + } else { + p->pbuf.payload = NULL; + } + p->pbuf.flags = PBUF_FLAG_IS_CUSTOM; + p->pbuf.len = p->pbuf.tot_len = length; + p->pbuf.type = type; + p->pbuf.ref = 1; + return &p->pbuf; +} +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/** + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s32_t grow; + + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; + /* proceed to next pbuf in chain */ + q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->type == PBUF_RAM) && (rem_len != q->len)) { + /* reallocate and adjust the length of the pbuf that will be split */ + q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len); + LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns succesful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + u16_t type; + void *payload; + u16_t increment_magnitude; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) { + return 0; + } + + if (header_size_increment < 0){ + increment_magnitude = -header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (type == PBUF_RAM || type == PBUF_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, (void *)(p + 1))); + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccesfully */ + return 1; + } + /* pbuf types refering to external payloads? */ + } else if (type == PBUF_REF || type == PBUF_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccesfully */ + return 1; + } + } else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param p The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + u16_t type; + struct pbuf *q; + u8_t count; + + if (p == NULL) { + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL); + + count = 0; + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); + /* this pbuf is no longer referenced to? */ + if (ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; +#if LWIP_SUPPORT_CUSTOM_PBUF + /* is this a custom pbuf? */ + if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { + struct pbuf_custom *pc = (struct pbuf_custom*)p; + LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); + pc->custom_free_function(p); + } else +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + { + /* is this a pbuf from the pool? */ + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (type == PBUF_ROM || type == PBUF_REF) { + memp_free(MEMP_PBUF, p); + /* type == PBUF_RAM */ + } else { + mem_free(p); + } + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); + /* stop walking through the chain */ + p = NULL; + } + } + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ + +u8_t +pbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + ++(p->ref); + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ + +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", + (void*)p_to, (void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); + } + + if((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_from->next == NULL), return ERR_VAL;); + } + if((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; +} + +/** + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough). No more + * than buf->tot_len will be copied, irrespective of len + * @param offset offset into the packet buffer from where to begin copying len bytes + * @return the number of bytes copied, or 0 on failure + */ +u16_t +pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) + buf_copy_len = len; + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} + +/** + * Copy application supplied data into a pbuf. + * This function can only be used to copy the equivalent of buf->tot_len data. + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) +{ + struct pbuf *p; + u16_t buf_copy_len; + u16_t total_copy_len = len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;); + + if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { + return ERR_ARG; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; total_copy_len != 0; p = p->next) { + LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); + buf_copy_len = total_copy_len; + if (buf_copy_len > p->len) { + /* this pbuf cannot hold all remaining data */ + buf_copy_len = p->len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len); + total_copy_len -= buf_copy_len; + copied_total += buf_copy_len; + } + LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); + return ERR_OK; +} + +/** + * Creates a single pbuf out of a queue of pbufs. + * + * @remark: Either the source pbuf 'p' is freed by this function or the original + * pbuf 'p' is returned, therefore the caller has to check the result! + * + * @param p the source pbuf + * @param layer pbuf_layer of the new pbuf + * + * @return a new, single pbuf (p->next is NULL) + * or the old pbuf if allocation fails + */ +struct pbuf* +pbuf_coalesce(struct pbuf *p, pbuf_layer layer) +{ + struct pbuf *q; + err_t err; + if (p->next == NULL) { + return p; + } + q = pbuf_alloc(layer, p->tot_len, PBUF_RAM); + if (q == NULL) { + /* @todo: what do we do now? */ + return p; + } + err = pbuf_copy(q, p); + LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); + pbuf_free(p); + return q; +} + +#if LWIP_CHECKSUM_ON_COPY +/** + * Copies data into a single pbuf (*not* into a pbuf queue!) and updates + * the checksum while copying + * + * @param p the pbuf to copy data into + * @param start_offset offset of p->payload where to copy the data to + * @param dataptr data to copy into the pbuf + * @param len length of data to copy into the pbuf + * @param chksum pointer to the checksum which is updated + * @return ERR_OK if successful, another error if the data does not fit + * within the (first) pbuf (no pbuf queues!) + */ +err_t +pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum) +{ + u32_t acc; + u16_t copy_chksum; + char *dst_ptr; + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("dataptr != NULL", dataptr != NULL); + LWIP_ASSERT("chksum != NULL", chksum != NULL); + LWIP_ASSERT("len != 0", len != 0); + + if ((start_offset >= p->len) || (start_offset + len > p->len)) { + return ERR_ARG; + } + + dst_ptr = ((char*)p->payload) + start_offset; + copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); + if ((start_offset & 1) != 0) { + copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); + } + acc = *chksum; + acc += copy_chksum; + *chksum = FOLD_U32T(acc); + return ERR_OK; +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /** Get one byte from the specified position in a pbuf + * WARNING: returns zero for offset >= p->tot_len + * + * @param p pbuf to parse + * @param offset offset into p of the byte to return + * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len + */ +u8_t +pbuf_get_at(struct pbuf* p, u16_t offset) +{ + u16_t copy_from = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= copy_from)) { + copy_from -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > copy_from)) { + return ((u8_t*)q->payload)[copy_from]; + } + return 0; +} + +/** Compare pbuf contents at specified offset with memory s2, both of length n + * + * @param p pbuf to compare + * @param offset offset into p at wich to start comparing + * @param s2 buffer to compare + * @param n length of buffer to compare + * @return zero if equal, nonzero otherwise + * (0xffff if p is too short, diffoffset+1 otherwise) + */ +u16_t +pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n) +{ + u16_t start = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= start)) { + start -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > start)) { + u16_t i; + for(i = 0; i < n; i++) { + u8_t a = pbuf_get_at(q, start + i); + u8_t b = ((u8_t*)s2)[i]; + if (a != b) { + return i+1; + } + } + return 0; + } + return 0xffff; +} + +/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset + * start_offset. + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param mem search for the contents of this buffer + * @param mem_len length of 'mem' + * @param start_offset offset into p at which to start searching + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset) +{ + u16_t i; + u16_t max = p->tot_len - mem_len; + if (p->tot_len >= mem_len + start_offset) { + for(i = start_offset; i <= max; ) { + u16_t plus = pbuf_memcmp(p, i, mem, mem_len); + if (plus == 0) { + return i; + } else { + i += plus; + } + } + } + return 0xFFFF; +} + +/** Find occurrence of substr with length substr_len in pbuf p, start at offset + * start_offset + * WARNING: in contrast to strstr(), this one does not stop at the first \0 in + * the pbuf/source string! + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param substr string to search for in p, maximum length is 0xFFFE + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_strstr(struct pbuf* p, const char* substr) +{ + size_t substr_len; + if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { + return 0xFFFF; + } + substr_len = strlen(substr); + if (substr_len >= 0xFFFF) { + return 0xFFFF; + } + return pbuf_memfind(p, substr, (u16_t)substr_len, 0); +} diff --git a/service/src/main/jni/badvpn/lwip/src/core/stats.c b/service/src/main/jni/badvpn/lwip/src/core/stats.c new file mode 100644 index 0000000..06fbe0f --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/stats.c @@ -0,0 +1,181 @@ +/** + * @file + * Statistics module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" + +#include + +struct stats_ lwip_stats; + +void stats_init(void) +{ +#ifdef LWIP_DEBUG +#if MEMP_STATS + const char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + int i; + for (i = 0; i < MEMP_MAX; i++) { + lwip_stats.memp[i].name = memp_names[i]; + } +#endif /* MEMP_STATS */ +#if MEM_STATS + lwip_stats.mem.name = "MEM"; +#endif /* MEM_STATS */ +#endif /* LWIP_DEBUG */ +} + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); +} + +#if IGMP_STATS +void +stats_display_igmp(struct stats_igmp *igmp, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); + LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); + LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general)); + LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); + LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); + LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); + LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report)); +} +#endif /* IGMP_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, const char *name) +{ + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); +} + +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + if(index < MEMP_MAX) { + stats_display_mem(mem, memp_names[index]); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used)); + LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max)); + LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + +void +stats_display(void) +{ + s16_t i; + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP6_FRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + ND6_STATS_DISPLAY(); + IP6_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + MLD6_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + ICMP6_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { + MEMP_STATS_DISPLAY(i); + } + SYS_STATS_DISPLAY(); +} +#endif /* LWIP_STATS_DISPLAY */ + +#endif /* LWIP_STATS */ + diff --git a/service/src/main/jni/badvpn/lwip/src/core/tcp.c b/service/src/main/jni/badvpn/lwip/src/core/tcp.c new file mode 100644 index 0000000..55496d0 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/tcp.c @@ -0,0 +1,1852 @@ +/** + * @file + * Transmission Control Protocol for IP + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/snmp.h" +#include "lwip/tcp.h" +#include "lwip/tcp_impl.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/nd6.h" + +#include + +#ifndef TCP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define TCP_LOCAL_PORT_RANGE_START 0xc000 +#define TCP_LOCAL_PORT_RANGE_END 0xffff +#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START) +#endif + +#if LWIP_TCP_KEEPALIVE +#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) +#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) +#else /* LWIP_TCP_KEEPALIVE */ +#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE +#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT +#endif /* LWIP_TCP_KEEPALIVE */ + +const char * const tcp_state_str[] = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "FIN_WAIT_1", + "FIN_WAIT_2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT" +}; + +/* last local TCP port */ +static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ +u32_t tcp_ticks; +const u8_t tcp_backoff[13] = + { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +#define NUM_TCP_PCB_LISTS 4 +#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 +/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ +struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, + &tcp_active_pcbs, &tcp_tw_pcbs}; + +/** Only used for temporary storage. */ +struct tcp_pcb *tcp_tmp_pcb; + +u8_t tcp_active_pcbs_changed; + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u8_t tcp_timer_ctr; +static u16_t tcp_new_port(void); + +/** + * Initialize this module. + */ +void +tcp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Called periodically to dispatch TCP timers. + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_tmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +/** + * Closes the TX side of a connection held by the PCB. + * For tcp_close(), a RST is sent if the application didn't receive all data + * (tcp_recved() not called for all data passed to recv callback). + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +static err_t +tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) +{ + err_t err; + + if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { + if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { + /* Not all data received by application, send RST to tell the remote + side about this. */ + LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); + + /* don't call tcp_abort here: we must not deallocate the pcb since + that might not be expected when calling tcp_close */ + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + if (pcb->state == ESTABLISHED) { + /* move to TIME_WAIT since we close actively */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ + memp_free(MEMP_TCP_PCB, pcb); + } + return ERR_OK; + } + } + + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + err = ERR_OK; + if (pcb->local_port != 0 || pcb->bound_to_netif) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + TCP_PCB_REMOVE_ACTIVE(pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1; + } + break; + case ESTABLISHED: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = LAST_ACK; + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if (pcb != NULL && err == ERR_OK) { + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + /* @todo: When implementing SO_LINGER, this must be changed somehow: + If SOF_LINGER is set, the data should be sent and acked before close returns. + This can only be valid for sequential APIs, not for the raw API. */ + tcp_output(pcb); + } + return err; +} + +/** + * Closes the connection held by the PCB. + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it (unless an error is returned). + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ +#if TCP_DEBUG + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ + + if (pcb->state != LISTEN) { + /* Set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + } + /* ... and close */ + return tcp_close_shutdown(pcb, 1); +} + +/** + * Causes all or part of a full-duplex connection of this PCB to be shut down. + * This doesn't deallocate the PCB unless shutting down both sides! + * Shutting down both sides is the same as calling tcp_close, so if it succeds, + * the PCB should not be referenced any more. + * + * @param pcb PCB to shutdown + * @param shut_rx shut down receive side if this is != 0 + * @param shut_tx shut down send side if this is != 0 + * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down) + * another err_t on error. + */ +err_t +tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx) +{ + if (pcb->state == LISTEN) { + return ERR_CONN; + } + if (shut_rx) { + /* shut down the receive side: set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + if (shut_tx) { + /* shutting down the tx AND rx side is the same as closing for the raw API */ + return tcp_close_shutdown(pcb, 1); + } + /* ... and free buffered data */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + } + if (shut_tx) { + /* This can't happen twice since if it succeeds, the pcb's state is changed. + Only close in these states as the others directly deallocate the PCB */ + switch (pcb->state) { + case SYN_RCVD: + case ESTABLISHED: + case CLOSE_WAIT: + return tcp_close_shutdown(pcb, shut_rx); + default: + /* Not (yet?) connected, cannot shutdown the TX side as that would bring us + into CLOSED state, where the PCB is deallocated. */ + return ERR_CONN; + } + } + return ERR_OK; +} + +/** + * Abandons a connection and optionally sends a RST to the remote + * host. Deletes the local protocol control block. This is done when + * a connection is killed because of shortage of memory. + * + * @param pcb the tcp_pcb to abort + * @param reset boolean to indicate whether a reset should be sent + */ +void +tcp_abandon(struct tcp_pcb *pcb, int reset) +{ + u32_t seqno, ackno; +#if LWIP_CALLBACK_API + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs", + pcb->state != LISTEN); + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + int send_rst = reset && (pcb->state != CLOSED); + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + TCP_PCB_REMOVE_ACTIVE(pcb); + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + if (send_rst) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + } + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + } +} + +/** + * Aborts the connection by sending a RST (reset) segment to the remote + * host. The pcb is deallocated. This function never fails. + * + * ATTENTION: When calling this from one of the TCP callbacks, make + * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + * or you will risk accessing deallocated memory or memory leaks! + * + * @param pcb the tcp pcb to abort + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + tcp_abandon(pcb, 1); +} + +/** + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_VAL if bind failed because the PCB is not in a valid state + * ERR_OK if bound + */ +err_t +tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + int i; + int max_pcb_list = NUM_TCP_PCB_LISTS; + struct tcp_pcb *cpcb; + + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL); + +#if SO_REUSE + /* Unless the REUSEADDR flag is set, + we have to check the pcbs in TIME-WAIT state, also. + We do not dump TIME_WAIT pcb's; they can still be matched by incoming + packets using both local and remote IP addresses and ports to distinguish. + */ + if (ip_get_option(pcb, SOF_REUSEADDR)) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + if (port == 0) { + return ERR_BUF; + } + } + + /* Check if the address already is in use (on all lists) */ + for (i = 0; i < max_pcb_list; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { +#if SO_REUSE + /* Omit checking for the same port if both pcbs have REUSEADDR set. + For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in + tcp_connect. */ + if (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(cpcb, SOF_REUSEADDR)) +#endif /* SO_REUSE */ + { + /* @todo: check accept_any_ip_version */ + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) || + ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) { + return ERR_USE; + } + } + } + } + } + + pcb->bound_to_netif = 0; + if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(ipaddr)); + } + pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} + +err_t +tcp_bind_to_netif(struct tcp_pcb *pcb, const char ifname[3]) +{ + LWIP_ERROR("tcp_bind_if: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + /* Check if the interface is already in use */ + for (int i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(struct tcp_pcb *cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + cpcb->bound_to_netif && + !memcmp(cpcb->local_netif, ifname, sizeof(cpcb->local_netif))) { + return ERR_USE; + } + } + } + + pcb->bound_to_netif = 1; + ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->local_ip); + pcb->local_port = 0; + memcpy(pcb->local_netif, ifname, sizeof(pcb->local_netif)); + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind_to_netif: bind to interface %c%c%c\n", ifname[0], ifname[1], ifname[2])); + return ERR_OK; +} + +#if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err); + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + * @param pcb the original tcp_pcb + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory. + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen(tpcb); + */ +struct tcp_pcb * +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb_listen *lpcb; + + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL); + + /* already listening? */ + if (pcb->state == LISTEN) { + return pcb; + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR) && !pcb->have_local_netif) { + /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage + is declared (listen-/connection-pcb), we have to make sure now that + this port is only used once for every local IP. */ + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((lpcb->local_port == pcb->local_port) && + IP_PCB_IPVER_EQ(pcb, lpcb)) { + if (ipX_addr_cmp(PCB_ISIPV6(pcb), &lpcb->local_ip, &pcb->local_ip)) { + /* this address/port is already used */ + return NULL; + } + } + } + } +#endif /* SO_REUSE */ + lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN); + if (lpcb == NULL) { + return NULL; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->bound_to_netif = pcb->bound_to_netif; + lpcb->local_port = pcb->local_port; + memcpy(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)); + lpcb->state = LISTEN; + lpcb->prio = pcb->prio; + lpcb->so_options = pcb->so_options; + ip_set_option(lpcb, SOF_ACCEPTCONN); + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; +#if LWIP_IPV6 + PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb); + lpcb->accept_any_ip_version = 0; +#endif /* LWIP_IPV6 */ + ipX_addr_copy(PCB_ISIPV6(pcb), lpcb->local_ip, pcb->local_ip); + if (pcb->local_port != 0 || pcb->bound_to_netif) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null; +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + lpcb->backlog = (backlog ? backlog : 1); +#endif /* TCP_LISTEN_BACKLOG */ + TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb); + return (struct tcp_pcb *)lpcb; +} + +#if LWIP_IPV6 +/** + * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6 + * connections, if the pcb's local address is set to ANY. + */ +struct tcp_pcb * +tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb *lpcb; + + lpcb = tcp_listen_with_backlog(pcb, backlog); + if ((lpcb != NULL) && + ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* The default behavior is to accept connections on either + * IPv4 or IPv6, if not bound. */ + /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */ + ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1; + } + return lpcb; +} +#endif /* LWIP_IPV6 */ + +/** + * Update the state that tracks the available window space to advertise. + * + * Returns how much extra window would be advertised if we sent an + * update now. + */ +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) +{ + u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; + + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + /* we can advertise more window */ + pcb->rcv_ann_wnd = pcb->rcv_wnd; + return new_right_edge - pcb->rcv_ann_right_edge; + } else { + if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) { + /* Can happen due to other end sending out of advertised window, + * but within actual available (but not yet advertised) window */ + pcb->rcv_ann_wnd = 0; + } else { + /* keep the right edge of window constant */ + u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; + LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); + pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd; + } + return 0; + } +} + +/** + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + * + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + int wnd_inflation; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_recved for listen-pcbs", + pcb->state != LISTEN); + LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n", + len <= 0xffff - pcb->rcv_wnd ); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } + + wnd_inflation = tcp_update_rcv_ann_wnd(pcb); + + /* If the change in the right edge of window is significant (default + * watermark is TCP_WND/4), then send an explicit update now. + * Otherwise wait for a packet to be sent in the normal course of + * events (or more window to be available later) */ + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + tcp_ack_now(pcb); + tcp_output(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * Allocate a new local TCP port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + u8_t i; + u16_t n = 0; + struct tcp_pcb *pcb; + +again: + if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { + tcp_port = TCP_LOCAL_PORT_RANGE_START; + } + /* Check all PCB lists. */ + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == tcp_port) { + if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + } + return tcp_port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @param port the remote tcp port to connect to + * @param connected callback function to call when connected (or on error) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent + */ +err_t +tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, + tcp_connected_fn connected) +{ + err_t ret; + u32_t iss; + u16_t old_local_port; + + LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + LWIP_ERROR("tcp_connect: cannot connect pcb bound to netif", !pcb->bound_to_netif, return ERR_VAL); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr)); + } else { + return ERR_VAL; + } + pcb->remote_port = port; + + /* check if we have a route to the remote host */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* no local IP address set, yet. */ + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the address as local address of the pcb. */ + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + old_local_port = pcb->local_port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + if (pcb->local_port == 0) { + return ERR_BUF; + } + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR)) { + /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure + now that the 5-tuple is unique. */ + struct tcp_pcb *cpcb; + int i; + /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ + for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if ((cpcb->local_port == pcb->local_port) && + (cpcb->remote_port == port) && + IP_PCB_IPVER_EQ(cpcb, pcb) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(ipaddr))) { + /* linux returns EISCONN here, but ERR_USE should be OK for us */ + return ERR_USE; + } + } + } + } +#endif /* SO_REUSE */ + iss = tcp_next_iss(); + pcb->rcv_nxt = 0; + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->snd_wnd = TCP_WND; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1; + pcb->ssthresh = pcb->mss * 10; +#if LWIP_CALLBACK_API + pcb->connected = connected; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(connected); +#endif /* LWIP_CALLBACK_API */ + + /* Send a SYN together with the MSS option. */ + ret = tcp_enqueue_flags(pcb, TCP_SYN); + if (ret == ERR_OK) { + /* SYN segment was enqueued, changed the pcbs state now */ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG_ACTIVE(pcb); + snmp_inc_tcpactiveopens(); + + tcp_output(pcb); + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *prev; + u16_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + u8_t pcb_reset; /* flag if a RST should be sent when removing */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + ++tcp_timer_ctr; + +tcp_slowtmr_start: + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + if (pcb->last_timer == tcp_timer_ctr) { + /* skip this pcb, we have already processed it */ + pcb = pcb->next; + continue; + } + pcb->last_timer = tcp_timer_ctr; + + pcb_remove = 0; + pcb_reset = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); + } + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) { + ++pcb->rtime; + } + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < (pcb->mss << 1)) { + pcb->ssthresh = (pcb->mss << 1); + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ + if (pcb->flags & TF_RXCLOSED) { + /* PCB was fully closed (either through close() or SHUT_RDWR): + normal FIN-WAIT timeout handling. */ + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + } + + /* Check if KEEPALIVE should be sent */ + if(ip_get_option(pcb, SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + ++pcb_remove; + ++pcb_reset; + } + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) + / TCP_SLOW_INTERVAL) + { + tcp_keepalive(pcb); + pcb->keep_cnt_sent++; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_err_fn err_fn; + void *err_arg; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + } + + err_fn = pcb->errf; + err_arg = pcb->callback_arg; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + + tcp_active_pcbs_changed = 0; + TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + } else { + /* get the 'next' element now and work with 'prev' below (in case of abort) */ + prev = pcb; + pcb = pcb->next; + + /* We check if we should poll the connection. */ + ++prev->polltmr; + if (prev->polltmr >= prev->pollinterval) { + prev->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + tcp_active_pcbs_changed = 0; + TCP_EVENT_POLL(prev, err); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + /* if err == ERR_ABRT, 'prev' is already deallocated */ + if (err == ERR_OK) { + tcp_output(prev); + } + } + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb; + + ++tcp_timer_ctr; + +tcp_fasttmr_start: + pcb = tcp_active_pcbs; + + while(pcb != NULL) { + if (pcb->last_timer != tcp_timer_ctr) { + struct tcp_pcb *next; + pcb->last_timer = tcp_timer_ctr; + /* send delayed ACKs */ + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + tcp_output(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + next = pcb->next; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + tcp_active_pcbs_changed = 0; + tcp_process_refused_data(pcb); + if (tcp_active_pcbs_changed) { + /* application callback has changed the pcb list: restart the loop */ + goto tcp_fasttmr_start; + } + } + pcb = next; + } + } +} + +/** Pass pcb->refused_data to the recv callback */ +err_t +tcp_process_refused_data(struct tcp_pcb *pcb) +{ + err_t err; + u8_t refused_flags = pcb->refused_data->flags; + /* set pcb->refused_data to NULL in case the callback frees it and then + closes the pcb */ + struct pbuf *refused_data = pcb->refused_data; + pcb->refused_data = NULL; + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err); + if (err == ERR_OK) { + /* did refused_data include a FIN? */ + if (refused_flags & PBUF_FLAG_TCP_FIN) { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + } + } else if (err == ERR_ABRT) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + /* Drop incoming packets because pcb is "full" (only if the incoming + segment contains data). */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + return ERR_ABRT; + } else { + /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ + pcb->refused_data = refused_data; + } + return ERR_OK; +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + */ +void +tcp_segs_free(struct tcp_seg *seg) +{ + while (seg != NULL) { + struct tcp_seg *next = seg->next; + tcp_seg_free(seg); + seg = next; + } +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + */ +void +tcp_seg_free(struct tcp_seg *seg) +{ + if (seg != NULL) { + if (seg->p != NULL) { + pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } +} + +/** + * Sets the priority of a connection. + * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} + +#if TCP_QUEUE_OOSEQ +/** + * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers + * + * @param seg the old tcp_seg + * @return a copy of seg + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif /* TCP_QUEUE_OOSEQ */ + +#if LWIP_CALLBACK_API +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Kills the oldest active connection that has the same or lower priority than + * 'prio'. + * + * @param prio minimum priority + */ +static void +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + + mprio = TCP_PRIO_MAX; + + /* We kill the oldest active connection that has lower priority than prio. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= prio && + pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ +static void +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Allocate a new tcp_pcb structure. + * + * @param prio priority for the new pcb + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + u32_t iss; + + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing active connections with lower priority than the new one. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio)); + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed twice before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: timewait PCB was freed above */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + memset(pcb, 0, sizeof(struct tcp_pcb)); + pcb->prio = prio; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_TTL; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rtime = -1; + pcb->cwnd = 1; + iss = tcp_next_iss(); + pcb->snd_wl2 = iss; + pcb->snd_nxt = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; + pcb->last_timer = tcp_timer_ctr; + + pcb->polltmr = 0; + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + + pcb->keep_cnt_sent = 0; + } + return pcb; +} + +/** + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +#if LWIP_IPV6 +/** + * Creates a new TCP-over-IPv6 protocol control block but doesn't + * place it on any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new_ip6(void) +{ + struct tcp_pcb * pcb; + pcb = tcp_alloc(TCP_PRIO_NORMAL); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +/** + * Used to specify the argument that should be passed callback + * functions. + * + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions + */ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + * + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received + */ +void +tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) +{ + LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent + */ +void +tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) +{ + LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + * @param pcb tcp_pcb to set the err callback + * @param err callback function to call for this pcb when a fatal error + * has occured on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) +{ + LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); + pcb->errf = err; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when LISTENing + * connection has been connected to another host + */ +void +tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->accept = accept; +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ + LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); +#if LWIP_CALLBACK_API + pcb->poll = poll; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(poll); +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). + * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + +#if TCP_LISTEN_BACKLOG + if (pcb->state == SYN_RCVD) { + /* Need to find the corresponding listen_pcb and decrease its accepts_pending */ + struct tcp_pcb_listen *lpcb; + LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL", + tcp_listen_pcbs.listen_pcbs != NULL); + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((!lpcb->bound_to_netif && !pcb->bound_to_netif && + (lpcb->local_port == pcb->local_port) && + IP_PCB_IPVER_EQ(pcb, lpcb) && + (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) || + (lpcb->bound_to_netif && pcb->bound_to_netif && + !memcmp(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)))) { + /* port and address of the listen pcb match the timed-out pcb */ + LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending", + lpcb->accepts_pending > 0); + lpcb->accepts_pending--; + break; + } + } + } +#endif /* TCP_LISTEN_BACKLOG */ + + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; +#if TCP_OVERSIZE + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated! + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + + pcb->state = CLOSED; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + * @return u32_t pseudo random sequence number + */ +u32_t +tcp_next_iss(void) +{ + static u32_t iss = 6510; + + iss += tcp_ticks; /* XXX */ + return iss; +} + +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calcluates the effective send mss that can be used for a specific IP address + * by using ip_route to determin the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + u16_t mss_s; + struct netif *outif; + s16_t mtu; + + outif = ipX_route(isipv6, src, dest); +#if LWIP_IPV6 + if (isipv6) { + /* First look in destination cache, to see if there is a Path MTU. */ + mtu = nd6_get_destination_mtu(ipX_2_ip6(dest), outif); + } else +#endif /* LWIP_IPV6 */ + { + if (outif == NULL) { + return sendmss; + } + mtu = outif->mtu; + } + + if (mtu != 0) { + mss_s = mtu - IP_HLEN - TCP_HLEN; +#if LWIP_IPV6 + /* for IPv6, substract the difference in header size */ + mss_s -= (IP6_HLEN - IP_HLEN); +#endif /* LWIP_IPV6 */ + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * We correct for TCP options in tcp_write(), and don't support IP options. + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +const char* +tcp_debug_state_str(enum tcp_state s) +{ + return tcp_state_str[s]; +} + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(tcphdr->src), ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + TCPH_FLAGS(tcphdr) >> 5 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s])); +} + +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); +} + +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +/** + * Check state consistency of the tcp_pcb lists. + */ +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ + +#endif /* LWIP_TCP */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/tcp_in.c b/service/src/main/jni/badvpn/lwip/src/core/tcp_in.c new file mode 100644 index 0000000..92ee2b2 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/tcp_in.c @@ -0,0 +1,1666 @@ +/** + * @file + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of the TCP layer. + * + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_ND6_TCP_REACHABILITY_HINTS +#include "lwip/nd6.h" +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; +static struct tcp_hdr *tcphdr; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + +/** + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + * + * @param p received TCP segment to process (p->payload pointing to the TCP header) + * @param inp network interface on which this segment was received + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; +#if SO_REUSE + struct tcp_pcb *lpcb_prev = NULL; + struct tcp_pcb_listen *lpcb_any = NULL; +#endif /* SO_REUSE */ + u8_t hdrlen; + err_t err; +#if CHECKSUM_CHECK_TCP + u16_t chksum; +#endif /* CHECKSUM_CHECK_TCP */ + + PERF_START; + + TCP_STATS_INC(tcp.recv); + snmp_inc_tcpinsegs(); + + tcphdr = (struct tcp_hdr *)p->payload; + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* Check that TCP header fits in payload */ + if (p->len < sizeof(struct tcp_hdr)) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) || + ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) { + TCP_STATS_INC(tcp.proterr); + goto dropped; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len, + ipX_current_src_addr(), ipX_current_dest_addr()); + if (chksum != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + chksum)); + tcp_debug_print(tcphdr); + TCP_STATS_INC(tcp.chkerr); + goto dropped; + } +#endif /* CHECKSUM_CHECK_TCP */ + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr); + if(pbuf_header(p, -(hdrlen * 4))){ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); + tcphdr->dest = ntohs(tcphdr->dest); + seqno = tcphdr->seqno = ntohl(tcphdr->seqno); + ackno = tcphdr->ackno = ntohl(tcphdr->ackno); + tcphdr->wnd = ntohs(tcphdr->wnd); + + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. */ + prev = NULL; + + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) { + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb; + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb); + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + struct tcp_pcb_listen *netif_pcb = NULL; + struct tcp_pcb *netif_pcb_prev; + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if (lpcb->bound_to_netif) { + if (IP_PCB_IPVER_INPUT_MATCH(lpcb) && netif_is_named(inp, lpcb->local_netif)) { + netif_pcb = lpcb; + netif_pcb_prev = prev; + } + } + else if (lpcb->local_port == tcphdr->dest) { +#if LWIP_IPV6 + if (lpcb->accept_any_ip_version) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; +#endif /* SO_REUSE */ + } else +#endif /* LWIP_IPV6 */ + if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) { + if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) { + /* found an exact match */ + break; + } else if (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; + #endif /* SO_REUSE */ + } + } + } + prev = (struct tcp_pcb *)lpcb; + } +#if SO_REUSE + /* first try specific local IP */ + if (lpcb == NULL) { + /* only pass to ANY if no specific local IP has been found */ + lpcb = lpcb_any; + prev = lpcb_prev; + } +#endif /* SO_REUSE */ + if (lpcb == NULL && netif_pcb) { + lpcb = netif_pcb; + prev = netif_pcb_prev; + } + if (lpcb != NULL) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb); + pbuf_free(p); + return; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + + if (flags & TCP_PSH) { + p->flags |= PBUF_FLAG_PUSH; + } + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + if ((tcp_process_refused_data(pcb) == ERR_ABRT) || + ((pcb->refused_data != NULL) && (tcplen > 0))) { + /* pcb has been aborted or refused data is still refused and the new + segment contains data */ + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + goto aborted; + } + } + tcp_input_pcb = pcb; + err = tcp_process(pcb); + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + if (!(pcb->flags & TF_RXCLOSED)) { + /* Connection closed although the application has only shut down the + tx side: call the PCB's err callback and indicate the closure to + ensure the application doesn't continue using the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD); + } + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + + if (recv_data != NULL) { + LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); + if (pcb->flags & TF_RXCLOSED) { + /* received data although already closed -> abort (send RST) to + notify the remote host that not all data has been processed */ + pbuf_free(recv_data); + tcp_abort(pcb); + goto aborted; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + if (err == ERR_ABRT) { + goto aborted; + } + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + if (pcb->refused_data != NULL) { + /* Delay this if we have refused data. */ + pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; + } else { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + } + + tcp_input_pcb = NULL; + /* Try to send something out. */ + tcp_output(pcb); +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + } + } + /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()). + Below this line, 'pcb' may not be dereferenced! */ +aborted: + tcp_input_pcb = NULL; + recv_data = NULL; + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p); + inseg.p = NULL; + } + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); + return; +dropped: + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); +} + +/** + * Called by tcp_input() when a segment arrives for a listening + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * @return ERR_OK if the segment was processed + * another err_t on error + * + * @note the return value is not (yet?) used in tcp_input() + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + err_t rc; + + if (flags & TCP_RST) { + /* An incoming RST should be ignored. Return. */ + return ERR_OK; + } + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } else if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); + return ERR_ABRT; + } +#endif /* TCP_LISTEN_BACKLOG */ + npcb = tcp_alloc(pcb->prio); + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ +#if LWIP_IPV6 + PCB_ISIPV6(npcb) = ip_current_is_v6(); +#endif /* LWIP_IPV6 */ + ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr()); + ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr()); + npcb->bound_to_netif = pcb->bound_to_netif; + npcb->local_port = tcphdr->dest; + memcpy(npcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)); + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->rcv_ann_right_edge = npcb->rcv_nxt; + npcb->snd_wnd = tcphdr->wnd; + npcb->snd_wnd_max = tcphdr->wnd; + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API + npcb->accept = pcb->accept; +#endif /* LWIP_CALLBACK_API */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & SOF_INHERITED; + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG_ACTIVE(npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, + &npcb->remote_ip, PCB_ISIPV6(npcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + snmp_inc_tcppassiveopens(); + + /* Send a SYN|ACK together with the MSS option. */ + rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK); + if (rc != ERR_OK) { + tcp_abandon(npcb, 0); + return rc; + } + return tcp_output(npcb); + } + return ERR_OK; +} + +/** + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return ERR_OK; + } + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + return ERR_OK; + } + } else if (flags & TCP_FIN) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; + } + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + return tcp_output(pcb); + } + return ERR_OK; +} + +/** + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags |= TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { + /* Cope with new connection attempt after remote end crashed */ + tcp_ack_now(pcb); + return ERR_OK; + } + + if ((pcb->flags & TF_RXCLOSED) == 0) { + /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */ + pcb->tmr = tcp_ticks; + } + pcb->keep_cnt_sent = 0; + + tcp_parseopt(pcb); + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->snd_buf++; + pcb->rcv_nxt = seqno + 1; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wnd_max = tcphdr->wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, + PCB_ISIPV6(pcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + /* Set ssthresh again after changing pcb->mss (already set in tcp_connect + * but for the default value of pcb->mss) */ + pcb->ssthresh = pcb->mss * 10; + + pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + tcp_seg_free(rseg); + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else { + pcb->rtime = 0; + pcb->nrtx = 0; + } + + /* Call the user specified function to call when sucessfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + tcp_ack_now(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + u16_t old_cwnd; + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); +#endif + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb, ERR_OK, err); + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + /* Already aborted? */ + if (err != ERR_ABRT) { + tcp_abort(pcb); + } + return ERR_ABRT; + } + old_cwnd = pcb->cwnd; + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + tcp_receive(pcb); + + /* Prevent ACK for SYN to generate a sent event */ + if (pcb->acked != 0) { + pcb->acked--; + } + + pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + + if (recv_flags & TF_GOT_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + } else { + /* incorrect ACK number, send RST */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { + /* Looks like another copy of the SYN - retransmit our SYN-ACK */ + tcp_rexmit(pcb); + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { /* passive close */ + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags |= TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) + * + * Called from tcp_receive() + */ +static void +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + struct tcp_seg *old_seg; + + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } + else { + /* delete some following segments + oos queue may have segments with FIN flag */ + while (next && + TCP_SEQ_GEQ((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + /* cseg with FIN already processed */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif /* TCP_QUEUE_OOSEQ */ + +/** + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). + */ +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif /* TCP_QUEUE_OOSEQ */ + struct pbuf *p; + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + int found_dupack = 0; +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + u32_t ooseq_blen; + u16_t ooseq_qlen; +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ + + LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; + + /* Update window. */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = tcphdr->wnd; + /* keep track of the biggest window announced by the remote host to calculate + the maximum segment size */ + if (pcb->snd_wnd_max < tcphdr->wnd) { + pcb->snd_wnd_max = tcphdr->wnd; + } + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* start persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + } else if (pcb->persist_backoff > 0) { + /* stop persist timer */ + pcb->persist_backoff = 0; + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != tcphdr->wnd) { + LWIP_DEBUGF(TCP_WND_DEBUG, + ("tcp_receive: no window update lastack %"U32_F" ackno %" + U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a + * duplicate ack if: + * 1) It doesn't ACK new data + * 2) length of received packet is zero (i.e. no payload) + * 3) the advertised window hasn't changed + * 4) There is outstanding unacknowledged data (retransmission timer running) + * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) + * + * If it passes all five, should process as a dupack: + * a) dupacks < 3: do nothing + * b) dupacks == 3: fast retransmit + * c) dupacks > 3: increase cwnd + * + * If it only passes 1-3, should reset dupack counter (and add to + * stats, which we don't do in lwIP) + * + * If it only passes 1, should reset dupack counter + * + */ + + /* Clause 1 */ + if (TCP_SEQ_LEQ(ackno, pcb->lastack)) { + pcb->acked = 0; + /* Clause 2 */ + if (tcplen == 0) { + /* Clause 3 */ + if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ + /* Clause 4 */ + if (pcb->rtime >= 0) { + /* Clause 5 */ + if (pcb->lastack == ackno) { + found_dupack = 1; + if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) { + ++pcb->dupacks; + } + if (pcb->dupacks > 3) { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) { + /* Do fast retransmit */ + tcp_rexmit_fast(pcb); + } + } + } + } + } + /* If Clause (1) or more is true, but not a duplicate ack, reset + * count of consecutive duplicate acks */ + if (!found_dupack) { + pcb->dupacks = 0; + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. Diff between the two can never exceed 64K? */ + pcb->acked = (u16_t)(ackno - pcb->lastack); + + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) { + if (pcb->cwnd < pcb->ssthresh) { + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd)); + } else { + u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. */ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else + pcb->rtime = 0; + + pcb->polltmr = 0; + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + } else { + /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + while (pcb->unsent != NULL && + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + } + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper */ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further unless the pcb already received a FIN. + (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, + LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ + if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){ + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + + off = pcb->rcv_nxt - seqno; + p = inseg.p; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); + if (inseg.p->len < off) { + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + if(pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } else { + if(pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ + tcplen = TCP_TCPLEN(&inseg); + + if (tcplen > pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + inseg.len = pcb->rcv_wnd; + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } +#if TCP_QUEUE_OOSEQ + /* Received in-sequence data, adjust ooseq data if: + - FIN has been received or + - inseq overlaps with ooseq */ + if (pcb->ooseq != NULL) { + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: received in-order FIN, binning ooseq queue\n")); + /* Received in-order FIN means anything that was received + * out of order must now have been received in-order, so + * bin the ooseq queue */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } else { + next = pcb->ooseq; + /* Remove all segments on ooseq that are covered by inseg already. + * FIN is copied from ooseq to inseg if present. */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg cannot have FIN here (already processed above) */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + prev = next; + next = next->next; + tcp_seg_free(prev); + } + /* Now trim right side of inseg if it overlaps with the first + * segment on ooseq */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* inseg cannot have FIN here (already processed above) */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n", + (seqno + tcplen) == next->tcphdr->seqno); + } + pcb->ooseq = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + pcb->rcv_nxt = seqno + tcplen; + + /* Update the receiver's (our) window. */ + LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen); + pcb->rcv_wnd -= tcplen; + + tcp_update_rcv_ann_wnd(pcb); + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags |= TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + are now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n", + pcb->rcv_wnd >= TCP_TCPLEN(cseg)); + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + + tcp_update_rcv_ann_wnd(pcb); + + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags |= TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } + } + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_send_empty_ack(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) { + if (seqno == next->tcphdr->seqno) { + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) { + /* The incoming segment is larger than the old + segment. We replace some segments with the new + one. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_oos_insert_segment(cseg, next); + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + pcb->ooseq = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } else { + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = (u16_t)(seqno - next->tcphdr->seqno); + pbuf_realloc(next->p, next->len); + } + /* check if the remote side overruns our receive window */ + if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno; + pbuf_realloc(next->next->p, next->next->len); + tcplen = TCP_TCPLEN(next->next); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } + } + break; + } + } + prev = next; + } + } +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + /* Check that the data on ooseq doesn't exceed one of the limits + and throw away everything above that limit. */ + ooseq_blen = 0; + ooseq_qlen = 0; + prev = NULL; + for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) { + struct pbuf *p = next->p; + ooseq_blen += p->tot_len; + ooseq_qlen += pbuf_clen(p); + if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || + (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { + /* too much ooseq data, dump this and everything after it */ + tcp_segs_free(next); + if (prev == NULL) { + /* first ooseq segment is too much, dump the whole queue */ + pcb->ooseq = NULL; + } else { + /* just dump 'next' and everything after it */ + prev->next = NULL; + } + break; + } + } +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ +#endif /* TCP_QUEUE_OOSEQ */ + } + } else { + /* The incoming segment is not withing the window. */ + tcp_send_empty_ack(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb); + } + } +} + +/** + * Parses the options contained in the incoming segment. + * + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! + * + * @param pcb the tcp_pcb for which a segment arrived + */ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u16_t c, max_c; + u16_t mss; + u8_t *opts, opt; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif + + opts = (u8_t *)tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if(TCPH_HDRLEN(tcphdr) > 0x5) { + max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2; + for (c = 0; c < max_c; ) { + opt = opts[c]; + switch (opt) { + case 0x00: + /* End of options. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case 0x01: + /* NOP option. */ + ++c; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case 0x02: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (opts[c + 1] != 0x04 || c + 0x04 > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + /* Advance to next option */ + c += 0x04; + break; +#if LWIP_TCP_TIMESTAMPS + case 0x08: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (opts[c + 1] != 0x0A || c + 0x0A > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = (opts[c+2]) | (opts[c+3] << 8) | + (opts[c+4] << 16) | (opts[c+5] << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = ntohl(tsval); + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = ntohl(tsval); + } + /* Advance to next option */ + c += 0x0A; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + if (opts[c + 1] == 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + /* If the length field is zero, the options are malformed + and we don't process them further. */ + return; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} + +#endif /* LWIP_TCP */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/tcp_out.c b/service/src/main/jni/badvpn/lwip/src/core/tcp_out.c new file mode 100644 index 0000000..b9fc339 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/tcp_out.c @@ -0,0 +1,1499 @@ +/** + * @file + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_TCP_TIMESTAMPS +#include "lwip/sys.h" +#endif + +#include + +/* Define some copy-macros for checksum-on-copy so that the code looks + nicer by preventing too many ifdef's. */ +#if TCP_CHECKSUM_ON_COPY +#define TCP_DATA_COPY(dst, src, len, seg) do { \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ + len, &seg->chksum, &seg->chksum_swapped); \ + seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped); +#else /* TCP_CHECKSUM_ON_COPY*/ +#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len) +#endif /* TCP_CHECKSUM_ON_COPY*/ + +/** Define this to 1 for an extra check that the output checksum is valid + * (usefule when the checksum is generated by the application, not the stack) */ +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0 +#endif + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +/** Allocate a pbuf and create a tcphdr at p->payload, used for output + * functions other than the default tcp_output -> tcp_output_segment + * (e.g. tcp_send_empty_ack, etc.) + * + * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) + * @param optlen length of header-options + * @param datalen length of tcp data to reserve in pbuf + * @param seqno_be seqno in network byte order (big-endian) + * @return pbuf with p->payload being the tcp_hdr + */ +static struct pbuf * +tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr; + struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= TCP_HLEN + optlen)); + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seqno_be; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + /* If we're sending a packet, update the announced right window edge */ + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + } + return p; +} + +/** + * Called by tcp_close() to send a segment including FIN flag but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_fin(struct tcp_pcb *pcb) +{ + /* first, try to add the fin to the last unsent segment */ + if (pcb->unsent != NULL) { + struct tcp_seg *last_unsent; + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { + /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ + TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); + pcb->flags |= TF_FIN; + return ERR_OK; + } + } + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue_flags(pcb, TCP_FIN); +} + +/** + * Create a TCP segment with prefilled header. + * + * Called by tcp_write and tcp_enqueue_flags. + * + * @param pcb Protocol control block for the TCP connection. + * @param p pbuf that is used to hold the TCP header. + * @param flags TCP flags for header. + * @param seqno TCP sequence number of this packet + * @param optflags options to include in TCP header + * @return a new tcp_seg pointing to p, or NULL. + * The TCP header is filled in except ackno and wnd. + * p is freed on failure. + */ +static struct tcp_seg * +tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags) +{ + struct tcp_seg *seg; + u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); + + if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); + pbuf_free(p); + return NULL; + } + seg->flags = optflags; + seg->next = NULL; + seg->p = p; + seg->len = p->tot_len - optlen; +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = 0; + seg->chksum_swapped = 0; + /* check optflags */ + LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", + (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* build TCP header */ + if (pbuf_header(p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + tcp_seg_free(seg); + return NULL; + } + seg->tcphdr = (struct tcp_hdr *)seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + /* ackno is set in tcp_output */ + TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); + /* wnd and chksum are set in tcp_output */ + seg->tcphdr->urgp = 0; + return seg; +} + +/** + * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. + * + * This function is like pbuf_alloc(layer, length, PBUF_RAM) except + * there may be extra bytes available at the end. + * + * @param layer flag to define header size. + * @param length size of the pbuf's payload. + * @param max_length maximum usable size of payload+oversize. + * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. + * @param pcb The TCP connection that willo enqueue the pbuf. + * @param apiflags API flags given to tcp_write. + * @param first_seg true when this pbuf will be used in the first enqueued segment. + * @param + */ +#if TCP_OVERSIZE +static struct pbuf * +tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, + u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, + u8_t first_seg) +{ + struct pbuf *p; + u16_t alloc = length; + +#if LWIP_NETIF_TX_SINGLE_PBUF + LWIP_UNUSED_ARG(max_length); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(apiflags); + LWIP_UNUSED_ARG(first_seg); + /* always create MSS-sized pbufs */ + alloc = max_length; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (length < max_length) { + /* Should we allocate an oversized pbuf, or just the minimum + * length required? If tcp_write is going to be called again + * before this segment is transmitted, we want the oversized + * buffer. If the segment will be transmitted immediately, we can + * save memory by allocating only length. We use a simple + * heuristic based on the following information: + * + * Did the user set TCP_WRITE_FLAG_MORE? + * + * Will the Nagle algorithm defer transmission of this segment? + */ + if ((apiflags & TCP_WRITE_FLAG_MORE) || + (!(pcb->flags & TF_NODELAY) && + (!first_seg || + pcb->unsent != NULL || + pcb->unacked != NULL))) { + alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE)); + } + } +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(layer, alloc, PBUF_RAM); + if (p == NULL) { + return NULL; + } + LWIP_ASSERT("need unchained pbuf", p->next == NULL); + *oversize = p->len - length; + /* trim p->len to the currently used size */ + p->len = p->tot_len = length; + return p; +} +#else /* TCP_OVERSIZE */ +#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM) +#endif /* TCP_OVERSIZE */ + +#if TCP_CHECKSUM_ON_COPY +/** Add a checksum of newly added data to the segment */ +static void +tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, + u8_t *seg_chksum_swapped) +{ + u32_t helper; + /* add chksum to old chksum and fold to u16_t */ + helper = chksum + *seg_chksum; + chksum = FOLD_U32T(helper); + if ((len & 1) != 0) { + *seg_chksum_swapped = 1 - *seg_chksum_swapped; + chksum = SWAP_BYTES_IN_WORD(chksum); + } + *seg_chksum = chksum; +} +#endif /* TCP_CHECKSUM_ON_COPY */ + +/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). + * + * @param pcb the tcp pcb to check for + * @param len length of data to send (checked agains snd_buf) + * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise + */ +static err_t +tcp_write_checks(struct tcp_pcb *pcb, u16_t len) +{ + /* connection is in invalid state for data transmission? */ + if ((pcb->state != ESTABLISHED) && + (pcb->state != CLOSE_WAIT) && + (pcb->state != SYN_SENT) && + (pcb->state != SYN_RCVD)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } else if (len == 0) { + return ERR_OK; + } + + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", + len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + return ERR_OK; +} + +/** + * Write data for sending (but does not send it immediately). + * + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for. + * @param arg Pointer to the data to be enqueued for sending. + * @param len Data length in bytes + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) +{ + struct pbuf *concat_p = NULL; + struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL; + u16_t pos = 0; /* position in 'arg' data */ + u16_t queuelen; + u8_t optlen = 0; + u8_t optflags = 0; +#if TCP_OVERSIZE + u16_t oversize = 0; + u16_t oversize_used = 0; +#endif /* TCP_OVERSIZE */ +#if TCP_CHECKSUM_ON_COPY + u16_t concat_chksum = 0; + u8_t concat_chksum_swapped = 0; + u16_t concat_chksummed = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + err_t err; + /* don't allocate segments bigger than half the maximum window we ever received */ + u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); + +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Always copy to try to create single pbufs for TX */ + apiflags |= TCP_WRITE_FLAG_COPY; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)apiflags)); + LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", + arg != NULL, return ERR_ARG;); + + err = tcp_write_checks(pcb, len); + if (err != ERR_OK) { + return err; + } + queuelen = pcb->snd_queuelen; + +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags = TF_SEG_OPTS_TS; + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif /* LWIP_TCP_TIMESTAMPS */ + + + /* + * TCP segmentation is done in three phases with increasing complexity: + * + * 1. Copy data directly into an oversized pbuf. + * 2. Chain a new pbuf to the end of pcb->unsent. + * 3. Create new segments. + * + * We may run out of memory at any point. In that case we must + * return ERR_MEM and not change anything in pcb. Therefore, all + * changes are recorded in local variables and committed at the end + * of the function. Some pcb fields are maintained in local copies: + * + * queuelen = pcb->snd_queuelen + * oversize = pcb->unsent_oversize + * + * These variables are set consistently by the phases: + * + * seg points to the last segment tampered with. + * + * pos records progress as data is segmented. + */ + + /* Find the tail of the unsent queue. */ + if (pcb->unsent != NULL) { + u16_t space; + u16_t unsent_optlen; + + /* @todo: this could be sped up by keeping last_unsent in the pcb */ + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + /* Usable space at the end of the last unsent segment */ + unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags); + space = mss_local - (last_unsent->len + unsent_optlen); + + /* + * Phase 1: Copy data directly into an oversized pbuf. + * + * The number of bytes copied is recorded in the oversize_used + * variable. The actual copying is done at the bottom of the + * function. + */ +#if TCP_OVERSIZE +#if TCP_OVERSIZE_DBGCHECK + /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */ + LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)", + pcb->unsent_oversize == last_unsent->oversize_left); +#endif /* TCP_OVERSIZE_DBGCHECK */ + oversize = pcb->unsent_oversize; + if (oversize > 0) { + LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space); + seg = last_unsent; + oversize_used = oversize < len ? oversize : len; + pos += oversize_used; + oversize -= oversize_used; + space -= oversize_used; + } + /* now we are either finished or oversize is zero */ + LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len)); +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: Chain a new pbuf to the end of pcb->unsent. + * + * We don't extend segments containing SYN/FIN flags or options + * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at + * the end. + */ + if ((pos < len) && (space > 0) && (last_unsent->len > 0)) { + u16_t seglen = space < len - pos ? space : len - pos; + seg = last_unsent; + + /* Create a pbuf with a copy or reference to seglen bytes. We + * can use PBUF_RAW here since the data appears in the middle of + * a segment. A header will never be prepended. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* Data is copied */ + if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", + seglen)); + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + last_unsent->oversize_left += oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ + TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); +#if TCP_CHECKSUM_ON_COPY + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + } else { + /* Data is not copied */ + if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen, + &concat_chksum, &concat_chksum_swapped); + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + concat_p->payload = (u8_t*)arg + pos; + } + + pos += seglen; + queuelen += pbuf_clen(concat_p); + } + } else { +#if TCP_OVERSIZE + LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)", + pcb->unsent_oversize == 0); +#endif /* TCP_OVERSIZE */ + } + + /* + * Phase 3: Create new segments. + * + * The new segments are chained together in the local 'queue' + * variable, ready to be appended to pcb->unsent. + */ + while (pos < len) { + struct pbuf *p; + u16_t left = len - pos; + u16_t max_len = mss_local - optlen; + u16_t seglen = left > max_len ? max_len : left; +#if TCP_CHECKSUM_ON_COPY + u16_t chksum = 0; + u8_t chksum_swapped = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* If copy is set, memory should be allocated and data copied + * into pbuf */ + if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", + (p->len >= seglen)); + TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); + } else { + /* Copy is not set: First allocate a pbuf for holding the data. + * Since the referenced data is available at least until it is + * sent out on the link (as it has to be ACKed by the remote + * party) we can safely use PBUF_ROM instead of PBUF_REF here. + */ + struct pbuf *p2; +#if TCP_OVERSIZE + LWIP_ASSERT("oversize == 0", oversize == 0); +#endif /* TCP_OVERSIZE */ + if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + chksum = ~inet_chksum((u8_t*)arg + pos, seglen); +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + p2->payload = (u8_t*)arg + pos; + + /* Second, allocate a pbuf for the headers. */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p2); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n")); + goto memerr; + } + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(p/*header*/, p2/*data*/); + } + + queuelen += pbuf_clen(p); + + /* Now that there are more segments queued, we check again if the + * length of the queue exceeds the configured maximum or + * overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + pbuf_free(p); + goto memerr; + } + + if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) { + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = chksum; + seg->chksum_swapped = chksum_swapped; + seg->flags |= TF_SEG_DATA_CHECKSUMMED; +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL); + prev_seg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + prev_seg = seg; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); + + pos += seglen; + } + + /* + * All three segmentation phases were successful. We can commit the + * transaction. + */ + + /* + * Phase 1: If data has been added to the preallocated tail of + * last_unsent, we update the length fields of the pbuf chain. + */ +#if TCP_OVERSIZE + if (oversize_used > 0) { + struct pbuf *p; + /* Bump tot_len of whole chain, len of tail */ + for (p = last_unsent->p; p; p = p->next) { + p->tot_len += oversize_used; + if (p->next == NULL) { + TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent); + p->len += oversize_used; + } + } + last_unsent->len += oversize_used; +#if TCP_OVERSIZE_DBGCHECK + LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", + last_unsent->oversize_left >= oversize_used); + last_unsent->oversize_left -= oversize_used; +#endif /* TCP_OVERSIZE_DBGCHECK */ + } + pcb->unsent_oversize = oversize; +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: concat_p can be concatenated onto last_unsent->p + */ + if (concat_p != NULL) { + LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", + (last_unsent != NULL)); + pbuf_cat(last_unsent->p, concat_p); + last_unsent->len += concat_p->tot_len; +#if TCP_CHECKSUM_ON_COPY + if (concat_chksummed) { + tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum, + &last_unsent->chksum_swapped); + last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; + } +#endif /* TCP_CHECKSUM_ON_COPY */ + } + + /* + * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that + * is harmless + */ + if (last_unsent == NULL) { + pcb->unsent = queue; + } else { + last_unsent->next = queue; + } + + /* + * Finally update the pcb state. + */ + pcb->snd_lbb += len; + pcb->snd_buf -= len; + pcb->snd_queuelen = queuelen; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n", + pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued. */ + if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + + if (concat_p != NULL) { + pbuf_free(concat_p); + } + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/** + * Enqueue TCP options for transmission. + * + * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl(). + * + * @param pcb Protocol control block for the TCP connection. + * @param flags TCP header flags to set in the outgoing segment. + * @param optdata pointer to TCP options, or NULL. + * @param optlen length of TCP options in bytes. + */ +err_t +tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) +{ + struct pbuf *p; + struct tcp_seg *seg; + u8_t optflags = 0; + u8_t optlen = 0; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)", + (flags & (TCP_SYN | TCP_FIN)) != 0); + + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + if (flags & TCP_SYN) { + optflags = TF_SEG_OPTS_MSS; + } +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags |= TF_SEG_OPTS_TS; + } +#endif /* LWIP_TCP_TIMESTAMPS */ + optlen = LWIP_TCP_OPT_LENGTH(optflags); + + /* tcp_enqueue_flags is always called with either SYN or FIN in flags. + * We need one available snd_buf byte to do that. + * This means we can't send FIN while snd_buf==0. A better fix would be to + * not include SYN and FIN sequence numbers in the snd_buf count. */ + if (pcb->snd_buf == 0) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + + /* Allocate pbuf with room for TCP header + options */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen", + (p->len >= optlen)); + + /* Allocate memory for tcp_seg, and fill in fields. */ + if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0); + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, + ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + /* Now append seg to pcb->unsent queue */ + if (pcb->unsent == NULL) { + pcb->unsent = seg; + } else { + struct tcp_seg *useg; + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + useg->next = seg; + } +#if TCP_OVERSIZE + /* The new unsent tail has no space */ + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + + /* SYN and FIN bump the sequence number */ + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + pcb->snd_lbb++; + /* optlen does not influence snd_buf */ + pcb->snd_buf--; + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + + /* update number of segments on the queues */ + pcb->snd_queuelen += pbuf_clen(seg->p); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue_flags: invalid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + return ERR_OK; +} + +#if LWIP_TCP_TIMESTAMPS +/* Build a timestamp option (12 bytes long) at the specified options pointer) + * + * @param pcb tcp_pcb + * @param opts option pointer where to store the timestamp option + */ +static void +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = PP_HTONL(0x0101080A); + opts[1] = htonl(sys_now()); + opts[2] = htonl(pcb->ts_recent); +} +#endif + +/** Send an ACK without data. + * + * @param pcb Protocol control block for the TCP connection to send the ACK + */ +err_t +tcp_send_empty_ack(struct tcp_pcb *pcb) +{ + struct pbuf *p; + u8_t optlen = 0; +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + +#if LWIP_TCP_TIMESTAMPS + if (pcb->flags & TF_TIMESTAMP) { + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif + + p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt)); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + /* NB. MSS option is only sent on SYNs, so ignore it here */ +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (pcb->flags & TF_TIMESTAMP) { + tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); + } +#endif + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + pbuf_free(p); + + return ERR_OK; +} + +/** + * Find out what we can send and send it + * + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ +err_t +tcp_output(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg, *useg; + u32_t wnd, snd_nxt; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_output for listen-pcbs", + pcb->state != LISTEN); + + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. */ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); + + seg = pcb->unsent; + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + return tcp_send_empty_ack(pcb); + } + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next); + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* data available and window allows it to be sent? */ + while (seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_write had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_write/tcp_output. + */ + if((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ + break; + } +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ + tcp_output_segment(seg, pcb); + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? */ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty? */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather somewhere before it. We need to check for + * this case. -STJ Jul 27, 2004 */ + if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) { + /* add segment to before tail of unacked list, keeping the list sorted */ + struct tcp_seg **cur_seg = &(pcb->unacked); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = (*cur_seg); + (*cur_seg) = seg; + } else { + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + /* last unsent has been removed, reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + pcb->flags &= ~TF_NAGLEMEMERR; + return ERR_OK; +} + +/** + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment + */ +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + u16_t len; + u32_t *opts; + + /** @bug Exclude retransmitted segments from this count. */ + snmp_inc_tcpoutsegs(); + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + /* Add any requested options. NB MSS option is only set on SYN + packets, so ignore it here */ + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + u16_t mss; +#if TCP_CALCULATE_EFF_SEND_MSS + mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#else /* TCP_CALCULATE_EFF_SEND_MSS */ + mss = TCP_MSS; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + *opts = TCP_BUILD_MSS_OPTION(mss); + opts += 1; + } +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (seg->flags & TF_SEG_OPTS_TS) { + tcp_build_timestamp_option(pcb, opts); + opts += 3; + } +#endif + + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. */ + if (pcb->rtime == -1) { + pcb->rtime = 0; + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + return; + } + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if TCP_CHECKSUM_ON_COPY + { + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { + LWIP_ASSERT("data included but not checksummed", + seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4)); + } + + /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ + acc = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip); + /* add payload checksum */ + if (seg->chksum_swapped) { + seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); + seg->chksum_swapped = 0; + } + acc += (u16_t)~(seg->chksum); + seg->tcphdr->chksum = FOLD_U32T(acc); +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + if (chksum_slow != seg->tcphdr->chksum) { + LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n", + seg->tcphdr->chksum, chksum_slow)); + seg->tcphdr->chksum = chksum_slow; + } +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + } +#else /* TCP_CHECKSUM_ON_COPY */ +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ +#endif /* TCP_CHECKSUM_ON_COPY */ + TCP_STATS_INC(tcp.xmit); + +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + pcb->tos, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ +} + +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); + tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len, + local_ip, remote_ip); +#endif + /* Send output with hardcoded TTL/HL since we have no access to the pcb */ + ipX_output(isipv6, p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; + /* last unsent hasn't changed, no need to reset unsent_oversize */ + + /* increment number of retransmissions */ + ++pcb->nrtx; + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retramsmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + struct tcp_seg **cur_seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + /* Keep the unsent queue sorted. */ + seg = pcb->unacked; + pcb->unacked = seg->next; + + cur_seg = &(pcb->unsent); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = *cur_seg; + *cur_seg = seg; +#if TCP_OVERSIZE + if (seg->next == NULL) { + /* the retransmitted segment is last in unsent, so reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + ++pcb->nrtx; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + snmp_inc_tcpretranssegs(); + /* No need to call tcp_output: we are always called from tcp_input() + and thus tcp_output directly returns. */ +} + + +/** + * Handle retransmission after three dupacks received + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit_fast(struct tcp_pcb *pcb) +{ + if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: dupacks %"U16_F" (%"U32_F + "), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + + /* Set ssthresh to half of the minimum of the current + * cwnd and the advertised window */ + if (pcb->cwnd > pcb->snd_wnd) { + pcb->ssthresh = pcb->snd_wnd / 2; + } else { + pcb->ssthresh = pcb->cwnd / 2; + } + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < 2*pcb->mss) { + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: The minimum value for ssthresh %"U16_F + " should be min 2 mss %"U16_F"...\n", + pcb->ssthresh, 2*pcb->mss)); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } +} + + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ +void +tcp_keepalive(struct tcp_pcb *pcb) +{ + struct pbuf *p; +#if CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* CHECKSUM_GEN_TCP */ + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1)); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } +#if CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; + + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} + + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + u16_t len; + u8_t is_fin; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) { + seg = pcb->unsent; + } + if(seg == NULL) { + return; + } + + is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); + /* we want to send one seqno: either FIN or data (no options) */ + len = is_fin ? 0 : 1; + + p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + tcphdr = (struct tcp_hdr *)p->payload; + + if (is_fin) { + /* FIN segment, no data */ + TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); + } else { + /* Data segment, copy in one byte from the head of the unacked queue */ + char *d = ((char *)p->payload + TCP_HLEN); + /* Depending on whether the segment has already been sent (unacked) or not + (unsent), seg->p->payload points to the IP header or TCP header. + Ensure we copy the first TCP data byte: */ + pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); + } + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} +#endif /* LWIP_TCP */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/timers.c b/service/src/main/jni/badvpn/lwip/src/core/timers.c new file mode 100644 index 0000000..c8ead4e --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/timers.c @@ -0,0 +1,546 @@ +/** + * @file + * Stack-internal timers implementation. + * This file includes timer callbacks for stack-internal timers as well as + * functions to set up or stop timers and check for expired timers. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#include "lwip/timers.h" +#include "lwip/tcp_impl.h" + +#if LWIP_TIMERS + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +#include "lwip/ip_frag.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" + +/** The one and only timeout list */ +static struct sys_timeo *next_timeout; +#if NO_SYS +static u32_t timeouts_last_time; +#endif /* NO_SYS */ + +#if LWIP_TCP +/** global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n")); + ip_reass_tmr(); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +} +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ + +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +#if LWIP_IPV6 +/** + * Timer callback function that calls nd6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +nd6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n")); + nd6_tmr(); + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +} + +#if LWIP_IPV6_REASS +/** + * Timer callback function that calls ip6_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip6_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n")); + ip6_reass_tmr(); + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +} +#endif /* LWIP_IPV6_REASS */ + +#if LWIP_IPV6_MLD +/** + * Timer callback function that calls mld6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +mld6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n")); + mld6_tmr(); + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +} +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +/** Initialize this module */ +void sys_timeouts_init(void) +{ +#if IP_REASSEMBLY + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +#if LWIP_IPV6_REASS + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +#endif /* LWIP_IPV6_REASS */ +#if LWIP_IPV6_MLD + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = sys_now(); +#endif +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_timeouts_mbox_fetch() + * - by calling sys_check_timeouts() (NO_SYS==1 only) + * + * @param msecs time in milliseconds after that the timer should expire + * @param handler callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +#if LWIP_DEBUG_TIMERNAMES +void +sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) +#else /* LWIP_DEBUG_TIMERNAMES */ +void +sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ +{ + struct sys_timeo *timeout, *t; + + timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs; +#if LWIP_DEBUG_TIMERNAMES + timeout->handler_name = handler_name; + LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", + (void *)timeout, msecs, handler_name, (void *)arg)); +#endif /* LWIP_DEBUG_TIMERNAMES */ + + if (next_timeout == NULL) { + next_timeout = timeout; + return; + } + + if (next_timeout->time > msecs) { + next_timeout->time -= msecs; + timeout->next = next_timeout; + next_timeout = timeout; + } else { + for(t = next_timeout; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry, even though the timeout has not triggered yet. + * + * @note This function only works as expected if there is only one timeout + * calling 'handler' in the list of timeouts. + * + * @param handler callback function that would be called by the timeout + * @param arg callback argument that would be passed to handler +*/ +void +sys_untimeout(sys_timeout_handler handler, void *arg) +{ + struct sys_timeo *prev_t, *t; + + if (next_timeout == NULL) { + return; + } + + for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + next_timeout = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +#if NO_SYS + +/** Handle timeouts for NO_SYS==1 (i.e. without using + * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout + * handler functions when timeouts expire. + * + * Must be called periodically from your main loop. + */ +void +sys_check_timeouts(void) +{ + if (next_timeout) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); + /* this cares for wraparounds */ + diff = now - timeouts_last_time; + do + { +#if PBUF_POOL_FREE_OOSEQ + PBUF_CHECK_FREE_OOSEQ(); +#endif /* PBUF_POOL_FREE_OOSEQ */ + had_one = 0; + tmptimeout = next_timeout; + if (tmptimeout && (tmptimeout->time <= diff)) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time = now; + diff -= tmptimeout->time; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + handler(arg); + } + } + /* repeat until all expired timers have been called */ + }while(had_one); + } +} + +/** Set back the timestamp of the last call to sys_check_timeouts() + * This is necessary if sys_check_timeouts() hasn't been called for a long + * time (e.g. while saving energy) to prevent all timer functions of that + * period being called. + */ +void +sys_restart_timeouts(void) +{ + timeouts_last_time = sys_now(); +} + +#else /* NO_SYS */ + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeo *tmptimeout; + sys_timeout_handler handler; + void *arg; + + again: + if (!next_timeout) { + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + } else { + if (next_timeout->time > 0) { + time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = next_timeout; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the + timeout handler function. */ + LOCK_TCPIP_CORE(); + handler(arg); + UNLOCK_TCPIP_CORE(); + } + LWIP_TCPIP_THREAD_ALIVE(); + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < next_timeout->time) { + next_timeout->time -= time_needed; + } else { + next_timeout->time = 0; + } + } + } +} + +#endif /* NO_SYS */ + +#else /* LWIP_TIMERS */ +/* Satisfy the TCP code which calls this function */ +void +tcp_timer_needed(void) +{ +} +#endif /* LWIP_TIMERS */ diff --git a/service/src/main/jni/badvpn/lwip/src/core/udp.c b/service/src/main/jni/badvpn/lwip/src/core/udp.c new file mode 100644 index 0000000..ac0e56d --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/core/udp.c @@ -0,0 +1,1151 @@ +/** + * @file + * User Datagram Protocol module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* udp.c + * + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). + * + */ + +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/icmp6.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/dhcp.h" + +#include + +#ifndef UDP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define UDP_LOCAL_PORT_RANGE_START 0xc000 +#define UDP_LOCAL_PORT_RANGE_END 0xffff +#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START) +#endif + +/* last local UDP port */ +static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Initialize this module. + */ +void +udp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Allocate a new local UDP port. + * + * @return a new (free) local UDP port number + */ +static u16_t +udp_new_port(void) +{ + u16_t n = 0; + struct udp_pcb *pcb; + +again: + if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { + udp_port = UDP_LOCAL_PORT_RANGE_START; + } + /* Check all PCBs. */ + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == udp_port) { + if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + return udp_port; +#if 0 + struct udp_pcb *ipcb = udp_pcbs; + while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == udp_port) { + /* port is already used by another udp_pcb */ + udp_port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else { + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + } + if (ipcb != NULL) { + return 0; + } + return udp_port; +#endif +} + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. + * + * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) + * @param inp network interface on which the datagram was received. + * + */ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + u16_t src, dest; + u8_t local_match; + u8_t broadcast; + u8_t for_us; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + /* Check minimum length (UDP header) */ + if (p->len < UDP_HLEN) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)p->payload; + + /* is broadcast packet ? */ +#if LWIP_IPV6 + broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#else /* LWIP_IPV6 */ + broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#endif /* LWIP_IPV6 */ + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + /* convert src and dest ports to host byte order */ + src = ntohs(udphdr->src); + dest = ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, ("udp (")); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src))); + +#if LWIP_DHCP + pcb = NULL; + /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by + the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ + if (dest == DHCP_CLIENT_PORT) { + /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ + if (src == DHCP_SERVER_PORT) { + if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { + /* accept the packe if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src + (no need to check for IPv6 since the dhcp struct always uses IPv4) */ + if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) { + pcb = inp->dhcp->pcb; + } + } + } + } else +#endif /* LWIP_DHCP */ + { + prev = NULL; + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + local_match = 0; + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if (pcb->local_port == dest) { + if ( +#if LWIP_IPV6 + ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) && + (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) || +#if LWIP_IPV6_MLD + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6_MLD */ + ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(pcb) && + (ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(pcb, SOF_BROADCAST) && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#endif /* IP_SOF_BROADCAST_RECV */ + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && IP_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + prev = pcb; + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL) { + for_us = 1; + } else { +#if LWIP_IPV6 + if (ip_current_is_v6()) { + for_us = netif_matches_ip6_addr(inp, ip6_current_dest_addr()); + } else +#endif /* LWIP_IPV6 */ + { + for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr()); + } + } + if (for_us) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if CHECKSUM_CHECK_UDP +#if LWIP_UDPLITE + if (ip_current_header_proto() == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ + u16_t chklen = ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + goto chkerr; + } + } + if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE, + p->tot_len, chklen, + ipX_current_src_addr(), ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } else +#endif /* LWIP_UDPLITE */ + { + if (udphdr->chksum != 0) { + if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len, + ipX_current_src_addr(), + ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } + } +#endif /* CHECKSUM_CHECK_UDP */ + if(pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + if (pcb != NULL) { + snmp_inc_udpindatagrams(); +#if SO_REUSE && SO_REUSE_RXTOALL + if ((broadcast || +#if LWIP_IPV6 + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(ip_current_dest_addr())) && + ip_get_option(pcb, SOF_REUSEADDR)) { + /* pass broadcast- or multicast packets to all multicast pcbs + if SOF_REUSEADDR is set on the first match */ + struct udp_pcb *mpcb; + u8_t p_header_changed = 0; + s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); + for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { + if (mpcb != pcb) { + /* compare PCB local addr+port to UDP destination addr+port */ + if ((mpcb->local_port == dest) && +#if LWIP_IPV6 + ((PCB_ISIPV6(mpcb) && + (ip6_addr_ismulticast(ip6_current_dest_addr()) || + ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(mpcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))))) { +#endif /* IP_SOF_BROADCAST_RECV */ + /* pass a copy of the packet to all local matches */ + if (mpcb->recv.ip4 != NULL) { + struct pbuf *q; + /* for that, move payload to IP header again */ + if (p_header_changed == 0) { + pbuf_header(p, hdrs_len); + p_header_changed = 1; + } + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + err_t err = pbuf_copy(q, p); + if (err == ERR_OK) { + /* move payload to UDP data */ + pbuf_header(q, -hdrs_len); +#if LWIP_IPV6 + if (PCB_ISIPV6(mpcb)) { + mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } + } + } + } + } + } + } + if (p_header_changed) { + /* and move payload to UDP data again */ + pbuf_header(p, -hdrs_len); + } + } +#endif /* SO_REUSE && SO_REUSE_RXTOALL */ + /* callback */ + if (pcb->recv.ip4 != NULL) { + /* now the recv function is responsible for freeing p */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); + } + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; + } + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); + +#if LWIP_ICMP || LWIP_ICMP6 + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!broadcast && +#if LWIP_IPV6 + !ip6_addr_ismulticast(ip6_current_dest_addr()) && +#endif /* LWIP_IPV6 */ + !ip_addr_ismulticast(ip_current_dest_addr())) { + /* move payload pointer back to ip header */ + pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN); + icmp_port_unreach(ip_current_is_v6(), p); + } +#endif /* LWIP_ICMP || LWIP_ICMP6 */ + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpnoports(); + pbuf_free(p); + } + } else { + pbuf_free(p); + } +end: + PERF_STOP("udp_input"); + return; +#if CHECKSUM_CHECK_UDP +chkerr: + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + PERF_STOP("udp_input"); +#endif /* CHECKSUM_CHECK_UDP */ +} + +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port); +} + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +/** Same as udp_send() but with checksum + */ +err_t +udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto_chksum(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port, + have_chksum, chksum); +} +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +/** + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); +} + +/** Same as udp_sendto(), but with checksum */ +err_t +udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, u8_t have_chksum, u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct netif *netif; + ipX_addr_t *dst_ip_route = ip_2_ipX(dst_ip); + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + +#if LWIP_IPV6 || LWIP_IGMP + if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) { + /* For multicast, find a netif based on source address. */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + dst_ip_route = &pcb->local_ip; + } else +#endif /* LWIP_IPV6 */ + { +#if LWIP_IGMP + dst_ip_route = ip_2_ipX(&pcb->multicast_ip); +#endif /* LWIP_IGMP */ + } + } +#endif /* LWIP_IPV6 || LWIP_IGMP */ + + /* find the outgoing network interface for this packet */ + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route); + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip)); + LWIP_DEBUGF(UDP_DEBUG, ("\n")); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +} + +/** + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); +} + +/** Same as udp_sendto_if(), but with checksum */ +err_t +udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, struct netif *netif, u8_t have_chksum, + u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct udp_hdr *udphdr; + ip_addr_t *src_ip; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + u8_t ip_proto; + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && +#if LWIP_IPV6 + !PCB_ISIPV6(pcb) && +#endif /* LWIP_IPV6 */ + ip_addr_isbroadcast(dst_ip, netif) ) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); + return err; + } + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a separate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p (only if p contains data) */ + pbuf_chain(q, p); + } + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* adding space for header within p succeeded */ + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ + udphdr = (struct udp_hdr *)q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* Multicast Loop? */ +#if LWIP_IGMP + if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && +#if LWIP_IPV6 + ( +#if LWIP_IPV6_MLD + (PCB_ISIPV6(pcb) && + ip6_addr_ismulticast(ip_2_ip6(dst_ip))) || +#endif /* LWIP_IPV6_MLD */ + (!PCB_ISIPV6(pcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(dst_ip)))) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* LWIP_IGMP */ + + + /* PCB local address is IP_ANY_ADDR? */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) { + src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip))); + if (src_ip == NULL) { + /* No suitable source address was found. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + } else { + /* use UDP PCB local IPv6 address as source address, if still valid. */ + if (netif_matches_ip6_addr(netif, ipX_2_ip6(&pcb->local_ip)) < 0) { + /* Address isn't valid anymore. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + src_ip = ipX_2_ip(&pcb->local_ip); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip))) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip_addr_cmp(ipX_2_ip(&(pcb->local_ip)), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + return ERR_VAL; + } + /* use UDP PCB local IP address as source address */ + src_ip = ipX_2_ip(&(pcb->local_ip)); + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + +#if LWIP_UDPLITE + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = htons(chklen_hdr); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + chklen = UDP_HLEN; + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE, + q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } +#endif /* CHECKSUM_GEN_UDP */ + + ip_proto = IP_PROTO_UDPLITE; + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + /* Checksum is mandatory over IPv6. */ + if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + u16_t udpchksum; +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + udpchksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, + q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + acc = udpchksum + (u16_t)~(chksum); + udpchksum = FOLD_U32T(acc); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + udpchksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len, + ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + udphdr->chksum = udpchksum; + } +#endif /* CHECKSUM_GEN_UDP */ + ip_proto = IP_PROTO_UDP; + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto)); + /* output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ipX_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* TODO: must this be increased even if error occured? */ + snmp_inc_udpoutdatagrams(); + + /* did we chain a separate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t +udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(ipaddr)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + /* pcb may occur at most once in active list */ + LWIP_ASSERT("rebind == 0", rebind == 0); + /* pcb already in list, just rebind */ + rebind = 1; + } + + /* By default, we don't allow to bind to a port that any other udp + PCB is alread bound to, unless *all* PCBs with that port have tha + REUSEADDR flag set. */ +#if SO_REUSE + else if (!ip_get_option(pcb, SOF_REUSEADDR) && + !ip_get_option(ipcb, SOF_REUSEADDR)) { +#else /* SO_REUSE */ + /* port matches that of PCB in list and REUSEADDR not set -> reject */ + else { +#endif /* SO_REUSE */ + if ((ipcb->local_port == port) && IP_PCB_IPVER_EQ(pcb, ipcb) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) || + ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(ipaddr)))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); + + /* no port specified? */ + if (port == 0) { + port = udp_new_port(); + if (port == 0) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } + pcb->local_port = port; + snmp_insert_udpidx_tree(pcb); + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port)); + return ERR_OK; +} + +/** + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * + * @see udp_disconnect() + */ +err_t +udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); + if (err != ERR_OK) { + return err; + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO +#if LWIP_IPV6 + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + struct netif *netif; + + if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", + ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip)))); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + ipX_addr_copy(0, pcb->local_ip, netif->ip_addr); + } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + ipX_addr_set_any(0, &pcb->local_ip); + } + } +#endif + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +/** + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ +void +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->remote_ip); + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +/** + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for wich to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ +void +udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv.ip4 = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + snmp_delete_udpidx_tree(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else { + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_UDP_PCB, pcb); +} + +/** + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new(void) +{ + struct udp_pcb *pcb; + pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; + } + return pcb; +} + +#if LWIP_IPV6 +/** + * Create a UDP PCB for IPv6. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new_ip6(void) +{ + struct udp_pcb *pcb; + pcb = udp_new(); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/autoip.h b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/autoip.h new file mode 100644 index 0000000..e62b72e --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/autoip.h @@ -0,0 +1,118 @@ +/** + * @file + * + * AutoIP Automatic LinkLocal IP Configuration + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +#ifndef __LWIP_AUTOIP_H__ +#define __LWIP_AUTOIP_H__ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* AutoIP Timing */ +#define AUTOIP_TMR_INTERVAL 100 +#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL) + +/* RFC 3927 Constants */ +#define PROBE_WAIT 1 /* second (initial random delay) */ +#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */ +#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */ +#define PROBE_NUM 3 /* (number of probe packets) */ +#define ANNOUNCE_NUM 2 /* (number of announcement packets) */ +#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ +#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ + +/* AutoIP client states */ +#define AUTOIP_STATE_OFF 0 +#define AUTOIP_STATE_PROBING 1 +#define AUTOIP_STATE_ANNOUNCING 2 +#define AUTOIP_STATE_BOUND 3 + +struct autoip +{ + ip_addr_t llipaddr; /* the currently selected, probed, announced or used LL IP-Address */ + u8_t state; /* current AutoIP state machine state */ + u8_t sent_num; /* sent number of probes or announces, dependent on state */ + u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */ + u8_t lastconflict; /* ticks until a conflict can be solved by defending */ + u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */ +}; + + +#define autoip_init() /* Compatibility define, no init needed. */ + +/** Set a struct autoip allocated by the application to work with */ +void autoip_set_struct(struct netif *netif, struct autoip *autoip); + +/** Start AutoIP client */ +err_t autoip_start(struct netif *netif); + +/** Stop AutoIP client */ +err_t autoip_stop(struct netif *netif); + +/** Handles every incoming ARP Packet, called by etharp_arp_input */ +void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr); + +/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */ +void autoip_tmr(void); + +/** Handle a possible change in the network configuration */ +void autoip_network_changed(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_AUTOIP */ + +#endif /* __LWIP_AUTOIP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/icmp.h b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/icmp.h new file mode 100644 index 0000000..fa893b6 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/icmp.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ICMP_H__ +#define __LWIP_ICMP_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +#if LWIP_IPV6 && LWIP_ICMP6 +#include "lwip/icmp6.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP_ER 0 /* echo reply */ +#define ICMP_DUR 3 /* destination unreachable */ +#define ICMP_SQ 4 /* source quench */ +#define ICMP_RD 5 /* redirect */ +#define ICMP_ECHO 8 /* echo */ +#define ICMP_TE 11 /* time exceeded */ +#define ICMP_PP 12 /* parameter problem */ +#define ICMP_TS 13 /* timestamp */ +#define ICMP_TSR 14 /* timestamp reply */ +#define ICMP_IRQ 15 /* information request */ +#define ICMP_IR 16 /* information reply */ + +enum icmp_dur_type { + ICMP_DUR_NET = 0, /* net unreachable */ + ICMP_DUR_HOST = 1, /* host unreachable */ + ICMP_DUR_PROTO = 2, /* protocol unreachable */ + ICMP_DUR_PORT = 3, /* port unreachable */ + ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + ICMP_DUR_SR = 5 /* source route failed */ +}; + +enum icmp_te_type { + ICMP_TE_TTL = 0, /* time to live exceeded in transit */ + ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ +}; + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +/** This is the standard ICMP header only that the u32_t data + * is splitted to two u16_t like ICMP echo needs it. + * This header is also used for other ICMP types that do not + * use the data part. + */ +PACK_STRUCT_BEGIN +struct icmp_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t */ +#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t)) +#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c)) + + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +void icmp_input(struct pbuf *p, struct netif *inp); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_ICMP */ + +#if (LWIP_IPV6 && LWIP_ICMP6) +#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \ + icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \ + icmp_dest_unreach(pbuf, ICMP_DUR_PORT)) +#elif LWIP_ICMP +#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT) +#else /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ +#define icmp_port_unreach(isipv6, pbuf) +#endif /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ICMP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/igmp.h b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/igmp.h new file mode 100644 index 0000000..8aabac2 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/igmp.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +#ifndef __LWIP_IGMP_H__ +#define __LWIP_IGMP_H__ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* IGMP timer */ +#define IGMP_TMR_INTERVAL 100 /* Milliseconds */ +#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL) + +/* MAC Filter Actions, these are passed to a netif's + * igmp_mac_filter callback function. */ +#define IGMP_DEL_MAC_FILTER 0 +#define IGMP_ADD_MAC_FILTER 1 + + +/** + * igmp group structure - there is + * a list of groups for each interface + * these should really be linked from the interface, but + * if we keep them separate we will not affect the lwip original code + * too much + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ +struct igmp_group { + /** next link */ + struct igmp_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting, negative is OFF */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/* Prototypes */ +void igmp_init(void); +err_t igmp_start(struct netif *netif); +err_t igmp_stop(struct netif *netif); +void igmp_report_groups(struct netif *netif); +struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr); +void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest); +err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +void igmp_tmr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/inet.h b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/inet.h new file mode 100644 index 0000000..7bff49b --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/inet.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_H__ +#define __LWIP_INET_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in_addr { + u32_t s_addr; +}; + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IN_CLASSA(a) IP_CLASSA(a) +#define IN_CLASSA_NET IP_CLASSA_NET +#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT +#define IN_CLASSA_HOST IP_CLASSA_HOST +#define IN_CLASSA_MAX IP_CLASSA_MAX + +#define IN_CLASSB(b) IP_CLASSB(b) +#define IN_CLASSB_NET IP_CLASSB_NET +#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT +#define IN_CLASSB_HOST IP_CLASSB_HOST +#define IN_CLASSB_MAX IP_CLASSB_MAX + +#define IN_CLASSC(c) IP_CLASSC(c) +#define IN_CLASSC_NET IP_CLASSC_NET +#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT +#define IN_CLASSC_HOST IP_CLASSC_HOST +#define IN_CLASSC_MAX IP_CLASSC_MAX + +#define IN_CLASSD(d) IP_CLASSD(d) +#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */ +#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */ +#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */ +#define IN_CLASSD_MAX IP_CLASSD_MAX + +#define IN_MULTICAST(a) IP_MULTICAST(a) + +#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a) +#define IN_BADCLASS(a) IP_BADCLASS(a) + +#define IN_LOOPBACKNET IP_LOOPBACKNET + +#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr)) +#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr)) +/* ATTENTION: the next define only works because both s_addr and ip_addr_t are an u32_t effectively! */ +#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr) ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr)) + +/* directly map this to the lwip internal functions */ +#define inet_addr(cp) ipaddr_addr(cp) +#define inet_aton(cp, addr) ipaddr_aton(cp, (ip_addr_t*)addr) +#define inet_ntoa(addr) ipaddr_ntoa((ip_addr_t*)&(addr)) +#define inet_ntoa_r(addr, buf, buflen) ipaddr_ntoa_r((ip_addr_t*)&(addr), buf, buflen) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4.h b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4.h new file mode 100644 index 0000000..04b5b8a --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP4_H__ +#define __LWIP_IP4_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the function ip_output_if_opt() is only used with IGMP */ +#define IP_OPTIONS_SEND LWIP_IGMP + +#define IP_HLEN 20 + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_IGMP 2 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 + + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_hdr { + /* version / header length */ + PACK_STRUCT_FIELD(u8_t _v_hl); + /* type of service */ + PACK_STRUCT_FIELD(u8_t _tos); + /* total length */ + PACK_STRUCT_FIELD(u16_t _len); + /* identification */ + PACK_STRUCT_FIELD(u16_t _id); + /* fragment offset field */ + PACK_STRUCT_FIELD(u16_t _offset); +#define IP_RF 0x8000U /* reserved fragment flag */ +#define IP_DF 0x4000U /* dont fragment flag */ +#define IP_MF 0x2000U /* more fragments flag */ +#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */ + /* time to live */ + PACK_STRUCT_FIELD(u8_t _ttl); + /* protocol*/ + PACK_STRUCT_FIELD(u8_t _proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip_addr_p_t src); + PACK_STRUCT_FIELD(ip_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IPH_V(hdr) ((hdr)->_v_hl >> 4) +#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) +#define IPH_TOS(hdr) ((hdr)->_tos) +#define IPH_LEN(hdr) ((hdr)->_len) +#define IPH_ID(hdr) ((hdr)->_id) +#define IPH_OFFSET(hdr) ((hdr)->_offset) +#define IPH_TTL(hdr) ((hdr)->_ttl) +#define IPH_PROTO(hdr) ((hdr)->_proto) +#define IPH_CHKSUM(hdr) ((hdr)->_chksum) + +#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl)) +#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos) +#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) +#define IPH_ID_SET(hdr, id) (hdr)->_id = (id) +#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) +#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl) +#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto) +#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) + + +#define ip_init() /* Compatibility define, no init needed. */ +struct netif *ip_route(ip_addr_t *dest); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, + struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if IP_OPTIONS_SEND +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen); +#endif /* IP_OPTIONS_SEND */ + +#define ip_netif_get_local_ipX(netif) (((netif) != NULL) ? ip_2_ipX(&((netif)->ip_addr)) : NULL) + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4_addr.h b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4_addr.h new file mode 100644 index 0000000..b05ae53 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip4_addr.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP4_ADDR_H__ +#define __LWIP_IP4_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the aligned version of ip_addr_t, + used as local variable, on the stack, etc. */ +struct ip_addr { + u32_t addr; +}; + +/* This is the packed version of ip_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr_packed { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip_addr_t as well as on ip_addr_p_t. */ +typedef struct ip_addr ip_addr_t; +typedef struct ip_addr_packed ip_addr_p_t; + +/* + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Forward declaration to not include netif.h */ +struct netif; + +extern const ip_addr_t ip_addr_any; +extern const ip_addr_t ip_addr_broadcast; + +/** IP_ADDR_ can be used as a fixed IP address + * for the wildcard and the broadcast address + */ +#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any) +#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast) + +/** 255.255.255.255 */ +#define IPADDR_NONE ((u32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((u32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((u32_t)0xffffffffUL) + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IP_CLASSA_NET 0xff000000 +#define IP_CLASSA_NSHIFT 24 +#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET) +#define IP_CLASSA_MAX 128 + +#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IP_CLASSB_NET 0xffff0000 +#define IP_CLASSB_NSHIFT 16 +#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET) +#define IP_CLASSB_MAX 65536 + +#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IP_CLASSC_NET 0xffffff00 +#define IP_CLASSC_NSHIFT 8 +#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET) + +#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IP_MULTICAST(a) IP_CLASSD(a) + +#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IP_LOOPBACKNET 127 /* official! */ + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IP address given by the four byte-parts */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IP address given by the four byte-parts. + Little-endian version that prevents the use of htonl. */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** MEMCPY-like copying of IP addresses where addresses are known to be + * 16-bit-aligned if the port is correctly configured (so a port could define + * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */ +#ifndef IPADDR2_COPY +#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip_addr_t)) +#endif + +/** Copy IP address - faster than ip_addr_set: no NULL check */ +#define ip_addr_copy(dest, src) ((dest).addr = (src).addr) +/** Safely copy one IP address to another (src may be NULL) */ +#define ip_addr_set(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0 : \ + (src)->addr)) +/** Set complete address to zero */ +#define ip_addr_set_zero(ipaddr) ((ipaddr)->addr = 0) +/** Set address to IPADDR_ANY (no need for htonl()) */ +#define ip_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY) +/** Set address to loopback address */ +#define ip_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK)) +/** Safely copy one IP address to another and change byte order + * from host- to network-order. */ +#define ip_addr_set_hton(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0:\ + htonl((src)->addr))) +/** IPv4 only: set the IP address given as an u32_t */ +#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32)) +/** IPv4 only: get the IP address as an u32_t */ +#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr) + +/** Get the network address by combining host address with netmask */ +#define ip_addr_get_network(target, host, netmask) ((target)->addr = ((host)->addr) & ((netmask)->addr)) + +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == IPADDR_ANY) + +#define ip_addr_isbroadcast(ipaddr, netif) ip4_addr_isbroadcast((ipaddr)->addr, (netif)) +u8_t ip4_addr_isbroadcast(u32_t addr, const struct netif *netif); + +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask); + +#define ip_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL)) + +#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL)) + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ + ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0)) + +/* Get one byte from the 4-byte address */ +#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0]) +#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1]) +#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2]) +#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3]) +/* These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ +#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr)) +#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr)) +#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr)) +#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr)) + +/** For backwards compatibility */ +#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr) + +u32_t ipaddr_addr(const char *cp); +int ipaddr_aton(const char *cp, ip_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ipaddr_ntoa(const ip_addr_t *addr); +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip_frag.h b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip_frag.h new file mode 100644 index 0000000..47eca9f --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv4/lwip/ip_frag.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * + */ + +#ifndef __LWIP_IP_FRAG_H__ +#define __LWIP_IP_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/* IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void); +void ip_reass_tmr(void); +struct pbuf * ip_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ +#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_FRAG_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/dhcp6.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/dhcp6.h new file mode 100644 index 0000000..4b905c5 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/dhcp6.h @@ -0,0 +1,58 @@ +/** + * @file + * + * IPv6 address autoconfiguration as per RFC 4862. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * IPv6 address autoconfiguration as per RFC 4862. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_IP6_DHCP6_H__ +#define __LWIP_IP6_DHCP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + + +struct dhcp6 +{ + /*TODO: implement DHCP6*/ +}; + +#endif /* LWIP_IPV6_DHCP6 */ + +#endif /* __LWIP_IP6_DHCP6_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ethip6.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ethip6.h new file mode 100644 index 0000000..e7f412b --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ethip6.h @@ -0,0 +1,68 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_ETHIP6_H__ +#define __LWIP_ETHIP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ + +#endif /* __LWIP_ETHIP6_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/icmp6.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/icmp6.h new file mode 100644 index 0000000..74bfdbe --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/icmp6.h @@ -0,0 +1,152 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_ICMP6_H__ +#define __LWIP_ICMP6_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +enum icmp6_type { + ICMP6_TYPE_DUR = 1, /* Destination unreachable */ + ICMP6_TYPE_PTB = 2, /* Packet too big */ + ICMP6_TYPE_TE = 3, /* Time exceeded */ + ICMP6_TYPE_PP = 4, /* Parameter problem */ + ICMP6_TYPE_PE1 = 100, /* Private experimentation */ + ICMP6_TYPE_PE2 = 101, /* Private experimentation */ + ICMP6_TYPE_RSV_ERR = 127, /* Reserved for expansion of error messages */ + + ICMP6_TYPE_EREQ = 128, /* Echo request */ + ICMP6_TYPE_EREP = 129, /* Echo reply */ + ICMP6_TYPE_MLQ = 130, /* Multicast listener query */ + ICMP6_TYPE_MLR = 131, /* Multicast listener report */ + ICMP6_TYPE_MLD = 132, /* Multicast listener done */ + ICMP6_TYPE_RS = 133, /* Router solicitation */ + ICMP6_TYPE_RA = 134, /* Router advertisement */ + ICMP6_TYPE_NS = 135, /* Neighbor solicitation */ + ICMP6_TYPE_NA = 136, /* Neighbor advertisement */ + ICMP6_TYPE_RD = 137, /* Redirect */ + ICMP6_TYPE_MRA = 151, /* Multicast router advertisement */ + ICMP6_TYPE_MRS = 152, /* Multicast router solicitation */ + ICMP6_TYPE_MRT = 153, /* Multicast router termination */ + ICMP6_TYPE_PE3 = 200, /* Private experimentation */ + ICMP6_TYPE_PE4 = 201, /* Private experimentation */ + ICMP6_TYPE_RSV_INF = 255 /* Reserved for expansion of informational messages */ +}; + +enum icmp6_dur_code { + ICMP6_DUR_NO_ROUTE = 0, /* No route to destination */ + ICMP6_DUR_PROHIBITED = 1, /* Communication with destination administratively prohibited */ + ICMP6_DUR_SCOPE = 2, /* Beyond scope of source address */ + ICMP6_DUR_ADDRESS = 3, /* Address unreachable */ + ICMP6_DUR_PORT = 4, /* Port unreachable */ + ICMP6_DUR_POLICY = 5, /* Source address failed ingress/egress policy */ + ICMP6_DUR_REJECT_ROUTE = 6 /* Reject route to destination */ +}; + +enum icmp6_te_code { + ICMP6_TE_HL = 0, /* Hop limit exceeded in transit */ + ICMP6_TE_FRAG = 1 /* Fragment reassembly time exceeded */ +}; + +enum icmp6_pp_code { + ICMP6_PP_FIELD = 0, /* Erroneous header field encountered */ + ICMP6_PP_HEADER = 1, /* Unrecognized next header type encountered */ + ICMP6_PP_OPTION = 2 /* Unrecognized IPv6 option encountered */ +}; + +/** This is the standard ICMP6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t data); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** This is the ICMP6 header adapted for echo req/resp. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +void icmp6_input(struct pbuf *p, struct netif *inp); +void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c); +void icmp6_packet_too_big(struct pbuf *p, u32_t mtu); +void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c); +void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer); + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* __LWIP_ICMP6_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/inet6.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/inet6.h new file mode 100644 index 0000000..dbf98df --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/inet6.h @@ -0,0 +1,92 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_INET6_H__ +#define __LWIP_INET6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in6_addr { + union { + u8_t u8_addr[16]; + u32_t u32_addr[4]; + } un; +#define s6_addr un.u32_addr +}; + +#define IN6ADDR_ANY_INIT {0,0,0,0} +#define IN6ADDR_LOOPBACK_INIT {0,0,0,PP_HTONL(1)} + + +#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \ + (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \ + (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \ + (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];} +#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \ + (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \ + (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \ + (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];} +/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */ +#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr)) + +/* directly map this to the lwip internal functions */ +#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr) +#define inet6_ntoa(addr) ip6addr_ntoa((ip6_addr_t*)&(addr)) +#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((ip6_addr_t*)&(addr), buf, buflen) + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_INET6_H__ */ + diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6.h new file mode 100644 index 0000000..b199c95 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6.h @@ -0,0 +1,197 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_H__ +#define __LWIP_IP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP6_HLEN 40 + +#define IP6_NEXTH_HOPBYHOP 0 +#define IP6_NEXTH_TCP 6 +#define IP6_NEXTH_UDP 17 +#define IP6_NEXTH_ENCAPS 41 +#define IP6_NEXTH_ROUTING 43 +#define IP6_NEXTH_FRAGMENT 44 +#define IP6_NEXTH_ICMP6 58 +#define IP6_NEXTH_NONE 59 +#define IP6_NEXTH_DESTOPTS 60 +#define IP6_NEXTH_UDPLITE 136 + + +/* The IPv6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hdr { + /* version / traffic class / flow label */ + PACK_STRUCT_FIELD(u32_t _v_tc_fl); + /* payload length */ + PACK_STRUCT_FIELD(u16_t _plen); + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* hop limit */ + PACK_STRUCT_FIELD(u8_t _hoplim); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip6_addr_p_t src); + PACK_STRUCT_FIELD(ip6_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Hop-by-hop router alert option. */ +#define IP6_HBH_HLEN 8 +#define IP6_PAD1_OPTION 0 +#define IP6_PADN_ALERT_OPTION 1 +#define IP6_ROUTER_ALERT_OPTION 5 +#define IP6_ROUTER_ALERT_VALUE_MLD 0 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hbh_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* header length */ + PACK_STRUCT_FIELD(u8_t _hlen); + /* router alert option type */ + PACK_STRUCT_FIELD(u8_t _ra_opt_type); + /* router alert option data len */ + PACK_STRUCT_FIELD(u8_t _ra_opt_dlen); + /* router alert option data */ + PACK_STRUCT_FIELD(u16_t _ra_opt_data); + /* PadN option type */ + PACK_STRUCT_FIELD(u8_t _padn_opt_type); + /* PadN option data len */ + PACK_STRUCT_FIELD(u8_t _padn_opt_dlen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Fragment header. */ +#define IP6_FRAG_HLEN 8 +#define IP6_FRAG_OFFSET_MASK 0xfff8 +#define IP6_FRAG_MORE_FLAG 0x0001 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_frag_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* reserved */ + PACK_STRUCT_FIELD(u8_t reserved); + /* fragment offset */ + PACK_STRUCT_FIELD(u16_t _fragment_offset); + /* fragmented packet identification */ + PACK_STRUCT_FIELD(u32_t _identification); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP6H_V(hdr) ((ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f) +#define IP6H_TC(hdr) ((ntohl((hdr)->_v_tc_fl) >> 20) & 0xff) +#define IP6H_FL(hdr) (ntohl((hdr)->_v_tc_fl) & 0x000fffff) +#define IP6H_PLEN(hdr) (ntohs((hdr)->_plen)) +#define IP6H_NEXTH(hdr) ((hdr)->_nexth) +#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6) +#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim) + +#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (htonl(((v) << 28) | ((tc) << 20) | (fl))) +#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = htons(plen) +#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth) +#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl) + + +#define ip6_init() /* TODO should we init current addresses and header pointer? */ +struct netif *ip6_route(struct ip6_addr *src, struct ip6_addr *dest); +ip6_addr_t *ip6_select_source_address(struct netif *netif, ip6_addr_t * dest); +err_t ip6_input(struct pbuf *p, struct netif *inp); +err_t ip6_output(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth); +err_t ip6_output_if(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if LWIP_IPV6_MLD +err_t ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value); +#endif /* LWIP_IPV6_MLD */ + +#define ip6_netif_get_local_ipX(netif, dest) (((netif) != NULL) ? \ + ip6_2_ipX(ip6_select_source_address(netif, dest)) : NULL) + +#if IP6_DEBUG +void ip6_debug_print(struct pbuf *p); +#else +#define ip6_debug_print(p) +#endif /* IP6_DEBUG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_addr.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_addr.h new file mode 100644 index 0000000..89b5b81 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_addr.h @@ -0,0 +1,286 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Structs and macros for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_ADDR_H__ +#define __LWIP_IP6_ADDR_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* This is the aligned version of ip6_addr_t, + used as local variable, on the stack, etc. */ +struct ip6_addr { + u32_t addr[4]; +}; + +/* This is the packed version of ip6_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_addr_packed { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip6_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip6_addr_t as well as on ip6_addr_p_t. */ +typedef struct ip6_addr ip6_addr_t; +typedef struct ip6_addr_packed ip6_addr_p_t; + + +/** IP6_ADDR_ANY can be used as a fixed IPv6 address + * for the wildcard + */ +extern const ip6_addr_t ip6_addr_any; +#define IP6_ADDR_ANY ((ip6_addr_t *)&ip6_addr_any) + + + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IPv6 partial address given by byte-parts. */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IPv6 partial address given by byte-parts. +Little-endian version, stored in network order (no htonl). */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0])) & 0xffff) +#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1])) & 0xffff) +#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2])) & 0xffff) +#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3])) & 0xffff) + +/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */ +#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3];}while(0) +/** Safely copy one IPv6 address to another (src may be NULL) */ +#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ + (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ + (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + +/** Set complete address to zero */ +#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = 0;}while(0) + +/** Set address to ipv6 'any' (no need for htonl()) */ +#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) +/** Set address to ipv6 loopback address */ +#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) +/** Safely copy one IPv6 address to another and change byte order + * from host- to network-order. */ +#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : htonl((src)->addr[0]); \ + (dest)->addr[1] = (src) == NULL ? 0 : htonl((src)->addr[1]); \ + (dest)->addr[2] = (src) == NULL ? 0 : htonl((src)->addr[2]); \ + (dest)->addr[3] = (src) == NULL ? 0 : htonl((src)->addr[3]);}while(0) + + + +/** + * Determine if two IPv6 address are on the same network. + * + * @arg addr1 IPv6 address 1 + * @arg addr2 IPv6 address 2 + * @return !0 if the network identifiers of both address match + */ +#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + +#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) + +#define ip6_get_subnet_id(ip6addr) (htonl((ip6addr)->addr[2]) & 0x0000ffffUL) + +#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || \ + (((ip6addr)->addr[0] == 0) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == 0) && \ + ((ip6addr)->addr[3] == 0))) + + +#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL)) + +#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL)) + +#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL)) + +#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL)) + +#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) +#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL)) +#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL)) +#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL)) +#define ip6_addr_multicast_scope(ip6addr) ((htonl((ip6addr)->addr[0]) >> 16) & 0xf) +#define IP6_MULTICAST_SCOPE_RESERVED 0x0 +#define IP6_MULTICAST_SCOPE_RESERVED0 0x0 +#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1 +#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2 +#define IP6_MULTICAST_SCOPE_RESERVED3 0x3 +#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4 +#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5 +#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8 +#define IP6_MULTICAST_SCOPE_GLOBAL 0xe +#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf +#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff010000UL)) +#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff020000UL)) +#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff040000UL)) +#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff050000UL)) +#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff080000UL)) +#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff0e0000UL)) + +/* TODO define get/set for well-know multicast addresses, e.g. ff02::1 */ +#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) +#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + +#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL))) +#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + +#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ) + +#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ + (ip6addr)->addr[3] = htonl(0xff000000UL | (htonl(if_id) & 0x00ffffffUL));}while(0) + +#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + ((ip6addr)->addr[3] == htonl(0xff000000UL | (htonl((sn_addr)->addr[3]) & 0x00ffffffUL)))) + +/* IPv6 address states. */ +#define IP6_ADDR_INVALID 0x00 +#define IP6_ADDR_TENTATIVE 0x08 +#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */ +#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */ +#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */ +#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */ +#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */ +#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */ +#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */ +#define IP6_ADDR_VALID 0x10 +#define IP6_ADDR_PREFERRED 0x30 +#define IP6_ADDR_DEPRECATED 0x50 + +#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID) +#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE) +#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */ +#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED) +#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED) + +#define ip6_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F, \ + ipaddr != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0)) + +int ip6addr_aton(const char *cp, ip6_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip6addr_ntoa(const ip6_addr_t *addr); +char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen); + + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_ADDR_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_frag.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_frag.h new file mode 100644 index 0000000..75898b8 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/ip6_frag.h @@ -0,0 +1,102 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_FRAG_H__ +#define __LWIP_IP6_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + +/* The IPv6 reassembly timer interval in milliseconds. */ +#define IP6_REASS_TMR_INTERVAL 1000 + +/* IPv6 reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip6_reassdata { + struct ip6_reassdata *next; + struct pbuf *p; + struct ip6_hdr * iphdr; + u32_t identification; + u16_t datagram_len; + u8_t nexth; + u8_t timer; +}; + +#define ip6_reass_init() /* Compatibility define */ +void ip6_reass_tmr(void); +struct pbuf * ip6_reass(struct pbuf *p); + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG /* don't build if not configured for use in lwipopts.h */ + +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ + +err_t ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest); + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP6_FRAG_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/mld6.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/mld6.h new file mode 100644 index 0000000..abd86e5 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/mld6.h @@ -0,0 +1,118 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_MLD6_H__ +#define __LWIP_MLD6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +struct mld_group { + /** next link */ + struct mld_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip6_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/** Multicast listener report/query/done message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mld_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t max_resp_delay); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t multicast_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define MLD6_TMR_INTERVAL 100 /* Milliseconds */ + +/* MAC Filter Actions, these are passed to a netif's + * mld_mac_filter callback function. */ +#define MLD6_DEL_MAC_FILTER 0 +#define MLD6_ADD_MAC_FILTER 1 + + +#define mld6_init() /* TODO should we init tables? */ +err_t mld6_stop(struct netif *netif); +void mld6_report_groups(struct netif *netif); +void mld6_tmr(void); +struct mld_group *mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr); +void mld6_input(struct pbuf *p, struct netif *inp); +err_t mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); +err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */ + +#endif /* __LWIP_MLD6_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/nd6.h b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/nd6.h new file mode 100644 index 0000000..28636e8 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/ipv6/lwip/nd6.h @@ -0,0 +1,369 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_ND6_H__ +#define __LWIP_ND6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct for tables. */ +struct nd6_neighbor_cache_entry { + ip6_addr_t next_hop_address; + struct netif * netif; + u8_t lladdr[NETIF_MAX_HWADDR_LEN]; + /*u32_t pmtu;*/ +#if LWIP_ND6_QUEUEING + /** Pointer to queue of pending outgoing packets on this entry. */ + struct nd6_q_entry *q; +#else /* LWIP_ND6_QUEUEING */ + /** Pointer to a single pending outgoing packet on this entry. */ + struct pbuf *q; +#endif /* LWIP_ND6_QUEUEING */ + u8_t state; + u8_t isrouter; + union { + u32_t reachable_time; + u32_t delay_time; + u32_t probes_sent; + u32_t stale_time; + } counter; +}; + +struct nd6_destination_cache_entry { + ip6_addr_t destination_addr; + ip6_addr_t next_hop_addr; + u32_t pmtu; + u32_t age; +}; + +struct nd6_prefix_list_entry { + ip6_addr_t prefix; + struct netif * netif; + u32_t invalidation_timer; +#if LWIP_IPV6_AUTOCONFIG + u8_t flags; +#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04 +#endif /* LWIP_IPV6_AUTOCONFIG */ +}; + +struct nd6_router_list_entry { + struct nd6_neighbor_cache_entry * neighbor_entry; + u32_t invalidation_timer; + u8_t flags; +}; + + +enum nd6_neighbor_cache_entry_state { + ND6_NO_ENTRY = 0, + ND6_INCOMPLETE, + ND6_REACHABLE, + ND6_STALE, + ND6_DELAY, + ND6_PROBE +}; + +#if LWIP_ND6_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct nd6_q_entry { + struct nd6_q_entry *next; + struct pbuf *p; +}; +#endif /* LWIP_ND6_QUEUEING */ + +/** Neighbor solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ns_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Neighbor advertisement message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct na_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u8_t reserved[3]); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define ND6_FLAG_ROUTER (0x80) +#define ND6_FLAG_SOLICITED (0x40) +#define ND6_FLAG_OVERRIDE (0x20) + +/** Router solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct rs_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Router advertisement message header. */ +#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80) +#define ND6_RA_FLAG_OTHER_STATEFUL_CONFIG (0x40) +#define ND6_RA_FLAG_HOME_AGENT (0x20) +#define ND6_RA_PREFERENCE_MASK (0x18) +#define ND6_RA_PREFERENCE_HIGH (0x08) +#define ND6_RA_PREFERENCE_MEDIUM (0x00) +#define ND6_RA_PREFERENCE_LOW (0x18) +#define ND6_RA_PREFERENCE_DISABLED (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ra_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t current_hop_limit); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u16_t router_lifetime); + PACK_STRUCT_FIELD(u32_t reachable_time); + PACK_STRUCT_FIELD(u32_t retrans_timer); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirect message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirect_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + PACK_STRUCT_FIELD(ip6_addr_p_t destination_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Link-layer address option. */ +#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01) +#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct lladdr_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t addr[NETIF_MAX_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Prefix information option. */ +#define ND6_OPTION_TYPE_PREFIX_INFO (0x03) +#define ND6_PREFIX_FLAG_ON_LINK (0x80) +#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40) +#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20) +#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct prefix_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u32_t valid_lifetime); + PACK_STRUCT_FIELD(u32_t preferred_lifetime); + PACK_STRUCT_FIELD(u8_t reserved2[3]); + PACK_STRUCT_FIELD(u8_t site_prefix_length); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirected header option. */ +#define ND6_OPTION_TYPE_REDIR_HDR (0x04) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirected_header_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t reserved[6]); + /* Portion of redirected packet follows. */ + /* PACK_STRUCT_FIELD(u8_t redirected[8]); */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** MTU option. */ +#define ND6_OPTION_TYPE_MTU (0x05) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mtu_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(u32_t mtu); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Route information option. */ +#define ND6_OPTION_TYPE_ROUTE_INFO (24) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct route_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t preference); + PACK_STRUCT_FIELD(u32_t route_lifetime); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* the possible states of an IP address */ +#define IP6_ADDRESS_STATE_INVALID (0) +#define IP6_ADDRESS_STATE_VALID (0x4) +#define IP6_ADDRESS_STATE_PREFERRED (0x5) /* includes valid */ +#define IP6_ADDRESS_STATE_DEPRECATED (0x6) /* includes valid */ +#define IP6_ADDRESS_STATE_TENTATIV (0x8) + +/** 1 second period */ +#define ND6_TMR_INTERVAL 1000 + +/* Router tables. */ +/* TODO make these static? and entries accessible through API? */ +extern struct nd6_neighbor_cache_entry neighbor_cache[]; +extern struct nd6_destination_cache_entry destination_cache[]; +extern struct nd6_prefix_list_entry prefix_list[]; +extern struct nd6_router_list_entry default_router_list[]; + +/* Default values, can be updated by a RA message. */ +extern u32_t reachable_time; +extern u32_t retrans_timer; + +#define nd6_init() /* TODO should we init tables? */ +void nd6_tmr(void); +void nd6_input(struct pbuf *p, struct netif *inp); +s8_t nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif); +s8_t nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif); +u16_t nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif); +err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf * p); +#if LWIP_ND6_TCP_REACHABILITY_HINTS +void nd6_reachability_hint(ip6_addr_t * ip6addr); +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_ND6_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/api.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/api.h new file mode 100644 index 0000000..ac58eeb --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/api.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_H__ +#define __LWIP_API_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/netbuf.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ + +/* Flags for netconn_write (u8_t) */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ +#define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 +#define NETCONN_DONTBLOCK 0x04 + +/* Flags for struct netconn.flags (u8_t) */ +/** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores whether to wake up the original application task + if data couldn't be sent in the first try. */ +#define NETCONN_FLAG_WRITE_DELAYED 0x01 +/** Should this netconn avoid blocking? */ +#define NETCONN_FLAG_NON_BLOCKING 0x02 +/** Was the last connect action a non-blocking one? */ +#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04 +/** If this is set, a TCP netconn must call netconn_recved() to update + the TCP receive window (done automatically if not set). */ +#define NETCONN_FLAG_NO_AUTO_RECVED 0x08 +/** If a nonblocking write has been rejected before, poll_tcp needs to + check if the netconn is writable again */ +#define NETCONN_FLAG_CHECK_WRITESPACE 0x10 +#if LWIP_IPV6 +/** If this flag is set then only IPv6 communication is allowed on the + netconn. As per RFC#3493 this features defaults to OFF allowing + dual-stack usage by default. */ +#define NETCONN_FLAG_IPV6_V6ONLY 0x20 +#endif /* LWIP_IPV6 */ + + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) ((t)&0xF0) +#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0) +#if LWIP_IPV6 +#define NETCONN_TYPE_IPV6 0x08 +#define NETCONNTYPE_ISIPV6(t) ((t)&0x08) +#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF7) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF7) == NETCONN_UDPNOCHKSUM) +#else /* LWIP_IPV6 */ +#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM) +#endif /* LWIP_IPV6 */ + +/** Protocol family and type of the netconn */ +enum netconn_type { + NETCONN_INVALID = 0, + /* NETCONN_TCP Group */ + NETCONN_TCP = 0x10, +#if LWIP_IPV6 + NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */, +#endif /* LWIP_IPV6 */ + /* NETCONN_UDP Group */ + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM = 0x22, +#if LWIP_IPV6 + NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */, + NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */, + NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */, +#endif /* LWIP_IPV6 */ + /* NETCONN_RAW Group */ + NETCONN_RAW = 0x40 +#if LWIP_IPV6 + , + NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */ +#endif /* LWIP_IPV6 */ +}; + +/** Current state of the netconn. Non-TCP netconns are always + * in state NETCONN_NONE! */ +enum netconn_state { + NETCONN_NONE, + NETCONN_WRITE, + NETCONN_LISTEN, + NETCONN_CONNECT, + NETCONN_CLOSE +}; + +/** Use to inform the callback function about changes */ +enum netconn_evt { + NETCONN_EVT_RCVPLUS, + NETCONN_EVT_RCVMINUS, + NETCONN_EVT_SENDPLUS, + NETCONN_EVT_SENDMINUS, + NETCONN_EVT_ERROR +}; + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** Used for netconn_join_leave_group() */ +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE +}; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +/* forward-declare some structs to avoid to include their headers */ +struct ip_pcb; +struct tcp_pcb; +struct udp_pcb; +struct raw_pcb; +struct netconn; +struct api_msg_msg; + +/** A callback prototype to inform about events for a netconn */ +typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len); + +/** A netconn descriptor */ +struct netconn { + /** type of the netconn (TCP, UDP or RAW) */ + enum netconn_type type; + /** current state of the netconn */ + enum netconn_state state; + /** the lwIP internal protocol control block */ + union { + struct ip_pcb *ip; + struct tcp_pcb *tcp; + struct udp_pcb *udp; + struct raw_pcb *raw; + } pcb; + /** the last error this netconn had */ + err_t last_err; + /** sem that is used to synchroneously execute functions in the core context */ + sys_sem_t op_completed; + /** mbox where received packets are stored until they are fetched + by the netconn application thread (can grow quite big) */ + sys_mbox_t recvmbox; +#if LWIP_TCP + /** mbox where new connections are stored until processed + by the application thread */ + sys_mbox_t acceptmbox; +#endif /* LWIP_TCP */ + /** only used for socket layer */ +#if LWIP_SOCKET + int socket; +#endif /* LWIP_SOCKET */ +#if LWIP_SO_SNDTIMEO + /** timeout to wait for sending data (which means enqueueing data for sending + in internal buffers) */ + s32_t send_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVTIMEO + /** timeout to wait for new data to be received + (or connections to arrive for listening netconns) */ + int recv_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + /** maximum amount of bytes queued in recvmbox + not used for TCP: adjust TCP_WND instead! */ + int recv_bufsize; + /** number of bytes currently in recvmbox to be received, + tested against recv_bufsize to limit bytes on recvmbox + for UDP and RAW, used for FIONREAD */ + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ + u8_t flags; +#if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores how much is already sent. */ + size_t write_offset; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. + Also used during connect and close. */ + struct api_msg_msg *current_msg; +#endif /* LWIP_TCP */ + /** A callback function that is informed about events for this netconn */ + netconn_callback callback; +}; + +/** Register an Network connection event */ +#define API_EVENT(c,e,l) if (c->callback) { \ + (*c->callback)(c, e, l); \ + } + +/** Set conn->last_err to err but don't overwrite fatal errors */ +#define NETCONN_SET_SAFE_ERR(conn, err) do { \ + SYS_ARCH_DECL_PROTECT(lev); \ + SYS_ARCH_PROTECT(lev); \ + if (!ERR_IS_FATAL((conn)->last_err)) { \ + (conn)->last_err = err; \ + } \ + SYS_ARCH_UNPROTECT(lev); \ +} while(0); + +/* Network connection functions: */ +#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL) +#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c) +struct +netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); +err_t netconn_delete(struct netconn *conn); +/** Get the type of a netconn (as enum netconn_type). */ +#define netconn_type(conn) (conn->type) + +err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr, + u16_t *port, u8_t local); +#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0) +#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1) + +err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_disconnect (struct netconn *conn); +err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog); +#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG) +err_t netconn_accept(struct netconn *conn, struct netconn **new_conn); +err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf); +err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf); +void netconn_recved(struct netconn *conn, u32_t length); +err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, + ip_addr_t *addr, u16_t port); +err_t netconn_send(struct netconn *conn, struct netbuf *buf); +err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written); +#define netconn_write(conn, dataptr, size, apiflags) \ + netconn_write_partly(conn, dataptr, size, apiflags, NULL) +err_t netconn_close(struct netconn *conn); +err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr, + ip_addr_t *netif_addr, enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + +#define netconn_bind_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_bind(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_connect_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_connect(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_sendto_ip6(conn, buf, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_sendto(conn, buf, ip6_2_ip(ip6addr), port) : ERR_VAL) +#if LWIP_IPV6_MLD +#define netconn_join_leave_group_ip6(conn, multiaddr, srcaddr, join_or_leave) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_join_leave_group(conn, ip6_2_ip(multiaddr), ip6_2_ip(srcaddr), join_or_leave) :\ + ERR_VAL) +#endif /* LWIP_IPV6_MLD*/ +#endif /* LWIP_IPV6 */ + +#define netconn_err(conn) ((conn)->last_err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +/** Set the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_set_nonblocking(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0) +/** Get the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0) + +/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_set_noautorecved(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0) +/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0) + +#if LWIP_SO_SNDTIMEO +/** Set the send timeout in milliseconds */ +#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout)) +/** Get the send timeout in milliseconds */ +#define netconn_get_sendtimeout(conn) ((conn)->send_timeout) +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO +/** Set the receive timeout in milliseconds */ +#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout)) +/** Get the receive timeout in milliseconds */ +#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout) +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF +/** Set the receive buffer in bytes */ +#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize)) +/** Get the receive buffer in bytes */ +#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize) +#endif /* LWIP_SO_RCVBUF*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/api_msg.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/api_msg.h new file mode 100644 index 0000000..8268036 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/api_msg.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_MSG_H__ +#define __LWIP_API_MSG_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* For the netconn API, these values are use as a bitmask! */ +#define NETCONN_SHUT_RD 1 +#define NETCONN_SHUT_WR 2 +#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR) + +/* IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ +/** This struct includes everything that is necessary to execute a function + for a netconn in another thread context (mainly used to process netconns + in the tcpip_thread context to be thread safe). */ +struct api_msg_msg { + /** The netconn which to process - always needed: it includes the semaphore + which is used to block the application thread until the function finished. */ + struct netconn *conn; + /** The return value of the function executed in tcpip_thread. */ + err_t err; + /** Depending on the executed function, one of these union members is used */ + union { + /** used for lwip_netconn_do_send */ + struct netbuf *b; + /** used for lwip_netconn_do_newconn */ + struct { + u8_t proto; + } n; + /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */ + struct { + ip_addr_t *ipaddr; + u16_t port; + } bc; + /** used for lwip_netconn_do_getaddr */ + struct { + ipX_addr_t *ipaddr; + u16_t *port; + u8_t local; + } ad; + /** used for lwip_netconn_do_write */ + struct { + const void *dataptr; + size_t len; + u8_t apiflags; +#if LWIP_SO_SNDTIMEO + u32_t time_started; +#endif /* LWIP_SO_SNDTIMEO */ + } w; + /** used for lwip_netconn_do_recv */ + struct { + u32_t len; + } r; + /** used for lwip_netconn_do_close (/shutdown) */ + struct { + u8_t shut; + } sd; +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + /** used for lwip_netconn_do_join_leave_group */ + struct { + ipX_addr_t *multiaddr; + ipX_addr_t *netif_addr; + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ + } msg; +}; + +/** This struct contains a function to execute in another thread context and + a struct api_msg_msg that serves as an argument for this function. + This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */ +struct api_msg { + /** function to execute in tcpip_thread context */ + void (* function)(struct api_msg_msg *msg); + /** arguments for this function */ + struct api_msg_msg msg; +}; + +#if LWIP_DNS +/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn, + it has its own struct (to avoid struct api_msg getting bigger than necessary). + lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + (see netconn_gethostbyname). */ +struct dns_api_msg { + /** Hostname to query or dotted IP address string */ + const char *name; + /** Rhe resolved address is stored here */ + ip_addr_t *addr; + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t *sem; + /** Errors are given back here */ + err_t *err; +}; +#endif /* LWIP_DNS */ + +void lwip_netconn_do_newconn ( struct api_msg_msg *msg); +void lwip_netconn_do_delconn ( struct api_msg_msg *msg); +void lwip_netconn_do_bind ( struct api_msg_msg *msg); +void lwip_netconn_do_connect ( struct api_msg_msg *msg); +void lwip_netconn_do_disconnect ( struct api_msg_msg *msg); +void lwip_netconn_do_listen ( struct api_msg_msg *msg); +void lwip_netconn_do_send ( struct api_msg_msg *msg); +void lwip_netconn_do_recv ( struct api_msg_msg *msg); +void lwip_netconn_do_write ( struct api_msg_msg *msg); +void lwip_netconn_do_getaddr ( struct api_msg_msg *msg); +void lwip_netconn_do_close ( struct api_msg_msg *msg); +void lwip_netconn_do_shutdown ( struct api_msg_msg *msg); +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +void lwip_netconn_do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +void lwip_netconn_do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_MSG_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/arch.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/arch.h new file mode 100644 index 0000000..101a753 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/arch.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ARCH_H__ +#define __LWIP_ARCH_H__ + +#include "arch/cc.h" + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +/** Temporary: define format string for size_t if not defined in cc.h */ +#ifndef SZT_F +#define SZT_F U32_F +#endif /* SZT_F */ +/** Temporary upgrade helper: define format string for u8_t as hex if not + defined in cc.h */ +#ifndef X8_F +#define X8_F "02x" +#endif /* X8_F */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PACK_STRUCT_BEGIN +#define PACK_STRUCT_BEGIN +#endif /* PACK_STRUCT_BEGIN */ + +#ifndef PACK_STRUCT_END +#define PACK_STRUCT_END +#endif /* PACK_STRUCT_END */ + +#ifndef PACK_STRUCT_FIELD +#define PACK_STRUCT_FIELD(x) x +#endif /* PACK_STRUCT_FIELD */ + + +#ifndef LWIP_UNUSED_ARG +#define LWIP_UNUSED_ARG(x) (void)x +#endif /* LWIP_UNUSED_ARG */ + + +#ifdef LWIP_PROVIDE_ERRNO + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ARCH_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/debug.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/debug.h new file mode 100644 index 0000000..0fe0413 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/debug.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEBUG_H__ +#define __LWIP_DEBUG_H__ + +#include "lwip/arch.h" +#include "lwip/opt.h" + +/** lower two bits indicate debug level + * - 0 all + * - 1 warning + * - 2 serious + * - 3 severe + */ +#define LWIP_DBG_LEVEL_ALL 0x00 +#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */ +#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */ +#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */ +#define LWIP_DBG_LEVEL_SEVERE 0x03 +#define LWIP_DBG_MASK_LEVEL 0x03 + +/** flag for LWIP_DEBUGF to enable that debug message */ +#define LWIP_DBG_ON 0x80U +/** flag for LWIP_DEBUGF to disable that debug message */ +#define LWIP_DBG_OFF 0x00U + +/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */ +#define LWIP_DBG_TRACE 0x40U +/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */ +#define LWIP_DBG_STATE 0x20U +/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */ +#define LWIP_DBG_FRESH 0x10U +/** flag for LWIP_DEBUGF to halt after printing this debug message */ +#define LWIP_DBG_HALT 0x08U + +#ifndef LWIP_NOASSERT +#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \ + LWIP_PLATFORM_ASSERT(message); } while(0) +#else /* LWIP_NOASSERT */ +#define LWIP_ASSERT(message, assertion) +#endif /* LWIP_NOASSERT */ + +/** if "expression" isn't true, then print "message" and execute "handler" expression */ +#ifndef LWIP_ERROR +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + LWIP_PLATFORM_ASSERT(message); handler;}} while(0) +#endif /* LWIP_ERROR */ + +#ifdef LWIP_DEBUG +/** print debug message only if debug message type is enabled... + * AND is of correct type AND is at least LWIP_DBG_LEVEL + */ +#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ + LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ + } \ + } while(0) + +#else /* LWIP_DEBUG */ +#define LWIP_DEBUGF(debug, message) +#endif /* LWIP_DEBUG */ + +#endif /* __LWIP_DEBUG_H__ */ + diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/def.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/def.h new file mode 100644 index 0000000..73a1b56 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/def.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEF_H__ +#define __LWIP_DEF_H__ + +/* arch.h might define NULL already */ +#include "lwip/arch.h" +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* Endianess-optimized shifting of two u8_t to create one u16_t */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define LWIP_MAKE_U16(a, b) ((a << 8) | b) +#else +#define LWIP_MAKE_U16(a, b) ((b << 8) | a) +#endif + +#ifndef LWIP_PLATFORM_BYTESWAP +#define LWIP_PLATFORM_BYTESWAP 0 +#endif + +#ifndef LWIP_PREFIX_BYTEORDER_FUNCS +/* workaround for naming collisions on some platforms */ + +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ + +#define htons(x) lwip_htons(x) +#define ntohs(x) lwip_ntohs(x) +#define htonl(x) lwip_htonl(x) +#define ntohl(x) lwip_ntohl(x) +#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */ + +#if BYTE_ORDER == BIG_ENDIAN +#define lwip_htons(x) (x) +#define lwip_ntohs(x) (x) +#define lwip_htonl(x) (x) +#define lwip_ntohl(x) (x) +#define PP_HTONS(x) (x) +#define PP_NTOHS(x) (x) +#define PP_HTONL(x) (x) +#define PP_NTOHL(x) (x) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#if LWIP_PLATFORM_BYTESWAP +#define lwip_htons(x) LWIP_PLATFORM_HTONS(x) +#define lwip_ntohs(x) LWIP_PLATFORM_HTONS(x) +#define lwip_htonl(x) LWIP_PLATFORM_HTONL(x) +#define lwip_ntohl(x) LWIP_PLATFORM_HTONL(x) +#else /* LWIP_PLATFORM_BYTESWAP */ +u16_t lwip_htons(u16_t x); +u16_t lwip_ntohs(u16_t x); +u32_t lwip_htonl(u32_t x); +u32_t lwip_ntohl(u32_t x); +#endif /* LWIP_PLATFORM_BYTESWAP */ + +/* These macros should be calculated by the preprocessor and are used + with compile-time constants only (so that there is no little-endian + overhead at runtime). */ +#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) +#define PP_NTOHS(x) PP_HTONS(x) +#define PP_HTONL(x) ((((x) & 0xff) << 24) | \ + (((x) & 0xff00) << 8) | \ + (((x) & 0xff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) +#define PP_NTOHL(x) PP_HTONL(x) + +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_DEF_H__ */ + diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/dhcp.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/dhcp.h new file mode 100644 index 0000000..32d9338 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/dhcp.h @@ -0,0 +1,242 @@ +/** @file + */ + +#ifndef __LWIP_DHCP_H__ +#define __LWIP_DHCP_H__ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** period (in seconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_SECS 60 +/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL) +/** period (in milliseconds) of the application calling dhcp_fine_tmr() */ +#define DHCP_FINE_TIMER_MSECS 500 + +#define DHCP_CHADDR_LEN 16U +#define DHCP_SNAME_LEN 64U +#define DHCP_FILE_LEN 128U + +struct dhcp +{ + /** transaction identifier of last sent request */ + u32_t xid; + /** our connection to the DHCP server */ + struct udp_pcb *pcb; + /** incoming msg */ + struct dhcp_msg *msg_in; + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif + u8_t subnet_mask_given; + + struct pbuf *p_out; /* pbuf of outcoming msg */ + struct dhcp_msg *msg_out; /* outgoing msg */ + u16_t options_out_len; /* outgoing msg options length */ + u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */ + u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ + u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ + ip_addr_t server_ip_addr; /* dhcp server address that offered this lease */ + ip_addr_t offered_ip_addr; + ip_addr_t offered_sn_mask; + ip_addr_t offered_gw_addr; + + u32_t offered_t0_lease; /* lease period (in seconds) */ + u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ + u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */ + /* @todo: LWIP_DHCP_BOOTP_FILE configuration option? + integrate with possible TFTP-client for booting? */ +#if LWIP_DHCP_BOOTP_FILE + ip_addr_t offered_si_addr; + char boot_file_name[DHCP_FILE_LEN]; +#endif /* LWIP_DHCP_BOOTPFILE */ +}; + +/* MUST be compiled with "pack structs" or equivalent! */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** minimum set of fields of any DHCP message */ +struct dhcp_msg +{ + PACK_STRUCT_FIELD(u8_t op); + PACK_STRUCT_FIELD(u8_t htype); + PACK_STRUCT_FIELD(u8_t hlen); + PACK_STRUCT_FIELD(u8_t hops); + PACK_STRUCT_FIELD(u32_t xid); + PACK_STRUCT_FIELD(u16_t secs); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FIELD(ip_addr_p_t ciaddr); + PACK_STRUCT_FIELD(ip_addr_p_t yiaddr); + PACK_STRUCT_FIELD(ip_addr_p_t siaddr); + PACK_STRUCT_FIELD(ip_addr_p_t giaddr); + PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]); + PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]); + PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]); + PACK_STRUCT_FIELD(u32_t cookie); +#define DHCP_MIN_OPTIONS_LEN 68U +/** make sure user does not configure this too small */ +#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN)) +# undef DHCP_OPTIONS_LEN +#endif +/** allow this to be configured in lwipopts.h, but not too small */ +#if (!defined(DHCP_OPTIONS_LEN)) +/** set this to be sufficient for your options in outgoing DHCP msgs */ +# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN +#endif + PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp); +/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */ +#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0) +void dhcp_cleanup(struct netif *netif); +/** start DHCP configuration */ +err_t dhcp_start(struct netif *netif); +/** enforce early lease renewal (not needed normally)*/ +err_t dhcp_renew(struct netif *netif); +/** release the DHCP lease, usually called before dhcp_stop()*/ +err_t dhcp_release(struct netif *netif); +/** stop DHCP configuration */ +void dhcp_stop(struct netif *netif); +/** inform server of our manual IP address */ +void dhcp_inform(struct netif *netif); +/** Handle a possible change in the network configuration */ +void dhcp_network_changed(struct netif *netif); + +/** if enabled, check whether the offered IP address is not in use, using ARP */ +#if DHCP_DOES_ARP_CHECK +void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr); +#endif + +/** to be called every minute */ +void dhcp_coarse_tmr(void); +/** to be called every half second */ +void dhcp_fine_tmr(void); + +/** DHCP message item offsets and length */ +#define DHCP_OP_OFS 0 +#define DHCP_HTYPE_OFS 1 +#define DHCP_HLEN_OFS 2 +#define DHCP_HOPS_OFS 3 +#define DHCP_XID_OFS 4 +#define DHCP_SECS_OFS 8 +#define DHCP_FLAGS_OFS 10 +#define DHCP_CIADDR_OFS 12 +#define DHCP_YIADDR_OFS 16 +#define DHCP_SIADDR_OFS 20 +#define DHCP_GIADDR_OFS 24 +#define DHCP_CHADDR_OFS 28 +#define DHCP_SNAME_OFS 44 +#define DHCP_FILE_OFS 108 +#define DHCP_MSG_LEN 236 + +#define DHCP_COOKIE_OFS DHCP_MSG_LEN +#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4) + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +/** DHCP client states */ +#define DHCP_OFF 0 +#define DHCP_REQUESTING 1 +#define DHCP_INIT 2 +#define DHCP_REBOOTING 3 +#define DHCP_REBINDING 4 +#define DHCP_RENEWING 5 +#define DHCP_SELECTING 6 +#define DHCP_INFORMING 7 +#define DHCP_CHECKING 8 +#define DHCP_PERMANENT 9 +#define DHCP_BOUND 10 +/** not yet implemented #define DHCP_RELEASING 11 */ +#define DHCP_BACKING_OFF 12 + +/** AUTOIP cooperatation flags */ +#define DHCP_AUTOIP_COOP_STATE_OFF 0 +#define DHCP_AUTOIP_COOP_STATE_ON 1 + +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/** DHCP message types */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/** DHCP hardware type, currently only ethernet is supported */ +#define DHCP_HTYPE_ETH 1 + +#define DHCP_MAGIC_COOKIE 0x63825363UL + +/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */ + +/** BootP options */ +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */ +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_HOSTNAME 12 +#define DHCP_OPTION_IP_TTL 23 +#define DHCP_OPTION_MTU 26 +#define DHCP_OPTION_BROADCAST 28 +#define DHCP_OPTION_TCP_TTL 37 +#define DHCP_OPTION_END 255 + +/** DHCP options */ +#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ +#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ +#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */ + +#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ +#define DHCP_OPTION_MESSAGE_TYPE_LEN 1 + +#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */ + +#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */ +#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2 + +#define DHCP_OPTION_T1 58 /* T1 renewal time */ +#define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 +#define DHCP_OPTION_CLIENT_ID 61 +#define DHCP_OPTION_TFTP_SERVERNAME 66 +#define DHCP_OPTION_BOOTFILE 67 + +/** possible combinations of overloading the file and sname fields with options */ +#define DHCP_OVERLOAD_NONE 0 +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_SNAME_FILE 3 + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DHCP */ + +#endif /*__LWIP_DHCP_H__*/ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/dns.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/dns.h new file mode 100644 index 0000000..6c7d9b0 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/dns.h @@ -0,0 +1,124 @@ +/** + * lwip DNS resolver header file. + + * Author: Jim Pettinato + * April 2007 + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LWIP_DNS_H__ +#define __LWIP_DNS_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/** DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ + +/** DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ + +/* The size used for the next line is rather a hack, but it prevents including socket.h in all files + that include memp.h, and that would possibly break portability (since socket.h defines some types + and constants possibly already define by the OS). + Calculation rule: + sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1 byte zero-termination */ +#define NETDB_ELEM_SIZE (32 + 16 + DNS_MAX_NAME_LENGTH + 1) + +#if DNS_LOCAL_HOSTLIST +/** struct used for local host-list */ +struct local_hostlist_entry { + /** static hostname */ + const char *name; + /** static host address in network byteorder */ + ip_addr_t addr; + struct local_hostlist_entry *next; +}; +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN +#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH +#endif +#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1)) +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg); + +void dns_init(void); +void dns_tmr(void); +void dns_setserver(u8_t numdns, ip_addr_t *dnsserver); +ip_addr_t dns_getserver(u8_t numdns); +err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr, + dns_found_callback found, void *callback_arg); + +#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +int dns_local_removehost(const char *hostname, const ip_addr_t *addr); +err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr); +#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/err.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/err.h new file mode 100644 index 0000000..ac90772 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/err.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ERR_H__ +#define __LWIP_ERR_H__ + +#include "lwip/opt.h" +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define LWIP_ERR_T in cc.h if you want to use + * a different type for your platform (must be signed). */ +#ifdef LWIP_ERR_T +typedef LWIP_ERR_T err_t; +#else /* LWIP_ERR_T */ +typedef s8_t err_t; +#endif /* LWIP_ERR_T*/ + +/* Definitions for error constants. */ + +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ +#define ERR_RTE -4 /* Routing problem. */ +#define ERR_INPROGRESS -5 /* Operation in progress */ +#define ERR_VAL -6 /* Illegal value. */ +#define ERR_WOULDBLOCK -7 /* Operation would block. */ +#define ERR_USE -8 /* Address in use. */ +#define ERR_ISCONN -9 /* Already connected. */ + +#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) + +#define ERR_ABRT -10 /* Connection aborted. */ +#define ERR_RST -11 /* Connection reset. */ +#define ERR_CLSD -12 /* Connection closed. */ +#define ERR_CONN -13 /* Not connected. */ + +#define ERR_ARG -14 /* Illegal argument. */ + +#define ERR_IF -15 /* Low-level netif error */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ERR_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/inet_chksum.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/inet_chksum.h new file mode 100644 index 0000000..e168788 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/inet_chksum.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_CHKSUM_H__ +#define __LWIP_INET_CHKSUM_H__ + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +/** Swap the bytes in an u16_t: much like htons() for little-endian */ +#ifndef SWAP_BYTES_IN_WORD +#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) +/* little endian and PLATFORM_BYTESWAP defined */ +#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w) +#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */ +/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */ +#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8) +#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/ +#endif /* SWAP_BYTES_IN_WORD */ + +/** Split an u32_t in two u16_ts and add them up */ +#ifndef FOLD_U32T +#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL)) +#endif + +#if LWIP_CHECKSUM_ON_COPY +/** Function-like macro: same as MEMCPY but returns the checksum of copied data + as u16_t */ +#ifndef LWIP_CHKSUM_COPY +#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len) +#ifndef LWIP_CHKSUM_COPY_ALGORITHM +#define LWIP_CHKSUM_COPY_ALGORITHM 1 +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ +#endif /* LWIP_CHKSUM_COPY */ +#else /* LWIP_CHECKSUM_ON_COPY */ +#define LWIP_CHKSUM_COPY_ALGORITHM 0 +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, + u16_t proto_len, u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest); +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#if LWIP_IPV6 +u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest); +u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest); + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo(p, proto, proto_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo(p, proto, proto_len, ipX_2_ip(src), ipX_2_ip(dest))) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip(src), ipX_2_ip(dest))) + +#else /* LWIP_IPV6 */ + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + inet_chksum_pseudo(p, proto, proto_len, src, dest) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, src, dest) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/init.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/init.h new file mode 100644 index 0000000..4e2e285 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/init.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INIT_H__ +#define __LWIP_INIT_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** X.x.x: Major version of the stack */ +#define LWIP_VERSION_MAJOR 1U +/** x.X.x: Minor version of the stack */ +#define LWIP_VERSION_MINOR 4U +/** x.x.X: Revision of the stack */ +#define LWIP_VERSION_REVISION 1U +/** For release candidates, this is set to 1..254 + * For official releases, this is set to 255 (LWIP_RC_RELEASE) + * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */ +#define LWIP_VERSION_RC 0U + +/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ +#define LWIP_RC_RELEASE 255U +/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */ +#define LWIP_RC_DEVELOPMENT 0U + +#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE) +#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT) +#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT)) + +/** Provides the version of the stack */ +#define LWIP_VERSION (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \ + LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC) + +/* Modules initialization */ +void lwip_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/ip.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/ip.h new file mode 100644 index 0000000..a0cd1d4 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/ip.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_H__ +#define __LWIP_IP_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" +#include "lwip/ip4.h" +#include "lwip/ip6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#ifdef IP_HDRINCL +#undef IP_HDRINCL +#endif /* IP_HDRINCL */ +#define IP_HDRINCL NULL + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +#if LWIP_IPV6 +#define IP_PCB_ISIPV6_MEMBER u8_t isipv6; +#define IP_PCB_IPVER_EQ(pcb1, pcb2) ((pcb1)->isipv6 == (pcb2)->isipv6) +#define IP_PCB_IPVER_INPUT_MATCH(pcb) (ip_current_is_v6() ? \ + ((pcb)->isipv6 != 0) : \ + ((pcb)->isipv6 == 0)) +#define PCB_ISIPV6(pcb) ((pcb)->isipv6) +#else +#define IP_PCB_ISIPV6_MEMBER +#define IP_PCB_IPVER_EQ(pcb1, pcb2) 1 +#define IP_PCB_IPVER_INPUT_MATCH(pcb) 1 +#define PCB_ISIPV6(pcb) 0 +#endif /* LWIP_IPV6 */ + +/* This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB \ + IP_PCB_ISIPV6_MEMBER \ + /* ip addresses in network byte order */ \ + ipX_addr_t local_ip; \ + ipX_addr_t remote_ip; \ + /* Socket options */ \ + u8_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; + +/* + * Option flags per-socket. These are the same like SO_XXX. + */ +/*#define SOF_DEBUG 0x01U Unimplemented: turn on debugging info recording */ +#define SOF_ACCEPTCONN 0x02U /* socket has had listen() */ +#define SOF_REUSEADDR 0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE 0x08U /* keep connections alive */ +/*#define SOF_DONTROUTE 0x10U Unimplemented: just use interface addresses */ +#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +/*#define SOF_USELOOPBACK 0x40U Unimplemented: bypass hardware when possible */ +#define SOF_LINGER 0x80U /* linger on close if data present */ +/*#define SOF_OOBINLINE 0x0100U Unimplemented: leave received OOB data in line */ +/*#define SOF_REUSEPORT 0x0200U Unimplemented: allow local address & port reuse */ + +/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ +#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/) + +/* Global variables of this module, kept in a struct for efficient access using base+index. */ +struct ip_globals +{ + /** The interface that provided the packet for the current callback invocation. */ + struct netif *current_netif; + /** Header of the input packet currently being processed. */ + const struct ip_hdr *current_ip4_header; +#if LWIP_IPV6 + /** Header of the input IPv6 packet currently being processed. */ + const struct ip6_hdr *current_ip6_header; +#endif /* LWIP_IPV6 */ + /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */ + u16_t current_ip_header_tot_len; + /** Source IP address of current_header */ + ipX_addr_t current_iphdr_src; + /** Destination IP address of current_header */ + ipX_addr_t current_iphdr_dest; +}; +extern struct ip_globals ip_data; + + +/** Get the interface that received the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_netif() (ip_data.current_netif) +/** Get the IP header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_header() (ip_data.current_ip4_header) +/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */ +#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len) +/** Source IP address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +#if LWIP_IPV6 +/** Get the IPv6 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip6_current_header() (ip_data.current_ip6_header) +/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */ +#define ip_current_is_v6() (ip6_current_header() != NULL) +/** Source IPv6 address of current_header */ +#define ip6_current_src_addr() (ipX_2_ip6(&ip_data.current_iphdr_src)) +/** Destination IPv6 address of current_header */ +#define ip6_current_dest_addr() (ipX_2_ip6(&ip_data.current_iphdr_dest)) +/** Get the transport layer protocol */ +#define ip_current_header_proto() (ip_current_is_v6() ? \ + IP6H_NEXTH(ip6_current_header()) :\ + IPH_PROTO(ip_current_header())) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((ip_current_is_v6() ? \ + (u8_t*)ip6_current_header() : (u8_t*)ip_current_header()) + ip_current_header_tot_len())) + +/** Set an IP_PCB to IPv6 (IPv4 is the default) */ +#define ip_set_v6(pcb, val) do{if(pcb != NULL) { pcb->isipv6 = val; }}while(0) + +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (ipX_2_ip(&ip_data.current_iphdr_src)) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (ipX_2_ip(&ip_data.current_iphdr_dest)) + +#else /* LWIP_IPV6 */ + +/** Always returns FALSE when only supporting IPv4 */ +#define ip_current_is_v6() 0 +/** Get the transport layer protocol */ +#define ip_current_header_proto() IPH_PROTO(ip_current_header()) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((u8_t*)ip_current_header() + ip_current_header_tot_len())) +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (&ip_data.current_iphdr_dest) + +#endif /* LWIP_IPV6 */ + +/** Union source address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Union destination address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +/** Gets an IP pcb option (SOF_* flags) */ +#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt)) +/** Sets an IP pcb option (SOF_* flags) */ +#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) +/** Resets an IP pcb option (SOF_* flags) */ +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt)) + +#if LWIP_IPV6 +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ((isipv6) ? \ + ip6_output(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto) : \ + ip_output(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto)) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ((isipv6) ? \ + ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip_output_if(p, (src), (dest), ttl, tos, proto, netif)) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ((isipv6) ? \ + ip6_output_hinted(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto, addr_hint) : \ + ip_output_hinted(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto, addr_hint)) +#define ipX_route(isipv6, src, dest) \ + ((isipv6) ? \ + ip6_route(ipX_2_ip6(src), ipX_2_ip6(dest)) : \ + ip_route(ipX_2_ip(dest))) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ((isipv6) ? \ + ip6_netif_get_local_ipX(netif, ipX_2_ip6(dest)) : \ + ip_netif_get_local_ipX(netif)) +#define ipX_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip_debug_print(p)) +#else /* LWIP_IPV6 */ +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ip_output(p, src, dest, ttl, tos, proto) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ip_output_if(p, src, dest, ttl, tos, proto, netif) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) +#define ipX_route(isipv6, src, dest) \ + ip_route(ipX_2_ip(dest)) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ip_netif_get_local_ipX(netif) +#define ipX_debug_print(is_ipv6, p) ip_debug_print(p) +#endif /* LWIP_IPV6 */ + +#define ipX_route_get_local_ipX(isipv6, src, dest, netif, ipXaddr) do { \ + (netif) = ipX_route(isipv6, src, dest); \ + (ipXaddr) = ipX_netif_get_local_ipX(isipv6, netif, dest); \ +}while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/ip_addr.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/ip_addr.h new file mode 100644 index 0000000..7bd03cb --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/ip_addr.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_ADDR_H__ +#define __LWIP_IP_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#include "lwip/ip4_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_IPV6 +/* A union struct for both IP version's addresses. */ +typedef union { + ip_addr_t ip4; + ip6_addr_t ip6; +} ipX_addr_t; + +/** These functions only exist for type-safe conversion from ip_addr_t to + ip6_addr_t and back */ +#ifdef LWIP_ALLOW_STATIC_FN_IN_HEADER +static ip6_addr_t* ip_2_ip6(ip_addr_t *ipaddr) +{ return (ip6_addr_t*)ipaddr;} +static ip_addr_t* ip6_2_ip(ip6_addr_t *ip6addr) +{ return (ip_addr_t*)ip6addr; } +static ipX_addr_t* ip_2_ipX(ip_addr_t *ipaddr) +{ return (ipX_addr_t*)ipaddr; } +static ipX_addr_t* ip6_2_ipX(ip6_addr_t *ip6addr) +{ return (ipX_addr_t*)ip6addr; } +#else /* LWIP_ALLOW_STATIC_FN_IN_HEADER */ +#define ip_2_ip6(ipaddr) ((ip6_addr_t*)(ipaddr)) +#define ip6_2_ip(ip6addr) ((ip_addr_t*)(ip6addr)) +#define ip_2_ipX(ipaddr) ((ipX_addr_t*)ipaddr) +#define ip6_2_ipX(ip6addr) ((ipX_addr_t*)ip6addr) +#endif /* LWIP_ALLOW_STATIC_FN_IN_HEADER*/ +#define ipX_2_ip6(ip6addr) (&((ip6addr)->ip6)) +#define ipX_2_ip(ipaddr) (&((ipaddr)->ip4)) + +#define ipX_addr_copy(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_copy((dest).ip6, (src).ip6); }else{ \ + ip_addr_copy((dest).ip4, (src).ip4); }}while(0) +#define ipX_addr_set(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ipX_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), ipX_2_ip(src)); }}while(0) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ip_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), src); }}while(0) +#define ipX_addr_set_zero(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_zero(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_zero(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_any(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_any(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_loopback(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_loopback(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_hton(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set_hton(ipX_2_ip6(ipaddr), (src)) ;}else{ \ + ip_addr_set_hton(ipX_2_ip(ipaddr), (src));}}while(0) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ((is_ipv6) ? \ + ip6_addr_cmp(ipX_2_ip6(addr1), ipX_2_ip6(addr2)) : \ + ip_addr_cmp(ipX_2_ip(addr1), ipX_2_ip(addr2))) +#define ipX_addr_isany(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_isany(ipX_2_ip6(ipaddr)) : \ + ip_addr_isany(ipX_2_ip(ipaddr))) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_ismulticast(ipX_2_ip6(ipaddr)) : \ + ip_addr_ismulticast(ipX_2_ip(ipaddr))) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) do { if(is_ipv6) { \ + ip6_addr_debug_print(debug, ipX_2_ip6(ipaddr)); } else { \ + ip_addr_debug_print(debug, ipX_2_ip(ipaddr)); }}while(0) + +#else /* LWIP_IPV6 */ + +typedef ip_addr_t ipX_addr_t; +#define ipX_2_ip(ipaddr) (ipaddr) +#define ip_2_ipX(ipaddr) (ipaddr) + +#define ipX_addr_copy(is_ipv6, dest, src) ip_addr_copy(dest, src) +#define ipX_addr_set(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_zero(is_ipv6, ipaddr) ip_addr_set_zero(ipaddr) +#define ipX_addr_set_any(is_ipv6, ipaddr) ip_addr_set_any(ipaddr) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) ip_addr_set_loopback(ipaddr) +#define ipX_addr_set_hton(is_ipv6, dest, src) ip_addr_set_hton(dest, src) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ip_addr_cmp(addr1, addr2) +#define ipX_addr_isany(is_ipv6, ipaddr) ip_addr_isany(ipaddr) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ip_addr_ismulticast(ipaddr) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) ip_addr_debug_print(debug, ipaddr) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/mem.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/mem.h new file mode 100644 index 0000000..5bb906b --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/mem.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_MEM_H__ +#define __LWIP_MEM_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; +#define MEM_SIZE_F SZT_F + +/* aliases for C library malloc() */ +#define mem_init() +/* in case C library malloc() needs extra protection, + * allow these defines to be overridden. + */ +#ifndef mem_free +#define mem_free free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#endif +/* Since there is no C library allocation function to shrink memory without + moving it, define this to nothing. */ +#ifndef mem_trim +#define mem_trim(mem, size) (mem) +#endif +#else /* MEM_LIBC_MALLOC */ + +/* MEM_SIZE would have to be aligned, but using 64000 here instead of + * 65535 leaves some room for alignment... + */ +#if MEM_SIZE > 64000L +typedef u32_t mem_size_t; +#define MEM_SIZE_F U32_F +#else +typedef u16_t mem_size_t; +#define MEM_SIZE_F U16_F +#endif /* MEM_SIZE > 64000 */ + +#if MEM_USE_POOLS +/** mem_init is not used when using pools instead of a heap */ +#define mem_init() +/** mem_trim is not used when using pools instead of a heap: + we can't free part of a pool element and don't want to copy the rest */ +#define mem_trim(mem, size) (mem) +#else /* MEM_USE_POOLS */ +/* lwIP alternative malloc */ +void mem_init(void); +void *mem_trim(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#endif /* MEM_LIBC_MALLOC */ + +/** Calculate memory size for an aligned buffer - returns the next highest + * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and + * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4). + */ +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) +#endif + +/** Calculate safe memory size for an aligned buffer when using an unaligned + * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the + * start (e.g. if buffer is u8_t[] and actual data will be u32_t*) + */ +#ifndef LWIP_MEM_ALIGN_BUFFER +#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1)) +#endif + +/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT + * so that ADDR % MEM_ALIGNMENT == 0 + */ +#ifndef LWIP_MEM_ALIGN +#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEM_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/memp.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/memp.h new file mode 100644 index 0000000..f0d0739 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/memp.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_MEMP_H__ +#define __LWIP_MEMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ +typedef enum { +#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, +#include "lwip/memp_std.h" + MEMP_MAX +} memp_t; + +#if MEM_USE_POOLS +/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ +typedef enum { + /* Get the first (via: + MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ + MEMP_POOL_HELPER_FIRST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START 1 +#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 +#define LWIP_MALLOC_MEMPOOL_END +#include "lwip/memp_std.h" + ) , + /* Get the last (via: + MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ + MEMP_POOL_HELPER_LAST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * +#define LWIP_MALLOC_MEMPOOL_END 1 +#include "lwip/memp_std.h" + ) +} memp_pool_helper_t; + +/* The actual start and stop values are here (cast them over) + We use this helper type and these defines so we can avoid using const memp_t values */ +#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) +#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) +#endif /* MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC || MEM_USE_POOLS +extern const u16_t memp_sizes[MEMP_MAX]; +#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC + +#include "mem.h" + +#define memp_init() +#define memp_malloc(type) mem_malloc(memp_sizes[type]) +#define memp_free(type, mem) mem_free(mem) + +#else /* MEMP_MEM_MALLOC */ + +#if MEM_USE_POOLS +/** This structure is used to save the pool one element came from. */ +struct memp_malloc_helper +{ + memp_t poolnr; +}; +#endif /* MEM_USE_POOLS */ + +void memp_init(void); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#endif /* MEMP_MEM_MALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/memp_std.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/memp_std.h new file mode 100644 index 0000000..592a282 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/memp_std.h @@ -0,0 +1,135 @@ +/* + * SETUP: Make sure we define everything we will need. + * + * We have create three types of pools: + * 1) MEMPOOL - standard pools + * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c + * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct + * + * If the include'r doesn't require any special treatment of each of the types + * above, then will declare #2 & #3 to be just standard mempools. + */ +#ifndef LWIP_MALLOC_MEMPOOL +/* This treats "malloc pools" just like any other pool. + The pools are a little bigger to provide 'size' as the amount of user data. */ +#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL_END +#endif /* LWIP_MALLOC_MEMPOOL */ + +#ifndef LWIP_PBUF_MEMPOOL +/* This treats "pbuf pools" just like any other pool. + * Allocates buffers for a pbuf struct AND a payload size */ +#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#endif /* LWIP_PBUF_MEMPOOL */ + + +/* + * A list of internal pools used by LWIP. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ +#if LWIP_RAW +LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* IP_REASSEMBLY */ +#if (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) || LWIP_IPV6_FRAG +LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF") +#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +#if LWIP_NETCONN +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +#if !LWIP_TCPIP_CORE_LOCKING_INPUT +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ +#endif /* NO_SYS==0 */ + +#if LWIP_ARP && ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* LWIP_ARP && ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#endif /* LWIP_IGMP */ + +#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */ +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") +#endif /* LWIP_TIMERS */ + +#if LWIP_SNMP +LWIP_MEMPOOL(SNMP_ROOTNODE, MEMP_NUM_SNMP_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE") +LWIP_MEMPOOL(SNMP_NODE, MEMP_NUM_SNMP_NODE, sizeof(struct mib_list_node), "SNMP_NODE") +LWIP_MEMPOOL(SNMP_VARBIND, MEMP_NUM_SNMP_VARBIND, sizeof(struct snmp_varbind), "SNMP_VARBIND") +LWIP_MEMPOOL(SNMP_VALUE, MEMP_NUM_SNMP_VALUE, SNMP_MAX_VALUE_SIZE, "SNMP_VALUE") +#endif /* LWIP_SNMP */ +#if LWIP_DNS && LWIP_SOCKET +LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB") +#endif /* LWIP_DNS && LWIP_SOCKET */ +#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST") +#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#if PPP_SUPPORT && PPPOE_SUPPORT +LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF") +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ + +#if LWIP_IPV6 && LWIP_ND6_QUEUEING +LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE") +#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */ + +#if LWIP_IPV6 && LWIP_IPV6_REASS +LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA") +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_MLD +LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP") +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + +/* + * A list of pools of pbuf's used by LWIP. + * + * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + * This allocates enough space for the pbuf struct and a payload. + * (Example: pbuf_payload_size=0 allocates only size for the struct) + */ +LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * Allow for user-defined pools; this must be explicitly set in lwipopts.h + * since the default is to NOT look for lwippools.h + */ +#if MEMP_USE_CUSTOM_POOLS +#include "lwippools.h" +#endif /* MEMP_USE_CUSTOM_POOLS */ + +/* + * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later + * (#undef is ignored for something that is not defined) + */ +#undef LWIP_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL_START +#undef LWIP_MALLOC_MEMPOOL_END +#undef LWIP_PBUF_MEMPOOL diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/netbuf.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/netbuf.h new file mode 100644 index 0000000..d12fe27 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/netbuf.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETBUF_H__ +#define __LWIP_NETBUF_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This netbuf has dest-addr/port set */ +#define NETBUF_FLAG_DESTADDR 0x01 +/** This netbuf includes a checksum */ +#define NETBUF_FLAG_CHKSUM 0x02 + +struct netbuf { + struct pbuf *p, *ptr; + ipX_addr_t addr; + u16_t port; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + u8_t flags; +#endif /* LWIP_CHECKSUM_ON_COPY */ + u16_t toport_chksum; +#if LWIP_NETBUF_RECVINFO + ipX_addr_t toaddr; +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#define netbuf_copy_partial(buf, dataptr, len, offset) \ + pbuf_copy_partial((buf)->p, (dataptr), (len), (offset)) +#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) +#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len) +#define netbuf_len(buf) ((buf)->p->tot_len) +#define netbuf_fromaddr(buf) (ipX_2_ip(&((buf)->addr))) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(ipX_2_ip(&((buf)->addr)), fromaddr) +#define netbuf_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) (ipX_2_ip(&((buf)->toaddr))) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(ipX_2_ip(&((buf)->toaddr)), destaddr) +#define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0) +#endif /* LWIP_NETBUF_RECVINFO */ +#if LWIP_CHECKSUM_ON_COPY +#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \ + (buf)->toport_chksum = chksum; } while(0) +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#if LWIP_IPV6 +#define netbuf_fromaddr_ip6(buf) (ipX_2_ip6(&((buf)->addr))) +#define netbuf_set_fromaddr_ip6(buf, fromaddr) ip6_addr_set(ipX_2_ip6(&((buf)->addr)), fromaddr) +#define netbuf_destaddr_ip6(buf) (ipX_2_ip6(&((buf)->toaddr))) +#define netbuf_set_destaddr_ip6(buf, destaddr) ip6_addr_set(ipX_2_ip6(&((buf)->toaddr)), destaddr) +#endif /* LWIP_IPV6 */ + +#define netbuf_fromaddr_ipX(buf) (&((buf)->addr)) +#define netbuf_destaddr_ipX(buf) (&((buf)->toaddr)) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETBUF_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/netif.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/netif.h new file mode 100644 index 0000000..f977032 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/netif.h @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETIF_H__ +#define __LWIP_NETIF_H__ + +#include "lwip/opt.h" + +#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + +#include "lwip/err.h" + +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#if LWIP_DHCP +struct dhcp; +#endif +#if LWIP_AUTOIP +struct autoip; +#endif +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses are expected to be in + * the same byte order as in IP_PCB. */ + +/** must be the maximum of all used hardware address lengths + across all types of interfaces in use */ +#define NETIF_MAX_HWADDR_LEN 6U + +/** Whether the network interface is 'up'. This is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + * It is set by the startup code (for static IP configuration) or + * by dhcp/autoip when an address has been assigned. + */ +#define NETIF_FLAG_UP 0x01U +/** If set, the netif has broadcast capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_BROADCAST 0x02U +/** If set, the netif is one end of a point-to-point connection. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_POINTTOPOINT 0x04U +/** If set, the interface is configured using DHCP. + * Set by the DHCP code when starting or stopping DHCP. */ +#define NETIF_FLAG_DHCP 0x08U +/** If set, the interface has an active link + * (set by the network interface driver). + * Either set by the netif driver in its init function (if the link + * is up at that time) or at a later point once the link comes up + * (if link detection is supported by the hardware). */ +#define NETIF_FLAG_LINK_UP 0x10U +/** If set, the netif is an ethernet device using ARP. + * Set by the netif driver in its init function. + * Used to check input packet types and use of DHCP. */ +#define NETIF_FLAG_ETHARP 0x20U +/** If set, the netif is an ethernet device. It might not use + * ARP or TCP/IP if it is used for PPPoE only. + */ +#define NETIF_FLAG_ETHERNET 0x40U +/** If set, the netif has IGMP capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_IGMP 0x80U +/** Whether to pretend that we are every host for TCP packets. + * Set by netif_set_pretend_tcp. */ +#define NETIF_FLAG_PRETEND_TCP 0x100U + +/** Function prototype for netif init functions. Set up flags and output/linkoutput + * callback functions in this function. + * + * @param netif The netif to initialize + */ +typedef err_t (*netif_init_fn)(struct netif *netif); +/** Function prototype for netif->input functions. This function is saved as 'input' + * callback function in the netif struct. Call it when a packet has been received. + * + * @param p The received packet, copied into a pbuf + * @param inp The netif which received the packet + */ +typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp); +/** Function prototype for netif->output functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'etharp_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IP address to which the packet shall be sent + */ +typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr); +#if LWIP_IPV6 +/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'nd_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IPv6 address to which the packet shall be sent + */ +typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p, + ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ +/** Function prototype for netif->linkoutput functions. Only used for ethernet + * netifs. This function is called by ARP when a packet shall be sent. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (raw ethernet packet) + */ +typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p); +/** Function prototype for netif status- or link-callback functions. */ +typedef void (*netif_status_callback_fn)(struct netif *netif); +/** Function prototype for netif igmp_mac_filter functions */ +typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif, + ip_addr_t *group, u8_t action); +#if LWIP_IPV6 && LWIP_IPV6_MLD +/** Function prototype for netif mld_mac_filter functions */ +typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, + ip6_addr_t *group, u8_t action); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +/** Generic data structure used for all lwIP network interfaces. + * The following fields should be filled in by the initialization + * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */ +struct netif { + /** pointer to next in linked list */ + struct netif *next; + + /** IP address configuration in network byte order */ + ip_addr_t ip_addr; + ip_addr_t netmask; + ip_addr_t gw; + +#if LWIP_IPV6 + /** Array of IPv6 addresses for this netif. */ + ip6_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; + /** The state of each IPv6 address (Tentative, Preferred, etc). + * @see ip6_addr.h */ + u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; +#endif /* LWIP_IPV6 */ + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. */ + netif_input_fn input; + /** This function is called by the IP module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_fn output; + /** This function is called by the ARP module when it wants + * to send a packet on the interface. This function outputs + * the pbuf as-is on the link medium. */ + netif_linkoutput_fn linkoutput; +#if LWIP_IPV6 + /** This function is called by the IPv6 module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_ip6_fn output_ip6; +#endif /* LWIP_IPV6 */ +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + netif_status_callback_fn status_callback; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + netif_status_callback_fn link_callback; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK + /** This function is called when the netif has been removed */ + netif_status_callback_fn remove_callback; +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + /** This field can be set by the device driver and could point + * to state information for the device. */ + void *state; +#if LWIP_DHCP + /** the DHCP client state information for this netif */ + struct dhcp *dhcp; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /** the AutoIP client state information for this netif */ + struct autoip *autoip; +#endif +#if LWIP_IPV6_AUTOCONFIG + /** is this netif enabled for IPv6 autoconfiguration */ + u8_t ip6_autoconfig_enabled; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /** Number of Router Solicitation messages that remain to be sent. */ + u8_t rs_count; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /** the DHCPv6 client state information for this netif */ + struct dhcp6 *dhcp6; +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_NETIF_HOSTNAME + /* the hostname for this netif, NULL is a valid value */ + char* hostname; +#endif /* LWIP_NETIF_HOSTNAME */ + /** maximum transfer unit (in bytes) */ + u16_t mtu; + /** number of bytes used in hwaddr */ + u8_t hwaddr_len; + /** link level hardware address of this interface */ + u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; + /** flags (see NETIF_FLAG_ above) */ + u16_t flags; + /** descriptive abbreviation */ + char name[2]; + /** number of this interface */ + u8_t num; +#if LWIP_SNMP + /** link type (from "snmp_ifType" enum from snmp.h) */ + u8_t link_type; + /** (estimate) link speed */ + u32_t link_speed; + /** timestamp at last change made (up/down) */ + u32_t ts; + /** counters */ + u32_t ifinoctets; + u32_t ifinucastpkts; + u32_t ifinnucastpkts; + u32_t ifindiscards; + u32_t ifoutoctets; + u32_t ifoutucastpkts; + u32_t ifoutnucastpkts; + u32_t ifoutdiscards; +#endif /* LWIP_SNMP */ +#if LWIP_IGMP + /** This function could be called to add or delete an entry in the multicast + filter table of the ethernet MAC.*/ + netif_igmp_mac_filter_fn igmp_mac_filter; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /** This function could be called to add or delete an entry in the IPv6 multicast + filter table of the ethernet MAC. */ + netif_mld_mac_filter_fn mld_mac_filter; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. */ + struct pbuf *loop_first; + struct pbuf *loop_last; +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t loop_cnt_current; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#endif /* ENABLE_LOOPBACK */ +}; + +#if LWIP_SNMP +#define NETIF_INIT_SNMP(netif, type, speed) \ + /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \ + (netif)->link_type = (type); \ + /* your link speed here (units: bits per second) */ \ + (netif)->link_speed = (speed); \ + (netif)->ts = 0; \ + (netif)->ifinoctets = 0; \ + (netif)->ifinucastpkts = 0; \ + (netif)->ifinnucastpkts = 0; \ + (netif)->ifindiscards = 0; \ + (netif)->ifoutoctets = 0; \ + (netif)->ifoutucastpkts = 0; \ + (netif)->ifoutnucastpkts = 0; \ + (netif)->ifoutdiscards = 0 +#else /* LWIP_SNMP */ +#define NETIF_INIT_SNMP(netif, type, speed) +#endif /* LWIP_SNMP */ + + +/** The list of network interfaces. */ +extern struct netif *netif_list; +/** The default network interface. */ +extern struct netif *netif_default; + +void netif_init(void); + +struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input); + +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw); +void netif_remove(struct netif * netif); + +/* Returns a network interface given its name. The name is of the form + "et0", where the first two letters are the "name" field in the + netif structure, and the digit is in the num field in the same + structure. */ +struct netif *netif_find(char *name); + +int netif_is_named (struct netif *netif, const char name[3]); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr); +void netif_set_netmask(struct netif *netif, ip_addr_t *netmask); +void netif_set_gw(struct netif *netif, ip_addr_t *gw); +void netif_set_pretend_tcp(struct netif *netif, u8_t pretend); + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +/** Ask if an interface is up */ +#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_STATUS_CALLBACK +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback); +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +/** Ask if a link is up */ +#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if LWIP_NETIF_HOSTNAME +#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0) +#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL) +#endif /* LWIP_NETIF_HOSTNAME */ + +#if LWIP_IGMP +#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0) +#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL) +#endif /* LWIP_IGMP */ + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p, ip_addr_t *dest_ip); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +#define netif_ip6_addr(netif, i) (&((netif)->ip6_addr[(i)])) +#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[(i)]) +#define netif_ip6_addr_set_state(netif, i, state) ((netif)->ip6_addr_state[(i)] = (state)) +s8_t netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr); +void netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit); +#endif /* LWIP_IPV6 */ + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint)) +#else /* LWIP_NETIF_HWADDRHINT */ +#define NETIF_SET_HWADDRHINT(netif, hint) +#endif /* LWIP_NETIF_HWADDRHINT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETIF_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/netifapi.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/netifapi.h new file mode 100644 index 0000000..33318ef --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/netifapi.h @@ -0,0 +1,108 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef __LWIP_NETIFAPI_H__ +#define __LWIP_NETIFAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*netifapi_void_fn)(struct netif *netif); +typedef err_t (*netifapi_errt_fn)(struct netif *netif); + +struct netifapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + err_t err; + struct netif *netif; + union { + struct { + ip_addr_t *ipaddr; + ip_addr_t *netmask; + ip_addr_t *gw; + void *state; + netif_init_fn init; + netif_input_fn input; + } add; + struct { + netifapi_void_fn voidfunc; + netifapi_errt_fn errtfunc; + } common; + } msg; +}; + +struct netifapi_msg { + void (* function)(struct netifapi_msg_msg *msg); + struct netifapi_msg_msg msg; +}; + + +/* API for application */ +err_t netifapi_netif_add ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input); + +err_t netifapi_netif_set_addr ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw ); + +err_t netifapi_netif_common ( struct netif *netif, + netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc); + +#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL) +#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL) +#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL) +#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL) +#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start) +#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL) +#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start) +#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETIF_API */ + +#endif /* __LWIP_NETIFAPI_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/opt.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/opt.h new file mode 100644 index 0000000..e51f8e5 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/opt.h @@ -0,0 +1,2417 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_OPT_H__ +#define __LWIP_OPT_H__ + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ +#include "lwipopts.h" +#include "lwip/debug.h" + +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#ifndef SYS_LIGHTWEIGHT_PROT +#define SYS_LIGHTWEIGHT_PROT 0 +#endif + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#ifndef NO_SYS +#define NO_SYS 0 +#endif + +/** + * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1 + * Mainly for compatibility to old versions. + */ +#ifndef NO_SYS_NO_TIMERS +#define NO_SYS_NO_TIMERS 0 +#endif + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#ifndef MEMCPY +#define MEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#ifndef SMEMCPY +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#ifndef MEM_LIBC_MALLOC +#define MEM_LIBC_MALLOC 0 +#endif + +/** +* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. +* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution +* speed and usage from interrupts! +*/ +#ifndef MEMP_MEM_MALLOC +#define MEMP_MEM_MALLOC 0 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> #define MEM_ALIGNMENT 4 + * 2 byte alignment -> #define MEM_ALIGNMENT 2 + */ +#ifndef MEM_ALIGNMENT +#define MEM_ALIGNMENT 1 +#endif + +/** + * MEM_SIZE: the size of the heap memory. If the application will send + * a lot of data that needs to be copied, this should be set high. + */ +#ifndef MEM_SIZE +#define MEM_SIZE 1600 +#endif + +/** + * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array. + * This can be used to individually change the location of each pool. + * Default is one big array for all pools + */ +#ifndef MEMP_SEPARATE_POOLS +#define MEMP_SEPARATE_POOLS 0 +#endif + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#ifndef MEMP_SANITY_CHECK +#define MEMP_SANITY_CHECK 0 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#ifndef MEM_USE_POOLS +#define MEM_USE_POOLS 0 +#endif + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 +#endif + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * inlude path somewhere. + */ +#ifndef MEMP_USE_CUSTOM_POOLS +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#ifndef MEMP_NUM_PBUF +#define MEMP_NUM_PBUF 16 +#endif + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#ifndef MEMP_NUM_RAW_PCB +#define MEMP_NUM_RAW_PCB 4 +#endif + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#ifndef MEMP_NUM_UDP_PCB +#define MEMP_NUM_UDP_PCB 4 +#endif + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB +#define MEMP_NUM_TCP_PCB 5 +#endif + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB_LISTEN +#define MEMP_NUM_TCP_PCB_LISTEN 8 +#endif + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_SEG +#define MEMP_NUM_TCP_SEG 16 +#endif + +/** + * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 5 +#endif + +/** + * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent + * (fragments, not whole packets!). + * This is only used with IP_FRAG_USES_STATIC_BUF==0 and + * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs + * where the packet is not yet sent when netif->output returns. + */ +#ifndef MEMP_NUM_FRAG_PBUF +#define MEMP_NUM_FRAG_PBUF 15 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#ifndef MEMP_NUM_ARP_QUEUE +#define MEMP_NUM_ARP_QUEUE 30 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members et the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#ifndef MEMP_NUM_IGMP_GROUP +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. + * (requires NO_SYS==0) + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)) +#endif + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETBUF +#define MEMP_NUM_NETBUF 2 +#endif + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 4 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_API 8 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_INPKT +#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#endif + +/** + * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree. + */ +#ifndef MEMP_NUM_SNMP_NODE +#define MEMP_NUM_SNMP_NODE 50 +#endif + +/** + * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree. + * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least! + */ +#ifndef MEMP_NUM_SNMP_ROOTNODE +#define MEMP_NUM_SNMP_ROOTNODE 30 +#endif + +/** + * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to + * be changed normally) - 2 of these are used per request (1 for input, + * 1 for output) + */ +#ifndef MEMP_NUM_SNMP_VARBIND +#define MEMP_NUM_SNMP_VARBIND 2 +#endif + +/** + * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used + * (does not have to be changed normally) - 3 of these are used per request + * (1 for the value read and 2 for OIDs - input and output) + */ +#ifndef MEMP_NUM_SNMP_VALUE +#define MEMP_NUM_SNMP_VALUE 3 +#endif + +/** + * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls + * (before freeing the corresponding memory using lwip_freeaddrinfo()). + */ +#ifndef MEMP_NUM_NETDB +#define MEMP_NUM_NETDB 1 +#endif + +/** + * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list + * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. + */ +#ifndef MEMP_NUM_LOCALHOSTLIST +#define MEMP_NUM_LOCALHOSTLIST 1 +#endif + +/** + * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE + * interfaces (only used with PPPOE_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOE_INTERFACES +#define MEMP_NUM_PPPOE_INTERFACES 1 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#ifndef PBUF_POOL_SIZE +#define PBUF_POOL_SIZE 16 +#endif + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 +#endif + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#ifndef ARP_TABLE_SIZE +#define ARP_TABLE_SIZE 10 +#endif + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#ifndef ARP_QUEUEING +#define ARP_QUEUEING 0 +#endif + +/** + * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be + * updated with the source MAC and IP addresses supplied in the packet. + * You may want to disable this if you do not trust LAN peers to have the + * correct addresses, or as a limited approach to attempt to handle + * spoofing. If disabled, lwIP will need to make a new ARP request if + * the peer is not already in the ARP table, adding a little latency. + * The peer *is* in the ARP table if it requested our address before. + * Also notice that this slows down input processing of every IP packet! + */ +#ifndef ETHARP_TRUST_IP_MAC +#define ETHARP_TRUST_IP_MAC 0 +#endif + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. + */ +#ifndef ETHARP_SUPPORT_VLAN +#define ETHARP_SUPPORT_VLAN 0 +#endif + +/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP + * might be disabled + */ +#ifndef LWIP_ETHERNET +#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT) +#endif + +/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure + * alignment of payload after that header. Since the header is 14 bytes long, + * without this padding e.g. addresses in the IP header will not be aligned + * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms. + */ +#ifndef ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#endif + +/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table + * entries (using etharp_add_static_entry/etharp_remove_static_entry). + */ +#ifndef ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_SUPPORT_STATIC_ENTRIES 0 +#endif + + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#ifndef IP_FORWARD +#define IP_FORWARD 0 +#endif + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#ifndef IP_OPTIONS_ALLOWED +#define IP_OPTIONS_ALLOWED 1 +#endif + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#ifndef IP_REASSEMBLY +#define IP_REASSEMBLY 1 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#ifndef IP_FRAG +#define IP_FRAG 1 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 10 +#endif + +/** + * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP + * fragmentation. Otherwise pbufs are allocated and reference the original + * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1, + * new PBUF_RAM pbufs are used for fragments). + * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs! + */ +#ifndef IP_FRAG_USES_STATIC_BUF +#define IP_FRAG_USES_STATIC_BUF 0 +#endif + +/** + * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer + * (requires IP_FRAG_USES_STATIC_BUF==1) + */ +#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU) +#define IP_FRAG_MAX_MTU 1500 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#ifndef IP_DEFAULT_TTL +#define IP_DEFAULT_TTL 255 +#endif + +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#ifndef IP_SOF_BROADCAST +#define IP_SOF_BROADCAST 0 +#endif + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#ifndef IP_SOF_BROADCAST_RECV +#define IP_SOF_BROADCAST_RECV 0 +#endif + +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 +#endif + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#ifndef LWIP_ICMP +#define LWIP_ICMP 1 +#endif + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#ifndef ICMP_TTL +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#ifndef LWIP_BROADCAST_PING +#define LWIP_BROADCAST_PING 0 +#endif + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#ifndef LWIP_MULTICAST_PING +#define LWIP_MULTICAST_PING 0 +#endif + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef LWIP_RAW +#define LWIP_RAW 1 +#endif + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef RAW_TTL +#define RAW_TTL (IP_DEFAULT_TTL) +#endif + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#ifndef LWIP_DHCP +#define LWIP_DHCP 0 +#endif + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#ifndef DHCP_DOES_ARP_CHECK +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP. This can be set + * as low as 1 to get an AutoIP address very quickly, but you should + * be prepared to handle a changing IP address when DHCP overrides + * AutoIP. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +#endif + +/* + ---------------------------------- + ---------- SNMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP + * transport. + */ +#ifndef LWIP_SNMP +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will + * allow. At least one request buffer is required. + * Does not have to be changed unless external MIBs answer request asynchronously + */ +#ifndef SNMP_CONCURRENT_REQUESTS +#define SNMP_CONCURRENT_REQUESTS 1 +#endif + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#ifndef SNMP_TRAP_DESTINATIONS +#define SNMP_TRAP_DESTINATIONS 1 +#endif + +/** + * SNMP_PRIVATE_MIB: + * When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. + */ +#ifndef SNMP_PRIVATE_MIB +#define SNMP_PRIVATE_MIB 0 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#ifndef SNMP_SAFE_REQUESTS +#define SNMP_SAFE_REQUESTS 1 +#endif + +/** + * The maximum length of strings used. This affects the size of + * MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_OCTET_STRING_LEN +#define SNMP_MAX_OCTET_STRING_LEN 127 +#endif + +/** + * The maximum depth of the SNMP tree. + * With private MIBs enabled, this depends on your MIB! + * This affects the size of MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_TREE_DEPTH +#define SNMP_MAX_TREE_DEPTH 15 +#endif + +/** + * The size of the MEMP_SNMP_VALUE elements, normally calculated from + * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH. + */ +#ifndef SNMP_MAX_VALUE_SIZE +#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH)) +#endif + +/* + ---------------------------------- + ---------- IGMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 0 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 0 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers */ +#ifndef DNS_MAX_SERVERS +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#ifndef DNS_DOES_NAME_CHECK +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** DNS message max. size. Default value is RFC compliant. */ +#ifndef DNS_MSG_SIZE +#define DNS_MSG_SIZE 512 +#endif + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, + * you have to define + * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}} + * (an array of structs name/address, where address is an u32_t in network + * byte order). + * + * Instead, you can also use an external function: + * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name) + * that returns the IP address or INADDR_NONE if not found. + */ +#ifndef DNS_LOCAL_HOSTLIST +#define DNS_LOCAL_HOSTLIST 0 +#endif /* DNS_LOCAL_HOSTLIST */ + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#ifndef LWIP_UDP +#define LWIP_UDP 1 +#endif + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#ifndef LWIP_UDPLITE +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#ifndef UDP_TTL +#define UDP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#ifndef LWIP_NETBUF_RECVINFO +#define LWIP_NETBUF_RECVINFO 0 +#endif + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#ifndef LWIP_TCP +#define LWIP_TCP 1 +#endif + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#ifndef TCP_TTL +#define TCP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well + */ +#ifndef TCP_WND +#define TCP_WND (4 * TCP_MSS) +#endif + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#ifndef TCP_MAXRTX +#define TCP_MAXRTX 12 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#ifndef TCP_SYNMAXRTX +#define TCP_SYNMAXRTX 6 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#ifndef TCP_QUEUE_OOSEQ +#define TCP_QUEUE_OOSEQ (LWIP_TCP) +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#ifndef TCP_MSS +#define TCP_MSS 536 +#endif + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#ifndef TCP_CALCULATE_EFF_SEND_MSS +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#ifndef TCP_SND_BUF +#define TCP_SND_BUF (2 * TCP_MSS) +#endif + +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ +#ifndef TCP_SND_QUEUELEN +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than + * TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less + * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below + * this number, select returns writable (combined with TCP_SNDLOWAT). + */ +#ifndef TCP_SNDQUEUELOWAT +#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#endif + +/** + * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_BYTES +#define TCP_OOSEQ_MAX_BYTES 0 +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_PBUFS +#define TCP_OOSEQ_MAX_PBUFS 0 +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 0 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#ifndef TCP_OVERSIZE +#define TCP_OVERSIZE TCP_MSS +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + */ +#ifndef LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_TIMESTAMPS 0 +#endif + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#ifndef TCP_WND_UPDATE_THRESHOLD +#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4) +#endif + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. This is the default. + */ +#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#endif + + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#ifndef PBUF_LINK_HLEN +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accomodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#ifndef PBUF_POOL_BUFSIZE +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) +#endif + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquistion) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#ifndef LWIP_NETIF_LINK_CALLBACK +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#ifndef LWIP_NETIF_REMOVE_CALLBACK +#define LWIP_NETIF_REMOVE_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#ifndef LWIP_NETIF_HWADDRHINT +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#ifndef LWIP_NETIF_LOOPBACK +#define LWIP_NETIF_LOOPBACK 0 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#ifndef LWIP_LOOPBACK_MAX_PBUFS +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#ifndef LWIP_NETIF_TX_SINGLE_PBUF +#define LWIP_NETIF_TX_SINGLE_PBUF 0 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c + */ +#ifndef LWIP_HAVE_LOOPIF +#define LWIP_HAVE_LOOPIF 0 +#endif + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +#ifndef LWIP_HAVE_SLIPIF +#define LWIP_HAVE_SLIPIF 0 +#endif + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#ifndef TCPIP_THREAD_NAME +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_PRIO +#define TCPIP_THREAD_PRIO 1 +#endif + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#ifndef TCPIP_MBOX_SIZE +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#ifndef SLIPIF_THREAD_NAME +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_STACKSIZE +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_PRIO +#define SLIPIF_THREAD_PRIO 1 +#endif + +/** + * PPP_THREAD_NAME: The name assigned to the pppInputThread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppInputThread" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_STACKSIZE +#define PPP_THREAD_STACKSIZE 0 +#endif + +/** + * PPP_THREAD_PRIO: The priority assigned to the pppInputThread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_PRIO +#define PPP_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#ifndef DEFAULT_THREAD_NAME +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_STACKSIZE +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_PRIO +#define DEFAULT_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_RAW_RECVMBOX_SIZE +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_UDP_RECVMBOX_SIZE +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_TCP_RECVMBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#ifndef DEFAULT_ACCEPTMBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 0 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 +#endif + +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 1 +#endif + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create + * timers running in tcpip_thread from another thread. + */ +#ifndef LWIP_TCPIP_TIMEOUT +#define LWIP_TCPIP_TIMEOUT 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 1 +#endif + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names. + * (only used if you use sockets.c) + */ +#ifndef LWIP_COMPAT_SOCKETS +#define LWIP_COMPAT_SOCKETS 1 +#endif + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#ifndef LWIP_POSIX_SOCKETS_IO_NAMES +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 0 +#endif + +/** + * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#ifndef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#ifndef RECV_BUFSIZE_DEFAULT +#define RECV_BUFSIZE_DEFAULT INT_MAX +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#ifndef SO_REUSE +#define SO_REUSE 0 +#endif + +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#ifndef SO_REUSE_RXTOALL +#define SO_REUSE_RXTOALL 0 +#endif + +/** + * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of + * pending data in the network buffer. This is the way windows does it. It's + * the default for lwIP since it is smaller. + * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next + * pending datagram in bytes. This is the way linux does it. This code is only + * here for compatibility. + */ +#ifndef LWIP_FIONREAD_LINUXMODE +#define LWIP_FIONREAD_LINUXMODE 0 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#ifndef LWIP_STATS +#define LWIP_STATS 1 +#endif + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#ifndef LWIP_STATS_DISPLAY +#define LWIP_STATS_DISPLAY 0 +#endif + +/** + * LINK_STATS==1: Enable link stats. + */ +#ifndef LINK_STATS +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#ifndef ETHARP_STATS +#define ETHARP_STATS (LWIP_ARP) +#endif + +/** + * IP_STATS==1: Enable IP stats. + */ +#ifndef IP_STATS +#define IP_STATS 1 +#endif + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#ifndef IPFRAG_STATS +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) +#endif + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#ifndef ICMP_STATS +#define ICMP_STATS 1 +#endif + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#ifndef IGMP_STATS +#define IGMP_STATS (LWIP_IGMP) +#endif + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#ifndef UDP_STATS +#define UDP_STATS (LWIP_UDP) +#endif + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#ifndef TCP_STATS +#define TCP_STATS (LWIP_TCP) +#endif + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#ifndef MEM_STATS +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) +#endif + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#ifndef MEMP_STATS +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) +#endif + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#ifndef SYS_STATS +#define SYS_STATS (NO_SYS == 0) +#endif + +/** + * IP6_STATS==1: Enable IPv6 stats. + */ +#ifndef IP6_STATS +#define IP6_STATS (LWIP_IPV6) +#endif + +/** + * ICMP6_STATS==1: Enable ICMP for IPv6 stats. + */ +#ifndef ICMP6_STATS +#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6) +#endif + +/** + * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats. + */ +#ifndef IP6_FRAG_STATS +#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS)) +#endif + +/** + * MLD6_STATS==1: Enable MLD for IPv6 stats. + */ +#ifndef MLD6_STATS +#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD) +#endif + +/** + * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats. + */ +#ifndef ND6_STATS +#define ND6_STATS (LWIP_IPV6) +#endif + +#else + +#define LINK_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define IP6_STATS 0 +#define ICMP6_STATS 0 +#define IP6_FRAG_STATS 0 +#define MLD6_STATS 0 +#define ND6_STATS 0 + +#endif /* LWIP_STATS */ + +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif + +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +#if PPP_SUPPORT + +/** + * NUM_PPP: Max PPP sessions. + */ +#ifndef NUM_PPP +#define NUM_PPP 1 +#endif + +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ +#ifndef CHAP_SUPPORT +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif + +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif + +/** + * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 +#endif + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 0 +#endif + +/** + * MD5_SUPPORT==1: Support MD5 (see also CHAP). + */ +#ifndef MD5_SUPPORT +#define MD5_SUPPORT 0 +#endif + +/* + * Timeouts + */ +#ifndef FSM_DEFTIMEOUT +#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef FSM_DEFMAXTERMREQS +#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXCONFREQS +#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif + +#ifndef UPAP_DEFTIMEOUT +#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif + +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif + +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif + +/* Interval in seconds between keepalive echo requests, 0 to disable. */ +#ifndef LCP_ECHOINTERVAL +#define LCP_ECHOINTERVAL 0 +#endif + +/* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS +#define LCP_MAXECHOFAILS 3 +#endif + +/* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG +#define PPP_MAXIDLEFLAG 100 +#endif + +/* + * Packet sizes + * + * Note - lcp shouldn't be allowed to negotiate stuff outside these + * limits. See lcp.h in the pppd directory. + * (XXX - these constants should simply be shared by lcp.c instead + * of living in lcp.h) + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#ifndef PPP_MAXMTU +/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */ +#define PPP_MAXMTU 1500 /* Largest MTU we allow */ +#endif +#define PPP_MINMTU 64 +#define PPP_MRU 1500 /* default MRU = max length of info field */ +#define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU +#define PPP_DEFMRU 296 /* Try for this */ +#endif +#define PPP_MINMRU 128 /* No MRUs below this */ + +#ifndef MAXNAMELEN +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#endif +#ifndef MAXSECRETLEN +#define MAXSECRETLEN 256 /* max length of password or secret */ +#endif + +#endif /* PPP_SUPPORT */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#ifndef CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP 1 +#endif + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#ifndef CHECKSUM_GEN_UDP +#define CHECKSUM_GEN_UDP 1 +#endif + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#ifndef CHECKSUM_GEN_TCP +#define CHECKSUM_GEN_TCP 1 +#endif + +/** + * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#ifndef CHECKSUM_GEN_ICMP +#define CHECKSUM_GEN_ICMP 1 +#endif + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#ifndef CHECKSUM_CHECK_IP +#define CHECKSUM_CHECK_IP 1 +#endif + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#ifndef CHECKSUM_CHECK_UDP +#define CHECKSUM_CHECK_UDP 1 +#endif + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#ifndef CHECKSUM_CHECK_TCP +#define CHECKSUM_CHECK_TCP 1 +#endif + +/** + * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from + * application buffers to pbufs. + */ +#ifndef LWIP_CHECKSUM_ON_COPY +#define LWIP_CHECKSUM_ON_COPY 0 +#endif + +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#ifndef LWIP_IPV6 +#define LWIP_IPV6 0 +#endif + +/** + * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. + */ +#ifndef LWIP_IPV6_NUM_ADDRESSES +#define LWIP_IPV6_NUM_ADDRESSES 3 +#endif + +/** + * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs + */ +#ifndef LWIP_IPV6_FORWARD +#define LWIP_IPV6_FORWARD 0 +#endif + +/** + * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC) + */ +#ifndef LWIP_ICMP6 +#define LWIP_ICMP6 (LWIP_IPV6) +#endif + +/** + * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in + * ICMPv6 error messages. + */ +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/** + * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages + */ +#ifndef LWIP_ICMP6_HL +#define LWIP_ICMP6_HL 255 +#endif + +/** + * LWIP_ICMP6_CHECKSUM_CHECK==1: verify checksum on ICMPv6 packets + */ +#ifndef LWIP_ICMP6_CHECKSUM_CHECK +#define LWIP_ICMP6_CHECKSUM_CHECK 1 +#endif + +/** + * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol. + */ +#ifndef LWIP_IPV6_MLD +#define LWIP_IPV6_MLD (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined. + */ +#ifndef MEMP_NUM_MLD6_GROUP +#define MEMP_NUM_MLD6_GROUP 4 +#endif + +/** + * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big. + */ +#ifndef LWIP_IPV6_FRAG +#define LWIP_IPV6_FRAG 0 +#endif + +/** + * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented + */ +#ifndef LWIP_IPV6_REASS +#define LWIP_IPV6_REASS (LWIP_IPV6) +#endif + +/** + * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address + * is being resolved. + */ +#ifndef LWIP_ND6_QUEUEING +#define LWIP_ND6_QUEUEING (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution. + */ +#ifndef MEMP_NUM_ND6_QUEUE +#define MEMP_NUM_ND6_QUEUE 20 +#endif + +/** + * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache + */ +#ifndef LWIP_ND6_NUM_NEIGHBORS +#define LWIP_ND6_NUM_NEIGHBORS 10 +#endif + +/** + * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache + */ +#ifndef LWIP_ND6_NUM_DESTINATIONS +#define LWIP_ND6_NUM_DESTINATIONS 10 +#endif + +/** + * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache + */ +#ifndef LWIP_ND6_NUM_PREFIXES +#define LWIP_ND6_NUM_PREFIXES 5 +#endif + +/** + * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache + */ +#ifndef LWIP_ND6_NUM_ROUTERS +#define LWIP_ND6_NUM_ROUTERS 3 +#endif + +/** + * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send + * (neighbor solicit and router solicit) + */ +#ifndef LWIP_ND6_MAX_MULTICAST_SOLICIT +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3 +#endif + +/** + * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages + * to send during neighbor reachability detection. + */ +#ifndef LWIP_ND6_MAX_UNICAST_SOLICIT +#define LWIP_ND6_MAX_UNICAST_SOLICIT 3 +#endif + +/** + * Unused: See ND RFC (time in milliseconds). + */ +#ifndef LWIP_ND6_MAX_ANYCAST_DELAY_TIME +#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000 +#endif + +/** + * Unused: See ND RFC + */ +#ifndef LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT +#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3 +#endif + +/** + * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds). + * May be updated by router advertisement messages. + */ +#ifndef LWIP_ND6_REACHABLE_TIME +#define LWIP_ND6_REACHABLE_TIME 30000 +#endif + +/** + * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages + */ +#ifndef LWIP_ND6_RETRANS_TIMER +#define LWIP_ND6_RETRANS_TIMER 1000 +#endif + +/** + * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation + * message is sent, during neighbor reachability detection. + */ +#ifndef LWIP_ND6_DELAY_FIRST_PROBE_TIME +#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000 +#endif + +/** + * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update + * Reachable time and retransmission timers, and netif MTU. + */ +#ifndef LWIP_ND6_ALLOW_RA_UPDATES +#define LWIP_ND6_ALLOW_RA_UPDATES 1 +#endif + +/** + * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during + * network startup. + */ +#ifndef LWIP_IPV6_SEND_ROUTER_SOLICIT +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#endif + +/** + * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery + * with reachability hints for connected destinations. This helps avoid sending + * unicast neighbor solicitation messages. + */ +#ifndef LWIP_ND6_TCP_REACHABILITY_HINTS +#define LWIP_ND6_TCP_REACHABILITY_HINTS 1 +#endif + +/** + * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862. + */ +#ifndef LWIP_IPV6_AUTOCONFIG +#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_DUP_DETECT_ATTEMPTS: Number of duplicate address detection attempts. + */ +#ifndef LWIP_IPV6_DUP_DETECT_ATTEMPTS +#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1 +#endif + +/** + * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. + */ +#ifndef LWIP_IPV6_DHCP6 +#define LWIP_IPV6_DHCP6 0 +#endif + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/* Hooks are undefined by default, define them to a function if you need them. */ + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +#ifndef LWIP_DBG_MIN_LEVEL +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL +#endif + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#ifndef LWIP_DBG_TYPES_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON +#endif + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#ifndef ETHARP_DEBUG +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#ifndef NETIF_DEBUG +#define NETIF_DEBUG LWIP_DBG_OFF +#endif + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#ifndef PBUF_DEBUG +#define PBUF_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#ifndef API_LIB_DEBUG +#define API_LIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#ifndef API_MSG_DEBUG +#define API_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#ifndef SOCKETS_DEBUG +#define SOCKETS_DEBUG LWIP_DBG_OFF +#endif + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#ifndef ICMP_DEBUG +#define ICMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#ifndef IGMP_DEBUG +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#ifndef INET_DEBUG +#define INET_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#ifndef IP_DEBUG +#define IP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#ifndef IP_REASS_DEBUG +#define IP_REASS_DEBUG LWIP_DBG_OFF +#endif + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#ifndef RAW_DEBUG +#define RAW_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#ifndef MEM_DEBUG +#define MEM_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#ifndef MEMP_DEBUG +#define MEMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#ifndef SYS_DEBUG +#define SYS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#ifndef TIMERS_DEBUG +#define TIMERS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#ifndef TCP_DEBUG +#define TCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#ifndef TCP_INPUT_DEBUG +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#ifndef TCP_FR_DEBUG +#define TCP_FR_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#ifndef TCP_RTO_DEBUG +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#ifndef TCP_CWND_DEBUG +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#ifndef TCP_WND_DEBUG +#define TCP_WND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#ifndef TCP_OUTPUT_DEBUG +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#ifndef TCP_RST_DEBUG +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#ifndef TCP_QLEN_DEBUG +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#endif + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#ifndef UDP_DEBUG +#define UDP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#ifndef TCPIP_DEBUG +#define TCPIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#ifndef SLIP_DEBUG +#define SLIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#ifndef DHCP_DEBUG +#define DHCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#ifndef AUTOIP_DEBUG +#define AUTOIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MSG_DEBUG: Enable debugging for SNMP messages. + */ +#ifndef SNMP_MSG_DEBUG +#define SNMP_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#ifndef SNMP_MIB_DEBUG +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#ifndef DNS_DEBUG +#define DNS_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#ifndef IP6_DEBUG +#define IP6_DEBUG LWIP_DBG_OFF +#endif + +#endif /* __LWIP_OPT_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/pbuf.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/pbuf.h new file mode 100644 index 0000000..4f8dca8 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/pbuf.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_PBUF_H__ +#define __LWIP_PBUF_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the pbuf_custom code is only needed for one specific configuration + * of IP_FRAG */ +#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) + +/* @todo: We need a mechanism to prevent wasting memory in every pbuf + (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */ + +#define PBUF_TRANSPORT_HLEN 20 +#if LWIP_IPV6 +#define PBUF_IP_HLEN 40 +#else +#define PBUF_IP_HLEN 20 +#endif + +typedef enum { + PBUF_TRANSPORT, + PBUF_IP, + PBUF_LINK, + PBUF_RAW +} pbuf_layer; + +typedef enum { + PBUF_RAM, /* pbuf data is stored in RAM */ + PBUF_ROM, /* pbuf data is stored in ROM */ + PBUF_REF, /* pbuf comes from the pbuf pool */ + PBUF_POOL /* pbuf payload refers to RAM */ +} pbuf_type; + + +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U +/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a + a pbuf differently */ +#define PBUF_FLAG_IS_CUSTOM 0x02U +/** indicates this pbuf is UDP multicast to be looped back */ +#define PBUF_FLAG_MCASTLOOP 0x04U +/** indicates this pbuf was received as link-level broadcast */ +#define PBUF_FLAG_LLBCAST 0x08U +/** indicates this pbuf was received as link-level multicast */ +#define PBUF_FLAG_LLMCAST 0x10U +/** indicates this pbuf includes a TCP FIN flag */ +#define PBUF_FLAG_TCP_FIN 0x20U + +struct pbuf { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + void *payload; + + /** + * total length of this buffer and all next buffers in chain + * belonging to the same packet. + * + * For non-queue packet chains this is the invariant: + * p->tot_len == p->len + (p->next? p->next->tot_len: 0) + */ + u16_t tot_len; + + /** length of this buffer */ + u16_t len; + + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + + /** + * the reference count always equals the number of pointers + * that refer to this pbuf. This can be pointers from an application, + * the stack itself, or pbuf->next pointers from a chain. + */ + u16_t ref; +}; + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Prototype for a function to free a custom pbuf */ +typedef void (*pbuf_free_custom_fn)(struct pbuf *p); + +/** A custom pbuf: like a pbuf, but following a function pointer to free it. */ +struct pbuf_custom { + /** The actual pbuf */ + struct pbuf pbuf; + /** This function is called when pbuf_free deallocates this pbuf(_custom) */ + pbuf_free_custom_fn custom_free_function; +}; +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +#if LWIP_TCP && TCP_QUEUE_OOSEQ +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ +#if NO_SYS && PBUF_POOL_FREE_OOSEQ +extern volatile u8_t pbuf_free_ooseq_pending; +void pbuf_free_ooseq(void); +/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level to check if ooseq pbufs need to be + freed! */ +#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \ + /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \ + ooseq queued pbufs now */ \ + pbuf_free_ooseq(); }}while(0) +#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/ +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */ + +/* Initializes the pbuf module. This call is empty for now, but may not be in future. */ +#define pbuf_init() + +struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type); +#if LWIP_SUPPORT_CUSTOM_PBUF +struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, + struct pbuf_custom *p, void *payload_mem, + u16_t payload_mem_len); +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +u8_t pbuf_free(struct pbuf *p); +u8_t pbuf_clen(struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); +#if LWIP_CHECKSUM_ON_COPY +err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum); +#endif /* LWIP_CHECKSUM_ON_COPY */ + +u8_t pbuf_get_at(struct pbuf* p, u16_t offset); +u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n); +u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset); +u16_t pbuf_strstr(struct pbuf* p, const char* substr); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_PBUF_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/raw.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/raw.h new file mode 100644 index 0000000..f0c8ed4 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/raw.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_RAW_H__ +#define __LWIP_RAW_H__ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct raw_pcb; + +/** Function prototype for raw pcb receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr); + +#if LWIP_IPV6 +/** Function prototype for raw pcb IPv6 receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_ip6_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define RAW_PCB_RECV_IP6 raw_recv_ip6_fn ip6; +#else +#define RAW_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +struct raw_pcb { + /* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /** receive callback function */ + union { + raw_recv_fn ip4; + RAW_PCB_RECV_IP6 + } recv; + /* user-supplied argument for the recv callback */ + void *recv_arg; +}; + +/* The following functions is the application layer interface to the + RAW code. */ +struct raw_pcb * raw_new (u8_t proto); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr); + +void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr); +err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); + +#if LWIP_IPV6 +struct raw_pcb * raw_new_ip6 (u8_t proto); +#define raw_bind_ip6(pcb, ip6addr) raw_bind(pcb, ip6_2_ip(ip6addr)) +#define raw_connect_ip6(pcb, ip6addr) raw_connect(pcb, ip6_2_ip(ip6addr)) +#define raw_recv_ip6(pcb, recv_ip6_fn, recv_arg) raw_recv(pcb, (raw_recv_fn)recv_ip6_fn, recv_arg) +#define raw_sendto_ip6(pcb, pbuf, ip6addr) raw_sendto(pcb, pbuf, ip6_2_ip(ip6addr)) +#endif /* LWIP_IPV6 */ + +/* The following functions are the lower layer interface to RAW. */ +u8_t raw_input (struct pbuf *p, struct netif *inp); +#define raw_init() /* Compatibility define, not init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* __LWIP_RAW_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp.h new file mode 100644 index 0000000..2ed043d --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2001, 2002 Leon Woestenberg + * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * + */ +#ifndef __LWIP_SNMP_H__ +#define __LWIP_SNMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/ip_addr.h" + +struct udp_pcb; +struct netif; + +/** + * @see RFC1213, "MIB-II, 6. Definitions" + */ +enum snmp_ifType { + snmp_ifType_other=1, /* none of the following */ + snmp_ifType_regular1822, + snmp_ifType_hdh1822, + snmp_ifType_ddn_x25, + snmp_ifType_rfc877_x25, + snmp_ifType_ethernet_csmacd, + snmp_ifType_iso88023_csmacd, + snmp_ifType_iso88024_tokenBus, + snmp_ifType_iso88025_tokenRing, + snmp_ifType_iso88026_man, + snmp_ifType_starLan, + snmp_ifType_proteon_10Mbit, + snmp_ifType_proteon_80Mbit, + snmp_ifType_hyperchannel, + snmp_ifType_fddi, + snmp_ifType_lapb, + snmp_ifType_sdlc, + snmp_ifType_ds1, /* T-1 */ + snmp_ifType_e1, /* european equiv. of T-1 */ + snmp_ifType_basicISDN, + snmp_ifType_primaryISDN, /* proprietary serial */ + snmp_ifType_propPointToPointSerial, + snmp_ifType_ppp, + snmp_ifType_softwareLoopback, + snmp_ifType_eon, /* CLNP over IP [11] */ + snmp_ifType_ethernet_3Mbit, + snmp_ifType_nsip, /* XNS over IP */ + snmp_ifType_slip, /* generic SLIP */ + snmp_ifType_ultra, /* ULTRA technologies */ + snmp_ifType_ds3, /* T-3 */ + snmp_ifType_sip, /* SMDS */ + snmp_ifType_frame_relay +}; + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** SNMP "sysuptime" Interval */ +#define SNMP_SYSUPTIME_INTERVAL 10 + +/** fixed maximum length for object identifier type */ +#define LWIP_SNMP_OBJ_ID_LEN 32 + +/** internal object identifier representation */ +struct snmp_obj_id +{ + u8_t len; + s32_t id[LWIP_SNMP_OBJ_ID_LEN]; +}; + +/* system */ +void snmp_set_sysdesr(u8_t* str, u8_t* len); +void snmp_set_sysobjid(struct snmp_obj_id *oid); +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid); +void snmp_inc_sysuptime(void); +void snmp_add_sysuptime(u32_t value); +void snmp_get_sysuptime(u32_t *value); +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen); + +/* network interface */ +void snmp_add_ifinoctets(struct netif *ni, u32_t value); +void snmp_inc_ifinucastpkts(struct netif *ni); +void snmp_inc_ifinnucastpkts(struct netif *ni); +void snmp_inc_ifindiscards(struct netif *ni); +void snmp_add_ifoutoctets(struct netif *ni, u32_t value); +void snmp_inc_ifoutucastpkts(struct netif *ni); +void snmp_inc_ifoutnucastpkts(struct netif *ni); +void snmp_inc_ifoutdiscards(struct netif *ni); +void snmp_inc_iflist(void); +void snmp_dec_iflist(void); + +/* ARP (for atTable and ipNetToMediaTable) */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip); +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip); + +/* IP */ +void snmp_inc_ipinreceives(void); +void snmp_inc_ipinhdrerrors(void); +void snmp_inc_ipinaddrerrors(void); +void snmp_inc_ipforwdatagrams(void); +void snmp_inc_ipinunknownprotos(void); +void snmp_inc_ipindiscards(void); +void snmp_inc_ipindelivers(void); +void snmp_inc_ipoutrequests(void); +void snmp_inc_ipoutdiscards(void); +void snmp_inc_ipoutnoroutes(void); +void snmp_inc_ipreasmreqds(void); +void snmp_inc_ipreasmoks(void); +void snmp_inc_ipreasmfails(void); +void snmp_inc_ipfragoks(void); +void snmp_inc_ipfragfails(void); +void snmp_inc_ipfragcreates(void); +void snmp_inc_iproutingdiscards(void); +void snmp_insert_ipaddridx_tree(struct netif *ni); +void snmp_delete_ipaddridx_tree(struct netif *ni); +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni); +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni); + +/* ICMP */ +void snmp_inc_icmpinmsgs(void); +void snmp_inc_icmpinerrors(void); +void snmp_inc_icmpindestunreachs(void); +void snmp_inc_icmpintimeexcds(void); +void snmp_inc_icmpinparmprobs(void); +void snmp_inc_icmpinsrcquenchs(void); +void snmp_inc_icmpinredirects(void); +void snmp_inc_icmpinechos(void); +void snmp_inc_icmpinechoreps(void); +void snmp_inc_icmpintimestamps(void); +void snmp_inc_icmpintimestampreps(void); +void snmp_inc_icmpinaddrmasks(void); +void snmp_inc_icmpinaddrmaskreps(void); +void snmp_inc_icmpoutmsgs(void); +void snmp_inc_icmpouterrors(void); +void snmp_inc_icmpoutdestunreachs(void); +void snmp_inc_icmpouttimeexcds(void); +void snmp_inc_icmpoutparmprobs(void); +void snmp_inc_icmpoutsrcquenchs(void); +void snmp_inc_icmpoutredirects(void); +void snmp_inc_icmpoutechos(void); +void snmp_inc_icmpoutechoreps(void); +void snmp_inc_icmpouttimestamps(void); +void snmp_inc_icmpouttimestampreps(void); +void snmp_inc_icmpoutaddrmasks(void); +void snmp_inc_icmpoutaddrmaskreps(void); + +/* TCP */ +void snmp_inc_tcpactiveopens(void); +void snmp_inc_tcppassiveopens(void); +void snmp_inc_tcpattemptfails(void); +void snmp_inc_tcpestabresets(void); +void snmp_inc_tcpinsegs(void); +void snmp_inc_tcpoutsegs(void); +void snmp_inc_tcpretranssegs(void); +void snmp_inc_tcpinerrs(void); +void snmp_inc_tcpoutrsts(void); + +/* UDP */ +void snmp_inc_udpindatagrams(void); +void snmp_inc_udpnoports(void); +void snmp_inc_udpinerrors(void); +void snmp_inc_udpoutdatagrams(void); +void snmp_insert_udpidx_tree(struct udp_pcb *pcb); +void snmp_delete_udpidx_tree(struct udp_pcb *pcb); + +/* SNMP */ +void snmp_inc_snmpinpkts(void); +void snmp_inc_snmpoutpkts(void); +void snmp_inc_snmpinbadversions(void); +void snmp_inc_snmpinbadcommunitynames(void); +void snmp_inc_snmpinbadcommunityuses(void); +void snmp_inc_snmpinasnparseerrs(void); +void snmp_inc_snmpintoobigs(void); +void snmp_inc_snmpinnosuchnames(void); +void snmp_inc_snmpinbadvalues(void); +void snmp_inc_snmpinreadonlys(void); +void snmp_inc_snmpingenerrs(void); +void snmp_add_snmpintotalreqvars(u8_t value); +void snmp_add_snmpintotalsetvars(u8_t value); +void snmp_inc_snmpingetrequests(void); +void snmp_inc_snmpingetnexts(void); +void snmp_inc_snmpinsetrequests(void); +void snmp_inc_snmpingetresponses(void); +void snmp_inc_snmpintraps(void); +void snmp_inc_snmpouttoobigs(void); +void snmp_inc_snmpoutnosuchnames(void); +void snmp_inc_snmpoutbadvalues(void); +void snmp_inc_snmpoutgenerrs(void); +void snmp_inc_snmpoutgetrequests(void); +void snmp_inc_snmpoutgetnexts(void); +void snmp_inc_snmpoutsetrequests(void); +void snmp_inc_snmpoutgetresponses(void); +void snmp_inc_snmpouttraps(void); +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid); +void snmp_set_snmpenableauthentraps(u8_t *value); +void snmp_get_snmpenableauthentraps(u8_t *value); + +/* LWIP_SNMP support not available */ +/* define everything to be empty */ +#else + +/* system */ +#define snmp_set_sysdesr(str, len) +#define snmp_set_sysobjid(oid); +#define snmp_get_sysobjid_ptr(oid) +#define snmp_inc_sysuptime() +#define snmp_add_sysuptime(value) +#define snmp_get_sysuptime(value) +#define snmp_set_syscontact(ocstr, ocstrlen); +#define snmp_set_sysname(ocstr, ocstrlen); +#define snmp_set_syslocation(ocstr, ocstrlen); + +/* network interface */ +#define snmp_add_ifinoctets(ni,value) +#define snmp_inc_ifinucastpkts(ni) +#define snmp_inc_ifinnucastpkts(ni) +#define snmp_inc_ifindiscards(ni) +#define snmp_add_ifoutoctets(ni,value) +#define snmp_inc_ifoutucastpkts(ni) +#define snmp_inc_ifoutnucastpkts(ni) +#define snmp_inc_ifoutdiscards(ni) +#define snmp_inc_iflist() +#define snmp_dec_iflist() + +/* ARP */ +#define snmp_insert_arpidx_tree(ni,ip) +#define snmp_delete_arpidx_tree(ni,ip) + +/* IP */ +#define snmp_inc_ipinreceives() +#define snmp_inc_ipinhdrerrors() +#define snmp_inc_ipinaddrerrors() +#define snmp_inc_ipforwdatagrams() +#define snmp_inc_ipinunknownprotos() +#define snmp_inc_ipindiscards() +#define snmp_inc_ipindelivers() +#define snmp_inc_ipoutrequests() +#define snmp_inc_ipoutdiscards() +#define snmp_inc_ipoutnoroutes() +#define snmp_inc_ipreasmreqds() +#define snmp_inc_ipreasmoks() +#define snmp_inc_ipreasmfails() +#define snmp_inc_ipfragoks() +#define snmp_inc_ipfragfails() +#define snmp_inc_ipfragcreates() +#define snmp_inc_iproutingdiscards() +#define snmp_insert_ipaddridx_tree(ni) +#define snmp_delete_ipaddridx_tree(ni) +#define snmp_insert_iprteidx_tree(dflt, ni) +#define snmp_delete_iprteidx_tree(dflt, ni) + +/* ICMP */ +#define snmp_inc_icmpinmsgs() +#define snmp_inc_icmpinerrors() +#define snmp_inc_icmpindestunreachs() +#define snmp_inc_icmpintimeexcds() +#define snmp_inc_icmpinparmprobs() +#define snmp_inc_icmpinsrcquenchs() +#define snmp_inc_icmpinredirects() +#define snmp_inc_icmpinechos() +#define snmp_inc_icmpinechoreps() +#define snmp_inc_icmpintimestamps() +#define snmp_inc_icmpintimestampreps() +#define snmp_inc_icmpinaddrmasks() +#define snmp_inc_icmpinaddrmaskreps() +#define snmp_inc_icmpoutmsgs() +#define snmp_inc_icmpouterrors() +#define snmp_inc_icmpoutdestunreachs() +#define snmp_inc_icmpouttimeexcds() +#define snmp_inc_icmpoutparmprobs() +#define snmp_inc_icmpoutsrcquenchs() +#define snmp_inc_icmpoutredirects() +#define snmp_inc_icmpoutechos() +#define snmp_inc_icmpoutechoreps() +#define snmp_inc_icmpouttimestamps() +#define snmp_inc_icmpouttimestampreps() +#define snmp_inc_icmpoutaddrmasks() +#define snmp_inc_icmpoutaddrmaskreps() +/* TCP */ +#define snmp_inc_tcpactiveopens() +#define snmp_inc_tcppassiveopens() +#define snmp_inc_tcpattemptfails() +#define snmp_inc_tcpestabresets() +#define snmp_inc_tcpinsegs() +#define snmp_inc_tcpoutsegs() +#define snmp_inc_tcpretranssegs() +#define snmp_inc_tcpinerrs() +#define snmp_inc_tcpoutrsts() + +/* UDP */ +#define snmp_inc_udpindatagrams() +#define snmp_inc_udpnoports() +#define snmp_inc_udpinerrors() +#define snmp_inc_udpoutdatagrams() +#define snmp_insert_udpidx_tree(pcb) +#define snmp_delete_udpidx_tree(pcb) + +/* SNMP */ +#define snmp_inc_snmpinpkts() +#define snmp_inc_snmpoutpkts() +#define snmp_inc_snmpinbadversions() +#define snmp_inc_snmpinbadcommunitynames() +#define snmp_inc_snmpinbadcommunityuses() +#define snmp_inc_snmpinasnparseerrs() +#define snmp_inc_snmpintoobigs() +#define snmp_inc_snmpinnosuchnames() +#define snmp_inc_snmpinbadvalues() +#define snmp_inc_snmpinreadonlys() +#define snmp_inc_snmpingenerrs() +#define snmp_add_snmpintotalreqvars(value) +#define snmp_add_snmpintotalsetvars(value) +#define snmp_inc_snmpingetrequests() +#define snmp_inc_snmpingetnexts() +#define snmp_inc_snmpinsetrequests() +#define snmp_inc_snmpingetresponses() +#define snmp_inc_snmpintraps() +#define snmp_inc_snmpouttoobigs() +#define snmp_inc_snmpoutnosuchnames() +#define snmp_inc_snmpoutbadvalues() +#define snmp_inc_snmpoutgenerrs() +#define snmp_inc_snmpoutgetrequests() +#define snmp_inc_snmpoutgetnexts() +#define snmp_inc_snmpoutsetrequests() +#define snmp_inc_snmpoutgetresponses() +#define snmp_inc_snmpouttraps() +#define snmp_get_snmpgrpid_ptr(oid) +#define snmp_set_snmpenableauthentraps(value) +#define snmp_get_snmpenableauthentraps(value) + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SNMP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp_msg.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp_msg.h new file mode 100644 index 0000000..1183e3a --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp_msg.h @@ -0,0 +1,315 @@ +/** + * @file + * SNMP Agent message handling structures. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_MSG_H__ +#define __LWIP_SNMP_MSG_H__ + +#include "lwip/opt.h" +#include "lwip/snmp.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#if LWIP_SNMP + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +#define SNMP_ES_NOERROR 0 +#define SNMP_ES_TOOBIG 1 +#define SNMP_ES_NOSUCHNAME 2 +#define SNMP_ES_BADVALUE 3 +#define SNMP_ES_READONLY 4 +#define SNMP_ES_GENERROR 5 + +#define SNMP_GENTRAP_COLDSTART 0 +#define SNMP_GENTRAP_WARMSTART 1 +#define SNMP_GENTRAP_AUTHFAIL 4 +#define SNMP_GENTRAP_ENTERPRISESPC 6 + +struct snmp_varbind +{ + /* next pointer, NULL for last in list */ + struct snmp_varbind *next; + /* previous pointer, NULL for first in list */ + struct snmp_varbind *prev; + + /* object identifier length (in s32_t) */ + u8_t ident_len; + /* object identifier array */ + s32_t *ident; + + /* object value ASN1 type */ + u8_t value_type; + /* object value length (in u8_t) */ + u8_t value_len; + /* object value */ + void *value; + + /* encoding varbind seq length length */ + u8_t seqlenlen; + /* encoding object identifier length length */ + u8_t olenlen; + /* encoding object value length length */ + u8_t vlenlen; + /* encoding varbind seq length */ + u16_t seqlen; + /* encoding object identifier length */ + u16_t olen; + /* encoding object value length */ + u16_t vlen; +}; + +struct snmp_varbind_root +{ + struct snmp_varbind *head; + struct snmp_varbind *tail; + /* number of variable bindings in list */ + u8_t count; + /* encoding varbind-list seq length length */ + u8_t seqlenlen; + /* encoding varbind-list seq length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_resp_header_lengths +{ + /* encoding error-index length length */ + u8_t erridxlenlen; + /* encoding error-status length length */ + u8_t errstatlenlen; + /* encoding request id length length */ + u8_t ridlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding error-index length */ + u16_t erridxlen; + /* encoding error-status length */ + u16_t errstatlen; + /* encoding request id length */ + u16_t ridlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_trap_header_lengths +{ + /* encoding timestamp length length */ + u8_t tslenlen; + /* encoding specific-trap length length */ + u8_t strplenlen; + /* encoding generic-trap length length */ + u8_t gtrplenlen; + /* encoding agent-addr length length */ + u8_t aaddrlenlen; + /* encoding enterprise-id length length */ + u8_t eidlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding timestamp length */ + u16_t tslen; + /* encoding specific-trap length */ + u16_t strplen; + /* encoding generic-trap length */ + u16_t gtrplen; + /* encoding agent-addr length */ + u16_t aaddrlen; + /* encoding enterprise-id length */ + u16_t eidlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/* Accepting new SNMP messages. */ +#define SNMP_MSG_EMPTY 0 +/* Search for matching object for variable binding. */ +#define SNMP_MSG_SEARCH_OBJ 1 +/* Perform SNMP operation on in-memory object. + Pass-through states, for symmetry only. */ +#define SNMP_MSG_INTERNAL_GET_OBJDEF 2 +#define SNMP_MSG_INTERNAL_GET_VALUE 3 +#define SNMP_MSG_INTERNAL_SET_TEST 4 +#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5 +#define SNMP_MSG_INTERNAL_SET_VALUE 6 +/* Perform SNMP operation on object located externally. + In theory this could be used for building a proxy agent. + Practical use is for an enterprise spc. app. gateway. */ +#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7 +#define SNMP_MSG_EXTERNAL_GET_VALUE 8 +#define SNMP_MSG_EXTERNAL_SET_TEST 9 +#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10 +#define SNMP_MSG_EXTERNAL_SET_VALUE 11 + +#define SNMP_COMMUNITY_STR_LEN 64 +struct snmp_msg_pstat +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* source IP address */ + ip_addr_t sip; + /* source UDP port */ + u16_t sp; + /* request type */ + u8_t rt; + /* request ID */ + s32_t rid; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* community name (zero terminated) */ + u8_t community[SNMP_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u8_t com_strlen; + /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */ + u8_t state; + /* saved arguments for MSG_EXTERNAL_x */ + struct mib_external_node *ext_mib_node; + struct snmp_name_ptr ext_name_ptr; + struct obj_def ext_object_def; + struct snmp_obj_id ext_oid; + /* index into input variable binding list */ + u8_t vb_idx; + /* ptr into input variable binding list */ + struct snmp_varbind *vb_ptr; + /* list of variable bindings from input */ + struct snmp_varbind_root invb; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output response lengths used in ASN encoding */ + struct snmp_resp_header_lengths rhl; +}; + +struct snmp_msg_trap +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* destination IP address in network order */ + ip_addr_t dip; + + /* source enterprise ID (sysObjectID) */ + struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + u8_t sip_raw[4]; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output trap lengths used in ASN encoding */ + struct snmp_trap_header_lengths thl; +}; + +/** Agent Version constant, 0 = v1 oddity */ +extern const s32_t snmp_version; +/** Agent default "public" community string */ +extern const char snmp_publiccommunity[7]; + +extern struct snmp_msg_trap trap_msg; + +/** Agent setup, start listening to port 161. */ +void snmp_init(void); +void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable); +void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst); + +/** Varbind-list functions. */ +struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len); +void snmp_varbind_free(struct snmp_varbind *vb); +void snmp_varbind_list_free(struct snmp_varbind_root *root); +void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb); +struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root); + +/** Handle an internal (recv) or external (private response) event. */ +void snmp_msg_event(u8_t request_id); +err_t snmp_send_response(struct snmp_msg_pstat *m_stat); +err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap); +void snmp_coldstart_trap(void); +void snmp_authfail_trap(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_MSG_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp_structs.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp_structs.h new file mode 100644 index 0000000..0d3b46a --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/snmp_structs.h @@ -0,0 +1,268 @@ +/** + * @file + * Generic MIB tree structures. + * + * @todo namespace prefixes + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_STRUCTS_H__ +#define __LWIP_SNMP_STRUCTS_H__ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* MIB object instance */ +#define MIB_OBJECT_NONE 0 +#define MIB_OBJECT_SCALAR 1 +#define MIB_OBJECT_TAB 2 + +/* MIB access types */ +#define MIB_ACCESS_READ 1 +#define MIB_ACCESS_WRITE 2 + +/* MIB object access */ +#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ +#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE) +#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE +#define MIB_OBJECT_NOT_ACCESSIBLE 0 + +/** object definition returned by (get_object_def)() */ +struct obj_def +{ + /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */ + u8_t instance; + /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */ + u8_t access; + /* ASN type for this object */ + u8_t asn_type; + /* value length (host length) */ + u16_t v_len; + /* length of instance part of supplied object identifier */ + u8_t id_inst_len; + /* instance part of supplied object identifier */ + s32_t *id_inst_ptr; +}; + +struct snmp_name_ptr +{ + u8_t ident_len; + s32_t *ident; +}; + +/** MIB const scalar (.0) node */ +#define MIB_NODE_SC 0x01 +/** MIB const array node */ +#define MIB_NODE_AR 0x02 +/** MIB array node (mem_malloced from RAM) */ +#define MIB_NODE_RA 0x03 +/** MIB list root node (mem_malloced from RAM) */ +#define MIB_NODE_LR 0x04 +/** MIB node for external objects */ +#define MIB_NODE_EX 0x05 + +/** node "base class" layout, the mandatory fields for a node */ +struct mib_node +{ + /** returns struct obj_def for the given object identifier */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + /** returns object value for the given object identifier, + @note the caller must allocate at least len bytes for the value */ + void (*get_value)(struct obj_def *od, u16_t len, void *value); + /** tests length and/or range BEFORE setting */ + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + /** sets object value, only to be called when set_test() */ + void (*set_value)(struct obj_def *od, u16_t len, void *value); + /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */ + u8_t node_type; + /* array or max list length */ + u16_t maxlength; +}; + +/** derived node for scalars .0 index */ +typedef struct mib_node mib_scalar_node; + +/** derived node, points to a fixed size const array + of sub-identifiers plus a 'child' pointer */ +struct mib_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + const s32_t *objid; + struct mib_node* const *nptr; +}; + +/** derived node, points to a fixed size mem_malloced array + of sub-identifiers plus a 'child' pointer */ +struct mib_ram_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + s32_t *objid; + struct mib_node **nptr; +}; + +struct mib_list_node +{ + struct mib_list_node *prev; + struct mib_list_node *next; + s32_t objid; + struct mib_node *nptr; +}; + +/** derived node, points to a doubly linked list + of sub-identifiers plus a 'child' pointer */ +struct mib_list_rootnode +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + struct mib_list_node *head; + struct mib_list_node *tail; + /* counts list nodes in list */ + u16_t count; +}; + +/** derived node, has access functions for mib object in external memory or device + using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */ +struct mib_external_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + /** points to an external (in memory) record of some sort of addressing + information, passed to and interpreted by the funtions below */ + void* addr_inf; + /** tree levels under this node */ + u8_t tree_levels; + /** number of objects at this level */ + u16_t (*level_length)(void* addr_inf, u8_t level); + /** compares object sub identifier with external id + return zero when equal, nonzero when unequal */ + s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id); + void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id); + + /** async Questions */ + void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_q)(u8_t rid, struct obj_def *od); + void (*set_test_q)(u8_t rid, struct obj_def *od); + void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Answers */ + void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Panic Close (agent returns error reply, + e.g. used for external transaction cleanup) */ + void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_pc)(u8_t rid, struct obj_def *od); + void (*set_test_pc)(u8_t rid, struct obj_def *od); + void (*set_value_pc)(u8_t rid, struct obj_def *od); +}; + +/** export MIB tree from mib2.c */ +extern const struct mib_array_node internet; + +/** dummy function pointers for non-leaf MIB nodes from mib2.c */ +void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +void noleafs_get_value(struct obj_def *od, u16_t len, void *value); +u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value); +void noleafs_set_value(struct obj_def *od, u16_t len, void *value); + +void snmp_oidtoip(s32_t *ident, ip_addr_t *ip); +void snmp_iptooid(ip_addr_t *ip, s32_t *ident); +void snmp_ifindextonetif(s32_t ifindex, struct netif **netif); +void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx); + +struct mib_list_node* snmp_mib_ln_alloc(s32_t id); +void snmp_mib_ln_free(struct mib_list_node *ln); +struct mib_list_rootnode* snmp_mib_lrn_alloc(void); +void snmp_mib_lrn_free(struct mib_list_rootnode *lrn); + +s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn); +s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn); +struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n); + +struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np); +struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); +u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident); +u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_STRUCTS_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/sockets.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/sockets.h new file mode 100644 index 0000000..7346137 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/sockets.h @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +#ifndef __LWIP_SOCKETS_H__ +#define __LWIP_SOCKETS_H__ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/inet.h" +#include "lwip/inet6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + u8_t sin_family; + u16_t sin_port; + struct in_addr sin_addr; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; +}; + +#if LWIP_IPV6 +struct sockaddr_in6 { + u8_t sin6_len; /* length of this structure */ + u8_t sin6_family; /* AF_INET6 */ + u16_t sin6_port; /* Transport layer port # */ + u32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ +}; +#endif /* LWIP_IPV6 */ + +struct sockaddr { + u8_t sa_len; + u8_t sa_family; +#if LWIP_IPV6 + u8_t sa_data[22]; +#else /* LWIP_IPV6 */ + u8_t sa_data[14]; +#endif /* LWIP_IPV6 */ +}; + +/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_t; +#endif + +/* Socket protocol types (TCP/UDP/RAW) */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/* + * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c) + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* Allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ + +#define SO_DONTLINGER ((int)(~SO_LINGER)) + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ + + +/* + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xfff /* options for socket level */ + + +#define AF_UNSPEC 0 +#define AF_INET 2 +#if LWIP_IPV6 +#define AF_INET6 10 +#else /* LWIP_IPV6 */ +#define AF_INET6 AF_UNSPEC +#endif /* LWIP_IPV6 */ +#define PF_INET AF_INET +#define PF_INET6 AF_INET6 +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#if LWIP_IPV6 +#define IPPROTO_IPV6 41 +#endif /* LWIP_IPV6 */ +#define IPPROTO_UDPLITE 136 + +/* Flags we can use with send and recv. */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ + + +/* + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + +#if LWIP_TCP +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* + * Options for level IPPROTO_IPV6 + */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +#endif /* LWIP_UDP && LWIP_UDPLITE*/ + + +#if LWIP_IGMP +/* + * Options and types for UDP multicast traffic handling + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; +#endif /* LWIP_IGMP */ + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * lwip_ioctl only supports FIONREAD and FIONBIO, for now + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/* Socket I/O Controls: unimplemented */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +/* commands for fnctl */ +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif + +/* File status flags and file access modes for fnctl, + these are bits in an int. */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 1 /* nonblocking I/O */ +#endif +#ifndef O_NDELAY +#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */ +#endif + +#ifndef SHUT_RD + #define SHUT_RD 0 + #define SHUT_WR 1 + #define SHUT_RDWR 2 +#endif + +/* FD_SET used for lwip_select */ +#ifndef FD_SET + #undef FD_SETSIZE + /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */ + #define FD_SETSIZE MEMP_NUM_NETCONN + #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7))) + #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7))) + #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7))) + #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p))) + + typedef struct fd_set { + unsigned char fd_bits [(FD_SETSIZE+7)/8]; + } fd_set; + +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#ifndef LWIP_TIMEVAL_PRIVATE +#define LWIP_TIMEVAL_PRIVATE 1 +#endif + +#if LWIP_TIMEVAL_PRIVATE +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ + +void lwip_socket_init(void); + +int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_shutdown(int s, int how); +int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); +int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); +int lwip_close(int s); +int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_listen(int s, int backlog); +int lwip_recv(int s, void *mem, size_t len, int flags); +int lwip_read(int s, void *mem, size_t len); +int lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen); +int lwip_send(int s, const void *dataptr, size_t size, int flags); +int lwip_sendto(int s, const void *dataptr, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); +int lwip_socket(int domain, int type, int protocol); +int lwip_write(int s, const void *dataptr, size_t size); +int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); +int lwip_ioctl(int s, long cmd, void *argp); +int lwip_fcntl(int s, int cmd, int val); + +#if LWIP_COMPAT_SOCKETS +#define accept(a,b,c) lwip_accept(a,b,c) +#define bind(a,b,c) lwip_bind(a,b,c) +#define shutdown(a,b) lwip_shutdown(a,b) +#define closesocket(s) lwip_close(s) +#define connect(a,b,c) lwip_connect(a,b,c) +#define getsockname(a,b,c) lwip_getsockname(a,b,c) +#define getpeername(a,b,c) lwip_getpeername(a,b,c) +#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e) +#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e) +#define listen(a,b) lwip_listen(a,b) +#define recv(a,b,c,d) lwip_recv(a,b,c,d) +#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f) +#define send(a,b,c,d) lwip_send(a,b,c,d) +#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f) +#define socket(a,b,c) lwip_socket(a,b,c) +#define select(a,b,c,d,e) lwip_select(a,b,c,d,e) +#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c) + +#if LWIP_POSIX_SOCKETS_IO_NAMES +#define read(a,b,c) lwip_read(a,b,c) +#define write(a,b,c) lwip_write(a,b,c) +#define close(s) lwip_close(s) +#define fcntl(a,b,c) lwip_fcntl(a,b,c) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* __LWIP_SOCKETS_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/stats.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/stats.h new file mode 100644 index 0000000..d911216 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/stats.h @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_STATS_H__ +#define __LWIP_STATS_H__ + +#include "lwip/opt.h" + +#include "lwip/mem.h" +#include "lwip/memp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_STATS + +#ifndef LWIP_STATS_LARGE +#define LWIP_STATS_LARGE 0 +#endif + +#if LWIP_STATS_LARGE +#define STAT_COUNTER u32_t +#define STAT_COUNTER_F U32_F +#else +#define STAT_COUNTER u16_t +#define STAT_COUNTER_F U16_F +#endif + +struct stats_proto { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER rterr; /* Routing error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER opterr; /* Error in options. */ + STAT_COUNTER err; /* Misc error. */ + STAT_COUNTER cachehit; +}; + +struct stats_igmp { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER rx_v1; /* Received v1 frames. */ + STAT_COUNTER rx_group; /* Received group-specific queries. */ + STAT_COUNTER rx_general; /* Received general queries. */ + STAT_COUNTER rx_report; /* Received reports. */ + STAT_COUNTER tx_join; /* Sent joins. */ + STAT_COUNTER tx_leave; /* Sent leaves. */ + STAT_COUNTER tx_report; /* Sent reports. */ +}; + +struct stats_mem { +#ifdef LWIP_DEBUG + const char *name; +#endif /* LWIP_DEBUG */ + mem_size_t avail; + mem_size_t used; + mem_size_t max; + STAT_COUNTER err; + STAT_COUNTER illegal; +}; + +struct stats_syselem { + STAT_COUNTER used; + STAT_COUNTER max; + STAT_COUNTER err; +}; + +struct stats_sys { + struct stats_syselem sem; + struct stats_syselem mutex; + struct stats_syselem mbox; +}; + +struct stats_ { +#if LINK_STATS + struct stats_proto link; +#endif +#if ETHARP_STATS + struct stats_proto etharp; +#endif +#if IPFRAG_STATS + struct stats_proto ip_frag; +#endif +#if IP_STATS + struct stats_proto ip; +#endif +#if ICMP_STATS + struct stats_proto icmp; +#endif +#if IGMP_STATS + struct stats_igmp igmp; +#endif +#if UDP_STATS + struct stats_proto udp; +#endif +#if TCP_STATS + struct stats_proto tcp; +#endif +#if MEM_STATS + struct stats_mem mem; +#endif +#if MEMP_STATS + struct stats_mem memp[MEMP_MAX]; +#endif +#if SYS_STATS + struct stats_sys sys; +#endif +#if IP6_STATS + struct stats_proto ip6; +#endif +#if ICMP6_STATS + struct stats_proto icmp6; +#endif +#if IP6_FRAG_STATS + struct stats_proto ip6_frag; +#endif +#if MLD6_STATS + struct stats_igmp mld6; +#endif +#if ND6_STATS + struct stats_proto nd6; +#endif +}; + +extern struct stats_ lwip_stats; + +void stats_init(void); + +#define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x +#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \ + if (lwip_stats.x.max < lwip_stats.x.used) { \ + lwip_stats.x.max = lwip_stats.x.used; \ + } \ + } while(0) +#else /* LWIP_STATS */ +#define stats_init() +#define STATS_INC(x) +#define STATS_DEC(x) +#define STATS_INC_USED(x) +#endif /* LWIP_STATS */ + +#if TCP_STATS +#define TCP_STATS_INC(x) STATS_INC(x) +#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") +#else +#define TCP_STATS_INC(x) +#define TCP_STATS_DISPLAY() +#endif + +#if UDP_STATS +#define UDP_STATS_INC(x) STATS_INC(x) +#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") +#else +#define UDP_STATS_INC(x) +#define UDP_STATS_DISPLAY() +#endif + +#if ICMP_STATS +#define ICMP_STATS_INC(x) STATS_INC(x) +#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") +#else +#define ICMP_STATS_INC(x) +#define ICMP_STATS_DISPLAY() +#endif + +#if IGMP_STATS +#define IGMP_STATS_INC(x) STATS_INC(x) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP") +#else +#define IGMP_STATS_INC(x) +#define IGMP_STATS_DISPLAY() +#endif + +#if IP_STATS +#define IP_STATS_INC(x) STATS_INC(x) +#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") +#else +#define IP_STATS_INC(x) +#define IP_STATS_DISPLAY() +#endif + +#if IPFRAG_STATS +#define IPFRAG_STATS_INC(x) STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") +#else +#define IPFRAG_STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() +#endif + +#if ETHARP_STATS +#define ETHARP_STATS_INC(x) STATS_INC(x) +#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") +#else +#define ETHARP_STATS_INC(x) +#define ETHARP_STATS_DISPLAY() +#endif + +#if LINK_STATS +#define LINK_STATS_INC(x) STATS_INC(x) +#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") +#else +#define LINK_STATS_INC(x) +#define LINK_STATS_DISPLAY() +#endif + +#if MEM_STATS +#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y +#define MEM_STATS_INC(x) STATS_INC(mem.x) +#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y) +#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y +#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") +#else +#define MEM_STATS_AVAIL(x, y) +#define MEM_STATS_INC(x) +#define MEM_STATS_INC_USED(x, y) +#define MEM_STATS_DEC_USED(x, y) +#define MEM_STATS_DISPLAY() +#endif + +#if MEMP_STATS +#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y +#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x) +#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x) +#define MEMP_STATS_INC_USED(x, i) STATS_INC_USED(memp[i], 1) +#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i) +#else +#define MEMP_STATS_AVAIL(x, i, y) +#define MEMP_STATS_INC(x, i) +#define MEMP_STATS_DEC(x, i) +#define MEMP_STATS_INC_USED(x, i) +#define MEMP_STATS_DISPLAY(i) +#endif + +#if SYS_STATS +#define SYS_STATS_INC(x) STATS_INC(sys.x) +#define SYS_STATS_DEC(x) STATS_DEC(sys.x) +#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1) +#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_INC_USED(x) +#define SYS_STATS_DISPLAY() +#endif + +#if IP6_STATS +#define IP6_STATS_INC(x) STATS_INC(x) +#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6") +#else +#define IP6_STATS_INC(x) +#define IP6_STATS_DISPLAY() +#endif + +#if ICMP6_STATS +#define ICMP6_STATS_INC(x) STATS_INC(x) +#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6") +#else +#define ICMP6_STATS_INC(x) +#define ICMP6_STATS_DISPLAY() +#endif + +#if IP6_FRAG_STATS +#define IP6_FRAG_STATS_INC(x) STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG") +#else +#define IP6_FRAG_STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() +#endif + +#if MLD6_STATS +#define MLD6_STATS_INC(x) STATS_INC(x) +#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1") +#else +#define MLD6_STATS_INC(x) +#define MLD6_STATS_DISPLAY() +#endif + +#if ND6_STATS +#define ND6_STATS_INC(x) STATS_INC(x) +#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND") +#else +#define ND6_STATS_INC(x) +#define ND6_STATS_DISPLAY() +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void); +void stats_display_proto(struct stats_proto *proto, const char *name); +void stats_display_igmp(struct stats_igmp *igmp, const char *name); +void stats_display_mem(struct stats_mem *mem, const char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#else /* LWIP_STATS_DISPLAY */ +#define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp, name) +#define stats_display_mem(mem, name) +#define stats_display_memp(mem, index) +#define stats_display_sys(sys) +#endif /* LWIP_STATS_DISPLAY */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_STATS_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/sys.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/sys.h new file mode 100644 index 0000000..fd45ee8 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/sys.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_SYS_H__ +#define __LWIP_SYS_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if NO_SYS + +/* For a totally minimal and standalone system, we provide null + definitions of the sys_ functions. */ +typedef u8_t sys_sem_t; +typedef u8_t sys_mutex_t; +typedef u8_t sys_mbox_t; + +#define sys_sem_new(s, c) ERR_OK +#define sys_sem_signal(s) +#define sys_sem_wait(s) +#define sys_arch_sem_wait(s,t) +#define sys_sem_free(s) +#define sys_sem_valid(s) 0 +#define sys_sem_set_invalid(s) +#define sys_mutex_new(mu) ERR_OK +#define sys_mutex_lock(mu) +#define sys_mutex_unlock(mu) +#define sys_mutex_free(mu) +#define sys_mutex_valid(mu) 0 +#define sys_mutex_set_invalid(mu) +#define sys_mbox_new(m, s) ERR_OK +#define sys_mbox_fetch(m,d) +#define sys_mbox_tryfetch(m,d) +#define sys_mbox_post(m,d) +#define sys_mbox_trypost(m,d) +#define sys_mbox_free(m) +#define sys_mbox_valid(m) +#define sys_mbox_set_invalid(m) + +#define sys_thread_new(n,t,a,s,p) + +#define sys_msleep(t) + +#else /* NO_SYS */ + +/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL + +/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate. + * For now we use the same magic value, but we allow this to change in future. + */ +#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT + +#include "lwip/err.h" +#include "arch/sys_arch.h" + +/** Function prototype for thread functions */ +typedef void (*lwip_thread_fn)(void *arg); + +/* Function prototypes for functions to be implemented by platform ports + (in sys_arch.c) */ + +/* Mutex functions: */ + +/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores + should be used instead */ +#if LWIP_COMPAT_MUTEX +/* for old ports that don't have mutexes: define them to binary semaphores */ +#define sys_mutex_t sys_sem_t +#define sys_mutex_new(mutex) sys_sem_new(mutex, 1) +#define sys_mutex_lock(mutex) sys_sem_wait(mutex) +#define sys_mutex_unlock(mutex) sys_sem_signal(mutex) +#define sys_mutex_free(mutex) sys_sem_free(mutex) +#define sys_mutex_valid(mutex) sys_sem_valid(mutex) +#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex) + +#else /* LWIP_COMPAT_MUTEX */ + +/** Create a new mutex + * @param mutex pointer to the mutex to create + * @return a new mutex */ +err_t sys_mutex_new(sys_mutex_t *mutex); +/** Lock a mutex + * @param mutex the mutex to lock */ +void sys_mutex_lock(sys_mutex_t *mutex); +/** Unlock a mutex + * @param mutex the mutex to unlock */ +void sys_mutex_unlock(sys_mutex_t *mutex); +/** Delete a semaphore + * @param mutex the mutex to delete */ +void sys_mutex_free(sys_mutex_t *mutex); +#ifndef sys_mutex_valid +/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mutex_valid(sys_mutex_t *mutex); +#endif +#ifndef sys_mutex_set_invalid +/** Set a mutex invalid so that sys_mutex_valid returns 0 */ +void sys_mutex_set_invalid(sys_mutex_t *mutex); +#endif +#endif /* LWIP_COMPAT_MUTEX */ + +/* Semaphore functions: */ + +/** Create a new semaphore + * @param sem pointer to the semaphore to create + * @param count initial count of the semaphore + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_sem_new(sys_sem_t *sem, u8_t count); +/** Signals a semaphore + * @param sem the semaphore to signal */ +void sys_sem_signal(sys_sem_t *sem); +/** Wait for a semaphore for the specified timeout + * @param sem the semaphore to wait for + * @param timeout timeout in milliseconds to wait (0 = wait forever) + * @return time (in milliseconds) waited for the semaphore + * or SYS_ARCH_TIMEOUT on timeout */ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout); +/** Delete a semaphore + * @param sem semaphore to delete */ +void sys_sem_free(sys_sem_t *sem); +/** Wait for a semaphore - forever/no timeout */ +#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0) +#ifndef sys_sem_valid +/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_sem_valid(sys_sem_t *sem); +#endif +#ifndef sys_sem_set_invalid +/** Set a semaphore invalid so that sys_sem_valid returns 0 */ +void sys_sem_set_invalid(sys_sem_t *sem); +#endif + +/* Time functions. */ +#ifndef sys_msleep +void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */ +#endif + +/* Mailbox functions. */ + +/** Create a new mbox of specified size + * @param mbox pointer to the mbox to create + * @param size (miminum) number of messages in this mbox + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_mbox_new(sys_mbox_t *mbox, int size); +/** Post a message to an mbox - may not fail + * -> blocks if full, only used from tasks not from ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +void sys_mbox_post(sys_mbox_t *mbox, void *msg); +/** Try to post a message to an mbox - may fail if full or ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg); +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @param timeout maximum time (in milliseconds) to wait for a message (0 = wait forever) + * @return time (in milliseconds) waited for a message, may be 0 if not waited + or SYS_ARCH_TIMEOUT on timeout + * The returned time has to be accurate to prevent timer jitter! */ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout); +/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */ +#ifndef sys_arch_mbox_tryfetch +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @return 0 (milliseconds) if a message has been received + * or SYS_MBOX_EMPTY if the mailbox is empty */ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg); +#endif +/** For now, we map straight to sys_arch implementation. */ +#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg) +/** Delete an mbox + * @param mbox mbox to delete */ +void sys_mbox_free(sys_mbox_t *mbox); +#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0) +#ifndef sys_mbox_valid +/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mbox_valid(sys_mbox_t *mbox); +#endif +#ifndef sys_mbox_set_invalid +/** Set an mbox invalid so that sys_mbox_valid returns 0 */ +void sys_mbox_set_invalid(sys_mbox_t *mbox); +#endif + +/** The only thread function: + * Creates a new thread + * @param name human-readable name for the thread (used for debugging purposes) + * @param thread thread-function + * @param arg parameter passed to 'thread' + * @param stacksize stack size in bytes for the new thread (may be ignored by ports) + * @param prio priority of the new thread (may be ignored by ports) */ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio); + +#endif /* NO_SYS */ + +/* sys_init() must be called before anthing else. */ +void sys_init(void); + +#ifndef sys_jiffies +/** Ticks/jiffies since power up. */ +u32_t sys_jiffies(void); +#endif + +/** Returns the current time in milliseconds, + * may be the same as sys_jiffies or at least based on it. */ +u32_t sys_now(void); + +/* Critical Region Protection */ +/* These functions must be implemented in the sys_arch.c file. + In some implementations they can provide a more light-weight protection + mechanism than using semaphores. Otherwise semaphores can be used for + implementation */ +#ifndef SYS_ARCH_PROTECT +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#if SYS_LIGHTWEIGHT_PROT + +/** SYS_ARCH_DECL_PROTECT + * declare a protection variable. This macro will default to defining a variable of + * type sys_prot_t. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h. + */ +#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev +/** SYS_ARCH_PROTECT + * Perform a "fast" protect. This could be implemented by + * disabling interrupts for an embedded system or by using a semaphore or + * mutex. The implementation should allow calling SYS_ARCH_PROTECT when + * already protected. The old protection level is returned in the variable + * "lev". This macro will default to calling the sys_arch_protect() function + * which should be implemented in sys_arch.c. If a particular port needs a + * different implementation, then this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect() +/** SYS_ARCH_UNPROTECT + * Perform a "fast" set of the protection level to "lev". This could be + * implemented by setting the interrupt level to "lev" within the MACRO or by + * using a semaphore or mutex. This macro will default to calling the + * sys_arch_unprotect() function which should be implemented in + * sys_arch.c. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev) +sys_prot_t sys_arch_protect(void); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#endif /* SYS_ARCH_PROTECT */ + +/* + * Macros to set/get and increase/decrease variables in a thread-safe way. + * Use these for accessing variable that are used from more than one thread. + */ + +#ifndef SYS_ARCH_INC +#define SYS_ARCH_INC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var += val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_INC */ + +#ifndef SYS_ARCH_DEC +#define SYS_ARCH_DEC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var -= val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_DEC */ + +#ifndef SYS_ARCH_GET +#define SYS_ARCH_GET(var, ret) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + ret = var; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_GET */ + +#ifndef SYS_ARCH_SET +#define SYS_ARCH_SET(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var = val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_SET */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SYS_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/tcp.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/tcp.h new file mode 100644 index 0000000..535b6a3 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/tcp.h @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_H__ +#define __LWIP_TCP_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcp_pcb; + +/** Function prototype for tcp accept callback functions. Called when a new + * connection can be accepted on a listening pcb. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param newpcb The new connection pcb + * @param err An error code if there has been an error accepting. + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err); + +/** Function prototype for tcp receive callback functions. Called when data has + * been received. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which received data + * @param p The received data (or NULL when the connection has been closed!) + * @param err An error code if there has been an error receiving + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err); + +/** Function prototype for tcp sent callback functions. Called when sent data has + * been acknowledged by the remote side. Use it to free corresponding resources. + * This also means that the pcb has now space available to send new data. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb for which data has been acknowledged + * @param len The amount of bytes acknowledged + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb, + u16_t len); + +/** Function prototype for tcp poll callback functions. Called periodically as + * specified by @see tcp_poll. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb tcp pcb + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb); + +/** Function prototype for tcp error callback functions. Called when the pcb + * receives a RST or is unexpectedly closed for any other reason. + * + * @note The corresponding pcb is already freed when this callback is called! + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param err Error code to indicate why the pcb has been closed + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ +typedef void (*tcp_err_fn)(void *arg, err_t err); + +/** Function prototype for tcp connected callback functions. Called when a pcb + * is connected to the remote side after initiating a connection attempt by + * calling tcp_connect(). + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which is connected + * @param err An unused error code, always ERR_OK currently ;-) TODO! + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + * + * @note When a connection attempt fails, the error callback is currently called! + */ +typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err); + +enum tcp_state { + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 +}; + +#if LWIP_CALLBACK_API + /* Function to call when a listener has been connected. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb a new tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: accept the new connection, + * any other err_t abortsthe new connection + */ +#define DEF_ACCEPT_CALLBACK tcp_accept_fn accept; +#else /* LWIP_CALLBACK_API */ +#define DEF_ACCEPT_CALLBACK +#endif /* LWIP_CALLBACK_API */ + +/** + * members common to struct tcp_pcb and struct tcp_listen_pcb + */ +#define TCP_PCB_COMMON(type) \ + type *next; /* for the linked list */ \ + void *callback_arg; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + /* ports are in host byte order */ \ + int bound_to_netif; \ + u16_t local_port; \ + char local_netif[3] + + +/* the TCP protocol control block */ +struct tcp_pcb { +/** common PCB members */ + IP_PCB; +/** protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb); + + /* ports are in host byte order */ + u16_t remote_port; + + u8_t flags; +#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */ +#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */ +#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */ +#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */ +#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */ +#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ + + /* the rest of the fields are in host byte order + as we have to do some math with them */ + + /* Timers */ + u8_t polltmr, pollinterval; + u8_t last_timer; + u32_t tmr; + + /* receiver variables */ + u32_t rcv_nxt; /* next seqno expected */ + u16_t rcv_wnd; /* receiver window available */ + u16_t rcv_ann_wnd; /* receiver window to announce */ + u32_t rcv_ann_right_edge; /* announced right edge of window */ + + /* Retransmission timer. */ + s16_t rtime; + + u16_t mss; /* maximum segment size */ + + /* RTT (round trip time) estimation variables */ + u32_t rttest; /* RTT estimate in 500ms ticks */ + u32_t rtseq; /* sequence number being timed */ + s16_t sa, sv; /* @todo document this */ + + s16_t rto; /* retransmission time-out */ + u8_t nrtx; /* number of retransmissions */ + + /* fast retransmit/recovery */ + u8_t dupacks; + u32_t lastack; /* Highest acknowledged seqno. */ + + /* congestion avoidance/control variables */ + u16_t cwnd; + u16_t ssthresh; + + /* sender variables */ + u32_t snd_nxt; /* next new seqno to be sent */ + u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last + window update. */ + u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ + u16_t snd_wnd; /* sender window */ + u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */ + + u16_t acked; + + u16_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3) + u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ + +#if TCP_OVERSIZE + /* Extra bytes available at the end of the last pbuf in unsent. */ + u16_t unsent_oversize; +#endif /* TCP_OVERSIZE */ + + /* These are ordered by sequence number: */ + struct tcp_seg *unsent; /* Unsent (queued) segments. */ + struct tcp_seg *unacked; /* Sent but unacknowledged segments. */ +#if TCP_QUEUE_OOSEQ + struct tcp_seg *ooseq; /* Received out of sequence segments. */ +#endif /* TCP_QUEUE_OOSEQ */ + + struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ + +#if LWIP_CALLBACK_API + /* Function to be called when more send buffer space is available. */ + tcp_sent_fn sent; + /* Function to be called when (in-sequence) data has arrived. */ + tcp_recv_fn recv; + /* Function to be called when a connection has been set up. */ + tcp_connected_fn connected; + /* Function which is called periodically. */ + tcp_poll_fn poll; + /* Function to be called whenever a fatal error occurs. */ + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + +#if LWIP_TCP_TIMESTAMPS + u32_t ts_lastacksent; + u32_t ts_recent; +#endif /* LWIP_TCP_TIMESTAMPS */ + + /* idle time before KEEPALIVE is sent */ + u32_t keep_idle; +#if LWIP_TCP_KEEPALIVE + u32_t keep_intvl; + u32_t keep_cnt; +#endif /* LWIP_TCP_KEEPALIVE */ + + /* Persist timer counter */ + u8_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + + /* KEEPALIVE counter */ + u8_t keep_cnt_sent; +}; + +struct tcp_pcb_listen { +/* Common members of all PCB types */ + IP_PCB; +/* Protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb_listen); + +#if TCP_LISTEN_BACKLOG + u8_t backlog; + u8_t accepts_pending; +#endif /* TCP_LISTEN_BACKLOG */ +#if LWIP_IPV6 + u8_t accept_any_ip_version; +#endif /* LWIP_IPV6 */ +}; + +#if LWIP_EVENT_API + +enum lwip_event { + LWIP_EVENT_ACCEPT, + LWIP_EVENT_SENT, + LWIP_EVENT_RECV, + LWIP_EVENT_CONNECTED, + LWIP_EVENT_POLL, + LWIP_EVENT_ERR +}; + +err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb, + enum lwip_event, + struct pbuf *p, + u16_t size, + err_t err); + +#endif /* LWIP_EVENT_API */ + +/* Application program's interface: */ +struct tcp_pcb * tcp_new (void); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept); +void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv); +void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent); +void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval); +void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err); + +#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss) +#define tcp_sndbuf(pcb) ((pcb)->snd_buf) +#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen) +#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY) +#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY) +#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0) + +#if TCP_LISTEN_BACKLOG +#define tcp_accepted(pcb) do { \ + LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \ + (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0) +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \ + (pcb)->state == LISTEN) +#endif /* TCP_LISTEN_BACKLOG */ + +void tcp_recved (struct tcp_pcb *pcb, u16_t len); +err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t tcp_bind_to_netif (struct tcp_pcb *pcb, const char ifname[3]); +err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, tcp_connected_fn connected); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb); +err_t tcp_close (struct tcp_pcb *pcb); +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx); + +/* Flags for "apiflags" parameter in tcp_write */ +#define TCP_WRITE_FLAG_COPY 0x01 +#define TCP_WRITE_FLAG_MORE 0x02 + +err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +err_t tcp_output (struct tcp_pcb *pcb); + + +const char* tcp_debug_state_str(enum tcp_state s); + +#if LWIP_IPV6 +struct tcp_pcb * tcp_new_ip6 (void); +#define tcp_bind_ip6(pcb, ip6addr, port) \ + tcp_bind(pcb, ip6_2_ip(ip6addr), port) +#define tcp_connect_ip6(pcb, ip6addr, port, connected) \ + tcp_connect(pcb, ip6_2_ip(ip6addr), port, connected) +struct tcp_pcb * tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen_dual(pcb) tcp_listen_dual_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) +#else /* LWIP_IPV6 */ +#define tcp_listen_dual_with_backlog(pcb, backlog) tcp_listen_with_backlog(pcb, backlog) +#define tcp_listen_dual(pcb) tcp_listen(pcb) +#endif /* LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/tcp_impl.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/tcp_impl.h new file mode 100644 index 0000000..2afc20d --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/tcp_impl.h @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_IMPL_H__ +#define __LWIP_TCP_IMPL_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +void tcp_init (void); /* Initialize this module. */ +void tcp_tmr (void); /* Must be called every + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ +/* It is also possible to call these two functions at the right + intervals (instead of calling tcp_tmr()). */ +void tcp_slowtmr (void); +void tcp_fasttmr (void); + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +struct tcp_pcb * tcp_alloc (u8_t prio); +void tcp_abandon (struct tcp_pcb *pcb, int reset); +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +err_t tcp_process_refused_data(struct tcp_pcb *pcb); + +/** + * This is the Nagle algorithm: try to combine user data to send as few TCP + * segments as possible. Only send if + * - no previously transmitted data on the connection remains unacknowledged or + * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or + * - the only unsent segment is at least pcb->mss bytes long (or there is more + * than one unsent segment - with lwIP, this can happen although unsent->len < mss) + * - or if we are in fast-retransmit (TF_INFR) + */ +#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ + ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ + (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ + ((tpcb)->unsent->len >= (tpcb)->mss))) || \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ) ? 1 : 0) +#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) + + +#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0) +/* is b<=a<=c? */ +#if 0 /* see bug #10548 */ +#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b)) +#endif +#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) +#define TCP_FIN 0x01U +#define TCP_SYN 0x02U +#define TCP_RST 0x04U +#define TCP_PSH 0x08U +#define TCP_ACK 0x10U +#define TCP_URG 0x20U +#define TCP_ECE 0x40U +#define TCP_CWR 0x80U + +#define TCP_FLAGS 0x3fU + +/* Length of the TCP header, excluding options. */ +#define TCP_HLEN 20 + +#ifndef TCP_TMR_INTERVAL +#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */ +#endif /* TCP_TMR_INTERVAL */ + +#ifndef TCP_FAST_INTERVAL +#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */ +#endif /* TCP_FAST_INTERVAL */ + +#ifndef TCP_SLOW_INTERVAL +#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */ +#endif /* TCP_SLOW_INTERVAL */ + +#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ +#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ + +#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */ + +#ifndef TCP_MSL +#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */ +#endif + +/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ +#ifndef TCP_KEEPIDLE_DEFAULT +#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */ +#endif + +#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ + +/* Fields are (of course) in network byte order. + * Some fields are converted to host byte order in tcp_input(). + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct tcp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); + PACK_STRUCT_FIELD(u32_t seqno); + PACK_STRUCT_FIELD(u32_t ackno); + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags); + PACK_STRUCT_FIELD(u16_t wnd); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t urgp); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) + +#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) +#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags)) +#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags)) + +#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags)) +#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) ) + +#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0)) + +/** Flags used on input processing, not on pcb->flags +*/ +#define TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ +#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ + + +#if LWIP_EVENT_API + +#define TCP_EVENT_ACCEPT(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_ACCEPT, NULL, 0, err) +#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_SENT, NULL, space, ERR_OK) +#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, (p), 0, (err)) +#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, NULL, 0, ERR_OK) +#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_CONNECTED, NULL, 0, (err)) +#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_POLL, NULL, 0, ERR_OK) +#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \ + LWIP_EVENT_ERR, NULL, 0, (err)) + +#else /* LWIP_EVENT_API */ + +#define TCP_EVENT_ACCEPT(pcb,err,ret) \ + do { \ + if((pcb)->accept != NULL) \ + (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_ARG; \ + } while (0) + +#define TCP_EVENT_SENT(pcb,space,ret) \ + do { \ + if((pcb)->sent != NULL) \ + (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_RECV(pcb,p,err,ret) \ + do { \ + if((pcb)->recv != NULL) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\ + } else { \ + (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \ + } \ + } while (0) + +#define TCP_EVENT_CLOSED(pcb,ret) \ + do { \ + if(((pcb)->recv != NULL)) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\ + } else { \ + (ret) = ERR_OK; \ + } \ + } while (0) + +#define TCP_EVENT_CONNECTED(pcb,err,ret) \ + do { \ + if((pcb)->connected != NULL) \ + (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_POLL(pcb,ret) \ + do { \ + if((pcb)->poll != NULL) \ + (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_ERR(errf,arg,err) \ + do { \ + if((errf) != NULL) \ + (errf)((arg),(err)); \ + } while (0) + +#endif /* LWIP_EVENT_API */ + +/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */ +#if TCP_OVERSIZE && defined(LWIP_DEBUG) +#define TCP_OVERSIZE_DBGCHECK 1 +#else +#define TCP_OVERSIZE_DBGCHECK 0 +#endif + +/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */ +#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP) + +/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */ +struct tcp_seg { + struct tcp_seg *next; /* used when putting segements on a queue */ + struct pbuf *p; /* buffer containing data + TCP header */ + u16_t len; /* the TCP length of this segment */ +#if TCP_OVERSIZE_DBGCHECK + u16_t oversize_left; /* Extra bytes available at the end of the last + pbuf in unsent (used for asserting vs. + tcp_pcb.unsent_oversized only) */ +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + u16_t chksum; + u8_t chksum_swapped; +#endif /* TCP_CHECKSUM_ON_COPY */ + u8_t flags; +#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ +#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ +#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is + checksummed into 'chksum' */ + struct tcp_hdr *tcphdr; /* the TCP header */ +}; + +#define LWIP_TCP_OPT_LENGTH(flags) \ + (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \ + (flags & TF_SEG_OPTS_TS ? 12 : 0) + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) + +/* Global variables: */ +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; +extern u8_t tcp_active_pcbs_changed; + +/* The TCP PCB lists. */ +union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ + struct tcp_pcb_listen *listen_pcbs; + struct tcp_pcb *pcbs; +}; +extern struct tcp_pcb *tcp_bound_pcbs; +extern union tcp_listen_pcbs_t tcp_listen_pcbs; +extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a + state in which they accept or send + data. */ +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ + +extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */ + +/* Axioms about the above lists: + 1) Every TCP PCB that is not CLOSED is in one of the lists. + 2) A PCB is only in one of the lists. + 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state. + 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state. +*/ +/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB + with a PCB list or removes a PCB from a list, respectively. */ +#ifndef TCP_DEBUG_PCB_LISTS +#define TCP_DEBUG_PCB_LISTS 0 +#endif +#if TCP_DEBUG_PCB_LISTS +#define TCP_REG(pcbs, npcb) do {\ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \ + for(tcp_tmp_pcb = *(pcbs); \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \ + } \ + LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \ + (npcb)->next = *(pcbs); \ + LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \ + *(pcbs) = (npcb); \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + tcp_timer_needed(); \ + } while(0) +#define TCP_RMV(pcbs, npcb) do { \ + LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*pcbs)->next; \ + } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \ + } while(0) + +#else /* LWIP_DEBUG */ + +#define TCP_REG(pcbs, npcb) \ + do { \ + (npcb)->next = *pcbs; \ + *(pcbs) = (npcb); \ + tcp_timer_needed(); \ + } while (0) + +#define TCP_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == (npcb)) { \ + (*(pcbs)) = (*pcbs)->next; \ + } \ + else { \ + for(tcp_tmp_pcb = *pcbs; \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + } \ + (npcb)->next = NULL; \ + } while(0) + +#endif /* LWIP_DEBUG */ + +#define TCP_REG_ACTIVE(npcb) \ + do { \ + TCP_REG(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_RMV_ACTIVE(npcb) \ + do { \ + TCP_RMV(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_PCB_REMOVE_ACTIVE(pcb) \ + do { \ + tcp_pcb_remove(&tcp_active_pcbs, pcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + + +/* Internal functions: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +void tcp_segs_free(struct tcp_seg *seg); +void tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#define tcp_ack(pcb) \ + do { \ + if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + } \ + else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } \ + } while (0) + +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + } while (0) + +err_t tcp_send_fin(struct tcp_pcb *pcb); +err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +void tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port) +#endif /* LWIP_IPV6 */ + +u32_t tcp_next_iss(void); + +void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest, src, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest) +#endif /* LWIP_IPV6 */ +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if LWIP_CALLBACK_API +err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +#endif /* LWIP_CALLBACK_API */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#else +# define tcp_debug_print(tcphdr) +# define tcp_debug_print_flags(flags) +# define tcp_debug_print_state(s) +# define tcp_debug_print_pcbs() +# define tcp_pcbs_sane() 1 +#endif /* TCP_DEBUG */ + +/** External function (implemented in timers.c), called when TCP detects + * that a timer is needed (i.e. active- or time-wait-pcb found). */ +void tcp_timer_needed(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/tcpip.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/tcpip.h new file mode 100644 index 0000000..04567f2 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/tcpip.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCPIP_H__ +#define __LWIP_TCPIP_H__ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" +#include "lwip/netifapi.h" +#include "lwip/pbuf.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define this to something that triggers a watchdog. This is called from + * tcpip_thread after processing a message. */ +#ifndef LWIP_TCPIP_THREAD_ALIVE +#define LWIP_TCPIP_THREAD_ALIVE() +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_mutex_t lock_tcpip_core; +#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) +#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) +#ifdef LWIP_DEBUG +#define TCIP_APIMSG_SET_ERR(m, e) (m)->msg.err = e /* catch functions that don't set err */ +#else +#define TCIP_APIMSG_SET_ERR(m, e) +#endif +#define TCPIP_APIMSG_NOERR(m,f) do { \ + TCIP_APIMSG_SET_ERR(m, ERR_VAL); \ + LOCK_TCPIP_CORE(); \ + f(&((m)->msg)); \ + UNLOCK_TCPIP_CORE(); \ +} while(0) +#define TCPIP_APIMSG(m,f,e) do { \ + TCPIP_APIMSG_NOERR(m,f); \ + (e) = (m)->msg.err; \ +} while(0) +#define TCPIP_APIMSG_ACK(m) +#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) +#define TCPIP_NETIFAPI_ACK(m) +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#define TCPIP_APIMSG_NOERR(m,f) do { (m)->function = f; tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed) +#define TCPIP_NETIFAPI(m) tcpip_netifapi(m) +#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem) +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** Function prototype for the init_done function passed to tcpip_init */ +typedef void (*tcpip_init_done_fn)(void *arg); +/** Function prototype for functions passed to tcpip_callback() */ +typedef void (*tcpip_callback_fn)(void *ctx); + +/* Forward declarations */ +struct tcpip_callback_msg; + +void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg); + +#if LWIP_NETCONN +err_t tcpip_apimsg(struct api_msg *apimsg); +#endif /* LWIP_NETCONN */ + +err_t tcpip_input(struct pbuf *p, struct netif *inp); + +#if LWIP_NETIF_API +err_t tcpip_netifapi(struct netifapi_msg *netifapimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block); +#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) + +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx); +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg); +err_t tcpip_trycallback(struct tcpip_callback_msg* msg); + +/* free pbufs or heap memory from another context without blocking */ +err_t pbuf_free_callback(struct pbuf *p); +err_t mem_free_callback(void *m); + +#if LWIP_TCPIP_TIMEOUT +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +err_t tcpip_untimeout(sys_timeout_handler h, void *arg); +#endif /* LWIP_TCPIP_TIMEOUT */ + +enum tcpip_msg_type { +#if LWIP_NETCONN + TCPIP_MSG_API, +#endif /* LWIP_NETCONN */ + TCPIP_MSG_INPKT, +#if LWIP_NETIF_API + TCPIP_MSG_NETIFAPI, +#endif /* LWIP_NETIF_API */ +#if LWIP_TCPIP_TIMEOUT + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT, +#endif /* LWIP_TCPIP_TIMEOUT */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_CALLBACK_STATIC +}; + +struct tcpip_msg { + enum tcpip_msg_type type; + sys_sem_t *sem; + union { +#if LWIP_NETCONN + struct api_msg *apimsg; +#endif /* LWIP_NETCONN */ +#if LWIP_NETIF_API + struct netifapi_msg *netifapimsg; +#endif /* LWIP_NETIF_API */ + struct { + struct pbuf *p; + struct netif *netif; + } inp; + struct { + tcpip_callback_fn function; + void *ctx; + } cb; +#if LWIP_TCPIP_TIMEOUT + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; +#endif /* LWIP_TCPIP_TIMEOUT */ + } msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* __LWIP_TCPIP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/timers.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/timers.h new file mode 100644 index 0000000..04e78e0 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/timers.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ +#ifndef __LWIP_TIMERS_H__ +#define __LWIP_TIMERS_H__ + +#include "lwip/opt.h" + +/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */ +#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) + +#if LWIP_TIMERS + +#include "lwip/err.h" +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LWIP_DEBUG_TIMERNAMES +#ifdef LWIP_DEBUG +#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG +#else /* LWIP_DEBUG */ +#define LWIP_DEBUG_TIMERNAMES 0 +#endif /* LWIP_DEBUG*/ +#endif + +/** Function prototype for a timeout callback function. Register such a function + * using sys_timeout(). + * + * @param arg Additional argument to pass to the function - set up by sys_timeout() + */ +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +#if LWIP_DEBUG_TIMERNAMES + const char* handler_name; +#endif /* LWIP_DEBUG_TIMERNAMES */ +}; + +void sys_timeouts_init(void); + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name); +#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler) +#else /* LWIP_DEBUG_TIMERNAMES */ +void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg); +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout(sys_timeout_handler handler, void *arg); +#if NO_SYS +void sys_check_timeouts(void); +void sys_restart_timeouts(void); +#else /* NO_SYS */ +void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg); +#endif /* NO_SYS */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TIMERS */ +#endif /* __LWIP_TIMERS_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/lwip/udp.h b/service/src/main/jni/badvpn/lwip/src/include/lwip/udp.h new file mode 100644 index 0000000..14d5c0a --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/lwip/udp.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_UDP_H__ +#define __LWIP_UDP_H__ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDP_HLEN 8 + +/* Fields are (of course) in network byte order. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct udp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */ + PACK_STRUCT_FIELD(u16_t len); + PACK_STRUCT_FIELD(u16_t chksum); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define UDP_FLAGS_NOCHKSUM 0x01U +#define UDP_FLAGS_UDPLITE 0x02U +#define UDP_FLAGS_CONNECTED 0x04U +#define UDP_FLAGS_MULTICAST_LOOP 0x08U + +struct udp_pcb; + +/** Function prototype for udp pcb receive callback functions + * addr and port are in same byte order as in the pcb + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf + * makes 'addr' invalid, too. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port); + +#if LWIP_IPV6 +/** Function prototype for udp pcb IPv6 receive callback functions + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_ip6_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr, u16_t port); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define UDP_PCB_RECV_IP6 udp_recv_ip6_fn ip6; +#else +#define UDP_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +struct udp_pcb { +/* Common members of all PCB types */ + IP_PCB; + +/* Protocol specific PCB members */ + + struct udp_pcb *next; + + u8_t flags; + /** ports are in host byte order */ + u16_t local_port, remote_port; + +#if LWIP_IGMP + /** outgoing network interface for multicast packets */ + ip_addr_t multicast_ip; +#endif /* LWIP_IGMP */ + +#if LWIP_UDPLITE + /** used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; +#endif /* LWIP_UDPLITE */ + + /** receive callback function */ + union { + udp_recv_fn ip4; + UDP_PCB_RECV_IP6 + }recv; + /** user-supplied argument for the recv callback */ + void *recv_arg; +}; +/* udp_pcbs export for exernal reference (e.g. SNMP agent) */ +extern struct udp_pcb *udp_pcbs; + +/* The following functions is the application layer interface to the + UDP code. */ +struct udp_pcb * udp_new (void); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +err_t udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif, u8_t have_chksum, + u16_t chksum); +err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + u8_t have_chksum, u16_t chksum); +err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +#define udp_flags(pcb) ((pcb)->flags) +#define udp_setflags(pcb, f) ((pcb)->flags = (f)) + +/* The following functions are the lower layer interface to UDP. */ +void udp_input (struct pbuf *p, struct netif *inp); + +void udp_init (void); + +#if LWIP_IPV6 +struct udp_pcb * udp_new_ip6(void); +#define udp_bind_ip6(pcb, ip6addr, port) \ + udp_bind(pcb, ip6_2_ip(ip6addr), port) +#define udp_connect_ip6(pcb, ip6addr, port) \ + udp_connect(pcb, ip6_2_ip(ip6addr), port) +#define udp_recv_ip6(pcb, recv_ip6_fn, recv_arg) \ + udp_recv(pcb, (udp_recv_fn)recv_ip6_fn, recv_arg) +#define udp_sendto_ip6(pcb, pbuf, ip6addr, port) \ + udp_sendto(pcb, pbuf, ip6_2_ip(ip6addr), port) +#define udp_sendto_if_ip6(pcb, pbuf, ip6addr, port, netif) \ + udp_sendto_if(pcb, pbuf, ip6_2_ip(ip6addr), port, netif) +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +#define udp_sendto_chksum_ip6(pcb, pbuf, ip6addr, port, have_chk, chksum) \ + udp_sendto_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, have_chk, chksum) +#define udp_sendto_if_chksum_ip6(pcb, pbuf, ip6addr, port, netif, have_chk, chksum) \ + udp_sendto_if_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, netif, have_chk, chksum) +#endif /*LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/netif/etharp.h b/service/src/main/jni/badvpn/lwip/src/include/netif/etharp.h new file mode 100644 index 0000000..8275a28 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/netif/etharp.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __NETIF_ETHARP_H__ +#define __NETIF_ETHARP_H__ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** Ethernet header */ +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#if ETHARP_SUPPORT_VLAN + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** VLAN header inserted between ethernet header and payload + * if 'type' in ethernet header is ETHTYPE_VLAN. + * See IEEE802.Q */ +struct eth_vlan_hdr { + PACK_STRUCT_FIELD(u16_t prio_vid); + PACK_STRUCT_FIELD(u16_t tpid); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_VLAN_HDR 4 +#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF) + +#endif /* ETHARP_SUPPORT_VLAN */ + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** the ARP message, see RFC 826 ("Packet format") */ +struct etharp_hdr { + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FIELD(u8_t hwlen); + PACK_STRUCT_FIELD(u8_t protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FIELD(struct eth_addr shwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); + PACK_STRUCT_FIELD(struct eth_addr dhwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETHARP_HDR 28 +#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 5000 + +#define ETHTYPE_ARP 0x0806U +#define ETHTYPE_IP 0x0800U +#define ETHTYPE_VLAN 0x8100U +#define ETHTYPE_IPV6 0x86DDU +#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */ + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables + * or known to be 32-bit aligned within the protocol header. */ +#ifndef ETHADDR32_COPY +#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local + * variables and known to be 16-bit aligned within the protocol header. */ +#ifndef ETHADDR16_COPY +#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type) + * to a filter function that returns the correct netif when using multiple + * netifs on one hardware interface where the netif's low-level receive + * routine cannot decide for the correct netif (e.g. when mapping multiple + * IP addresses to one hardware interface). + */ +#ifndef LWIP_ARP_FILTER_NETIF +#define LWIP_ARP_FILTER_NETIF 0 +#endif + +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, not init needed. */ +void etharp_tmr(void); +s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret); +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr); +err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr); +/** For Ethernet network interfaces, we might want to send "gratuitous ARP"; + * this is an ARP packet sent by a node in order to spontaneously cause other + * nodes to update an entry in their ARP cache. + * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ +#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) +void etharp_cleanup_netif(struct netif *netif); + +#if ETHARP_SUPPORT_STATIC_ENTRIES +err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr); +err_t etharp_remove_static_entry(ip_addr_t *ipaddr); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_AUTOIP +err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode); +#endif /* LWIP_AUTOIP */ + +#endif /* LWIP_ARP */ + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#endif /* __NETIF_ARP_H__ */ diff --git a/service/src/main/jni/badvpn/lwip/src/include/netif/ppp_oe.h b/service/src/main/jni/badvpn/lwip/src/include/netif/ppp_oe.h new file mode 100644 index 0000000..e1cdfa5 --- /dev/null +++ b/service/src/main/jni/badvpn/lwip/src/include/netif/ppp_oe.h @@ -0,0 +1,190 @@ +/***************************************************************************** +* ppp_oe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#include "netif/etharp.h" + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +#ifndef PPPOE_MAX_AC_COOKIE_LEN +#define PPPOE_MAX_AC_COOKIE_LEN 64 +#endif + +struct pppoe_softc { + struct pppoe_softc *next; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + +#ifdef PPPOE_TODO + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ +#endif /* PPPOE_TODO */ + u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + + +#define pppoe_init() /* compatibility define, no initialization needed */ + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +/** used in ppp.c */ +#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN) + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/service/src/main/jni/badvpn/misc/balign.h b/service/src/main/jni/badvpn/misc/balign.h new file mode 100644 index 0000000..57152af --- /dev/null +++ b/service/src/main/jni/badvpn/misc/balign.h @@ -0,0 +1,76 @@ +/** + * @file balign.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Integer alignment macros. + */ + +#ifndef BADVPN_MISC_BALIGN_H +#define BADVPN_MISC_BALIGN_H + +#include +#include + +/** + * Checks if aligning x up to n would overflow. + */ +static int balign_up_overflows (size_t x, size_t n) +{ + size_t r = x % n; + + return (r && x > SIZE_MAX - (n - r)); +} + +/** + * Aligns x up to n. + */ +static size_t balign_up (size_t x, size_t n) +{ + size_t r = x % n; + return (r ? x + (n - r) : x); +} + +/** + * Aligns x down to n. + */ +static size_t balign_down (size_t x, size_t n) +{ + return (x - (x % n)); +} + +/** + * Calculates the quotient of a and b, rounded up. + */ +static size_t bdivide_up (size_t a, size_t b) +{ + size_t r = a % b; + return (r > 0 ? a / b + 1 : a / b); +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/balloc.h b/service/src/main/jni/badvpn/misc/balloc.h new file mode 100644 index 0000000..7d2d54f --- /dev/null +++ b/service/src/main/jni/badvpn/misc/balloc.h @@ -0,0 +1,248 @@ +/** + * @file balloc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Memory allocation functions. + */ + +#ifndef BADVPN_MISC_BALLOC_H +#define BADVPN_MISC_BALLOC_H + +#include +#include +#include +#include + +#include +#include +#include + +/** + * Allocates memory. + * + * @param bytes number of bytes to allocate. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAlloc (size_t bytes); + +/** + * Frees memory. + * + * @param m memory to free. Must have been obtained with {@link BAlloc}, + * {@link BAllocArray}, or {@link BAllocArray2}. May be NULL; + * in this case, this function does nothing. + */ +static void BFree (void *m); + +/** + * Changes the size of a memory block. On success, the memory block + * may be moved to a different address. + * + * @param m pointer to a memory block obtained from {@link BAlloc} + * or other functions in this group. If this is NULL, the + * call is equivalent to {@link BAlloc}(bytes). + * @param bytes new size of the memory block + * @return new pointer to the memory block, or NULL on failure + */ +static void * BRealloc (void *m, size_t bytes); + +/** + * Allocates memory, with size given as a {@link bsize_t}. + * + * @param bytes number of bytes to allocate. If the size is overflow, + * this function will return NULL. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocSize (bsize_t bytes); + +/** + * Allocates memory for an array. + * A check is first done to make sure the multiplication doesn't overflow; + * otherwise, this is equivalent to {@link BAlloc}(count * bytes). + * This may be slightly faster if 'bytes' is constant, because a division + * with 'bytes' is performed. + * + * @param count number of elements. + * @param bytes size of one array element. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocArray (size_t count, size_t bytes); + +/** + * Reallocates memory that was allocated using one of the allocation + * functions in this file. On success, the memory may be moved to a + * different address, leaving the old address invalid. + * + * @param mem pointer to an existing memory block. May be NULL, in which + * case this is equivalent to {@link BAllocArray}. + * @param count number of elements for reallocation + * @param bytes size of one array element for reallocation + * @return a non-NULL pointer to the address of the reallocated memory + * block, or NULL on failure. On failure, the original memory + * block is left intact. + */ +static void * BReallocArray (void *mem, size_t count, size_t bytes); + +/** + * Allocates memory for a two-dimensional array. + * + * Checks are first done to make sure the multiplications don't overflow; + * otherwise, this is equivalent to {@link BAlloc}((count2 * (count1 * bytes)). + * + * @param count2 number of elements in one dimension. + * @param count1 number of elements in the other dimension. + * @param bytes size of one array element. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocArray2 (size_t count2, size_t count1, size_t bytes); + +/** + * Adds to a size_t with overflow detection. + * + * @param s pointer to a size_t to add to + * @param add number to add + * @return 1 on success, 0 on failure + */ +static int BSizeAdd (size_t *s, size_t add); + +/** + * Aligns a size_t upwards with overflow detection. + * + * @param s pointer to a size_t to align + * @param align alignment value. Must be >0. + * @return 1 on success, 0 on failure + */ +static int BSizeAlign (size_t *s, size_t align); + +void * BAlloc (size_t bytes) +{ + if (bytes == 0) { + return malloc(1); + } + + return malloc(bytes); +} + +void BFree (void *m) +{ + free(m); +} + +void * BRealloc (void *m, size_t bytes) +{ + if (bytes == 0) { + return realloc(m, 1); + } + + return realloc(m, bytes); +} + +void * BAllocSize (bsize_t bytes) +{ + if (bytes.is_overflow) { + return NULL; + } + + return BAlloc(bytes.value); +} + +void * BAllocArray (size_t count, size_t bytes) +{ + if (count == 0 || bytes == 0) { + return malloc(1); + } + + if (count > SIZE_MAX / bytes) { + return NULL; + } + + return BAlloc(count * bytes); +} + +void * BReallocArray (void *mem, size_t count, size_t bytes) +{ + if (count == 0 || bytes == 0) { + return realloc(mem, 1); + } + + if (count > SIZE_MAX / bytes) { + return NULL; + } + + return realloc(mem, count * bytes); +} + +void * BAllocArray2 (size_t count2, size_t count1, size_t bytes) +{ + if (count2 == 0 || count1 == 0 || bytes == 0) { + return malloc(1); + } + + if (count1 > SIZE_MAX / bytes) { + return NULL; + } + + if (count2 > SIZE_MAX / (count1 * bytes)) { + return NULL; + } + + return BAlloc(count2 * (count1 * bytes)); +} + +int BSizeAdd (size_t *s, size_t add) +{ + ASSERT(s) + + if (add > SIZE_MAX - *s) { + return 0; + } + *s += add; + return 1; +} + +int BSizeAlign (size_t *s, size_t align) +{ + ASSERT(s) + ASSERT(align > 0) + + size_t mod = *s % align; + if (mod > 0) { + if (align - mod > SIZE_MAX - *s) { + return 0; + } + *s += align - mod; + } + return 1; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/bsize.h b/service/src/main/jni/badvpn/misc/bsize.h new file mode 100644 index 0000000..2d724df --- /dev/null +++ b/service/src/main/jni/badvpn/misc/bsize.h @@ -0,0 +1,117 @@ +/** + * @file bsize.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Arithmetic with overflow detection. + */ + +#ifndef BADVPN_MISC_BSIZE_H +#define BADVPN_MISC_BSIZE_H + +#include +#include +#include + +typedef struct { + int is_overflow; + size_t value; +} bsize_t; + +static bsize_t bsize_fromsize (size_t v); +static bsize_t bsize_fromint (int v); +static bsize_t bsize_overflow (void); +static int bsize_tosize (bsize_t s, size_t *out); +static int bsize_toint (bsize_t s, int *out); +static bsize_t bsize_add (bsize_t s1, bsize_t s2); +static bsize_t bsize_max (bsize_t s1, bsize_t s2); +static bsize_t bsize_mul (bsize_t s1, bsize_t s2); + +bsize_t bsize_fromsize (size_t v) +{ + bsize_t s = {0, v}; + return s; +} + +bsize_t bsize_fromint (int v) +{ + bsize_t s = {(v < 0 || v > SIZE_MAX), v}; + return s; +} + +static bsize_t bsize_overflow (void) +{ + bsize_t s = {1, 0}; + return s; +} + +int bsize_tosize (bsize_t s, size_t *out) +{ + if (s.is_overflow) { + return 0; + } + + if (out) { + *out = s.value; + } + + return 1; +} + +int bsize_toint (bsize_t s, int *out) +{ + if (s.is_overflow || s.value > INT_MAX) { + return 0; + } + + if (out) { + *out = s.value; + } + + return 1; +} + +bsize_t bsize_add (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow || s2.value > SIZE_MAX - s1.value), (s1.value + s2.value)}; + return s; +} + +bsize_t bsize_max (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow), ((s1.value > s2.value) * s1.value + (s1.value <= s2.value) * s2.value)}; + return s; +} + +bsize_t bsize_mul (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow || (s1.value != 0 && s2.value > SIZE_MAX / s1.value)), (s1.value * s2.value)}; + return s; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/byteorder.h b/service/src/main/jni/badvpn/misc/byteorder.h new file mode 100644 index 0000000..055b0a5 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/byteorder.h @@ -0,0 +1,196 @@ +/** + * @file byteorder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Byte order conversion functions. + * + * hton* functions convert from host to big-endian (network) byte order. + * htol* functions convert from host to little-endian byte order. + * ntoh* functions convert from big-endian (network) to host byte order. + * ltoh* functions convert from little-endian to host byte order. + */ + +#ifndef BADVPN_MISC_BYTEORDER_H +#define BADVPN_MISC_BYTEORDER_H + +#if (defined(BADVPN_LITTLE_ENDIAN) + defined(BADVPN_BIG_ENDIAN)) != 1 +#error Unknown byte order or too many byte orders +#endif + +#include + +static uint16_t badvpn_reverse16 (uint16_t x) +{ + uint16_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+1); + *((uint8_t *)&y+1) = *((uint8_t *)&x+0); + return y; +} + +static uint32_t badvpn_reverse32 (uint32_t x) +{ + uint32_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+3); + *((uint8_t *)&y+1) = *((uint8_t *)&x+2); + *((uint8_t *)&y+2) = *((uint8_t *)&x+1); + *((uint8_t *)&y+3) = *((uint8_t *)&x+0); + return y; +} + +static uint64_t badvpn_reverse64 (uint64_t x) +{ + uint64_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+7); + *((uint8_t *)&y+1) = *((uint8_t *)&x+6); + *((uint8_t *)&y+2) = *((uint8_t *)&x+5); + *((uint8_t *)&y+3) = *((uint8_t *)&x+4); + *((uint8_t *)&y+4) = *((uint8_t *)&x+3); + *((uint8_t *)&y+5) = *((uint8_t *)&x+2); + *((uint8_t *)&y+6) = *((uint8_t *)&x+1); + *((uint8_t *)&y+7) = *((uint8_t *)&x+0); + return y; +} + +static uint8_t hton8 (uint8_t x) +{ + return x; +} + +static uint8_t htol8 (uint8_t x) +{ + return x; +} + +#if defined(BADVPN_LITTLE_ENDIAN) + +static uint16_t hton16 (uint16_t x) +{ + return badvpn_reverse16(x); +} + +static uint32_t hton32 (uint32_t x) +{ + return badvpn_reverse32(x); +} + +static uint64_t hton64 (uint64_t x) +{ + return badvpn_reverse64(x); +} + +static uint16_t htol16 (uint16_t x) +{ + return x; +} + +static uint32_t htol32 (uint32_t x) +{ + return x; +} + +static uint64_t htol64 (uint64_t x) +{ + return x; +} + +#elif defined(BADVPN_BIG_ENDIAN) + +static uint16_t hton16 (uint16_t x) +{ + return x; +} + +static uint32_t hton32 (uint32_t x) +{ + return x; +} + +static uint64_t hton64 (uint64_t x) +{ + return x; +} + +static uint16_t htol16 (uint16_t x) +{ + return badvpn_reverse16(x); +} + +static uint32_t htol32 (uint32_t x) +{ + return badvpn_reverse32(x); +} + +static uint64_t htol64 (uint64_t x) +{ + return badvpn_reverse64(x); +} + +#endif + +static uint8_t ntoh8 (uint8_t x) +{ + return hton8(x); +} + +static uint16_t ntoh16 (uint16_t x) +{ + return hton16(x); +} + +static uint32_t ntoh32 (uint32_t x) +{ + return hton32(x); +} + +static uint64_t ntoh64 (uint64_t x) +{ + return hton64(x); +} + +static uint8_t ltoh8 (uint8_t x) +{ + return htol8(x); +} + +static uint16_t ltoh16 (uint16_t x) +{ + return htol16(x); +} + +static uint32_t ltoh32 (uint32_t x) +{ + return htol32(x); +} + +static uint64_t ltoh64 (uint64_t x) +{ + return htol64(x); +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/compare.h b/service/src/main/jni/badvpn/misc/compare.h new file mode 100644 index 0000000..8d1a1b9 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/compare.h @@ -0,0 +1,37 @@ +/** + * @file compare.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_COMPARE_H +#define BADVPN_COMPARE_H + +#define B_COMPARE(a, b) (((a) > (b)) - ((a) < (b))) +#define B_COMPARE_COMBINE(cmp1, cmp2) ((cmp1) ? (cmp1) : (cmp2)) +#define B_COMPARE2(a, b, c, d) B_COMPARE_COMBINE(B_COMPARE((a), (b)), B_COMPARE((c), (d))) + +#endif diff --git a/service/src/main/jni/badvpn/misc/concat_strings.h b/service/src/main/jni/badvpn/misc/concat_strings.h new file mode 100644 index 0000000..83e0977 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/concat_strings.h @@ -0,0 +1,85 @@ +/** + * @file concat_strings.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for concatenating strings. + */ + +#ifndef BADVPN_MISC_CONCAT_STRINGS_H +#define BADVPN_MISC_CONCAT_STRINGS_H + +#include +#include +#include + +#include + +static char * concat_strings (int num, ...) +{ + ASSERT(num >= 0) + + // calculate sum of lengths + size_t sum = 0; + va_list ap; + va_start(ap, num); + for (int i = 0; i < num; i++) { + const char *str = va_arg(ap, const char *); + size_t str_len = strlen(str); + if (str_len > SIZE_MAX - 1 - sum) { + va_end(ap); + return NULL; + } + sum += str_len; + } + va_end(ap); + + // allocate memory + char *res_str = malloc(sum + 1); + if (!res_str) { + return NULL; + } + + // copy strings + sum = 0; + va_start(ap, num); + for (int i = 0; i < num; i++) { + const char *str = va_arg(ap, const char *); + size_t str_len = strlen(str); + memcpy(res_str + sum, str, str_len); + sum += str_len; + } + va_end(ap); + + // terminate + res_str[sum] = '\0'; + + return res_str; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/cstring.h b/service/src/main/jni/badvpn/misc/cstring.h new file mode 100644 index 0000000..bd8de75 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/cstring.h @@ -0,0 +1,347 @@ +/** + * @file cstring.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_COMPOSED_STRING_H +#define BADVPN_COMPOSED_STRING_H + +#include +#include +#include + +#include +#include + +struct b_cstring_s; + +/** + * Callback function which is called by {@link b_cstring_get} to access the underlying resource. + * \a cstr points to the cstring being accessed, and the callback can use the userN members to + * retrieve any state information. + * \a offset is the offset from the beginning of the string; offset < cstr->length. + * This callback must set *\a out_length and return a pointer, representing a continuous + * region of the string that starts at the byte at index \a offset. Returning a region that + * spans past the end of the string is allowed. + */ +typedef const char * (*b_cstring_func) (const struct b_cstring_s *cstr, size_t offset, size_t *out_length); + +/** + * An abstract string which is not necessarily continuous. Given a cstring, its length + * can be determined by reading the 'length' member, and its data can be read using + * {@link b_cstring_get} (which internally invokes the {@link b_cstring_func} callback). + */ +typedef struct b_cstring_s { + size_t length; + b_cstring_func func; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user1; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user2; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user3; +} b_cstring; + +/** + * Makes a cstring pointing to a buffer. + * \a data may be NULL if \a length is 0. + */ +static b_cstring b_cstring_make_buf (const char *data, size_t length); + +/** + * Makes a cstring which represents an empty string. + */ +static b_cstring b_cstring_make_empty (void); + +/** + * Retrieves a pointer to a continuous region of the string. + * \a offset specifies the starting offset of the region to retrieve, and must be < cstr.length. + * \a maxlen specifies the maximum length of the returned region, and must be > 0. + * The length of the region will be stored in *\a out_chunk_len, and it will always be > 0. + * It is possible that the returned region spans past the end of the string, unless limited + * by \a maxlen. The pointer to the region will be returned; it will point to the byte + * at offset exactly \a offset into the string. + */ +static const char * b_cstring_get (b_cstring cstr, size_t offset, size_t maxlen, size_t *out_chunk_len); + +/** + * Retrieves the byte in the string at position \a pos. + */ +static char b_cstring_at (b_cstring cstr, size_t pos); + +/** + * Asserts that the range given by \a offset and \a length is valid for the string. + */ +static void b_cstring_assert_range (b_cstring cstr, size_t offset, size_t length); + +/** + * Copies a range to an external buffer. + */ +static void b_cstring_copy_to_buf (b_cstring cstr, size_t offset, size_t length, char *dest); + +/** + * Performs a memcmp-like operation on the given ranges of two cstrings. + */ +static int b_cstring_memcmp (b_cstring cstr1, b_cstring cstr2, size_t offset1, size_t offset2, size_t length); + +/** + * Determines if a range within a string is equal to the bytes in an external buffer. + */ +static int b_cstring_equals_buffer (b_cstring cstr, size_t offset, size_t length, const char *data); + +/** + * Determines if a range within a string contains the byte \a ch. + * Returns 1 if it does, and 0 if it does not. If it does contain it, and \a out_pos is not + * NULL, *\a out_pos is set to the index of the first matching byte in the range, relative + * to the beginning of the range \a offset. + */ +static int b_cstring_memchr (b_cstring cstr, size_t offset, size_t length, char ch, size_t *out_pos); + +/** + * Allocates a buffer for a range and copies it. The buffer is allocated using {@link BAlloc}. + * An extra null byte will be appended. On failure, returns NULL. + */ +static char * b_cstring_strdup (b_cstring cstr, size_t offset, size_t length); + +/** + * Macro which iterates the continuous regions of a range within a cstring. + * For reach region, the statements in \a body are executed, in order. + * \a cstr is the string to be iterated. + * \a offset and \a length specify the range of the string to iterate; they must + * refer to a valid range for the string. + * \a rel_pos_var, \a chunk_data_var and \a chunk_length_var specify names of variables + * which will be available in \a body. + * \a rel_pos_var will hold the offset (size_t) of the current continuous region, relative + * to \a offset. + * \a chunk_data_var will hold a pointer (const char *) to the beginning of the region, and + * \a chunk_length_var will hold its length (size_t). + * + * Note: \a cstr, \a offset and \a length may be evaluated multiple times, or not at all. + * Note: do not use 'continue' or 'break' from inside the body, their behavior depends + * on the internal implementation of this macro. + * + * See the implementation of {@link b_cstring_copy_to_buf} for a usage example. + */ +#define B_CSTRING_LOOP_RANGE(cstr, offset, length, rel_pos_var, chunk_data_var, chunk_length_var, body) \ +{ \ + size_t rel_pos_var = 0; \ + while (rel_pos_var < (length)) { \ + size_t chunk_length_var; \ + const char *chunk_data_var = b_cstring_get((cstr), (offset) + rel_pos_var, (length) - rel_pos_var, &chunk_length_var); \ + { body } \ + rel_pos_var += chunk_length_var; \ + } \ +} + +/** + * Like {@link B_CSTRING_LOOP_RANGE}, but iterates the entire string, + * i.e. offset==0 and length==cstr.length. + */ +#define B_CSTRING_LOOP(cstr, rel_pos_var, chunk_data_var, chunk_length_var, body) B_CSTRING_LOOP_RANGE(cstr, 0, (cstr).length, rel_pos_var, chunk_data_var, chunk_length_var, body) + +/** + * Macro which iterates the characters of a range within a cstring. + * For each character, the statements in \a body are executed, in order. + * \a cstr is the string to be iterated. + * \a offset and \a length specify the range of the string to iterate; they must + * refer to a valid range for the string. + * \a char_rel_pos_var and \a char_var specify names of variables which will be + * available in \a body. + * \a char_rel_pos_var will hold the position (size_t) of the current character + * relative to \a offset. + * \a char_var will hold the current character (char). + * + * Note: \a cstr, \a offset and \a length may be evaluated multiple times, or not at all. + * Note: do not use 'continue' or 'break' from inside the body, their behavior depends + * on the internal implementation of this macro. + */ +#define B_CSTRING_LOOP_CHARS_RANGE(cstr, offset, length, char_rel_pos_var, char_var, body) \ +B_CSTRING_LOOP_RANGE(cstr, offset, length, b_cstring_loop_chars_pos, b_cstring_loop_chars_chunk_data, b_cstring_loop_chars_chunk_length, { \ + for (size_t b_cstring_loop_chars_chunk_pos = 0; b_cstring_loop_chars_chunk_pos < b_cstring_loop_chars_chunk_length; b_cstring_loop_chars_chunk_pos++) { \ + char char_rel_pos_var = b_cstring_loop_chars_pos + b_cstring_loop_chars_chunk_pos; \ + B_USE(char_rel_pos_var) \ + char char_var = b_cstring_loop_chars_chunk_data[b_cstring_loop_chars_chunk_pos]; \ + { body } \ + } \ +}) + +/** + * Like {@link B_CSTRING_LOOP_CHARS_RANGE}, but iterates the entire string, + * i.e. offset==0 and length==cstr.length. + */ +#define B_CSTRING_LOOP_CHARS(cstr, char_rel_pos_var, char_var, body) B_CSTRING_LOOP_CHARS_RANGE(cstr, 0, (cstr).length, char_rel_pos_var, char_var, body) + +static const char * b_cstring__buf_func (const b_cstring *cstr, size_t offset, size_t *out_length) +{ + ASSERT(offset < cstr->length) + ASSERT(out_length) + ASSERT(cstr->func == b_cstring__buf_func) + ASSERT(cstr->user1.ptr) + + *out_length = cstr->length - offset; + return (const char *)cstr->user1.ptr + offset; +} + +static b_cstring b_cstring_make_buf (const char *data, size_t length) +{ + ASSERT(length == 0 || data) + + b_cstring cstr; + cstr.length = length; + cstr.func = b_cstring__buf_func; + cstr.user1.ptr = (void *)data; + return cstr; +} + +static b_cstring b_cstring_make_empty (void) +{ + b_cstring cstr; + cstr.length = 0; + cstr.func = NULL; + return cstr; +} + +static const char * b_cstring_get (b_cstring cstr, size_t offset, size_t maxlen, size_t *out_chunk_len) +{ + ASSERT(offset < cstr.length) + ASSERT(maxlen > 0) + ASSERT(out_chunk_len) + ASSERT(cstr.func) + + const char *data = cstr.func(&cstr, offset, out_chunk_len); + ASSERT(data) + ASSERT(*out_chunk_len > 0) + + if (*out_chunk_len > maxlen) { + *out_chunk_len = maxlen; + } + + return data; +} + +static char b_cstring_at (b_cstring cstr, size_t pos) +{ + ASSERT(pos < cstr.length) + ASSERT(cstr.func) + + size_t chunk_len; + const char *data = cstr.func(&cstr, pos, &chunk_len); + ASSERT(data) + ASSERT(chunk_len > 0) + + return *data; +} + +static void b_cstring_assert_range (b_cstring cstr, size_t offset, size_t length) +{ + ASSERT(offset <= cstr.length) + ASSERT(length <= cstr.length - offset) +} + +static void b_cstring_copy_to_buf (b_cstring cstr, size_t offset, size_t length, char *dest) +{ + b_cstring_assert_range(cstr, offset, length); + ASSERT(length == 0 || dest) + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + memcpy(dest + pos, chunk_data, chunk_length); + }) +} + +static int b_cstring_memcmp (b_cstring cstr1, b_cstring cstr2, size_t offset1, size_t offset2, size_t length) +{ + b_cstring_assert_range(cstr1, offset1, length); + b_cstring_assert_range(cstr2, offset2, length); + + B_CSTRING_LOOP_RANGE(cstr1, offset1, length, pos1, chunk_data1, chunk_len1, { + B_CSTRING_LOOP_RANGE(cstr2, offset2 + pos1, chunk_len1, pos2, chunk_data2, chunk_len2, { + int cmp = memcmp(chunk_data1 + pos2, chunk_data2, chunk_len2); + if (cmp) { + return cmp; + } + }) + }) + + return 0; +} + +static int b_cstring_equals_buffer (b_cstring cstr, size_t offset, size_t length, const char *data) +{ + b_cstring_assert_range(cstr, offset, length); + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_len, { + if (memcmp(chunk_data, data + pos, chunk_len)) { + return 0; + } + }) + + return 1; +} + +static int b_cstring_memchr (b_cstring cstr, size_t offset, size_t length, char ch, size_t *out_pos) +{ + b_cstring_assert_range(cstr, offset, length); + + B_CSTRING_LOOP_CHARS_RANGE(cstr, offset, length, cur_ch_pos, cur_ch, { + if (cur_ch == ch) { + if (out_pos) { + *out_pos = cur_ch_pos; + } + return 1; + } + }) + + return 0; +} + +static char * b_cstring_strdup (b_cstring cstr, size_t offset, size_t length) +{ + b_cstring_assert_range(cstr, offset, length); + + if (length == SIZE_MAX) { + return NULL; + } + + char *buf = (char *)BAlloc(length + 1); + if (buf) { + b_cstring_copy_to_buf(cstr, offset, length, buf); + buf[length] = '\0'; + } + + return buf; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/dead.h b/service/src/main/jni/badvpn/misc/dead.h new file mode 100644 index 0000000..7f57421 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/dead.h @@ -0,0 +1,134 @@ +/** + * @file dead.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dead mechanism definitions. + * + * The dead mechanism is a way for a piece of code to detect whether some + * specific event has occured during some operation (usually during calling + * a user-provided handler function), without requiring access to memory + * that might no longer be available because of the event. + * + * It works somehow like that: + * + * First a dead variable ({@link dead_t}) is allocated somewhere, and + * initialized with {@link DEAD_INIT}, e.g.: + * DEAD_INIT(dead); + * + * When the event that needs to be caught occurs, {@link DEAD_KILL} is + * called, e.g.: + * DEAD_KILL(dead); + * The memory used by the dead variable is no longer needed after + * that. + * + * If a piece of code needs to know whether the event occured during some + * operation (but it must not have occured before!), it puts {@link DEAD_ENTER}} + * in front of the operation, and does {@link DEAD_LEAVE} at the end. If + * {@link DEAD_LEAVE} returned nonzero, the event occured, otherwise it did + * not. Example: + * DEAD_ENTER(dead) + * HandlerFunction(); + * if (DEAD_LEAVE(dead)) { + * (event occured) + * } + * + * If is is needed to check for the event more than once in a single block, + * {@link DEAD_DECLARE} should be put somewhere before, and {@link DEAD_ENTER2} + * should be used instead of {@link DEAD_ENTER}. + * + * If it is needed to check for multiple events (dead variables) at the same + * time, DEAD_*_N macros should be used instead, specifying different + * identiers as the first argument for different dead variables. + */ + +#ifndef BADVPN_MISC_DEAD_H +#define BADVPN_MISC_DEAD_H + +#include + +/** + * Dead variable. + */ +typedef int *dead_t; + +/** + * Initializes a dead variable. + */ +#define DEAD_INIT(ptr) { ptr = NULL; } + +/** + * Kills the dead variable, + */ +#define DEAD_KILL(ptr) { if (ptr) *(ptr) = 1; } + +/** + * Kills the dead variable with the given value, or does nothing + * if the value is 0. The value will seen by {@link DEAD_KILLED}. + */ +#define DEAD_KILL_WITH(ptr, val) { if (ptr) *(ptr) = (val); } + +/** + * Declares dead catching variables. + */ +#define DEAD_DECLARE int badvpn__dead; dead_t badvpn__prev_ptr; + +/** + * Enters a dead catching using already declared dead catching variables. + * The dead variable must have been initialized with {@link DEAD_INIT}, + * and {@link DEAD_KILL} must not have been called yet. + * {@link DEAD_LEAVE2} must be called before the current scope is left. + */ +#define DEAD_ENTER2(ptr) { badvpn__dead = 0; badvpn__prev_ptr = ptr; ptr = &badvpn__dead; } + +/** + * Declares dead catching variables and enters a dead catching. + * The dead variable must have been initialized with {@link DEAD_INIT}, + * and {@link DEAD_KILL} must not have been called yet. + * {@link DEAD_LEAVE2} must be called before the current scope is left. + */ +#define DEAD_ENTER(ptr) DEAD_DECLARE DEAD_ENTER2(ptr) + +/** + * Leaves a dead catching. + */ +#define DEAD_LEAVE2(ptr) { if (!badvpn__dead) ptr = badvpn__prev_ptr; if (badvpn__prev_ptr) *badvpn__prev_ptr = badvpn__dead; } + +/** + * Returns 1 if {@link DEAD_KILL} was called for the dead variable, 0 otherwise. + * Must be called after entering a dead catching. + */ +#define DEAD_KILLED (badvpn__dead) + +#define DEAD_DECLARE_N(n) int badvpn__dead##n; dead_t badvpn__prev_ptr##n; +#define DEAD_ENTER2_N(n, ptr) { badvpn__dead##n = 0; badvpn__prev_ptr##n = ptr; ptr = &badvpn__dead##n;} +#define DEAD_ENTER_N(n, ptr) DEAD_DECLARE_N(n) DEAD_ENTER2_N(n, ptr) +#define DEAD_LEAVE2_N(n, ptr) { if (!badvpn__dead##n) ptr = badvpn__prev_ptr##n; if (badvpn__prev_ptr##n) *badvpn__prev_ptr##n = badvpn__dead##n; } +#define DEAD_KILLED_N(n) (badvpn__dead##n) + +#endif diff --git a/service/src/main/jni/badvpn/misc/debug.h b/service/src/main/jni/badvpn/misc/debug.h new file mode 100644 index 0000000..759a2fe --- /dev/null +++ b/service/src/main/jni/badvpn/misc/debug.h @@ -0,0 +1,142 @@ +/** + * @file debug.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Debugging macros. + */ + +/** + * @def DEBUG + * + * Macro for printing debugging text. Use the same way as printf, + * but without a newline. + * Prepends "function_name: " and appends a newline. + */ + +/** + * @def ASSERT_FORCE + * + * Macro for forced assertions. + * Evaluates the argument and terminates the program abnormally + * if the result is false. + */ + +/** + * @def ASSERT + * + * Macro for assertions. + * The argument may or may not be evaluated. + * If the argument is evaluated, it must not evaluate to false. + */ + +/** + * @def ASSERT_EXECUTE + * + * Macro for always-evaluated assertions. + * The argument is evaluated. + * The argument must not evaluate to false. + */ + +/** + * @def DEBUG_ZERO_MEMORY + * + * If debugging is enabled, zeroes the given memory region. + * First argument is pointer to the memory region, second is + * number of bytes. + */ + +/** + * @def WARN_UNUSED + * + * Tells the compiler that the result of a function should not be unused. + * Insert at the end of the declaration of a function before the semicolon. + */ + +/** + * @def B_USE + * + * This can be used to suppress warnings about unused variables. It can + * be applied to a variable or any expression. It does not evaluate the + * expression. + */ + +#ifndef BADVPN_MISC_DEBUG_H +#define BADVPN_MISC_DEBUG_H + +#include +#include +#include +#include +#include + +#define DEBUG(...) \ + { \ + fprintf(stderr, "%s: ", __FUNCTION__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } + +#define ASSERT_FORCE(e) \ + { \ + if (!(e)) { \ + fprintf(stderr, "%s:%d Assertion failed\n", __FILE__, __LINE__); \ + abort(); \ + } \ + } + +#ifdef NDEBUG + #define DEBUG_ZERO_MEMORY(buf, len) {} + #define ASSERT(e) {} + #define ASSERT_EXECUTE(e) { (void)(e); } +#else + #define DEBUG_ZERO_MEMORY(buf, len) { memset((buf), 0, (len)); } + #ifdef BADVPN_USE_C_ASSERT + #define ASSERT(e) { assert(e); } + #define ASSERT_EXECUTE(e) \ + { \ + int _assert_res = !!(e); \ + assert(_assert_res); \ + } + #else + #define ASSERT(e) ASSERT_FORCE(e) + #define ASSERT_EXECUTE(e) ASSERT_FORCE(e) + #endif +#endif + +#ifdef __GNUC__ + #define WARN_UNUSED __attribute__((warn_unused_result)) +#else + #define WARN_UNUSED +#endif + +#define B_USE(expr) (void)(sizeof((expr))); + +#define B_ASSERT_USE(expr) { ASSERT(expr) B_USE(expr) } + +#endif diff --git a/service/src/main/jni/badvpn/misc/debugcounter.h b/service/src/main/jni/badvpn/misc/debugcounter.h new file mode 100644 index 0000000..a8a54a1 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/debugcounter.h @@ -0,0 +1,118 @@ +/** + * @file debugcounter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Counter for detecting leaks. + */ + +#ifndef BADVPN_MISC_DEBUGCOUNTER_H +#define BADVPN_MISC_DEBUGCOUNTER_H + +#include + +#include + +/** + * Counter for detecting leaks. + */ +typedef struct { +#ifndef NDEBUG + int32_t c; +#endif +} DebugCounter; + +#ifndef NDEBUG +#define DEBUGCOUNTER_STATIC { 0 } +#else +#define DEBUGCOUNTER_STATIC {} +#endif + +/** + * Initializes the object. + * The object is initialized with counter value zero. + * + * @param obj the object + */ +static void DebugCounter_Init (DebugCounter *obj) +{ +#ifndef NDEBUG + obj->c = 0; +#endif +} + +/** + * Frees the object. + * This does not have to be called when the counter is no longer needed. + * The counter value must be zero. + * + * @param obj the object + */ +static void DebugCounter_Free (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c == 0 || obj->c == INT32_MAX) +#endif +} + +/** + * Increments the counter. + * Increments the counter value by one. + * + * @param obj the object + */ +static void DebugCounter_Increment (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c >= 0) + + if (obj->c != INT32_MAX) { + obj->c++; + } +#endif +} + +/** + * Decrements the counter. + * The counter value must be >0. + * Decrements the counter value by one. + * + * @param obj the object + */ +static void DebugCounter_Decrement (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c > 0) + + if (obj->c != INT32_MAX) { + obj->c--; + } +#endif +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/debugerror.h b/service/src/main/jni/badvpn/misc/debugerror.h new file mode 100644 index 0000000..182afd7 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/debugerror.h @@ -0,0 +1,90 @@ +/** + * @file debugerror.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Mechanism for ensuring an object is destroyed from inside an error handler + * or its jobs. + */ + +#ifndef BADVPN_MISC_DEBUGERROR_H +#define BADVPN_MISC_DEBUGERROR_H + +#include +#include + +#ifndef NDEBUG + #define DEBUGERROR(de, call) \ + { \ + ASSERT(!BPending_IsSet(&(de)->job)) \ + BPending_Set(&(de)->job); \ + (call); \ + } +#else + #define DEBUGERROR(de, call) { (call); } +#endif + +typedef struct { + #ifndef NDEBUG + BPending job; + #endif +} DebugError; + +static void DebugError_Init (DebugError *o, BPendingGroup *pg); +static void DebugError_Free (DebugError *o); +static void DebugError_AssertNoError (DebugError *o); + +#ifndef NDEBUG +static void _DebugError_job_handler (DebugError *o) +{ + ASSERT(0); +} +#endif + +void DebugError_Init (DebugError *o, BPendingGroup *pg) +{ + #ifndef NDEBUG + BPending_Init(&o->job, pg, (BPending_handler)_DebugError_job_handler, o); + #endif +} + +void DebugError_Free (DebugError *o) +{ + #ifndef NDEBUG + BPending_Free(&o->job); + #endif +} + +void DebugError_AssertNoError (DebugError *o) +{ + #ifndef NDEBUG + ASSERT(!BPending_IsSet(&o->job)) + #endif +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/find_char.h b/service/src/main/jni/badvpn/misc/find_char.h new file mode 100644 index 0000000..9277ac6 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/find_char.h @@ -0,0 +1,58 @@ +/** + * @file find_char.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_FIND_CHAR_H +#define BADVPN_FIND_CHAR_H + +#include + +#include + +/** + * Finds the first character 'c' in the string represented by 'str' and 'len'. + * If found, returns 1 and writes the position to *out_pos (if out_pos!=NULL). + * If not found, returns 0 and does not modify *out_pos. + */ +static int b_find_char_bin (const char *str, size_t len, char c, size_t *out_pos) +{ + ASSERT(str) + + for (size_t i = 0; i < len; i++) { + if (str[i] == c) { + if (out_pos) { + *out_pos = i; + } + return 1; + } + } + + return 0; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/ipaddr6.h b/service/src/main/jni/badvpn/misc/ipaddr6.h new file mode 100644 index 0000000..dc48fa4 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/ipaddr6.h @@ -0,0 +1,400 @@ +/** + * @file ipaddr6.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv6 address parsing functions. + */ + +#ifndef BADVPN_MISC_IPADDR6_H +#define BADVPN_MISC_IPADDR6_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct ipv6_addr { + uint8_t bytes[16]; +}; + +struct ipv6_ifaddr { + struct ipv6_addr addr; + int prefix; +}; + +static int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr); +static int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr); +static int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num); +static int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num); +static int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out); +static int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out); +static int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out); +static void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask); +static int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix); +static int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix); + +#define IPADDR6_PRINT_MAX 44 + +static void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf); +static void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf); + +int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr) +{ + int num_blocks = 0; + int compress_pos = -1; + uint16_t block = 0; + int empty = 1; + + size_t i = 0; + + while (i < name_len) { + if (name[i] == '.') { + goto ipv4_ending; + } else if (name[i] == ':') { + int is_double = (i + 1 < name_len && name[i + 1] == ':'); + + if (i > 0) { + if (empty || num_blocks == 7) { + return 0; + } + out_addr->bytes[2 * num_blocks + 0] = block >> 8; + out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; + num_blocks++; + block = 0; + empty = 1; + } + else if (!is_double) { + return 0; + } + + if (is_double) { + if (compress_pos != -1) { + return 0; + } + compress_pos = num_blocks; + } + + i += 1 + is_double; + } else { + int digit = decode_hex_digit(name[i]); + if (digit < 0) { + return 0; + } + if (block > UINT16_MAX / 16) { + return 0; + } + block *= 16; + if (digit > UINT16_MAX - block) { + return 0; + } + block += digit; + empty = 0; + i += 1; + } + } + + if (!empty) { + out_addr->bytes[2 * num_blocks + 0] = block >> 8; + out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; + num_blocks++; + } + else if (num_blocks != compress_pos) { + return 0; + } + +ipv4_done: + if (compress_pos == -1) { + if (num_blocks != 8) { + return 0; + } + compress_pos = 0; + } + + int num_rear = num_blocks - compress_pos; + memmove(out_addr->bytes + 2 * (8 - num_rear), out_addr->bytes + 2 * compress_pos, 2 * num_rear); + memset(out_addr->bytes + 2 * compress_pos, 0, 2 * (8 - num_rear - compress_pos)); + + return 1; + +ipv4_ending: + if (empty || (num_blocks == 0 && compress_pos == -1)) { + return 0; + } + + while (name[i - 1] != ':') { + i--; + } + + uint8_t bytes[4]; + int cur_byte = 0; + uint8_t byte = 0; + empty = 1; + + while (i < name_len) { + if (name[i] == '.') { + if (empty || cur_byte == 3) { + return 0; + } + bytes[cur_byte] = byte; + cur_byte++; + byte = 0; + empty = 1; + } else { + if (!empty && byte == 0) { + return 0; + } + int digit = decode_decimal_digit(name[i]); + if (digit < 0) { + return 0; + } + if (byte > UINT8_MAX / 10) { + return 0; + } + byte *= 10; + if (digit > UINT8_MAX - byte) { + return 0; + } + byte += digit; + empty = 0; + } + i++; + } + + if (cur_byte != 3 || empty) { + return 0; + } + bytes[cur_byte] = byte; + + if (8 - num_blocks < 2) { + return 0; + } + memcpy(out_addr->bytes + 2 * num_blocks, bytes, 4); + num_blocks += 2; + + goto ipv4_done; +} + +int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr) +{ + return ipaddr6_parse_ipv6_addr_bin(name, strlen(name), out_addr); +} + +int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num) +{ + uintmax_t d; + if (!parse_unsigned_integer_bin(str, str_len, &d)) { + return 0; + } + if (d > 128) { + return 0; + } + + *out_num = d; + return 1; +} + +int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num) +{ + return ipaddr6_parse_ipv6_prefix_bin(str, strlen(str), out_num); +} + +int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out) +{ + size_t slash_pos; + if (!b_find_char_bin(str, str_len, '/', &slash_pos)) { + return 0; + } + + return (ipaddr6_parse_ipv6_addr_bin(str, slash_pos, &out->addr) && + ipaddr6_parse_ipv6_prefix_bin(str + slash_pos + 1, str_len - slash_pos - 1, &out->prefix)); +} + +int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out) +{ + return ipaddr6_parse_ipv6_ifaddr_bin(str, strlen(str), out); +} + +int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out) +{ + int prefix; + if (!ipaddr6_ipv6_prefix_from_mask(mask, &prefix)) { + return 0; + } + + out->addr = addr; + out->prefix = prefix; + return 1; +} + +void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask) +{ + ASSERT(prefix >= 0) + ASSERT(prefix <= 128) + + int quot = prefix / 8; + int rem = prefix % 8; + + if (quot > 0) { + memset(out_mask->bytes, UINT8_MAX, quot); + } + if (16 - quot > 0) { + memset(out_mask->bytes + quot, 0, 16 - quot); + } + + for (int i = 0; i < rem; i++) { + out_mask->bytes[quot] |= (uint8_t)1 << (8 - i - 1); + } +} + +int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix) +{ + int prefix = 0; + int i = 0; + + while (i < 16 && mask.bytes[i] == UINT8_MAX) { + prefix += 8; + i++; + } + + if (i < 16) { + uint8_t t = 0; + int j; + for (j = 0; j <= 8; j++) { + if (mask.bytes[i] == t) { + break; + } + if (j < 8) { + t |= ((uint8_t)1 << (8 - j - 1)); + } + } + if (!(j <= 8)) { + return 0; + } + + prefix += j; + i++; + + while (i < 16) { + if (mask.bytes[i] != 0) { + return 0; + } + i++; + } + } + + *out_prefix = prefix; + return 1; +} + +int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix) +{ + ASSERT(netprefix >= 0) + ASSERT(netprefix <= 128) + + int quot = netprefix / 8; + int rem = netprefix % 8; + + if (memcmp(addr1.bytes, addr2.bytes, quot)) { + return 0; + } + + if (rem == 0) { + return 1; + } + + uint8_t t = 0; + for (int i = 0; i < rem; i++) { + t |= (uint8_t)1 << (8 - i - 1); + } + + return ((addr1.bytes[quot] & t) == (addr2.bytes[quot] & t)); +} + +void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf) +{ + int largest_start = 0; + int largest_len = 0; + int current_start = 0; + int current_len = 0; + + for (int i = 0; i < 8; i++) { + if (addr.bytes[2 * i] == 0 && addr.bytes[2 * i + 1] == 0) { + current_len++; + if (current_len > largest_len) { + largest_start = current_start; + largest_len = current_len; + } + } else { + current_start = i + 1; + current_len = 0; + } + } + + if (largest_len > 1) { + for (int i = 0; i < largest_start; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, "%"PRIx16":", block); + } + if (largest_start == 0) { + out_buf += sprintf(out_buf, ":"); + } + + for (int i = largest_start + largest_len; i < 8; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, ":%"PRIx16, block); + } + if (largest_start + largest_len == 8) { + out_buf += sprintf(out_buf, ":"); + } + } else { + const char *prefix = ""; + for (int i = 0; i < 8; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, "%s%"PRIx16, prefix, block); + prefix = ":"; + } + } +} + +void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf) +{ + ASSERT(addr.prefix >= 0) + ASSERT(addr.prefix <= 128) + + ipaddr6_print_addr(addr.addr, out_buf); + sprintf(out_buf + strlen(out_buf), "/%d", addr.prefix); +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/ipv4_proto.h b/service/src/main/jni/badvpn/misc/ipv4_proto.h new file mode 100644 index 0000000..fea7260 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/ipv4_proto.h @@ -0,0 +1,145 @@ +/** + * @file ipv4_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the IPv4 protocol. + */ + +#ifndef BADVPN_MISC_IPV4_PROTO_H +#define BADVPN_MISC_IPV4_PROTO_H + +#include +#include + +#include +#include +#include +#include + +#define IPV4_PROTOCOL_IGMP 2 +#define IPV4_PROTOCOL_UDP 17 + +B_START_PACKED +struct ipv4_header { + uint8_t version4_ihl4; + uint8_t ds; + uint16_t total_length; + // + uint16_t identification; + uint16_t flags3_fragmentoffset13; + // + uint8_t ttl; + uint8_t protocol; + uint16_t checksum; + // + uint32_t source_address; + // + uint32_t destination_address; +} B_PACKED; +B_END_PACKED + +#define IPV4_GET_VERSION(_header) (((_header).version4_ihl4&0xF0)>>4) +#define IPV4_GET_IHL(_header) (((_header).version4_ihl4&0x0F)>>0) + +#define IPV4_MAKE_VERSION_IHL(size) (((size)/4) + (4 << 4)) + +static uint16_t ipv4_checksum (const struct ipv4_header *header, const char *extra, uint16_t extra_len) +{ + ASSERT(extra_len % 2 == 0) + ASSERT(extra_len == 0 || extra) + + uint32_t t = 0; + + for (uint16_t i = 0; i < sizeof(*header) / 2; i++) { + t += badvpn_read_be16((const char *)header + 2 * i); + } + + for (uint16_t i = 0; i < extra_len / 2; i++) { + t += badvpn_read_be16((const char *)extra + 2 * i); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + return hton16(~t); +} + +static int ipv4_check (uint8_t *data, int data_len, struct ipv4_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // check base header + if (data_len < sizeof(struct ipv4_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + + // check version + if (IPV4_GET_VERSION(*out_header) != 4) { + return 0; + } + + // check options + uint16_t header_len = IPV4_GET_IHL(*out_header) * 4; + if (header_len < sizeof(struct ipv4_header)) { + return 0; + } + if (header_len > data_len) { + return 0; + } + + // check total length + uint16_t total_length = ntoh16(out_header->total_length); + if (total_length < header_len) { + return 0; + } + if (total_length > data_len) { + return 0; + } + + // check checksum + uint16_t checksum_in_packet = out_header->checksum; + out_header->checksum = hton16(0); + uint16_t checksum_computed = ipv4_checksum(out_header, (char *)data + sizeof(*out_header), header_len - sizeof(*out_header)); + out_header->checksum = checksum_in_packet; + if (checksum_in_packet != checksum_computed) { + return 0; + } + + *out_payload = data + header_len; + *out_payload_len = total_length - header_len; + + return 1; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/ipv6_proto.h b/service/src/main/jni/badvpn/misc/ipv6_proto.h new file mode 100644 index 0000000..b255541 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/ipv6_proto.h @@ -0,0 +1,86 @@ +/** + * @file ipv6_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_IPV6_PROTO_H +#define BADVPN_IPV6_PROTO_H + +#include +#include + +#include +#include +#include + +#define IPV6_NEXT_IGMP 2 +#define IPV6_NEXT_UDP 17 + +B_START_PACKED +struct ipv6_header { + uint8_t version4_tc4; + uint8_t tc4_fl4; + uint16_t fl; + uint16_t payload_length; + uint8_t next_header; + uint8_t hop_limit; + uint8_t source_address[16]; + uint8_t destination_address[16]; +} B_PACKED; +B_END_PACKED + +static int ipv6_check (uint8_t *data, int data_len, struct ipv6_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // check base header + if (data_len < sizeof(struct ipv6_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + + // check version + if ((ntoh8(out_header->version4_tc4) >> 4) != 6) { + return 0; + } + + // check payload length + uint16_t payload_length = ntoh16(out_header->payload_length); + if (payload_length > data_len - sizeof(struct ipv6_header)) { + return 0; + } + + *out_payload = data + sizeof(struct ipv6_header); + *out_payload_len = payload_length; + + return 1; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/loggers_string.h b/service/src/main/jni/badvpn/misc/loggers_string.h new file mode 100644 index 0000000..66986b8 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/loggers_string.h @@ -0,0 +1,43 @@ +/** + * @file loggers_string.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * List of available loggers. + */ + +#ifndef BADVPN_MISC_LOGGERSSTRING_H +#define BADVPN_MISC_LOGGERSSTRING_H + +#ifdef BADVPN_USE_WINAPI +#define LOGGERS_STRING "stdout" +#else +#define LOGGERS_STRING "stdout/syslog" +#endif + +#endif diff --git a/service/src/main/jni/badvpn/misc/loglevel.h b/service/src/main/jni/badvpn/misc/loglevel.h new file mode 100644 index 0000000..6e9f911 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/loglevel.h @@ -0,0 +1,80 @@ +/** + * @file loglevel.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Log level specification parsing function. + */ + +#ifndef BADVPN_MISC_LOGLEVEL_H +#define BADVPN_MISC_LOGLEVEL_H + +#include + +#include + +/** + * Parses the log level string. + * + * @param str log level string. Recognizes none, error, warning, notice, + * info, debug. + * @return 0 for none, one of BLOG_* for some log level, -1 for unrecognized + */ +static int parse_loglevel (char *str); + +int parse_loglevel (char *str) +{ + if (!strcmp(str, "none")) { + return 0; + } + if (!strcmp(str, "error")) { + return BLOG_ERROR; + } + if (!strcmp(str, "warning")) { + return BLOG_WARNING; + } + if (!strcmp(str, "notice")) { + return BLOG_NOTICE; + } + if (!strcmp(str, "info")) { + return BLOG_INFO; + } + if (!strcmp(str, "debug")) { + return BLOG_DEBUG; + } + + char *endptr; + long int res = strtol(str, &endptr, 10); + if (*str && !*endptr && res >= 0 && res <= BLOG_DEBUG) { + return res; + } + + return -1; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/maxalign.h b/service/src/main/jni/badvpn/misc/maxalign.h new file mode 100644 index 0000000..cb1f460 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/maxalign.h @@ -0,0 +1,53 @@ +/** + * @file maxalign.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MAXALIGN_H +#define BADVPN_MAXALIGN_H + +#include +#include + +typedef union { + short a; + long b; + long long c; + double d; + long double e; + void *f; + uint8_t g; + uint16_t h; + uint32_t i; + uint64_t j; + size_t k; + void (*l) (void); +} bmax_align_t; + +#define BMAX_ALIGN (__alignof(bmax_align_t)) + +#endif diff --git a/service/src/main/jni/badvpn/misc/merge.h b/service/src/main/jni/badvpn/misc/merge.h new file mode 100644 index 0000000..5322771 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/merge.h @@ -0,0 +1,36 @@ +/** + * @file merge.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MERGE_H +#define BADVPN_MERGE_H + +#define MERGE_HELPER(x, y) x ## y +#define MERGE(x, y) MERGE_HELPER(x, y) + +#endif diff --git a/service/src/main/jni/badvpn/misc/minmax.h b/service/src/main/jni/badvpn/misc/minmax.h new file mode 100644 index 0000000..ca82055 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/minmax.h @@ -0,0 +1,56 @@ +/** + * @file minmax.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Minimum and maximum macros. + */ + +#ifndef BADVPN_MISC_MINMAX_H +#define BADVPN_MISC_MINMAX_H + +#include +#include + +#define DEFINE_BMINMAX(name, type) \ +static type bmin ## name (type a, type b) { return (a < b ? a : b); } \ +static type bmax ## name (type a, type b) { return (a > b ? a : b); } + +DEFINE_BMINMAX(_size, size_t) +DEFINE_BMINMAX(_int, int) +DEFINE_BMINMAX(_int8, int8_t) +DEFINE_BMINMAX(_int16, int16_t) +DEFINE_BMINMAX(_int32, int32_t) +DEFINE_BMINMAX(_int64, int64_t) +DEFINE_BMINMAX(_uint, unsigned int) +DEFINE_BMINMAX(_uint8, uint8_t) +DEFINE_BMINMAX(_uint16, uint16_t) +DEFINE_BMINMAX(_uint32, uint32_t) +DEFINE_BMINMAX(_uint64, uint64_t) + +#endif diff --git a/service/src/main/jni/badvpn/misc/mswsock.h b/service/src/main/jni/badvpn/misc/mswsock.h new file mode 100644 index 0000000..4f4c38d --- /dev/null +++ b/service/src/main/jni/badvpn/misc/mswsock.h @@ -0,0 +1,229 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include + +#ifndef _MSWSOCK_ +#define _MSWSOCK_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SO_CONNDATA 0x7000 +#define SO_CONNOPT 0x7001 +#define SO_DISCDATA 0x7002 +#define SO_DISCOPT 0x7003 +#define SO_CONNDATALEN 0x7004 +#define SO_CONNOPTLEN 0x7005 +#define SO_DISCDATALEN 0x7006 +#define SO_DISCOPTLEN 0x7007 + +#define SO_OPENTYPE 0x7008 + +#define SO_SYNCHRONOUS_ALERT 0x10 +#define SO_SYNCHRONOUS_NONALERT 0x20 + +#define SO_MAXDG 0x7009 +#define SO_MAXPATHDG 0x700A +#define SO_UPDATE_ACCEPT_CONTEXT 0x700B +#define SO_CONNECT_TIME 0x700C +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 + +#define TCP_BSDURGENT 0x7000 + +#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) +#if (_WIN32_WINNT < 0x0600) && (_WIN32_WINNT >= 0x0501) +#define SIO_SOCKET_CLOSE_NOTIFY _WSAIOW(IOC_VENDOR,13) +#endif /* >= XP && < VISTA */ +#if (_WIN32_WINNT >= 0x0600) +#define SIO_BSP_HANDLE _WSAIOR(IOC_WS2,27) +#define SIO_BSP_HANDLE_SELECT _WSAIOR(IOC_WS2,28) +#define SIO_BSP_HANDLE_POLL _WSAIOR(IOC_WS2,29) + +#define SIO_EXT_SELECT _WSAIORW(IOC_WS2,30) +#define SIO_EXT_POLL _WSAIORW(IOC_WS2,31) +#define SIO_EXT_SENDMSG _WSAIORW(IOC_WS2,32) + +#define SIO_BASE_HANDLE _WSAIOR(IOC_WS2,34) +#endif /* _WIN32_WINNT >= 0x0600 */ + +#ifndef __MSWSOCK_WS1_SHARED + int WINAPI WSARecvEx(SOCKET s,char *buf,int len,int *flags); +#endif /* __MSWSOCK_WS1_SHARED */ + +#define TF_DISCONNECT 0x01 +#define TF_REUSE_SOCKET 0x02 +#define TF_WRITE_BEHIND 0x04 +#define TF_USE_DEFAULT_WORKER 0x00 +#define TF_USE_SYSTEM_THREAD 0x10 +#define TF_USE_KERNEL_APC 0x20 + +#include +#ifndef __MSWSOCK_WS1_SHARED + WINBOOL WINAPI TransmitFile(SOCKET hSocket,HANDLE hFile,DWORD nNumberOfBytesToWrite,DWORD nNumberOfBytesPerSend,LPOVERLAPPED lpOverlapped,LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,DWORD dwReserved); + WINBOOL WINAPI AcceptEx(SOCKET sListenSocket,SOCKET sAcceptSocket,PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,LPDWORD lpdwBytesReceived,LPOVERLAPPED lpOverlapped); + VOID WINAPI GetAcceptExSockaddrs(PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,struct sockaddr **LocalSockaddr,LPINT LocalSockaddrLength,struct sockaddr **RemoteSockaddr,LPINT RemoteSockaddrLength); +#endif /* __MSWSOCK_WS1_SHARED */ + + typedef WINBOOL (WINAPI *LPFN_TRANSMITFILE)(SOCKET hSocket,HANDLE hFile,DWORD nNumberOfBytesToWrite,DWORD nNumberOfBytesPerSend,LPOVERLAPPED lpOverlapped,LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,DWORD dwReserved); + +#define WSAID_TRANSMITFILE {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef WINBOOL (WINAPI *LPFN_ACCEPTEX)(SOCKET sListenSocket,SOCKET sAcceptSocket,PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,LPDWORD lpdwBytesReceived,LPOVERLAPPED lpOverlapped); + +#define WSAID_ACCEPTEX {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef VOID (WINAPI *LPFN_GETACCEPTEXSOCKADDRS)(PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,struct sockaddr **LocalSockaddr,LPINT LocalSockaddrLength,struct sockaddr **RemoteSockaddr,LPINT RemoteSockaddrLength); + +#define WSAID_GETACCEPTEXSOCKADDRS {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef struct _TRANSMIT_PACKETS_ELEMENT { + ULONG dwElFlags; +#define TP_ELEMENT_MEMORY 1 +#define TP_ELEMENT_FILE 2 +#define TP_ELEMENT_EOP 4 + ULONG cLength; + __MINGW_EXTENSION union { + __MINGW_EXTENSION struct { + LARGE_INTEGER nFileOffset; + HANDLE hFile; + }; + PVOID pBuffer; + }; + } TRANSMIT_PACKETS_ELEMENT,*PTRANSMIT_PACKETS_ELEMENT,*LPTRANSMIT_PACKETS_ELEMENT; + +#define TP_DISCONNECT TF_DISCONNECT +#define TP_REUSE_SOCKET TF_REUSE_SOCKET +#define TP_USE_DEFAULT_WORKER TF_USE_DEFAULT_WORKER +#define TP_USE_SYSTEM_THREAD TF_USE_SYSTEM_THREAD +#define TP_USE_KERNEL_APC TF_USE_KERNEL_APC + + typedef WINBOOL (WINAPI *LPFN_TRANSMITPACKETS) (SOCKET hSocket,LPTRANSMIT_PACKETS_ELEMENT lpPacketArray,DWORD nElementCount,DWORD nSendSize,LPOVERLAPPED lpOverlapped,DWORD dwFlags); + +#define WSAID_TRANSMITPACKETS {0xd9689da0,0x1f90,0x11d3,{0x99,0x71,0x00,0xc0,0x4f,0x68,0xc8,0x76}} + + typedef WINBOOL (WINAPI *LPFN_CONNECTEX)(SOCKET s,const struct sockaddr *name,int namelen,PVOID lpSendBuffer,DWORD dwSendDataLength,LPDWORD lpdwBytesSent,LPOVERLAPPED lpOverlapped); + +#define WSAID_CONNECTEX {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} + + typedef WINBOOL (WINAPI *LPFN_DISCONNECTEX)(SOCKET s,LPOVERLAPPED lpOverlapped,DWORD dwFlags,DWORD dwReserved); + +#define WSAID_DISCONNECTEX {0x7fda2e11,0x8630,0x436f,{0xa0,0x31,0xf5,0x36,0xa6,0xee,0xc1,0x57}} + +#define DE_REUSE_SOCKET TF_REUSE_SOCKET + +#define NLA_NAMESPACE_GUID {0x6642243a,0x3ba8,0x4aa6,{0xba,0xa5,0x2e,0xb,0xd7,0x1f,0xdd,0x83}} + +#define NLA_SERVICE_CLASS_GUID {0x37e515,0xb5c9,0x4a43,{0xba,0xda,0x8b,0x48,0xa8,0x7a,0xd2,0x39}} + +#define NLA_ALLUSERS_NETWORK 0x00000001 +#define NLA_FRIENDLY_NAME 0x00000002 + + typedef enum _NLA_BLOB_DATA_TYPE { + NLA_RAW_DATA = 0,NLA_INTERFACE = 1,NLA_802_1X_LOCATION = 2,NLA_CONNECTIVITY = 3,NLA_ICS = 4 + } NLA_BLOB_DATA_TYPE,*PNLA_BLOB_DATA_TYPE; + + typedef enum _NLA_CONNECTIVITY_TYPE { + NLA_NETWORK_AD_HOC = 0,NLA_NETWORK_MANAGED = 1,NLA_NETWORK_UNMANAGED = 2,NLA_NETWORK_UNKNOWN = 3 + } NLA_CONNECTIVITY_TYPE,*PNLA_CONNECTIVITY_TYPE; + + typedef enum _NLA_INTERNET { + NLA_INTERNET_UNKNOWN = 0,NLA_INTERNET_NO = 1,NLA_INTERNET_YES = 2 + } NLA_INTERNET,*PNLA_INTERNET; + + typedef struct _NLA_BLOB { + struct { + NLA_BLOB_DATA_TYPE type; + DWORD dwSize; + DWORD nextOffset; + } header; + union { + CHAR rawData[1]; + struct { + DWORD dwType; + DWORD dwSpeed; + CHAR adapterName[1]; + } interfaceData; + struct { + CHAR information[1]; + } locationData; + struct { + NLA_CONNECTIVITY_TYPE type; + NLA_INTERNET internet; + } connectivity; + struct { + struct { + DWORD speed; + DWORD type; + DWORD state; + WCHAR machineName[256]; + WCHAR sharedAdapterName[256]; + } remote; + } ICS; + } data; + } NLA_BLOB,*PNLA_BLOB,*LPNLA_BLOB; + +#ifdef BADVPN_SHIPPED_MSWSOCK_DECLARE_WSAMSG + typedef struct _WSAMSG { + LPSOCKADDR name; + INT namelen; + LPWSABUF lpBuffers; + DWORD dwBufferCount; + WSABUF Control; + DWORD dwFlags; + } WSAMSG,*PWSAMSG,*LPWSAMSG; +#endif + + typedef struct _WSACMSGHDR { + SIZE_T cmsg_len; + INT cmsg_level; + INT cmsg_type; + } WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; + +#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) +#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) +#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) +#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) +#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) +#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) +#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) + +#define MSG_TRUNC 0x0100 +#define MSG_CTRUNC 0x0200 +#define MSG_BCAST 0x0400 +#define MSG_MCAST 0x0800 + + typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg, + LPDWORD lpdwNumberOfBytesRecvd, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + +#define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}} + +#if(_WIN32_WINNT >= 0x0600) + typedef struct { + LPWSAMSG lpMsg; + DWORD dwFlags; + LPDWORD lpNumberOfBytesSent; + LPWSAOVERLAPPED lpOverlapped; + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine; + } WSASENDMSG, *LPWSASENDMSG; + + typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags, + LPDWORD lpNumberOfBytesSent, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + +#define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}} + +#endif /* (_WIN32_WINNT >= 0x0600) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _MSWSOCK_ */ diff --git a/service/src/main/jni/badvpn/misc/nonblocking.h b/service/src/main/jni/badvpn/misc/nonblocking.h new file mode 100644 index 0000000..fe82584 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/nonblocking.h @@ -0,0 +1,51 @@ +/** + * @file nonblocking.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for enabling non-blocking mode for a file descriptor. + */ + +#ifndef BADVPN_MISC_NONBLOCKING_H +#define BADVPN_MISC_NONBLOCKING_H + +#include +#include + +static int badvpn_set_nonblocking (int fd); + +int badvpn_set_nonblocking (int fd) +{ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + return 0; + } + + return 1; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/offset.h b/service/src/main/jni/badvpn/misc/offset.h new file mode 100644 index 0000000..23b7683 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/offset.h @@ -0,0 +1,51 @@ +/** + * @file offset.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Macros for determining offsets of members in structs. + */ + +#ifndef BADVPN_MISC_OFFSET_H +#define BADVPN_MISC_OFFSET_H + +#include +#include + +/** + * Returns a pointer to a struct, given a pointer to its member. + */ +#define UPPER_OBJECT(_ptr, _object_type, _field_name) ((_object_type *)((char *)(_ptr) - offsetof(_object_type, _field_name))) + +/** + * Returns the offset of one struct member from another. + * Expands to an int. + */ +#define OFFSET_DIFF(_object_type, _field1, _field2) ((int)offsetof(_object_type, _field1) - (int)offsetof(_object_type, _field2)) + +#endif diff --git a/service/src/main/jni/badvpn/misc/open_standard_streams.h b/service/src/main/jni/badvpn/misc/open_standard_streams.h new file mode 100644 index 0000000..7fd6d41 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/open_standard_streams.h @@ -0,0 +1,54 @@ +/** + * @file open_standard_streams.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_OPEN_STANDARD_STREAMS_H +#define BADVPN_OPEN_STANDARD_STREAMS_H + +#ifndef BADVPN_USE_WINAPI +#include +#include +#include +#include +#endif + +static void open_standard_streams (void) +{ +#ifndef BADVPN_USE_WINAPI + int fd; + + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) { + close(fd); + } + } while (fd >= 0 && fd <= 2); +#endif +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/overflow.h b/service/src/main/jni/badvpn/misc/overflow.h new file mode 100644 index 0000000..9fde52a --- /dev/null +++ b/service/src/main/jni/badvpn/misc/overflow.h @@ -0,0 +1,66 @@ +/** + * @file overflow.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Functions for checking for overflow of integer addition. + */ + +#ifndef BADVPN_MISC_OVERFLOW_H +#define BADVPN_MISC_OVERFLOW_H + +#include +#include + +#define DEFINE_UNSIGNED_OVERFLOW(_name, _type, _max) \ +static int add_ ## _name ## _overflows (_type a, _type b) \ +{\ + return (b > _max - a); \ +} + +#define DEFINE_SIGNED_OVERFLOW(_name, _type, _min, _max) \ +static int add_ ## _name ## _overflows (_type a, _type b) \ +{\ + if ((a < 0) ^ (b < 0)) return 0; \ + if (a < 0) return -(a < _min - b); \ + return (a > _max - b); \ +} + +DEFINE_UNSIGNED_OVERFLOW(uint, unsigned int, UINT_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint8, uint8_t, UINT8_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint16, uint16_t, UINT16_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint32, uint32_t, UINT32_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint64, uint64_t, UINT64_MAX) + +DEFINE_SIGNED_OVERFLOW(int, int, INT_MIN, INT_MAX) +DEFINE_SIGNED_OVERFLOW(int8, int8_t, INT8_MIN, INT8_MAX) +DEFINE_SIGNED_OVERFLOW(int16, int16_t, INT16_MIN, INT16_MAX) +DEFINE_SIGNED_OVERFLOW(int32, int32_t, INT32_MIN, INT32_MAX) +DEFINE_SIGNED_OVERFLOW(int64, int64_t, INT64_MIN, INT64_MAX) + +#endif diff --git a/service/src/main/jni/badvpn/misc/packed.h b/service/src/main/jni/badvpn/misc/packed.h new file mode 100644 index 0000000..2175536 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/packed.h @@ -0,0 +1,51 @@ +/** + * @file packed.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Structure packing macros. + */ + +#ifndef BADVPN_PACKED_H +#define BADVPN_PACKED_H + +#ifdef _MSC_VER + +#define B_START_PACKED __pragma(pack(push, 1)) +#define B_END_PACKED __pragma(pack(pop)) +#define B_PACKED + +#else + +#define B_START_PACKED +#define B_END_PACKED +#define B_PACKED __attribute__((packed)) + +#endif + +#endif diff --git a/service/src/main/jni/badvpn/misc/parse_number.h b/service/src/main/jni/badvpn/misc/parse_number.h new file mode 100644 index 0000000..2601ebb --- /dev/null +++ b/service/src/main/jni/badvpn/misc/parse_number.h @@ -0,0 +1,304 @@ +/** + * @file parse_number.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Numeric string parsing. + */ + +#ifndef BADVPN_MISC_PARSE_NUMBER_H +#define BADVPN_MISC_PARSE_NUMBER_H + +#include +#include +#include +#include + +#include +#include + +// public parsing functions +static int decode_decimal_digit (char c); +static int decode_hex_digit (char c); +static int parse_unsigned_integer_bin (const char *str, size_t str_len, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_integer (const char *str, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_integer_cstr (b_cstring cstr, size_t offset, size_t length, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_hex_integer_bin (const char *str, size_t str_len, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_hex_integer (const char *str, uintmax_t *out) WARN_UNUSED; +static int parse_signmag_integer_bin (const char *str, size_t str_len, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; +static int parse_signmag_integer (const char *str, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; +static int parse_signmag_integer_cstr (b_cstring cstr, size_t offset, size_t length, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; + +// public generation functions +static int compute_decimal_repr_size (uintmax_t x); +static void generate_decimal_repr (uintmax_t x, char *out, int repr_size); +static int generate_decimal_repr_string (uintmax_t x, char *out); + +// implementation follows + +// decimal representation of UINTMAX_MAX +static const char parse_number__uintmax_max_str[] = "18446744073709551615"; + +// make sure UINTMAX_MAX is what we think it is +static const char parse_number__uintmax_max_str_assert[(UINTMAX_MAX == UINTMAX_C(18446744073709551615)) ? 1 : -1]; + +static int decode_decimal_digit (char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + } + + return -1; +} + +static int decode_hex_digit (char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'A': case 'a': return 10; + case 'B': case 'b': return 11; + case 'C': case 'c': return 12; + case 'D': case 'd': return 13; + case 'E': case 'e': return 14; + case 'F': case 'f': return 15; + } + + return -1; +} + +static int parse__no_overflow (const char *str, size_t str_len, uintmax_t *out) +{ + uintmax_t n = 0; + + while (str_len > 0) { + if (*str < '0' || *str > '9') { + return 0; + } + + n = 10 * n + (*str - '0'); + + str++; + str_len--; + } + + *out = n; + return 1; +} + +int parse_unsigned_integer_bin (const char *str, size_t str_len, uintmax_t *out) +{ + // we do not allow empty strings + if (str_len == 0) { + return 0; + } + + // remove leading zeros + while (str_len > 0 && *str == '0') { + str++; + str_len--; + } + + // detect overflow + if (str_len > sizeof(parse_number__uintmax_max_str) - 1 || + (str_len == sizeof(parse_number__uintmax_max_str) - 1 && memcmp(str, parse_number__uintmax_max_str, sizeof(parse_number__uintmax_max_str) - 1) > 0)) { + return 0; + } + + // will not overflow (but can still have invalid characters) + return parse__no_overflow(str, str_len, out); +} + +int parse_unsigned_integer (const char *str, uintmax_t *out) +{ + return parse_unsigned_integer_bin(str, strlen(str), out); +} + +int parse_unsigned_integer_cstr (b_cstring cstr, size_t offset, size_t length, uintmax_t *out) +{ + b_cstring_assert_range(cstr, offset, length); + + if (length == 0) { + return 0; + } + + uintmax_t n = 0; + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + for (size_t i = 0; i < chunk_length; i++) { + int digit = decode_decimal_digit(chunk_data[i]); + if (digit < 0) { + return 0; + } + if (n > UINTMAX_MAX / 10) { + return 0; + } + n *= 10; + if (digit > UINTMAX_MAX - n) { + return 0; + } + n += digit; + } + }) + + *out = n; + return 1; +} + +int parse_unsigned_hex_integer_bin (const char *str, size_t str_len, uintmax_t *out) +{ + uintmax_t n = 0; + + if (str_len == 0) { + return 0; + } + + while (str_len > 0) { + int digit = decode_hex_digit(*str); + if (digit < 0) { + return 0; + } + + if (n > UINTMAX_MAX / 16) { + return 0; + } + n *= 16; + + if (digit > UINTMAX_MAX - n) { + return 0; + } + n += digit; + + str++; + str_len--; + } + + *out = n; + return 1; +} + +int parse_unsigned_hex_integer (const char *str, uintmax_t *out) +{ + return parse_unsigned_hex_integer_bin(str, strlen(str), out); +} + +int parse_signmag_integer_bin (const char *str, size_t str_len, int *out_sign, uintmax_t *out_mag) +{ + int sign = 1; + if (str_len > 0 && (str[0] == '+' || str[0] == '-')) { + sign = 1 - 2 * (str[0] == '-'); + str++; + str_len--; + } + + if (!parse_unsigned_integer_bin(str, str_len, out_mag)) { + return 0; + } + + *out_sign = sign; + return 1; +} + +int parse_signmag_integer (const char *str, int *out_sign, uintmax_t *out_mag) +{ + return parse_signmag_integer_bin(str, strlen(str), out_sign, out_mag); +} + +int parse_signmag_integer_cstr (b_cstring cstr, size_t offset, size_t length, int *out_sign, uintmax_t *out_mag) +{ + b_cstring_assert_range(cstr, offset, length); + + int sign = 1; + if (length > 0 && (b_cstring_at(cstr, offset) == '+' || b_cstring_at(cstr, offset) == '-')) { + sign = 1 - 2 * (b_cstring_at(cstr, offset) == '-'); + offset++; + length--; + } + + if (!parse_unsigned_integer_cstr(cstr, offset, length, out_mag)) { + return 0; + } + + *out_sign = sign; + return 1; +} + +int compute_decimal_repr_size (uintmax_t x) +{ + int size = 0; + + do { + size++; + x /= 10; + } while (x > 0); + + return size; +} + +void generate_decimal_repr (uintmax_t x, char *out, int repr_size) +{ + ASSERT(out) + ASSERT(repr_size == compute_decimal_repr_size(x)) + + out += repr_size; + + do { + *(--out) = '0' + (x % 10); + x /= 10; + } while (x > 0); +} + +int generate_decimal_repr_string (uintmax_t x, char *out) +{ + ASSERT(out) + + int repr_size = compute_decimal_repr_size(x); + generate_decimal_repr(x, out, repr_size); + out[repr_size] = '\0'; + + return repr_size; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/print_macros.h b/service/src/main/jni/badvpn/misc/print_macros.h new file mode 100644 index 0000000..a07fdbe --- /dev/null +++ b/service/src/main/jni/badvpn/misc/print_macros.h @@ -0,0 +1,98 @@ +/** + * @file print_macros.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Format macros for printf() for non-standard compilers. + */ + +#ifndef BADVPN_PRINT_MACROS +#define BADVPN_PRINT_MACROS + +#ifdef _MSC_VER + +// size_t +#define PRIsz "Iu" + +// signed exact width (intN_t) +#define PRId8 "d" +#define PRIi8 "i" +#define PRId16 "d" +#define PRIi16 "i" +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRId64 "I64d" +#define PRIi64 "I64i" + +// unsigned exact width (uintN_t) +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIo16 "o" +#define PRIu16 "u" +#define PRIx16 "x" +#define PRIX16 "X" +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" + +// signed maximum width (intmax_t) +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +// unsigned maximum width (uintmax_t) +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +// signed pointer (intptr_t) +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// unsigned pointer (uintptr_t) +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +#else + +#include + +#define PRIsz "zu" + +#endif + +#endif diff --git a/service/src/main/jni/badvpn/misc/read_file.h b/service/src/main/jni/badvpn/misc/read_file.h new file mode 100644 index 0000000..e1862e5 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/read_file.h @@ -0,0 +1,98 @@ +/** + * @file read_file.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for reading a file into memory using stdio. + */ + +#ifndef BADVPN_MISC_READ_FILE_H +#define BADVPN_MISC_READ_FILE_H + +#include +#include +#include +#include + +static int read_file (const char *file, uint8_t **out_data, size_t *out_len) +{ + FILE *f = fopen(file, "r"); + if (!f) { + goto fail0; + } + + size_t buf_len = 0; + size_t buf_size = 128; + + uint8_t *buf = (uint8_t *)malloc(buf_size); + if (!buf) { + goto fail1; + } + + while (1) { + if (buf_len == buf_size) { + if (2 > SIZE_MAX / buf_size) { + goto fail; + } + size_t newsize = 2 * buf_size; + + uint8_t *newbuf = (uint8_t *)realloc(buf, newsize); + if (!newbuf) { + goto fail; + } + + buf = newbuf; + buf_size = newsize; + } + + size_t bytes = fread(buf + buf_len, 1, buf_size - buf_len, f); + if (bytes == 0) { + if (feof(f)) { + break; + } + goto fail; + } + + buf_len += bytes; + } + + fclose(f); + + *out_data = buf; + *out_len = buf_len; + return 1; + +fail: + free(buf); +fail1: + fclose(f); +fail0: + return 0; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/read_write_int.h b/service/src/main/jni/badvpn/misc/read_write_int.h new file mode 100644 index 0000000..bc4ed2c --- /dev/null +++ b/service/src/main/jni/badvpn/misc/read_write_int.h @@ -0,0 +1,181 @@ +/** + * @file read_write_int.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_READ_WRITE_INT_H +#define BADVPN_READ_WRITE_INT_H + +#include + +static uint8_t badvpn_read_le8 (const char *c_ptr); +static uint16_t badvpn_read_le16 (const char *c_ptr); +static uint32_t badvpn_read_le32 (const char *c_ptr); +static uint64_t badvpn_read_le64 (const char *c_ptr); + +static uint8_t badvpn_read_be8 (const char *c_ptr); +static uint16_t badvpn_read_be16 (const char *c_ptr); +static uint32_t badvpn_read_be32 (const char *c_ptr); +static uint64_t badvpn_read_be64 (const char *c_ptr); + +static void badvpn_write_le8 (uint8_t x, char *c_ptr); +static void badvpn_write_le16 (uint16_t x, char *c_ptr); +static void badvpn_write_le32 (uint32_t x, char *c_ptr); +static void badvpn_write_le64 (uint64_t x, char *c_ptr); + +static void badvpn_write_be8 (uint8_t x, char *c_ptr); +static void badvpn_write_be16 (uint16_t x, char *c_ptr); +static void badvpn_write_be32 (uint32_t x, char *c_ptr); +static void badvpn_write_be64 (uint64_t x, char *c_ptr); + +static uint8_t badvpn_read_le8 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint8_t)ptr[0] << 0); +} + +static uint16_t badvpn_read_le16 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint16_t)ptr[1] << 8) | ((uint16_t)ptr[0] << 0); +} + +static uint32_t badvpn_read_le32 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint32_t)ptr[3] << 24) | ((uint32_t)ptr[2] << 16) | + ((uint32_t)ptr[1] << 8) | ((uint32_t)ptr[0] << 0); +} + +static uint64_t badvpn_read_le64 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint64_t)ptr[7] << 56) | ((uint64_t)ptr[6] << 48) | + ((uint64_t)ptr[5] << 40) | ((uint64_t)ptr[4] << 32) | + ((uint64_t)ptr[3] << 24) | ((uint64_t)ptr[2] << 16) | + ((uint64_t)ptr[1] << 8) | ((uint64_t)ptr[0] << 0); +} + +static uint8_t badvpn_read_be8 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint8_t)ptr[0] << 0); +} + +static uint16_t badvpn_read_be16 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint16_t)ptr[0] << 8) | ((uint16_t)ptr[1] << 0); +} + +static uint32_t badvpn_read_be32 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | + ((uint32_t)ptr[2] << 8) | ((uint32_t)ptr[3] << 0); +} + +static uint64_t badvpn_read_be64 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint64_t)ptr[0] << 56) | ((uint64_t)ptr[1] << 48) | + ((uint64_t)ptr[2] << 40) | ((uint64_t)ptr[3] << 32) | + ((uint64_t)ptr[4] << 24) | ((uint64_t)ptr[5] << 16) | + ((uint64_t)ptr[6] << 8) | ((uint64_t)ptr[7] << 0); +} + +static void badvpn_write_le8 (uint8_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 0; +} + +static void badvpn_write_le16 (uint16_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_le32 (uint32_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[3] = x >> 24; + ptr[2] = x >> 16; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_le64 (uint64_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[7] = x >> 56; + ptr[6] = x >> 48; + ptr[5] = x >> 40; + ptr[4] = x >> 32; + ptr[3] = x >> 24; + ptr[2] = x >> 16; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_be8 (uint8_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 0; +} + +static void badvpn_write_be16 (uint16_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 8; + ptr[1] = x >> 0; +} + +static void badvpn_write_be32 (uint32_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 24; + ptr[1] = x >> 16; + ptr[2] = x >> 8; + ptr[3] = x >> 0; +} + +static void badvpn_write_be64 (uint64_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 56; + ptr[1] = x >> 48; + ptr[2] = x >> 40; + ptr[3] = x >> 32; + ptr[4] = x >> 24; + ptr[5] = x >> 16; + ptr[6] = x >> 8; + ptr[7] = x >> 0; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/socks_proto.h b/service/src/main/jni/badvpn/misc/socks_proto.h new file mode 100644 index 0000000..41f5a1f --- /dev/null +++ b/service/src/main/jni/badvpn/misc/socks_proto.h @@ -0,0 +1,118 @@ +/** + * @file socks_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the SOCKS protocol. + */ + +#ifndef BADVPN_MISC_SOCKS_PROTO_H +#define BADVPN_MISC_SOCKS_PROTO_H + +#include + +#include + +#define SOCKS_VERSION 0x05 + +#define SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED 0x00 +#define SOCKS_METHOD_GSSAPI 0x01 +#define SOCKS_METHOD_USERNAME_PASSWORD 0x02 +#define SOCKS_METHOD_NO_ACCEPTABLE_METHODS 0xFF + +#define SOCKS_CMD_CONNECT 0x01 +#define SOCKS_CMD_BIND 0x02 +#define SOCKS_CMD_UDP_ASSOCIATE 0x03 + +#define SOCKS_ATYP_IPV4 0x01 +#define SOCKS_ATYP_DOMAINNAME 0x03 +#define SOCKS_ATYP_IPV6 0x04 + +#define SOCKS_REP_SUCCEEDED 0x00 +#define SOCKS_REP_GENERAL_FAILURE 0x01 +#define SOCKS_REP_CONNECTION_NOT_ALLOWED 0x02 +#define SOCKS_REP_NETWORK_UNREACHABLE 0x03 +#define SOCKS_REP_HOST_UNREACHABLE 0x04 +#define SOCKS_REP_CONNECTION_REFUSED 0x05 +#define SOCKS_REP_TTL_EXPIRED 0x06 +#define SOCKS_REP_COMMAND_NOT_SUPPORTED 0x07 +#define SOCKS_REP_ADDRESS_TYPE_NOT_SUPPORTED 0x08 + +B_START_PACKED +struct socks_client_hello_header { + uint8_t ver; + uint8_t nmethods; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_client_hello_method { + uint8_t method; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_server_hello { + uint8_t ver; + uint8_t method; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_request_header { + uint8_t ver; + uint8_t cmd; + uint8_t rsv; + uint8_t atyp; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_reply_header { + uint8_t ver; + uint8_t rep; + uint8_t rsv; + uint8_t atyp; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_addr_ipv4 { + uint32_t addr; + uint16_t port; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_addr_ipv6 { + uint8_t addr[16]; + uint16_t port; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/service/src/main/jni/badvpn/misc/strdup.h b/service/src/main/jni/badvpn/misc/strdup.h new file mode 100644 index 0000000..2e475bb --- /dev/null +++ b/service/src/main/jni/badvpn/misc/strdup.h @@ -0,0 +1,86 @@ +/** + * @file strdup.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Allocate memory for a string and copy it there. + */ + +#ifndef BADVPN_STRDUP_H +#define BADVPN_STRDUP_H + +#include +#include +#include + +#include + +/** + * Allocate and copy a null-terminated string. + */ +static char * b_strdup (const char *str) +{ + ASSERT(str) + + size_t len = strlen(str); + + char *s = (char *)malloc(len + 1); + if (!s) { + return NULL; + } + + memcpy(s, str, len + 1); + + return s; +} + +/** + * Allocate memory for a null-terminated string and use the + * given data as its contents. A null terminator is appended + * after the specified data. + */ +static char * b_strdup_bin (const char *str, size_t len) +{ + ASSERT(str) + + if (len == SIZE_MAX) { + return NULL; + } + + char *s = (char *)malloc(len + 1); + if (!s) { + return NULL; + } + + memcpy(s, str, len); + s[len] = '\0'; + + return s; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/udp_proto.h b/service/src/main/jni/badvpn/misc/udp_proto.h new file mode 100644 index 0000000..23ec69a --- /dev/null +++ b/service/src/main/jni/badvpn/misc/udp_proto.h @@ -0,0 +1,170 @@ +/** + * @file udp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the UDP protocol. + */ + +#ifndef BADVPN_MISC_UDP_PROTO_H +#define BADVPN_MISC_UDP_PROTO_H + +#include + +#include +#include +#include +#include +#include + +B_START_PACKED +struct udp_header { + uint16_t source_port; + uint16_t dest_port; + uint16_t length; + uint16_t checksum; +} B_PACKED; +B_END_PACKED + +static uint32_t udp_checksum_summer (const char *data, uint16_t len) +{ + ASSERT(len % 2 == 0) + + uint32_t t = 0; + + for (uint16_t i = 0; i < len / 2; i++) { + t += badvpn_read_be16(data + 2 * i); + } + + return t; +} + +static uint16_t udp_checksum (const struct udp_header *header, const uint8_t *payload, uint16_t payload_len, uint32_t source_addr, uint32_t dest_addr) +{ + uint32_t t = 0; + + t += udp_checksum_summer((char *)&source_addr, sizeof(source_addr)); + t += udp_checksum_summer((char *)&dest_addr, sizeof(dest_addr)); + + uint16_t x; + x = hton16(IPV4_PROTOCOL_UDP); + t += udp_checksum_summer((char *)&x, sizeof(x)); + x = hton16(sizeof(*header) + payload_len); + t += udp_checksum_summer((char *)&x, sizeof(x)); + + t += udp_checksum_summer((const char *)header, sizeof(*header)); + + if (payload_len % 2 == 0) { + t += udp_checksum_summer((const char *)payload, payload_len); + } else { + t += udp_checksum_summer((const char *)payload, payload_len - 1); + + x = hton16(((uint16_t)payload[payload_len - 1]) << 8); + t += udp_checksum_summer((char *)&x, sizeof(x)); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + if (t == 0) { + t = UINT16_MAX; + } + + return hton16(~t); +} + +static uint16_t udp_ip6_checksum (const struct udp_header *header, const uint8_t *payload, uint16_t payload_len, const uint8_t *source_addr, const uint8_t *dest_addr) +{ + uint32_t t = 0; + + t += udp_checksum_summer((const char *)source_addr, 16); + t += udp_checksum_summer((const char *)dest_addr, 16); + + uint32_t x; + x = hton32(sizeof(*header) + payload_len); + t += udp_checksum_summer((char *)&x, sizeof(x)); + x = hton32(IPV6_NEXT_UDP); + t += udp_checksum_summer((char *)&x, sizeof(x)); + + t += udp_checksum_summer((const char *)header, sizeof(*header)); + + if (payload_len % 2 == 0) { + t += udp_checksum_summer((const char *)payload, payload_len); + } else { + t += udp_checksum_summer((const char *)payload, payload_len - 1); + + uint16_t y; + y = hton16(((uint16_t)payload[payload_len - 1]) << 8); + t += udp_checksum_summer((char *)&y, sizeof(y)); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + if (t == 0) { + t = UINT16_MAX; + } + + return hton16(~t); +} + +static int udp_check (const uint8_t *data, int data_len, struct udp_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // parse UDP header + if (data_len < sizeof(struct udp_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + data += sizeof(*out_header); + data_len -= sizeof(*out_header); + + // verify UDP payload + int udp_length = ntoh16(out_header->length); + if (udp_length < sizeof(*out_header)) { + return 0; + } + if (udp_length > sizeof(*out_header) + data_len) { + return 0; + } + + // ignore stray data + data_len = udp_length - sizeof(*out_header); + + *out_payload = (uint8_t *)data; + *out_payload_len = data_len; + return 1; +} + +#endif diff --git a/service/src/main/jni/badvpn/misc/version.h b/service/src/main/jni/badvpn/misc/version.h new file mode 100644 index 0000000..4468849 --- /dev/null +++ b/service/src/main/jni/badvpn/misc/version.h @@ -0,0 +1,41 @@ +/** + * @file version.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Product information definitions. + */ + +#ifndef BADVPN_MISC_VERSION_H +#define BADVPN_MISC_VERSION_H + +#define GLOBAL_PRODUCT_NAME "BadVPN" +#define GLOBAL_VERSION "1.999.127" +#define GLOBAL_COPYRIGHT_NOTICE "Copyright (C) 2010 Ambroz Bizjak " + +#endif diff --git a/service/src/main/jni/badvpn/protocol/packetproto.h b/service/src/main/jni/badvpn/protocol/packetproto.h new file mode 100644 index 0000000..0f0982b --- /dev/null +++ b/service/src/main/jni/badvpn/protocol/packetproto.h @@ -0,0 +1,68 @@ +/** + * @file packetproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for PacketProto, a protocol that allows sending of packets + * over a reliable stream connection. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * Packets are encoded into a stream by representing each packet with: + * - a 16-bit little-endian unsigned integer representing the length + * of the payload + * - that many bytes of payload + */ + +#ifndef BADVPN_PROTOCOL_PACKETPROTO_H +#define BADVPN_PROTOCOL_PACKETPROTO_H + +#include +#include + +#include + +/** + * PacketProto packet header. + * Wraps a single uint16_t in a packed struct for easy access. + */ +B_START_PACKED +struct packetproto_header +{ + /** + * Length of the packet payload that follows. + */ + uint16_t len; +} B_PACKED; +B_END_PACKED + +#define PACKETPROTO_ENCLEN(_len) (sizeof(struct packetproto_header) + (_len)) + +#define PACKETPROTO_MAXPAYLOAD UINT16_MAX + +#endif diff --git a/service/src/main/jni/badvpn/protocol/udpgw_proto.h b/service/src/main/jni/badvpn/protocol/udpgw_proto.h new file mode 100644 index 0000000..da5f02d --- /dev/null +++ b/service/src/main/jni/badvpn/protocol/udpgw_proto.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_PROTOCOL_UDPGW_PROTO_H +#define BADVPN_PROTOCOL_UDPGW_PROTO_H + +#include + +#include +#include + +#define UDPGW_CLIENT_FLAG_KEEPALIVE (1 << 0) +#define UDPGW_CLIENT_FLAG_REBIND (1 << 1) +#define UDPGW_CLIENT_FLAG_DNS (1 << 2) +#define UDPGW_CLIENT_FLAG_IPV6 (1 << 3) + +#ifdef ANDROID +B_START_PACKED +struct socks_udp_header { + uint16_t rsv; + uint8_t frag; + uint8_t atyp; +} B_PACKED; +B_END_PACKED +#endif + +B_START_PACKED +struct udpgw_header { + uint8_t flags; + uint16_t conid; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct udpgw_addr_ipv4 { + uint32_t addr_ip; + uint16_t addr_port; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct udpgw_addr_ipv6 { + uint8_t addr_ip[16]; + uint16_t addr_port; +} B_PACKED; +B_END_PACKED + +static int udpgw_compute_mtu (int dgram_mtu) +{ + bsize_t bs = bsize_add( +#ifdef ANDROID + bsize_fromsize(sizeof(struct socks_udp_header)), +#else + bsize_fromsize(sizeof(struct udpgw_header)), +#endif + bsize_add( + bsize_max( + bsize_fromsize(sizeof(struct udpgw_addr_ipv4)), + bsize_fromsize(sizeof(struct udpgw_addr_ipv6)) + ), + bsize_fromint(dgram_mtu) + ) + ); + + int s; + if (!bsize_toint(bs, &s)) { + return -1; + } + + return s; +} + +#endif diff --git a/service/src/main/jni/badvpn/socksclient/BSocksClient.c b/service/src/main/jni/badvpn/socksclient/BSocksClient.c new file mode 100644 index 0000000..21415af --- /dev/null +++ b/service/src/main/jni/badvpn/socksclient/BSocksClient.c @@ -0,0 +1,608 @@ +/** + * @file BSocksClient.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#define STATE_CONNECTING 1 +#define STATE_SENDING_HELLO 2 +#define STATE_SENT_HELLO 3 +#define STATE_SENDING_PASSWORD 10 +#define STATE_SENT_PASSWORD 11 +#define STATE_SENDING_REQUEST 4 +#define STATE_SENT_REQUEST 5 +#define STATE_RECEIVED_REPLY_HEADER 6 +#define STATE_UP 7 + +static void report_error (BSocksClient *o, int error); +static void init_control_io (BSocksClient *o); +static void free_control_io (BSocksClient *o); +static void init_up_io (BSocksClient *o); +static void free_up_io (BSocksClient *o); +static int reserve_buffer (BSocksClient *o, bsize_t size); +static void start_receive (BSocksClient *o, uint8_t *dest, int total); +static void do_receive (BSocksClient *o); +static void connector_handler (BSocksClient* o, int is_error); +static void connection_handler (BSocksClient* o, int event); +static void recv_handler_done (BSocksClient *o, int data_len); +static void send_handler_done (BSocksClient *o); +static void auth_finished (BSocksClient *p); + +void report_error (BSocksClient *o, int error) +{ + DEBUGERROR(&o->d_err, o->handler(o->user, error)) +} + +void init_control_io (BSocksClient *o) +{ + // init receiving + BConnection_RecvAsync_Init(&o->con); + o->control.recv_if = BConnection_RecvAsync_GetIf(&o->con); + StreamRecvInterface_Receiver_Init(o->control.recv_if, (StreamRecvInterface_handler_done)recv_handler_done, o); + + // init sending + BConnection_SendAsync_Init(&o->con); + PacketStreamSender_Init(&o->control.send_sender, BConnection_SendAsync_GetIf(&o->con), INT_MAX, BReactor_PendingGroup(o->reactor)); + o->control.send_if = PacketStreamSender_GetInput(&o->control.send_sender); + PacketPassInterface_Sender_Init(o->control.send_if, (PacketPassInterface_handler_done)send_handler_done, o); +} + +void free_control_io (BSocksClient *o) +{ + // free sending + PacketStreamSender_Free(&o->control.send_sender); + BConnection_SendAsync_Free(&o->con); + + // free receiving + BConnection_RecvAsync_Free(&o->con); +} + +void init_up_io (BSocksClient *o) +{ + // init receiving + BConnection_RecvAsync_Init(&o->con); + + // init sending + BConnection_SendAsync_Init(&o->con); +} + +void free_up_io (BSocksClient *o) +{ + // free sending + BConnection_SendAsync_Free(&o->con); + + // free receiving + BConnection_RecvAsync_Free(&o->con); +} + +int reserve_buffer (BSocksClient *o, bsize_t size) +{ + if (size.is_overflow) { + BLog(BLOG_ERROR, "size overflow"); + return 0; + } + + char *buffer = (char *)BRealloc(o->buffer, size.value); + if (!buffer) { + BLog(BLOG_ERROR, "BRealloc failed"); + return 0; + } + + o->buffer = buffer; + + return 1; +} + +void start_receive (BSocksClient *o, uint8_t *dest, int total) +{ + ASSERT(total > 0) + + o->control.recv_dest = dest; + o->control.recv_len = 0; + o->control.recv_total = total; + + do_receive(o); +} + +void do_receive (BSocksClient *o) +{ + ASSERT(o->control.recv_len < o->control.recv_total) + + StreamRecvInterface_Receiver_Recv(o->control.recv_if, o->control.recv_dest + o->control.recv_len, o->control.recv_total - o->control.recv_len); +} + +void connector_handler (BSocksClient* o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_CONNECTING) + + // check connection result + if (is_error) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + BLog(BLOG_DEBUG, "connected"); + + // init control I/O + init_control_io(o); + + // check number of methods + if (o->num_auth_info == 0 || o->num_auth_info > 255) { + BLog(BLOG_ERROR, "invalid number of authentication methods"); + goto fail1; + } + + // allocate buffer for sending hello + bsize_t size = bsize_add( + bsize_fromsize(sizeof(struct socks_client_hello_header)), + bsize_mul( + bsize_fromsize(o->num_auth_info), + bsize_fromsize(sizeof(struct socks_client_hello_method)) + ) + ); + if (!reserve_buffer(o, size)) { + goto fail1; + } + + // write hello header + struct socks_client_hello_header header; + header.ver = hton8(SOCKS_VERSION); + header.nmethods = hton8(o->num_auth_info); + memcpy(o->buffer, &header, sizeof(header)); + + // write hello methods + for (size_t i = 0; i < o->num_auth_info; i++) { + struct socks_client_hello_method method; + method.method = hton8(o->auth_info[i].auth_type); + memcpy(o->buffer + sizeof(header) + i * sizeof(method), &method, sizeof(method)); + } + + // send + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_HELLO; + + return; + +fail1: + free_control_io(o); + BConnection_Free(&o->con); +fail0: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; +} + +void connection_handler (BSocksClient* o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state != STATE_CONNECTING) + + if (o->state == STATE_UP && event == BCONNECTION_EVENT_RECVCLOSED) { + report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED); + return; + } + + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; +} + +void recv_handler_done (BSocksClient *o, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->control.recv_total - o->control.recv_len) + DebugObject_Access(&o->d_obj); + + o->control.recv_len += data_len; + + if (o->control.recv_len < o->control.recv_total) { + do_receive(o); + return; + } + + switch (o->state) { + case STATE_SENT_HELLO: { + BLog(BLOG_DEBUG, "received hello"); + + struct socks_server_hello imsg; + memcpy(&imsg, o->buffer, sizeof(imsg)); + + if (ntoh8(imsg.ver) != SOCKS_VERSION) { + BLog(BLOG_NOTICE, "wrong version"); + goto fail; + } + + size_t auth_index; + for (auth_index = 0; auth_index < o->num_auth_info; auth_index++) { + if (o->auth_info[auth_index].auth_type == ntoh8(imsg.method)) { + break; + } + } + + if (auth_index == o->num_auth_info) { + BLog(BLOG_NOTICE, "server didn't accept any authentication method"); + goto fail; + } + + const struct BSocksClient_auth_info *ai = &o->auth_info[auth_index]; + + switch (ai->auth_type) { + case SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED: { + BLog(BLOG_DEBUG, "no authentication"); + + auth_finished(o); + } break; + + case SOCKS_METHOD_USERNAME_PASSWORD: { + BLog(BLOG_DEBUG, "password authentication"); + + if (ai->password.username_len == 0 || ai->password.username_len > 255 || + ai->password.password_len == 0 || ai->password.password_len > 255 + ) { + BLog(BLOG_NOTICE, "invalid username/password length"); + goto fail; + } + + // allocate password packet + bsize_t size = bsize_fromsize(1 + 1 + ai->password.username_len + 1 + ai->password.password_len); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // write password packet + char *ptr = o->buffer; + *ptr++ = 1; + *ptr++ = ai->password.username_len; + memcpy(ptr, ai->password.username, ai->password.username_len); + ptr += ai->password.username_len; + *ptr++ = ai->password.password_len; + memcpy(ptr, ai->password.password, ai->password.password_len); + ptr += ai->password.password_len; + + // start sending + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_PASSWORD; + } break; + + default: ASSERT(0); + } + } break; + + case STATE_SENT_REQUEST: { + BLog(BLOG_DEBUG, "received reply header"); + + struct socks_reply_header imsg; + memcpy(&imsg, o->buffer, sizeof(imsg)); + + if (ntoh8(imsg.ver) != SOCKS_VERSION) { + BLog(BLOG_NOTICE, "wrong version"); + goto fail; + } + + if (ntoh8(imsg.rep) != SOCKS_REP_SUCCEEDED) { + BLog(BLOG_NOTICE, "reply not successful"); + goto fail; + } + + int addr_len; + switch (ntoh8(imsg.atyp)) { + case SOCKS_ATYP_IPV4: + addr_len = sizeof(struct socks_addr_ipv4); + break; + case SOCKS_ATYP_IPV6: + addr_len = sizeof(struct socks_addr_ipv6); + break; + default: + BLog(BLOG_NOTICE, "reply has unknown address type"); + goto fail; + } + + // receive the rest of the reply + start_receive(o, (uint8_t *)o->buffer + sizeof(imsg), addr_len); + + // set state + o->state = STATE_RECEIVED_REPLY_HEADER; + } break; + + case STATE_SENT_PASSWORD: { + BLog(BLOG_DEBUG, "received password reply"); + + if (o->buffer[0] != 1) { + BLog(BLOG_NOTICE, "password reply has unknown version"); + goto fail; + } + + if (o->buffer[1] != 0) { + BLog(BLOG_NOTICE, "password reply is negative"); + goto fail; + } + + auth_finished(o); + } break; + + case STATE_RECEIVED_REPLY_HEADER: { + BLog(BLOG_DEBUG, "received reply rest"); + + // free buffer + BFree(o->buffer); + o->buffer = NULL; + + // free control I/O + free_control_io(o); + + // init up I/O + init_up_io(o); + + // set state + o->state = STATE_UP; + + // call handler + o->handler(o->user, BSOCKSCLIENT_EVENT_UP); + return; + } break; + + default: + ASSERT(0); + } + + return; + +fail: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); +} + +void send_handler_done (BSocksClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->buffer) + + switch (o->state) { + case STATE_SENDING_HELLO: { + BLog(BLOG_DEBUG, "sent hello"); + + // allocate buffer for receiving hello + bsize_t size = bsize_fromsize(sizeof(struct socks_server_hello)); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive hello + start_receive(o, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENT_HELLO; + } break; + + case STATE_SENDING_REQUEST: { + BLog(BLOG_DEBUG, "sent request"); + + // allocate buffer for receiving reply + bsize_t size = bsize_add( + bsize_fromsize(sizeof(struct socks_reply_header)), + bsize_max(bsize_fromsize(sizeof(struct socks_addr_ipv4)), bsize_fromsize(sizeof(struct socks_addr_ipv6))) + ); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive reply header + start_receive(o, (uint8_t *)o->buffer, sizeof(struct socks_reply_header)); + + // set state + o->state = STATE_SENT_REQUEST; + } break; + + case STATE_SENDING_PASSWORD: { + BLog(BLOG_DEBUG, "send password"); + + // allocate buffer for receiving reply + bsize_t size = bsize_fromsize(2); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive reply header + start_receive(o, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENT_PASSWORD; + } break; + + default: + ASSERT(0); + } + + return; + +fail: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); +} + +void auth_finished (BSocksClient *o) +{ + // allocate request buffer + bsize_t size = bsize_fromsize(sizeof(struct socks_request_header)); + switch (o->dest_addr.type) { + case BADDR_TYPE_IPV4: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv4))); break; + case BADDR_TYPE_IPV6: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv6))); break; + } + if (!reserve_buffer(o, size)) { + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; + } + + // write request + struct socks_request_header header; + header.ver = hton8(SOCKS_VERSION); + header.cmd = hton8(SOCKS_CMD_CONNECT); + header.rsv = hton8(0); + switch (o->dest_addr.type) { + case BADDR_TYPE_IPV4: { + header.atyp = hton8(SOCKS_ATYP_IPV4); + struct socks_addr_ipv4 addr; + addr.addr = o->dest_addr.ipv4.ip; + addr.port = o->dest_addr.ipv4.port; + memcpy(o->buffer + sizeof(header), &addr, sizeof(addr)); + } break; + case BADDR_TYPE_IPV6: { + header.atyp = hton8(SOCKS_ATYP_IPV6); + struct socks_addr_ipv6 addr; + memcpy(addr.addr, o->dest_addr.ipv6.ip, sizeof(o->dest_addr.ipv6.ip)); + addr.port = o->dest_addr.ipv6.port; + memcpy(o->buffer + sizeof(header), &addr, sizeof(addr)); + } break; + default: + ASSERT(0); + } + memcpy(o->buffer, &header, sizeof(header)); + + // send request + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_REQUEST; +} + +struct BSocksClient_auth_info BSocksClient_auth_none (void) +{ + struct BSocksClient_auth_info info; + info.auth_type = SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED; + return info; +} + +struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len) +{ + struct BSocksClient_auth_info info; + info.auth_type = SOCKS_METHOD_USERNAME_PASSWORD; + info.password.username = username; + info.password.username_len = username_len; + info.password.password = password; + info.password.password_len = password_len; + return info; +} + +int BSocksClient_Init (BSocksClient *o, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) +{ + ASSERT(!BAddr_IsInvalid(&server_addr)) + ASSERT(dest_addr.type == BADDR_TYPE_IPV4 || dest_addr.type == BADDR_TYPE_IPV6) +#ifndef NDEBUG + for (size_t i = 0; i < num_auth_info; i++) { + ASSERT(auth_info[i].auth_type == SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED || + auth_info[i].auth_type == SOCKS_METHOD_USERNAME_PASSWORD) + } +#endif + + // init arguments + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + o->dest_addr = dest_addr; + o->handler = handler; + o->user = user; + o->reactor = reactor; + + // set no buffer + o->buffer = NULL; + + // init connector + if (!BConnector_Init(&o->connector, server_addr, o->reactor, o, (BConnector_handler)connector_handler)) { + BLog(BLOG_ERROR, "BConnector_Init failed"); + goto fail0; + } + + // set state + o->state = STATE_CONNECTING; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void BSocksClient_Free (BSocksClient *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + if (o->state != STATE_CONNECTING) { + if (o->state == STATE_UP) { + // free up I/O + free_up_io(o); + } else { + // free control I/O + free_control_io(o); + } + + // free connection + BConnection_Free(&o->con); + } + + // free connector + BConnector_Free(&o->connector); + + // free buffer + if (o->buffer) { + BFree(o->buffer); + } +} + +StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o) +{ + ASSERT(o->state == STATE_UP) + DebugObject_Access(&o->d_obj); + + return BConnection_SendAsync_GetIf(&o->con); +} + +StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o) +{ + ASSERT(o->state == STATE_UP) + DebugObject_Access(&o->d_obj); + + return BConnection_RecvAsync_GetIf(&o->con); +} diff --git a/service/src/main/jni/badvpn/socksclient/BSocksClient.h b/service/src/main/jni/badvpn/socksclient/BSocksClient.h new file mode 100644 index 0000000..f19b3a8 --- /dev/null +++ b/service/src/main/jni/badvpn/socksclient/BSocksClient.h @@ -0,0 +1,147 @@ +/** + * @file BSocksClient.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * SOCKS5 client. TCP only, no authentication. + */ + +#ifndef BADVPN_SOCKS_BSOCKSCLIENT_H +#define BADVPN_SOCKS_BSOCKSCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BSOCKSCLIENT_EVENT_ERROR 1 +#define BSOCKSCLIENT_EVENT_UP 2 +#define BSOCKSCLIENT_EVENT_ERROR_CLOSED 3 + +/** + * Handler for events generated by the SOCKS client. + * + * @param user as in {@link BSocksClient_Init} + * @param event event type. One of BSOCKSCLIENT_EVENT_ERROR, BSOCKSCLIENT_EVENT_UP + * and BSOCKSCLIENT_EVENT_ERROR_CLOSED. + * If event is BSOCKSCLIENT_EVENT_UP, the object was previously in down + * state and has transitioned to up state; I/O can be done from this point on. + * If event is BSOCKSCLIENT_EVENT_ERROR or BSOCKSCLIENT_EVENT_ERROR_CLOSED, + * the object must be freed from within the job closure of this handler, + * and no further I/O must be attempted. + */ +typedef void (*BSocksClient_handler) (void *user, int event); + +struct BSocksClient_auth_info { + int auth_type; + union { + struct { + const char *username; + size_t username_len; + const char *password; + size_t password_len; + } password; + }; +}; + +typedef struct { + const struct BSocksClient_auth_info *auth_info; + size_t num_auth_info; + BAddr dest_addr; + BSocksClient_handler handler; + void *user; + BReactor *reactor; + int state; + char *buffer; + BConnector connector; + BConnection con; + union { + struct { + PacketPassInterface *send_if; + PacketStreamSender send_sender; + StreamRecvInterface *recv_if; + uint8_t *recv_dest; + int recv_len; + int recv_total; + } control; + }; + DebugError d_err; + DebugObject d_obj; +} BSocksClient; + +struct BSocksClient_auth_info BSocksClient_auth_none (void); +struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len); + +/** + * Initializes the object. + * The object is initialized in down state. The object must transition to up + * state before the user may begin any I/O. + * + * @param o the object + * @param server_addr SOCKS5 server address + * @param dest_addr remote address + * @param handler handler for up and error events + * @param user value passed to handler + * @param reactor reactor we live in + * @return 1 on success, 0 on failure + */ +int BSocksClient_Init (BSocksClient *o, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void BSocksClient_Free (BSocksClient *o); + +/** + * Returns the send interface. + * The object must be in up state. + * + * @param o the object + * @return send interface + */ +StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o); + +/** + * Returns the receive interface. + * The object must be in up state. + * + * @param o the object + * @return receive interface + */ +StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o); + +#endif diff --git a/service/src/main/jni/badvpn/structure/BAVL.h b/service/src/main/jni/badvpn/structure/BAVL.h new file mode 100644 index 0000000..bc67eeb --- /dev/null +++ b/service/src/main/jni/badvpn/structure/BAVL.h @@ -0,0 +1,797 @@ +/** + * @file BAVL.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * AVL tree. + */ + +#ifndef BADVPN_STRUCTURE_BAVL_H +#define BADVPN_STRUCTURE_BAVL_H + +//#define BAVL_DEBUG + +#include +#include + +#include + +/** + * Handler function called by tree algorithms to compare two values. + * For any two values, the comparator must always return the same result. + * The <= relation defined by the comparator must be a total order. + * Values are obtained like that: + * - The value of a node in the tree, or a node that is being inserted is: + * (uint8_t *)node + offset. + * - The value being looked up is the same as given to the lookup function. + * + * @param user as in {@link BAVL_Init} + * @param val1 first value + * @param val2 second value + * @return -1 if val1 < val2, 0 if val1 = val2, 1 if val1 > val2 + */ +typedef int (*BAVL_comparator) (void *user, void *val1, void *val2); + +struct BAVLNode; + +/** + * AVL tree. + */ +typedef struct { + int offset; + BAVL_comparator comparator; + void *user; + struct BAVLNode *root; +} BAVL; + +/** + * AVL tree node. + */ +typedef struct BAVLNode { + struct BAVLNode *parent; + struct BAVLNode *link[2]; + int8_t balance; +#ifdef BAVL_COUNT + uint64_t count; +#endif +} BAVLNode; + +/** + * Initializes the tree. + * + * @param o tree to initialize + * @param offset offset of a value from its node + * @param comparator value comparator handler function + * @param user value to pass to comparator + */ +static void BAVL_Init (BAVL *o, int offset, BAVL_comparator comparator, void *user); + +/** + * Inserts a node into the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param node uninitialized node to insert. Must have a valid value (its value + * may be passed to the comparator during insertion). + * @param ref if not NULL, will return (regardless if insertion succeeded): + * - the greatest node lesser than the inserted value, or (not in order) + * - the smallest node greater than the inserted value, or + * - NULL meaning there were no nodes in the tree. + * @param 1 on success, 0 if an element with an equal value is already in the tree + */ +static int BAVL_Insert (BAVL *o, BAVLNode *node, BAVLNode **ref); + +/** + * Removes a node from the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param node node to remove + */ +static void BAVL_Remove (BAVL *o, BAVLNode *node); + +/** + * Checks if the tree is empty. + * Must not be called from comparator. + * + * @param o the tree + * @return 1 if empty, 0 if not + */ +static int BAVL_IsEmpty (const BAVL *o); + +/** + * Looks for a value in the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param val value to look for + * @return If a node is in the thee with an equal value, that node. + * Else if the tree is not empty: + * - the greatest node lesser than the given value, or (not in order) + * - the smallest node greater than the given value. + * NULL if the tree is empty. + */ +static BAVLNode * BAVL_Lookup (const BAVL *o, void *val); + +/** + * Looks for a value in the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param val value to look for + * @return If a node is in the thee with an equal value, that node. + * Else NULL. + */ +static BAVLNode * BAVL_LookupExact (const BAVL *o, void *val); + +/** + * Returns the smallest node in the tree, or NULL if the tree is empty. + * + * @param o the tree + * @return smallest node or NULL + */ +static BAVLNode * BAVL_GetFirst (const BAVL *o); + +/** + * Returns the greatest node in the tree, or NULL if the tree is empty. + * + * @param o the tree + * @return greatest node or NULL + */ +static BAVLNode * BAVL_GetLast (const BAVL *o); + +/** + * Returns the node that follows the given node, or NULL if it's the + * last node. + * + * @param o the tree + * @param n node + * @return next node, or NULL + */ +static BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n); + +/** + * Returns the node that precedes the given node, or NULL if it's the + * first node. + * + * @param o the tree + * @param n node + * @return previous node, or NULL + */ +static BAVLNode * BAVL_GetPrev (const BAVL *o, BAVLNode *n); + +#ifdef BAVL_COUNT +static uint64_t BAVL_Count (const BAVL *o); +static uint64_t BAVL_IndexOf (const BAVL *o, BAVLNode *n); +static BAVLNode * BAVL_GetAt (const BAVL *o, uint64_t index); +#endif + +static void BAVL_Verify (BAVL *o); + +#define BAVL_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#define BAVL_OPTNEG(_a, _neg) ((_neg) ? -(_a) : (_a)) + +static void * _BAVL_node_value (const BAVL *o, BAVLNode *n) +{ + return ((uint8_t *)n + o->offset); +} + +static int _BAVL_compare_values (const BAVL *o, void *v1, void *v2) +{ + int res = o->comparator(o->user, v1, v2); + + ASSERT(res == -1 || res == 0 || res == 1) + + return res; +} + +static int _BAVL_compare_nodes (BAVL *o, BAVLNode *n1, BAVLNode *n2) +{ + return _BAVL_compare_values(o, _BAVL_node_value(o, n1), _BAVL_node_value(o, n2)); +} + +#ifdef BAVL_AUTO_VERIFY +#define BAVL_ASSERT(_h) BAVL_Verify((_h)); +#else +#define BAVL_ASSERT(_h) +#endif + +static int _BAVL_assert_recurser (BAVL *o, BAVLNode *n) +{ + ASSERT_FORCE(n->balance >= -1) + ASSERT_FORCE(n->balance <= 1) + + int height_left = 0; + int height_right = 0; +#ifdef BAVL_COUNT + uint64_t count_left = 0; + uint64_t count_right = 0; +#endif + + // check left subtree + if (n->link[0]) { + // check parent link + ASSERT_FORCE(n->link[0]->parent == n) + // check binary search tree + ASSERT_FORCE(_BAVL_compare_nodes(o, n->link[0], n) == -1) + // recursively calculate height + height_left = _BAVL_assert_recurser(o, n->link[0]); +#ifdef BAVL_COUNT + count_left = n->link[0]->count; +#endif + } + + // check right subtree + if (n->link[1]) { + // check parent link + ASSERT_FORCE(n->link[1]->parent == n) + // check binary search tree + ASSERT_FORCE(_BAVL_compare_nodes(o, n->link[1], n) == 1) + // recursively calculate height + height_right = _BAVL_assert_recurser(o, n->link[1]); +#ifdef BAVL_COUNT + count_right = n->link[1]->count; +#endif + } + + // check balance factor + ASSERT_FORCE(n->balance == height_right - height_left) + +#ifdef BAVL_COUNT + // check count + ASSERT_FORCE(n->count == 1 + count_left + count_right) +#endif + + return (BAVL_MAX(height_left, height_right) + 1); +} + +#ifdef BAVL_COUNT +static void _BAVL_update_count_from_children (BAVLNode *n) +{ + n->count = 1 + (n->link[0] ? n->link[0]->count : 0) + (n->link[1] ? n->link[1]->count : 0); +} +#endif + +static void _BAVL_rotate (BAVL *tree, BAVLNode *r, uint8_t dir) +{ + BAVLNode *nr = r->link[!dir]; + + r->link[!dir] = nr->link[dir]; + if (r->link[!dir]) { + r->link[!dir]->parent = r; + } + nr->link[dir] = r; + nr->parent = r->parent; + if (nr->parent) { + nr->parent->link[r == r->parent->link[1]] = nr; + } else { + tree->root = nr; + } + r->parent = nr; + +#ifdef BAVL_COUNT + // update counts + _BAVL_update_count_from_children(r); // first r! + _BAVL_update_count_from_children(nr); +#endif +} + +static BAVLNode * _BAVL_subtree_max (BAVLNode *n) +{ + ASSERT(n) + while (n->link[1]) { + n = n->link[1]; + } + return n; +} + +static void _BAVL_replace_subtree (BAVL *tree, BAVLNode *dest, BAVLNode *n) +{ + ASSERT(dest) + + if (dest->parent) { + dest->parent->link[dest == dest->parent->link[1]] = n; + } else { + tree->root = n; + } + if (n) { + n->parent = dest->parent; + } + +#ifdef BAVL_COUNT + // update counts + for (BAVLNode *c = dest->parent; c; c = c->parent) { + ASSERT(c->count >= dest->count) + c->count -= dest->count; + if (n) { + ASSERT(n->count <= UINT64_MAX - c->count) + c->count += n->count; + } + } +#endif +} + +static void _BAVL_swap_nodes (BAVL *tree, BAVLNode *n1, BAVLNode *n2) +{ + if (n2->parent == n1 || n1->parent == n2) { + // when the nodes are directly connected we need special handling + // make sure n1 is above n2 + if (n1->parent == n2) { + BAVLNode *t = n1; + n1 = n2; + n2 = t; + } + + uint8_t side = (n2 == n1->link[1]); + BAVLNode *c = n1->link[!side]; + + if ((n1->link[0] = n2->link[0])) { + n1->link[0]->parent = n1; + } + if ((n1->link[1] = n2->link[1])) { + n1->link[1]->parent = n1; + } + + if ((n2->parent = n1->parent)) { + n2->parent->link[n1 == n1->parent->link[1]] = n2; + } else { + tree->root = n2; + } + + n2->link[side] = n1; + n1->parent = n2; + if ((n2->link[!side] = c)) { + c->parent = n2; + } + } else { + BAVLNode *temp; + + // swap parents + temp = n1->parent; + if ((n1->parent = n2->parent)) { + n1->parent->link[n2 == n2->parent->link[1]] = n1; + } else { + tree->root = n1; + } + if ((n2->parent = temp)) { + n2->parent->link[n1 == temp->link[1]] = n2; + } else { + tree->root = n2; + } + + // swap left children + temp = n1->link[0]; + if ((n1->link[0] = n2->link[0])) { + n1->link[0]->parent = n1; + } + if ((n2->link[0] = temp)) { + n2->link[0]->parent = n2; + } + + // swap right children + temp = n1->link[1]; + if ((n1->link[1] = n2->link[1])) { + n1->link[1]->parent = n1; + } + if ((n2->link[1] = temp)) { + n2->link[1]->parent = n2; + } + } + + // swap balance factors + int8_t b = n1->balance; + n1->balance = n2->balance; + n2->balance = b; + +#ifdef BAVL_COUNT + // swap counts + uint64_t c = n1->count; + n1->count = n2->count; + n2->count = c; +#endif +} + +static void _BAVL_rebalance (BAVL *o, BAVLNode *node, uint8_t side, int8_t deltac) +{ + ASSERT(side == 0 || side == 1) + ASSERT(deltac >= -1 && deltac <= 1) + ASSERT(node->balance >= -1 && node->balance <= 1) + + // if no subtree changed its height, no more rebalancing is needed + if (deltac == 0) { + return; + } + + // calculate how much our height changed + int8_t delta = BAVL_MAX(deltac, BAVL_OPTNEG(node->balance, side)) - BAVL_MAX(0, BAVL_OPTNEG(node->balance, side)); + ASSERT(delta >= -1 && delta <= 1) + + // update our balance factor + node->balance -= BAVL_OPTNEG(deltac, side); + + BAVLNode *child; + BAVLNode *gchild; + + // perform transformations if the balance factor is wrong + if (node->balance == 2 || node->balance == -2) { + uint8_t bside; + int8_t bsidef; + if (node->balance == 2) { + bside = 1; + bsidef = 1; + } else { + bside = 0; + bsidef = -1; + } + + ASSERT(node->link[bside]) + child = node->link[bside]; + switch (child->balance * bsidef) { + case 1: + _BAVL_rotate(o, node, !bside); + node->balance = 0; + child->balance = 0; + node = child; + delta -= 1; + break; + case 0: + _BAVL_rotate(o, node, !bside); + node->balance = 1 * bsidef; + child->balance = -1 * bsidef; + node = child; + break; + case -1: + ASSERT(child->link[!bside]) + gchild = child->link[!bside]; + _BAVL_rotate(o, child, bside); + _BAVL_rotate(o, node, !bside); + node->balance = -BAVL_MAX(0, gchild->balance * bsidef) * bsidef; + child->balance = BAVL_MAX(0, -gchild->balance * bsidef) * bsidef; + gchild->balance = 0; + node = gchild; + delta -= 1; + break; + default: + ASSERT(0); + } + } + + ASSERT(delta >= -1 && delta <= 1) + // Transformations above preserve this. Proof: + // - if a child subtree gained 1 height and rebalancing was needed, + // it was the heavier subtree. Then delta was was originally 1, because + // the heaviest subtree gained one height. If the transformation reduces + // delta by one, it becomes 0. + // - if a child subtree lost 1 height and rebalancing was needed, it + // was the lighter subtree. Then delta was originally 0, because + // the height of the heaviest subtree was unchanged. If the transformation + // reduces delta by one, it becomes -1. + + if (node->parent) { + _BAVL_rebalance(o, node->parent, node == node->parent->link[1], delta); + } +} + +void BAVL_Init (BAVL *o, int offset, BAVL_comparator comparator, void *user) +{ + o->offset = offset; + o->comparator = comparator; + o->user = user; + o->root = NULL; + + BAVL_ASSERT(o) +} + +int BAVL_Insert (BAVL *o, BAVLNode *node, BAVLNode **ref) +{ + // insert to root? + if (!o->root) { + o->root = node; + node->parent = NULL; + node->link[0] = NULL; + node->link[1] = NULL; + node->balance = 0; +#ifdef BAVL_COUNT + node->count = 1; +#endif + + BAVL_ASSERT(o) + + if (ref) { + *ref = NULL; + } + return 1; + } + + // find node to insert to + BAVLNode *c = o->root; + int side; + while (1) { + // compare + int comp = _BAVL_compare_nodes(o, node, c); + + // have we found a node that compares equal? + if (comp == 0) { + if (ref) { + *ref = c; + } + return 0; + } + + side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + break; + } + + c = c->link[side]; + } + + // insert + c->link[side] = node; + node->parent = c; + node->link[0] = NULL; + node->link[1] = NULL; + node->balance = 0; +#ifdef BAVL_COUNT + node->count = 1; +#endif + +#ifdef BAVL_COUNT + // update counts + for (BAVLNode *p = c; p; p = p->parent) { + ASSERT(p->count < UINT64_MAX) + p->count++; + } +#endif + + // rebalance + _BAVL_rebalance(o, c, side, 1); + + BAVL_ASSERT(o) + + if (ref) { + *ref = c; + } + return 1; +} + +void BAVL_Remove (BAVL *o, BAVLNode *node) +{ + // if we have both subtrees, swap the node and the largest node + // in the left subtree, so we have at most one subtree + if (node->link[0] && node->link[1]) { + // find the largest node in the left subtree + BAVLNode *max = _BAVL_subtree_max(node->link[0]); + // swap the nodes + _BAVL_swap_nodes(o, node, max); + } + + // have at most one child now + ASSERT(!(node->link[0] && node->link[1])) + + BAVLNode *parent = node->parent; + BAVLNode *child = (node->link[0] ? node->link[0] : node->link[1]); + + if (parent) { + // remember on which side node is + int side = (node == parent->link[1]); + // replace node with child + _BAVL_replace_subtree(o, node, child); + // rebalance + _BAVL_rebalance(o, parent, side, -1); + } else { + // replace node with child + _BAVL_replace_subtree(o, node, child); + } + + BAVL_ASSERT(o) +} + +int BAVL_IsEmpty (const BAVL *o) +{ + return (!o->root); +} + +BAVLNode * BAVL_Lookup (const BAVL *o, void *val) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *c = o->root; + while (1) { + // compare + int comp = _BAVL_compare_values(o, val, _BAVL_node_value(o, c)); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + return c; + } + + c = c->link[side]; + } +} + +BAVLNode * BAVL_LookupExact (const BAVL *o, void *val) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *c = o->root; + while (1) { + // compare + int comp = _BAVL_compare_values(o, val, _BAVL_node_value(o, c)); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + return NULL; + } + + c = c->link[side]; + } +} + +BAVLNode * BAVL_GetFirst (const BAVL *o) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *n = o->root; + while (n->link[0]) { + n = n->link[0]; + } + + return n; +} + +BAVLNode * BAVL_GetLast (const BAVL *o) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *n = o->root; + while (n->link[1]) { + n = n->link[1]; + } + + return n; +} + +BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n) +{ + if (n->link[1]) { + n = n->link[1]; + while (n->link[0]) { + n = n->link[0]; + } + } else { + while (n->parent && n == n->parent->link[1]) { + n = n->parent; + } + n = n->parent; + } + + return n; +} + +BAVLNode * BAVL_GetPrev (const BAVL *o, BAVLNode *n) +{ + if (n->link[0]) { + n = n->link[0]; + while (n->link[1]) { + n = n->link[1]; + } + } else { + while (n->parent && n == n->parent->link[0]) { + n = n->parent; + } + n = n->parent; + } + + return n; +} + +#ifdef BAVL_COUNT + +static uint64_t BAVL_Count (const BAVL *o) +{ + return (o->root ? o->root->count : 0); +} + +static uint64_t BAVL_IndexOf (const BAVL *o, BAVLNode *n) +{ + uint64_t index = (n->link[0] ? n->link[0]->count : 0); + + for (BAVLNode *c = n; c->parent; c = c->parent) { + if (c == c->parent->link[1]) { + ASSERT(c->parent->count > c->count) + ASSERT(c->parent->count - c->count <= UINT64_MAX - index) + index += c->parent->count - c->count; + } + } + + return index; +} + +static BAVLNode * BAVL_GetAt (const BAVL *o, uint64_t index) +{ + if (index >= BAVL_Count(o)) { + return NULL; + } + + BAVLNode *c = o->root; + + while (1) { + ASSERT(c) + ASSERT(index < c->count) + + uint64_t left_count = (c->link[0] ? c->link[0]->count : 0); + + if (index == left_count) { + return c; + } + + if (index < left_count) { + c = c->link[0]; + } else { + c = c->link[1]; + index -= left_count + 1; + } + } +} + +#endif + +static void BAVL_Verify (BAVL *o) +{ + if (o->root) { + ASSERT(!o->root->parent) + _BAVL_assert_recurser(o, o->root); + } +} + +#endif diff --git a/service/src/main/jni/badvpn/structure/CAvl.h b/service/src/main/jni/badvpn/structure/CAvl.h new file mode 100644 index 0000000..ea33a3e --- /dev/null +++ b/service/src/main/jni/badvpn/structure/CAvl.h @@ -0,0 +1,36 @@ +/** + * @file CAvl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_CAVL_H +#define BADVPN_CAVL_H + +#include +#include + +#endif diff --git a/service/src/main/jni/badvpn/structure/CAvl_decl.h b/service/src/main/jni/badvpn/structure/CAvl_decl.h new file mode 100644 index 0000000..7d54a81 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/CAvl_decl.h @@ -0,0 +1,77 @@ +/** + * @file CAvl_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CAvl_header.h" + +typedef struct { + CAvlLink root; +} CAvl; + +typedef struct { + CAvlEntry *ptr; + CAvlLink link; +} CAvlRef; + +static int CAvlIsNullRef (CAvlRef node); +static int CAvlIsValidRef (CAvlRef node); +static CAvlRef CAvlDeref (CAvlArg arg, CAvlLink link); + +static void CAvl_Init (CAvl *o); +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +static int CAvl_Insert (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef *out_ref); +#else +static void CAvl_InsertAt (CAvl *o, CAvlArg arg, CAvlRef node, CAvlCount index); +#endif +static void CAvl_Remove (CAvl *o, CAvlArg arg, CAvlRef node); +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_NOKEYS +static CAvlRef CAvl_Lookup (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_LookupExact (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetFirstGreater (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetLastLesser (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetFirstGreaterEqual (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetLastLesserEqual (const CAvl *o, CAvlArg arg, CAvlKey key); +#endif +static CAvlRef CAvl_GetFirst (const CAvl *o, CAvlArg arg); +static CAvlRef CAvl_GetLast (const CAvl *o, CAvlArg arg); +static CAvlRef CAvl_GetNext (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_GetPrev (const CAvl *o, CAvlArg arg, CAvlRef node); +static int CAvl_IsEmpty (const CAvl *o); +static void CAvl_Verify (const CAvl *o, CAvlArg arg); +#if CAVL_PARAM_FEATURE_COUNTS +static CAvlCount CAvl_Count (const CAvl *o, CAvlArg arg); +static CAvlCount CAvl_IndexOf (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_GetAt (const CAvl *o, CAvlArg arg, CAvlCount index); +#endif +#if CAVL_PARAM_FEATURE_ASSOC +static CAvlAssoc CAvl_AssocSum (const CAvl *o, CAvlArg arg); +static CAvlAssoc CAvl_ExclusiveAssocPrefixSum (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual (const CAvl *o, CAvlArg arg, CAvlAssoc sum, int (*sum_less) (void *, CAvlAssoc, CAvlAssoc), void *user); +#endif + +#include "CAvl_footer.h" diff --git a/service/src/main/jni/badvpn/structure/CAvl_footer.h b/service/src/main/jni/badvpn/structure/CAvl_footer.h new file mode 100644 index 0000000..43b85c3 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/CAvl_footer.h @@ -0,0 +1,113 @@ +/** + * @file CAvl_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef CAVL_PARAM_NAME +#undef CAVL_PARAM_FEATURE_COUNTS +#undef CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +#undef CAVL_PARAM_FEATURE_NOKEYS +#undef CAVL_PARAM_FEATURE_ASSOC +#undef CAVL_PARAM_TYPE_ENTRY +#undef CAVL_PARAM_TYPE_LINK +#undef CAVL_PARAM_TYPE_KEY +#undef CAVL_PARAM_TYPE_ARG +#undef CAVL_PARAM_TYPE_COUNT +#undef CAVL_PARAM_TYPE_ASSOC +#undef CAVL_PARAM_VALUE_COUNT_MAX +#undef CAVL_PARAM_VALUE_NULL +#undef CAVL_PARAM_VALUE_ASSOC_ZERO +#undef CAVL_PARAM_FUN_DEREF +#undef CAVL_PARAM_FUN_COMPARE_ENTRIES +#undef CAVL_PARAM_FUN_COMPARE_KEY_ENTRY +#undef CAVL_PARAM_FUN_ASSOC_VALUE +#undef CAVL_PARAM_FUN_ASSOC_OPER +#undef CAVL_PARAM_MEMBER_CHILD +#undef CAVL_PARAM_MEMBER_BALANCE +#undef CAVL_PARAM_MEMBER_PARENT +#undef CAVL_PARAM_MEMBER_COUNT +#undef CAVL_PARAM_MEMBER_ASSOC + +#undef CAvl +#undef CAvlEntry +#undef CAvlLink +#undef CAvlRef +#undef CAvlArg +#undef CAvlKey +#undef CAvlCount +#undef CAvlAssoc + +#undef CAvlIsNullRef +#undef CAvlIsValidRef +#undef CAvlDeref + +#undef CAvl_Init +#undef CAvl_Insert +#undef CAvl_InsertAt +#undef CAvl_Remove +#undef CAvl_Lookup +#undef CAvl_LookupExact +#undef CAvl_GetFirstGreater +#undef CAvl_GetLastLesser +#undef CAvl_GetFirstGreaterEqual +#undef CAvl_GetLastLesserEqual +#undef CAvl_GetFirst +#undef CAvl_GetLast +#undef CAvl_GetNext +#undef CAvl_GetPrev +#undef CAvl_IsEmpty +#undef CAvl_Verify +#undef CAvl_Count +#undef CAvl_IndexOf +#undef CAvl_GetAt +#undef CAvl_AssocSum +#undef CAvl_ExclusiveAssocPrefixSum +#undef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual + +#undef CAvl_link +#undef CAvl_balance +#undef CAvl_parent +#undef CAvl_count +#undef CAvl_assoc +#undef CAvl_nulllink +#undef CAvl_nullref +#undef CAvl_compare_entries +#undef CAvl_compare_key_entry +#undef CAvl_compute_node_assoc +#undef CAvl_check_parent +#undef CAvl_verify_recurser +#undef CAvl_assert_tree +#undef CAvl_update_count_from_children +#undef CAvl_rotate +#undef CAvl_subtree_min +#undef CAvl_subtree_max +#undef CAvl_replace_subtree_fix_assoc +#undef CAvl_swap_for_remove +#undef CAvl_rebalance +#undef CAvl_child_count +#undef CAvl_MAX +#undef CAvl_OPTNEG diff --git a/service/src/main/jni/badvpn/structure/CAvl_header.h b/service/src/main/jni/badvpn/structure/CAvl_header.h new file mode 100644 index 0000000..91ea7df --- /dev/null +++ b/service/src/main/jni/badvpn/structure/CAvl_header.h @@ -0,0 +1,141 @@ +/** + * @file CAvl_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// CAVL_PARAM_NAME - name of this data structure +// CAVL_PARAM_FEATURE_COUNTS - whether to keep count information (0 or 1) +// CAVL_PARAM_FEATURE_KEYS_ARE_INDICES - (0 or 1) whether to assume the keys are entry indices +// (number of entries lesser than given entry). If yes, CAVL_PARAM_TYPE_KEY is unused. +// Requires CAVL_PARAM_FEATURE_COUNTS. +// CAVL_PARAM_FEATURE_NOKEYS - define to 1 if there is no need for a lookup operation +// CAVL_PARAM_FEATURE_ASSOC - define to 1 for computation of an associative operation on subtrees. +// If enabled, the following macros must be defined: CAVL_PARAM_TYPE_ASSOC, +// CAVL_PARAM_VALUE_ASSOC_ZERO, CAVL_PARAM_FUN_ASSOC_VALUE, +// CAVL_PARAM_FUN_ASSOC_OPER, CAVL_PARAM_MEMBER_ASSOC. +// CAVL_PARAM_TYPE_ENTRY - type of entry +// CAVL_PARAM_TYPE_LINK - type of entry link (usually pointer or index) +// CAVL_PARAM_TYPE_KEY - type of key (only if not CAVL_PARAM_FEATURE_KEYS_ARE_INDICES and +// not CAVL_PARAM_FEATURE_NOKEYS) +// CAVL_PARAM_TYPE_ARG - type of argument pass through to callbacks +// CAVL_PARAM_TYPE_COUNT - type of count (only if CAVL_PARAM_FEATURE_COUNTS) +// CAVL_PARAM_TYPE_ASSOC - type of associative operation result +// CAVL_PARAM_VALUE_COUNT_MAX - maximum value of count (type is CAVL_PARAM_TYPE_COUNT) +// CAVL_PARAM_VALUE_NULL - value of invalid link (type is CAVL_PARAM_TYPE_LINK) +// CAVL_PARAM_VALUE_ASSOC_ZERO - zero value for associative operation (type is CAVL_PARAM_TYPE_ASSOC). +// This must be both a left- and right-identity for the associative operation. +// CAVL_PARAM_FUN_DEREF(arg, link) - dereference a non-null link; returns pointer to CAVL_PARAM_TYPE_LINK +// CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) - compare to entries; returns -1/0/1 +// CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) - compare key and entry; returns -1/0/1 +// CAVL_PARAM_FUN_ASSOC_VALUE(arg, entry) - get value of a node for associative operation. +// The result will be cast to CAVL_PARAM_TYPE_ASSOC. +// CAVL_PARAM_FUN_ASSOC_OPER(arg, value1, value2) - compute the associative operation on two values. +// The type of the two values is CAVL_PARAM_TYPE_ASSOC, and the result will be cast to +// CAVL_PARAM_TYPE_ASSOC. +// CAVL_PARAM_MEMBER_CHILD - name of the child member in entry (type is CAVL_PARAM_TYPE_LINK[2]) +// CAVL_PARAM_MEMBER_BALANCE - name of the balance member in entry (type is any signed integer) +// CAVL_PARAM_MEMBER_PARENT - name of the parent member in entry (type is CAVL_PARAM_TYPE_LINK) +// CAVL_PARAM_MEMBER_COUNT - name of the count member in entry (type is CAVL_PARAM_TYPE_COUNT) +// (only if CAVL_PARAM_FEATURE_COUNTS) +// CAVL_PARAM_MEMBER_ASSOC - name of assoc member in entry (type is CAVL_PARAM_TYPE_ASSOC) + +#ifndef BADVPN_CAVL_H +#error CAvl.h has not been included +#endif + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_COUNTS +#error CAVL_PARAM_FEATURE_KEYS_ARE_INDICES requires CAVL_PARAM_FEATURE_COUNTS +#endif + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && CAVL_PARAM_FEATURE_NOKEYS +#error CAVL_PARAM_FEATURE_KEYS_ARE_INDICES and CAVL_PARAM_FEATURE_NOKEYS cannot be used together +#endif + +// types +#define CAvl CAVL_PARAM_NAME +#define CAvlEntry CAVL_PARAM_TYPE_ENTRY +#define CAvlLink CAVL_PARAM_TYPE_LINK +#define CAvlRef MERGE(CAVL_PARAM_NAME, Ref) +#define CAvlArg CAVL_PARAM_TYPE_ARG +#define CAvlKey CAVL_PARAM_TYPE_KEY +#define CAvlCount CAVL_PARAM_TYPE_COUNT +#define CAvlAssoc CAVL_PARAM_TYPE_ASSOC + +// non-object public functions +#define CAvlIsNullRef MERGE(CAvl, IsNullRef) +#define CAvlIsValidRef MERGE(CAvl, IsValidRef) +#define CAvlDeref MERGE(CAvl, Deref) + +// public functions +#define CAvl_Init MERGE(CAvl, _Init) +#define CAvl_Insert MERGE(CAvl, _Insert) +#define CAvl_InsertAt MERGE(CAvl, _InsertAt) +#define CAvl_Remove MERGE(CAvl, _Remove) +#define CAvl_Lookup MERGE(CAvl, _Lookup) +#define CAvl_LookupExact MERGE(CAvl, _LookupExact) +#define CAvl_GetFirstGreater MERGE(CAvl, _GetFirstGreater) +#define CAvl_GetLastLesser MERGE(CAvl, _GetLastLesser) +#define CAvl_GetFirstGreaterEqual MERGE(CAvl, _GetFirstGreaterEqual) +#define CAvl_GetLastLesserEqual MERGE(CAvl, _GetLastLesserEqual) +#define CAvl_GetFirst MERGE(CAvl, _GetFirst) +#define CAvl_GetLast MERGE(CAvl, _GetLast) +#define CAvl_GetNext MERGE(CAvl, _GetNext) +#define CAvl_GetPrev MERGE(CAvl, _GetPrev) +#define CAvl_IsEmpty MERGE(CAvl, _IsEmpty) +#define CAvl_Verify MERGE(CAvl, _Verify) +#define CAvl_Count MERGE(CAvl, _Count) +#define CAvl_IndexOf MERGE(CAvl, _IndexOf) +#define CAvl_GetAt MERGE(CAvl, _GetAt) +#define CAvl_AssocSum MERGE(CAvl, _AssocSum) +#define CAvl_ExclusiveAssocPrefixSum MERGE(CAvl, _ExclusiveAssocPrefixSum) +#define CAvl_FindLastExclusiveAssocPrefixSumLesserEqual MERGE(CAvl, _FindLastExclusiveAssocPrefixSumLesserEqual) + +// private stuff +#define CAvl_link(entry) ((entry).ptr->CAVL_PARAM_MEMBER_CHILD) +#define CAvl_balance(entry) ((entry).ptr->CAVL_PARAM_MEMBER_BALANCE) +#define CAvl_parent(entry) ((entry).ptr->CAVL_PARAM_MEMBER_PARENT) +#define CAvl_count(entry) ((entry).ptr->CAVL_PARAM_MEMBER_COUNT) +#define CAvl_assoc(entry) ((entry).ptr->CAVL_PARAM_MEMBER_ASSOC) +#define CAvl_nulllink MERGE(CAvl, __nulllink) +#define CAvl_nullref MERGE(CAvl, __nullref) +#define CAvl_compare_entries MERGE(CAVL_PARAM_NAME, _compare_entries) +#define CAvl_compare_key_entry MERGE(CAVL_PARAM_NAME, _compare_key_entry) +#define CAvl_compute_node_assoc MERGE(CAVL_PARAM_NAME, _compute_node_assoc) +#define CAvl_check_parent MERGE(CAVL_PARAM_NAME, _check_parent) +#define CAvl_verify_recurser MERGE(CAVL_PARAM_NAME, _verify_recurser) +#define CAvl_assert_tree MERGE(CAVL_PARAM_NAME, _assert_tree) +#define CAvl_update_count_from_children MERGE(CAVL_PARAM_NAME, _update_count_from_children) +#define CAvl_rotate MERGE(CAVL_PARAM_NAME, _rotate) +#define CAvl_subtree_min MERGE(CAVL_PARAM_NAME, _subtree_min) +#define CAvl_subtree_max MERGE(CAVL_PARAM_NAME, _subtree_max) +#define CAvl_replace_subtree_fix_assoc MERGE(CAVL_PARAM_NAME, _replace_subtree_fix_counts) +#define CAvl_swap_for_remove MERGE(CAVL_PARAM_NAME, _swap_entries) +#define CAvl_rebalance MERGE(CAVL_PARAM_NAME, _rebalance) +#define CAvl_child_count MERGE(CAvl, __child_count) +#define CAvl_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#define CAvl_OPTNEG(_a, _neg) ((_neg) ? -(_a) : (_a)) diff --git a/service/src/main/jni/badvpn/structure/CAvl_impl.h b/service/src/main/jni/badvpn/structure/CAvl_impl.h new file mode 100644 index 0000000..984bdea --- /dev/null +++ b/service/src/main/jni/badvpn/structure/CAvl_impl.h @@ -0,0 +1,949 @@ +/** + * @file CAvl_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CAvl_header.h" + +static CAvlLink CAvl_nulllink (void) +{ + return CAVL_PARAM_VALUE_NULL; +} + +static CAvlRef CAvl_nullref (void) +{ + CAvlRef n; + n.link = CAVL_PARAM_VALUE_NULL; + n.ptr = NULL; + return n; +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + +static int CAvl_compare_entries (CAvlArg arg, CAvlRef node1, CAvlRef node2) +{ + int res = CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, node1, node2); + ASSERT(res >= -1) + ASSERT(res <= 1) + + return res; +} + +#if !CAVL_PARAM_FEATURE_NOKEYS + +static int CAvl_compare_key_entry (CAvlArg arg, CAvlKey key1, CAvlRef node2) +{ + int res = CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, node2); + ASSERT(res >= -1) + ASSERT(res <= 1) + + return res; +} + +#endif + +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + +static CAvlAssoc CAvl_compute_node_assoc (CAvlArg arg, CAvlRef node) +{ + CAvlAssoc sum = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); + if (CAvl_link(node)[0] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[0])), sum); + } + if (CAvl_link(node)[1] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, sum, CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[1]))); + } + return sum; +} + +#endif + +static int CAvl_check_parent (CAvlRef p, CAvlRef c) +{ + return (p.link == CAvl_parent(c)) && (p.link == CAvl_nulllink() || c.link == CAvl_link(p)[0] || c.link == CAvl_link(p)[1]); +} + +static int CAvl_verify_recurser (CAvlArg arg, CAvlRef n) +{ + ASSERT_FORCE(CAvl_balance(n) >= -1) + ASSERT_FORCE(CAvl_balance(n) <= 1) + + int height_left = 0; + int height_right = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvlCount count_left = 0; + CAvlCount count_right = 0; +#endif + + // check left subtree + if (CAvl_link(n)[0] != CAvl_nulllink()) { + // check parent link + ASSERT_FORCE(CAvl_parent(CAvlDeref(arg, CAvl_link(n)[0])) == n.link) + // check binary search tree +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + ASSERT_FORCE(CAvl_compare_entries(arg, CAvlDeref(arg, CAvl_link(n)[0]), n) == -1) +#endif + // recursively calculate height + height_left = CAvl_verify_recurser(arg, CAvlDeref(arg, CAvl_link(n)[0])); +#if CAVL_PARAM_FEATURE_COUNTS + count_left = CAvl_count(CAvlDeref(arg, CAvl_link(n)[0])); +#endif + } + + // check right subtree + if (CAvl_link(n)[1] != CAvl_nulllink()) { + // check parent link + ASSERT_FORCE(CAvl_parent(CAvlDeref(arg, CAvl_link(n)[1])) == n.link) + // check binary search tree +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + ASSERT_FORCE(CAvl_compare_entries(arg, CAvlDeref(arg, CAvl_link(n)[1]), n) == 1) +#endif + // recursively calculate height + height_right = CAvl_verify_recurser(arg, CAvlDeref(arg, CAvl_link(n)[1])); +#if CAVL_PARAM_FEATURE_COUNTS + count_right = CAvl_count(CAvlDeref(arg, CAvl_link(n)[1])); +#endif + } + + // check balance factor + ASSERT_FORCE(CAvl_balance(n) == height_right - height_left) + +#if CAVL_PARAM_FEATURE_COUNTS + // check count + ASSERT_FORCE(CAvl_count(n) == 1 + count_left + count_right) +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + // check assoc + ASSERT_FORCE(CAvl_assoc(n) == CAvl_compute_node_assoc(arg, n)) +#endif + + return CAvl_MAX(height_left, height_right) + 1; +} + +static void CAvl_assert_tree (CAvl *o, CAvlArg arg) +{ +#ifdef CAVL_AUTO_VERIFY + CAvl_Verify(o, arg); +#endif +} + +#if CAVL_PARAM_FEATURE_COUNTS +static void CAvl_update_count_from_children (CAvlArg arg, CAvlRef n) +{ + CAvlCount left_count = CAvl_link(n)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[0])) : 0; + CAvlCount right_count = CAvl_link(n)[1] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[1])) : 0; + CAvl_count(n) = 1 + left_count + right_count; +} +#endif + +static void CAvl_rotate (CAvl *o, CAvlArg arg, CAvlRef r, uint8_t dir, CAvlRef r_parent) +{ + ASSERT(CAvl_check_parent(r_parent, r)) + CAvlRef nr = CAvlDeref(arg, CAvl_link(r)[!dir]); + + CAvl_link(r)[!dir] = CAvl_link(nr)[dir]; + if (CAvl_link(r)[!dir] != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(r)[!dir])) = r.link; + } + CAvl_link(nr)[dir] = r.link; + CAvl_parent(nr) = r_parent.link; + if (r_parent.link != CAvl_nulllink()) { + CAvl_link(r_parent)[r.link == CAvl_link(r_parent)[1]] = nr.link; + } else { + o->root = nr.link; + } + CAvl_parent(r) = nr.link; + +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_update_count_from_children(arg, r); + CAvl_update_count_from_children(arg, nr); +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(r) = CAvl_compute_node_assoc(arg, r); + CAvl_assoc(nr) = CAvl_compute_node_assoc(arg, nr); +#endif +} + +static CAvlRef CAvl_subtree_min (CAvlArg arg, CAvlRef n) +{ + ASSERT(n.link != CAvl_nulllink()) + + while (CAvl_link(n)[0] != CAvl_nulllink()) { + n = CAvlDeref(arg, CAvl_link(n)[0]); + } + + return n; +} + +static CAvlRef CAvl_subtree_max (CAvlArg arg, CAvlRef n) +{ + ASSERT(n.link != CAvl_nulllink()) + + while (CAvl_link(n)[1] != CAvl_nulllink()) { + n = CAvlDeref(arg, CAvl_link(n)[1]); + } + + return n; +} + +static void CAvl_replace_subtree_fix_assoc (CAvl *o, CAvlArg arg, CAvlRef dest, CAvlRef n, CAvlRef dest_parent) +{ + ASSERT(dest.link != CAvl_nulllink()) + ASSERT(CAvl_check_parent(dest_parent, dest)) + + if (dest_parent.link != CAvl_nulllink()) { + CAvl_link(dest_parent)[dest.link == CAvl_link(dest_parent)[1]] = n.link; + } else { + o->root = n.link; + } + if (n.link != CAvl_nulllink()) { + CAvl_parent(n) = CAvl_parent(dest); + } + +#if CAVL_PARAM_FEATURE_COUNTS || CAVL_PARAM_FEATURE_ASSOC + for (CAvlRef c = dest_parent; c.link != CAvl_nulllink(); c = CAvlDeref(arg, CAvl_parent(c))) { +#if CAVL_PARAM_FEATURE_COUNTS + ASSERT(CAvl_count(c) >= CAvl_count(dest)) + CAvl_count(c) -= CAvl_count(dest); + if (n.link != CAvl_nulllink()) { + ASSERT(CAvl_count(n) <= CAVL_PARAM_VALUE_COUNT_MAX - CAvl_count(c)) + CAvl_count(c) += CAvl_count(n); + } +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(c) = CAvl_compute_node_assoc(arg, c); +#endif + } +#endif +} + +static void CAvl_swap_for_remove (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef enode, CAvlRef node_parent, CAvlRef enode_parent) +{ + ASSERT(CAvl_check_parent(node_parent, node)) + ASSERT(CAvl_check_parent(enode_parent, enode)) + + if (enode_parent.link == node.link) { + // when the nodes are directly connected we need special handling + + uint8_t side = (enode.link == CAvl_link(node)[1]); + CAvlRef c = CAvlDeref(arg, CAvl_link(node)[!side]); + + if ((CAvl_link(node)[0] = CAvl_link(enode)[0]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[0])) = node.link; + } + if ((CAvl_link(node)[1] = CAvl_link(enode)[1]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[1])) = node.link; + } + + CAvl_parent(enode) = CAvl_parent(node); + if (node_parent.link != CAvl_nulllink()) { + CAvl_link(node_parent)[node.link == CAvl_link(node_parent)[1]] = enode.link; + } else { + o->root = enode.link; + } + + CAvl_link(enode)[side] = node.link; + CAvl_parent(node) = enode.link; + if ((CAvl_link(enode)[!side] = c.link) != CAvl_nulllink()) { + CAvl_parent(c) = enode.link; + } + } else { + CAvlRef temp; + + // swap parents + temp = node_parent; + CAvl_parent(node) = CAvl_parent(enode); + if (enode_parent.link != CAvl_nulllink()) { + CAvl_link(enode_parent)[enode.link == CAvl_link(enode_parent)[1]] = node.link; + } else { + o->root = node.link; + } + CAvl_parent(enode) = temp.link; + if (temp.link != CAvl_nulllink()) { + CAvl_link(temp)[node.link == CAvl_link(temp)[1]] = enode.link; + } else { + o->root = enode.link; + } + + // swap left children + temp = CAvlDeref(arg, CAvl_link(node)[0]); + if ((CAvl_link(node)[0] = CAvl_link(enode)[0]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[0])) = node.link; + } + if ((CAvl_link(enode)[0] = temp.link) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(enode)[0])) = enode.link; + } + + // swap right children + temp = CAvlDeref(arg, CAvl_link(node)[1]); + if ((CAvl_link(node)[1] = CAvl_link(enode)[1]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[1])) = node.link; + } + if ((CAvl_link(enode)[1] = temp.link) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(enode)[1])) = enode.link; + } + } + + // swap balance factors + int8_t b = CAvl_balance(node); + CAvl_balance(node) = CAvl_balance(enode); + CAvl_balance(enode) = b; + +#if CAVL_PARAM_FEATURE_COUNTS + // swap counts + CAvlCount c = CAvl_count(node); + CAvl_count(node) = CAvl_count(enode); + CAvl_count(enode) = c; +#endif + + // not fixing assoc values here because CAvl_replace_subtree_fix_assoc() will do it +} + +static void CAvl_rebalance (CAvl *o, CAvlArg arg, CAvlRef node, uint8_t side, int8_t deltac) +{ + ASSERT(side == 0 || side == 1) + ASSERT(deltac >= -1 && deltac <= 1) + ASSERT(CAvl_balance(node) >= -1 && CAvl_balance(node) <= 1) + + // if no subtree changed its height, no more rebalancing is needed + if (deltac == 0) { + return; + } + + // calculate how much our height changed + int8_t delta = CAvl_MAX(deltac, CAvl_OPTNEG(CAvl_balance(node), side)) - CAvl_MAX(0, CAvl_OPTNEG(CAvl_balance(node), side)); + ASSERT(delta >= -1 && delta <= 1) + + // update our balance factor + CAvl_balance(node) -= CAvl_OPTNEG(deltac, side); + + CAvlRef child; + CAvlRef gchild; + + // perform transformations if the balance factor is wrong + if (CAvl_balance(node) == 2 || CAvl_balance(node) == -2) { + uint8_t bside; + int8_t bsidef; + if (CAvl_balance(node) == 2) { + bside = 1; + bsidef = 1; + } else { + bside = 0; + bsidef = -1; + } + + ASSERT(CAvl_link(node)[bside] != CAvl_nulllink()) + child = CAvlDeref(arg, CAvl_link(node)[bside]); + + switch (CAvl_balance(child) * bsidef) { + case 1: + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = 0; + CAvl_balance(child) = 0; + node = child; + delta -= 1; + break; + case 0: + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = 1 * bsidef; + CAvl_balance(child) = -1 * bsidef; + node = child; + break; + case -1: + ASSERT(CAvl_link(child)[!bside] != CAvl_nulllink()) + gchild = CAvlDeref(arg, CAvl_link(child)[!bside]); + CAvl_rotate(o, arg, child, bside, node); + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = -CAvl_MAX(0, CAvl_balance(gchild) * bsidef) * bsidef; + CAvl_balance(child) = CAvl_MAX(0, -CAvl_balance(gchild) * bsidef) * bsidef; + CAvl_balance(gchild) = 0; + node = gchild; + delta -= 1; + break; + default: + ASSERT(0); + } + } + + ASSERT(delta >= -1 && delta <= 1) + // Transformations above preserve this. Proof: + // - if a child subtree gained 1 height and rebalancing was needed, + // it was the heavier subtree. Then delta was was originally 1, because + // the heaviest subtree gained one height. If the transformation reduces + // delta by one, it becomes 0. + // - if a child subtree lost 1 height and rebalancing was needed, it + // was the lighter subtree. Then delta was originally 0, because + // the height of the heaviest subtree was unchanged. If the transformation + // reduces delta by one, it becomes -1. + + if (CAvl_parent(node) != CAvl_nulllink()) { + CAvlRef node_parent = CAvlDeref(arg, CAvl_parent(node)); + CAvl_rebalance(o, arg, node_parent, node.link == CAvl_link(node_parent)[1], delta); + } +} + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +static CAvlCount CAvl_child_count (CAvlArg arg, CAvlRef n, int dir) +{ + return (CAvl_link(n)[dir] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[dir])) : 0); +} +#endif + +static int CAvlIsNullRef (CAvlRef node) +{ + return node.link == CAvl_nulllink(); +} + +static int CAvlIsValidRef (CAvlRef node) +{ + return node.link != CAvl_nulllink(); +} + +static CAvlRef CAvlDeref (CAvlArg arg, CAvlLink link) +{ + if (link == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef n; + n.ptr = CAVL_PARAM_FUN_DEREF(arg, link); + n.link = link; + + ASSERT(n.ptr) + + return n; +} + +static void CAvl_Init (CAvl *o) +{ + o->root = CAvl_nulllink(); +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + +static int CAvl_Insert (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef *out_ref) +{ + ASSERT(node.link != CAvl_nulllink()) +#if CAVL_PARAM_FEATURE_COUNTS + ASSERT(CAvl_Count(o, arg) < CAVL_PARAM_VALUE_COUNT_MAX) +#endif + + // insert to root? + if (o->root == CAvl_nulllink()) { + o->root = node.link; + CAvl_parent(node) = CAvl_nulllink(); + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(node) = 1; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + CAvl_assert_tree(o, arg); + + if (out_ref) { + *out_ref = CAvl_nullref(); + } + return 1; + } + + CAvlRef c = CAvlDeref(arg, o->root); + int side; + while (1) { + int comp = CAvl_compare_entries(arg, node, c); + + if (comp == 0) { + if (out_ref) { + *out_ref = c; + } + return 0; + } + + side = (comp == 1); + + if (CAvl_link(c)[side] == CAvl_nulllink()) { + break; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } + + CAvl_link(c)[side] = node.link; + CAvl_parent(node) = c.link; + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(node) = 1; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + +#if CAVL_PARAM_FEATURE_COUNTS || CAVL_PARAM_FEATURE_ASSOC + for (CAvlRef p = c; p.link != CAvl_nulllink(); p = CAvlDeref(arg, CAvl_parent(p))) { +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(p)++; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(p) = CAvl_compute_node_assoc(arg, p); +#endif + } +#endif + + CAvl_rebalance(o, arg, c, side, 1); + + CAvl_assert_tree(o, arg); + + if (out_ref) { + *out_ref = c; + } + return 1; +} + +#else + +static void CAvl_InsertAt (CAvl *o, CAvlArg arg, CAvlRef node, CAvlCount index) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(index <= CAvl_Count(o, arg)) + ASSERT(CAvl_Count(o, arg) < CAVL_PARAM_VALUE_COUNT_MAX) + + // insert to root? + if (o->root == CAvl_nulllink()) { + o->root = node.link; + CAvl_parent(node) = CAvl_nulllink(); + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; + CAvl_count(node) = 1; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + CAvl_assert_tree(o, arg); + return; + } + + CAvlRef c = CAvlDeref(arg, o->root); + CAvlCount c_idx = CAvl_child_count(arg, c, 0); + int side; + while (1) { + side = (index > c_idx); + + if (CAvl_link(c)[side] == CAvl_nulllink()) { + break; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + + if (side == 0) { + c_idx -= 1 + CAvl_child_count(arg, c, 1); + } else { + c_idx += 1 + CAvl_child_count(arg, c, 0); + } + } + + CAvl_link(c)[side] = node.link; + CAvl_parent(node) = c.link; + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; + CAvl_count(node) = 1; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + for (CAvlRef p = c; p.link != CAvl_nulllink(); p = CAvlDeref(arg, CAvl_parent(p))) { + CAvl_count(p)++; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(p) = CAvl_compute_node_assoc(arg, p); +#endif + } + + CAvl_rebalance(o, arg, c, side, 1); + + CAvl_assert_tree(o, arg); + return; +} + +#endif + +static void CAvl_Remove (CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[0] != CAvl_nulllink() && CAvl_link(node)[1] != CAvl_nulllink()) { + CAvlRef max = CAvl_subtree_max(arg, CAvlDeref(arg, CAvl_link(node)[0])); + CAvl_swap_for_remove(o, arg, node, max, CAvlDeref(arg, CAvl_parent(node)), CAvlDeref(arg, CAvl_parent(max))); + } + + ASSERT(CAvl_link(node)[0] == CAvl_nulllink() || CAvl_link(node)[1] == CAvl_nulllink()) + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + CAvlRef child = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvlDeref(arg, CAvl_link(node)[0]) : CAvlDeref(arg, CAvl_link(node)[1])); + + if (paren.link != CAvl_nulllink()) { + int side = (node.link == CAvl_link(paren)[1]); + CAvl_replace_subtree_fix_assoc(o, arg, node, child, paren); + CAvl_rebalance(o, arg, paren, side, -1); + } else { + CAvl_replace_subtree_fix_assoc(o, arg, node, child, paren); + } + + CAvl_assert_tree(o, arg); +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_NOKEYS + +static CAvlRef CAvl_Lookup (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + while (1) { + // compare + int comp = CAvl_compare_key_entry(arg, key, c); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (CAvl_link(c)[side] == CAvl_nulllink()) { + return c; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } +} + +static CAvlRef CAvl_LookupExact (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + while (1) { + // compare + int comp = CAvl_compare_key_entry(arg, key, c); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (CAvl_link(c)[side] == CAvl_nulllink()) { + return CAvl_nullref(); + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } +} + +static CAvlRef CAvl_GetFirstGreater (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) >= 0) { + c = CAvl_GetNext(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) < 0); + + return c; +} + +static CAvlRef CAvl_GetLastLesser (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) <= 0) { + c = CAvl_GetPrev(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) > 0); + + return c; +} + +static CAvlRef CAvl_GetFirstGreaterEqual (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) > 0) { + c = CAvl_GetNext(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) <= 0); + + return c; +} + +static CAvlRef CAvl_GetLastLesserEqual (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) < 0) { + c = CAvl_GetPrev(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) >= 0); + + return c; +} + +#endif + +static CAvlRef CAvl_GetFirst (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + return CAvl_subtree_min(arg, CAvlDeref(arg, o->root)); +} + +static CAvlRef CAvl_GetLast (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + return CAvl_subtree_max(arg, CAvlDeref(arg, o->root)); +} + +static CAvlRef CAvl_GetNext (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[1] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[1]); + while (CAvl_link(node)[0] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[0]); + } + } else { + while (CAvl_parent(node) != CAvl_nulllink() && node.link == CAvl_link(CAvlDeref(arg, CAvl_parent(node)))[1]) { + node = CAvlDeref(arg, CAvl_parent(node)); + } + node = CAvlDeref(arg, CAvl_parent(node)); + } + + return node; +} + +static CAvlRef CAvl_GetPrev (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[0] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[0]); + while (CAvl_link(node)[1] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[1]); + } + } else { + while (CAvl_parent(node) != CAvl_nulllink() && node.link == CAvl_link(CAvlDeref(arg, CAvl_parent(node)))[0]) { + node = CAvlDeref(arg, CAvl_parent(node)); + } + node = CAvlDeref(arg, CAvl_parent(node)); + } + + return node; +} + +static int CAvl_IsEmpty (const CAvl *o) +{ + return o->root == CAvl_nulllink(); +} + +static void CAvl_Verify (const CAvl *o, CAvlArg arg) +{ + if (o->root != CAvl_nulllink()) { + CAvlRef root = CAvlDeref(arg, o->root); + ASSERT(CAvl_parent(root) == CAvl_nulllink()) + CAvl_verify_recurser(arg, root); + } +} + +#if CAVL_PARAM_FEATURE_COUNTS + +static CAvlCount CAvl_Count (const CAvl *o, CAvlArg arg) +{ + return (o->root != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, o->root)) : 0); +} + +static CAvlCount CAvl_IndexOf (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + CAvlCount index = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(node)[0])) : 0); + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + + for (CAvlRef c = node; paren.link != CAvl_nulllink(); c = paren, paren = CAvlDeref(arg, CAvl_parent(c))) { + if (c.link == CAvl_link(paren)[1]) { + ASSERT(CAvl_count(paren) > CAvl_count(c)) + ASSERT(CAvl_count(paren) - CAvl_count(c) <= CAVL_PARAM_VALUE_COUNT_MAX - index) + index += CAvl_count(paren) - CAvl_count(c); + } + } + + return index; +} + +static CAvlRef CAvl_GetAt (const CAvl *o, CAvlArg arg, CAvlCount index) +{ + if (index >= CAvl_Count(o, arg)) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + + while (1) { + ASSERT(c.link != CAvl_nulllink()) + ASSERT(index < CAvl_count(c)) + + CAvlCount left_count = (CAvl_link(c)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(c)[0])) : 0); + + if (index == left_count) { + return c; + } + + if (index < left_count) { + c = CAvlDeref(arg, CAvl_link(c)[0]); + } else { + c = CAvlDeref(arg, CAvl_link(c)[1]); + index -= left_count + 1; + } + } +} + +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + +static CAvlAssoc CAvl_AssocSum (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAVL_PARAM_VALUE_ASSOC_ZERO; + } + CAvlRef root = CAvlDeref(arg, o->root); + return CAvl_assoc(root); +} + +static CAvlAssoc CAvl_ExclusiveAssocPrefixSum (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + CAvlAssoc sum = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[0])) : CAVL_PARAM_VALUE_ASSOC_ZERO); + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + + for (CAvlRef c = node; paren.link != CAvl_nulllink(); c = paren, paren = CAvlDeref(arg, CAvl_parent(c))) { + if (c.link == CAvl_link(paren)[1]) { + CAvlAssoc c_val = CAVL_PARAM_FUN_ASSOC_VALUE(arg, paren); + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, c_val, sum); + if (CAvl_link(paren)[0] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, CAvl_assoc(CAvlDeref(arg, CAvl_link(paren)[0])), sum); + } + } + } + + return sum; +} + +static CAvlRef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual (const CAvl *o, CAvlArg arg, CAvlAssoc sum, int (*sum_less) (void *, CAvlAssoc, CAvlAssoc), void *user) +{ + CAvlRef result = CAvl_nullref(); + CAvlRef c = CAvlDeref(arg, o->root); + CAvlAssoc sum_offset = CAVL_PARAM_VALUE_ASSOC_ZERO; + + while (c.link != CAvl_nulllink()) { + CAvlAssoc left_sum = (CAvl_link(c)[0] != CAvl_nulllink() ? CAvl_assoc(CAvlDeref(arg, CAvl_link(c)[0])) : CAVL_PARAM_VALUE_ASSOC_ZERO); + CAvlAssoc c_prefixsum = CAVL_PARAM_FUN_ASSOC_OPER(arg, sum_offset, left_sum); + + if (sum_less(user, sum, c_prefixsum)) { + c = CAvlDeref(arg, CAvl_link(c)[0]); + } else { + result = c; + CAvlAssoc c_val = CAVL_PARAM_FUN_ASSOC_VALUE(arg, c); + sum_offset = CAVL_PARAM_FUN_ASSOC_OPER(arg, c_prefixsum, c_val); + c = CAvlDeref(arg, CAvl_link(c)[1]); + } + } + + return result; +} + +#endif + +#include "CAvl_footer.h" diff --git a/service/src/main/jni/badvpn/structure/ChunkBuffer2.h b/service/src/main/jni/badvpn/structure/ChunkBuffer2.h new file mode 100644 index 0000000..98073ad --- /dev/null +++ b/service/src/main/jni/badvpn/structure/ChunkBuffer2.h @@ -0,0 +1,317 @@ +/** + * @file ChunkBuffer2.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Circular packet buffer + */ + +#ifndef BADVPN_STRUCTURE_CHUNKBUFFER2_H +#define BADVPN_STRUCTURE_CHUNKBUFFER2_H + +#include +#include +#include + +#include +#include + +#ifndef NDEBUG +#define CHUNKBUFFER2_ASSERT_BUFFER(_buf) _ChunkBuffer2_assert_buffer(_buf); +#define CHUNKBUFFER2_ASSERT_IO(_buf) _ChunkBuffer2_assert_io(_buf); +#else +#define CHUNKBUFFER2_ASSERT_BUFFER(_buf) +#define CHUNKBUFFER2_ASSERT_IO(_buf) +#endif + +struct ChunkBuffer2_block { + int len; +}; + +typedef struct { + struct ChunkBuffer2_block *buffer; + int size; + int wrap; + int start; + int used; + int mtu; + uint8_t *input_dest; + int input_avail; + uint8_t *output_dest; + int output_avail; +} ChunkBuffer2; + +// calculates a buffer size needed to hold at least 'num' packets long at least 'chunk_len' +static int ChunkBuffer2_calc_blocks (int chunk_len, int num); + +// initialize +static void ChunkBuffer2_Init (ChunkBuffer2 *buf, struct ChunkBuffer2_block *buffer, int blocks, int mtu); + +// submit a packet written to the buffer +static void ChunkBuffer2_SubmitPacket (ChunkBuffer2 *buf, int len); + +// remove the first packet +static void ChunkBuffer2_ConsumePacket (ChunkBuffer2 *buf); + +static int _ChunkBuffer2_end (ChunkBuffer2 *buf) +{ + if (buf->used >= buf->wrap - buf->start) { + return (buf->used - (buf->wrap - buf->start)); + } else { + return (buf->start + buf->used); + } +} + +#ifndef NDEBUG + +static void _ChunkBuffer2_assert_buffer (ChunkBuffer2 *buf) +{ + ASSERT(buf->size > 0) + ASSERT(buf->wrap > 0) + ASSERT(buf->wrap <= buf->size) + ASSERT(buf->start >= 0) + ASSERT(buf->start < buf->wrap) + ASSERT(buf->used >= 0) + ASSERT(buf->used <= buf->wrap) + ASSERT(buf->wrap == buf->size || buf->used >= buf->wrap - buf->start) + ASSERT(buf->mtu >= 0) +} + +static void _ChunkBuffer2_assert_io (ChunkBuffer2 *buf) +{ + // check input + + int end = _ChunkBuffer2_end(buf); + + if (buf->size - end - 1 < buf->mtu) { + // it will never be possible to write a MTU long packet here + ASSERT(!buf->input_dest) + ASSERT(buf->input_avail == -1) + } else { + // calculate number of free blocks + int free; + if (buf->used >= buf->wrap - buf->start) { + free = buf->start - end; + } else { + free = buf->size - end; + } + + if (free > 0) { + // got space at least for a header. More space will become available as packets are + // read from the buffer, up to MTU. + ASSERT(buf->input_dest == (uint8_t *)&buf->buffer[end + 1]) + ASSERT(buf->input_avail == (free - 1) * sizeof(struct ChunkBuffer2_block)) + } else { + // no space + ASSERT(!buf->input_dest) + ASSERT(buf->input_avail == -1) + } + } + + // check output + + if (buf->used > 0) { + int datalen = buf->buffer[buf->start].len; + ASSERT(datalen >= 0) + int blocklen = bdivide_up(datalen, sizeof(struct ChunkBuffer2_block)); + ASSERT(blocklen <= buf->used - 1) + ASSERT(blocklen <= buf->wrap - buf->start - 1) + ASSERT(buf->output_dest == (uint8_t *)&buf->buffer[buf->start + 1]) + ASSERT(buf->output_avail == datalen) + } else { + ASSERT(!buf->output_dest) + ASSERT(buf->output_avail == -1) + } +} + +#endif + +static void _ChunkBuffer2_update_input (ChunkBuffer2 *buf) +{ + int end = _ChunkBuffer2_end(buf); + + if (buf->size - end - 1 < buf->mtu) { + // it will never be possible to write a MTU long packet here + buf->input_dest = NULL; + buf->input_avail = -1; + return; + } + + // calculate number of free blocks + int free; + if (buf->used >= buf->wrap - buf->start) { + free = buf->start - end; + } else { + free = buf->size - end; + } + + if (free > 0) { + // got space at least for a header. More space will become available as packets are + // read from the buffer, up to MTU. + buf->input_dest = (uint8_t *)&buf->buffer[end + 1]; + buf->input_avail = (free - 1) * sizeof(struct ChunkBuffer2_block); + } else { + // no space + buf->input_dest = NULL; + buf->input_avail = -1; + } +} + +static void _ChunkBuffer2_update_output (ChunkBuffer2 *buf) +{ + if (buf->used > 0) { + int datalen = buf->buffer[buf->start].len; + ASSERT(datalen >= 0) +#ifndef NDEBUG + int blocklen = bdivide_up(datalen, sizeof(struct ChunkBuffer2_block)); + ASSERT(blocklen <= buf->used - 1) + ASSERT(blocklen <= buf->wrap - buf->start - 1) +#endif + buf->output_dest = (uint8_t *)&buf->buffer[buf->start + 1]; + buf->output_avail = datalen; + } else { + buf->output_dest = NULL; + buf->output_avail = -1; + } +} + +int ChunkBuffer2_calc_blocks (int chunk_len, int num) +{ + int chunk_data_blocks = bdivide_up(chunk_len, sizeof(struct ChunkBuffer2_block)); + + if (chunk_data_blocks > INT_MAX - 1) { + return -1; + } + int chunk_blocks = 1 + chunk_data_blocks; + + if (num > INT_MAX - 1) { + return -1; + } + int num_chunks = num + 1; + + if (chunk_blocks > INT_MAX / num_chunks) { + return -1; + } + int blocks = chunk_blocks * num_chunks; + + return blocks; +} + +void ChunkBuffer2_Init (ChunkBuffer2 *buf, struct ChunkBuffer2_block *buffer, int blocks, int mtu) +{ + ASSERT(blocks > 0) + ASSERT(mtu >= 0) + + buf->buffer = buffer; + buf->size = blocks; + buf->wrap = blocks; + buf->start = 0; + buf->used = 0; + buf->mtu = bdivide_up(mtu, sizeof(struct ChunkBuffer2_block)); + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + _ChunkBuffer2_update_input(buf); + _ChunkBuffer2_update_output(buf); + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +void ChunkBuffer2_SubmitPacket (ChunkBuffer2 *buf, int len) +{ + ASSERT(buf->input_dest) + ASSERT(len >= 0) + ASSERT(len <= buf->input_avail) + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + CHUNKBUFFER2_ASSERT_IO(buf) + + int end = _ChunkBuffer2_end(buf); + int blocklen = bdivide_up(len, sizeof(struct ChunkBuffer2_block)); + + ASSERT(blocklen <= buf->size - end - 1) + ASSERT(buf->used < buf->wrap - buf->start || blocklen <= buf->start - end - 1) + + buf->buffer[end].len = len; + buf->used += 1 + blocklen; + + if (buf->used <= buf->wrap - buf->start && buf->mtu > buf->size - (end + 1 + blocklen) - 1) { + buf->wrap = end + 1 + blocklen; + } + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + // update input + _ChunkBuffer2_update_input(buf); + + // update output + if (buf->used == 1 + blocklen) { + _ChunkBuffer2_update_output(buf); + } + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +void ChunkBuffer2_ConsumePacket (ChunkBuffer2 *buf) +{ + ASSERT(buf->output_dest) + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + CHUNKBUFFER2_ASSERT_IO(buf) + + ASSERT(1 <= buf->wrap - buf->start) + ASSERT(1 <= buf->used) + + int blocklen = bdivide_up(buf->buffer[buf->start].len, sizeof(struct ChunkBuffer2_block)); + + ASSERT(blocklen <= buf->wrap - buf->start - 1) + ASSERT(blocklen <= buf->used - 1) + + int data_wrapped = (buf->used >= buf->wrap - buf->start); + + buf->start += 1 + blocklen; + buf->used -= 1 + blocklen; + if (buf->start == buf->wrap) { + buf->start = 0; + buf->wrap = buf->size; + } + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + // update input + if (data_wrapped) { + _ChunkBuffer2_update_input(buf); + } + + // update output + _ChunkBuffer2_update_output(buf); + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +#endif diff --git a/service/src/main/jni/badvpn/structure/LinkedList1.h b/service/src/main/jni/badvpn/structure/LinkedList1.h new file mode 100644 index 0000000..bdb7161 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/LinkedList1.h @@ -0,0 +1,275 @@ +/** + * @file LinkedList1.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Simple doubly linked list. + */ + +#ifndef BADVPN_STRUCTURE_LINKEDLIST1_H +#define BADVPN_STRUCTURE_LINKEDLIST1_H + +#include + +#include + +/** + * Linked list node. + */ +typedef struct LinkedList1Node_t +{ + struct LinkedList1Node_t *p; + struct LinkedList1Node_t *n; +} LinkedList1Node; + +/** + * Simple doubly linked list. + */ +typedef struct +{ + LinkedList1Node *first; + LinkedList1Node *last; +} LinkedList1; + +/** + * Initializes the linked list. + * + * @param list list to initialize + */ +static void LinkedList1_Init (LinkedList1 *list); + +/** + * Determines if the list is empty. + * + * @param list the list + * @return 1 if empty, 0 if not + */ +static int LinkedList1_IsEmpty (LinkedList1 *list); + +/** + * Returns the first node of the list. + * + * @param list the list + * @return first node of the list, or NULL if the list is empty + */ +static LinkedList1Node * LinkedList1_GetFirst (LinkedList1 *list); + +/** + * Returns the last node of the list. + * + * @param list the list + * @return last node of the list, or NULL if the list is empty + */ +static LinkedList1Node * LinkedList1_GetLast (LinkedList1 *list); + +/** + * Inserts a node to the beginning of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList1_Prepend (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts a node to the end of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList1_Append (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts a node before a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert before + */ +static void LinkedList1_InsertBefore (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target); + +/** + * Inserts a node after a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert after + */ +static void LinkedList1_InsertAfter (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target); + +/** + * Removes a node from the list. + * + * @param list the list + * @param node node to remove + */ +static void LinkedList1_Remove (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts the nodes in the list \a ins_list into this list, after the node \a target. + * If \a target is NULL, the nodes are inserted to the beginning. + * Note that because the first and last node in \a ins_list are modified + * (unless the list is empty), \a ins_list is invalidated and must no longer + * be used to access the inserted nodes. + */ +static void LinkedList1_InsertListAfter (LinkedList1 *list, LinkedList1 ins_list, LinkedList1Node *target); + +/** + * Returns the next node of a given node. + * + * @param node reference node + * @return next node, or NULL if none + */ +static LinkedList1Node * LinkedList1Node_Next (LinkedList1Node *node); + +/** + * Returns the previous node of a given node. + * + * @param node reference node + * @return previous node, or NULL if none + */ +static LinkedList1Node * LinkedList1Node_Prev (LinkedList1Node *node); + +void LinkedList1_Init (LinkedList1 *list) +{ + list->first = NULL; + list->last = NULL; +} + +int LinkedList1_IsEmpty (LinkedList1 *list) +{ + return (!list->first); +} + +LinkedList1Node * LinkedList1_GetFirst (LinkedList1 *list) +{ + return (list->first); +} + +LinkedList1Node * LinkedList1_GetLast (LinkedList1 *list) +{ + return (list->last); +} + +void LinkedList1_Prepend (LinkedList1 *list, LinkedList1Node *node) +{ + node->p = NULL; + node->n = list->first; + if (list->first) { + list->first->p = node; + } else { + list->last = node; + } + list->first = node; +} + +void LinkedList1_Append (LinkedList1 *list, LinkedList1Node *node) +{ + node->p = list->last; + node->n = NULL; + if (list->last) { + list->last->n = node; + } else { + list->first = node; + } + list->last = node; +} + +void LinkedList1_InsertBefore (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target) +{ + node->p = target->p; + node->n = target; + if (target->p) { + target->p->n = node; + } else { + list->first = node; + } + target->p = node; +} + +void LinkedList1_InsertAfter (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target) +{ + node->p = target; + node->n = target->n; + if (target->n) { + target->n->p = node; + } else { + list->last = node; + } + target->n = node; +} + +void LinkedList1_Remove (LinkedList1 *list, LinkedList1Node *node) +{ + // remove from list + if (node->p) { + node->p->n = node->n; + } else { + list->first = node->n; + } + if (node->n) { + node->n->p = node->p; + } else { + list->last = node->p; + } +} + +void LinkedList1_InsertListAfter (LinkedList1 *list, LinkedList1 ins_list, LinkedList1Node *target) +{ + if (!ins_list.first) { + return; + } + + LinkedList1Node *t_next = (target ? target->n : list->first); + + ins_list.first->p = target; + ins_list.last->n = t_next; + + if (target) { + target->n = ins_list.first; + } else { + list->first = ins_list.first; + } + + if (t_next) { + t_next->p = ins_list.last; + } else { + list->last = ins_list.last; + } +} + +LinkedList1Node * LinkedList1Node_Next (LinkedList1Node *node) +{ + return node->n; +} + +LinkedList1Node * LinkedList1Node_Prev (LinkedList1Node *node) +{ + return node->p; +} + +#endif diff --git a/service/src/main/jni/badvpn/structure/SAvl.h b/service/src/main/jni/badvpn/structure/SAvl.h new file mode 100644 index 0000000..6ea1399 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SAvl.h @@ -0,0 +1,40 @@ +/** + * @file SAvl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SAVL_H +#define BADVPN_SAVL_H + +#include +#include + +#include +#include +#include + +#endif diff --git a/service/src/main/jni/badvpn/structure/SAvl_decl.h b/service/src/main/jni/badvpn/structure/SAvl_decl.h new file mode 100644 index 0000000..8a13e49 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SAvl_decl.h @@ -0,0 +1,73 @@ +/** + * @file SAvl_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SAvl_header.h" + +typedef SAvlEntry *SAvl__TreeLink; + +#include "SAvl_tree.h" +#include + +typedef struct { + SAvl__Tree tree; +} SAvl; + +typedef struct { + SAvlEntry *child[2]; + SAvlEntry *parent; + int8_t balance; +#if SAVL_PARAM_FEATURE_COUNTS + SAvlCount count; +#endif +} SAvlNode; + +static void SAvl_Init (SAvl *o); +static int SAvl_Insert (SAvl *o, SAvlArg arg, SAvlEntry *entry, SAvlEntry **out_existing); +static void SAvl_Remove (SAvl *o, SAvlArg arg, SAvlEntry *entry); +#if !SAVL_PARAM_FEATURE_NOKEYS +static SAvlEntry * SAvl_Lookup (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_LookupExact (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetFirstGreater (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetLastLesser (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetFirstGreaterEqual (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetLastLesserEqual (const SAvl *o, SAvlArg arg, SAvlKey key); +#endif +static SAvlEntry * SAvl_GetFirst (const SAvl *o, SAvlArg arg); +static SAvlEntry * SAvl_GetLast (const SAvl *o, SAvlArg arg); +static SAvlEntry * SAvl_GetNext (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static SAvlEntry * SAvl_GetPrev (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static int SAvl_IsEmpty (const SAvl *o); +static void SAvl_Verify (const SAvl *o, SAvlArg arg); +#if SAVL_PARAM_FEATURE_COUNTS +static SAvlCount SAvl_Count (const SAvl *o, SAvlArg arg); +static SAvlCount SAvl_IndexOf (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static SAvlEntry * SAvl_GetAt (const SAvl *o, SAvlArg arg, SAvlCount index); +#endif + +#include "SAvl_footer.h" diff --git a/service/src/main/jni/badvpn/structure/SAvl_footer.h b/service/src/main/jni/badvpn/structure/SAvl_footer.h new file mode 100644 index 0000000..ad90e40 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SAvl_footer.h @@ -0,0 +1,89 @@ +/** + * @file SAvl_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef SAVL_PARAM_NAME +#undef SAVL_PARAM_FEATURE_COUNTS +#undef SAVL_PARAM_FEATURE_NOKEYS +#undef SAVL_PARAM_TYPE_ENTRY +#undef SAVL_PARAM_TYPE_KEY +#undef SAVL_PARAM_TYPE_ARG +#undef SAVL_PARAM_TYPE_COUNT +#undef SAVL_PARAM_VALUE_COUNT_MAX +#undef SAVL_PARAM_FUN_COMPARE_ENTRIES +#undef SAVL_PARAM_FUN_COMPARE_KEY_ENTRY +#undef SAVL_PARAM_MEMBER_NODE + +#undef SAvl +#undef SAvlEntry +#undef SAvlArg +#undef SAvlKey +#undef SAvlCount +#undef SAvlNode + +#undef SAvl_Init +#undef SAvl_Insert +#undef SAvl_Remove +#undef SAvl_Lookup +#undef SAvl_LookupExact +#undef SAvl_GetFirstGreater +#undef SAvl_GetLastLesser +#undef SAvl_GetFirstGreaterEqual +#undef SAvl_GetLastLesserEqual +#undef SAvl_GetFirst +#undef SAvl_GetLast +#undef SAvl_GetNext +#undef SAvl_GetPrev +#undef SAvl_IsEmpty +#undef SAvl_Verify +#undef SAvl_Count +#undef SAvl_IndexOf +#undef SAvl_GetAt + +#undef SAvl__Tree +#undef SAvl__TreeRef +#undef SAvl__TreeLink +#undef SAvl__TreeDeref +#undef SAvl__Tree_Init +#undef SAvl__Tree_Insert +#undef SAvl__Tree_Remove +#undef SAvl__Tree_Lookup +#undef SAvl__Tree_LookupExact +#undef SAvl__Tree_GetFirstGreater +#undef SAvl__Tree_GetLastLesser +#undef SAvl__Tree_GetFirstGreaterEqual +#undef SAvl__Tree_GetLastLesserEqual +#undef SAvl__Tree_GetFirst +#undef SAvl__Tree_GetLast +#undef SAvl__Tree_GetNext +#undef SAvl__Tree_GetPrev +#undef SAvl__Tree_IsEmpty +#undef SAvl__Tree_Verify +#undef SAvl__Tree_Count +#undef SAvl__Tree_IndexOf +#undef SAvl__Tree_GetAt diff --git a/service/src/main/jni/badvpn/structure/SAvl_header.h b/service/src/main/jni/badvpn/structure/SAvl_header.h new file mode 100644 index 0000000..5d7d9b1 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SAvl_header.h @@ -0,0 +1,93 @@ +/** + * @file SAvl_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs. All types must be typedef names unless stated otherwise. +// SAVL_PARAM_NAME - name of this data structure +// SAVL_PARAM_FEATURE_COUNTS - whether to keep count information (0 or 1) +// SAVL_PARAM_FEATURE_NOKEYS - define to 1 if there is no need for a lookup operation +// SAVL_PARAM_TYPE_ENTRY - type of entry. This can also be a struct (e.g.: struct Foo). +// SAVL_PARAM_TYPE_KEY - type of key (ignored if SAVL_PARAM_FEATURE_NOKEYS) +// SAVL_PARAM_TYPE_ARG - type of argument pass through to callbacks +// SAVL_PARAM_TYPE_COUNT - type of count (only if SAVL_PARAM_FEATURE_COUNTS) +// SAVL_PARAM_VALUE_COUNT_MAX - maximum value of count (of type SAVL_PARAM_TYPE_COUNT) (only if SAVL_PARAM_FEATURE_COUNTS) +// SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) - compare two entries; returns -1/0/1 +// SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) - compare key and entry; returns -1/0/1 (ignored if SAVL_PARAM_FEATURE_NOKEYS) +// SAVL_PARAM_MEMBER_NODE - node member in entry + +// types +#define SAvl SAVL_PARAM_NAME +#define SAvlEntry SAVL_PARAM_TYPE_ENTRY +#define SAvlArg SAVL_PARAM_TYPE_ARG +#define SAvlKey SAVL_PARAM_TYPE_KEY +#define SAvlCount SAVL_PARAM_TYPE_COUNT +#define SAvlNode MERGE(SAvl, Node) + +// public functions +#define SAvl_Init MERGE(SAvl, _Init) +#define SAvl_Insert MERGE(SAvl, _Insert) +#define SAvl_Remove MERGE(SAvl, _Remove) +#define SAvl_Lookup MERGE(SAvl, _Lookup) +#define SAvl_LookupExact MERGE(SAvl, _LookupExact) +#define SAvl_GetFirstGreater MERGE(SAvl, _GetFirstGreater) +#define SAvl_GetLastLesser MERGE(SAvl, _GetLastLesser) +#define SAvl_GetFirstGreaterEqual MERGE(SAvl, _GetFirstGreaterEqual) +#define SAvl_GetLastLesserEqual MERGE(SAvl, _GetLastLesserEqual) +#define SAvl_GetFirst MERGE(SAvl, _GetFirst) +#define SAvl_GetLast MERGE(SAvl, _GetLast) +#define SAvl_GetNext MERGE(SAvl, _GetNext) +#define SAvl_GetPrev MERGE(SAvl, _GetPrev) +#define SAvl_IsEmpty MERGE(SAvl, _IsEmpty) +#define SAvl_Verify MERGE(SAvl, _Verify) +#define SAvl_Count MERGE(SAvl, _Count) +#define SAvl_IndexOf MERGE(SAvl, _IndexOf) +#define SAvl_GetAt MERGE(SAvl, _GetAt) + +// internal stuff +#define SAvl__Tree MERGE(SAvl, __Tree) +#define SAvl__TreeRef MERGE(SAvl, __TreeRef) +#define SAvl__TreeLink MERGE(SAvl, __TreeLink) +#define SAvl__TreeDeref MERGE(SAvl, __TreeDeref) +#define SAvl__Tree_Init MERGE(SAvl, __Tree_Init) +#define SAvl__Tree_Insert MERGE(SAvl, __Tree_Insert) +#define SAvl__Tree_Remove MERGE(SAvl, __Tree_Remove) +#define SAvl__Tree_Lookup MERGE(SAvl, __Tree_Lookup) +#define SAvl__Tree_LookupExact MERGE(SAvl, __Tree_LookupExact) +#define SAvl__Tree_GetFirstGreater MERGE(SAvl, __Tree_GetFirstGreater) +#define SAvl__Tree_GetLastLesser MERGE(SAvl, __Tree_GetLastLesser) +#define SAvl__Tree_GetFirstGreaterEqual MERGE(SAvl, __Tree_GetFirstGreaterEqual) +#define SAvl__Tree_GetLastLesserEqual MERGE(SAvl, __Tree_GetLastLesserEqual) +#define SAvl__Tree_GetFirst MERGE(SAvl, __Tree_GetFirst) +#define SAvl__Tree_GetLast MERGE(SAvl, __Tree_GetLast) +#define SAvl__Tree_GetNext MERGE(SAvl, __Tree_GetNext) +#define SAvl__Tree_GetPrev MERGE(SAvl, __Tree_GetPrev) +#define SAvl__Tree_IsEmpty MERGE(SAvl, __Tree_IsEmpty) +#define SAvl__Tree_Verify MERGE(SAvl, __Tree_Verify) +#define SAvl__Tree_Count MERGE(SAvl, __Tree_Count) +#define SAvl__Tree_IndexOf MERGE(SAvl, __Tree_IndexOf) +#define SAvl__Tree_GetAt MERGE(SAvl, __Tree_GetAt) diff --git a/service/src/main/jni/badvpn/structure/SAvl_impl.h b/service/src/main/jni/badvpn/structure/SAvl_impl.h new file mode 100644 index 0000000..0d3973a --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SAvl_impl.h @@ -0,0 +1,164 @@ +/** + * @file SAvl_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SAvl_header.h" + +#include "SAvl_tree.h" +#include + +static void SAvl_Init (SAvl *o) +{ + SAvl__Tree_Init(&o->tree); +} + +static int SAvl_Insert (SAvl *o, SAvlArg arg, SAvlEntry *entry, SAvlEntry **out_existing) +{ + ASSERT(entry) +#if SAVL_PARAM_FEATURE_COUNTS + ASSERT(SAvl_Count(o, arg) < SAVL_PARAM_VALUE_COUNT_MAX) +#endif + + SAvl__TreeRef out_ref; + int res = SAvl__Tree_Insert(&o->tree, arg, SAvl__TreeDeref(arg, entry), &out_ref); + + if (out_existing) { + *out_existing = out_ref.link; + } + + return res; +} + +static void SAvl_Remove (SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__Tree_Remove(&o->tree, arg, SAvl__TreeDeref(arg, entry)); +} + +#if !SAVL_PARAM_FEATURE_NOKEYS + +static SAvlEntry * SAvl_Lookup (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_Lookup(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_LookupExact (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_LookupExact(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetFirstGreater (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirstGreater(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetLastLesser (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLastLesser(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetFirstGreaterEqual (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirstGreaterEqual(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetLastLesserEqual (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLastLesserEqual(&o->tree, arg, key); + return ref.link; +} + +#endif + +static SAvlEntry * SAvl_GetFirst (const SAvl *o, SAvlArg arg) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirst(&o->tree, arg); + return ref.link; +} + +static SAvlEntry * SAvl_GetLast (const SAvl *o, SAvlArg arg) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLast(&o->tree, arg); + return ref.link; +} + +static SAvlEntry * SAvl_GetNext (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__TreeRef ref = SAvl__Tree_GetNext(&o->tree, arg, SAvl__TreeDeref(arg, entry)); + return ref.link; +} + +static SAvlEntry * SAvl_GetPrev (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__TreeRef ref = SAvl__Tree_GetPrev(&o->tree, arg, SAvl__TreeDeref(arg, entry)); + return ref.link; +} + +static int SAvl_IsEmpty (const SAvl *o) +{ + return SAvl__Tree_IsEmpty(&o->tree); +} + +static void SAvl_Verify (const SAvl *o, SAvlArg arg) +{ + return SAvl__Tree_Verify(&o->tree, arg); +} + +#if SAVL_PARAM_FEATURE_COUNTS + +static SAvlCount SAvl_Count (const SAvl *o, SAvlArg arg) +{ + return SAvl__Tree_Count(&o->tree, arg); +} + +static SAvlCount SAvl_IndexOf (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + return SAvl__Tree_IndexOf(&o->tree, arg, SAvl__TreeDeref(arg, entry)); +} + +static SAvlEntry * SAvl_GetAt (const SAvl *o, SAvlArg arg, SAvlCount index) +{ + SAvl__TreeRef ref = SAvl__Tree_GetAt(&o->tree, arg, index); + return ref.link; +} + +#endif + +#include "SAvl_footer.h" diff --git a/service/src/main/jni/badvpn/structure/SAvl_tree.h b/service/src/main/jni/badvpn/structure/SAvl_tree.h new file mode 100644 index 0000000..5e5b18b --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SAvl_tree.h @@ -0,0 +1,18 @@ +#define CAVL_PARAM_NAME SAvl__Tree +#define CAVL_PARAM_FEATURE_COUNTS SAVL_PARAM_FEATURE_COUNTS +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS SAVL_PARAM_FEATURE_NOKEYS +#define CAVL_PARAM_TYPE_ENTRY SAvlEntry +#define CAVL_PARAM_TYPE_LINK SAvl__TreeLink +#define CAVL_PARAM_TYPE_KEY SAvlKey +#define CAVL_PARAM_TYPE_ARG SAvlArg +#define CAVL_PARAM_TYPE_COUNT SAvlCount +#define CAVL_PARAM_VALUE_COUNT_MAX SAVL_PARAM_VALUE_COUNT_MAX +#define CAVL_PARAM_VALUE_NULL ((SAvl__TreeLink)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) SAVL_PARAM_FUN_COMPARE_ENTRIES((arg), (entry1).link, (entry2).link) +#define CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) SAVL_PARAM_FUN_COMPARE_KEY_ENTRY((arg), (key1), (entry2).link) +#define CAVL_PARAM_MEMBER_CHILD SAVL_PARAM_MEMBER_NODE . child +#define CAVL_PARAM_MEMBER_BALANCE SAVL_PARAM_MEMBER_NODE . balance +#define CAVL_PARAM_MEMBER_PARENT SAVL_PARAM_MEMBER_NODE . parent +#define CAVL_PARAM_MEMBER_COUNT SAVL_PARAM_MEMBER_NODE . count diff --git a/service/src/main/jni/badvpn/structure/SLinkedList.h b/service/src/main/jni/badvpn/structure/SLinkedList.h new file mode 100644 index 0000000..1edb016 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SLinkedList.h @@ -0,0 +1,38 @@ +/** + * @file SLinkedList.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SLINKEDLIST_H +#define BADVPN_SLINKEDLIST_H + +#include + +#include +#include + +#endif diff --git a/service/src/main/jni/badvpn/structure/SLinkedList_decl.h b/service/src/main/jni/badvpn/structure/SLinkedList_decl.h new file mode 100644 index 0000000..3b366d6 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SLinkedList_decl.h @@ -0,0 +1,61 @@ +/** + * @file SLinkedList_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SLinkedList_header.h" + +typedef struct { + SLinkedListEntry *first; +#if SLINKEDLIST_PARAM_FEATURE_LAST + SLinkedListEntry *last; +#endif +} SLinkedList; + +typedef struct { + SLinkedListEntry *prev; + SLinkedListEntry *next; +} SLinkedListNode; + +static SLinkedListEntry * SLinkedListNext (SLinkedListEntry *entry); +static SLinkedListEntry * SLinkedListPrev (SLinkedListEntry *entry); + +static void SLinkedList_Init (SLinkedList *o); +static void SLinkedList_Prepend (SLinkedList *o, SLinkedListEntry *entry); +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_Append (SLinkedList *o, SLinkedListEntry *entry); +#endif +static void SLinkedList_InsertBefore (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *before_entry); +static void SLinkedList_InsertAfter (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *after_entry); +static void SLinkedList_Remove (SLinkedList *o, SLinkedListEntry *entry); +static SLinkedListEntry * SLinkedList_First (const SLinkedList *o); +#if SLINKEDLIST_PARAM_FEATURE_LAST +static SLinkedListEntry * SLinkedList_Last (const SLinkedList *o); +#endif +static int SLinkedList_IsEmpty (const SLinkedList *o); + +#include "SLinkedList_footer.h" diff --git a/service/src/main/jni/badvpn/structure/SLinkedList_footer.h b/service/src/main/jni/badvpn/structure/SLinkedList_footer.h new file mode 100644 index 0000000..014b87f --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SLinkedList_footer.h @@ -0,0 +1,53 @@ +/** + * @file SLinkedList_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef SLINKEDLIST_PARAM_NAME +#undef SLINKEDLIST_PARAM_FEATURE_LAST +#undef SLINKEDLIST_PARAM_TYPE_ENTRY +#undef SLINKEDLIST_PARAM_MEMBER_NODE + +#undef SLinkedList +#undef SLinkedListEntry +#undef SLinkedListNode + +#undef SLinkedListNext +#undef SLinkedListPrev + +#undef SLinkedList_Init +#undef SLinkedList_Prepend +#undef SLinkedList_Append +#undef SLinkedList_InsertBefore +#undef SLinkedList_InsertAfter +#undef SLinkedList_Remove +#undef SLinkedList_First +#undef SLinkedList_Last +#undef SLinkedList_IsEmpty + +#undef SLinkedList_prev +#undef SLinkedList_next diff --git a/service/src/main/jni/badvpn/structure/SLinkedList_header.h b/service/src/main/jni/badvpn/structure/SLinkedList_header.h new file mode 100644 index 0000000..0662d7d --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SLinkedList_header.h @@ -0,0 +1,58 @@ +/** + * @file SLinkedList_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// SLINKEDLIST_PARAM_NAME - name of this data structure +// SLINKEDLIST_PARAM_FEATURE_LAST - whether to keep track of the last entry in the list (0/1) +// SLINKEDLIST_PARAM_TYPE_ENTRY - type of entry +// SLINKEDLIST_PARAM_MEMBER_NODE - name of the node member in entry + +// types +#define SLinkedList SLINKEDLIST_PARAM_NAME +#define SLinkedListEntry SLINKEDLIST_PARAM_TYPE_ENTRY +#define SLinkedListNode MERGE(SLinkedList, Node) + +// non-object public functions +#define SLinkedListNext MERGE(SLinkedList, Next) +#define SLinkedListPrev MERGE(SLinkedList, Prev) + +// public functions +#define SLinkedList_Init MERGE(SLinkedList, _Init) +#define SLinkedList_Prepend MERGE(SLinkedList, _Prepend) +#define SLinkedList_Append MERGE(SLinkedList, _Append) +#define SLinkedList_InsertBefore MERGE(SLinkedList, _InsertBefore) +#define SLinkedList_InsertAfter MERGE(SLinkedList, _InsertAfter) +#define SLinkedList_Remove MERGE(SLinkedList, _Remove) +#define SLinkedList_First MERGE(SLinkedList, _First) +#define SLinkedList_Last MERGE(SLinkedList, _Last) +#define SLinkedList_IsEmpty MERGE(SLinkedList, _IsEmpty) + +// private things +#define SLinkedList_prev(entry) ((entry)->SLINKEDLIST_PARAM_MEMBER_NODE . prev) +#define SLinkedList_next(entry) ((entry)->SLINKEDLIST_PARAM_MEMBER_NODE . next) diff --git a/service/src/main/jni/badvpn/structure/SLinkedList_impl.h b/service/src/main/jni/badvpn/structure/SLinkedList_impl.h new file mode 100644 index 0000000..c69d196 --- /dev/null +++ b/service/src/main/jni/badvpn/structure/SLinkedList_impl.h @@ -0,0 +1,151 @@ +/** + * @file SLinkedList_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SLinkedList_header.h" + +static SLinkedListEntry * SLinkedListNext (SLinkedListEntry *entry) +{ + ASSERT(entry) + + return SLinkedList_next(entry); +} + +static SLinkedListEntry * SLinkedListPrev (SLinkedListEntry *entry) +{ + ASSERT(entry) + + return SLinkedList_prev(entry); +} + +static void SLinkedList_Init (SLinkedList *o) +{ + o->first = NULL; +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = NULL; +#endif +} + +static void SLinkedList_Prepend (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + SLinkedList_prev(entry) = NULL; + SLinkedList_next(entry) = o->first; + if (o->first) { + SLinkedList_prev(o->first) = entry; + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = entry; +#endif + } + o->first = entry; +} + +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_Append (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + SLinkedList_next(entry) = NULL; + SLinkedList_prev(entry) = o->last; + if (o->last) { + SLinkedList_next(o->last) = entry; + } else { + o->first = entry; + } + o->last = entry; +} +#endif + +static void SLinkedList_InsertBefore (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *before_entry) +{ + ASSERT(entry) + ASSERT(before_entry) + + SLinkedList_prev(entry) = SLinkedList_prev(before_entry); + SLinkedList_next(entry) = before_entry; + if (SLinkedList_prev(before_entry)) { + SLinkedList_next(SLinkedList_prev(before_entry)) = entry; + } else { + o->first = entry; + } + SLinkedList_prev(before_entry) = entry; +} + +static void SLinkedList_InsertAfter (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *after_entry) +{ + ASSERT(entry) + ASSERT(after_entry) + + SLinkedList_next(entry) = SLinkedList_next(after_entry); + SLinkedList_prev(entry) = after_entry; + if (SLinkedList_next(after_entry)) { + SLinkedList_prev(SLinkedList_next(after_entry)) = entry; + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = entry; +#endif + } + SLinkedList_next(after_entry) = entry; +} + +static void SLinkedList_Remove (SLinkedList *o, SLinkedListEntry *entry) +{ + if (SLinkedList_prev(entry)) { + SLinkedList_next(SLinkedList_prev(entry)) = SLinkedList_next(entry); + } else { + o->first = SLinkedList_next(entry); + } + if (SLinkedList_next(entry)) { + SLinkedList_prev(SLinkedList_next(entry)) = SLinkedList_prev(entry); + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = SLinkedList_prev(entry); +#endif + } +} + +static SLinkedListEntry * SLinkedList_First (const SLinkedList *o) +{ + return o->first; +} + +#if SLINKEDLIST_PARAM_FEATURE_LAST +static SLinkedListEntry * SLinkedList_Last (const SLinkedList *o) +{ + return o->last; +} +#endif + +static int SLinkedList_IsEmpty (const SLinkedList *o) +{ + return !o->first; +} + +#include "SLinkedList_footer.h" diff --git a/service/src/main/jni/badvpn/system/BAddr.h b/service/src/main/jni/badvpn/system/BAddr.h new file mode 100644 index 0000000..a5f3f10 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BAddr.h @@ -0,0 +1,808 @@ +/** + * @file BAddr.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Network address abstractions. + */ + +#ifndef BADVPN_SYSTEM_BADDR_H +#define BADVPN_SYSTEM_BADDR_H + +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define BADDR_TYPE_NONE 0 +#define BADDR_TYPE_IPV4 1 +#define BADDR_TYPE_IPV6 2 +#ifdef BADVPN_LINUX + #define BADDR_TYPE_PACKET 5 +#endif + +#define BADDR_MAX_ADDR_LEN 128 + +#define BIPADDR_MAX_PRINT_LEN 40 +#define BADDR_MAX_PRINT_LEN 120 + +#define BADDR_PACKET_HEADER_TYPE_ETHERNET 1 + +#define BADDR_PACKET_PACKET_TYPE_HOST 1 +#define BADDR_PACKET_PACKET_TYPE_BROADCAST 2 +#define BADDR_PACKET_PACKET_TYPE_MULTICAST 3 +#define BADDR_PACKET_PACKET_TYPE_OTHERHOST 4 +#define BADDR_PACKET_PACKET_TYPE_OUTGOING 5 + +typedef struct { + int type; + union { + uint32_t ipv4; + uint8_t ipv6[16]; + }; +} BIPAddr; + +static void BIPAddr_InitInvalid (BIPAddr *addr); + +static void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip); + +static void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip); + +static void BIPAddr_Assert (BIPAddr *addr); + +static int BIPAddr_IsInvalid (BIPAddr *addr); + +static int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) WARN_UNUSED; + +static int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2); + +/** + * Converts an IP address to human readable form. + * + * @param addr IP address to convert + * @param out destination buffer. Must be at least BIPADDR_MAX_PRINT_LEN characters long. + */ +static void BIPAddr_Print (BIPAddr *addr, char *out); + +/** + * Socket address - IP address and transport protocol port number + */ +typedef struct { + int type; + union { + struct { + uint32_t ip; + uint16_t port; + } ipv4; + struct { + uint8_t ip[16]; + uint16_t port; + } ipv6; + struct { + uint16_t phys_proto; + int interface_index; + int header_type; + int packet_type; + uint8_t phys_addr[8]; + } packet; + }; +} BAddr; + +/** + * Makes an invalid address. + */ +static BAddr BAddr_MakeNone (void); + +/** + * Makes an IPv4 address. + * + * @param ip IP address in network byte order + * @param port port number in network byte order + */ +static BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port); + +/** + * Makes an IPv6 address. + * + * @param ip IP address (16 bytes) + * @param port port number in network byte order + */ +static BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port); + +/** + * Makes an address from a BIPAddr and port number. + * + * @param ipaddr the BIPAddr + * @param port port number in network byte order + */ +static BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port); + +/** + * Deprecated, use BAddr_MakeNone. + */ +static void BAddr_InitNone (BAddr *addr); + +/** + * Deprecated, use BAddr_MakeIPv4. + */ +static void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port); + +/** + * Deprecated, use BAddr_MakeIPv6. + */ +static void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port); + +/** + * Deprecated, use BAddr_MakeFromIpaddrAndPort. + */ +static void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port); + +/** + * Initializes a packet socket (data link layer) address. + * Only Ethernet addresses are supported. + * + * @param addr the object + * @param phys_proto identifier for the upper protocol, network byte order (EtherType) + * @param interface_index network interface index + * @param header_type data link layer header type. Must be BADDR_PACKET_HEADER_TYPE_ETHERNET. + * @param packet_type the manner in which packets are sent/received. Must be one of + * BADDR_PACKET_PACKET_TYPE_HOST, BADDR_PACKET_PACKET_TYPE_BROADCAST, + * BADDR_PACKET_PACKET_TYPE_MULTICAST, BADDR_PACKET_PACKET_TYPE_OTHERHOST, + * BADDR_PACKET_PACKET_TYPE_OUTGOING. + * @param phys_addr data link layer address (MAC address) + */ +static void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr); + +/** + * Does nothing. + * + * @param addr the object + */ +static void BAddr_Assert (BAddr *addr); + +/** + * Determines whether the address is an invalid address. + * + * @param addr the object + * @return 1 if invalid, 0 if invalid + **/ +static int BAddr_IsInvalid (BAddr *addr); + +/** + * Returns the port number in the address. + * + * @param addr the object + * Must be an IPv4 or IPv6 address. + * @return port number, in network byte order + */ +static uint16_t BAddr_GetPort (BAddr *addr); + +/** + * Returns the IP address in the address. + * + * @param addr the object + * @param ipaddr IP address will be returned here. If \a addr is not + * an IPv4 or IPv6 address, an invalid address will be + * returned. + */ +static void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr); + +/** + * Sets the port number in the address. + * + * @param addr the object + * Must be an IPv4 or IPv6 address. + * @param port port number, in network byte order + */ +static void BAddr_SetPort (BAddr *addr, uint16_t port); + +/** + * Converts an IP address to human readable form. + * + * @param addr address to convert + * @param out destination buffer. Must be at least BADDR_MAX_PRINT_LEN characters long. + */ +static void BAddr_Print (BAddr *addr, char *out); + +/** + * Resolves an address string. + * Format is "addr:port" for IPv4, "[addr]:port" for IPv6. + * addr is be a numeric address or a name. + * port is a numeric port number. + * + * @param addr output address + * @param name if not NULL, the name portion of the address will be + * stored here + * @param name_len if name is not NULL, the size of the name buffer + * @param noresolve only accept numeric addresses. Avoids blocking the caller. + * @return 1 on success, 0 on parse error + */ +static int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve) WARN_UNUSED; + +/** + * Resolves an address string. + * IPv4 input format is "a.b.c.d:p", where a.b.c.d is the IP address + * and d is the port number. + * IPv6 input format is "[addr]:p", where addr is an IPv6 addres in + * standard notation and p is the port number. + * + * @param addr output address + * @param name if not NULL, the name portion of the address will be + * stored here + * @param name_len if name is not NULL, the size of the name buffer + * @return 1 on success, 0 on parse error + */ +static int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len) WARN_UNUSED; + +static int BAddr_Compare (BAddr *addr1, BAddr *addr2); + +static int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2); + +void BIPAddr_InitInvalid (BIPAddr *addr) +{ + addr->type = BADDR_TYPE_NONE; +} + +void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip) +{ + addr->type = BADDR_TYPE_IPV4; + addr->ipv4 = ip; +} + +void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip) +{ + addr->type = BADDR_TYPE_IPV6; + memcpy(addr->ipv6, ip, 16); +} + +void BIPAddr_Assert (BIPAddr *addr) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + return; + default: + ASSERT(0); + } +} + +int BIPAddr_IsInvalid (BIPAddr *addr) +{ + BIPAddr_Assert(addr); + + return (addr->type == BADDR_TYPE_NONE); +} + +int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) +{ + int len = strlen(str); + + char *addr_start; + int addr_len; + + // determine address type + if (len >= 1 && str[0] == '[' && str[len - 1] == ']') { + addr->type = BADDR_TYPE_IPV6; + addr_start = str + 1; + addr_len = len - 2; + } else { + addr->type = BADDR_TYPE_IPV4; + addr_start = str; + addr_len = len; + } + + // copy + char addr_str[BADDR_MAX_ADDR_LEN + 1]; + if (addr_len > BADDR_MAX_ADDR_LEN) { + return 0; + } + memcpy(addr_str, addr_start, addr_len); + addr_str[addr_len] = '\0'; + + // initialize hints + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + switch (addr->type) { + case BADDR_TYPE_IPV6: + hints.ai_family = AF_INET6; + break; + case BADDR_TYPE_IPV4: + hints.ai_family = AF_INET; + break; + } + if (noresolve) { + hints.ai_flags |= AI_NUMERICHOST; + } + + // call getaddrinfo + struct addrinfo *addrs; + int res; + if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) { + return 0; + } + + // set address + switch (addr->type) { + case BADDR_TYPE_IPV6: + memcpy(addr->ipv6, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6)); + break; + case BADDR_TYPE_IPV4: + addr->ipv4 = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr; + break; + } + + freeaddrinfo(addrs); + + return 1; +} + +int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2) +{ + BIPAddr_Assert(addr1); + BIPAddr_Assert(addr2); + + if (addr1->type != addr2->type) { + return 0; + } + + switch (addr1->type) { + case BADDR_TYPE_NONE: + return 0; + case BADDR_TYPE_IPV4: + return (addr1->ipv4 == addr2->ipv4); + case BADDR_TYPE_IPV6: + return (!memcmp(addr1->ipv6, addr2->ipv6, sizeof(addr1->ipv6))); + default: + ASSERT(0) + return 0; + } +} + +uint16_t BAddr_GetPort (BAddr *addr) +{ + BAddr_Assert(addr); + ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6) + + switch (addr->type) { + case BADDR_TYPE_IPV4: + return addr->ipv4.port; + case BADDR_TYPE_IPV6: + return addr->ipv6.port; + default: + ASSERT(0) + return 0; + } +} + +void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr) +{ + BAddr_Assert(addr); + + switch (addr->type) { + case BADDR_TYPE_IPV4: + BIPAddr_InitIPv4(ipaddr, addr->ipv4.ip); + return; + case BADDR_TYPE_IPV6: + BIPAddr_InitIPv6(ipaddr, addr->ipv6.ip); + return; + default: + BIPAddr_InitInvalid(ipaddr); + } +} + +void BAddr_SetPort (BAddr *addr, uint16_t port) +{ + BAddr_Assert(addr); + ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6) + + switch (addr->type) { + case BADDR_TYPE_IPV4: + addr->ipv4.port = port; + break; + case BADDR_TYPE_IPV6: + addr->ipv6.port = port; + break; + default: + ASSERT(0); + } +} + +void BIPAddr_Print (BIPAddr *addr, char *out) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + sprintf(out, "(none)"); + break; + case BADDR_TYPE_IPV4: + sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, + *((uint8_t *)&addr->ipv4 + 0), + *((uint8_t *)&addr->ipv4 + 1), + *((uint8_t *)&addr->ipv4 + 2), + *((uint8_t *)&addr->ipv4 + 3) + ); + break; + case BADDR_TYPE_IPV6: { + const char *ptr = (const char *)addr->ipv6; + sprintf(out, + "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16":" + "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16, + badvpn_read_be16(ptr + 0), + badvpn_read_be16(ptr + 2), + badvpn_read_be16(ptr + 4), + badvpn_read_be16(ptr + 6), + badvpn_read_be16(ptr + 8), + badvpn_read_be16(ptr + 10), + badvpn_read_be16(ptr + 12), + badvpn_read_be16(ptr + 14) + ); + } break; + default: + ASSERT(0); + } +} + +BAddr BAddr_MakeNone (void) +{ + BAddr addr; + addr.type = BADDR_TYPE_NONE; + return addr; +} + +BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port) +{ + BAddr addr; + addr.type = BADDR_TYPE_IPV4; + addr.ipv4.ip = ip; + addr.ipv4.port = port; + return addr; +} + +BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port) +{ + BAddr addr; + addr.type = BADDR_TYPE_IPV6; + memcpy(addr.ipv6.ip, ip, 16); + addr.ipv6.port = port; + return addr; +} + +BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port) +{ + BIPAddr_Assert(&ipaddr); + + switch (ipaddr.type) { + case BADDR_TYPE_NONE: + return BAddr_MakeNone(); + case BADDR_TYPE_IPV4: + return BAddr_MakeIPv4(ipaddr.ipv4, port); + case BADDR_TYPE_IPV6: + return BAddr_MakeIPv6(ipaddr.ipv6, port); + default: + ASSERT(0); + return BAddr_MakeNone(); + } +} + +void BAddr_InitNone (BAddr *addr) +{ + *addr = BAddr_MakeNone(); +} + +void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port) +{ + *addr = BAddr_MakeIPv4(ip, port); +} + +void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port) +{ + *addr = BAddr_MakeIPv6(ip, port); +} + +void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port) +{ + BIPAddr_Assert(&ipaddr); + + *addr = BAddr_MakeFromIpaddrAndPort(ipaddr, port); +} + +#ifdef BADVPN_LINUX + +void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr) +{ + ASSERT(header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + ASSERT(packet_type == BADDR_PACKET_PACKET_TYPE_HOST || packet_type == BADDR_PACKET_PACKET_TYPE_BROADCAST || + packet_type == BADDR_PACKET_PACKET_TYPE_MULTICAST || packet_type == BADDR_PACKET_PACKET_TYPE_OTHERHOST || + packet_type == BADDR_PACKET_PACKET_TYPE_OUTGOING) + + addr->type = BADDR_TYPE_PACKET; + addr->packet.phys_proto = phys_proto; + addr->packet.interface_index = interface_index; + addr->packet.header_type = header_type; + addr->packet.packet_type = packet_type; + memcpy(addr->packet.phys_addr, phys_addr, 6); +} + +#endif + +void BAddr_Assert (BAddr *addr) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + #ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + #endif + return; + default: + ASSERT(0); + } +} + +int BAddr_IsInvalid (BAddr *addr) +{ + BAddr_Assert(addr); + + return (addr->type == BADDR_TYPE_NONE); +} + +void BAddr_Print (BAddr *addr, char *out) +{ + BAddr_Assert(addr); + + BIPAddr ipaddr; + + switch (addr->type) { + case BADDR_TYPE_NONE: + sprintf(out, "(none)"); + break; + case BADDR_TYPE_IPV4: + BIPAddr_InitIPv4(&ipaddr, addr->ipv4.ip); + BIPAddr_Print(&ipaddr, out); + sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv4.port)); + break; + case BADDR_TYPE_IPV6: + BIPAddr_InitIPv6(&ipaddr, addr->ipv6.ip); + BIPAddr_Print(&ipaddr, out); + sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv6.port)); + break; + #ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + ASSERT(addr->packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + sprintf(out, "proto=%"PRIu16",ifindex=%d,htype=eth,ptype=%d,addr=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8, + addr->packet.phys_proto, (int)addr->packet.interface_index, (int)addr->packet.packet_type, + addr->packet.phys_addr[0], addr->packet.phys_addr[1], addr->packet.phys_addr[2], + addr->packet.phys_addr[3], addr->packet.phys_addr[4], addr->packet.phys_addr[5]); + break; + #endif + default: + ASSERT(0); + } +} + +int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve) +{ + int len = strlen(str); + if (len < 1 || len > 1000) { + return 0; + } + + int addr_start; + int addr_len; + int port_start; + int port_len; + + // leading '[' indicates an IPv6 address + if (str[0] == '[') { + addr->type = BADDR_TYPE_IPV6; + // find ']' + int i=1; + while (i < len && str[i] != ']') i++; + if (i >= len) { + return 0; + } + addr_start = 1; + addr_len = i - addr_start; + // follows ':' and port number + if (i + 1 >= len || str[i + 1] != ':') { + return 0; + } + port_start = i + 2; + port_len = len - port_start; + } + // otherwise it's an IPv4 address + else { + addr->type = BADDR_TYPE_IPV4; + // find ':' + int i=0; + while (i < len && str[i] != ':') i++; + if (i >= len) { + return 0; + } + addr_start = 0; + addr_len = i - addr_start; + port_start = i + 1; + port_len = len - port_start; + } + + // copy address and port to zero-terminated buffers + + char addr_str[128]; + if (addr_len >= sizeof(addr_str)) { + return 0; + } + memcpy(addr_str, str + addr_start, addr_len); + addr_str[addr_len] = '\0'; + + char port_str[6]; + if (port_len >= sizeof(port_str)) { + return 0; + } + memcpy(port_str, str + port_start, port_len); + port_str[port_len] = '\0'; + + // parse port + char *err; + long int conv_res = strtol(port_str, &err, 10); + if (port_str[0] == '\0' || *err != '\0') { + return 0; + } + if (conv_res < 0 || conv_res > UINT16_MAX) { + return 0; + } + uint16_t port = conv_res; + port = hton16(port); + + // initialize hints + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + switch (addr->type) { + case BADDR_TYPE_IPV6: + hints.ai_family = AF_INET6; + break; + case BADDR_TYPE_IPV4: + hints.ai_family = AF_INET; + break; + } + if (noresolve) { + hints.ai_flags |= AI_NUMERICHOST; + } + + // call getaddrinfo + struct addrinfo *addrs; + int res; + if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) { + return 0; + } + + // set address + switch (addr->type) { + case BADDR_TYPE_IPV6: + memcpy(addr->ipv6.ip, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6.ip)); + addr->ipv6.port = port; + break; + case BADDR_TYPE_IPV4: + addr->ipv4.ip = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr; + addr->ipv4.port = port; + break; + } + + freeaddrinfo(addrs); + + if (name) { + if (strlen(addr_str) >= name_len) { + return 0; + } + strcpy(name, addr_str); + } + + return 1; +} + +int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len) +{ + return BAddr_Parse2(addr, str, name, name_len, 0); +} + +int BAddr_Compare (BAddr *addr1, BAddr *addr2) +{ + BAddr_Assert(addr1); + BAddr_Assert(addr2); + + if (addr1->type != addr2->type) { + return 0; + } + + switch (addr1->type) { + case BADDR_TYPE_IPV4: + return (addr1->ipv4.ip == addr2->ipv4.ip && addr1->ipv4.port == addr2->ipv4.port); + case BADDR_TYPE_IPV6: + return (!memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip)) && addr1->ipv6.port == addr2->ipv6.port); + default: + return 0; + } +} + +int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2) +{ + BAddr_Assert(addr1); + BAddr_Assert(addr2); + + int cmp = B_COMPARE(addr1->type, addr2->type); + if (cmp) { + return cmp; + } + + switch (addr1->type) { + case BADDR_TYPE_NONE: { + return 0; + } break; + case BADDR_TYPE_IPV4: { + uint32_t ip1 = ntoh32(addr1->ipv4.ip); + uint32_t ip2 = ntoh32(addr2->ipv4.ip); + cmp = B_COMPARE(ip1, ip2); + if (cmp) { + return cmp; + } + uint16_t port1 = ntoh16(addr1->ipv4.port); + uint16_t port2 = ntoh16(addr2->ipv4.port); + return B_COMPARE(port1, port2); + } break; + case BADDR_TYPE_IPV6: { + cmp = memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip)); + if (cmp) { + return B_COMPARE(cmp, 0); + } + uint16_t port1 = ntoh16(addr1->ipv6.port); + uint16_t port2 = ntoh16(addr2->ipv6.port); + return B_COMPARE(port1, port2); + } break; + default: { + return 0; + } break; + } +} + +#endif diff --git a/service/src/main/jni/badvpn/system/BConnection.h b/service/src/main/jni/badvpn/system/BConnection.h new file mode 100644 index 0000000..eac25ec --- /dev/null +++ b/service/src/main/jni/badvpn/system/BConnection.h @@ -0,0 +1,369 @@ +/** + * @file BConnection.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BCONNECTION +#define BADVPN_SYSTEM_BCONNECTION + +#include +#include +#include +#include +#include +#include + + + +/** + * Checks if the given address is supported by {@link BConnection} and related objects. + * + * @param addr address to check. Must be a proper {@link BAddr} object according to + * {@link BIPAddr_Assert}. + * @return 1 if supported, 0 if not + */ +int BConnection_AddressSupported (BAddr addr); + + + +struct BListener_s; + +/** + * Object which listens for connections on an address. + * When a connection is ready, the {@link BListener_handler} handler is called, from which + * the connection can be accepted into a new {@link BConnection} object. + */ +typedef struct BListener_s BListener; + +/** + * Handler called when a new connection is ready. + * The connection can be accepted by calling {@link BConnection_Init} with the a + * BCONNECTION_SOURCE_LISTENER 'source' argument. + * If no attempt is made to accept the connection from the job closure of this handler, + * the connection will be discarded. + * + * @param user as in {@link BListener_Init} + */ +typedef void (*BListener_handler) (void *user); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param addr address to listen on + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when a connection can be accepted + * @return 1 on success, 0 on failure + */ +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; + +#ifndef BADVPN_USE_WINAPI +/** + * Initializes the object for listening on a Unix socket. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param socket_path socket path for listening + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when a connection can be accepted + * @return 1 on success, 0 on failure + */ +int BListener_InitUnix (BListener *o, const char *socket_path, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; +#endif + +/** + * Frees the object. + * + * @param o the object + */ +void BListener_Free (BListener *o); + + + +struct BConnector_s; + +/** + * Object which connects to an address. + * When the connection attempt finishes, the {@link BConnector_handler} handler is called, from which, + * if successful, the resulting connection can be moved to a new {@link BConnection} object. + */ +typedef struct BConnector_s BConnector; + +/** + * Handler called when the connection attempt finishes. + * If the connection attempt succeeded (is_error==0), the new connection can be used by calling + * {@link BConnection_Init} with a BCONNECTION_SOURCE_TYPE_CONNECTOR 'source' argument. + * This handler will be called at most once. The connector object need not be freed after it + * is called. + * + * @param user as in {@link BConnector_Init} + * @param is_error whether the connection attempt succeeded (0) or failed (1) + */ +typedef void (*BConnector_handler) (void *user, int is_error); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param addr address to connect to + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when the connection attempt finishes + * @return 1 on success, 0 on failure + */ +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; + +#ifndef BADVPN_USE_WINAPI +/** + * Initializes the object for connecting to a Unix socket. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param socket_path socket path for connecting + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when the connection attempt finishes + * @return 1 on success, 0 on failure + */ +int BConnector_InitUnix (BConnector *o, const char *socket_path, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; +#endif + +/** + * Frees the object. + * + * @param o the object + */ +void BConnector_Free (BConnector *o); + + + +#define BCONNECTION_SOURCE_TYPE_LISTENER 1 +#define BCONNECTION_SOURCE_TYPE_CONNECTOR 2 +#define BCONNECTION_SOURCE_TYPE_PIPE 3 + +struct BConnection_source { + int type; + union { + struct { + BListener *listener; + BAddr *out_addr; + } listener; + struct { + BConnector *connector; + } connector; +#ifndef BADVPN_USE_WINAPI + struct { + int pipefd; + } pipe; +#endif + } u; +}; + +static struct BConnection_source BConnection_source_listener (BListener *listener, BAddr *out_addr) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_LISTENER; + s.u.listener.listener = listener; + s.u.listener.out_addr = out_addr; + return s; +} + +static struct BConnection_source BConnection_source_connector (BConnector *connector) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_CONNECTOR; + s.u.connector.connector = connector; + return s; +} + +#ifndef BADVPN_USE_WINAPI +static struct BConnection_source BConnection_source_pipe (int pipefd) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_PIPE; + s.u.pipe.pipefd = pipefd; + return s; +} +#endif + +struct BConnection_s; + +/** + * Object which represents a stream connection. This is usually a TCP connection, either client + * or server, but may also be used with any file descriptor (e.g. pipe) on Unix-like systems. + * Sending and receiving is performed via {@link StreamPassInterface} and {@link StreamRecvInterface}, + * respectively. + */ +typedef struct BConnection_s BConnection; + +#define BCONNECTION_EVENT_ERROR 1 +#define BCONNECTION_EVENT_RECVCLOSED 2 + +/** + * Handler called when an error occurs or the receive end of the connection was closed + * by the remote peer. + * - If event is BCONNECTION_EVENT_ERROR, the connection is no longer usable and must be freed + * from withing the job closure of this handler. No further I/O or interface initialization + * must occur. + * - If event is BCONNECTION_EVENT_RECVCLOSED, no further receive I/O or receive interface + * initialization must occur. It is guarantted that the receive interface was initialized. + * + * @param user as in {@link BConnection_Init} or {@link BConnection_SetHandlers} + * @param event what happened - BCONNECTION_EVENT_ERROR or BCONNECTION_EVENT_RECVCLOSED + */ +typedef void (*BConnection_handler) (void *user, int event); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param source specifies what the connection comes from. This argument must be created with one of the + * following macros: + * - BCONNECTION_SOURCE_LISTENER(BListener *, BAddr *) + * Accepts a connection ready on a {@link BListener} object. Must be called from the job + * closure of the listener's {@link BListener_handler}, and must be the first attempt + * for this handler invocation. The address of the client is written if the address + * argument is not NULL (theoretically an invalid address may be returned). + * - BCONNECTION_SOURCE_CONNECTOR(Bconnector *) + * Uses a connection establised with {@link BConnector}. Must be called from the job + * closure of the connector's {@link BConnector_handler}, the handler must be reporting + * successful connection, and must be the first attempt for this handler invocation. + * - BCONNECTION_SOURCE_PIPE(int) + * On Unix-like systems, uses the provided file descriptor. The file descriptor number must + * be >=0. + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when an error occurs or the receive end of the connection was closed + * by the remote peer. + * @return 1 on success, 0 on failure + */ +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) WARN_UNUSED; + +/** + * Frees the object. + * The send and receive interfaces must not be initialized. + * If the connection was created with a BCONNECTION_SOURCE_PIPE 'source' argument, the file descriptor + * will not be closed. + * + * @param o the object + */ +void BConnection_Free (BConnection *o); + +/** + * Updates the handler function. + * + * @param o the object + * @param user argument to handler + * @param handler new handler function, as in {@link BConnection_Init}. Additionally, may be NULL to + * remove the current handler. In this case, a proper handler must be set before anything + * can happen with the connection. This is used when moving the connection ownership from + * one module to another. + */ +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler); + +/** + * Sets the SO_SNDBUF socket option. + * + * @param o the object + * @param buf_size value for SO_SNDBUF option + * @return 1 on success, 0 on failure + */ +int BConnection_SetSendBuffer (BConnection *o, int buf_size); + +/** + * Initializes the send interface for the connection. + * The send interface must not be initialized. + * + * @param o the object + */ +void BConnection_SendAsync_Init (BConnection *o); + +/** + * Frees the send interface for the connection. + * The send interface must be initialized. + * If the send interface was busy when this is called, the connection is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BConnection_SendAsync_Free (BConnection *o); + +/** + * Returns the send interface. + * The send interface must be initialized. + * + * @param o the object + * @return send interface + */ +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o); + +/** + * Initializes the receive interface for the connection. + * The receive interface must not be initialized. + * + * @param o the object + */ +void BConnection_RecvAsync_Init (BConnection *o); + +/** + * Frees the receive interface for the connection. + * The receive interface must be initialized. + * If the receive interface was busy when this is called, the connection is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BConnection_RecvAsync_Free (BConnection *o); + +/** + * Returns the receive interface. + * The receive interface must be initialized. + * + * @param o the object + * @return receive interface + */ +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o); + + + +#ifdef BADVPN_USE_WINAPI +#include "BConnection_win.h" +#else +#include "BConnection_unix.h" +#endif + +#endif diff --git a/service/src/main/jni/badvpn/system/BConnection_unix.c b/service/src/main/jni/badvpn/system/BConnection_unix.c new file mode 100644 index 0000000..f6fa356 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BConnection_unix.c @@ -0,0 +1,1057 @@ +/** + * @file BConnection_unix.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "BConnection.h" + +#include + +#define MAX_UNIX_SOCKET_PATH 200 + +#define SEND_STATE_NOT_INITED 0 +#define SEND_STATE_READY 1 +#define SEND_STATE_BUSY 2 + +#define RECV_STATE_NOT_INITED 0 +#define RECV_STATE_READY 1 +#define RECV_STATE_BUSY 2 +#define RECV_STATE_INITED_CLOSED 3 +#define RECV_STATE_NOT_INITED_CLOSED 4 + +struct sys_addr { + socklen_t len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +struct unix_addr { + socklen_t len; + union { + struct sockaddr_un addr; + uint8_t bytes[offsetof(struct sockaddr_un, sun_path) + MAX_UNIX_SOCKET_PATH + 1]; + } u; +}; + +static int build_unix_address (struct unix_addr *out, const char *socket_path); +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void listener_fd_handler (BListener *o, int events); +static void listener_default_job_handler (BListener *o); +static void connector_fd_handler (BConnector *o, int events); +static void connector_job_handler (BConnector *o); +static void connection_report_error (BConnection *o); +static void connection_send (BConnection *o); +static void connection_recv (BConnection *o); +static void connection_fd_handler (BConnection *o, int events); +static void connection_send_job_handler (BConnection *o); +static void connection_recv_job_handler (BConnection *o); +static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len); +static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_len); + +static int build_unix_address (struct unix_addr *out, const char *socket_path) +{ + ASSERT(socket_path); + + if (strlen(socket_path) > MAX_UNIX_SOCKET_PATH) { + return 0; + } + + out->len = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path) + 1; + out->u.addr.sun_family = AF_UNIX; + strcpy(out->u.addr.sun_path, socket_path); + + return 1; +} + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + + default: { + BAddr_InitNone(out); + } break; + } +} + +static void listener_fd_handler (BListener *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // set default job + BPending_Set(&o->default_job); + + // call handler + o->handler(o->user); + return; +} + +static void listener_default_job_handler (BListener *o) +{ + DebugObject_Access(&o->d_obj); + + BLog(BLOG_ERROR, "discarding connection"); + + // accept + int newfd = accept(o->fd, NULL, NULL); + if (newfd < 0) { + BLog(BLOG_ERROR, "accept failed"); + return; + } + + // close new fd + if (close(newfd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +static void connector_fd_handler (BConnector *o, int events) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->fd >= 0) + ASSERT(!o->connected) + ASSERT(o->have_bfd) + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // set have no BFileDescriptor + o->have_bfd = 0; + + // read connection result + int result; + socklen_t result_len = sizeof(result); + if (getsockopt(o->fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) { + BLog(BLOG_ERROR, "getsockopt failed"); + goto fail0; + } + ASSERT_FORCE(result_len == sizeof(result)) + + if (result != 0) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + // set connected + o->connected = 1; + +fail0: + // call handler + o->handler(o->user, !o->connected); + return; +} + +static void connector_job_handler (BConnector *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->fd >= 0) + ASSERT(o->connected) + ASSERT(!o->have_bfd) + + // call handler + o->handler(o->user, 0); + return; +} + +static void connection_report_error (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->handler) + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BCONNECTION_EVENT_ERROR)); + return; +} + +static void connection_send (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_BUSY) + + // limit + if (!o->is_hupd) { + if (!BReactorLimit_Increment(&o->send.limit)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + } + + // send + int bytes = write(o->fd, o->send.busy_data, o->send.busy_data_len); + if (bytes < 0) { + if (!o->is_hupd && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "send failed"); + connection_report_error(o); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->send.busy_data_len) + + // set ready + o->send.state = SEND_STATE_READY; + + // done + StreamPassInterface_Done(&o->send.iface, bytes); +} + +static void connection_recv (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_BUSY) + + // limit + if (!o->is_hupd) { + if (!BReactorLimit_Increment(&o->recv.limit)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + } + + // recv + int bytes = read(o->fd, o->recv.busy_data, o->recv.busy_data_avail); + if (bytes < 0) { + if (!o->is_hupd && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "recv failed"); + connection_report_error(o); + return; + } + + if (bytes == 0) { + // set recv inited closed + o->recv.state = RECV_STATE_INITED_CLOSED; + + // report recv closed + o->handler(o->user, BCONNECTION_EVENT_RECVCLOSED); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->recv.busy_data_avail) + + // set not busy + o->recv.state = RECV_STATE_READY; + + // done + StreamRecvInterface_Done(&o->recv.iface, bytes); +} + +static void connection_fd_handler (BConnection *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->is_hupd) + + // clear handled events + o->wait_events &= ~events; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + int have_send = 0; + int have_recv = 0; + + // if we got a HUP event, stop monitoring the file descriptor + if ((events & BREACTOR_HUP)) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + o->is_hupd = 1; + } + + if ((events & BREACTOR_WRITE) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->send.state == SEND_STATE_BUSY)) { + ASSERT(o->send.state == SEND_STATE_BUSY) + have_send = 1; + } + + if ((events & BREACTOR_READ) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->recv.state == RECV_STATE_BUSY)) { + ASSERT(o->recv.state == RECV_STATE_BUSY) + have_recv = 1; + } + + if (have_send) { + if (have_recv) { + BPending_Set(&o->recv.job); + } + + connection_send(o); + return; + } + + if (have_recv) { + connection_recv(o); + return; + } + + if (!o->is_hupd) { + BLog(BLOG_ERROR, "fd error event"); + connection_report_error(o); + return; + } +} + +static void connection_send_job_handler (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_BUSY) + + connection_send(o); + return; +} + +static void connection_recv_job_handler (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_BUSY) + + connection_recv(o); + return; +} + +static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_READY) + ASSERT(data_len > 0) + + // remember data + o->send.busy_data = data; + o->send.busy_data_len = data_len; + + // set busy + o->send.state = SEND_STATE_BUSY; + + connection_send(o); + return; +} + +static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_avail) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_READY) + ASSERT(data_avail > 0) + + // remember data + o->recv.busy_data = data; + o->recv.busy_data_avail = data_avail; + + // set busy + o->recv.state = RECV_STATE_BUSY; + + connection_recv(o); + return; +} + +int BConnection_AddressSupported (BAddr addr) +{ + BAddr_Assert(&addr); + + return (addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6); +} + +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // set no unix socket path + o->unix_socket_path = NULL; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // init fd + if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // set SO_REUSEADDR + int optval = 1; + if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + BLog(BLOG_ERROR, "setsockopt(SO_REUSEADDR) failed"); + } + + // bind + if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail1; + } + + // listen + if (listen(o->fd, BCONNECTION_LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail1; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)listener_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init default job + BPending_Init(&o->default_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_default_job_handler, o); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +int BListener_InitUnix (BListener *o, const char *socket_path, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(socket_path) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // copy socket path + o->unix_socket_path = b_strdup(socket_path); + if (!o->unix_socket_path) { + BLog(BLOG_ERROR, "b_strdup failed"); + goto fail0; + } + + // build address + struct unix_addr addr; + if (!build_unix_address(&addr, socket_path)) { + BLog(BLOG_ERROR, "build_unix_address failed"); + goto fail1; + } + + // init fd + if ((o->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // unlink existing socket + if (unlink(o->unix_socket_path) < 0 && errno != ENOENT) { + BLog(BLOG_ERROR, "unlink existing socket failed"); + goto fail2; + } + + // bind + if (bind(o->fd, (struct sockaddr *)&addr.u.addr, addr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail2; + } + + // listen + if (listen(o->fd, BCONNECTION_LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail3; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)listener_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail3; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init default job + BPending_Init(&o->default_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_default_job_handler, o); + + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + if (unlink(o->unix_socket_path) < 0) { + BLog(BLOG_ERROR, "unlink socket failed"); + } +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + free(o->unix_socket_path); +fail0: + return 0; +} + +void BListener_Free (BListener *o) +{ + DebugObject_Free(&o->d_obj); + + // free default job + BPending_Free(&o->default_job); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free fd + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + + // unlink unix socket + if (o->unix_socket_path) { + if (unlink(o->unix_socket_path) < 0) { + BLog(BLOG_ERROR, "unlink socket failed"); + } + } + + // free unix socket path + if (o->unix_socket_path) { + free(o->unix_socket_path); + } +} + +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // init job + BPending_Init(&o->job, BReactor_PendingGroup(o->reactor), (BPending_handler)connector_job_handler, o); + + // init fd + if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // connect fd + int res = connect(o->fd, &sysaddr.addr.generic, sysaddr.len); + if (res < 0 && errno != EINPROGRESS) { + BLog(BLOG_ERROR, "connect failed"); + goto fail2; + } + + // set not connected + o->connected = 0; + + // set have no BFileDescriptor + o->have_bfd = 0; + + if (res < 0) { + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connector_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_WRITE); + + // set have BFileDescriptor + o->have_bfd = 1; + } else { + // set connected + o->connected = 1; + + // set job + BPending_Set(&o->job); + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + BPending_Free(&o->job); +fail0: + return 0; +} + +int BConnector_InitUnix (BConnector *o, const char *socket_path, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(socket_path) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // build address + struct unix_addr addr; + if (!build_unix_address(&addr, socket_path)) { + BLog(BLOG_ERROR, "build_unix_address failed"); + goto fail0; + } + + // init job + BPending_Init(&o->job, BReactor_PendingGroup(o->reactor), (BPending_handler)connector_job_handler, o); + + // init fd + if ((o->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // connect fd + int res = connect(o->fd, (struct sockaddr *)&addr.u.addr, addr.len); + if (res < 0 && errno != EINPROGRESS) { + BLog(BLOG_ERROR, "connect failed"); + goto fail2; + } + + // set not connected + o->connected = 0; + + // set have no BFileDescriptor + o->have_bfd = 0; + + if (res < 0) { + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connector_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_WRITE); + + // set have BFileDescriptor + o->have_bfd = 1; + } else { + // set connected + o->connected = 1; + + // set job + BPending_Set(&o->job); + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + BPending_Free(&o->job); +fail0: + return 0; +} + +void BConnector_Free (BConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free BFileDescriptor + if (o->have_bfd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close fd + if (o->fd != -1) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } + + // free job + BPending_Free(&o->job); +} + +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) +{ + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + DebugObject_Access(&listener->d_obj); + ASSERT(BPending_IsSet(&listener->default_job)) + } break; + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + DebugObject_Access(&connector->d_obj); + ASSERT(connector->fd >= 0) + ASSERT(connector->connected) + ASSERT(!connector->have_bfd) + ASSERT(!BPending_IsSet(&connector->job)) + } break; + case BCONNECTION_SOURCE_TYPE_PIPE: { + ASSERT(source.u.pipe.pipefd >= 0) + } break; + default: ASSERT(0); + } + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + + // unset listener's default job + BPending_Unset(&listener->default_job); + + // accept + struct sys_addr sysaddr; + sysaddr.len = sizeof(sysaddr.addr); + if ((o->fd = accept(listener->fd, &sysaddr.addr.generic, &sysaddr.len)) < 0) { + BLog(BLOG_ERROR, "accept failed"); + goto fail0; + } + o->close_fd = 1; + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // return address + if (source.u.listener.out_addr) { + addr_sys_to_socket(source.u.listener.out_addr, sysaddr); + } + } break; + + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + + // grab fd from connector + o->fd = connector->fd; + connector->fd = -1; + o->close_fd = 1; + } break; + + case BCONNECTION_SOURCE_TYPE_PIPE: { + // use user-provided fd + o->fd = source.u.pipe.pipefd; + o->close_fd = 0; + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + } break; + } + + // set not HUPd + o->is_hupd = 0; + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connection_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + // set no wait events + o->wait_events = 0; + + // init limits + BReactorLimit_Init(&o->send.limit, o->reactor, BCONNECTION_SEND_LIMIT); + BReactorLimit_Init(&o->recv.limit, o->reactor, BCONNECTION_RECV_LIMIT); + + // set send and recv not inited + o->send.state = SEND_STATE_NOT_INITED; + o->recv.state = RECV_STATE_NOT_INITED; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (o->close_fd) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } +fail0: + return 0; +} + +void BConnection_Free (BConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(o->send.state == SEND_STATE_NOT_INITED) + ASSERT(o->recv.state == RECV_STATE_NOT_INITED || o->recv.state == RECV_STATE_NOT_INITED_CLOSED) + + // free limits + BReactorLimit_Free(&o->recv.limit); + BReactorLimit_Free(&o->send.limit); + + // free BFileDescriptor + if (!o->is_hupd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close fd + if (o->close_fd) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } +} + +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler) +{ + DebugObject_Access(&o->d_obj); + + // set handlers + o->user = user; + o->handler = handler; +} + +int BConnection_SetSendBuffer (BConnection *o, int buf_size) +{ + DebugObject_Access(&o->d_obj); + + if (setsockopt(o->fd, SOL_SOCKET, SO_SNDBUF, (void *)&buf_size, sizeof(buf_size)) < 0) { + BLog(BLOG_ERROR, "setsockopt failed"); + return 0; + } + + return 1; +} + +void BConnection_SendAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_NOT_INITED) + + // init interface + StreamPassInterface_Init(&o->send.iface, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_send_job_handler, o); + + // set ready + o->send.state = SEND_STATE_READY; +} + +void BConnection_SendAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.state == SEND_STATE_READY || o->send.state == SEND_STATE_BUSY) + + // update events + if (!o->is_hupd) { + o->wait_events &= ~BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + } + + // free job + BPending_Free(&o->send.job); + + // free interface + StreamPassInterface_Free(&o->send.iface); + + // set not inited + o->send.state = SEND_STATE_NOT_INITED; +} + +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.state == SEND_STATE_READY || o->send.state == SEND_STATE_BUSY) + + return &o->send.iface; +} + +void BConnection_RecvAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_NOT_INITED) + + // init interface + StreamRecvInterface_Init(&o->recv.iface, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_recv_job_handler, o); + + // set ready + o->recv.state = RECV_STATE_READY; +} + +void BConnection_RecvAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.state == RECV_STATE_READY || o->recv.state == RECV_STATE_BUSY || o->recv.state == RECV_STATE_INITED_CLOSED) + + // update events + if (!o->is_hupd) { + o->wait_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + } + + // free job + BPending_Free(&o->recv.job); + + // free interface + StreamRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.state = RECV_STATE_NOT_INITED; +} + +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.state == RECV_STATE_READY || o->recv.state == RECV_STATE_BUSY || o->recv.state == RECV_STATE_INITED_CLOSED) + + return &o->recv.iface; +} diff --git a/service/src/main/jni/badvpn/system/BConnection_unix.h b/service/src/main/jni/badvpn/system/BConnection_unix.h new file mode 100644 index 0000000..e134f6c --- /dev/null +++ b/service/src/main/jni/badvpn/system/BConnection_unix.h @@ -0,0 +1,87 @@ +/** + * @file BConnection_unix.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define BCONNECTION_SEND_LIMIT 2 +#define BCONNECTION_RECV_LIMIT 2 +#define BCONNECTION_LISTEN_BACKLOG 128 + +struct BListener_s { + BReactor *reactor; + void *user; + BListener_handler handler; + char *unix_socket_path; + int fd; + BFileDescriptor bfd; + BPending default_job; + DebugObject d_obj; +}; + +struct BConnector_s { + BReactor *reactor; + void *user; + BConnector_handler handler; + BPending job; + int fd; + int connected; + int have_bfd; + BFileDescriptor bfd; + DebugObject d_obj; +}; + +struct BConnection_s { + BReactor *reactor; + void *user; + BConnection_handler handler; + int fd; + int close_fd; + int is_hupd; + BFileDescriptor bfd; + int wait_events; + struct { + BReactorLimit limit; + StreamPassInterface iface; + BPending job; + const uint8_t *busy_data; + int busy_data_len; + int state; + } send; + struct { + BReactorLimit limit; + StreamRecvInterface iface; + BPending job; + uint8_t *busy_data; + int busy_data_avail; + int state; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/service/src/main/jni/badvpn/system/BConnection_win.h b/service/src/main/jni/badvpn/system/BConnection_win.h new file mode 100644 index 0000000..da9a8ed --- /dev/null +++ b/service/src/main/jni/badvpn/system/BConnection_win.h @@ -0,0 +1,101 @@ +/** + * @file BConnection_win.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#ifdef BADVPN_USE_SHIPPED_MSWSOCK +# include +#else +# include +#endif + +#include +#include + +struct BListener_addrbuf_stub { + union { + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; + uint8_t extra[16]; +}; + +struct BListener_s { + BReactor *reactor; + void *user; + BListener_handler handler; + int sys_family; + SOCKET sock; + LPFN_ACCEPTEX fnAcceptEx; + LPFN_GETACCEPTEXSOCKADDRS fnGetAcceptExSockaddrs; + BReactorIOCPOverlapped olap; + SOCKET newsock; + uint8_t addrbuf[2 * sizeof(struct BListener_addrbuf_stub)]; + BPending next_job; + int busy; + int ready; + DebugObject d_obj; +}; + +struct BConnector_s { + BReactor *reactor; + void *user; + BConnector_handler handler; + SOCKET sock; + LPFN_CONNECTEX fnConnectEx; + BReactorIOCPOverlapped olap; + int busy; + int ready; + DebugObject d_obj; +}; + +struct BConnection_s { + BReactor *reactor; + void *user; + BConnection_handler handler; + SOCKET sock; + int aborted; + struct { + BReactorIOCPOverlapped olap; + int inited; + StreamPassInterface iface; + int busy; + int busy_data_len; + } send; + struct { + BReactorIOCPOverlapped olap; + int closed; + int inited; + StreamRecvInterface iface; + int busy; + int busy_data_len; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/service/src/main/jni/badvpn/system/BDatagram.h b/service/src/main/jni/badvpn/system/BDatagram.h new file mode 100644 index 0000000..33efb45 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BDatagram.h @@ -0,0 +1,209 @@ +/** + * @file BDatagram.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BDATAGRAM +#define BADVPN_SYSTEM_BDATAGRAM + +#include +#include +#include +#include +#include +#include + +struct BDatagram_s; + +/** + * Represents datagram communication. This is usually UDP, but may also be Linux packet sockets. + * Sending and receiving is performed via {@link PacketPassInterface} and {@link PacketRecvInterface}, + * respectively. + */ +typedef struct BDatagram_s BDatagram; + +#define BDATAGRAM_EVENT_ERROR 1 + +/** + * Handler called when an error occurs with the datagram object. + * The datagram object is no longer usable and must be freed from withing the job closure of + * this handler. No further I/O, interface initialization, binding and send address setting + * must occur. + * + * @param user as in {@link BDatagram_Init} + * @param event always BDATAGRAM_EVENT_ERROR + */ +typedef void (*BDatagram_handler) (void *user, int event); + +/** + * Checks if the given address family (from {@link BAddr.h}) is supported by {@link BDatagram} + * and related objects. + * + * @param family family to check + * @return 1 if supported, 0 if not + */ +int BDatagram_AddressFamilySupported (int family); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param family address family. Must be supported according to {@link BDatagram_AddressFamilySupported}. + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when an error occurs + * @return 1 on success, 0 on failure + */ +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) WARN_UNUSED; + +/** + * Frees the object. + * The send and receive interfaces must not be initialized. + * + * @param o the object + */ +void BDatagram_Free (BDatagram *o); + +/** + * Binds to the given local address. + * May initiate I/O. + * + * @param o the object + * @param addr address to bind to. Its family must be supported according to {@link BDatagram_AddressFamilySupported}. + * @return 1 on success, 0 on failure + */ +int BDatagram_Bind (BDatagram *o, BAddr addr) WARN_UNUSED; + +/** + * Sets addresses for sending. + * May initiate I/O. + * + * @param o the object + * @param remote_addr destination address for sending datagrams. Its family must be supported according + * to {@link BDatagram_AddressFamilySupported}. + * @param local_addr local source IP address. May be an invalid address, otherwise its family must be + * supported according to {@link BDatagram_AddressFamilySupported}. + */ +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr); + +/** + * Returns the remote and local address of the last datagram received. + * Fails if and only if no datagrams have been received yet. + * + * @param o the object + * @param remote_addr returns the remote source address of the datagram. May be an invalid address, theoretically. + * @param local_addr returns the local destination IP address. May be an invalid address. + * @return 1 on success, 0 on failure + */ +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr); + +#ifndef BADVPN_USE_WINAPI +/** + * Returns the underlying socket file descriptor of the datagram object. + * Available on Unix-like systems only. + * + * @param o the object + * @return file descriptor + */ +int BDatagram_GetFd (BDatagram *o); +#endif + +/** + * Sets the SO_REUSEADDR option for the underlying socket. + * + * @param o the object + * @param reuse value of the option. Must be 0 or 1. + */ +int BDatagram_SetReuseAddr (BDatagram *o, int reuse); + +/** + * Initializes the send interface. + * The send interface must not be initialized. + * + * @param o the object + * @param mtu maximum transmission unit. Must be >=0. + */ +void BDatagram_SendAsync_Init (BDatagram *o, int mtu); + +/** + * Frees the send interface. + * The send interface must be initialized. + * If the send interface was busy when this is called, the datagram object is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BDatagram_SendAsync_Free (BDatagram *o); + +/** + * Returns the send interface. + * The send interface must be initialized. + * The MTU of the interface will be as in {@link BDatagram_SendAsync_Init}. + * + * @param o the object + * @return send interface + */ +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o); + +/** + * Initializes the receive interface. + * The receive interface must not be initialized. + * + * @param o the object + * @param mtu maximum transmission unit. Must be >=0. + */ +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu); + +/** + * Frees the receive interface. + * The receive interface must be initialized. + * If the receive interface was busy when this is called, the datagram object is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BDatagram_RecvAsync_Free (BDatagram *o); + +/** + * Returns the receive interface. + * The receive interface must be initialized. + * The MTU of the interface will be as in {@link BDatagram_RecvAsync_Init}. + * + * @param o the object + * @return receive interface + */ +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o); + +#ifdef BADVPN_USE_WINAPI +#include "BDatagram_win.h" +#else +#include "BDatagram_unix.h" +#endif + +#endif diff --git a/service/src/main/jni/badvpn/system/BDatagram_unix.c b/service/src/main/jni/badvpn/system/BDatagram_unix.c new file mode 100644 index 0000000..67853db --- /dev/null +++ b/service/src/main/jni/badvpn/system/BDatagram_unix.c @@ -0,0 +1,855 @@ +/** + * @file BDatagram_unix.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#ifdef BADVPN_LINUX +# include +# include +#endif + +#include +#include + +#include "BDatagram.h" + +#include + +struct sys_addr { + socklen_t len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; +#ifdef BADVPN_LINUX + struct sockaddr_ll packet; +#endif + } addr; +}; + +static int family_socket_to_sys (int family); +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void set_pktinfo (int fd, int family); +static void report_error (BDatagram *o); +static void do_send (BDatagram *o); +static void do_recv (BDatagram *o); +static void fd_handler (BDatagram *o, int events); +static void send_job_handler (BDatagram *o); +static void recv_job_handler (BDatagram *o); +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len); +static void recv_if_handler_recv (BDatagram *o, uint8_t *data); + +static int family_socket_to_sys (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + return AF_INET; + case BADDR_TYPE_IPV6: + return AF_INET6; +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + return AF_PACKET; +#endif + } + + ASSERT(0); + return 0; +} + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: { + ASSERT(addr.packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + memset(&out->addr.packet, 0, sizeof(out->addr.packet)); + out->len = sizeof(out->addr.packet); + out->addr.packet.sll_family = AF_PACKET; + out->addr.packet.sll_protocol = addr.packet.phys_proto; + out->addr.packet.sll_ifindex = addr.packet.interface_index; + out->addr.packet.sll_hatype = 1; // linux/if_arp.h: #define ARPHRD_ETHER 1 + switch (addr.packet.packet_type) { + case BADDR_PACKET_PACKET_TYPE_HOST: + out->addr.packet.sll_pkttype = PACKET_HOST; + break; + case BADDR_PACKET_PACKET_TYPE_BROADCAST: + out->addr.packet.sll_pkttype = PACKET_BROADCAST; + break; + case BADDR_PACKET_PACKET_TYPE_MULTICAST: + out->addr.packet.sll_pkttype = PACKET_MULTICAST; + break; + case BADDR_PACKET_PACKET_TYPE_OTHERHOST: + out->addr.packet.sll_pkttype = PACKET_OTHERHOST; + break; + case BADDR_PACKET_PACKET_TYPE_OUTGOING: + out->addr.packet.sll_pkttype = PACKET_OUTGOING; + break; + default: + ASSERT(0); + } + out->addr.packet.sll_halen = 6; + memcpy(out->addr.packet.sll_addr, addr.packet.phys_addr, 6); + } break; +#endif + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + +#ifdef BADVPN_LINUX + case AF_PACKET: { + if (addr.len < offsetof(struct sockaddr_ll, sll_addr) + 6) { + goto fail; + } + if (addr.addr.packet.sll_hatype != 1) { // linux/if_arp.h: #define ARPHRD_ETHER 1 + goto fail; + } + int packet_type; + switch (addr.addr.packet.sll_pkttype) { + case PACKET_HOST: + packet_type = BADDR_PACKET_PACKET_TYPE_HOST; + break; + case PACKET_BROADCAST: + packet_type = BADDR_PACKET_PACKET_TYPE_BROADCAST; + break; + case PACKET_MULTICAST: + packet_type = BADDR_PACKET_PACKET_TYPE_MULTICAST; + break; + case PACKET_OTHERHOST: + packet_type = BADDR_PACKET_PACKET_TYPE_OTHERHOST; + break; + case PACKET_OUTGOING: + packet_type = BADDR_PACKET_PACKET_TYPE_OUTGOING; + break; + default: + goto fail; + } + if (addr.addr.packet.sll_halen != 6) { + goto fail; + } + BAddr_InitPacket(out, addr.addr.packet.sll_protocol, addr.addr.packet.sll_ifindex, BADDR_PACKET_HEADER_TYPE_ETHERNET, packet_type, addr.addr.packet.sll_addr); + } break; +#endif + + fail: + default: { + BAddr_InitNone(out); + } break; + } +} + +static void set_pktinfo (int fd, int family) +{ + int opt = 1; + + switch (family) { + case BADDR_TYPE_IPV4: { +#ifdef BADVPN_FREEBSD + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_RECVDSTADDR) failed"); + } +#else + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed"); + } +#endif + } break; + +#ifdef IPV6_RECVPKTINFO + case BADDR_TYPE_IPV6: { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IPV6_RECVPKTINFO) failed"); + } + } break; +#endif + } +} + +static void report_error (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR)); + return; +} + +static void do_send (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + // limit + if (!BReactorLimit_Increment(&o->send.limit)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + // convert destination address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, o->send.remote_addr); + + struct iovec iov; + iov.iov_base = (uint8_t *)o->send.busy_data; + iov.iov_len = o->send.busy_data_len; + + union { +#ifdef BADVPN_FREEBSD + char in[CMSG_SPACE(sizeof(struct in_addr))]; +#else + char in[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#endif + char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sysaddr.addr.generic; + msg.msg_namelen = sysaddr.len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cdata; + msg.msg_controllen = sizeof(cdata); + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + size_t controllen = 0; + + switch (o->send.local_addr.type) { + case BADDR_TYPE_IPV4: { +#ifdef BADVPN_FREEBSD + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg); + addrinfo->s_addr = o->send.local_addr.ipv4; + controllen += CMSG_SPACE(sizeof(struct in_addr)); +#else + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + pktinfo->ipi_spec_dst.s_addr = o->send.local_addr.ipv4; + controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); +#endif + } break; + + case BADDR_TYPE_IPV6: { + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo))); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16); + controllen += CMSG_SPACE(sizeof(struct in6_pktinfo)); + } break; + } + + msg.msg_controllen = controllen; + + if (msg.msg_controllen == 0) { + msg.msg_control = NULL; + } + + // send + int bytes = sendmsg(o->fd, &msg, 0); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "send failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->send.busy_data_len) + + if (bytes < o->send.busy_data_len) { + BLog(BLOG_ERROR, "send sent too little"); + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.busy) { + BPending_Set(&o->recv.job); + } + } + + // set not busy + o->send.busy = 0; + + // done + PacketPassInterface_Done(&o->send.iface); +} + +static void do_recv (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + // limit + if (!BReactorLimit_Increment(&o->recv.limit)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + struct sys_addr sysaddr; + + struct iovec iov; + iov.iov_base = o->recv.busy_data; + iov.iov_len = o->recv.mtu; + + union { +#ifdef BADVPN_FREEBSD + char in[CMSG_SPACE(sizeof(struct in_addr))]; +#else + char in[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#endif + char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sysaddr.addr.generic; + msg.msg_namelen = sizeof(sysaddr.addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cdata; + msg.msg_controllen = sizeof(cdata); + + // recv + int bytes = recvmsg(o->fd, &msg, 0); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "recv failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->recv.mtu) + + // read returned address + sysaddr.len = msg.msg_namelen; + addr_sys_to_socket(&o->recv.remote_addr, sysaddr); + + // read returned local address + BIPAddr_InitInvalid(&o->recv.local_addr); + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { +#ifdef BADVPN_FREEBSD + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { + struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, addrinfo->s_addr); + } +#else + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr); + } +#endif + else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr); + } + } + + // set have addresses + o->recv.have_addrs = 1; + + // set not busy + o->recv.busy = 0; + + // done + PacketRecvInterface_Done(&o->recv.iface, bytes); +} + +static void fd_handler (BDatagram *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + // clear handled events + o->wait_events &= ~events; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + int have_send = 0; + int have_recv = 0; + + if ((events & BREACTOR_WRITE) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->send.inited && o->send.busy && o->send.have_addrs)) { + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + have_send = 1; + } + + if ((events & BREACTOR_READ) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->recv.inited && o->recv.busy && o->recv.started)) { + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + have_recv = 1; + } + + if (have_send) { + if (have_recv) { + BPending_Set(&o->recv.job); + } + + do_send(o); + return; + } + + if (have_recv) { + do_recv(o); + return; + } + + BLog(BLOG_ERROR, "fd error event"); + report_error(o); + return; +} + +static void send_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + do_send(o); + return; +} + +static void recv_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + do_recv(o); + return; +} + +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(!o->send.busy) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->send.mtu) + + // remember data + o->send.busy_data = data; + o->send.busy_data_len = data_len; + + // set busy + o->send.busy = 1; + + // if have no addresses, wait + if (!o->send.have_addrs) { + return; + } + + // set job + BPending_Set(&o->send.job); +} + +static void recv_if_handler_recv (BDatagram *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(!o->recv.busy) + + // remember data + o->recv.busy_data = data; + + // set busy + o->recv.busy = 1; + + // if recv not started yet, wait + if (!o->recv.started) { + return; + } + + // set job + BPending_Set(&o->recv.job); +} + +int BDatagram_AddressFamilySupported (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: +#endif + return 1; + } + + return 0; +} + +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) +{ + ASSERT(BDatagram_AddressFamilySupported(family)) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // init fd + if ((o->fd = socket(family_socket_to_sys(family), SOCK_DGRAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // enable receiving pktinfo + set_pktinfo(o->fd, family); + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + // set no wait events + o->wait_events = 0; + + // init limits + BReactorLimit_Init(&o->send.limit, o->reactor, BDATAGRAM_SEND_LIMIT); + BReactorLimit_Init(&o->recv.limit, o->reactor, BDATAGRAM_RECV_LIMIT); + + // set have no send and recv addresses + o->send.have_addrs = 0; + o->recv.have_addrs = 0; + + // set recv not started + o->recv.started = 0; + + // set send and recv not inited + o->send.inited = 0; + o->recv.inited = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +void BDatagram_Free (BDatagram *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(!o->send.inited) + + // free limits + BReactorLimit_Free(&o->recv.limit); + BReactorLimit_Free(&o->send.limit); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free fd + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +int BDatagram_Bind (BDatagram *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(BDatagram_AddressFamilySupported(addr.type)) + + // translate address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // bind + if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + return 0; + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.busy) { + BPending_Set(&o->recv.job); + } + } + + return 1; +} + +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(BDatagram_AddressFamilySupported(remote_addr.type)) + ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type)) + + // set addresses + o->send.remote_addr = remote_addr; + o->send.local_addr = local_addr; + + if (!o->send.have_addrs) { + // set have addresses + o->send.have_addrs = 1; + + // start sending + if (o->send.inited && o->send.busy) { + BPending_Set(&o->send.job); + } + } +} + +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr) +{ + DebugObject_Access(&o->d_obj); + + if (!o->recv.have_addrs) { + return 0; + } + + *remote_addr = o->recv.remote_addr; + *local_addr = o->recv.local_addr; + return 1; +} + +int BDatagram_GetFd (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + + return o->fd; +} + +int BDatagram_SetReuseAddr (BDatagram *o, int reuse) +{ + DebugObject_Access(&o->d_obj); + ASSERT(reuse == 0 || reuse == 1) + + if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { + return 0; + } + + return 1; +} + +void BDatagram_SendAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->send.inited) + ASSERT(mtu >= 0) + + // init arguments + o->send.mtu = mtu; + + // init interface + PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o); + + // set not busy + o->send.busy = 0; + + // set inited + o->send.inited = 1; +} + +void BDatagram_SendAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + // update events + o->wait_events &= ~BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + // free job + BPending_Free(&o->send.job); + + // free interface + PacketPassInterface_Free(&o->send.iface); + + // set not inited + o->send.inited = 0; +} + +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + return &o->send.iface; +} + +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(mtu >= 0) + + // init arguments + o->recv.mtu = mtu; + + // init interface + PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o); + + // set not busy + o->recv.busy = 0; + + // set inited + o->recv.inited = 1; +} + +void BDatagram_RecvAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + // update events + o->wait_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + // free job + BPending_Free(&o->recv.job); + + // free interface + PacketRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.inited = 0; +} + +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + return &o->recv.iface; +} diff --git a/service/src/main/jni/badvpn/system/BDatagram_unix.h b/service/src/main/jni/badvpn/system/BDatagram_unix.h new file mode 100644 index 0000000..3ef0262 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BDatagram_unix.h @@ -0,0 +1,71 @@ +/** + * @file BDatagram_unix.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define BDATAGRAM_SEND_LIMIT 2 +#define BDATAGRAM_RECV_LIMIT 2 + +struct BDatagram_s { + BReactor *reactor; + void *user; + BDatagram_handler handler; + int fd; + BFileDescriptor bfd; + int wait_events; + struct { + BReactorLimit limit; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketPassInterface iface; + BPending job; + int busy; + const uint8_t *busy_data; + int busy_data_len; + } send; + struct { + BReactorLimit limit; + int started; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketRecvInterface iface; + BPending job; + int busy; + uint8_t *busy_data; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/service/src/main/jni/badvpn/system/BDatagram_win.h b/service/src/main/jni/badvpn/system/BDatagram_win.h new file mode 100644 index 0000000..9831946 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BDatagram_win.h @@ -0,0 +1,99 @@ +/** + * @file BDatagram_win.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#ifdef BADVPN_USE_SHIPPED_MSWSOCK +# include +#else +# include +#endif + +#include +#include + +struct BDatagram_sys_addr { + int len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +struct BDatagram_s { + BReactor *reactor; + void *user; + BDatagram_handler handler; + SOCKET sock; + LPFN_WSASENDMSG fnWSASendMsg; + LPFN_WSARECVMSG fnWSARecvMsg; + int aborted; + struct { + BReactorIOCPOverlapped olap; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketPassInterface iface; + BPending job; + int data_len; + uint8_t *data; + int data_busy; + struct BDatagram_sys_addr sysaddr; + union { + char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; + char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + WSAMSG msg; + } send; + struct { + BReactorIOCPOverlapped olap; + int started; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketRecvInterface iface; + BPending job; + int data_have; + uint8_t *data; + int data_busy; + struct BDatagram_sys_addr sysaddr; + union { + char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; + char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + WSAMSG msg; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/service/src/main/jni/badvpn/system/BNetwork.c b/service/src/main/jni/badvpn/system/BNetwork.c new file mode 100644 index 0000000..b48ae61 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BNetwork.c @@ -0,0 +1,99 @@ +/** + * @file BNetwork.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef BADVPN_USE_WINAPI +#include +#include +#include +#else +#include +#include +#endif + +#include +#include + +#include + +#include + +extern int bnetwork_initialized; + +#ifndef BADVPN_PLUGIN +int bnetwork_initialized = 0; +#endif + +int BNetwork_GlobalInit (void) +{ + ASSERT(!bnetwork_initialized) + +#ifdef BADVPN_USE_WINAPI + + WORD requested = MAKEWORD(2, 2); + WSADATA wsadata; + if (WSAStartup(requested, &wsadata) != 0) { + BLog(BLOG_ERROR, "WSAStartup failed"); + goto fail0; + } + if (wsadata.wVersion != requested) { + BLog(BLOG_ERROR, "WSAStartup returned wrong version"); + goto fail1; + } + +#else + + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGPIPE, &act, NULL) < 0) { + BLog(BLOG_ERROR, "sigaction failed"); + goto fail0; + } + +#endif + + bnetwork_initialized = 1; + + return 1; + +#ifdef BADVPN_USE_WINAPI +fail1: + WSACleanup(); +#endif + +fail0: + return 0; +} + +void BNetwork_Assert (void) +{ + ASSERT(bnetwork_initialized) +} diff --git a/service/src/main/jni/badvpn/system/BNetwork.h b/service/src/main/jni/badvpn/system/BNetwork.h new file mode 100644 index 0000000..0db1744 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BNetwork.h @@ -0,0 +1,36 @@ +/** + * @file BNetwork.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BNETWORK_H +#define BADVPN_SYSTEM_BNETWORK_H + +int BNetwork_GlobalInit (void); +void BNetwork_Assert (void); + +#endif diff --git a/service/src/main/jni/badvpn/system/BReactor.h b/service/src/main/jni/badvpn/system/BReactor.h new file mode 100644 index 0000000..4c0fa5b --- /dev/null +++ b/service/src/main/jni/badvpn/system/BReactor.h @@ -0,0 +1,11 @@ +#if defined(BADVPN_BREACTOR_BADVPN) + defined(BADVPN_BREACTOR_GLIB) + defined(BADVPN_BREACTOR_EMSCRIPTEN) != 1 +#error No reactor backend or too many reactor backens +#endif + +#if defined(BADVPN_BREACTOR_BADVPN) +#include "BReactor_badvpn.h" +#elif defined(BADVPN_BREACTOR_GLIB) +#include "BReactor_glib.h" +#elif defined(BADVPN_BREACTOR_EMSCRIPTEN) +#include "BReactor_emscripten.h" +#endif diff --git a/service/src/main/jni/badvpn/system/BReactor_badvpn.c b/service/src/main/jni/badvpn/system/BReactor_badvpn.c new file mode 100644 index 0000000..0d4f4ad --- /dev/null +++ b/service/src/main/jni/badvpn/system/BReactor_badvpn.c @@ -0,0 +1,1430 @@ +/** + * @file BReactor_badvpn.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#define KEVENT_TAG_FD 1 +#define KEVENT_TAG_KEVENT 2 + +#define TIMER_STATE_INACTIVE 1 +#define TIMER_STATE_RUNNING 2 +#define TIMER_STATE_EXPIRED 3 + +static int compare_timers (BSmallTimer *t1, BSmallTimer *t2) +{ + int cmp = B_COMPARE(t1->absTime, t2->absTime); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)t1, (uintptr_t)t2); +} + +#include "BReactor_badvpn_timerstree.h" +#include + +static void assert_timer (BSmallTimer *bt) +{ + ASSERT(bt->state == TIMER_STATE_INACTIVE || bt->state == TIMER_STATE_RUNNING || + bt->state == TIMER_STATE_EXPIRED) +} + +static int move_expired_timers (BReactor *bsys, btime_t now) +{ + int moved = 0; + + // move timed out timers to the expired list + BReactor__TimersTreeRef ref; + BSmallTimer *timer; + while ((timer = ((ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0))).link)) { + ASSERT(timer->state == TIMER_STATE_RUNNING) + + // if it's in the future, stop + if (timer->absTime > now) { + break; + } + moved = 1; + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node); + + // set expired + timer->state = TIMER_STATE_EXPIRED; + } + + return moved; +} + +static void move_first_timers (BReactor *bsys) +{ + BReactor__TimersTreeRef ref; + + // get the time of the first timer + BSmallTimer *first_timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link; + ASSERT(first_timer) + ASSERT(first_timer->state == TIMER_STATE_RUNNING) + btime_t first_time = first_timer->absTime; + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &first_timer->u.list_node); + + // set expired + first_timer->state = TIMER_STATE_EXPIRED; + + // also move other timers with the same timeout + BSmallTimer *timer; + while ((timer = ((ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0))).link)) { + ASSERT(timer->state == TIMER_STATE_RUNNING) + ASSERT(timer->absTime >= first_time) + + // if it's in the future, stop + if (timer->absTime > first_time) { + break; + } + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node); + + // set expired + timer->state = TIMER_STATE_EXPIRED; + } +} + +#ifdef BADVPN_USE_WINAPI + +static void set_iocp_ready (BReactorIOCPOverlapped *olap, int succeeded, DWORD bytes) +{ + BReactor *reactor = olap->reactor; + ASSERT(!olap->is_ready) + + // set parameters + olap->ready_succeeded = succeeded; + olap->ready_bytes = bytes; + + // insert to IOCP ready list + LinkedList1_Append(&reactor->iocp_ready_list, &olap->ready_list_node); + + // set ready + olap->is_ready = 1; +} + +#endif + +#ifdef BADVPN_USE_EPOLL + +static void set_epoll_fd_pointers (BReactor *bsys) +{ + // Write pointers to our entry pointers into file descriptors. + // If a handler function frees some other file descriptor, the + // free routine will set our pointer to NULL so we don't dispatch it. + for (int i = 0; i < bsys->epoll_results_num; i++) { + struct epoll_event *event = &bsys->epoll_results[i]; + ASSERT(event->data.ptr) + BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr; + ASSERT(bfd->active) + ASSERT(!bfd->epoll_returned_ptr) + bfd->epoll_returned_ptr = (BFileDescriptor **)&event->data.ptr; + } +} + +#endif + +#ifdef BADVPN_USE_KEVENT + +static void set_kevent_fd_pointers (BReactor *bsys) +{ + for (int i = 0; i < bsys->kevent_results_num; i++) { + struct kevent *event = &bsys->kevent_results[i]; + ASSERT(event->udata) + int *tag = event->udata; + switch (*tag) { + case KEVENT_TAG_FD: { + BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag); + ASSERT(bfd->active) + ASSERT(!bfd->kevent_returned_ptr) + bfd->kevent_returned_ptr = (int **)&event->udata; + } break; + + case KEVENT_TAG_KEVENT: { + BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag); + ASSERT(kev->reactor == bsys) + ASSERT(!kev->kevent_returned_ptr) + kev->kevent_returned_ptr = (int **)&event->udata; + } break; + + default: + ASSERT(0); + } + } +} + +static void update_kevent_fd_events (BReactor *bsys, BFileDescriptor *bs, int events) +{ + struct kevent event; + + if (!(bs->waitEvents & BREACTOR_READ) && (events & BREACTOR_READ)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_READ; + event.flags = EV_ADD; + event.udata = &bs->kevent_tag; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + else if ((bs->waitEvents & BREACTOR_READ) && !(events & BREACTOR_READ)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_READ; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + + if (!(bs->waitEvents & BREACTOR_WRITE) && (events & BREACTOR_WRITE)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_WRITE; + event.flags = EV_ADD; + event.udata = &bs->kevent_tag; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + else if ((bs->waitEvents & BREACTOR_WRITE) && !(events & BREACTOR_WRITE)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_WRITE; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } +} + +#endif + +#ifdef BADVPN_USE_POLL + +static void set_poll_fd_pointers (BReactor *bsys) +{ + for (int i = 0; i < bsys->poll_results_num; i++) { + BFileDescriptor *bfd = bsys->poll_results_bfds[i]; + ASSERT(bfd) + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == -1) + bfd->poll_returned_index = i; + } +} + +#endif + +static void wait_for_events (BReactor *bsys) +{ + // must have processed all pending events + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list)) + #ifdef BADVPN_USE_WINAPI + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list)) + #endif + #ifdef BADVPN_USE_EPOLL + ASSERT(bsys->epoll_results_pos == bsys->epoll_results_num) + #endif + #ifdef BADVPN_USE_KEVENT + ASSERT(bsys->kevent_results_pos == bsys->kevent_results_num) + #endif + #ifdef BADVPN_USE_POLL + ASSERT(bsys->poll_results_pos == bsys->poll_results_num) + #endif + + // clean up epoll results + #ifdef BADVPN_USE_EPOLL + bsys->epoll_results_num = 0; + bsys->epoll_results_pos = 0; + #endif + + // clean up kevent results + #ifdef BADVPN_USE_KEVENT + bsys->kevent_results_num = 0; + bsys->kevent_results_pos = 0; + #endif + + // clean up poll results + #ifdef BADVPN_USE_POLL + bsys->poll_results_num = 0; + bsys->poll_results_pos = 0; + #endif + + // timeout vars + int have_timeout = 0; + btime_t timeout_abs; + btime_t now = 0; // to remove warning + + // compute timeout + BSmallTimer *first_timer = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0).link; + if (first_timer) { + ASSERT(first_timer->state == TIMER_STATE_RUNNING) + + // get current time + now = btime_gettime(); + + // if some timers have already timed out, return them immediately + if (move_expired_timers(bsys, now)) { + BLog(BLOG_DEBUG, "Got already expired timers"); + return; + } + + // timeout is first timer, remember absolute time + have_timeout = 1; + timeout_abs = first_timer->absTime; + } + + // wait until the timeout is reached or the file descriptor / handle in ready + while (1) { + // compute timeout + btime_t timeout_rel = 0; // to remove warning + btime_t timeout_rel_trunc = 0; // to remove warning + if (have_timeout) { + timeout_rel = timeout_abs - now; + timeout_rel_trunc = timeout_rel; + } + + // perform wait + + #ifdef BADVPN_USE_WINAPI + + if (have_timeout) { + if (timeout_rel_trunc > INFINITE - 1) { + timeout_rel_trunc = INFINITE - 1; + } + } + + DWORD bytes = 0; + ULONG_PTR key; + BReactorIOCPOverlapped *olap = NULL; + BOOL res = GetQueuedCompletionStatus(bsys->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, (have_timeout ? timeout_rel_trunc : INFINITE)); + + ASSERT_FORCE(olap || have_timeout) + + if (olap || timeout_rel_trunc == timeout_rel) { + if (olap) { + BLog(BLOG_DEBUG, "GetQueuedCompletionStatus returned event"); + + DebugObject_Access(&olap->d_obj); + ASSERT(olap->reactor == bsys) + ASSERT(!olap->is_ready) + + set_iocp_ready(olap, (res == TRUE), bytes); + } else { + BLog(BLOG_DEBUG, "GetQueuedCompletionStatus timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_EPOLL + + if (have_timeout) { + if (timeout_rel_trunc > INT_MAX) { + timeout_rel_trunc = INT_MAX; + } + } + + BLog(BLOG_DEBUG, "Calling epoll_wait"); + + int waitres = epoll_wait(bsys->efd, bsys->epoll_results, BSYSTEM_MAX_RESULTS, (have_timeout ? timeout_rel_trunc : -1)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "epoll_wait interrupted"); + goto try_again; + } + perror("epoll_wait"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "epoll_wait returned %d file descriptors", waitres); + bsys->epoll_results_num = waitres; + set_epoll_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "epoll_wait timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + struct timespec ts; + if (have_timeout) { + if (timeout_rel_trunc > 86400000) { + timeout_rel_trunc = 86400000; + } + ts.tv_sec = timeout_rel_trunc / 1000; + ts.tv_nsec = (timeout_rel_trunc % 1000) * 1000000; + } + + BLog(BLOG_DEBUG, "Calling kevent"); + + int waitres = kevent(bsys->kqueue_fd, NULL, 0, bsys->kevent_results, BSYSTEM_MAX_RESULTS, (have_timeout ? &ts : NULL)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "kevent interrupted"); + goto try_again; + } + perror("kevent"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "kevent returned %d events", waitres); + bsys->kevent_results_num = waitres; + set_kevent_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "kevent timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_POLL + + if (have_timeout) { + if (timeout_rel_trunc > INT_MAX) { + timeout_rel_trunc = INT_MAX; + } + } + + ASSERT(bsys->poll_num_enabled_fds >= 0) + ASSERT(bsys->poll_num_enabled_fds <= BSYSTEM_MAX_POLL_FDS) + int num_fds = 0; + + LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->poll_enabled_fds_list); + while (list_node) { + BFileDescriptor *bfd = UPPER_OBJECT(list_node, BFileDescriptor, poll_enabled_fds_list_node); + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == -1) + + // calculate poll events + int pevents = 0; + if ((bfd->waitEvents & BREACTOR_READ)) { + pevents |= POLLIN; + } + if ((bfd->waitEvents & BREACTOR_WRITE)) { + pevents |= POLLOUT; + } + + // write pollfd entry + struct pollfd *pfd = &bsys->poll_results_pollfds[num_fds]; + pfd->fd = bfd->fd; + pfd->events = pevents; + pfd->revents = 0; + + // write BFileDescriptor reference entry + bsys->poll_results_bfds[num_fds] = bfd; + + // increment number of fds in array + num_fds++; + + list_node = LinkedList1Node_Next(list_node); + } + + BLog(BLOG_DEBUG, "Calling poll"); + + int waitres = poll(bsys->poll_results_pollfds, num_fds, (have_timeout ? timeout_rel_trunc : -1)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "poll interrupted"); + goto try_again; + } + perror("poll"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "poll returned %d file descriptors", waitres); + bsys->poll_results_num = num_fds; + bsys->poll_results_pos = 0; + set_poll_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "poll timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + try_again: + if (have_timeout) { + // get current time + now = btime_gettime(); + // check if we already reached the time we're waiting for + if (now >= timeout_abs) { + BLog(BLOG_DEBUG, "already timed out while trying again"); + move_first_timers(bsys); + break; + } + } + } + + // reset limit objects + LinkedList1Node *list_node; + while ((list_node = LinkedList1_GetFirst(&bsys->active_limits_list))) { + BReactorLimit *limit = UPPER_OBJECT(list_node, BReactorLimit, active_limits_list_node); + ASSERT(limit->count > 0) + limit->count = 0; + LinkedList1_Remove(&bsys->active_limits_list, &limit->active_limits_list_node); + } +} + +#ifndef BADVPN_USE_WINAPI + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user) +{ + bs->fd = fd; + bs->handler = handler; + bs->user = user; + bs->active = 0; +} + +#endif + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler) +{ + bt->handler.smalll = handler; + bt->state = TIMER_STATE_INACTIVE; + bt->is_small = 1; +} + +int BSmallTimer_IsRunning (BSmallTimer *bt) +{ + assert_timer(bt); + + return (bt->state != TIMER_STATE_INACTIVE); +} + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user) +{ + bt->base.handler.heavy = handler; + bt->base.state = TIMER_STATE_INACTIVE; + bt->base.is_small = 0; + bt->user = user; + bt->msTime = msTime; +} + +int BTimer_IsRunning (BTimer *bt) +{ + return BSmallTimer_IsRunning(&bt->base); +} + +int BReactor_Init (BReactor *bsys) +{ + BLog(BLOG_DEBUG, "Reactor initializing"); + + // set not exiting + bsys->exiting = 0; + + // init jobs + BPendingGroup_Init(&bsys->pending_jobs); + + // init timers + BReactor__TimersTree_Init(&bsys->timers_tree); + LinkedList1_Init(&bsys->timers_expired_list); + + // init limits + LinkedList1_Init(&bsys->active_limits_list); + + #ifdef BADVPN_USE_WINAPI + + // init IOCP list + LinkedList1_Init(&bsys->iocp_list); + + // init IOCP handle + if (!(bsys->iocp_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1))) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail0; + } + + // init IOCP ready list + LinkedList1_Init(&bsys->iocp_ready_list); + + #endif + + #ifdef BADVPN_USE_EPOLL + + // create epoll fd + if ((bsys->efd = epoll_create(10)) < 0) { + BLog(BLOG_ERROR, "epoll_create failed"); + goto fail0; + } + + // init results array + bsys->epoll_results_num = 0; + bsys->epoll_results_pos = 0; + + #endif + + #ifdef BADVPN_USE_KEVENT + + // create kqueue fd + if ((bsys->kqueue_fd = kqueue()) < 0) { + BLog(BLOG_ERROR, "kqueue failed"); + goto fail0; + } + + // init results array + bsys->kevent_results_num = 0; + bsys->kevent_results_pos = 0; + + #endif + + #ifdef BADVPN_USE_POLL + + // init enabled fds list + LinkedList1_Init(&bsys->poll_enabled_fds_list); + + // set zero enabled fds + bsys->poll_num_enabled_fds = 0; + + // allocate results arrays + if (!(bsys->poll_results_pollfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_pollfds[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + if (!(bsys->poll_results_bfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_bfds[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + + // init results array + bsys->poll_results_num = 0; + bsys->poll_results_pos = 0; + + #endif + + DebugObject_Init(&bsys->d_obj); + #ifndef BADVPN_USE_WINAPI + DebugCounter_Init(&bsys->d_fds_counter); + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter_Init(&bsys->d_kevent_ctr); + #endif + DebugCounter_Init(&bsys->d_limits_ctr); + + return 1; + + #ifdef BADVPN_USE_POLL +fail1: + BFree(bsys->poll_results_pollfds); + #endif +fail0: + BPendingGroup_Free(&bsys->pending_jobs); + BLog(BLOG_ERROR, "Reactor failed to initialize"); + return 0; +} + +void BReactor_Free (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + #ifdef BADVPN_USE_WINAPI + while (!LinkedList1_IsEmpty(&bsys->iocp_list)) { + BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetLast(&bsys->iocp_list), BReactorIOCPOverlapped, iocp_list_node); + ASSERT(olap->reactor == bsys) + olap->handler(olap->user, BREACTOR_IOCP_EVENT_EXITING, 0); + } + #endif + + // {pending group has no BPending objects} + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(BReactor__TimersTree_IsEmpty(&bsys->timers_tree)) + ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list)) + ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list)) + DebugObject_Free(&bsys->d_obj); + #ifdef BADVPN_USE_WINAPI + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list)) + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_list)) + #endif + #ifndef BADVPN_USE_WINAPI + DebugCounter_Free(&bsys->d_fds_counter); + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter_Free(&bsys->d_kevent_ctr); + #endif + DebugCounter_Free(&bsys->d_limits_ctr); + #ifdef BADVPN_USE_POLL + ASSERT(bsys->poll_num_enabled_fds == 0) + ASSERT(LinkedList1_IsEmpty(&bsys->poll_enabled_fds_list)) + #endif + + BLog(BLOG_DEBUG, "Reactor freeing"); + + #ifdef BADVPN_USE_WINAPI + + // close IOCP handle + ASSERT_FORCE(CloseHandle(bsys->iocp_handle)) + + #endif + + #ifdef BADVPN_USE_EPOLL + + // close epoll fd + ASSERT_FORCE(close(bsys->efd) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + // close kqueue fd + ASSERT_FORCE(close(bsys->kqueue_fd) == 0) + + #endif + + #ifdef BADVPN_USE_POLL + + // free results arrays + BFree(bsys->poll_results_bfds); + BFree(bsys->poll_results_pollfds); + + #endif + + // free jobs + BPendingGroup_Free(&bsys->pending_jobs); +} + +int BReactor_Exec (BReactor *bsys) +{ + BLog(BLOG_DEBUG, "Entering event loop"); + + while (!bsys->exiting) { + // dispatch job + if (BPendingGroup_HasJobs(&bsys->pending_jobs)) { + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + continue; + } + + // dispatch timer + LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->timers_expired_list); + if (list_node) { + BSmallTimer *timer = UPPER_OBJECT(list_node, BSmallTimer, u.list_node); + ASSERT(timer->state == TIMER_STATE_EXPIRED) + + // remove from expired list + LinkedList1_Remove(&bsys->timers_expired_list, &timer->u.list_node); + + // set inactive + timer->state = TIMER_STATE_INACTIVE; + + // call handler + BLog(BLOG_DEBUG, "Dispatching timer"); + if (timer->is_small) { + timer->handler.smalll(timer); + } else { + BTimer *btimer = UPPER_OBJECT(timer, BTimer, base); + timer->handler.heavy(btimer->user); + } + continue; + } + + #ifdef BADVPN_USE_WINAPI + + if (!LinkedList1_IsEmpty(&bsys->iocp_ready_list)) { + BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetFirst(&bsys->iocp_ready_list), BReactorIOCPOverlapped, ready_list_node); + ASSERT(olap->is_ready) + ASSERT(olap->handler) + + // remove from ready list + LinkedList1_Remove(&bsys->iocp_ready_list, &olap->ready_list_node); + + // set not ready + olap->is_ready = 0; + + int event = (olap->ready_succeeded ? BREACTOR_IOCP_EVENT_SUCCEEDED : BREACTOR_IOCP_EVENT_FAILED); + + // call handler + olap->handler(olap->user, event, olap->ready_bytes); + continue; + } + + #endif + + #ifdef BADVPN_USE_EPOLL + + // dispatch file descriptor + if (bsys->epoll_results_pos < bsys->epoll_results_num) { + // grab event + struct epoll_event *event = &bsys->epoll_results[bsys->epoll_results_pos]; + bsys->epoll_results_pos++; + + // check if the BFileDescriptor was removed + if (!event->data.ptr) { + continue; + } + + // get BFileDescriptor + BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr; + ASSERT(bfd->active) + ASSERT(bfd->epoll_returned_ptr == (BFileDescriptor **)&event->data.ptr) + + // zero pointer to the epoll entry + bfd->epoll_returned_ptr = NULL; + + // calculate events to report + int events = 0; + if ((bfd->waitEvents&BREACTOR_READ) && (event->events&EPOLLIN)) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents&BREACTOR_WRITE) && (event->events&EPOLLOUT)) { + events |= BREACTOR_WRITE; + } + if ((event->events&EPOLLERR)) { + events |= BREACTOR_ERROR; + } + if ((event->events&EPOLLHUP)) { + events |= BREACTOR_HUP; + } + + if (!events) { + BLog(BLOG_ERROR, "no events detected?"); + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // dispatch kevent + if (bsys->kevent_results_pos < bsys->kevent_results_num) { + // grab event + struct kevent *event = &bsys->kevent_results[bsys->kevent_results_pos]; + bsys->kevent_results_pos++; + + // check if the event was removed + if (!event->udata) { + continue; + } + + // check tag + int *tag = event->udata; + switch (*tag) { + case KEVENT_TAG_FD: { + // get BFileDescriptor + BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag); + ASSERT(bfd->active) + ASSERT(bfd->kevent_returned_ptr == (int **)&event->udata) + + // zero pointer to the kevent entry + bfd->kevent_returned_ptr = NULL; + + // calculate event to report + int events = 0; + if ((bfd->waitEvents&BREACTOR_READ) && event->filter == EVFILT_READ) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents&BREACTOR_WRITE) && event->filter == EVFILT_WRITE) { + events |= BREACTOR_WRITE; + } + + if (!events) { + BLog(BLOG_ERROR, "no events detected?"); + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } break; + + case KEVENT_TAG_KEVENT: { + // get BReactorKEvent + BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag); + ASSERT(kev->reactor == bsys) + ASSERT(kev->kevent_returned_ptr == (int **)&event->udata) + + // zero pointer to the kevent entry + kev->kevent_returned_ptr = NULL; + + // call handler + BLog(BLOG_DEBUG, "Dispatching kevent"); + kev->handler(kev->user, event->fflags, event->data); + continue; + } break; + + default: + ASSERT(0); + } + } + + #endif + + #ifdef BADVPN_USE_POLL + + if (bsys->poll_results_pos < bsys->poll_results_num) { + // grab event + struct pollfd *pfd = &bsys->poll_results_pollfds[bsys->poll_results_pos]; + BFileDescriptor *bfd = bsys->poll_results_bfds[bsys->poll_results_pos]; + bsys->poll_results_pos++; + + // skip removed entry + if (!bfd) { + continue; + } + + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == bsys->poll_results_pos - 1) + + // remove result reference + bfd->poll_returned_index = -1; + + // calculate events to report + int events = 0; + if ((bfd->waitEvents & BREACTOR_READ) && (pfd->revents & POLLIN)) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents & BREACTOR_WRITE) && (pfd->revents & POLLOUT)) { + events |= BREACTOR_WRITE; + } + if ((pfd->revents & POLLERR) || (pfd->revents & POLLHUP)) { + events |= BREACTOR_ERROR; + } + + if (!events) { + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } + + #endif + + wait_for_events(bsys); + } + + BLog(BLOG_DEBUG, "Exiting event loop, exit code %d", bsys->exit_code); + + return bsys->exit_code; +} + +void BReactor_Quit (BReactor *bsys, int code) +{ + bsys->exiting = 1; + bsys->exit_code = code; +} + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time) +{ + assert_timer(bt); + ASSERT(mode == BTIMER_SET_ABSOLUTE || mode == BTIMER_SET_RELATIVE) + + // unlink it if it's already in the list + BReactor_RemoveSmallTimer(bsys, bt); + + // if mode is relative, add current time + if (mode == BTIMER_SET_RELATIVE) { + time = btime_add(btime_gettime(), time); + } + + // set time + bt->absTime = time; + + // set running + bt->state = TIMER_STATE_RUNNING; + + // insert to running timers tree + BReactor__TimersTreeRef ref = {bt, bt}; + int res = BReactor__TimersTree_Insert(&bsys->timers_tree, 0, ref, NULL); + ASSERT_EXECUTE(res) +} + +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt) +{ + assert_timer(bt); + + if (bt->state == TIMER_STATE_INACTIVE) { + return; + } + + if (bt->state == TIMER_STATE_EXPIRED) { + // remove from expired list + LinkedList1_Remove(&bsys->timers_expired_list, &bt->u.list_node); + } else { + // remove from running tree + BReactor__TimersTreeRef ref = {bt, bt}; + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + } + + // set inactive + bt->state = TIMER_STATE_INACTIVE; +} + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, bt->msTime); +} + +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, after); +} + +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_ABSOLUTE, time); +} + +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt) +{ + return BReactor_RemoveSmallTimer(bsys, &bt->base); +} + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys) +{ + return &bsys->pending_jobs; +} + +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref) +{ + ASSERT(ref) + + while (!bsys->exiting) { + ASSERT(BPendingGroup_HasJobs(&bsys->pending_jobs)) + + if (BPendingGroup_PeekJob(&bsys->pending_jobs) == ref) { + return 1; + } + + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + } + + return 0; +} + +#ifndef BADVPN_USE_WINAPI + +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + ASSERT(!bs->active) + + #ifdef BADVPN_USE_EPOLL + + // add epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = 0; + event.data.ptr = bs; + if (epoll_ctl(bsys->efd, EPOLL_CTL_ADD, bs->fd, &event) < 0) { + int error = errno; + BLog(BLOG_ERROR, "epoll_ctl failed: %d", error); + return 0; + } + + // set epoll returned pointer + bs->epoll_returned_ptr = NULL; + + #endif + + #ifdef BADVPN_USE_KEVENT + + // set kevent tag + bs->kevent_tag = KEVENT_TAG_FD; + + // set kevent returned pointer + bs->kevent_returned_ptr = NULL; + + #endif + + #ifdef BADVPN_USE_POLL + + if (bsys->poll_num_enabled_fds == BSYSTEM_MAX_POLL_FDS) { + BLog(BLOG_ERROR, "too many fds"); + return 0; + } + + // append to enabled fds list + LinkedList1_Append(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node); + bsys->poll_num_enabled_fds++; + + // set not returned + bs->poll_returned_index = -1; + + #endif + + bs->active = 1; + bs->waitEvents = 0; + + DebugCounter_Increment(&bsys->d_fds_counter); + return 1; +} + +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + ASSERT(bs->active) + DebugCounter_Decrement(&bsys->d_fds_counter); + + bs->active = 0; + + #ifdef BADVPN_USE_EPOLL + + // delete epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_DEL, bs->fd, &event) == 0) + + // write through epoll returned pointer + if (bs->epoll_returned_ptr) { + *bs->epoll_returned_ptr = NULL; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // delete kevents + update_kevent_fd_events(bsys, bs, 0); + + // write through kevent returned pointer + if (bs->kevent_returned_ptr) { + *bs->kevent_returned_ptr = NULL; + } + + #endif + + #ifdef BADVPN_USE_POLL + + // invalidate results entry + if (bs->poll_returned_index != -1) { + ASSERT(bs->poll_returned_index >= bsys->poll_results_pos) + ASSERT(bs->poll_returned_index < bsys->poll_results_num) + ASSERT(bsys->poll_results_bfds[bs->poll_returned_index] == bs) + + bsys->poll_results_bfds[bs->poll_returned_index] = NULL; + } + + // remove from enabled fds list + LinkedList1_Remove(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node); + bsys->poll_num_enabled_fds--; + + #endif +} + +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events) +{ + ASSERT(bs->active) + ASSERT(!(events&~(BREACTOR_READ|BREACTOR_WRITE))) + + if (bs->waitEvents == events) { + return; + } + + #ifdef BADVPN_USE_EPOLL + + // calculate epoll events + int eevents = 0; + if ((events & BREACTOR_READ)) { + eevents |= EPOLLIN; + } + if ((events & BREACTOR_WRITE)) { + eevents |= EPOLLOUT; + } + + // update epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = eevents; + event.data.ptr = bs; + ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_MOD, bs->fd, &event) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + update_kevent_fd_events(bsys, bs, events); + + #endif + + // update events + bs->waitEvents = events; +} + +#endif + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit) +{ + DebugObject_Access(&reactor->d_obj); + ASSERT(limit > 0) + + // init arguments + o->reactor = reactor; + o->limit = limit; + + // set count zero + o->count = 0; + + DebugCounter_Increment(&reactor->d_limits_ctr); + DebugObject_Init(&o->d_obj); +} + +void BReactorLimit_Free (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&reactor->d_limits_ctr); + + // remove from active limits list + if (o->count > 0) { + LinkedList1_Remove(&reactor->active_limits_list, &o->active_limits_list_node); + } +} + +int BReactorLimit_Increment (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // check count against limit + if (o->count >= o->limit) { + return 0; + } + + // increment count + o->count++; + + // if limit was zero, add to active limits list + if (o->count == 1) { + LinkedList1_Append(&reactor->active_limits_list, &o->active_limits_list_node); + } + + return 1; +} + +void BReactorLimit_SetLimit (BReactorLimit *o, int limit) +{ + DebugObject_Access(&o->d_obj); + ASSERT(limit > 0) + + // set limit + o->limit = limit; +} + +#ifdef BADVPN_USE_KEVENT + +int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data) +{ + DebugObject_Access(&reactor->d_obj); + + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + o->ident = ident; + o->filter = filter; + + // add kevent + struct kevent event; + memset(&event, 0, sizeof(event)); + event.ident = o->ident; + event.filter = o->filter; + event.flags = EV_ADD; + event.fflags = fflags; + event.data = data; + event.udata = &o->kevent_tag; + if (kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) < 0) { + return 0; + } + + // set kevent tag + o->kevent_tag = KEVENT_TAG_KEVENT; + + // set kevent returned pointer + o->kevent_returned_ptr = NULL; + + DebugObject_Init(&o->d_obj); + DebugCounter_Increment(&o->reactor->d_kevent_ctr); + return 1; +} + +void BReactorKEvent_Free (BReactorKEvent *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->reactor->d_kevent_ctr); + + // write through kevent returned pointer + if (o->kevent_returned_ptr) { + *o->kevent_returned_ptr = NULL; + } + + // delete kevent + struct kevent event; + memset(&event, 0, sizeof(event)); + event.ident = o->ident; + event.filter = o->filter; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) +} + +#endif + +#ifdef BADVPN_USE_WINAPI + +HANDLE BReactor_GetIOCPHandle (BReactor *reactor) +{ + DebugObject_Access(&reactor->d_obj); + + return reactor->iocp_handle; +} + +void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler) +{ + DebugObject_Access(&reactor->d_obj); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // zero overlapped + memset(&o->olap, 0, sizeof(o->olap)); + + // append to IOCP list + LinkedList1_Append(&reactor->iocp_list, &o->iocp_list_node); + + // set not ready + o->is_ready = 0; + + DebugObject_Init(&o->d_obj); +} + +void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + + // remove from IOCP ready list + if (o->is_ready) { + LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node); + } + + // remove from IOCP list + LinkedList1_Remove(&reactor->iocp_list, &o->iocp_list_node); +} + +void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // wait for IOCP events until we get an event for this olap + while (!o->is_ready) { + DWORD bytes = 0; + ULONG_PTR key; + BReactorIOCPOverlapped *olap = NULL; + BOOL res = GetQueuedCompletionStatus(reactor->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, INFINITE); + + ASSERT_FORCE(olap) + DebugObject_Access(&olap->d_obj); + ASSERT(olap->reactor == reactor) + + // regular I/O should be done synchronously, so we shoudln't ever get a second completion before an + // existing one is dispatched. If however PostQueuedCompletionStatus is being used to signal events, + // just discard any excess events. + if (!olap->is_ready) { + set_iocp_ready(olap, (res == TRUE), bytes); + } + } + + // remove from IOCP ready list + LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node); + + // set not ready + o->is_ready = 0; + + if (out_succeeded) { + *out_succeeded = o->ready_succeeded; + } + if (out_bytes) { + *out_bytes = o->ready_bytes; + } +} + +#endif diff --git a/service/src/main/jni/badvpn/system/BReactor_badvpn.h b/service/src/main/jni/badvpn/system/BReactor_badvpn.h new file mode 100644 index 0000000..2c5ab4c --- /dev/null +++ b/service/src/main/jni/badvpn/system/BReactor_badvpn.h @@ -0,0 +1,572 @@ +/** + * @file BReactor_badvpn.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events + * and timers. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_USE_EPOLL) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_POLL)) != 1 +#error Unknown event backend or too many event backends +#endif + +#ifdef BADVPN_USE_WINAPI +#include +#endif + +#ifdef BADVPN_USE_EPOLL +#include +#endif + +#ifdef BADVPN_USE_KEVENT +#include +#include +#include +#endif + +#ifdef BADVPN_USE_POLL +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +struct BSmallTimer_t; +typedef struct BSmallTimer_t *BReactor_timerstree_link; + +#include "BReactor_badvpn_timerstree.h" +#include + +#define BTIMER_SET_ABSOLUTE 1 +#define BTIMER_SET_RELATIVE 2 + +/** + * Handler function invoked when the timer expires. + * The timer was in running state. + * The timer enters not running state before this function is invoked. + * This function is being called from within the timer's previosly + * associated reactor. + * + * @param timer pointer to the timer. Use the {@link UPPER_OBJECT} macro + * to obtain the pointer to the containing structure. + */ +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); + +/** + * Handler function invoked when the timer expires. + * The timer was in running state. + * The timer enters not running state before this function is invoked. + * This function is being called from within the timer's previosly + * associated reactor. + * + * @param user value passed to {@link BTimer_Init} + */ +typedef void (*BTimer_handler) (void *user); + +/** + * Timer object used with {@link BReactor}. + */ +typedef struct BSmallTimer_t { + union { + BSmallTimer_handler smalll; // MSVC doesn't like "small" + BTimer_handler heavy; + } handler; + union { + LinkedList1Node list_node; + struct BSmallTimer_t *tree_child[2]; + } u; + struct BSmallTimer_t *tree_parent; + btime_t absTime; + int8_t tree_balance; + uint8_t state; + uint8_t is_small; +} BSmallTimer; + +/** + * Initializes the timer object. + * The timer object is initialized in not running state. + * + * @param bt the object + * @param handler handler function invoked when the timer expires + */ +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); + +/** + * Checks if the timer is running. + * + * @param bt the object + * @return 1 if running, 0 if not running + */ +int BSmallTimer_IsRunning (BSmallTimer *bt); + +/** + * Timer object used with {@link BReactor}. This is a legacy wrapper + * around {@link BSmallTimer} with an extra field for the default time. + */ +typedef struct { + BSmallTimer base; + void *user; + btime_t msTime; +} BTimer; + +/** + * Initializes the timer object. + * The timer object is initialized in not running state. + * + * @param bt the object + * @param msTime default timeout in milliseconds + * @param handler handler function invoked when the timer expires + * @param user value to pass to the handler function + */ +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); + +/** + * Checks if the timer is running. + * + * @param bt the object + * @return 1 if running, 0 if not running + */ +int BTimer_IsRunning (BTimer *bt); + +#ifndef BADVPN_USE_WINAPI + +struct BFileDescriptor_t; + +#define BREACTOR_READ (1 << 0) +#define BREACTOR_WRITE (1 << 1) +#define BREACTOR_ERROR (1 << 2) +#define BREACTOR_HUP (1 << 3) + +/** + * Handler function invoked by the reactor when one or more events are detected. + * The events argument will contain a subset of the monitored events (BREACTOR_READ, BREACTOR_WRITE), + * plus possibly the error event (BREACTOR_ERROR). + * The file descriptor object is in active state, being called from within + * the associated reactor. + * + * @param user value passed to {@link BFileDescriptor_Init} + * @param events bitmask composed of a subset of monitored events (BREACTOR_READ, BREACTOR_WRITE), + * and possibly the error event BREACTOR_ERROR and the hang-up event BREACTOR_HUP. + * Will be nonzero. + */ +typedef void (*BFileDescriptor_handler) (void *user, int events); + +/** + * File descriptor object used with {@link BReactor}. + */ +typedef struct BFileDescriptor_t { + int fd; + BFileDescriptor_handler handler; + void *user; + int active; + int waitEvents; + + #ifdef BADVPN_USE_EPOLL + struct BFileDescriptor_t **epoll_returned_ptr; + #endif + + #ifdef BADVPN_USE_KEVENT + int kevent_tag; + int **kevent_returned_ptr; + #endif + + #ifdef BADVPN_USE_POLL + LinkedList1Node poll_enabled_fds_list_node; + int poll_returned_index; + #endif +} BFileDescriptor; + +/** + * Intializes the file descriptor object. + * The object is initialized in not active state. + * + * @param bs file descriptor object to initialize + * @param fb file descriptor to represent + * @param handler handler function invoked by the reactor when a monitored event is detected + * @param user value passed to the handler functuon + */ +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user); + +#endif + +// BReactor + +#define BSYSTEM_MAX_RESULTS 64 +#define BSYSTEM_MAX_HANDLES 64 +#define BSYSTEM_MAX_POLL_FDS 4096 + +/** + * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events + * and timers. + */ +typedef struct { + int exiting; + int exit_code; + + // jobs + BPendingGroup pending_jobs; + + // timers + BReactor__TimersTree timers_tree; + LinkedList1 timers_expired_list; + + // limits + LinkedList1 active_limits_list; + + #ifdef BADVPN_USE_WINAPI + LinkedList1 iocp_list; + HANDLE iocp_handle; + LinkedList1 iocp_ready_list; + #endif + + #ifdef BADVPN_USE_EPOLL + int efd; // epoll fd + struct epoll_event epoll_results[BSYSTEM_MAX_RESULTS]; // epoll returned events buffer + int epoll_results_num; // number of events in the array + int epoll_results_pos; // number of events processed so far + #endif + + #ifdef BADVPN_USE_KEVENT + int kqueue_fd; + struct kevent kevent_results[BSYSTEM_MAX_RESULTS]; + int kevent_results_num; + int kevent_results_pos; + #endif + + #ifdef BADVPN_USE_POLL + LinkedList1 poll_enabled_fds_list; + int poll_num_enabled_fds; + int poll_results_num; + int poll_results_pos; + struct pollfd *poll_results_pollfds; + BFileDescriptor **poll_results_bfds; + #endif + + DebugObject d_obj; + #ifndef BADVPN_USE_WINAPI + DebugCounter d_fds_counter; + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter d_kevent_ctr; + #endif + DebugCounter d_limits_ctr; +} BReactor; + +/** + * Initializes the reactor. + * {@link BLog_Init} must have been done. + * {@link BTime_Init} must have been done. + * + * @param bsys the object + * @return 1 on success, 0 on failure + */ +int BReactor_Init (BReactor *bsys) WARN_UNUSED; + +/** + * Frees the reactor. + * Must not be called from within the event loop ({@link BReactor_Exec}). + * There must be no {@link BPending} or {@link BSmallPending} objects using the + * pending group returned by {@link BReactor_PendingGroup}. + * There must be no running timers in this reactor. + * There must be no limit objects in this reactor. + * There must be no file descriptors or handles registered + * with this reactor. + * There must be no {@link BReactorKEvent} objects in this reactor. + * + * @param bsys the object + */ +void BReactor_Free (BReactor *bsys); + +/** + * Runs the event loop. + * + * @param bsys the object + * @return value passed to {@link BReactor_Quit} + */ +int BReactor_Exec (BReactor *bsys); + +/** + * Causes the event loop ({@link BReactor_Exec}) to cease + * dispatching events and return. + * Any further calls of {@link BReactor_Exec} will return immediately. + * + * @param bsys the object + * @param code value {@link BReactor_Exec} should return. If this is + * called more than once, it will return the last code. + */ +void BReactor_Quit (BReactor *bsys, int code); + +/** + * Starts a timer to expire at the specified time. + * The timer must have been initialized with {@link BSmallTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + * @param mode interpretation of time (BTIMER_SET_ABSOLUTE or BTIMER_SET_RELATIVE) + * @param time absolute or relative expiration time + */ +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); + +/** + * Stops a timer. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters not running state. + * + * @param bsys the object + * @param bt timer to stop + */ +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); + +/** + * Starts a timer to expire after its default time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + */ +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); + +/** + * Starts a timer to expire after a given time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + * @param after relative expiration time + */ +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); + +/** + * Starts a timer to expire at the specified time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * The timer's expiration time is set to the time argument. + * + * @param bsys the object + * @param bt timer to start + * @param time absolute expiration time (according to {@link btime_gettime}) + */ +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time); + +/** + * Stops a timer. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters not running state. + * + * @param bsys the object + * @param bt timer to stop + */ +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); + +/** + * Returns a {@link BPendingGroup} object that can be used to schedule jobs for + * the reactor to execute. These jobs have complete priority over other events + * (timers, file descriptors and Windows handles). + * The returned pending group may only be used as an argument to {@link BPending_Init}, + * and must not be accessed by other means. + * All {@link BPending} and {@link BSmallPending} objects using this group must be + * freed before freeing the reactor. + * + * @param bsys the object + * @return pending group for scheduling jobs for the reactor to execute + */ +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); + +/** + * Executes pending jobs until either: + * - the reference job is reached, or + * - {@link BReactor_Quit} is called. + * The reference job must be reached before the job list empties. + * The reference job will not be executed. + * + * WARNING: Use with care. This should only be used to to work around third-party software + * that does not integrade into the jobs system. In particular, you should think about: + * - the effects the jobs to be executed may have, and + * - the environment those jobs expect to be executed in. + * + * @param bsys the object + * @param ref reference job. It is not accessed in any way, only its address is compared to + * pending jobs before they are executed. + * @return 1 if the reference job was reached, + * 0 if {@link BReactor_Quit} was called (either while executing a job, or before) + */ +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref); + +#ifndef BADVPN_USE_WINAPI + +/** + * Starts monitoring a file descriptor. + * + * @param bsys the object + * @param bs file descriptor object. Must have been initialized with + * {@link BFileDescriptor_Init} Must be in not active state. + * On success, the file descriptor object enters active state, + * associated with this reactor. + * @return 1 on success, 0 on failure + */ +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED; + +/** + * Stops monitoring a file descriptor. + * + * @param bsys the object + * @param bs {@link BFileDescriptor} object. Must be in active state, + * associated with this reactor. The file descriptor object + * enters not active state. + */ +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs); + +/** + * Sets monitored file descriptor events. + * + * @param bsys the object + * @param bs {@link BFileDescriptor} object. Must be in active state, + * associated with this reactor. + * @param events events to watch for. Must not have any bits other than + * BREACTOR_READ and BREACTOR_WRITE. + * This overrides previosly monitored events. + */ +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events); + +#endif + +typedef struct { + BReactor *reactor; + int limit; + int count; + LinkedList1Node active_limits_list_node; + DebugObject d_obj; +} BReactorLimit; + +/** + * Initializes a limit object. + * A limit object consists of a counter integer, which is initialized to + * zero, is incremented by {@link BReactorLimit_Increment} up to \a limit, + * and is reset to zero every time the event loop performs a wait. + * If the event loop has processed all detected events, and before performing + * a wait, it determines that timers have expired, the counter will not be reset. + * + * @param o the object + * @param reactor reactor the object is tied to + * @param limit maximum counter value. Must be >0. + */ +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit); + +/** + * Frees a limit object. + * + * @param o the object + */ +void BReactorLimit_Free (BReactorLimit *o); + +/** + * Attempts to increment the counter of a limit object. + * + * @param o the object + * @return 1 if the counter was lesser than the limit and was incremented, + * 0 if the counter was greater or equal to the limit and could not be + * incremented + */ +int BReactorLimit_Increment (BReactorLimit *o); + +/** + * Sets the limit of a limit object. + * + * @param o the object + * @param limit new limit. Must be >0. + */ +void BReactorLimit_SetLimit (BReactorLimit *o, int limit); + +#ifdef BADVPN_USE_KEVENT + +typedef void (*BReactorKEvent_handler) (void *user, u_int fflags, intptr_t data); + +typedef struct { + BReactor *reactor; + BReactorKEvent_handler handler; + void *user; + uintptr_t ident; + short filter; + int kevent_tag; + int **kevent_returned_ptr; + DebugObject d_obj; +} BReactorKEvent; + +int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data); +void BReactorKEvent_Free (BReactorKEvent *o); + +#endif + +#ifdef BADVPN_USE_WINAPI + +#define BREACTOR_IOCP_EVENT_SUCCEEDED 1 +#define BREACTOR_IOCP_EVENT_FAILED 2 +#define BREACTOR_IOCP_EVENT_EXITING 3 + +typedef void (*BReactorIOCPOverlapped_handler) (void *user, int event, DWORD bytes); + +typedef struct { + OVERLAPPED olap; + BReactor *reactor; + void *user; + BReactorIOCPOverlapped_handler handler; + LinkedList1Node iocp_list_node; + int is_ready; + LinkedList1Node ready_list_node; + int ready_succeeded; + DWORD ready_bytes; + DebugObject d_obj; +} BReactorIOCPOverlapped; + +HANDLE BReactor_GetIOCPHandle (BReactor *reactor); + +void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler); +void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o); +void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes); + +#endif + +#endif diff --git a/service/src/main/jni/badvpn/system/BReactor_badvpn_timerstree.h b/service/src/main/jni/badvpn/system/BReactor_badvpn_timerstree.h new file mode 100644 index 0000000..3cecd75 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BReactor_badvpn_timerstree.h @@ -0,0 +1,13 @@ +#define CAVL_PARAM_NAME BReactor__TimersTree +#define CAVL_PARAM_FEATURE_COUNTS 0 +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS 1 +#define CAVL_PARAM_TYPE_ENTRY struct BSmallTimer_t +#define CAVL_PARAM_TYPE_LINK BReactor_timerstree_link +#define CAVL_PARAM_TYPE_ARG int +#define CAVL_PARAM_VALUE_NULL ((BReactor_timerstree_link)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_timers((entry1).link, (entry2).link) +#define CAVL_PARAM_MEMBER_CHILD u.tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent diff --git a/service/src/main/jni/badvpn/system/BReactor_emscripten.h b/service/src/main/jni/badvpn/system/BReactor_emscripten.h new file mode 100644 index 0000000..c004d16 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BReactor_emscripten.h @@ -0,0 +1,87 @@ +/** + * @file BReactor_emscripten.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#include + +#include +#include +#include + +#define BTIMER_SET_RELATIVE 2 + +typedef struct BReactor_s BReactor; + +typedef void (*BTimer_handler) (void *user); + +typedef struct BTimer_t { + btime_t msTime; + BTimer_handler handler; + void *handler_pointer; + uint8_t active; + int timerid; + BReactor *reactor; +} BTimer; + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); +int BTimer_IsRunning (BTimer *bt); + +struct BSmallTimer_t; + +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); + +typedef struct BSmallTimer_t { + BTimer timer; + BSmallTimer_handler handler; +} BSmallTimer; + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); +int BSmallTimer_IsRunning (BSmallTimer *bt); + +struct BReactor_s { + BPendingGroup pending_jobs; + DebugObject d_obj; +}; + +void BReactor_EmscriptenInit (BReactor *bsys); +void BReactor_EmscriptenFree (BReactor *bsys); +void BReactor_EmscriptenSync (BReactor *bsys); + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); + +#endif diff --git a/service/src/main/jni/badvpn/system/BReactor_glib.h b/service/src/main/jni/badvpn/system/BReactor_glib.h new file mode 100644 index 0000000..0102be9 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BReactor_glib.h @@ -0,0 +1,148 @@ +/** + * @file BReactor_glib.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct BReactor_s BReactor; + +struct BSmallTimer_t; + +#define BTIMER_SET_ABSOLUTE 1 +#define BTIMER_SET_RELATIVE 2 + +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); +typedef void (*BTimer_handler) (void *user); + +typedef struct BSmallTimer_t { + union { + BSmallTimer_handler smalll; // MSVC doesn't like "small" + BTimer_handler heavy; + } handler; + BReactor *reactor; + GSource *source; + uint8_t active; + uint8_t is_small; +} BSmallTimer; + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); +int BSmallTimer_IsRunning (BSmallTimer *bt); + +typedef struct { + BSmallTimer base; + void *user; + btime_t msTime; +} BTimer; + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); +int BTimer_IsRunning (BTimer *bt); + +struct BFileDescriptor_t; + +#define BREACTOR_READ (1 << 0) +#define BREACTOR_WRITE (1 << 1) +#define BREACTOR_ERROR (1 << 2) +#define BREACTOR_HUP (1 << 3) + +typedef void (*BFileDescriptor_handler) (void *user, int events); + +typedef struct BFileDescriptor_t { + int fd; + BFileDescriptor_handler handler; + void *user; + int active; + int waitEvents; + BReactor *reactor; + GSource *source; + GPollFD pollfd; +} BFileDescriptor; + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user); + +struct BReactor_s { + int exiting; + int exit_code; + GMainLoop *gloop; + int unref_gloop_on_free; + GSourceFuncs fd_source_funcs; + BPendingGroup pending_jobs; + LinkedList1 active_limits_list; + + DebugCounter d_fds_counter; + DebugCounter d_limits_ctr; + DebugCounter d_timers_ctr; + DebugObject d_obj; +}; + +int BReactor_Init (BReactor *bsys) WARN_UNUSED; +void BReactor_Free (BReactor *bsys); +int BReactor_Exec (BReactor *bsys); +void BReactor_Quit (BReactor *bsys, int code); +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time); +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref); +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED; +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs); +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events); + +int BReactor_InitFromExistingGMainLoop (BReactor *bsys, GMainLoop *gloop, int unref_gloop_on_free); +GMainLoop * BReactor_GetGMainLoop (BReactor *bsys); +int BReactor_SynchronizeAll (BReactor *bsys); + +typedef struct { + BReactor *reactor; + int limit; + int count; + LinkedList1Node active_limits_list_node; + DebugObject d_obj; +} BReactorLimit; + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit); +void BReactorLimit_Free (BReactorLimit *o); +int BReactorLimit_Increment (BReactorLimit *o); +void BReactorLimit_SetLimit (BReactorLimit *o, int limit); + +#endif diff --git a/service/src/main/jni/badvpn/system/BSignal.c b/service/src/main/jni/badvpn/system/BSignal.c new file mode 100644 index 0000000..520a167 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BSignal.c @@ -0,0 +1,188 @@ +/** + * @file BSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#endif + +#include +#include + +#include + +#include + +static struct { + int initialized; + int finished; + BReactor *reactor; + BSignal_handler handler; + void *user; + #ifdef BADVPN_USE_WINAPI + BReactorIOCPOverlapped olap; + CRITICAL_SECTION iocp_handle_mutex; + HANDLE iocp_handle; + #else + BUnixSignal signal; + #endif +} bsignal_global = { + 0 +}; + +#ifdef BADVPN_USE_WINAPI + +static void olap_handler (void *user, int event, DWORD bytes) +{ + ASSERT(bsignal_global.initialized) + ASSERT(!(event == BREACTOR_IOCP_EVENT_EXITING) || bsignal_global.finished) + + if (event == BREACTOR_IOCP_EVENT_EXITING) { + BReactorIOCPOverlapped_Free(&bsignal_global.olap); + return; + } + + if (!bsignal_global.finished) { + // call handler + bsignal_global.handler(bsignal_global.user); + return; + } +} + +static BOOL WINAPI ctrl_handler (DWORD type) +{ + EnterCriticalSection(&bsignal_global.iocp_handle_mutex); + + if (bsignal_global.iocp_handle) { + PostQueuedCompletionStatus(bsignal_global.iocp_handle, 0, 0, &bsignal_global.olap.olap); + } + + LeaveCriticalSection(&bsignal_global.iocp_handle_mutex); + + return TRUE; +} + +#else + +static void unix_signal_handler (void *user, int signo) +{ + ASSERT(signo == SIGTERM || signo == SIGINT) + ASSERT(bsignal_global.initialized) + ASSERT(!bsignal_global.finished) + + BLog(BLOG_DEBUG, "Dispatching signal"); + + // call handler + bsignal_global.handler(bsignal_global.user); + return; +} + +#endif + +int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user) +{ + ASSERT(!bsignal_global.initialized) + + // init arguments + bsignal_global.reactor = reactor; + bsignal_global.handler = handler; + bsignal_global.user = user; + + BLog(BLOG_DEBUG, "BSignal initializing"); + + #ifdef BADVPN_USE_WINAPI + + // init olap + BReactorIOCPOverlapped_Init(&bsignal_global.olap, bsignal_global.reactor, NULL, olap_handler); + + // init handler mutex + InitializeCriticalSection(&bsignal_global.iocp_handle_mutex); + + // remember IOCP handle + bsignal_global.iocp_handle = BReactor_GetIOCPHandle(bsignal_global.reactor); + + // configure ctrl handler + if (!SetConsoleCtrlHandler(ctrl_handler, TRUE)) { + BLog(BLOG_ERROR, "SetConsoleCtrlHandler failed"); + goto fail1; + } + + #else + + sigset_t sset; + ASSERT_FORCE(sigemptyset(&sset) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGTERM) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGINT) == 0) + + // init BUnixSignal + if (!BUnixSignal_Init(&bsignal_global.signal, bsignal_global.reactor, sset, unix_signal_handler, NULL)) { + BLog(BLOG_ERROR, "BUnixSignal_Init failed"); + goto fail0; + } + + #endif + + bsignal_global.initialized = 1; + bsignal_global.finished = 0; + + return 1; + + #ifdef BADVPN_USE_WINAPI +fail1: + DeleteCriticalSection(&bsignal_global.iocp_handle_mutex); + BReactorIOCPOverlapped_Free(&bsignal_global.olap); + #endif + +fail0: + return 0; +} + +void BSignal_Finish (void) +{ + ASSERT(bsignal_global.initialized) + ASSERT(!bsignal_global.finished) + + #ifdef BADVPN_USE_WINAPI + + // forget IOCP handle + EnterCriticalSection(&bsignal_global.iocp_handle_mutex); + bsignal_global.iocp_handle = NULL; + LeaveCriticalSection(&bsignal_global.iocp_handle_mutex); + + #else + + // free BUnixSignal + BUnixSignal_Free(&bsignal_global.signal, 0); + + #endif + + bsignal_global.finished = 1; +} diff --git a/service/src/main/jni/badvpn/system/BSignal.h b/service/src/main/jni/badvpn/system/BSignal.h new file mode 100644 index 0000000..41f17e2 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BSignal.h @@ -0,0 +1,64 @@ +/** + * @file BSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A global object for catching program termination requests. + */ + +#ifndef BADVPN_SYSTEM_BSIGNAL_H +#define BADVPN_SYSTEM_BSIGNAL_H + +#include +#include + +typedef void (*BSignal_handler) (void *user); + +/** + * Initializes signal handling. + * The object is created in not capturing state. + * {@link BLog_Init} must have been done. + * + * WARNING: make sure this won't interfere with other components: + * - on Linux, this uses {@link BUnixSignal} to catch SIGTERM and SIGINT, + * - on Windows, this sets up a handler with SetConsoleCtrlHandler. + * + * @param reactor {@link BReactor} from which the handler will be called + * @param handler callback function invoked from the reactor + * @param user value passed to callback function + * @return 1 on success, 0 on failure + */ +int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user) WARN_UNUSED; + +/** + * Finishes signal handling. + * {@link BSignal_Init} must not be called again. + */ +void BSignal_Finish (void); + +#endif diff --git a/service/src/main/jni/badvpn/system/BTime.c b/service/src/main/jni/badvpn/system/BTime.c new file mode 100644 index 0000000..a1fa9e7 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BTime.c @@ -0,0 +1,38 @@ +/** + * @file BTime.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifndef BADVPN_PLUGIN +struct _BTime_global btime_global = { + #ifndef NDEBUG + 0 + #endif +}; +#endif diff --git a/service/src/main/jni/badvpn/system/BTime.h b/service/src/main/jni/badvpn/system/BTime.h new file mode 100644 index 0000000..6998814 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BTime.h @@ -0,0 +1,163 @@ +/** + * @file BTime.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * System time abstraction used by {@link BReactor}. + */ + +#ifndef BADVPN_SYSTEM_BTIME_H +#define BADVPN_SYSTEM_BTIME_H + +#if defined(BADVPN_USE_WINAPI) +#include +#elif defined(BADVPN_EMSCRIPTEN) +#include +#else +#include +#include +#endif + +#include + +#include +#include +#include + +#include + +typedef int64_t btime_t; + +#define BTIME_MIN INT64_MIN + +struct _BTime_global { + #ifndef NDEBUG + int initialized; // initialized statically + #endif + #if defined(BADVPN_USE_WINAPI) + LARGE_INTEGER start_time; + #elif defined(BADVPN_EMSCRIPTEN) + btime_t start_time; + #else + btime_t start_time; + int use_gettimeofday; + #endif +}; + +extern struct _BTime_global btime_global; + +static void BTime_Init (void) +{ + ASSERT(!btime_global.initialized) + + #if defined(BADVPN_USE_WINAPI) + + ASSERT_FORCE(QueryPerformanceCounter(&btime_global.start_time)) + + #elif defined(BADVPN_EMSCRIPTEN) + + btime_global.start_time = emscripten_get_now(); + + #else + + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { + BLog(BLOG_WARNING, "CLOCK_MONOTONIC is not available. Timers will be confused by clock changes."); + + struct timeval tv; + ASSERT_FORCE(gettimeofday(&tv, NULL) == 0) + + btime_global.start_time = (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec/1000; + btime_global.use_gettimeofday = 1; + } else { + btime_global.start_time = (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec/1000000; + btime_global.use_gettimeofday = 0; + } + + #endif + + #ifndef NDEBUG + btime_global.initialized = 1; + #endif +} + +static btime_t btime_gettime (void) +{ + ASSERT(btime_global.initialized) + + #if defined(BADVPN_USE_WINAPI) + + LARGE_INTEGER count; + LARGE_INTEGER freq; + ASSERT_FORCE(QueryPerformanceCounter(&count)) + ASSERT_FORCE(QueryPerformanceFrequency(&freq)) + return (((count.QuadPart - btime_global.start_time.QuadPart) * 1000) / freq.QuadPart); + + #elif defined(BADVPN_EMSCRIPTEN) + + return (btime_t)emscripten_get_now() - btime_global.start_time; + + #else + + if (btime_global.use_gettimeofday) { + struct timeval tv; + ASSERT_FORCE(gettimeofday(&tv, NULL) == 0) + return ((int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec/1000); + } else { + struct timespec ts; + ASSERT_FORCE(clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (((int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec/1000000) - btime_global.start_time); + } + + #endif +} + +static btime_t btime_add (btime_t t1, btime_t t2) +{ + // handle overflow + int overflows = add_int64_overflows(t1, t2); + btime_t sum; + if (overflows != 0) { + if (overflows > 0) { + sum = INT64_MAX; + } else { + sum = INT64_MIN; + } + } else { + sum = t1 + t2; + } + + return sum; +} + +static btime_t btime_getpast (void) +{ + return INT64_MIN; +} + +#endif diff --git a/service/src/main/jni/badvpn/system/BUnixSignal.c b/service/src/main/jni/badvpn/system/BUnixSignal.c new file mode 100644 index 0000000..94edd6b --- /dev/null +++ b/service/src/main/jni/badvpn/system/BUnixSignal.c @@ -0,0 +1,406 @@ +/** + * @file BUnixSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_SIGNALFD +#include +#endif + +#include +#include +#include + +#include + +#include + +#define BUNIXSIGNAL_MAX_SIGNALS 64 + +#ifdef BADVPN_USE_SIGNALFD + +static void signalfd_handler (BUnixSignal *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // read a signal + struct signalfd_siginfo siginfo; + int bytes = read(o->signalfd_fd, &siginfo, sizeof(siginfo)); + if (bytes < 0) { + int error = errno; + if (error == EAGAIN || error == EWOULDBLOCK) { + return; + } + BLog(BLOG_ERROR, "read failed (%d)", error); + return; + } + ASSERT_FORCE(bytes == sizeof(siginfo)) + + // check signal + if (siginfo.ssi_signo > INT_MAX) { + BLog(BLOG_ERROR, "read returned out of int range signo (%"PRIu32")", siginfo.ssi_signo); + return; + } + int signo = siginfo.ssi_signo; + if (sigismember(&o->signals, signo) <= 0) { + BLog(BLOG_ERROR, "read returned wrong signo (%d)", signo); + return; + } + + BLog(BLOG_DEBUG, "dispatching signal %d", signo); + + // call handler + o->handler(o->user, signo); + return; +} + +#endif + +#ifdef BADVPN_USE_KEVENT + +static void kevent_handler (struct BUnixSignal_kevent_entry *entry, u_int fflags, intptr_t data) +{ + BUnixSignal *o = entry->parent; + DebugObject_Access(&o->d_obj); + + // call signal + o->handler(o->user, entry->signo); + return; +} + +#endif + +#ifdef BADVPN_USE_SELFPIPE + +struct BUnixSignal_selfpipe_entry *bunixsignal_selfpipe_entries[BUNIXSIGNAL_MAX_SIGNALS]; + +static void free_selfpipe_entry (struct BUnixSignal_selfpipe_entry *entry) +{ + BUnixSignal *o = entry->parent; + + // uninstall signal handler + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + ASSERT_FORCE(sigaction(entry->signo, &act, NULL) == 0) + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd); + + // close pipe + ASSERT_FORCE(close(entry->pipefds[0]) == 0) + ASSERT_FORCE(close(entry->pipefds[1]) == 0) +} + +static void pipe_read_fd_handler (struct BUnixSignal_selfpipe_entry *entry, int events) +{ + BUnixSignal *o = entry->parent; + DebugObject_Access(&o->d_obj); + + // read a byte + uint8_t b; + if (read(entry->pipefds[0], &b, sizeof(b)) < 0) { + int error = errno; + if (error == EAGAIN || error == EWOULDBLOCK) { + return; + } + BLog(BLOG_ERROR, "read failed (%d)", error); + return; + } + + // call handler + o->handler(o->user, entry->signo); + return; +} + +static void signal_handler (int signo) +{ + ASSERT(signo >= 0) + ASSERT(signo < BUNIXSIGNAL_MAX_SIGNALS) + + struct BUnixSignal_selfpipe_entry *entry = bunixsignal_selfpipe_entries[signo]; + + uint8_t b = 0; + write(entry->pipefds[1], &b, sizeof(b)); +} + +#endif + +int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user) +{ + // init arguments + o->reactor = reactor; + o->signals = signals; + o->handler = handler; + o->user = user; + + #ifdef BADVPN_USE_SIGNALFD + + // init signalfd fd + if ((o->signalfd_fd = signalfd(-1, &o->signals, 0)) < 0) { + BLog(BLOG_ERROR, "signalfd failed"); + goto fail0; + } + + // set non-blocking + if (fcntl(o->signalfd_fd, F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "cannot set non-blocking"); + goto fail1; + } + + // init signalfd BFileDescriptor + BFileDescriptor_Init(&o->signalfd_bfd, o->signalfd_fd, (BFileDescriptor_handler)signalfd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->signalfd_bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->signalfd_bfd, BREACTOR_READ); + + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + goto fail2; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // count signals + int num_signals = 0; + for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + num_signals++; + } + + // allocate array + if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init kevents + o->num_entries = 0; + for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + struct BUnixSignal_kevent_entry *entry = &o->entries[o->num_entries]; + entry->parent = o; + entry->signo = i; + if (!BReactorKEvent_Init(&entry->kevent, o->reactor, (BReactorKEvent_handler)kevent_handler, entry, entry->signo, EVFILT_SIGNAL, 0, 0)) { + BLog(BLOG_ERROR, "BReactorKEvent_Init failed"); + goto fail2; + } + o->num_entries++; + } + + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + goto fail2; + } + + #endif + + #ifdef BADVPN_USE_SELFPIPE + + // count signals + int num_signals = 0; + for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + num_signals++; + } + + // allocate array + if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init entries + o->num_entries = 0; + for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + + struct BUnixSignal_selfpipe_entry *entry = &o->entries[o->num_entries]; + entry->parent = o; + entry->signo = i; + + // init pipe + if (pipe(entry->pipefds) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto loop_fail0; + } + + // set pipe ends non-blocking + if (!badvpn_set_nonblocking(entry->pipefds[0]) || !badvpn_set_nonblocking(entry->pipefds[1])) { + BLog(BLOG_ERROR, "set nonblocking failed"); + goto loop_fail1; + } + + // init read end BFileDescriptor + BFileDescriptor_Init(&entry->pipe_read_bfd, entry->pipefds[0], (BFileDescriptor_handler)pipe_read_fd_handler, entry); + if (!BReactor_AddFileDescriptor(o->reactor, &entry->pipe_read_bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto loop_fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &entry->pipe_read_bfd, BREACTOR_READ); + + // set global entry pointer + bunixsignal_selfpipe_entries[entry->signo] = entry; + + // install signal handler + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + sigemptyset(&act.sa_mask); + if (sigaction(entry->signo, &act, NULL) < 0) { + BLog(BLOG_ERROR, "sigaction failed"); + goto loop_fail2; + } + + o->num_entries++; + + continue; + + loop_fail2: + BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd); + loop_fail1: + ASSERT_FORCE(close(entry->pipefds[0]) == 0) + ASSERT_FORCE(close(entry->pipefds[1]) == 0) + loop_fail0: + goto fail2; + } + + #endif + + DebugObject_Init(&o->d_obj); + + return 1; + + #ifdef BADVPN_USE_SIGNALFD +fail2: + BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd); +fail1: + ASSERT_FORCE(close(o->signalfd_fd) == 0) + #endif + + #ifdef BADVPN_USE_KEVENT +fail2: + while (o->num_entries > 0) { + BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent); + o->num_entries--; + } + BFree(o->entries); + #endif + + #ifdef BADVPN_USE_SELFPIPE +fail2: + while (o->num_entries > 0) { + free_selfpipe_entry(&o->entries[o->num_entries - 1]); + o->num_entries--; + } + BFree(o->entries); + #endif + +fail0: + return 0; +} + +void BUnixSignal_Free (BUnixSignal *o, int unblock) +{ + ASSERT(unblock == 0 || unblock == 1) + DebugObject_Free(&o->d_obj); + + #ifdef BADVPN_USE_SIGNALFD + + if (unblock) { + // unblock signals + ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0) + } + + // free signalfd BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd); + + // free signalfd fd + ASSERT_FORCE(close(o->signalfd_fd) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + if (unblock) { + // unblock signals + ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0) + } + + // free kevents + while (o->num_entries > 0) { + BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent); + o->num_entries--; + } + + // free array + BFree(o->entries); + + #endif + + #ifdef BADVPN_USE_SELFPIPE + + if (!unblock) { + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + } + } + + // free entries + while (o->num_entries > 0) { + free_selfpipe_entry(&o->entries[o->num_entries - 1]); + o->num_entries--; + } + + // free array + BFree(o->entries); + + #endif +} diff --git a/service/src/main/jni/badvpn/system/BUnixSignal.h b/service/src/main/jni/badvpn/system/BUnixSignal.h new file mode 100644 index 0000000..2438be9 --- /dev/null +++ b/service/src/main/jni/badvpn/system/BUnixSignal.h @@ -0,0 +1,132 @@ +/** + * @file BUnixSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for catching unix signals. + */ + +#ifndef BADVPN_SYSTEM_BUNIXSIGNAL_H +#define BADVPN_SYSTEM_BUNIXSIGNAL_H + +#if (defined(BADVPN_USE_SIGNALFD) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_SELFPIPE)) != 1 +#error Unknown signal backend or too many signal backends +#endif + +#include +#include + +#include +#include +#include + +struct BUnixSignal_s; + +/** + * Handler function called when a signal is received. + * + * @param user as in {@link BUnixSignal_Init} + * @param signo signal number. Will be one of the signals provided to {@link signals}. + */ +typedef void (*BUnixSignal_handler) (void *user, int signo); + +#ifdef BADVPN_USE_KEVENT +struct BUnixSignal_kevent_entry { + struct BUnixSignal_s *parent; + int signo; + BReactorKEvent kevent; +}; +#endif + +#ifdef BADVPN_USE_SELFPIPE +struct BUnixSignal_selfpipe_entry { + struct BUnixSignal_s *parent; + int signo; + int pipefds[2]; + BFileDescriptor pipe_read_bfd; +}; +#endif + +/** + * Object for catching unix signals. + */ +typedef struct BUnixSignal_s { + BReactor *reactor; + sigset_t signals; + BUnixSignal_handler handler; + void *user; + + #ifdef BADVPN_USE_SIGNALFD + int signalfd_fd; + BFileDescriptor signalfd_bfd; + #endif + + #ifdef BADVPN_USE_KEVENT + struct BUnixSignal_kevent_entry *entries; + int num_entries; + #endif + + #ifdef BADVPN_USE_SELFPIPE + struct BUnixSignal_selfpipe_entry *entries; + int num_entries; + #endif + + DebugObject d_obj; +} BUnixSignal; + +/** + * Initializes the object. + * {@link BLog_Init} must have been done. + * + * WARNING: for every signal number there should be at most one {@link BUnixSignal} + * object handling it (or anything else that could interfere). + * + * This blocks the signal using sigprocmask() and sets up signalfd() for receiving + * signals. + * + * @param o the object + * @param reactor reactor we live in + * @param signals signals to handle. See man 3 sigsetops. + * @param handler handler function to call when a signal is received + * @param user value passed to callback function + * @return 1 on success, 0 on failure + */ +int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + * @param unblock whether to unblock the signals using sigprocmask(). Not unblocking it + * can be used while the program is exiting gracefully to prevent the + * signals from being handled handled according to its default disposition + * after this function is called. Must be 0 or 1. + */ +void BUnixSignal_Free (BUnixSignal *o, int unblock); + +#endif diff --git a/service/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.c b/service/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.c new file mode 100644 index 0000000..bb60188 --- /dev/null +++ b/service/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.c @@ -0,0 +1,607 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +#ifdef ANDROID_UDP + +#include +#define CONNECTION_UDP_BUFFER_SIZE 1 + +#else + +static void free_socks (SocksUdpGwClient *o); +static void try_connect (SocksUdpGwClient *o); +static void reconnect_timer_handler (SocksUdpGwClient *o); +static void socks_client_handler (SocksUdpGwClient *o, int event); +static void udpgw_handler_servererror (SocksUdpGwClient *o); +static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +#endif + +#ifdef ANDROID_UDP +static void dgram_handler (SocksUdpGwClient_connection *o, int event); +static void dgram_handler_received (SocksUdpGwClient_connection *o, uint8_t *data, int data_len); +static int conaddr_comparator (void *unused, SocksUdpGwClient_conaddr *v1, SocksUdpGwClient_conaddr *v2); +static SocksUdpGwClient_connection * find_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr); +static SocksUdpGwClient_connection * reuse_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr); +static void connection_send (SocksUdpGwClient_connection *o, const uint8_t *data, int data_len); +static void connection_first_job_handler (SocksUdpGwClient_connection *con); +static SocksUdpGwClient_connection *connection_init (SocksUdpGwClient *client, SocksUdpGwClient_conaddr conaddr, const uint8_t *data, int data_len); +static void connection_free (SocksUdpGwClient_connection *o); + +static void dgram_handler (SocksUdpGwClient_connection *o, int event) +{ + SocksUdpGwClient *client = o->client; + ASSERT(client); + + DebugObject_Access(&client->d_obj); + + BLog(BLOG_INFO, "UDP error"); +} + +static void dgram_handler_received (SocksUdpGwClient_connection *o, uint8_t *data, int data_len) +{ + SocksUdpGwClient *client = o->client; + ASSERT(client); + + DebugObject_Access(&client->d_obj); + ASSERT(data_len >= 0) + ASSERT(data_len <= client->udpgw_mtu) + + // accept packet + PacketPassInterface_Done(&o->udp_recv_if); + + // check header + if (data_len < sizeof(struct socks_udp_header)) { + BLog(BLOG_ERROR, "missing header"); + return; + } + struct socks_udp_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t frag = header.frag; + uint8_t atyp = header.atyp; + + // check fragment + if (frag) { + BLog(BLOG_ERROR, "unexpected frag"); + return; + } + + // parse address + BAddr remote_addr; + if (atyp == SOCKS_ATYP_IPV6) { + if (data_len < sizeof(struct udpgw_addr_ipv6)) { + BLog(BLOG_ERROR, "missing ipv6 address"); + return; + } + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); + BAddr_InitIPv6(&remote_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); + } else { + if (data_len < sizeof(struct udpgw_addr_ipv4)) { + BLog(BLOG_ERROR, "missing ipv4 address"); + return; + } + struct udpgw_addr_ipv4 addr_ipv4; + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); + BAddr_InitIPv4(&remote_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); + } + + // check remote addr + if (!BAddr_Compare(&remote_addr, &o->conaddr.remote_addr)) { + BLog(BLOG_ERROR, "remote addr not match"); + return; + } + + // check remaining data + if (data_len > client->udp_mtu) { + BLog(BLOG_ERROR, "too much data"); + return; + } + + // submit to user + client->handler_received(client->user, o->conaddr.local_addr, remote_addr, data, data_len); +} + +static int conaddr_comparator (void *unused, SocksUdpGwClient_conaddr *v1, SocksUdpGwClient_conaddr *v2) +{ + int r = BAddr_CompareOrder(&v1->remote_addr, &v2->remote_addr); + if (r) { + return r; + } + return BAddr_CompareOrder(&v1->local_addr, &v2->local_addr); +} + +static SocksUdpGwClient_connection * find_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree, &conaddr); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, SocksUdpGwClient_connection, connections_tree_node); +} + +static SocksUdpGwClient_connection * reuse_connection (SocksUdpGwClient *o, SocksUdpGwClient_conaddr conaddr) +{ + ASSERT(!find_connection(o, conaddr)) + ASSERT(o->num_connections > 0) + + // get least recently used connection + SocksUdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), SocksUdpGwClient_connection, connections_list_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree, &con->connections_tree_node); + + // set new conaddr + con->conaddr = conaddr; + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree, &con->connections_tree_node, NULL)) + + return con; +} + +static void connection_send (SocksUdpGwClient_connection *o, const uint8_t *data, int data_len) +{ + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(&o->udp_send_writer, &out)) { + BLog(BLOG_ERROR, "out of UDP buffer"); + return; + } + int out_pos = 0; + + // write header + BAddr remote_addr = o->conaddr.remote_addr; + struct socks_udp_header header; + header.rsv = 0; + header.frag = 0; + if (remote_addr.type == BADDR_TYPE_IPV4) { + header.atyp = SOCKS_ATYP_IPV4; + } else { + header.atyp = SOCKS_ATYP_IPV6; + } + memcpy(out + out_pos, &header, sizeof(header)); + out_pos += sizeof(header); + + // write address + switch (remote_addr.type) { + case BADDR_TYPE_IPV4: { + struct udpgw_addr_ipv4 addr_ipv4; + addr_ipv4.addr_ip = remote_addr.ipv4.ip; + addr_ipv4.addr_port = remote_addr.ipv4.port; + memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); + out_pos += sizeof(addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr_ip, remote_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); + addr_ipv6.addr_port = remote_addr.ipv6.port; + memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); + out_pos += sizeof(addr_ipv6); + } break; + } + + // write packet to buffer + memcpy(out + out_pos, data, data_len); + out_pos += data_len; + + // submit written message + BufferWriter_EndPacket(&o->udp_send_writer, out_pos); +} + +static void connection_first_job_handler (SocksUdpGwClient_connection *con) +{ + connection_send(con, con->first_data, con->first_data_len); +} + +static SocksUdpGwClient_connection *connection_init (SocksUdpGwClient *client, SocksUdpGwClient_conaddr conaddr, const uint8_t *data, int data_len) +{ + // allocate structure + SocksUdpGwClient_connection *o = (SocksUdpGwClient_connection *) malloc(sizeof(*o)); + if (!o) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail; + } + + // init arguments + o->client = client; + o->conaddr = conaddr; + o->first_data = data; + o->first_data_len = data_len; + + // init first job + BPending_Init(&o->first_job, BReactor_PendingGroup(client->reactor), (BPending_handler)connection_first_job_handler, o); + BPending_Set(&o->first_job); + + // init UDP dgram + if (!BDatagram_Init(&o->udp_dgram, client->socks_server_addr.type, client->reactor, o, (BDatagram_handler)dgram_handler)) { + goto fail0; + } + + // set SO_REUSEADDR + if (!BDatagram_SetReuseAddr(&o->udp_dgram, 1)) { + BLog(BLOG_ERROR, "set SO_REUSEADDR failed"); + goto fail1; + } + + // set UDP dgram send address + BIPAddr ipaddr; + memset(&ipaddr, 0, sizeof(ipaddr)); + ipaddr.type = client->socks_server_addr.type; + BDatagram_SetSendAddrs(&o->udp_dgram, client->socks_server_addr, ipaddr); + + // init UDP dgram interfaces + BDatagram_SendAsync_Init(&o->udp_dgram, client->udp_mtu); + BDatagram_RecvAsync_Init(&o->udp_dgram, client->udp_mtu); + + // init UDP writer + BufferWriter_Init(&o->udp_send_writer, client->udp_mtu, BReactor_PendingGroup(client->reactor)); + + // init UDP buffer + if (!PacketBuffer_Init(&o->udp_send_buffer, BufferWriter_GetOutput(&o->udp_send_writer), BDatagram_SendAsync_GetIf(&o->udp_dgram), CONNECTION_UDP_BUFFER_SIZE, BReactor_PendingGroup(client->reactor))) { + BLog(BLOG_ERROR, "PacketBuffer_Init failed"); + goto fail2; + } + + // init UDP recv interface + PacketPassInterface_Init(&o->udp_recv_if, client->udp_mtu, (PacketPassInterface_handler_send)dgram_handler_received, o, BReactor_PendingGroup(client->reactor)); + + // init UDP recv buffer + if (!SinglePacketBuffer_Init(&o->udp_recv_buffer, BDatagram_RecvAsync_GetIf(&o->udp_dgram), &o->udp_recv_if, BReactor_PendingGroup(client->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail3; + } + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&client->connections_tree, &o->connections_tree_node, NULL)); + + // insert to connections list + LinkedList1_Append(&client->connections_list, &o->connections_list_node); + + // increment number of connections + client->num_connections++; + + // succeed to init + return o; + +fail3: + PacketPassInterface_Free(&o->udp_recv_if); + PacketBuffer_Free(&o->udp_send_buffer); +fail2: + BufferWriter_Free(&o->udp_send_writer); + BDatagram_RecvAsync_Free(&o->udp_dgram); + BDatagram_SendAsync_Free(&o->udp_dgram); +fail1: + BDatagram_Free(&o->udp_dgram); + +fail0: + BPending_Free(&o->first_job); + free(o); +fail: + return NULL; +} + +static void connection_free (SocksUdpGwClient_connection *o) +{ + SocksUdpGwClient *client = o->client; + + // decrement number of connections + client->num_connections--; + + // remove from connections list + LinkedList1_Remove(&client->connections_list, &o->connections_list_node); + + // remove from connections tree by conaddr + BAVL_Remove(&client->connections_tree, &o->connections_tree_node); + + // free UDP receive buffer + SinglePacketBuffer_Free(&o->udp_recv_buffer); + + // free UDP receive interface + PacketPassInterface_Free(&o->udp_recv_if); + + // free UDP buffer + PacketBuffer_Free(&o->udp_send_buffer); + + // free UDP writer + BufferWriter_Free(&o->udp_send_writer); + + // free UDP dgram interfaces + BDatagram_RecvAsync_Free(&o->udp_dgram); + BDatagram_SendAsync_Free(&o->udp_dgram); + + // free UDP dgram + BDatagram_Free(&o->udp_dgram); + + // free structure + free(o); +} + +#else + +static void free_socks (SocksUdpGwClient *o) +{ + ASSERT(o->have_socks) + + // disconnect udpgw client from SOCKS + if (o->socks_up) { + UdpGwClient_DisconnectServer(&o->udpgw_client); + } + + // free SOCKS client + BSocksClient_Free(&o->socks_client); + + // set have no SOCKS + o->have_socks = 0; +} + +static void try_connect (SocksUdpGwClient *o) +{ + ASSERT(!o->have_socks) + ASSERT(!BTimer_IsRunning(&o->reconnect_timer)) + + // init SOCKS client + if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->auth_info, o->num_auth_info, o->remote_udpgw_addr, (BSocksClient_handler)socks_client_handler, o, o->reactor)) { + BLog(BLOG_ERROR, "BSocksClient_Init failed"); + goto fail0; + } + + // set have SOCKS + o->have_socks = 1; + + // set SOCKS not up + o->socks_up = 0; + + return; + +fail0: + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); +} + +static void reconnect_timer_handler (SocksUdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_socks) + + // try connecting + try_connect(o); +} + +static void socks_client_handler (SocksUdpGwClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_socks) + + switch (event) { + case BSOCKSCLIENT_EVENT_UP: { + ASSERT(!o->socks_up) + + BLog(BLOG_INFO, "SOCKS up"); + + // connect udpgw client to SOCKS + if (!UdpGwClient_ConnectServer(&o->udpgw_client, BSocksClient_GetSendInterface(&o->socks_client), BSocksClient_GetRecvInterface(&o->socks_client))) { + BLog(BLOG_ERROR, "UdpGwClient_ConnectServer failed"); + goto fail0; + } + + // set SOCKS up + o->socks_up = 1; + + return; + + fail0: + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); + } break; + + case BSOCKSCLIENT_EVENT_ERROR: + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + BLog(BLOG_INFO, "SOCKS error"); + + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); + } break; + + default: ASSERT(0); + } +} + +static void udpgw_handler_servererror (SocksUdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_socks) + ASSERT(o->socks_up) + + BLog(BLOG_ERROR, "client reports server error"); + + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); +} + +static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // submit to user + o->handler_received(o->user, local_addr, remote_addr, data, data_len); + return; +} + +#endif + +int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, + BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user, + SocksUdpGwClient_handler_received handler_received) +{ + // see asserts in UdpGwClient_Init + ASSERT(!BAddr_IsInvalid(&socks_server_addr)) +#ifndef ANDROID_UDP + ASSERT(remote_udpgw_addr.type == BADDR_TYPE_IPV4 || remote_udpgw_addr.type == BADDR_TYPE_IPV6) +#endif + + // init arguments + o->udp_mtu = udp_mtu; + o->socks_server_addr = socks_server_addr; + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + o->remote_udpgw_addr = remote_udpgw_addr; + o->reactor = reactor; + o->user = user; + o->handler_received = handler_received; + +#ifdef ANDROID_UDP + // compute MTUs + o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu); + o->max_connections = max_connections; + + // limit max connections to number of conid's + if (o->max_connections > UINT16_MAX + 1) { + o->max_connections = UINT16_MAX + 1; + } + + // init connections tree by conaddr + BAVL_Init(&o->connections_tree, OFFSET_DIFF(SocksUdpGwClient_connection, conaddr, connections_tree_node), (BAVL_comparator)conaddr_comparator, NULL); + + // init connections list + LinkedList1_Init(&o->connections_list); +#else + // init udpgw client + if (!UdpGwClient_Init(&o->udpgw_client, udp_mtu, max_connections, send_buffer_size, keepalive_time, o->reactor, o, + (UdpGwClient_handler_servererror)udpgw_handler_servererror, + (UdpGwClient_handler_received)udpgw_handler_received + )) { + goto fail0; + } + + // init reconnect timer + BTimer_Init(&o->reconnect_timer, reconnect_time, (BTimer_handler)reconnect_timer_handler, o); + + // set have no SOCKS + o->have_socks = 0; + + // try connecting + try_connect(o); +#endif + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void SocksUdpGwClient_Free (SocksUdpGwClient *o) +{ + DebugObject_Free(&o->d_obj); + +#ifdef ANDROID_UDP + // free connections + while (!LinkedList1_IsEmpty(&o->connections_list)) { + SocksUdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), SocksUdpGwClient_connection, connections_list_node); + connection_free(con); + } +#else + // free SOCKS + if (o->have_socks) { + free_socks(o); + } + + // free reconnect timer + BReactor_RemoveTimer(o->reactor, &o->reconnect_timer); + + // free udpgw client + UdpGwClient_Free(&o->udpgw_client); +#endif +} + +void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + // see asserts in UdpGwClient_SubmitPacket + +#ifdef ANDROID_UDP + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // build conaddr + SocksUdpGwClient_conaddr conaddr; + conaddr.local_addr = local_addr; + conaddr.remote_addr = remote_addr; + + // lookup connection + SocksUdpGwClient_connection *con = find_connection(o, conaddr); + + // if no connection and can't create a new one, reuse the least recently used une + if (!con && o->num_connections == o->max_connections) { + con = reuse_connection(o, conaddr); + } + + if (!con) { + // create new connection + con = connection_init(o, conaddr, data, data_len); + } else { + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // send packet to existing connection + connection_send(con, data, data_len); + } +#else + // submit to udpgw client + UdpGwClient_SubmitPacket(&o->udpgw_client, local_addr, remote_addr, is_dns, data, data_len); +#endif +} + diff --git a/service/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.h b/service/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.h new file mode 100644 index 0000000..ebb7902 --- /dev/null +++ b/service/src/main/jni/badvpn/tun2socks/SocksUdpGwClient.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_TUN2SOCKS_SOCKSUDPGWCLIENT_H +#define BADVPN_TUN2SOCKS_SOCKSUDPGWCLIENT_H + +#include +#include +#include +#ifdef ANDROID_UDP +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif + +typedef void (*SocksUdpGwClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +typedef struct { + int udp_mtu; + BAddr socks_server_addr; + const struct BSocksClient_auth_info *auth_info; + size_t num_auth_info; + BAddr remote_udpgw_addr; + BReactor *reactor; + void *user; + SocksUdpGwClient_handler_received handler_received; +#ifdef ANDROID_UDP + int udpgw_mtu; + int num_connections; + int max_connections; + BAVL connections_tree; + LinkedList1 connections_list; +#else + UdpGwClient udpgw_client; + BTimer reconnect_timer; + int have_socks; + BSocksClient socks_client; + int socks_up; +#endif + DebugObject d_obj; +} SocksUdpGwClient; + +#ifdef ANDROID_UDP +typedef struct { + BAddr local_addr; + BAddr remote_addr; +} SocksUdpGwClient_conaddr; + +typedef struct { + SocksUdpGwClient *client; + SocksUdpGwClient_conaddr conaddr; + BPending first_job; + const uint8_t *first_data; + int first_data_len; + BDatagram udp_dgram; + BufferWriter udp_send_writer; + PacketBuffer udp_send_buffer; + SinglePacketBuffer udp_recv_buffer; + PacketPassInterface udp_recv_if; + BAVLNode connections_tree_node; + LinkedList1Node connections_list_node; +} SocksUdpGwClient_connection; +#endif + +int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, + BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user, + SocksUdpGwClient_handler_received handler_received) WARN_UNUSED; +void SocksUdpGwClient_Free (SocksUdpGwClient *o); +void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len); + +#endif diff --git a/service/src/main/jni/badvpn/tun2socks/tun2socks.c b/service/src/main/jni/badvpn/tun2socks/tun2socks.c new file mode 100644 index 0000000..6db837c --- /dev/null +++ b/service/src/main/jni/badvpn/tun2socks/tun2socks.c @@ -0,0 +1,2379 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#ifdef ANDROID + +#include + +#include +#include +#include + +BAVL connections_tree; +typedef struct { + BAddr local_addr; + BAddr remote_addr; + uint16_t port; + int count; + BAVLNode connections_tree_node; +} Connection; + +static int conaddr_comparator (void *unused, uint16_t *v1, uint16_t *v2) +{ + if (*v1 == *v2) return 0; + else if (*v1 > *v2) return 1; + else return -1; +} + +static Connection * find_connection (uint16_t port) +{ + BAVLNode *tree_node = BAVL_LookupExact(&connections_tree, &port); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, Connection, connections_tree_node); +} + +static void remove_connection (Connection *con) +{ + con->count -= 1; + if (con->count <= 0) + { + BAVL_Remove(&connections_tree, &con->connections_tree_node); + free(con); + } +} + +static void insert_connection (BAddr local_addr, BAddr remote_addr, uint16_t port) +{ + Connection * con = find_connection(port); + if (con != NULL) + con->count += 1; + else + { + Connection * tmp = (Connection *)malloc(sizeof(Connection)); + tmp->local_addr = local_addr; + tmp->remote_addr = remote_addr; + tmp->port = port; + tmp->count = 1; + BAVL_Insert(&connections_tree, &tmp->connections_tree_node, NULL); + } +} + +static void free_connections() +{ + while (!BAVL_IsEmpty(&connections_tree)) { + Connection *con = UPPER_OBJECT(BAVL_GetLast(&connections_tree), Connection, connections_tree_node); + BAVL_Remove(&connections_tree, &con->connections_tree_node); + } +} + +static void tcp_remove(struct tcp_pcb* pcb_list) +{ + struct tcp_pcb *pcb = pcb_list; + struct tcp_pcb *pcb2; + + while(pcb != NULL) + { + pcb2 = pcb; + pcb = pcb->next; + tcp_abort(pcb2); + } +} + +#endif + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +#define SYNC_DECL \ + BPending sync_mark; \ + +#define SYNC_FROMHERE \ + BPending_Init(&sync_mark, BReactor_PendingGroup(&ss), NULL, NULL); \ + BPending_Set(&sync_mark); + +#define SYNC_BREAK \ + BPending_Free(&sync_mark); + +#define SYNC_COMMIT \ + BReactor_Synchronize(&ss, &sync_mark.base); \ + BPending_Free(&sync_mark); + +// command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + char *netif_ipaddr; + char *netif_netmask; + char *netif_ip6addr; + char *socks_server_addr; + char *username; + char *password; + char *password_file; + int append_source_to_username; + char *udpgw_remote_server_addr; + int udpgw_max_connections; + int udpgw_connection_buffer_size; + int udpgw_transparent_dns; +#ifdef ANDROID + int tun_fd; + int tun_mtu; + int fake_proc; + char *pid; + char *sock; + char *dnsgw; +#else + char *tundev; +#endif +} options; + +// TCP client +struct tcp_client { + dead_t dead; + dead_t dead_client; + LinkedList1Node list_node; + BAddr local_addr; + BAddr remote_addr; + struct tcp_pcb *pcb; + int client_closed; + uint8_t buf[TCP_WND]; + int buf_used; + char *socks_username; + BSocksClient socks_client; + int socks_up; + int socks_closed; + StreamPassInterface *socks_send_if; + StreamRecvInterface *socks_recv_if; + uint8_t socks_recv_buf[CLIENT_SOCKS_RECV_BUF_SIZE]; + int socks_recv_buf_used; + int socks_recv_buf_sent; + int socks_recv_waiting; + int socks_recv_tcp_pending; +}; + +// IP address of netif +BIPAddr netif_ipaddr; + +// netmask of netif +BIPAddr netif_netmask; + +// IP6 address of netif +struct ipv6_addr netif_ip6addr; + +// SOCKS server address +BAddr socks_server_addr; + +// allocated password file contents +uint8_t *password_file_contents; + +// SOCKS authentication information +struct BSocksClient_auth_info socks_auth_info[2]; +size_t socks_num_auth_info; + +// remote udpgw server addr, if provided +BAddr udpgw_remote_server_addr; + +// reactor +BReactor ss; + +// set to 1 by terminate +int quitting; + +// TUN device +BTap device; + +// device write buffer +uint8_t *device_write_buf; + +// device reading +SinglePacketBuffer device_read_buffer; +PacketPassInterface device_read_interface; + +// udpgw client +SocksUdpGwClient udpgw_client; +int udp_mtu; + +// TCP timer +BTimer tcp_timer; + +// job for initializing lwip +BPending lwip_init_job; + +// lwip netif +int have_netif; +struct netif netif; + +// lwip TCP listener +struct tcp_pcb *listener; + +// lwip TCP/IPv6 listener +struct tcp_pcb *listener_ip6; + +// TCP clients +LinkedList1 tcp_clients; + +// number of clients +int num_clients; + +#ifdef ANDROID +// Address of dnsgw +BAddr dnsgw; +void terminate (void); +#else +static void terminate (void); +#endif + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder); +static void lwip_init_job_hadler (void *unused); +static void tcp_timer_handler (void *unused); +static void device_error_handler (void *unused); +static void device_read_handler_send (void *unused, uint8_t *data, int data_len); +#ifdef ANDROID +static int process_device_dns_packet (uint8_t *data, int data_len); +#endif +static int process_device_udp_packet (uint8_t *data, int data_len); +static err_t netif_init_func (struct netif *netif); +static err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr); +static err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +static err_t common_netif_output (struct netif *netif, struct pbuf *p); +static err_t netif_input_func (struct pbuf *p, struct netif *inp); +static void client_logfunc (struct tcp_client *client); +static void client_log (struct tcp_client *client, int level, const char *fmt, ...); +static err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err); +static void client_handle_freed_client (struct tcp_client *client); +static void client_free_client (struct tcp_client *client); +static void client_abort_client (struct tcp_client *client); +static void client_free_socks (struct tcp_client *client); +static void client_murder (struct tcp_client *client); +static void client_dealloc (struct tcp_client *client); +static void client_err_func (void *arg, err_t err); +static err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); +static void client_socks_handler (struct tcp_client *client, int event); +static void client_send_to_socks (struct tcp_client *client); +static void client_socks_send_handler_done (struct tcp_client *client, int data_len); +static void client_socks_recv_initiate (struct tcp_client *client); +static void client_socks_recv_handler_done (struct tcp_client *client, int data_len); +static int client_socks_recv_send_out (struct tcp_client *client); +static err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len); +static void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +#ifdef ANDROID +static void daemonize(const char* path) { + + /* Our process ID and Session ID */ + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + /* If we got a good PID, then + we can exit the parent process. */ + if (pid > 0) { + FILE *file = fopen(path, "w"); + if (file == NULL) { + BLog(BLOG_ERROR, "Invalid pid file"); + exit(EXIT_FAILURE); + } + + fprintf(file, "%d", pid); + fclose(file); + exit(EXIT_SUCCESS); + } + + /* Change the file mode mask */ + umask(0); + + /* Open any logs here */ + + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +} +#endif + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + if (options.fake_proc) { + // Fake process name to cheat on Lollipop + strcpy(argv[0], "net.typeblog.socks"); + prctl(PR_SET_NAME, "net.typeblog.socks"); + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + +#ifdef ANDROID + if (options.pid) { + daemonize(options.pid); + } +#endif + + // clear password contents pointer + password_file_contents = NULL; + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // set not quitting + quitting = 0; + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + +#ifdef ANDROID + // use supplied file descriptor + + int sock, fd; + struct sockaddr_un addr; + + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + BLog(BLOG_ERROR, "socket() failed: %s (socket sock = %d)\n", strerror(errno), sock); + goto fail2; + } + + unlink(options.sock); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, options.sock, sizeof(addr.sun_path)-1); + + if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + BLog(BLOG_ERROR, "bind() failed: %s (sock = %d)\n", strerror(errno), sock); + close(sock); + goto fail2; + } + + if (listen(sock, 5) == -1) { + BLog(BLOG_ERROR, "listen() failed: %s (sock = %d)\n", strerror(errno), sock); + close(sock); + goto fail2; + } + + for (;;) { + int sock2; + struct sockaddr_un remote; + socklen_t t = (socklen_t) sizeof(remote); + if ((sock2 = accept(sock, (struct sockaddr *)&remote, &t)) == -1) { + BLog(BLOG_ERROR, "accept() failed: %s (sock = %d)\n", strerror(errno), sock); + continue; + } + if (ancil_recv_fd(sock2, &fd)) { + BLog(BLOG_ERROR, "ancil_recv_fd: %s (sock = %d)\n", strerror(errno), sock2); + close(sock2); + } else { + close(sock2); + BLog(BLOG_INFO, "received fd = %d", fd); + break; + } + } + close(sock); + + struct BTap_init_data init_data; + init_data.dev_type = BTAP_DEV_TUN; + init_data.init_type = BTAP_INIT_FD; + init_data.init.fd.fd = fd; + init_data.init.fd.mtu = options.tun_mtu; + + if (!BTap_Init2(&device, &ss, init_data, device_error_handler, NULL)) { + BLog(BLOG_ERROR, "BTap_Init2 failed"); + goto fail3; + } +#else + // init TUN device + if (!BTap_Init(&device, &ss, options.tundev, device_error_handler, NULL, 1)) { + BLog(BLOG_ERROR, "BTap_Init failed"); + goto fail3; + } +#endif + + // NOTE: the order of the following is important: + // first device writing must evaluate, + // then lwip (so it can send packets to the device), + // then device reading (so it can pass received packets to lwip). + + // init device reading + PacketPassInterface_Init(&device_read_interface, BTap_GetMTU(&device), device_read_handler_send, NULL, BReactor_PendingGroup(&ss)); + if (!SinglePacketBuffer_Init(&device_read_buffer, BTap_GetOutput(&device), &device_read_interface, BReactor_PendingGroup(&ss))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail4; + } + + if (options.udpgw_remote_server_addr) { + // compute maximum UDP payload size we need to pass through udpgw + udp_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)); + if (options.netif_ip6addr) { + int udp_ip6_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)); + if (udp_mtu < udp_ip6_mtu) { + udp_mtu = udp_ip6_mtu; + } + } + if (udp_mtu < 0) { + udp_mtu = 0; + } + + // make sure our UDP payloads aren't too large for udpgw + int udpgw_mtu = udpgw_compute_mtu(udp_mtu); + if (udpgw_mtu < 0 || udpgw_mtu > PACKETPROTO_MAXPAYLOAD) { + BLog(BLOG_ERROR, "device MTU is too large for UDP"); + goto fail4a; + } + + // init udpgw client + if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME, + socks_server_addr, socks_auth_info, socks_num_auth_info, + udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udpgw_client_handler_received + )) { + BLog(BLOG_ERROR, "SocksUdpGwClient_Init failed"); + goto fail4a; + } + } + + // init lwip init job + BPending_Init(&lwip_init_job, BReactor_PendingGroup(&ss), lwip_init_job_hadler, NULL); + BPending_Set(&lwip_init_job); + + // init device write buffer + if (!(device_write_buf = (uint8_t *)BAlloc(BTap_GetMTU(&device)))) { + BLog(BLOG_ERROR, "BAlloc failed"); + goto fail5; + } + + // init TCP timer + // it won't trigger before lwip is initialized, becuase the lwip init is a job + BTimer_Init(&tcp_timer, TCP_TMR_INTERVAL, tcp_timer_handler, NULL); + BReactor_SetTimer(&ss, &tcp_timer); + + // set no netif + have_netif = 0; + + // set no listener + listener = NULL; + listener_ip6 = NULL; + + // init clients list + LinkedList1_Init(&tcp_clients); + + // init number of clients + num_clients = 0; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + LinkedList1Node *node; + while ((node = LinkedList1_GetFirst(&tcp_clients))) { + struct tcp_client *client = UPPER_OBJECT(node, struct tcp_client, list_node); + client_murder(client); + } + + // free listener + if (listener_ip6) { + tcp_close(listener_ip6); + } + if (listener) { + tcp_close(listener); + } + + // free netif + if (have_netif) { + netif_remove(&netif); + } + +#ifdef ANDROID + BLog(BLOG_NOTICE, "Free TCP connections"); + tcp_remove(tcp_bound_pcbs); + tcp_remove(tcp_active_pcbs); + tcp_remove(tcp_tw_pcbs); + free_connections(); +#endif + + BReactor_RemoveTimer(&ss, &tcp_timer); + BFree(device_write_buf); + +fail5: + BPending_Free(&lwip_init_job); + if (options.udpgw_remote_server_addr) { + SocksUdpGwClient_Free(&udpgw_client); + } +fail4a: + SinglePacketBuffer_Free(&device_read_buffer); +fail4: + PacketPassInterface_Free(&device_read_interface); + BTap_Free(&device); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&ss); +fail1: + BFree(password_file_contents); + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void terminate (void) +{ + ASSERT(!quitting) + + BLog(BLOG_NOTICE, "tearing down"); + + // set quitting + quitting = 1; + + // exit event loop + BReactor_Quit(&ss, 1); +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" +#ifdef ANDROID + " [--fake-proc]\n" + " [--tunfd ]\n" + " [--tunmtu ]\n" + " [--dnsgw ]\n" + " [--pid ]\n" + " [--sock ]\n" +#else + " [--tundev ]\n" +#endif + " --netif-ipaddr \n" + " --netif-netmask \n" + " --socks-server-addr \n" + " [--netif-ip6addr ]\n" + " [--username ]\n" + " [--password ]\n" + " [--password-file ]\n" + " [--append-source-to-username]\n" +#ifdef ANDROID_UDP + " [--enable-udprelay]\n" + " [--udprelay-max-connections ]\n" +#else + " [--udpgw-remote-server-addr ]\n" + " [--udpgw-max-connections ]\n" + " [--udpgw-connection-buffer-size ]\n" + " [--udpgw-transparent-dns]\n" +#endif + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } +#ifdef ANDROID + options.tun_fd = -1; + options.tun_mtu = 1500; + options.fake_proc = 0; + options.pid = NULL; +#else + options.tundev = NULL; +#endif + options.netif_ipaddr = NULL; + options.netif_netmask = NULL; + options.netif_ip6addr = NULL; + options.socks_server_addr = NULL; + options.username = NULL; + options.password = NULL; + options.password_file = NULL; + options.append_source_to_username = 0; + options.udpgw_remote_server_addr = NULL; + options.udpgw_max_connections = DEFAULT_UDPGW_MAX_CONNECTIONS; + options.udpgw_connection_buffer_size = DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE; + options.udpgw_transparent_dns = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } +#ifdef ANDROID + else if (!strcmp(arg, "--fake-proc")) { + options.fake_proc = 1; + } + else if (!strcmp(arg, "--tunfd")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.tun_fd = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--tunmtu")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.tun_mtu = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--dnsgw")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.dnsgw = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--pid")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.pid = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--sock")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.sock = argv[i + 1]; + i++; + } +#else + else if (!strcmp(arg, "--tundev")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.tundev = argv[i + 1]; + i++; + } +#endif + else if (!strcmp(arg, "--netif-ipaddr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_ipaddr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--netif-netmask")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_netmask = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--netif-ip6addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_ip6addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--socks-server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.socks_server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--username")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.username = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--password")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.password = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--password-file")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.password_file = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--append-source-to-username")) { + options.append_source_to_username = 1; + } +#ifdef ANDROID_UDP + else if (!strcmp(arg, "--enable-udprelay")) { + options.udpgw_remote_server_addr = "0.0.0.0:0"; +#else + else if (!strcmp(arg, "--udpgw-remote-server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.udpgw_remote_server_addr = argv[i + 1]; + i++; +#endif + } +#ifdef ANDROID_UDP + else if (!strcmp(arg, "--udprelay-max-connections")) { +#else + else if (!strcmp(arg, "--udpgw-max-connections")) { +#endif + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udpgw_max_connections = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } +#ifndef ANDROID_UDP + else if (!strcmp(arg, "--udpgw-connection-buffer-size")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udpgw_connection_buffer_size = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--udpgw-transparent-dns")) { + options.udpgw_transparent_dns = 1; + } +#endif + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.netif_ipaddr) { + fprintf(stderr, "--netif-ipaddr is required\n"); + return 0; + } + + if (!options.netif_netmask) { + fprintf(stderr, "--netif-netmask is required\n"); + return 0; + } + + if (!options.socks_server_addr) { + fprintf(stderr, "--socks-server-addr is required\n"); + return 0; + } + + if (options.username) { + if (!options.password && !options.password_file) { + fprintf(stderr, "username given but password not given\n"); + return 0; + } + + if (options.password && options.password_file) { + fprintf(stderr, "--password and --password-file cannot both be given\n"); + return 0; + } + } + +#ifdef ANDROID + if (!options.sock) { + fprintf(stderr, "--sock is required\n"); + return 0; + } +#endif + + return 1; +} + +int process_arguments (void) +{ + ASSERT(!password_file_contents) + + // resolve netif ipaddr + if (!BIPAddr_Resolve(&netif_ipaddr, options.netif_ipaddr, 0)) { + BLog(BLOG_ERROR, "netif ipaddr: BIPAddr_Resolve failed"); + return 0; + } + if (netif_ipaddr.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "netif ipaddr: must be an IPv4 address"); + return 0; + } + + // resolve netif netmask + if (!BIPAddr_Resolve(&netif_netmask, options.netif_netmask, 0)) { + BLog(BLOG_ERROR, "netif netmask: BIPAddr_Resolve failed"); + return 0; + } + if (netif_netmask.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "netif netmask: must be an IPv4 address"); + return 0; + } + + // parse IP6 address + if (options.netif_ip6addr) { + if (!ipaddr6_parse_ipv6_addr(options.netif_ip6addr, &netif_ip6addr)) { + BLog(BLOG_ERROR, "netif ip6addr: incorrect"); + return 0; + } + } + + // resolve SOCKS server address + if (!BAddr_Parse2(&socks_server_addr, options.socks_server_addr, NULL, 0, 0)) { + BLog(BLOG_ERROR, "socks server addr: BAddr_Parse2 failed"); + return 0; + } + + // add none socks authentication method + socks_auth_info[0] = BSocksClient_auth_none(); + socks_num_auth_info = 1; + + // add password socks authentication method + if (options.username) { + const char *password; + size_t password_len; + if (options.password) { + password = options.password; + password_len = strlen(options.password); + } else { + if (!read_file(options.password_file, &password_file_contents, &password_len)) { + BLog(BLOG_ERROR, "failed to read password file"); + return 0; + } + password = (char *)password_file_contents; + } + + socks_auth_info[socks_num_auth_info++] = BSocksClient_auth_password( + options.username, strlen(options.username), + password, password_len + ); + } + + // resolve remote udpgw server address + if (options.udpgw_remote_server_addr) { + if (!BAddr_Parse2(&udpgw_remote_server_addr, options.udpgw_remote_server_addr, NULL, 0, 0)) { +#ifdef ANDROID_UDP + BLog(BLOG_ERROR, "udprelay server addr: BAddr_Parse2 failed"); +#else + BLog(BLOG_ERROR, "remote udpgw server addr: BAddr_Parse2 failed"); +#endif + return 0; + } + } + +#ifdef ANDROID + // resolve dnsgw addr + if (options.dnsgw) { + if (!BAddr_Parse2(&dnsgw, options.dnsgw, NULL, 0, 0)) { + BLog(BLOG_ERROR, "dnsgw addr: BAddr_Parse2 failed"); + return 0; + } + if (dnsgw.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "dnsgw addr: must be an IPv4 address"); + return 0; + } + } +#endif + + return 1; +} + +void signal_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_NOTICE, "termination requested"); + + terminate(); +} + +BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder) +{ + BAddr addr; + if (is_ipv6) { + BAddr_InitIPv6(&addr, (uint8_t *)ipx_addr->ip6.addr, hton16(port_hostorder)); + } else { + BAddr_InitIPv4(&addr, ipx_addr->ip4.addr, hton16(port_hostorder)); + } + return addr; +} + +void lwip_init_job_hadler (void *unused) +{ + ASSERT(!quitting) + ASSERT(netif_ipaddr.type == BADDR_TYPE_IPV4) + ASSERT(netif_netmask.type == BADDR_TYPE_IPV4) + ASSERT(!have_netif) + ASSERT(!listener) + ASSERT(!listener_ip6) + + BLog(BLOG_DEBUG, "lwip init"); + + // NOTE: the device may fail during this, but there's no harm in not checking + // for that at every step + + // init lwip + lwip_init(); + + // make addresses for netif + ip_addr_t addr; + addr.addr = netif_ipaddr.ipv4; + ip_addr_t netmask; + netmask.addr = netif_netmask.ipv4; + ip_addr_t gw; + ip_addr_set_any(&gw); + + // init netif + if (!netif_add(&netif, &addr, &netmask, &gw, NULL, netif_init_func, netif_input_func)) { + BLog(BLOG_ERROR, "netif_add failed"); + goto fail; + } + have_netif = 1; + + // set netif up + netif_set_up(&netif); + + // set netif pretend TCP + netif_set_pretend_tcp(&netif, 1); + + // set netif default + netif_set_default(&netif); + + if (options.netif_ip6addr) { + // add IPv6 address + memcpy(netif_ip6_addr(&netif, 0), netif_ip6addr.bytes, sizeof(netif_ip6addr.bytes)); + netif_ip6_addr_set_state(&netif, 0, IP6_ADDR_VALID); + } + + // init listener + struct tcp_pcb *l = tcp_new(); + if (!l) { + BLog(BLOG_ERROR, "tcp_new failed"); + goto fail; + } + + // bind listener + if (tcp_bind_to_netif(l, "ho0") != ERR_OK) { + BLog(BLOG_ERROR, "tcp_bind_to_netif failed"); + tcp_close(l); + goto fail; + } + + // listen listener + if (!(listener = tcp_listen(l))) { + BLog(BLOG_ERROR, "tcp_listen failed"); + tcp_close(l); + goto fail; + } + + // setup listener accept handler + tcp_accept(listener, listener_accept_func); + + if (options.netif_ip6addr) { + struct tcp_pcb *l_ip6 = tcp_new_ip6(); + if (!l_ip6) { + BLog(BLOG_ERROR, "tcp_new_ip6 failed"); + goto fail; + } + + if (tcp_bind_to_netif(l_ip6, "ho0") != ERR_OK) { + BLog(BLOG_ERROR, "tcp_bind_to_netif failed"); + tcp_close(l_ip6); + goto fail; + } + + if (!(listener_ip6 = tcp_listen(l_ip6))) { + BLog(BLOG_ERROR, "tcp_listen failed"); + tcp_close(l_ip6); + goto fail; + } + + tcp_accept(listener_ip6, listener_accept_func); + } + + return; + +fail: + if (!quitting) { + terminate(); + } +} + +void tcp_timer_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_DEBUG, "TCP timer"); + + // schedule next timer + // TODO: calculate timeout so we don't drift + BReactor_SetTimer(&ss, &tcp_timer); + + tcp_tmr(); + return; +} + +void device_error_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_ERROR, "device error"); + + terminate(); + return; +} + +void device_read_handler_send (void *unused, uint8_t *data, int data_len) +{ + ASSERT(!quitting) + ASSERT(data_len >= 0) + + BLog(BLOG_DEBUG, "device: received packet"); + + // accept packet + PacketPassInterface_Done(&device_read_interface); + +#ifdef ANDROID + // process DNS directly + if (process_device_dns_packet(data, data_len)) { + return; + } +#endif + + // process UDP directly + if (process_device_udp_packet(data, data_len)) { + return; + } + + // obtain pbuf + if (data_len > UINT16_MAX) { + BLog(BLOG_WARNING, "device read: packet too large"); + return; + } + struct pbuf *p = pbuf_alloc(PBUF_RAW, data_len, PBUF_POOL); + if (!p) { + BLog(BLOG_WARNING, "device read: pbuf_alloc failed"); + return; + } + + // write packet to pbuf + ASSERT_FORCE(pbuf_take(p, data, data_len) == ERR_OK) + + // pass pbuf to input + if (netif.input(p, &netif) != ERR_OK) { + BLog(BLOG_WARNING, "device read: input failed"); + pbuf_free(p); + } +} + +#ifdef ANDROID +int process_device_dns_packet (uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + + // do nothing if we don't have dnsgw + if (!options.dnsgw) { + goto fail; + } + + static int init = 0; + + int to_dns; + int from_dns; + int packet_length = 0; + + uint8_t ip_version = 0; + if (data_len > 0) { + ip_version = (data[0] >> 4); + } + + switch (ip_version) { + case 4: { + // ignore non-UDP packets + if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) { + goto fail; + } + + // parse IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + // to port 53 is considered a DNS packet + to_dns = udp_header.dest_port == hton16(53); + + // from port 8153 is considered a DNS packet + from_dns = udp_header.source_port == dnsgw.ipv4.port; + + // if not DNS packet, just bypass it. + if (!to_dns && !from_dns) { + goto fail; + } + + // modify DNS packet + if (to_dns) { + BLog(BLOG_INFO, "UDP: to DNS %d bytes", data_len); + + // construct addresses + if (!init) { + init = 1; + BAVL_Init(&connections_tree, OFFSET_DIFF(Connection, port, connections_tree_node), (BAVL_comparator)conaddr_comparator, NULL); + } + BAddr local_addr; + BAddr remote_addr; + BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + insert_connection(local_addr, remote_addr, udp_header.source_port); + + // build IP header + ipv4_header.destination_address = dnsgw.ipv4.ip; + ipv4_header.source_address = netif_ipaddr.ipv4; + + // build UDP header + udp_header.dest_port = dnsgw.ipv4.port; + + } else if (from_dns) { + + // if not initialized + if (!init) { + goto fail; + } + + BLog(BLOG_INFO, "UDP: from DNS %d bytes", data_len); + + Connection * con = find_connection(udp_header.dest_port); + if (con != NULL) + { + // build IP header + ipv4_header.source_address = con->remote_addr.ipv4.ip; + ipv4_header.destination_address = con->local_addr.ipv4.ip; + + // build UDP header + udp_header.source_port = con->remote_addr.ipv4.port; + + remove_connection(con); + + } + else + { + goto fail; + } + } + + // update IPv4 header's checksum + ipv4_header.checksum = hton16(0); + ipv4_header.checksum = ipv4_checksum(&ipv4_header, NULL, 0); + + // update UDP header's checksum + udp_header.checksum = hton16(0); + udp_header.checksum = udp_checksum(&udp_header, data, data_len, + ipv4_header.source_address, ipv4_header.destination_address); + + // write packet + memcpy(device_write_buf, &ipv4_header, sizeof(ipv4_header)); + memcpy(device_write_buf + sizeof(ipv4_header), &udp_header, sizeof(udp_header)); + memcpy(device_write_buf + sizeof(ipv4_header) + sizeof(udp_header), data, data_len); + packet_length = sizeof(ipv4_header) + sizeof(udp_header) + data_len; + + } break; + + case 6: { + // TODO: support IPv6 DNS Gateway + goto fail; + } break; + + default: { + goto fail; + } break; + } + + // submit packet + BTap_Send(&device, device_write_buf, packet_length); + + return 1; + +fail: + return 0; +} +#endif + +int process_device_udp_packet (uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + + // do nothing if we don't have udpgw + if (!options.udpgw_remote_server_addr) { + goto fail; + } + + BAddr local_addr; + BAddr remote_addr; + int is_dns; + + uint8_t ip_version = 0; + if (data_len > 0) { + ip_version = (data[0] >> 4); + } + + switch (ip_version) { + case 4: { + // ignore non-UDP packets + if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) { + goto fail; + } + + // parse IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + BLog(BLOG_INFO, "UDP: from device %d bytes", data_len); + + // construct addresses + BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + + // if transparent DNS is enabled, any packet arriving at out netif + // address to port 53 is considered a DNS packet + is_dns = (options.udpgw_transparent_dns && + ipv4_header.destination_address == netif_ipaddr.ipv4 && + udp_header.dest_port == hton16(53)); + } break; + + case 6: { + // ignore if IPv6 support is disabled + if (!options.netif_ip6addr) { + goto fail; + } + + // ignore non-UDP packets + if (data_len < sizeof(struct ipv6_header) || data[offsetof(struct ipv6_header, next_header)] != IPV6_NEXT_UDP) { + goto fail; + } + + // parse IPv6 header + struct ipv6_header ipv6_header; + if (!ipv6_check(data, data_len, &ipv6_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_ip6_checksum(&udp_header, data, data_len, ipv6_header.source_address, ipv6_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + BLog(BLOG_INFO, "UDP/IPv6: from device %d bytes", data_len); + + // construct addresses + BAddr_InitIPv6(&local_addr, ipv6_header.source_address, udp_header.source_port); + BAddr_InitIPv6(&remote_addr, ipv6_header.destination_address, udp_header.dest_port); + + // TODO dns + is_dns = 0; + } break; + + default: { + goto fail; + } break; + } + + // check payload length + if (data_len > udp_mtu) { + BLog(BLOG_ERROR, "packet is too large, cannot send to udpgw"); + goto fail; + } + + // submit packet to udpgw + SocksUdpGwClient_SubmitPacket(&udpgw_client, local_addr, remote_addr, is_dns, data, data_len); + + return 1; + +fail: + return 0; +} + +err_t netif_init_func (struct netif *netif) +{ + BLog(BLOG_DEBUG, "netif func init"); + + netif->name[0] = 'h'; + netif->name[1] = 'o'; + netif->output = netif_output_func; + netif->output_ip6 = netif_output_ip6_func; + + return ERR_OK; +} + +err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +{ + return common_netif_output(netif, p); +} + +err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + return common_netif_output(netif, p); +} + +err_t common_netif_output (struct netif *netif, struct pbuf *p) +{ + SYNC_DECL + + BLog(BLOG_DEBUG, "device write: send packet"); + + if (quitting) { + return ERR_OK; + } + + // if there is just one chunk, send it directly, else via buffer + if (!p->next) { + if (p->len > BTap_GetMTU(&device)) { + BLog(BLOG_WARNING, "netif func output: no space left"); + goto out; + } + + SYNC_FROMHERE + BTap_Send(&device, (uint8_t *)p->payload, p->len); + SYNC_COMMIT + } else { + int len = 0; + do { + if (p->len > BTap_GetMTU(&device) - len) { + BLog(BLOG_WARNING, "netif func output: no space left"); + goto out; + } + memcpy(device_write_buf + len, p->payload, p->len); + len += p->len; + } while ((p = p->next)); + + SYNC_FROMHERE + BTap_Send(&device, device_write_buf, len); + SYNC_COMMIT + } + +out: + return ERR_OK; +} + +err_t netif_input_func (struct pbuf *p, struct netif *inp) +{ + uint8_t ip_version = 0; + if (p->len > 0) { + ip_version = (((uint8_t *)p->payload)[0] >> 4); + } + + switch (ip_version) { + case 4: { + return ip_input(p, inp); + } break; + case 6: { + if (options.netif_ip6addr) { + return ip6_input(p, inp); + } + } break; + } + + pbuf_free(p); + return ERR_OK; +} + +void client_logfunc (struct tcp_client *client) +{ + char local_addr_s[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->local_addr, local_addr_s); + char remote_addr_s[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->remote_addr, remote_addr_s); + + BLog_Append("%05d (%s %s): ", num_clients, local_addr_s, remote_addr_s); +} + +void client_log (struct tcp_client *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err) +{ + ASSERT(err == ERR_OK) + + // signal accepted + struct tcp_pcb *this_listener = (PCB_ISIPV6(newpcb) ? listener_ip6 : listener); + tcp_accepted(this_listener); + + // allocate client structure + struct tcp_client *client = (struct tcp_client *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "listener accept: malloc failed"); + goto fail0; + } + client->socks_username = NULL; + + SYNC_DECL + SYNC_FROMHERE + + // read addresses + client->local_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->local_ip, newpcb->local_port); + client->remote_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->remote_ip, newpcb->remote_port); + + // get destination address + BAddr addr = client->local_addr; +#ifdef OVERRIDE_DEST_ADDR + ASSERT_FORCE(BAddr_Parse2(&addr, OVERRIDE_DEST_ADDR, NULL, 0, 1)) +#endif + + // add source address to username if requested + if (options.username && options.append_source_to_username) { + char addr_str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->remote_addr, addr_str); + client->socks_username = concat_strings(3, options.username, "@", addr_str); + if (!client->socks_username) { + goto fail1; + } + socks_auth_info[1].password.username = client->socks_username; + socks_auth_info[1].password.username_len = strlen(client->socks_username); + } + + // init SOCKS + if (!BSocksClient_Init(&client->socks_client, socks_server_addr, socks_auth_info, socks_num_auth_info, + addr, (BSocksClient_handler)client_socks_handler, client, &ss)) { + BLog(BLOG_ERROR, "listener accept: BSocksClient_Init failed"); + goto fail1; + } + + // init dead vars + DEAD_INIT(client->dead); + DEAD_INIT(client->dead_client); + + // add to linked list + LinkedList1_Append(&tcp_clients, &client->list_node); + + // increment counter + ASSERT(num_clients >= 0) + num_clients++; + + // set pcb + client->pcb = newpcb; + + // set client not closed + client->client_closed = 0; + + // enable TCP_NODELAY + tcp_nagle_disable(client->pcb); + + // setup handler argument + tcp_arg(client->pcb, client); + + // setup handlers + tcp_err(client->pcb, client_err_func); + tcp_recv(client->pcb, client_recv_func); + + // setup buffer + client->buf_used = 0; + + // set SOCKS not up, not closed + client->socks_up = 0; + client->socks_closed = 0; + + client_log(client, BLOG_INFO, "accepted"); + + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + + return ERR_OK; + +fail1: + SYNC_BREAK + free(client->socks_username); + free(client); +fail0: + return ERR_MEM; +} + +void client_handle_freed_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // pcb was taken care of by the caller + + // kill client dead var + DEAD_KILL(client->dead_client); + + // set client closed + client->client_closed = 1; + + // if we have data to be sent to SOCKS and can send it, keep sending + if (client->buf_used > 0 && !client->socks_closed) { + client_log(client, BLOG_INFO, "waiting untill buffered data is sent to SOCKS"); + } else { + if (!client->socks_closed) { + client_free_socks(client); + } else { + client_dealloc(client); + } + } +} + +void client_free_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // free pcb + err_t err = tcp_close(client->pcb); + if (err != ERR_OK) { + client_log(client, BLOG_ERROR, "tcp_close failed (%d)", err); + tcp_abort(client->pcb); + } + + client_handle_freed_client(client); +} + +void client_abort_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // free pcb + tcp_abort(client->pcb); + + client_handle_freed_client(client); +} + +void client_free_socks (struct tcp_client *client) +{ + ASSERT(!client->socks_closed) + + // stop sending to SOCKS + if (client->socks_up) { + // stop receiving from client + if (!client->client_closed) { + tcp_recv(client->pcb, NULL); + } + } + + // free SOCKS + BSocksClient_Free(&client->socks_client); + + // set SOCKS closed + client->socks_closed = 1; + + // if we have data to be sent to the client and we can send it, keep sending + if (client->socks_up && (client->socks_recv_buf_used >= 0 || client->socks_recv_tcp_pending > 0) && !client->client_closed) { + client_log(client, BLOG_INFO, "waiting until buffered data is sent to client"); + } else { + if (!client->client_closed) { + client_free_client(client); + } else { + client_dealloc(client); + } + } +} + +void client_murder (struct tcp_client *client) +{ + // free client + if (!client->client_closed) { + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // abort + tcp_abort(client->pcb); + + // kill client dead var + DEAD_KILL(client->dead_client); + + // set client closed + client->client_closed = 1; + } + + // free SOCKS + if (!client->socks_closed) { + // free SOCKS + BSocksClient_Free(&client->socks_client); + + // set SOCKS closed + client->socks_closed = 1; + } + + // dealloc entry + client_dealloc(client); +} + +void client_dealloc (struct tcp_client *client) +{ + ASSERT(client->client_closed) + ASSERT(client->socks_closed) + + // decrement counter + ASSERT(num_clients > 0) + num_clients--; + + // remove client entry + LinkedList1_Remove(&tcp_clients, &client->list_node); + + // kill dead var + DEAD_KILL(client->dead); + + // free memory + free(client->socks_username); + free(client); +} + +void client_err_func (void *arg, err_t err) +{ + struct tcp_client *client = (struct tcp_client *)arg; + ASSERT(!client->client_closed) + + client_log(client, BLOG_INFO, "client error (%d)", (int)err); + + // the pcb was taken care of by the caller + client_handle_freed_client(client); +} + +err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + struct tcp_client *client = (struct tcp_client *)arg; + ASSERT(!client->client_closed) + ASSERT(err == ERR_OK) // checked in lwIP source. Otherwise, I've no idea what should + // be done with the pbuf in case of an error. + + if (!p) { + client_log(client, BLOG_INFO, "client closed"); + client_free_client(client); + return ERR_ABRT; + } + + ASSERT(p->tot_len > 0) + + // check if we have enough buffer + if (p->tot_len > sizeof(client->buf) - client->buf_used) { + client_log(client, BLOG_ERROR, "no buffer for data !?!"); + return ERR_MEM; + } + + // copy data to buffer + ASSERT_EXECUTE(pbuf_copy_partial(p, client->buf + client->buf_used, p->tot_len, 0) == p->tot_len) + client->buf_used += p->tot_len; + + // if there was nothing in the buffer before, and SOCKS is up, start send data + if (client->buf_used == p->tot_len && client->socks_up) { + ASSERT(!client->socks_closed) // this callback is removed when SOCKS is closed + + SYNC_DECL + SYNC_FROMHERE + client_send_to_socks(client); + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + } + + // free pbuff + pbuf_free(p); + + return ERR_OK; +} + +void client_socks_handler (struct tcp_client *client, int event) +{ + ASSERT(!client->socks_closed) + + switch (event) { + case BSOCKSCLIENT_EVENT_ERROR: { + client_log(client, BLOG_INFO, "SOCKS error"); + + client_free_socks(client); + } break; + + case BSOCKSCLIENT_EVENT_UP: { + ASSERT(!client->socks_up) + + client_log(client, BLOG_INFO, "SOCKS up"); + + // init sending + client->socks_send_if = BSocksClient_GetSendInterface(&client->socks_client); + StreamPassInterface_Sender_Init(client->socks_send_if, (StreamPassInterface_handler_done)client_socks_send_handler_done, client); + + // init receiving + client->socks_recv_if = BSocksClient_GetRecvInterface(&client->socks_client); + StreamRecvInterface_Receiver_Init(client->socks_recv_if, (StreamRecvInterface_handler_done)client_socks_recv_handler_done, client); + client->socks_recv_buf_used = -1; + client->socks_recv_tcp_pending = 0; + if (!client->client_closed) { + tcp_sent(client->pcb, client_sent_func); + } + + // set up + client->socks_up = 1; + + // start sending data if there is any + if (client->buf_used > 0) { + client_send_to_socks(client); + } + + // start receiving data if client is still up + if (!client->client_closed) { + client_socks_recv_initiate(client); + } + } break; + + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + ASSERT(client->socks_up) + + client_log(client, BLOG_INFO, "SOCKS closed"); + + client_free_socks(client); + } break; + + default: + ASSERT(0); + } +} + +void client_send_to_socks (struct tcp_client *client) +{ + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->buf_used > 0) + + // schedule sending + StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used); +} + +void client_socks_send_handler_done (struct tcp_client *client, int data_len) +{ + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->buf_used > 0) + ASSERT(data_len > 0) + ASSERT(data_len <= client->buf_used) + + // remove sent data from buffer + memmove(client->buf, client->buf + data_len, client->buf_used - data_len); + client->buf_used -= data_len; + + if (!client->client_closed) { + // confirm sent data + tcp_recved(client->pcb, data_len); + } + + if (client->buf_used > 0) { + // send any further data + StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used); + } + else if (client->client_closed) { + // client was closed we've sent everything we had buffered; we're done with it + client_log(client, BLOG_INFO, "removing after client went down"); + + client_free_socks(client); + } +} + +void client_socks_recv_initiate (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used == -1) + + StreamRecvInterface_Receiver_Recv(client->socks_recv_if, client->socks_recv_buf, sizeof(client->socks_recv_buf)); +} + +void client_socks_recv_handler_done (struct tcp_client *client, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= sizeof(client->socks_recv_buf)) + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used == -1) + + // if client was closed, stop receiving + if (client->client_closed) { + return; + } + + // set amount of data in buffer + client->socks_recv_buf_used = data_len; + client->socks_recv_buf_sent = 0; + client->socks_recv_waiting = 0; + + // send to client + if (client_socks_recv_send_out(client) < 0) { + return; + } + + // continue receiving if needed + if (client->socks_recv_buf_used == -1) { + client_socks_recv_initiate(client); + } +} + +int client_socks_recv_send_out (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used > 0) + ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used) + ASSERT(!client->socks_recv_waiting) + + // return value -1 means tcp_abort() was done, + // 0 means it wasn't and the client (pcb) is still up + + do { + int to_write = bmin_int(client->socks_recv_buf_used - client->socks_recv_buf_sent, tcp_sndbuf(client->pcb)); + if (to_write == 0) { + break; + } + + err_t err = tcp_write(client->pcb, client->socks_recv_buf + client->socks_recv_buf_sent, to_write, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + if (err == ERR_MEM) { + break; + } + + client_log(client, BLOG_INFO, "tcp_write failed (%d)", (int)err); + + client_abort_client(client); + return -1; + } + + client->socks_recv_buf_sent += to_write; + client->socks_recv_tcp_pending += to_write; + } while (client->socks_recv_buf_sent < client->socks_recv_buf_used); + + // start sending now + err_t err = tcp_output(client->pcb); + if (err != ERR_OK) { + client_log(client, BLOG_INFO, "tcp_output failed (%d)", (int)err); + + client_abort_client(client); + return -1; + } + + // more data to queue? + if (client->socks_recv_buf_sent < client->socks_recv_buf_used) { + if (client->socks_recv_tcp_pending == 0) { + client_log(client, BLOG_ERROR, "can't queue data, but all data was confirmed !?!"); + + client_abort_client(client); + return -1; + } + + // set waiting, continue in client_sent_func + client->socks_recv_waiting = 1; + return 0; + } + + // everything was queued + client->socks_recv_buf_used = -1; + + return 0; +} + +err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + struct tcp_client *client = (struct tcp_client *)arg; + + ASSERT(!client->client_closed) + ASSERT(client->socks_up) + ASSERT(len > 0) + ASSERT(len <= client->socks_recv_tcp_pending) + + // decrement pending + client->socks_recv_tcp_pending -= len; + + // continue queuing + if (client->socks_recv_buf_used > 0) { + ASSERT(client->socks_recv_waiting) + ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used) + + // set not waiting + client->socks_recv_waiting = 0; + + // possibly send more data + if (client_socks_recv_send_out(client) < 0) { + return ERR_ABRT; + } + + // we just queued some data, so it can't have been confirmed yet + ASSERT(client->socks_recv_tcp_pending > 0) + + // continue receiving if needed + if (client->socks_recv_buf_used == -1 && !client->socks_closed) { + SYNC_DECL + SYNC_FROMHERE + client_socks_recv_initiate(client); + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + } + + return ERR_OK; + } + + // have we sent everything after SOCKS was closed? + if (client->socks_closed && client->socks_recv_tcp_pending == 0) { + client_log(client, BLOG_INFO, "removing after SOCKS went down"); + client_free_client(client); + return ERR_ABRT; + } + + return ERR_OK; +} + +void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +{ + ASSERT(options.udpgw_remote_server_addr) + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(local_addr.type == remote_addr.type) + ASSERT(data_len >= 0) + + int packet_length = 0; + + switch (local_addr.type) { + case BADDR_TYPE_IPV4: { +#ifdef ANDROID + BLog(BLOG_INFO, "UDP: from udprelay %d bytes", data_len); +#else + BLog(BLOG_INFO, "UDP: from udpgw %d bytes", data_len); +#endif + + if (data_len > UINT16_MAX - (sizeof(struct ipv4_header) + sizeof(struct udp_header)) || + data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)) + ) { + BLog(BLOG_ERROR, "UDP: packet is too large"); + return; + } + + // build IP header + struct ipv4_header iph; + iph.version4_ihl4 = IPV4_MAKE_VERSION_IHL(sizeof(iph)); + iph.ds = hton8(0); + iph.total_length = hton16(sizeof(iph) + sizeof(struct udp_header) + data_len); + iph.identification = hton16(0); + iph.flags3_fragmentoffset13 = hton16(0); + iph.ttl = hton8(64); + iph.protocol = hton8(IPV4_PROTOCOL_UDP); + iph.checksum = hton16(0); + iph.source_address = remote_addr.ipv4.ip; + iph.destination_address = local_addr.ipv4.ip; + iph.checksum = ipv4_checksum(&iph, NULL, 0); + + // build UDP header + struct udp_header udph; + udph.source_port = remote_addr.ipv4.port; + udph.dest_port = local_addr.ipv4.port; + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_checksum(&udph, data, data_len, iph.source_address, iph.destination_address); + + // write packet + memcpy(device_write_buf, &iph, sizeof(iph)); + memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph)); + memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len); + packet_length = sizeof(iph) + sizeof(udph) + data_len; + } break; + + case BADDR_TYPE_IPV6: { +#ifdef ANDROID_UDP + BLog(BLOG_INFO, "UDP/IPv6: from udprelay %d bytes", data_len); +#else + BLog(BLOG_INFO, "UDP/IPv6: from udpgw %d bytes", data_len); +#endif + + if (!options.netif_ip6addr) { +#ifdef ANDROID_UDP + BLog(BLOG_ERROR, "got IPv6 packet from udprelay but IPv6 is disabled"); +#else + BLog(BLOG_ERROR, "got IPv6 packet from udpgw but IPv6 is disabled"); +#endif + return; + } + + if (data_len > UINT16_MAX - sizeof(struct udp_header) || + data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)) + ) { + BLog(BLOG_ERROR, "UDP/IPv6: packet is too large"); + return; + } + + // build IPv6 header + struct ipv6_header iph; + iph.version4_tc4 = hton8((6 << 4)); + iph.tc4_fl4 = hton8(0); + iph.fl = hton16(0); + iph.payload_length = hton16(sizeof(struct udp_header) + data_len); + iph.next_header = hton8(IPV6_NEXT_UDP); + iph.hop_limit = hton8(64); + memcpy(iph.source_address, remote_addr.ipv6.ip, 16); + memcpy(iph.destination_address, local_addr.ipv6.ip, 16); + + // build UDP header + struct udp_header udph; + udph.source_port = remote_addr.ipv6.port; + udph.dest_port = local_addr.ipv6.port; + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_ip6_checksum(&udph, data, data_len, iph.source_address, iph.destination_address); + + // write packet + memcpy(device_write_buf, &iph, sizeof(iph)); + memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph)); + memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len); + packet_length = sizeof(iph) + sizeof(udph) + data_len; + } break; + } + + // submit packet + BTap_Send(&device, device_write_buf, packet_length); +} diff --git a/service/src/main/jni/badvpn/tun2socks/tun2socks.h b/service/src/main/jni/badvpn/tun2socks/tun2socks.h new file mode 100644 index 0000000..caf5778 --- /dev/null +++ b/service/src/main/jni/badvpn/tun2socks/tun2socks.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) Ambroz Bizjak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "tun2socks" + +// size of temporary buffer for passing data from the SOCKS server to TCP for sending +#define CLIENT_SOCKS_RECV_BUF_SIZE 8192 + +// maximum number of udpgw connections +#define DEFAULT_UDPGW_MAX_CONNECTIONS 256 + +// udpgw per-connection send buffer size, in number of packets +#define DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE 8 + +// udpgw reconnect time after connection fails +#define UDPGW_RECONNECT_TIME 5000 + +// udpgw keepalive sending interval +#define UDPGW_KEEPALIVE_TIME 10000 + +// option to override the destination addresses to give the SOCKS server +//#define OVERRIDE_DEST_ADDR "10.111.0.2:2000" diff --git a/service/src/main/jni/badvpn/tuntap/BTap.c b/service/src/main/jni/badvpn/tuntap/BTap.c new file mode 100644 index 0000000..43d3e7a --- /dev/null +++ b/service/src/main/jni/badvpn/tuntap/BTap.c @@ -0,0 +1,577 @@ +/** + * @file BTap.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef BADVPN_USE_WINAPI + #include + #include + #include + #include + #include "wintap-common.h" + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #ifdef BADVPN_LINUX + #include + #endif + #ifdef BADVPN_FREEBSD + #include + #include + #endif +#endif + +#include + +#include + +#include + +static void report_error (BTap *o); +static void output_handler_recv (BTap *o, uint8_t *data); + +#ifdef BADVPN_USE_WINAPI + +static void recv_olap_handler (BTap *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->output_packet) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set no output packet + o->output_packet = NULL; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "read operation failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->frame_mtu) + + // done + PacketRecvInterface_Done(&o->output, bytes); +} + +#else + +static void fd_handler (BTap *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + if (events&(BREACTOR_ERROR|BREACTOR_HUP)) { + BLog(BLOG_WARNING, "device fd reports error?"); + } + + if (events&BREACTOR_READ) do { + ASSERT(o->output_packet) + + // try reading into the buffer + int bytes = read(o->fd, o->output_packet, o->frame_mtu); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // retry later + break; + } + // report fatal error + report_error(o); + return; + } + + ASSERT_FORCE(bytes <= o->frame_mtu) + + // set no output packet + o->output_packet = NULL; + + // update events + o->poll_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->poll_events); + + // inform receiver we finished the packet + PacketRecvInterface_Done(&o->output, bytes); + } while (0); +} + +#endif + +void report_error (BTap *o) +{ + DEBUGERROR(&o->d_err, o->handler_error(o->handler_error_user)); +} + +void output_handler_recv (BTap *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data) + ASSERT(!o->output_packet) + +#ifdef BADVPN_USE_WINAPI + + memset(&o->recv_olap.olap, 0, sizeof(o->recv_olap.olap)); + + // read + BOOL res = ReadFile(o->device, data, o->frame_mtu, NULL, &o->recv_olap.olap); + if (res == FALSE && GetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "ReadFile failed (%u)", GetLastError()); + report_error(o); + return; + } + + o->output_packet = data; + +#else + + // attempt read + int bytes = read(o->fd, data, o->frame_mtu); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // retry later in fd_handler + // remember packet + o->output_packet = data; + // update events + o->poll_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->poll_events); + return; + } + // report fatal error + report_error(o); + return; + } + + ASSERT_FORCE(bytes <= o->frame_mtu) + + PacketRecvInterface_Done(&o->output, bytes); + +#endif +} + +int BTap_Init (BTap *o, BReactor *reactor, char *devname, BTap_handler_error handler_error, void *handler_error_user, int tun) +{ + ASSERT(tun == 0 || tun == 1) + + struct BTap_init_data init_data; + init_data.dev_type = tun ? BTAP_DEV_TUN : BTAP_DEV_TAP; + init_data.init_type = BTAP_INIT_STRING; + init_data.init.string = devname; + + return BTap_Init2(o, reactor, init_data, handler_error, handler_error_user); +} + +int BTap_Init2 (BTap *o, BReactor *reactor, struct BTap_init_data init_data, BTap_handler_error handler_error, void *handler_error_user) +{ + ASSERT(init_data.dev_type == BTAP_DEV_TUN || init_data.dev_type == BTAP_DEV_TAP) + + // init arguments + o->reactor = reactor; + o->handler_error = handler_error; + o->handler_error_user = handler_error_user; + + #ifdef BADVPN_USE_WINAPI + + ASSERT(init_data.init_type == BTAP_INIT_STRING) + + // parse device specification + + if (!init_data.init.string) { + BLog(BLOG_ERROR, "no device specification provided"); + goto fail0; + } + + char *device_component_id; + char *device_name; + uint32_t tun_addrs[3]; + + if (init_data.dev_type == BTAP_DEV_TUN) { + if (!tapwin32_parse_tun_spec(init_data.init.string, &device_component_id, &device_name, tun_addrs)) { + BLog(BLOG_ERROR, "failed to parse TUN device specification"); + goto fail0; + } + } else { + if (!tapwin32_parse_tap_spec(init_data.init.string, &device_component_id, &device_name)) { + BLog(BLOG_ERROR, "failed to parse TAP device specification"); + goto fail0; + } + } + + // locate device path + + char device_path[TAPWIN32_MAX_REG_SIZE]; + + BLog(BLOG_INFO, "Looking for TAP-Win32 with component ID %s, name %s", device_component_id, device_name); + + if (!tapwin32_find_device(device_component_id, device_name, &device_path)) { + BLog(BLOG_ERROR, "Could not find device"); + goto fail1; + } + + // open device + + BLog(BLOG_INFO, "Opening device %s", device_path); + + o->device = CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED, 0); + if (o->device == INVALID_HANDLE_VALUE) { + BLog(BLOG_ERROR, "CreateFile failed"); + goto fail1; + } + + // set TUN if needed + + DWORD len; + + if (init_data.dev_type == BTAP_DEV_TUN) { + if (!DeviceIoControl(o->device, TAP_IOCTL_CONFIG_TUN, tun_addrs, sizeof(tun_addrs), tun_addrs, sizeof(tun_addrs), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_CONFIG_TUN) failed"); + goto fail2; + } + } + + // get MTU + + ULONG umtu; + + if (!DeviceIoControl(o->device, TAP_IOCTL_GET_MTU, NULL, 0, &umtu, sizeof(umtu), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_GET_MTU) failed"); + goto fail2; + } + + if (init_data.dev_type == BTAP_DEV_TUN) { + o->frame_mtu = umtu; + } else { + o->frame_mtu = umtu + BTAP_ETHERNET_HEADER_LENGTH; + } + + // set connected + + ULONG upstatus = TRUE; + if (!DeviceIoControl(o->device, TAP_IOCTL_SET_MEDIA_STATUS, &upstatus, sizeof(upstatus), &upstatus, sizeof(upstatus), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_SET_MEDIA_STATUS) failed"); + goto fail2; + } + + BLog(BLOG_INFO, "Device opened"); + + // associate device with IOCP + + if (!CreateIoCompletionPort(o->device, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail2; + } + + // init send olap + BReactorIOCPOverlapped_Init(&o->send_olap, o->reactor, o, NULL); + + // init recv olap + BReactorIOCPOverlapped_Init(&o->recv_olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler); + + free(device_name); + free(device_component_id); + + goto success; + +fail2: + ASSERT_FORCE(CloseHandle(o->device)) +fail1: + free(device_name); + free(device_component_id); +fail0: + return 0; + + #endif + + #if defined(BADVPN_LINUX) || defined(BADVPN_FREEBSD) + + o->close_fd = (init_data.init_type != BTAP_INIT_FD); + + switch (init_data.init_type) { + case BTAP_INIT_FD: { + ASSERT(init_data.init.fd.fd >= 0) + ASSERT(init_data.init.fd.mtu >= 0) + ASSERT(init_data.dev_type != BTAP_DEV_TAP || init_data.init.fd.mtu >= BTAP_ETHERNET_HEADER_LENGTH) + + o->fd = init_data.init.fd.fd; + o->frame_mtu = init_data.init.fd.mtu; + } break; + + case BTAP_INIT_STRING: { + char devname_real[IFNAMSIZ]; + + #ifdef BADVPN_LINUX + + // open device + + if ((o->fd = open("/dev/net/tun", O_RDWR)) < 0) { + BLog(BLOG_ERROR, "error opening device"); + goto fail0; + } + + // configure device + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags |= IFF_NO_PI; + if (init_data.dev_type == BTAP_DEV_TUN) { + ifr.ifr_flags |= IFF_TUN; + } else { + ifr.ifr_flags |= IFF_TAP; + } + if (init_data.init.string) { + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", init_data.init.string); + } + + if (ioctl(o->fd, TUNSETIFF, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error configuring device"); + goto fail1; + } + + strcpy(devname_real, ifr.ifr_name); + + #endif + + #ifdef BADVPN_FREEBSD + + if (init_data.dev_type == BTAP_DEV_TUN) { + BLog(BLOG_ERROR, "TUN not supported on FreeBSD"); + goto fail0; + } + + if (!init_data.init.string) { + BLog(BLOG_ERROR, "no device specified"); + goto fail0; + } + + // open device + + char devnode[10 + IFNAMSIZ]; + snprintf(devnode, sizeof(devnode), "/dev/%s", init_data.init.string); + + if ((o->fd = open(devnode, O_RDWR)) < 0) { + BLog(BLOG_ERROR, "error opening device"); + goto fail0; + } + + // get name + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + if (ioctl(o->fd, TAPGIFNAME, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error configuring device"); + goto fail1; + } + + strcpy(devname_real, ifr.ifr_name); + + #endif + + // get MTU + + // open dummy socket for ioctls + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname_real); + + if (ioctl(sock, SIOCGIFMTU, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error getting MTU"); + close(sock); + goto fail1; + } + + if (init_data.dev_type == BTAP_DEV_TUN) { + o->frame_mtu = ifr.ifr_mtu; + } else { + o->frame_mtu = ifr.ifr_mtu + BTAP_ETHERNET_HEADER_LENGTH; + } + + close(sock); + } break; + + default: ASSERT(0); + } + + // set non-blocking + if (fcntl(o->fd, F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "cannot set non-blocking"); + goto fail1; + } + + // init file descriptor object + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + o->poll_events = 0; + + goto success; + +fail1: + if (o->close_fd) { + ASSERT_FORCE(close(o->fd) == 0) + } +fail0: + return 0; + + #endif + +success: + // init output + PacketRecvInterface_Init(&o->output, o->frame_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // set no output packet + o->output_packet = NULL; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; +} + +void BTap_Free (BTap *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free output + PacketRecvInterface_Free(&o->output); + +#ifdef BADVPN_USE_WINAPI + + // cancel I/O + ASSERT_FORCE(CancelIo(o->device)) + + // wait receiving to finish + if (o->output_packet) { + BLog(BLOG_DEBUG, "waiting for receiving to finish"); + BReactorIOCPOverlapped_Wait(&o->recv_olap, NULL, NULL); + } + + // free recv olap + BReactorIOCPOverlapped_Free(&o->recv_olap); + + // free send olap + BReactorIOCPOverlapped_Free(&o->send_olap); + + // close device + ASSERT_FORCE(CloseHandle(o->device)) + +#else + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + if (o->close_fd) { + // close file descriptor + ASSERT_FORCE(close(o->fd) == 0) + } + +#endif +} + +int BTap_GetMTU (BTap *o) +{ + DebugObject_Access(&o->d_obj); + + return o->frame_mtu; +} + +void BTap_Send (BTap *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data_len >= 0) + ASSERT(data_len <= o->frame_mtu) + +#ifdef BADVPN_USE_WINAPI + + // ignore frames without an Ethernet header, or we get errors in WriteFile + if (data_len < 14) { + return; + } + + memset(&o->send_olap.olap, 0, sizeof(o->send_olap.olap)); + + // write + BOOL res = WriteFile(o->device, data, data_len, NULL, &o->send_olap.olap); + if (res == FALSE && GetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "WriteFile failed (%u)", GetLastError()); + return; + } + + // wait + int succeeded; + DWORD bytes; + BReactorIOCPOverlapped_Wait(&o->send_olap, &succeeded, &bytes); + + if (!succeeded) { + BLog(BLOG_ERROR, "write operation failed"); + } else { + ASSERT(bytes >= 0) + ASSERT(bytes <= data_len) + + if (bytes < data_len) { + BLog(BLOG_ERROR, "write operation didn't write everything"); + } + } + +#else + + int bytes = write(o->fd, data, data_len); + if (bytes < 0) { + // malformed packets will cause errors, ignore them and act like + // the packet was accepeted + } else { + if (bytes != data_len) { + BLog(BLOG_WARNING, "written %d expected %d", bytes, data_len); + } + } + +#endif +} + +PacketRecvInterface * BTap_GetOutput (BTap *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/service/src/main/jni/badvpn/tuntap/BTap.h b/service/src/main/jni/badvpn/tuntap/BTap.h new file mode 100644 index 0000000..021ac83 --- /dev/null +++ b/service/src/main/jni/badvpn/tuntap/BTap.h @@ -0,0 +1,196 @@ +/** + * @file BTap.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * TAP device abstraction. + */ + +#ifndef BADVPN_TUNTAP_BTAP_H +#define BADVPN_TUNTAP_BTAP_H + +#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_LINUX) + defined(BADVPN_FREEBSD)) != 1 +#error Unknown TAP backend or too many TAP backends +#endif + +#include + +#ifdef BADVPN_USE_WINAPI +#else +#include +#endif + +#include +#include +#include +#include +#include + +#define BTAP_ETHERNET_HEADER_LENGTH 14 + +/** + * Handler called when an error occurs on the device. + * The object must be destroyed from the job context of this + * handler, and no further I/O may occur. + * + * @param user as in {@link BTap_Init} + */ +typedef void (*BTap_handler_error) (void *used); + +typedef struct { + BReactor *reactor; + BTap_handler_error handler_error; + void *handler_error_user; + int frame_mtu; + PacketRecvInterface output; + uint8_t *output_packet; + +#ifdef BADVPN_USE_WINAPI + HANDLE device; + BReactorIOCPOverlapped send_olap; + BReactorIOCPOverlapped recv_olap; +#else + int close_fd; + int fd; + BFileDescriptor bfd; + int poll_events; +#endif + + DebugError d_err; + DebugObject d_obj; +} BTap; + +/** + * Initializes the TAP device. + * + * @param o the object + * @param BReactor {@link BReactor} we live in + * @param devname name of the devece to open. + * On Linux: a network interface name. If it is NULL, no + * specific device will be requested, and the operating system + * may create a new device. + * On Windows: a string "component_id:device_name", where + * component_id is a string identifying the driver, and device_name + * is the name of the network interface. If component_id is empty, + * a hardcoded default will be used instead. If device_name is empty, + * the first device found with a matching component_id will be used. + * Specifying a NULL devname is equivalent to specifying ":". + * @param handler_error error handler function + * @param handler_error_user value passed to error handler + * @param tun whether to create a TUN (IP) device or a TAP (Ethernet) device. Must be 0 or 1. + * @return 1 on success, 0 on failure + */ +int BTap_Init (BTap *o, BReactor *bsys, char *devname, BTap_handler_error handler_error, void *handler_error_user, int tun) WARN_UNUSED; + +enum BTap_dev_type {BTAP_DEV_TUN, BTAP_DEV_TAP}; + +enum BTap_init_type { + BTAP_INIT_STRING, +#ifndef BADVPN_USE_WINAPI + BTAP_INIT_FD, +#endif +}; + +struct BTap_init_data { + enum BTap_dev_type dev_type; + enum BTap_init_type init_type; + union { + char *string; + struct { + int fd; + int mtu; + } fd; + } init; +}; + +/** + * Initializes the TAP device. + * + * @param o the object + * @param BReactor {@link BReactor} we live in + * @param init_data struct containing initialization parameters (to allow transparent passing). + * init.data.dev_type must be either BTAP_DEV_TUN for an IP device, or + * BTAP_DEV_TAP for an Ethernet device. + * init_data.init_type must be either BTAP_INIT_STRING or BTAP_INIT_FD. + * For BTAP_INIT_STRING, init_data.init.string specifies the TUN or TAP + * device, as described next. + * On Linux: a network interface name. If it is NULL, no + * specific device will be requested, and the operating system + * may create a new device. + * On Windows: a string "component_id:device_name", where + * component_id is a string identifying the driver, and device_name + * is the name of the network interface. If component_id is empty, + * a hardcoded default will be used instead. If device_name is empty, + * the first device found with a matching component_id will be used. + * Specifying NULL is equivalent to specifying ":". + * For BTAP_INIT_FD, the device is initialized using a file descriptor. + * In this case, init_data.init.fd.fd must be set to the file descriptor, + * and init_data.init.fd.mtu must be set to the largest IP packet or + * Ethernet frame supported, for a TUN or TAP device, respectively. + * File descriptor initialization is not supported on Windows. + * @param handler_error error handler function + * @param handler_error_user value passed to error handler + * @return 1 on success, 0 on failure + */ +int BTap_Init2 (BTap *o, BReactor *reactor, struct BTap_init_data init_data, BTap_handler_error handler_error, void *handler_error_user) WARN_UNUSED; + +/** + * Frees the TAP device. + * + * @param o the object + */ +void BTap_Free (BTap *o); + +/** + * Returns the device's maximum transmission unit (including any protocol headers). + * + * @param o the object + * @return device's MTU + */ +int BTap_GetMTU (BTap *o); + +/** + * Sends a packet to the device. + * Any errors will be reported via a job. + * + * @param o the object + * @param data packet to send + * @param data_len length of packet. Must be >=0 and <=MTU, as reported by {@link BTap_GetMTU}. + */ +void BTap_Send (BTap *o, uint8_t *data, int data_len); + +/** + * Returns a {@link PacketRecvInterface} for reading packets from the device. + * The MTU of the interface will be {@link BTap_GetMTU}. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * BTap_GetOutput (BTap *o); + +#endif diff --git a/service/src/main/jni/badvpn/tuntap/tapwin32-funcs.h b/service/src/main/jni/badvpn/tuntap/tapwin32-funcs.h new file mode 100644 index 0000000..fed66de --- /dev/null +++ b/service/src/main/jni/badvpn/tuntap/tapwin32-funcs.h @@ -0,0 +1,42 @@ +/** + * @file tapwin32-funcs.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_TUNTAP_TAPWIN32_FUNCS_H +#define BADVPN_TUNTAP_TAPWIN32_FUNCS_H + +#include +#include + +#define TAPWIN32_MAX_REG_SIZE 256 + +int tapwin32_parse_tap_spec (char *name, char **out_component_id, char **out_human_name); +int tapwin32_parse_tun_spec (char *name, char **out_component_id, char **out_human_name, uint32_t out_addrs[3]); +int tapwin32_find_device (char *device_component_id, char *device_name, char (*device_path)[TAPWIN32_MAX_REG_SIZE]); + +#endif diff --git a/service/src/main/jni/badvpn/tuntap/wintap-common.h b/service/src/main/jni/badvpn/tuntap/wintap-common.h new file mode 100644 index 0000000..922c851 --- /dev/null +++ b/service/src/main/jni/badvpn/tuntap/wintap-common.h @@ -0,0 +1,39 @@ +/** + * @file wintap-common.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * API definitions for TAP-Win32. + */ + +#define TAP_IOCTL_CONFIG_TUN CTL_CODE(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define TAP_IOCTL_GET_MTU CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define TAP_IOCTL_SET_MEDIA_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" +#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" diff --git a/service/src/main/jni/badvpn/udpgw_client/UdpGwClient.c b/service/src/main/jni/badvpn/udpgw_client/UdpGwClient.c new file mode 100644 index 0000000..85d069a --- /dev/null +++ b/service/src/main/jni/badvpn/udpgw_client/UdpGwClient.c @@ -0,0 +1,597 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2); +static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2); +static void free_server (UdpGwClient *o); +static void decoder_handler_error (UdpGwClient *o); +static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len); +static void send_monitor_handler (UdpGwClient *o); +static void keepalive_if_handler_done (UdpGwClient *o); +static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); +static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid); +static uint16_t find_unused_conid (UdpGwClient *o); +static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len); +static void connection_free (struct UdpGwClient_connection *con); +static void connection_first_job_handler (struct UdpGwClient_connection *con); +static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len); +static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); + +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2) +{ + return B_COMPARE(*v1, *v2); +} + +static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2) +{ + int r = BAddr_CompareOrder(&v1->remote_addr, &v2->remote_addr); + if (r) { + return r; + } + return BAddr_CompareOrder(&v1->local_addr, &v2->local_addr); +} + +static void free_server (UdpGwClient *o) +{ + // disconnect send connector + PacketPassConnector_DisconnectOutput(&o->send_connector); + + // free send sender + PacketStreamSender_Free(&o->send_sender); + + // free receive decoder + PacketProtoDecoder_Free(&o->recv_decoder); + + // free receive interface + PacketPassInterface_Free(&o->recv_if); +} + +static void decoder_handler_error (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + + BLog(BLOG_ERROR, "decoder error"); + + // report error + o->handler_servererror(o->user); + return; +} + +static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udpgw_mtu) + + // accept packet + PacketPassInterface_Done(&o->recv_if); + + // check header + if (data_len < sizeof(struct udpgw_header)) { + BLog(BLOG_ERROR, "missing header"); + return; + } + struct udpgw_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t flags = ltoh8(header.flags); + uint16_t conid = ltoh16(header.conid); + + // parse address + BAddr remote_addr; + if ((flags & UDPGW_CLIENT_FLAG_IPV6)) { + if (data_len < sizeof(struct udpgw_addr_ipv6)) { + BLog(BLOG_ERROR, "missing ipv6 address"); + return; + } + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); + BAddr_InitIPv6(&remote_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); + } else { + if (data_len < sizeof(struct udpgw_addr_ipv4)) { + BLog(BLOG_ERROR, "missing ipv4 address"); + return; + } + struct udpgw_addr_ipv4 addr_ipv4; + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); + BAddr_InitIPv4(&remote_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); + } + + // check remaining data + if (data_len > o->udp_mtu) { + BLog(BLOG_ERROR, "too much data"); + return; + } + + // find connection + struct UdpGwClient_connection *con = find_connection_by_conid(o, conid); + if (!con) { + BLog(BLOG_ERROR, "unknown conid"); + return; + } + + // check remote address + if (BAddr_CompareOrder(&con->conaddr.remote_addr, &remote_addr) != 0) { + BLog(BLOG_ERROR, "wrong remote address"); + return; + } + + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // pass packet to user + o->handler_received(o->user, con->conaddr.local_addr, con->conaddr.remote_addr, data, data_len); + return; +} + +static void send_monitor_handler (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + + if (o->keepalive_sending) { + return; + } + + BLog(BLOG_INFO, "keepalive"); + + // send keepalive + PacketPassInterface_Sender_Send(o->keepalive_if, (uint8_t *)&o->keepalive_packet, sizeof(o->keepalive_packet)); + + // set sending keep-alive + o->keepalive_sending = 1; +} + +static void keepalive_if_handler_done (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->keepalive_sending) + + // set not sending keepalive + o->keepalive_sending = 0; +} + +static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conaddr, &conaddr); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conaddr_node); +} + +static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conid, &conid); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conid_node); +} + +static uint16_t find_unused_conid (UdpGwClient *o) +{ + ASSERT(o->num_connections < o->max_connections) + + while (1) { + if (!find_connection_by_conid(o, o->next_conid)) { + return o->next_conid; + } + + if (o->next_conid == o->max_connections - 1) { + o->next_conid = 0; + } else { + o->next_conid++; + } + } +} + +static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len) +{ + ASSERT(o->num_connections < o->max_connections) + ASSERT(!find_connection_by_conaddr(o, conaddr)) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // allocate structure + struct UdpGwClient_connection *con = (struct UdpGwClient_connection *)malloc(sizeof(*con)); + if (!con) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init arguments + con->client = o; + con->conaddr = conaddr; + con->first_flags = flags; + con->first_data = data; + con->first_data_len = data_len; + + // allocate conid + con->conid = find_unused_conid(o); + + // init first job + BPending_Init(&con->first_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_first_job_handler, con); + BPending_Set(&con->first_job); + + // init queue flow + PacketPassFairQueueFlow_Init(&con->send_qflow, &o->send_queue); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init(&con->send_ppflow, o->udpgw_mtu, o->send_buffer_size, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail1; + } + con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow); + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) + + // insert to connections tree by conid + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node, NULL)) + + // insert to connections list + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // increment number of connections + o->num_connections++; + + return; + +fail1: + PacketPassFairQueueFlow_Free(&con->send_qflow); + BPending_Free(&con->first_job); + free(con); +fail0: + return; +} + +static void connection_free (struct UdpGwClient_connection *con) +{ + UdpGwClient *o = con->client; + PacketPassFairQueueFlow_AssertFree(&con->send_qflow); + + // decrement number of connections + o->num_connections--; + + // remove from connections list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + + // remove from connections tree by conid + BAVL_Remove(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); + + // free PacketProtoFlow + PacketProtoFlow_Free(&con->send_ppflow); + + // free queue flow + PacketPassFairQueueFlow_Free(&con->send_qflow); + + // free first job + BPending_Free(&con->first_job); + + // free structure + free(con); +} + +static void connection_first_job_handler (struct UdpGwClient_connection *con) +{ + connection_send(con, UDPGW_CLIENT_FLAG_REBIND|con->first_flags, con->first_data, con->first_data_len); +} + +static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len) +{ + UdpGwClient *o = con->client; + B_USE(o) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(con->send_if, &out)) { + BLog(BLOG_ERROR, "out of buffer"); + return; + } + int out_pos = 0; + + if (con->conaddr.remote_addr.type == BADDR_TYPE_IPV6) { + flags |= UDPGW_CLIENT_FLAG_IPV6; + } + + // write header + struct udpgw_header header; + header.flags = ltoh8(flags); + header.conid = ltoh16(con->conid); + memcpy(out + out_pos, &header, sizeof(header)); + out_pos += sizeof(header); + + // write address + switch (con->conaddr.remote_addr.type) { + case BADDR_TYPE_IPV4: { + struct udpgw_addr_ipv4 addr_ipv4; + addr_ipv4.addr_ip = con->conaddr.remote_addr.ipv4.ip; + addr_ipv4.addr_port = con->conaddr.remote_addr.ipv4.port; + memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); + out_pos += sizeof(addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr_ip, con->conaddr.remote_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); + addr_ipv6.addr_port = con->conaddr.remote_addr.ipv6.port; + memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); + out_pos += sizeof(addr_ipv6); + } break; + } + + // write packet to buffer + memcpy(out + out_pos, data, data_len); + out_pos += data_len; + + // submit packet to buffer + BufferWriter_EndPacket(con->send_if, out_pos); +} + +static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) +{ + ASSERT(!find_connection_by_conaddr(o, conaddr)) + ASSERT(o->num_connections > 0) + + // get least recently used connection + struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); + + // set new conaddr + con->conaddr = conaddr; + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) + + return con; +} + +int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user, + UdpGwClient_handler_servererror handler_servererror, + UdpGwClient_handler_received handler_received) +{ + ASSERT(udp_mtu >= 0) + ASSERT(udpgw_compute_mtu(udp_mtu) >= 0) + ASSERT(udpgw_compute_mtu(udp_mtu) <= PACKETPROTO_MAXPAYLOAD) + ASSERT(max_connections > 0) + ASSERT(send_buffer_size > 0) + + // init arguments + o->udp_mtu = udp_mtu; + o->max_connections = max_connections; + o->send_buffer_size = send_buffer_size; + o->keepalive_time = keepalive_time; + o->reactor = reactor; + o->user = user; + o->handler_servererror = handler_servererror; + o->handler_received = handler_received; + + // limit max connections to number of conid's + if (o->max_connections > UINT16_MAX + 1) { + o->max_connections = UINT16_MAX + 1; + } + + // compute MTUs + o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu); + o->pp_mtu = o->udpgw_mtu + sizeof(struct packetproto_header); + + // init connections tree by conaddr + BAVL_Init(&o->connections_tree_by_conaddr, OFFSET_DIFF(struct UdpGwClient_connection, conaddr, connections_tree_by_conaddr_node), (BAVL_comparator)conaddr_comparator, NULL); + + // init connections tree by conid + BAVL_Init(&o->connections_tree_by_conid, OFFSET_DIFF(struct UdpGwClient_connection, conid, connections_tree_by_conid_node), (BAVL_comparator)uint16_comparator, NULL); + + // init connections list + LinkedList1_Init(&o->connections_list); + + // set zero connections + o->num_connections = 0; + + // set next conid + o->next_conid = 0; + + // init send connector + PacketPassConnector_Init(&o->send_connector, o->pp_mtu, BReactor_PendingGroup(o->reactor)); + + // init send monitor + PacketPassInactivityMonitor_Init(&o->send_monitor, PacketPassConnector_GetInput(&o->send_connector), o->reactor, o->keepalive_time, (PacketPassInactivityMonitor_handler)send_monitor_handler, o); + + // init send queue + if (!PacketPassFairQueue_Init(&o->send_queue, PacketPassInactivityMonitor_GetInput(&o->send_monitor), BReactor_PendingGroup(o->reactor), 0, 1)) { + goto fail0; + } + + // construct keepalive packet + o->keepalive_packet.pp.len = sizeof(o->keepalive_packet.udpgw); + memset(&o->keepalive_packet.udpgw, 0, sizeof(o->keepalive_packet.udpgw)); + o->keepalive_packet.udpgw.flags = UDPGW_CLIENT_FLAG_KEEPALIVE; + + // init keepalive queue flow + PacketPassFairQueueFlow_Init(&o->keepalive_qflow, &o->send_queue); + o->keepalive_if = PacketPassFairQueueFlow_GetInput(&o->keepalive_qflow); + + // init keepalive output + PacketPassInterface_Sender_Init(o->keepalive_if, (PacketPassInterface_handler_done)keepalive_if_handler_done, o); + + // set not sending keepalive + o->keepalive_sending = 0; + + // set have no server + o->have_server = 0; + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + PacketPassInactivityMonitor_Free(&o->send_monitor); + PacketPassConnector_Free(&o->send_connector); + return 0; +} + +void UdpGwClient_Free (UdpGwClient *o) +{ + DebugObject_Free(&o->d_obj); + + // allow freeing send queue flows + PacketPassFairQueue_PrepareFree(&o->send_queue); + + // free connections + while (!LinkedList1_IsEmpty(&o->connections_list)) { + struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); + connection_free(con); + } + + // free server + if (o->have_server) { + free_server(o); + } + + // free keepalive queue flow + PacketPassFairQueueFlow_Free(&o->keepalive_qflow); + + // free send queue + PacketPassFairQueue_Free(&o->send_queue); + + // free send + PacketPassInactivityMonitor_Free(&o->send_monitor); + + // free send connector + PacketPassConnector_Free(&o->send_connector); +} + +void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // build conaddr + struct UdpGwClient_conaddr conaddr; + conaddr.local_addr = local_addr; + conaddr.remote_addr = remote_addr; + + // lookup connection + struct UdpGwClient_connection *con = find_connection_by_conaddr(o, conaddr); + + uint8_t flags = 0; + + if (is_dns) { + // route to remote DNS server instead of provided address + flags |= UDPGW_CLIENT_FLAG_DNS; + } + + // if no connection and can't create a new one, reuse the least recently used une + if (!con && o->num_connections == o->max_connections) { + con = reuse_connection(o, conaddr); + flags |= UDPGW_CLIENT_FLAG_REBIND; + } + + if (!con) { + // create new connection + connection_init(o, conaddr, flags, data, data_len); + } else { + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // send packet to existing connection + connection_send(con, flags, data, data_len); + } +} + +int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_server) + + // init receive interface + PacketPassInterface_Init(&o->recv_if, o->udpgw_mtu, (PacketPassInterface_handler_send)recv_interface_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init receive decoder + if (!PacketProtoDecoder_Init(&o->recv_decoder, recv_if, &o->recv_if, BReactor_PendingGroup(o->reactor), o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail1; + } + + // init send sender + PacketStreamSender_Init(&o->send_sender, send_if, o->pp_mtu, BReactor_PendingGroup(o->reactor)); + + // connect send connector + PacketPassConnector_ConnectOutput(&o->send_connector, PacketStreamSender_GetInput(&o->send_sender)); + + // set have server + o->have_server = 1; + + return 1; + +fail1: + PacketPassInterface_Free(&o->recv_if); + return 0; +} + +void UdpGwClient_DisconnectServer (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + + // free server + free_server(o); + + // set have no server + o->have_server = 0; +} diff --git a/service/src/main/jni/badvpn/udpgw_client/UdpGwClient.h b/service/src/main/jni/badvpn/udpgw_client/UdpGwClient.h new file mode 100644 index 0000000..0a1086b --- /dev/null +++ b/service/src/main/jni/badvpn/udpgw_client/UdpGwClient.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDPGW_CLIENT_UDPGWCLIENT_H +#define BADVPN_UDPGW_CLIENT_UDPGWCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*UdpGwClient_handler_servererror) (void *user); +typedef void (*UdpGwClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +B_START_PACKED +struct UdpGwClient__keepalive_packet { + struct packetproto_header pp; + struct udpgw_header udpgw; +} B_PACKED; +B_END_PACKED + +typedef struct { + int udp_mtu; + int max_connections; + int send_buffer_size; + btime_t keepalive_time; + BReactor *reactor; + void *user; + UdpGwClient_handler_servererror handler_servererror; + UdpGwClient_handler_received handler_received; + int udpgw_mtu; + int pp_mtu; + BAVL connections_tree_by_conaddr; + BAVL connections_tree_by_conid; + LinkedList1 connections_list; + int num_connections; + int next_conid; + PacketPassFairQueue send_queue; + PacketPassInactivityMonitor send_monitor; + PacketPassConnector send_connector; + struct UdpGwClient__keepalive_packet keepalive_packet; + PacketPassInterface *keepalive_if; + PacketPassFairQueueFlow keepalive_qflow; + int keepalive_sending; + int have_server; + PacketStreamSender send_sender; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + DebugObject d_obj; +} UdpGwClient; + +struct UdpGwClient_conaddr { + BAddr local_addr; + BAddr remote_addr; +}; + +struct UdpGwClient_connection { + UdpGwClient *client; + struct UdpGwClient_conaddr conaddr; + uint8_t first_flags; + const uint8_t *first_data; + int first_data_len; + uint16_t conid; + BPending first_job; + BufferWriter *send_if; + PacketProtoFlow send_ppflow; + PacketPassFairQueueFlow send_qflow; + BAVLNode connections_tree_by_conaddr_node; + BAVLNode connections_tree_by_conid_node; + LinkedList1Node connections_list_node; +}; + +int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user, + UdpGwClient_handler_servererror handler_servererror, + UdpGwClient_handler_received handler_received) WARN_UNUSED; +void UdpGwClient_Free (UdpGwClient *o); +void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len); +int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) WARN_UNUSED; +void UdpGwClient_DisconnectServer (UdpGwClient *o); + +#endif diff --git a/service/src/main/jni/build-shared-executable.mk b/service/src/main/jni/build-shared-executable.mk new file mode 100644 index 0000000..05239df --- /dev/null +++ b/service/src/main/jni/build-shared-executable.mk @@ -0,0 +1,31 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# this file is included from Android.mk files to build a target-specific +# executable program +# +# Modified by @Mygod, based on: +# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-shared-library.mk +# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-executable.mk +LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE +LOCAL_MAKEFILE := $(local-makefile) +$(call check-defined-LOCAL_MODULE,$(LOCAL_BUILD_SCRIPT)) +$(call check-LOCAL_MODULE,$(LOCAL_MAKEFILE)) +$(call check-LOCAL_MODULE_FILENAME) +# we are building target objects +my := TARGET_ +$(call handle-module-filename,lib,$(TARGET_SONAME_EXTENSION)) +$(call handle-module-built) +LOCAL_MODULE_CLASS := EXECUTABLE +include $(BUILD_SYSTEM)/build-module.mk diff --git a/service/src/main/jni/libancillary/COPYING b/service/src/main/jni/libancillary/COPYING new file mode 100644 index 0000000..5bcd9c2 --- /dev/null +++ b/service/src/main/jni/libancillary/COPYING @@ -0,0 +1,21 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/service/src/main/jni/libancillary/ancillary.h b/service/src/main/jni/libancillary/ancillary.h new file mode 100644 index 0000000..636d867 --- /dev/null +++ b/service/src/main/jni/libancillary/ancillary.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * ancillary.c - public header + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANCILLARY_H__ +#define ANCILLARY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************************** + * Start of the readable part. + ***************************************************************************/ + +#define ANCIL_MAX_N_FDS 960 +/* + * Maximum number of fds that can be sent or received using the "esay" + * functions; this is so that all can fit in one page. + */ + +extern int +ancil_send_fds_with_buffer(int, const int *, unsigned, void *); +/* + * ancil_send_fds_with_buffer(sock, n_fds, fds, buffer) + * + * Sends the file descriptors in the array pointed by fds, of length n_fds + * on the socket sock. + * buffer is a writeable memory area large enough to hold the required data + * structures. + * Returns: -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fds_with_buffer(int, int *, unsigned, void *); +/* + * ancil_recv_fds_with_buffer(sock, n_fds, fds, buffer) + * + * Receives *n_fds file descriptors into the array pointed by fds + * from the socket sock. + * buffer is a writeable memory area large enough to hold the required data + * structures. + * Returns: -1 and errno in case of error, the actual number of received fd + * in case of success + */ + +#define ANCIL_FD_BUFFER(n) \ + struct { \ + struct cmsghdr h; \ + int fd[n]; \ + } +/* ANCIL_FD_BUFFER(n) + * + * A structure type suitable to be used as buffer for n file descriptors. + * Requires . + * Example: + * ANCIL_FD_BUFFER(42) buffer; + * ancil_recv_fds_with_buffer(sock, 42, my_fds, &buffer); + */ + +extern int +ancil_send_fds(int, const int *, unsigned); +/* + * ancil_send_fds(sock, n_fds, fds) + * + * Sends the file descriptors in the array pointed by fds, of length n_fds + * on the socket sock. + * n_fds must not be greater than ANCIL_MAX_N_FDS. + * Returns: -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fds(int, int *, unsigned); +/* + * ancil_recv_fds(sock, n_fds, fds) + * + * Receives *n_fds file descriptors into the array pointed by fds + * from the socket sock. + * *n_fds must not be greater than ANCIL_MAX_N_FDS. + * Returns: -1 and errno in case of error, the actual number of received fd + * in case of success. + */ + + +extern int +ancil_send_fd(int, int); +/* ancil_recv_fd(sock, fd); + * + * Sends the file descriptor fd on the socket sock. + * Returns : -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fd(int, int *); +/* ancil_send_fd(sock, &fd); + * + * Receives the file descriptor fd from the socket sock. + * Returns : -1 and errno in case of error, 0 in case of success. + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ANCILLARY_H__ */ diff --git a/service/src/main/jni/libancillary/fd_recv.c b/service/src/main/jni/libancillary/fd_recv.c new file mode 100644 index 0000000..46c2e69 --- /dev/null +++ b/service/src/main/jni/libancillary/fd_recv.c @@ -0,0 +1,98 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * fd_send.c - receiving file descriptors + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XPG4_2 /* Solaris sucks */ +# define _XPG4_2 +#endif + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include /* FreeBSD sucks */ +#endif + +#include "ancillary.h" + +int +ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer) +{ + struct msghdr msghdr; + char nothing; + struct iovec nothing_ptr; + struct cmsghdr *cmsg; + int i; + + nothing_ptr.iov_base = ¬hing; + nothing_ptr.iov_len = 1; + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = ¬hing_ptr; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; + msghdr.msg_control = buffer; + msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds; + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_len = msghdr.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + for(i = 0; i < n_fds; i++) + ((int *)CMSG_DATA(cmsg))[i] = -1; + + if(recvmsg(sock, &msghdr, 0) < 0) + return(-1); + for(i = 0; i < n_fds; i++) + fds[i] = ((int *)CMSG_DATA(cmsg))[i]; + n_fds = (msghdr.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); + return(n_fds); +} + +#ifndef SPARE_RECV_FDS +int +ancil_recv_fds(int sock, int *fd, unsigned n_fds) +{ + ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer; + + assert(n_fds <= ANCIL_MAX_N_FDS); + return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer)); +} +#endif /* SPARE_RECV_FDS */ + +#ifndef SPARE_RECV_FD +int +ancil_recv_fd(int sock, int *fd) +{ + ANCIL_FD_BUFFER(1) buffer; + + return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1); +} +#endif /* SPARE_RECV_FD */ diff --git a/service/src/main/jni/libancillary/fd_send.c b/service/src/main/jni/libancillary/fd_send.c new file mode 100644 index 0000000..01de87f --- /dev/null +++ b/service/src/main/jni/libancillary/fd_send.c @@ -0,0 +1,92 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * fd_send.c - sending file descriptors + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XPG4_2 /* Solaris sucks */ +# define _XPG4_2 +#endif + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include /* FreeBSD sucks */ +#endif + +#include "ancillary.h" + +int +ancil_send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer) +{ + struct msghdr msghdr; + char nothing = '!'; + struct iovec nothing_ptr; + struct cmsghdr *cmsg; + int i; + + nothing_ptr.iov_base = ¬hing; + nothing_ptr.iov_len = 1; + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = ¬hing_ptr; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; + msghdr.msg_control = buffer; + msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds; + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_len = msghdr.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + for(i = 0; i < n_fds; i++) + ((int *)CMSG_DATA(cmsg))[i] = fds[i]; + return(sendmsg(sock, &msghdr, 0) >= 0 ? 0 : -1); +} + +#ifndef SPARE_SEND_FDS +int +ancil_send_fds(int sock, const int *fds, unsigned n_fds) +{ + ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer; + + assert(n_fds <= ANCIL_MAX_N_FDS); + return(ancil_send_fds_with_buffer(sock, fds, n_fds, &buffer)); +} +#endif /* SPARE_SEND_FDS */ + +#ifndef SPARE_SEND_FD +int +ancil_send_fd(int sock, int fd) +{ + ANCIL_FD_BUFFER(1) buffer; + + return(ancil_send_fds_with_buffer(sock, &fd, 1, &buffer)); +} +#endif /* SPARE_SEND_FD */ diff --git a/service/src/main/jni/pdnsd/AUTHORS b/service/src/main/jni/pdnsd/AUTHORS new file mode 100644 index 0000000..fa0454e --- /dev/null +++ b/service/src/main/jni/pdnsd/AUTHORS @@ -0,0 +1,58 @@ +Most of pdnsd was written by Thomas Moestl (tmoestl@gmx.net). +In the "par" versions large parts of the code have been revised +and several features have been added by Paul Rombouts. + +Small parts of this program are based on code that was taken from nmap (IP +checksumming), the isdn4k-utils (ippp interface uptest), glibc 2.1.2 (some +definitions for kernel 2.2.x missing in 2.0 glibcs) and FreeBSD +(SIZEOF_ADDR_IFREQ in netdev.c). +nmap was written by Fyodor. The insd4k-utils were written by Fritz Elfert and +others. The GNU C library (glibc) is copyright by the Free Software +Foundation. + +The following people have contributed code: +Andrew M. Bishop contributed support for server labels +Carsten Block contributed 'configure'-able rc scripts +Stephan Boettcher contributed the SCHEME= option. +P.J. Bostley contributed patches to get pdnsd working on + alpha +Frank Elsner contributed rc script fixes +Christian Engstler contributed patches for SuSE compatability +Bjoern Fischer contributed code to make pdnsd leave the case of names + in the cache unchanged +Torben Janssen contributed RedHat rc scripts +Olaf Kirch contributed a security fix for the run_as() + function +Bernd Leibing contributed fixes to the spec file. +Sourav K. Mandal contributed the autoconf/automake code, gdbm + caching facility and many suggestions +Markus Mohr contributed Debian rc scripts +Alexandre Nunes contributed autoconf fixes +Wolfgang Ocker contributed the server_ip option +Soenke J. Peters contributed patches and suggestions for RedHat + compatability +Roman Shterenzon contributed many helpful hints and patches for + FreeBSD compatability. +Andreas Steinmetz contributed the code for the query_port_start and + query_port_end options (which I changed slightly, + so blame any breakage on me ;) +Marko Stolle contributed the contrib/pdnsd_update.pl script that + makes pdnsd usable in a DHCP setup. +Lyonel Vincent extended the serve_aliases option to support an + arbitrary number of aliases +Paul Wagland contributed a patches for bind9-compatability + and for some memory leaks on error paths. +Sverker Wiberg contributed IPv6 build fixes +Michael Wiedmann contributed the pdnsd-ctl.8 man page. +Ron Yorston contributed the dev-uptest for Linux ppp dial- + on-demand devices +Nikita V. Youshchenko contributed extensions to the "if" uptest +Mahesh T. Pai contributed the pdnsd.8 man page. +Nikola Kotur contributed the Slackware start-up script. +Kiyo Kelvin Lee contributed a patch for Cygwin support. +Rodney Brown contributed a patch for Darwin (Apple Mac OS X) support. +Jan-Marek Glogowski contributed a patch implementing the "use_nss" option. + +Please look into the THANKS file for people who helped me in various ways on +this project. +If this list is incomplete, pease drop me a mail! diff --git a/service/src/main/jni/pdnsd/COPYING b/service/src/main/jni/pdnsd/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/service/src/main/jni/pdnsd/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/service/src/main/jni/pdnsd/COPYING.BSD b/service/src/main/jni/pdnsd/COPYING.BSD new file mode 100644 index 0000000..99fe14a --- /dev/null +++ b/service/src/main/jni/pdnsd/COPYING.BSD @@ -0,0 +1,26 @@ +A small part of the pdnsd source is licensed under the following BSD-style +license: + +Copyright (C) 2001 Thomas Moestl + +This file is part of the pdnsd package. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/service/src/main/jni/pdnsd/ChangeLog b/service/src/main/jni/pdnsd/ChangeLog new file mode 100644 index 0000000..fe77465 --- /dev/null +++ b/service/src/main/jni/pdnsd/ChangeLog @@ -0,0 +1,3304 @@ +2012-04-23 Paul A. Rombouts + + * src/dns_query.c + Refine the return values of p_dns_cached_resolve(), p_dns_resolve() and + p_recursive_query() so that they distinguish between answers found in + the cache and replies obtained by querying other servers. + This, among other things, can be used to prevent data that was recently + obtained from the cache needlessly being added back to the cache. + +2012-04-22 Paul A. Rombouts + + * configure.in + On the Linux platform, check if we can compile and link with the + -pthread flag instead of linking with -lpthread. + +2012-04-21 Paul A. Rombouts + + * src/dns_query.c + When following the delegation chain trying to get an authoritative + answer, pdnsd would answer with SERVFAIL if it failed to get a reply + from the last server in the chain. Instead pdnsd will now use the last + reply in the chain with RCode=0 that raised the AA or RA flag, if there + is one. + +2012-04-19 Paul A. Rombouts + + * src/cache.c + In report_cache_stat(), make copies of volatile data to get a + consistent data set before making calculations with cache size and + entry numbers. + +2012-04-16 Paul A. Rombouts + + * src/netdev.c + If we can't open /proc/net/if_inet6 in is_local_addr() log a warning + message. + +2012-04-15 Paul A. Rombouts + + * src/dns_query.c + The code checking for duplicate IP addresses obtained from NS records + in auth_ok() has been slightly optimized. + +2012-04-12 Paul A. Rombouts + + * src/dns_query.c + When resolving nameservers obtained from NS records, allow pdnsd to use + more than one IP address per nameserver. + In rare cases, using just one IP address for each nameserver will cause + unnecessary resolve failures if the address chosen for each nameserver + happens to be unreachable while the other addresses would lead to + successful resolution, as demonstrated by Yuri Vorobyev. + +2012-03-16 Paul A. Rombouts + + * src/cache.c + When adding RR records one by one to a cache entry using add_cent_rr(), + use the smallest ttl value in case of conflicting ttls. + Code for local/nonlocal conflict resolution has been taken out of + add_cent_rr_int() and put into add_cent_rr() and cr_check_add() + which should be slightly more efficient. + +2012-03-15 Paul A. Rombouts + + * src/dns_query.c + Enforcing strict RFC 2181 compliance by rejecting all the answers + with inconsistent ttl timestamps can cause undesirable resolve failures. + I have tried to implement a more compromising solution, whereby + inconsistent answers that should be normally rejected are still never + cached, but are nevertheless used as intermediary or temporary results + if all else fails. + +2012-03-13 Paul A. Rombouts + + * src/dns_query.c + Fixed a typo in rr_to_cache() that caused pdnsd to fail to compile when + configured with the --enable-strict-rfc2181 option. + Thanks to Gonzalo L. R. for reporting this problem. + Also changed the return value of rr_to_cache() from a simple boolean to + an RC code in order to properly distinguish between memory allocation + errors and time-stamp inconsistencies. + +2012-02-21 Paul A. Rombouts + + * src/dns_query.c + If we have used EDNS in a query and the remote server answered + with rcode "format error", try again with the OPT pseudo-record + removed from the additional section of the query. + + Also fixed a bug in p_exec_query() that caused pdnsd to behave + as if every reply with a non-empty additional section contained + an OPT record. + +2012-02-15 Paul A. Rombouts + + * src/dns_answer.c,src/helpers.c,src/helpers.h,src/icmp.c, + src/ipvers.h,src/main.c,src/netdev.c + Introduced a new macro SEL_IPVER() to reduce some of the clutter in the + code caused by having to support both IPv4 and IPv6. + +2012-01-31 Paul A. Rombouts + + * configure.in + Add AM_PROG_CC_C_O line to configure.in to prevent automake warning. + +2012-01-29 Paul A. Rombouts + + * src/cache.c + In report_cache_stat(), add the average number of bytes used per cache + entry when reporting the cache status, as suggested by M. Galabant. + +2012-01-28 Paul A. Rombouts + + * src/dns_answer.c,src/dns_query.c + Cleaned up the code a bit to avoid warning messages when + compiling with '-Wall -Winline' flags. + +2012-01-18 Paul A. Rombouts + + * src/conff.c + Set the default of the edns_query option to false. + +2011-07-31 Paul Rombouts + + * src/cache.c + Use a slightly more sophisticated merge-sort algorithm in sort_rrl(). + +2011-05-09 Paul Rombouts + + * src/dns_answer.c + In compose_answer(), also add an OPT pseudo-RR to the additional section + of a NXDOMAIN reply when appropriate. + +2011-05-08 Paul Rombouts + + * src/cache.c,src/cache.h,src/dns_query.c,src/status.c + Make the dns_cent_t struct more compact by putting the fields that are + only used for either non-existent or existent domains, but not both, + into a union so that these fields can share memory. + When saving the cache to file, only write the TTL and time-stamp for + a whole domain when it is negatively cached. + +2011-05-06 Paul Rombouts + + * src/cache.c,src/cache.h,src/dns_query.c + At the request of Andrei Caraman, the TTL of a negatively cached domain + is now adjusted in accordance with the min_ttl and max_ttl options, just + as it is done for (negatively) cached records. + Additional change to the TTL policy is that for negative records (and + negative domains) the neg_ttl setting overrides min_ttl if + neg_ttl < min_ttl. + +2011-04-26 Paul Rombouts + + * src/conf-parser.c + Fixed memory leak that can occur when the configuration file is reloaded + and an error is encountered while parsing the definition of a TXT + record. + +2011-03-21 Paul Rombouts + + * src/make_rr_types_h.pl,src/cache.h,src/cache.c,src/dns_answer.c + Introduced arrays rrmuiterlist and rrcachiterlist to make iterating + over all possible RR types in a cache entry in strict ascending order + a little more efficient. + +2011-03-09 Paul Rombouts + + * src/dns_query.c,src/conf-parser.c,src/conf-keywords.h + Implemented a new config option "outgoing_ip", which + makes it possible to bind outgoing connections to + a specific interface. + +2011-02-21 Paul Rombouts + + * src/netdev.c + Fixed UDP socket descriptors leak in the implementation of + is_local_addr() for the FreeBSD platform. Thanks to Ashish Shukla for + reporting this bug. + +2011-02-14 Paul Rombouts + + * src/cache.c + In purge_all_rrsets(), also free the rrext array if it has become empty after + purging all the RR sets. + +2011-02-04 Paul Rombouts + + * src/conff.c,src/conff.h,src/conf-parser.c,src/conf-keywords.h, + src/dns_query.c,src/dns_query.h,src/servers.c + Changed "edns_query" from a "global" option to a "server" + configuration option. + +2011-02-04 Paul Rombouts + + * src/conff.c,src/conff.h,src/dns_query.c,src/dns_query.h,src/servers.c, + src/conf-parser.c + The query uptest sometimes fails because some DNS servers are configured + to ignore empty queries. The new config option "query_test_name" makes + it possible to query for a specific name instead. + +2011-02-01 Paul Rombouts + + * src/dns_query.c + When processing a reply from a remote name server which seems to delegate + to other name servers, check if the names for which NS records have + been supplied have locally defined NS records. If so, the local + records will now override those supplied by the remote server. + +2011-01-31 Paul Rombouts + + * src/conf-parser.c + Added support for defining TXT records in the configuration file. + +2011-01-30 Paul Rombouts + + * src/dns_query.c + Do not cache additional records from a response that is rejected because + it contains IP addresses in the reject list, even when the reply + is processed as a NXDOMAIN reply. + +2011-01-25 Paul Rombouts + + * src/conf-parser.c + Modified the function scan_string() to allow back-slashed escape + sequences in strings. + +2011-01-21 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/conff.h,src/conff.c, + src/conf-parser.c + Added support for EDNS (Extension mechanisms for DNS). + Currently this is only useful for allowing UDP message sizes + to be larger than 512 bytes. + +2011-01-20 Paul Rombouts + + * src/dns_answer.c + To avoid frequent reallocs when composing a DNS reply message, + grow the message buffer in multiples of a certain minimum chunk size. + +2011-01-19 Paul Rombouts + + * src/dns.c,src/dns.h,src/dns_answer.c + Extended debugging info with DNS-message lengths and flags of incoming + messages. + +2011-01-17 Paul Rombouts + + * src/conff.c,src/conff.h,src/conf-parser.c,src/dns_answer.c + Made "ignore_cd" option obsolete. It is now effectively always on. + +2010-12-27 Paul Rombouts + + * src/cache.c,src/cache.h,src/dns_answer.c,src/dns_query.c, + src/make_rr_types.pl,src/rr_types.in,src/rr_types.c + The array of pointers to rr_set_t structs in the dns_cent_t struct + contains mostly null pointers in practice, so is somewhat + inefficient in storage usage. This problem is exacerbated if we add + support for caching more RR-types. To ameliorate to the problem + I have decided to split the array in two, with one part fixed in the + dns_cent_t struct as before, and an extension part that will be + separately allocated, if necessary. If the extension part is used only + for very rarely cached types, in most cases the extension array will not + need to be allocated thus hopefully saving memory overall. + The lookup tables which are necessary to support the new cache entry + structure are cumbersome to write by hand, so I have written a perl + script to do this automatically. As an additional benefit, which RR + types are cache-able is now configurable for each type separately via + rr_types.in. + +2010-03-14 Paul Rombouts + + * src/dns_query.c + Using randomized source ports for outgoing queries in IPv6 mode failed + with the warning "Out of ports in the range 1024-65535, dropping query!", + because the pdnsd tried to bind to the fixed port for incoming queries, + instead of the dynamically chosen port. This is a very old bug, but it + has only become apparent since source port randomization has become the + default. + Thanks to Philip-Andr Fillion, Phil Sutter, Radoslaw Szkodzinski and + others for reporting this bug and sending patches. + +2009-12-25 Paul Rombouts + + * src/status.c,src/status.h,src/pdnsd-ctl/pdnsd-ctl.c + Add a magic number to pdnsd-ctl command codes to guard against + possible incompatibility between the pdnsd-ctl utility and the + pdnsd server. + +2009-10-18 Paul Rombouts + + * src/dns_query.c + Make root-server discovery a little more fault tolerant, i.e. if some + of the root-server names don't resolve don't necessarily reject the + whole result. + +2009-10-17 Paul Rombouts + + * src/servers.c,src/dns_query.c,src/dns_query.h + Implemented automatic root-server discovery, which can now be configured + by setting "root_server=discover". + +2009-06-14 Paul Rombouts + + * src/dns_query.c,src/consts.c,src/consts.h,src/conf-parser.c + Changed the default behaviour of the "neg_rrs_pol" option. The default + used to be to only cache records negatively in case the AA (authoritive + answer) bit in the reply was set. The new default is to also allow + negative caching in case the reply has the RA (recursion available) bit + set and the query had the RD (recursion desired) bit set. + This gives the behaviour that is usually wanted in case "proxy_only=on" + is set without having to set "neg_rrs_pol=on", which can be more + problematic. The new default can be explicitly set using + "neg_rrs_pol=default". The values "on","off" and "auth" are also + still available. + +2009-06-13 Paul Rombouts + + * src/conff.c,src/conff.h,src/dns_answer.c,src/conf-parser.c,src/conf-keywords.h + Included a patch contributed by Andreas Steinmetz that implements a new + global configuration option "ignore_cd". pdnsd used to check that the CD + bit in the DNS header of queries is zero and return the error code + "format error" if it is not. However, considering the meaning of this + bit today it appears to be harmless to ignore it, so the new "ignore_cd" + is on by default. Setting "ignore_cd=off" gives the earlier strict + behavior. + Also renamed the the Z1, AU, Z2 bits to correspond with their modern names + CD, AD, Z. + +2008-12-19 Paul Rombouts + + * pdnsd-1.2.7/src/dns_query.c + If pdnsd receives a SERVFAIL response with a non-empty answer section, + use the information tentatively if no better response is available. + The previous behaviour was to discard the reply completely, which could + cause failure to resolve some names. + Thanks to Rafal Wijata for providing an example involving PowerDNS servers + replying with CNAME records. + +2008-09-01 Paul Rombouts + + * src/dns_query.c + In p_dns_resolve(), try to reduce the burden on root servers further for + names ending in "arpa". + +2008-08-31 Paul Rombouts + + * src/dns_query.c + In p_exec_query(), if the reply from a remote name server is negative + (either because the rcode is NXDOMAIN or because the answer section + contains no records for the queried name), ignore the remaining records + in the answer section (in particular do not add them to the cache). + +2008-07-29 Paul Rombouts + + * src/conff.c,src/dns_query.c + Made the default of the configuration option query_port_start equal to + 1024. Also improved the algorithm used by pdnsd to select random source + ports to ensure that each (free) port gets an equal chance of being + selected. This should guarantee random source ports in the range + 1024-65535, making pdnsd less vulnerable to some of the issues described + in CERT VU#800113. + The old situation, where pdnsd lets the kernel select the source ports, + is still available by specifying query_port_start=none. + +2008-07-25 Paul Rombouts + + * src/dns_query.c + Fixed a dangling pointer bug in p_exec_query(), which could cause pdnsd + to crash when processing a long reply with many entries in the answer + section. + +2008-05-12 Paul Rombouts + + * src/conf-parser.c,src/conff.c + Added a recursive-depth counter to the read_config_file() and + confparse() functions to prevent the possibility of infinite + recursion when processing include files. + In confparse(), warn when in a server section the root_server option is + set in combination with policy=simple_only or policy=fqdn_only. + +2008-05-10 Paul Rombouts + + * src/ipvers.h + Included a patch contributed by Georg Schwarz which selectively undoes + a Debian patch contributed by Juliusz Chroboczek on platforms for which + the IPV6_RECVPKTINFO macro is not defined (e.g. MacOS X). + +2008-05-08 Paul Rombouts + + * src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + The pdnsd-ctl add command can now also be used to define NS records. + A wildcard record defined with this command now behaves the same way as + one defined in the config file. + +2008-05-07 Paul Rombouts + + * src/conf-parser.c,src/conf-keywords.h,src/conff.c + Added the ability to process "include" sections in the configuration + file. This makes it possible to place local definitions in separate + files and include them from the main configuration file. + +2008-05-05 Paul Rombouts + + * src/conff.c,src/conf-parser.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + Implemented two new pdnsd-ctl commands, which make it easier to add + definitions to the pdnsd cache at run time. "pdnsd-ctl include" is + similar to "pdnsd-ctl config" but only processes configuration sections + that effect the cache and disallows global and server sections. + "pdnsd-ctl eval" directly parses its string arguments as if they were + part of a configuration (include) file. + +2007-09-15 Paul Rombouts + + * src/dns.h,src/dns_answer.c,src/dns_query.c + Changed the declarations of various packed structs, by moving the + __attribute__((packed)) specifiers from the field level to the struct level. + This was necessary to get the correct value for sizeof(rr_hdr_t) when + compiling with gcc for the ARM architecture. + Thanks to Dirk Armbrust for reporting the problem and supplying the solution. + +2007-08-10 Paul Rombouts + + * src/dns_answer.c + Applied a Debian patch contributed by Juliusz Chroboczek which + reportedly fixes a problem with pdnsd running in IPv6 mode + (IPV6_RECVPKTINFO instead of IPV6_PKTINFO). + +2007-08-04 Paul Rombouts + + * src/dns_query.c + When resolving a name recursively, pdnsd would stop querying further + name servers as soon as it received a reply with the authority (aa) flag + set. Unfortunately, it appears this flag is sometimes raised erroneously + in replies. I have implemented a work-around that ignores the aa flag + when there appears to be a clear delegation to a sub-domain. + Thanks to Nico Erfurth for reporting this problem. + + It appears that pdnsd would also fail to consult servers in the authority + section when configured with neg_rrs_pol=on. This has been fixed. + +2007-08-01 Paul Rombouts + + * src/pdnsd-ctl/pdnsd-ctl.c + Made the matching of pdnsd-ctl command names and most of the arguments + case-insensitive. + +2007-07-22 Paul Rombouts + + * src/dns_answer.c + Instead of sharing the responsibility for freeing the answer buffer in + case of an error amongst different functions, only free it in + compose_answer(). + + * configure.in, src/Makefile.am, src/test/Makefile.am + Merged patch contributed by Pierre Habouzit to deal with CFLAGS the + automake way (allowing packagers to override CFLAGS properly). + +2007-07-21 Paul Rombouts + + * src/dns_answer.c + For each target name in a SRV record in the answer section, add + addresses to the additional section of the response, as is recommended + by the RFCs. + +2007-07-14 Paul Rombouts + + * src/list.c,src/list.h + Made modifications to the implementation of dynamic arrays, which + should ensure proper alignment on all supported architectures. + +2007-07-10 Paul Rombouts + + * Upgraded pdnsd's license to GPL version 3. + +2007-07-08 Paul Rombouts + + * src/cache.h,src/dns_query.c + The data field of the rr_bucket_t struct is now aligned such that + it possible to use straightforward assignment to copy IP addresses, + making memcpy unnecessary for this purpose. + +2007-07-07 Paul Rombouts + + * src/dns_query.c + If pdnsd fails to connect to a name server using a IPv6 address, it will + now retry the connection using a IPv4 address, if available. This allows + pdnsd to recover from situations where IPv6 connectivity is temporarily + unavailable, but IPv4 connectivity still functions. + Thanks to Andreas Ferber for reporting this problem. + +2007-07-04 Paul Rombouts + + * src/dns_answer.c + I have reordered the arguments of the add_rr() and related + functions to make them more consistent with each other. + +2007-07-03 Paul Rombouts + + * src/cache.c,src/hash.c + pdnsd will no longer immediately abort in add_dns_hash() if it fails + to allocate memory for a new hash entry. + +2007-07-01 Paul Rombouts + + * src/conff.c,src/conff.h,src/consts.c,src/consts.h, + src/conf-parser.c,src/conf-keywords.h,src/dns_query.c + Implemented the new "reject", "reject_policy" and "reject_recursively" + options for the server section of the configuration file. + + * src/ipvers.h,src/conf-parser.c,src/dns.c,src/status.c, + src/pdnsd-ctl/pdnsd-ctl.c + Allow local AAAA records to be defined even if pdnsd is compiled + without --enable-ipv6, provided there is sufficient support in the + C libraries and --disable-new-rrs was not used. + +2007-06-30 Paul Rombouts + + * src/dns_answer.c + Previously, when the answer buffer was realloced in add_rr(), an + extra 2 bytes used to be reserved, which are unnecessary, as far + as I can tell. I have decided to do without these extra 2 bytes, + which originate from Thomas Moestl's code. As compensation, I have + added extra PDNSD_ASSERT() statements to check that the answer + buffer does not overflow. + +2007-06-27 Paul Rombouts + + * src/status.c, src/pdnsd-ctl/pdnsd-ctl.c + Extended the pdnsd-ctl 'add a' and 'add aaaa' commands to allow + multiple IP addresses to be specified. + +2007-06-25 Paul Rombouts + + * src/conff.c,src/conff.h,src/conf-parser.c,src/conf-keywords.h, + src/dns_query.c + Implemented a new option for the server section of the configuration + file: randomize_servers. + + * src/servers.c + Improved the debug messages in uptest(). + +2007-01-30 Paul Rombouts + + * src/icmp.c + Fixed up the code implementing the ping test in icmp.c, + which was broken for 64-bit systems. + Thanks to Michael Uleysky for reporting this bug. + +2007-01-09 Paul Rombouts + + * src/dns_query.c + auth_ok() now returns 1 if the cache entry has the DF_NEGATIVE flag set, + without providing a list of authoritative servers to continue querying. + Otherwise if we receive a non-authoritative NXDOMAIN reply and pdnsd + is configured with neg_domain_pol=on, pdnsd will continue to try to + get an authoritative answer. The intention is that pdnsd + stops querying as soon as it gets an "unknown domain" answer. + +2006-04-29 Paul Rombouts + + * src/main.c + pdnsd would segfault if it tried to call log_message() (via the + log_warn() and log_error() macros) before the FILE pointer to the debug + output stream was properly initialized. + Thanks to Thomas Cort for discovering this problem and suggesting a fix. + +2006-04-09 Paul Rombouts + + * src/conf-parser.c,src/helpers.c,src/conff.h,src/conff.c + I have included a patch contributed by Jan-Marek Glogowski, that + implements the configuration option "use_nss". With use_nss=off pdnsd + will avoid system functions that may use NSS (i.e. initgroups()), which + may need DNS for LDAP lookups, which can lead to long timeouts and + stalls if pdnsd itself is used for the DNS lookup. + +2006-03-26 Paul Rombouts + + * src/dns_query.c + Negative caching of RR sets is now also supported with lean_query=off. + +2006-03-25 Paul Rombouts + + * src/dns_query.c,src/conf-parser.c,src/main.c + I have implemented a new query method: udp_tcp. With this method a UDP + query is tried first and, if the UDP answer is truncated, the query is + repeated using TCP. This is the behaviour that seems to be recommended + by the DNS standards. However, pdnsd wil not discard the truncated + answer if the TCP requery fails. + +2006-03-24 Paul Rombouts + + * src/dns_answer.c + Previously, pdnsd would add at most one additional A (and AAA) record + for each record in the answer and authority sections. At the request of + Angel Marin, pdnsd will now add all A and AAA records it can find in the + cache for each name that produces additional records. + +2006-01-02 Paul Rombouts + + * src/dns_answer.c + compose_answer() would leak memory if the query contained + an unsupported QTYPE or QCLASS. This has now been fixed. + +2005-12-27 Paul Rombouts + + * configure.in + TCP-query support is now compiled in by default. + It can still be disabled using the configure option + --disable-tcp-queries. + +2005-12-23 Paul Rombouts + + * src/dns_answer.c + Queries received from clients with non-empty answer, authority or + additional sections are now treated as malformed and rejected with + rcode 1 (format error). + +2005-11-06 Paul Rombouts + + * src/conf-parser.c + Time intervals in the configuration files can now be expressed in + seconds, minutes, hours, days and weeks, using the suffixes + s,m,h,d,and w. + +2005-10-14 Paul Rombouts + + * src/consts.c + In the pdnsd configuration file, true/false and yes/no are now accepted + as synonyms for the constants on/off. + +2005-08-24 Paul Rombouts + + * src/helpers.c + I have fixed a potential buffer overflow problem that could occur with + the 'pdnsd-ctl dump' command. + In case of the root domain, the function rhn2str() would write 2 bytes + to the output buffer even if size==1. Theoretically (under pathological + circumstances) this could have allowed the dbuf buffer in the function + dump_cent() to overflow by one byte. + +2005-08-21 Paul Rombouts + + * acconfig.h,src/cache.c,src/conff.c,src/conf-parser.c,src/dns.c, + src/dns_answer.c,src/dns_query.c,src/error.h,src/helpers.c,src/main.c, + status.c + + It appears the newer versions of gcc won't convert a pointer to char + into a pointer to unsigned char and vice versa without complaining. + The changes I have made should get rid of these distracting warning + messages. Unfortunately I had to introduce casts in some cases, + which reduces type safety :-(. + +2005-08-16 Paul Rombouts + + * src/dns.h + Some changes were made to the endianess detection code to + address problems on Mac OS X v10.4 Tiger. + +2005-08-15 Paul Rombouts + + * configure.in + Some changes where made to address the reported problems with the + configure script on Mac OS X v10.4 Tiger. + +2005-08-05 Paul Rombouts + + * src/status.c,src/dns_answer.c + The output of the 'pdnsd-ctl status' command now includes some + statistics on the number of query threads. + +2005-07-29 Paul Rombouts + + * src/main.c + It appears that sigwait() can return EINTR under certain conditions. + This explains the problems reported by Sanjoy Mahajan with strace + and ACPI S3 sleep, which both caused pdnsd to exit prematurely. + The return value of sigwait() is now checked and sigwait() is retried + if the return value is EINTR. + +2005-07-04 Paul Rombouts + + * src/dns_query.c + It appears that some servers that do not support recursive queries + answer with "query refused" instead of "not supported". The + p_exec_query() function now takes that possibility into account. + +2005-07-01 Paul Rombouts + + * src/dns_query.c + In the processing of queries, I will make a distinction between + recoverable errors and non-recoverable ones (typically caused by out of + memory conditions). In the case of non-recoverable errors, no attempt to + query alternative name servers is made. + +2005-06-26 Paul Rombouts + + * src/dns_query.c + In p_recursive_query(), as soon as one of the servers in the q list + replied "no error" or "name error", only this reply was examined and + the other servers in the q list were ignored. Joshua Coombs has brought + to my attention that this strategy sometimes fails when this reply is not + authoritative and doesn't contain any usable references to name servers + in the authority section. + I have modified p_recursive_query() to allow pdnsd to continue querying + the remaining servers in the q list as long as we haven't received an + authoritative answer or usable authority information. This will allow + pdnsd to arrive at the correct answer in some cases where it would + formerly fail. + +2005-06-25 Paul Rombouts + + * src/status.c + The "pdnsd dump" command may now also be given an argument + consisting of a name beginning with a dot. This will dump information + about all names in the cache ending in the given name. An argument + consisting of a name without a leading dot will only give information + about the exact name, as it did before. + +2005-06-24 Paul Rombouts + + * src/servers.c,src/status.c + All uptests are now conducted by the server status thread. If a retest + is requested via a "pdnsd-ctl server", an existing server status thread + is signaled or a new server status thread is spawned if the old one has + exited. This has the effect that a "pdnsd-ctl server label retest" + command will now return immediately without waiting for the tests to + finish. + +2005-06-20 Paul Rombouts + + * src/conf-parser.c,src/servers.c,src/servers.h + At the request of Al-Junaid Walker I have added a new configuration + option for the uptest interval. With "interval=ontimeout" the server is + not tested at startup/reconfig, or at regular intervals, but only after + a DNS query to a server times out. However, once a server is declared + dead it is never considered again unless it is revived using a + "pdnsd-ctl config" or "pdnsd-ctl server" command. + +2005-06-19 Paul Rombouts + + * src/servers.c,src/dns_query.c,src/icmp.c + During an uptest the server configuration data is locked. Especially + with ping or query uptests of unresponsive servers this means that the + execution of "pdnsd-ctl config" or "pdnsd-ctl server" commands can be + delayed for a long time (or even time out). I have made modifications + that allow a "pdnsd-ctl config" or "pdnsd-ctl server" commands to + interrupt pending uptests to allow these commands to proceed without + delay in most cases. + + * src/thread.h + Use the POSIX sigaction() instead of signal() to install signal handlers. + +2005-06-08 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c + I have defined a struct dns_msg_t that includes a message length field. + In the case of sending a DNS message over TCP, we no longer need a + separate write() call to send the message length. This prevents possible + packet fragmentation. + +2005-06-07 Paul Rombouts + + * src/dns_query.c + The query_method=tcp_udp option only used to work with cooperative name + servers, i.e. servers that either send back a TCP reply or explicitly + refuse the TCP connection request. This wasn't sufficiently satisfactory + in practice, because some name servers are completely unresponsive to TCP + connection requests. I have made modifications to allow pdnsd to try UDP + queries in case TCP connections time out. When a short server timeout is + combined with a global timeout that is at least twice as long, this may + allow a query to a name server that only responds to UDP queries to + succeed with query_method=tcp_udp. + +2005-04-20 Paul Rombouts + + * src/cache.c,src/hash.c,src/conff.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + The "pdnsd-ctl empty-cache" command now accepts additional arguments; + these are interpreted as include/exclude names. During execution of the + command the name of each cache entry is matched against the names in the + include/exclude list. If the name ends in a name to be included, the + cache entry is deleted, otherwise not. + This feature was added at the request of Joshua Coombs. + +2005-04-19 Paul Rombouts + + * src/cache.c, src/hash.c + pdnsd will now (temporarily) unlock the cache between emptying hash + buckets, this should allow pdnsd to remain responsive while executing + the "pdnsd-ctl empty-cache" command. However, this only applies to DNS + queries; pdnsd will not accept any new pdnsd-ctl commands while a + pdnsd-ctl command is still running. + +2005-03-29 Paul Rombouts + + * configure.in, src/hash.h + I have added a new configure option --with-hash-buckets=... + This makes it possible to specify a different number of + hash buckets without editing the source files. + +2005-03-17 Paul Rombouts + + * src/error.c + When running in both daemon and debug mode, print warning and + error messages to debug file as well as the syslog. + +2005-03-15 Paul Rombouts + + * src/dns_answer.c + Only call pthread_setspecific() in debug mode, because + pthread_getspecific() is also only used in debug mode. + If pthread_setspecific() fails, treat this as a non-fatal error. + +2005-03-10 Paul Rombouts + + * configure.in + On Linux systems the configure script will now try to detect automatically + whether the system implements the Native POSIX Thread Library, but + the method is not necessarily foolproof. + + * src/dns.c + Local PTR records generated for resolving numeric IPv6 addresses back into + names, are now based on ip6.arpa instead of ip6.int, because the latter domain + will be phased out eventually. + +2005-03-06 Paul Rombouts + + * Makefile.am,src/cache.c + Create an empty cache-file at install time and don't complain about empty + cache files at start up. + +2005-02-20 Paul Rombouts + + * acconfig.h,configure.in,src/conf-parser.c,src/conff.h,src/dns.h, + src/dns_answer.c,src/dns_query.c,src/error.h,src/helpers.h,src/icmp.c, + src/ipvers.h + + I have applied some changes to the code proposed by Rodney Brown to improve + portability. In particular, pdnsd should now compile on the Darwin platform + (Apple Mac OS X). + To support some of these changes, the source package is now built with a + slightly more modern version of autoconf (2.57) and automake (1.6.3). + +2005-01-29 Paul Rombouts + + * src/dns.c,src/dns_answer.c,src/dns_query.c + + I have added some extra debug code to make it easier to discover the + reason that pdnsd considers a query or reply malformed (format error). + +2005-01-12 Paul Rombouts + + * src/dns.c,src/dns_answer.c,src/dns_query.c + + I have extended some debug code contributed by Kiyo Kelvin Lee to dump + the data received by pdnsd in debug mode (queries from clients, replies + from name servers). Because this will give very verbose debug output, + I've arranged it so that this data dump only occurs if pdnsd has been + configured and compiled with --with-debug=9 and pdnsd has been called + with -v9. + + Additionally, in the case that pdnsd rejects a reply from a name server + because it is not well formed, I have refined the debug messages to + distinguish between format errors due to unexpected truncation and + others kinds of format errors. + +2004-10-30 Paul Rombouts + + * src/rr_types.c + I have included some changes proposed by Joseph Pecquet to address + the compilation problems reported by FreeBSD users. + +2004-10-18 Paul Rombouts + + * acconfig.h,configure.in,src/helpers.c,src/helpers.h,src/dns.h + I have merged a patch for CYGWIN support by Kiyo Kelvin Lee into + my version of the code. + +2004-10-15 Paul Rombouts + + * src/cache.c + Invalidating local records with the pdnsd-ctl did not work the way the + documentation described. An invalidated local record would be always be + purged at the next lookup, thus invalidation would practically have the + same effect as deletion. An invalidated local record is of no use at all and + would occupy space until it is purged during a lookup (but not by purge_cache). + The function invalidate_record() now behaves as the documentation describes, i.e. + invalidation of local records has no effect. + +2004-09-27 Paul Rombouts + + * doc/pdnsd.conf.5.in + A new man page describing the format of the pdnsd config file has been + added to the pdnsd package. I've used a customized Perl script to generate + one automatically from the html documentation. + +2004-09-14 Paul Rombouts + + * src/hash.c + The cache entries in a hash chain are now stored in order of increasing long hash + value. The advantage is that if an name is looked up that is not present in the + cache, this can be done by comparing with only half (on average) of the number + of entries in the hash chain. Not a huge speed up, but still worth while, I think. + Additionally, the number of hash computations for each add_cache() call has + been halved. + +2004-09-11 Paul Rombouts + + * src/cache.c + insert_rrl() will no longer add local records to the rr_l list, because + purge_cache() ignores them anyway. + +2004-09-08 Paul Rombouts + + * src/dns.h,src/cache.c,src/dns_query.c,src/dns_answer.c,src/conf-parser.c + I've started using GETINT16,GETINT32,PUTINT16,PUTINT32 macros, which are based + on the NS_GET/NS_PUT macros that can be found in the BIND source, instead of memcpy + for fetching and storing non-aligned integer data. + +2004-09-08 Paul Rombouts + + * src/cache.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + New pdnsd-ctl command: "pdnsd-ctl dump" will print information about all the + entries contained in the cache. + "pdnsd-ctl dump " will only print entries belonging to . + The data fields of the more common rr-types will be printed in human readable + form, the remaining ones in a hexadecimal representation. + With thanks to Dan Jacobson for suggesting this feature. + +2004-08-31 Paul Rombouts + + * src/conf-parser.c + At the suggestion of Dan Tihelka, I have expanded to the server_ip= option + to allow the name of an interface to be specified instead of an IP address. + pdnsd will not bind to the interface name, but will lookup the address the + interface has at start up, and listen on that address. If the address + of the interface changes while pdnsd is running, pdnsd will not notice that. + +2004-08-30 Paul Rombouts + + * src/cache.h,src/cache.c + I've reversed the meaning of the CF_NOAUTH and renamed it CF_AUTH. + I've also added a domain level flag DF_AUTH, which is used to + mark cache entries obtained from authoritave replies in response to + a query of type * (all).. + +2004-08-30 Paul Rombouts + + * src/cache.c + I've changed the format of the cache file. A typical cache entry has empty + sets for most RR types (even more if DNS_NEW_RRS is defined). In the old + format, each empty RR set was represented by a zero byte. + In the new format only non-empty sets are respresented, leading + to a (modest) reduction is size. + +2004-08-28 Paul Rombouts + + * src/conf-parser.c + New option for "rr" sections in the config file: reverse=on/off. + If you want a locally defined name to resolve to a numeric address and vice + versa, you can now achieve this by setting reverse=on before defining the + A record, making it unnecessary to define a seperate PTR record for the reverse + resolving. + +2004-08-20 Paul Rombouts + + * src/cache.h,src/cache.c,src/conf-parser.c,src/dns_query.c + At the request of Daniel Black, I have added support for defining local wildcard records + in pdnsd. The only type supported presently is records beginning with '*.'. + +2004-08-10 Paul Rombouts + + * src/hash.c,src/cache.c,src/dns_query.c,src/dns_answer.c + Sampo Lehtinen has remarked that pdnsd sometimes failed to resolve classless + reversed-delegated IP addresses, and that this has something to do with the fact + that pdnsd did not accept '/' characters in domain names. After reading Sampo's + and Thomas' remarks, and also rfc2317 and some of the rfc's referenced in rfc2317, + I decided pdnsd should place no restrictions at all on the types of characters it + allows in domain names, only on the lengths of the byte sequences. + This led me to make some quite extensive internal changes to pdnsd. Among other + things domain names are now stored in transport format (sequences of bytes preceded + by length bytes) instead of C strings. This is also more efficient because there + is no need any more to convert from one representation to the other, except when + reading the config file, interacting with pdnsd-ctl or running in debug mode. + Conversion between the two representations isn't always possible, though. + For example, domain names in transport format might contain non-printable characters. + These are now printed as escape sequences (three octal digits preceded by a back slash). + Presently there are still restrictions on the characters in the domain names that can + be defined in local records. I doubt this will ever be considered a problem. + +2004-08-02 Paul Rombouts + + * src/dns_query.c + The code for handling NXT records was flawed. A response from a remote server + containing NXT records (even well-formed ones) could cause pdnsd to crash. + The code for handling NAPTR records contained incorrect PDNSD_ASSERT statements, + which could cause pdnsd to abort unnecessarily. + +2004-07-25 Paul A. Rombouts + + * src/list.h,src/list.c,src/dns.c,src/dns_query,src/dns_answer.c + I've noticed that some of the (dynamic) arrays that pdnsd uses are quite sparse. + Instead of using an array structure with elements that are large enough to contain + the largest possible domain name, I've implemented a "list" data structure that + is more compact. The elements of a list can only be accessed sequentially from + beginning to end, but it allows more efficient memory use in case the names are + significantly shorter that the maximum. + +2004-07-22 Paul Rombouts + + * src/conf-parser.c + I've expanded pdnsd's configuration options by adding support in pdnsd for reading + /etc/resolv.conf style files. Instead of specifying IP addresses in a server section, + the option "file=" can be used. + The IP addresses in the lines beginning with "nameserver" will be added to + the list of address for that section, the remaining lines will be ignored. + To avoid the possibility that pdnsd will query itself, local addresses are skipped + (unless pdnsd is configured to listen on a different port number). + +2004-07-21 Paul Rombouts + + * src/cache.h,src/cache.c,src/dns_query.c,src/conf-parser.c + New option for "server" sections in the config file: root_server=on/off. + In case a server section contains only addresses of root servers, which + usually only give the nameservers of top level domains in their reply, + setting root_server=on will enable certain optimizations. This involves using + cached information to reduce queries to the root servers, thus speeding up + the resolving of new names. This option is also necessary to make the + delegation_only option work in combination with root servers. + +2004-07-16 Paul Rombouts + + * src/cache.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + New pdnsd-ctl command: "pdnsd-ctl empty-cache" will make pdnsd delete its entire + cache, freeing all entries. This is useful for debugging purposes, or in situations + where you suspect that stale cache entries are causing you problems, but you are not + sure which ones. + +2004-07-11 Paul Rombouts + + * src/cache.c,src/dns_query.c + I've removed the use of the function add_cache_rr_add(), which was used to + add additional RR records to the cache one at a time. I've changed the code + in dns_query.c such that additional (or off-topic) records are first collected + in arrays of dns_cent_t structures, and then added to the cache using add_cache(). + With this approach only one function, viz. add_cache(), is used for adding + new entries to the cache, which I believe leads to a cleaner programming + interface. Added benefit is that query serial numbers are no longer + necessary. + +2004-07-10 Paul Rombouts + + * src/cache.h,src/cache.c,src/dns_query.c,src/dns_answer.c + I've added two new field to the dns_cent_t struct, namely c_ns and c_soa. + These will be used to remember references to NS and SOA records in the authority + sections of replies from remote name servers. + This information can be used by pdnsd to fill in the authority section of its + own reply. + +2004-06-25 Paul Rombouts + + * src/dns_query.c,src/servers.c,src/consts.c + I've added an new server availability test which can be selected with "uptest=query". + This can be useful as an alternative to "uptest=ping" in case the remote server does not + respond to ICMP_ECHO requests at all, which unfortunately is quite common these days. + "uptest=query" causes pdnsd to send an empty query to remote nameservers. Any well-formed + response (apart from SERVFAIL) within the timeout period will be interpreted as a sign that the + server is "up". + In a sense this new availability test can actually be considered more reliable than the + other ones that pdnsd supports. + With thanks to Juliusz Chroboczek for suggesting this feature. + +2004-06-24 Paul Rombouts + + * src/helpers.c + Don't use getpwnam() while we are multi-threaded, because it returns a pointer to + a statically allocated structure. I will use getpwnam_r() instead, which is thread + safe. Unfortunately there seem to be some portability problems with getpwnam_r(). + For those platforms that lack getpwnam_r(), I will keep the old code with getpwnam() + as an alternative. + +2004-06-23 Paul Rombouts + + * src/servers.c + Check that the number of IP addresses in a server section is nonzero before + testing servers for availability. Otherwise pdnsd could crash in debug mode. + +2004-06-21 Paul Rombouts + + * src/conff.c,src/conf-parser.c,src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + New pdnsd-ctl command: "pdnsd-ctl config" will make pdnsd re-load its configuration file. + In most cases (but there are still some exceptions) this is preferable + to restarting pdnsd after making changes to the configuration file. + An important advantage is that there should be no perceptible interruption in the dns service + when using the reload command. + An alternative config file can be specified with "pdnsd-ctl config ". + +2004-05-31 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/dns_query.h + I've made an adjustment to p_recursive_query() and related functions, so that + when pdnsd chases name servers in pursuit of authoritative records, it avoids + all the name servers already queried for the same name in the recursive calling + chain, not just the servers most recently used. + Although the hops counter will already break any possible cycles, this will + allow pdnsd to detect pathological cycles earlier and waste less resources. + + * src/cache.c + In add_cache(), don't add empty entries to the cache. Empty cache entries + waste memory and are more persistent than non-empty ones, because purge_cache() + cannot get rid of them. + +2004-05-30 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/icmp.c,src/netdev.c + I've removed the calls to getprotobyname() and used the constants IPPROTO_TCP + and IPPROTO_UDP instead. First of all, it doesn't seem very efficient to call + a function repeatedly to look up the same well-known protocol numbers. + More importantly, getprotobyname() stores its results in a statically-allocated + structure and thus cannot be considered thread safe. (getprotobyname_r() + is thread safe, but is not portable.) + +2004-05-27 Paul Rombouts + + * src/dns_answer.c + I've noticed that when pdnsd is restarted shortly after it has answered a TCP + query, it is often not able to bind to the TCP socket again, resulting in a + disabled TCP server thread. The solution appears to be to set the SO_REUSEADDR + socket option before binding the socket. This allows you to use the same port even + if it is busy (in the TIME_WAIT state). + I found the code for this in a patch file from an old Debian package. + +2004-05-20 Paul Rombouts + + * src/dns_query.c + Joseph Pecquet has reported that version 1.1.11 does not compile under FreeBSD v4.x + because the macro ENONET is undefined. I've bypassed the problem by surrounding + the case line using this value with conditional preprocessor directives. + +2004-05-08 Paul Rombouts + + * src/rc/Slackware/rc.pdnsd + I've included a Slackware start-up script contributed by Nikola Kotur. + +2004-05-05 Paul Rombouts + + * doc/pdnsd.8 + I'm very grateful to Mahesh T. Pai for contributing a pdnsd man page, + which was still missing up till now. + +2004-04-30 Paul Rombouts + + * src/servers.c,src/dns_query.c + After considering some suggestions made by Juliusz Chroboczek I have made the + following changes: + + - After receiving a reply from a remote server mark the server up and update the + timestamp so that pdnsd doesn't bother testing this server for availability for a + while. + - After detecting an error with an send/recv call that indicates a server is + unavailable, mark a server down so that pdnsd doesn't bother testing this server + for a while. + - After server timeouts, uptests are never performed by a query/answer thread, + because this may delay the sending of an answer to the client. Instead the + timestamp of a server that needs to be tested for availability is set to zero and + a condition signal is sent to alert the server status thread, which will carry out + the test. Unresponsive servers with uptest=ping will not be marked down + immediately any more, but only after the ping test has definitely failed. + + * src/error.c,src/error.h + I've moved most of the code previously contained in the DEBUG_MSG macro to a new + function debug_msg(). + The DEBUG_MSG macro now simply expands to "if(debug_p) debug_msg();". + This should make the executable a little smaller, and be just as fast when + debugging is off. The DEBUG_MSG macro still expands to nothing if pdnsd is built + without debugging support. + +2004-04-28 Paul Rombouts + + * src/dns_query.h,src/dns_query.c + I've tried to simplify the finite state machine used for processing parallel + queries, by merging the "state" and "nstate" variables used by p_exec_query() and + p_query_sm() resp. into one "state" variable. + By introducing an extra field "iolen" to keep track of the number of bytes read + from or written to a socket, I could also reduce the number of states for TCP + queries. The new code has the additional advantage that it can handle situations + that require multiple read() calls to receive a response. + +2004-04-14 Paul Rombouts + + * src/dns_query.c + I've added an extra check comparing the number if poll/select events actually + handled to the return value of poll/select. This should reduce the chance that + pdnsd will get caught in a busy spin due to unknown remaining bugs. An error + message is logged and an error code is returned when this comparison fails. + +2004-04-13 Paul Rombouts + + * src/dns_query.h,src/dns_query.c + I got rid of the event field in the query_stat_t struct. + I think it is redundant, because its value can be quite simply derived from + the nstate field. + +2004-04-12 Paul Rombouts + + * src/dns_query.c + I appears there was flaw in the code for handling a "Not Implemented" response + from a remote server with the RA (recursion available) bit equal to zero. This + could cause pdnsd to get into a busy spin. I traced the flaw back to Thomas + Moestl's code, so it must be in all the versions of pdnsd I know of. In previous + versions of pdnsd the busy spin would eventually time out. Due to some recent + changes the loop would no longer time out, making the bug more noticeable. + With thanks to Nicolas George for reporting the bug. + + I also discovered a closely related flaw that would cause pdnsd to poll() closed + file descriptors. It usually works out OK in practice, but it is definitively not + the correct way to do it. + + Additionally, I discovered some opportunities to save memory, e.g. by replacing + the nsname buffer in the query_stat_t struct by a pointer to an already existing + copy of a name. + +2004-04-10 Paul Rombouts + + * src/cache.c + Nicolas George remarked that he thought it was strange that subdomains of domains + negated with "neg" sections in the config file were not also negated. I thought that + he had a point, and I've implemented a change so that negating example.com will + now also negate www.example.com, xxx.adserver.example.com, etc. + +2004-04-09 Paul Rombouts + + * src/error.c,src/error.h + I noticed that the code for the log_warn() and log_error() functions was almost + identical, even to the point that log_warn() called syslog() with LOG_ERR + priority. I've merged these two functions into one log_message() function. + +2004-04-08 Paul Rombouts + + * src/main.c,src/conf-parser.c + The -4 and -6 command-line options should now work as advertised. + This wasn't entirely trivial. The rule is that options on the command line + override those in the configuration file. The easiest way to implement this is to + process the command-line options after reading the configuration file. But this + doesn't work for the -4 and -6 options, because the run_ipv4 flag determines how + IP addresses in the config file are parsed. I've inserted some extra tests and + warning messages that will hopefully make this setting nearly foolproof. + + I've added two new command-line options, "-a" and "-i ". + With the -a flag pdnsd will try to detect automatically if IPv6 support is + available on a system, and fall back to IPv4 if not. The -a flag can be used + instead of -4 or -6. + + In IPv6 mode, pdnsd will now automatically convert IPv4 addresses to IPv6-mapped + addresses. The -i option can be used to specify a prefix for this mapping. The + default is ::ffff.0.0.0.0 + There is also a corresponding ipv4_6_prefix= option for the config file. + + In IPv4 mode, if IPv6 support is compiled in, pdnsd will now skip IPv6 addresses + in the config file (except for the server_ip and ping_ip options) with a warning + message. This allows you to have mixed sets of IPv4 and IPv6 address in the same + config file, although in IPv4 mode some server sections may become inactive. + + With thanks to Juliusz Chroboczek for suggesting these changes. + +2004-04-07 Paul Rombouts + + * src/cache.c + I've changed some of the cache-flag definitions to make debugging a little simpler. + Unfortunately, this makes the cache files of previous pdnsd versions incompatible + with the new one. I've introduced a cache version identifier to be added at the + beginning of each cache file. This enables pdnsd to recognize and discard + incompatible cache files. + +2004-04-05 Paul Rombouts + + * src/cache.h,src/cache.c + I've changed the way CACHE_LAT (cache latency, normally 120 secs) is used to + determine whether a cache entry has timed out. Instead of simply adding it to the + ttl (time to live), I use CACHE_LAT if the ttl is less then CACHE_LAT, else the + ttl itself, making CACHE_LAT the minimum ammount of time a cache entry stays in + the cache. + +2004-04-02 Paul Rombouts + + * src/dns_query.c + I've introduced a global timeout parameter. This is the minimum period of time + pdnsd will wait after sending the first query to a remote server before giving + up without having received a reply. + The timeout options in the configuration file are now only minimum timeout intervals. + Setting the global timeout option makes it possible to specify quite short timeout + intervals in the server sections. This will have the effect that pdnsd will start + querying additional servers fairly quickly if the first servers are slow to respond + (but will still continue to listen for responses from the first ones). + This may allow pdnsd to get an answer more quickly in certain situations. + + * src/dns_query.c + When receiving a NXDOMAIN (unknown domain) response from a remote name server, + I think it is still useful to process the authority and additional sections, + so that pdnsd can possibly add a SOA record to its own response. + +2004-04-01 Paul Rombouts + + * src/dns_query.c + In p_recursive_query(), I've slightly changed the way pdnsd does parallel + queries. Active queries or not canceled until we have received a useful response + from a remote name server, or all the queries have failed or timed out. + Thus the par_queries parameter is no longer the maximum number of parallel + queries, but rather the increment with which the number of parallel queries is + increased when the previous set has timed out. + In the worst case all the servers in the list of available servers will be queried + simultaneously. We may be wasting more system resources this way, but the advantage + is that we have a greater chance of catching a reply. + After all, if we wait longer anyway, why not for more servers. + +2004-03-31 Paul Rombouts + + * src/dns_answer.c + I've noticed that in compose_answer() that while adding the name in the query + section it was not passed through compress_name(). While it is true that the + first name occurrence cannot be compressed, it is still sensible to process the + query name with compress_name() so that the offset can be stored and provide + additional opportunities for future compressions. + I've tested this with dig and the responses of pdnsd are now usually a little + smaller in size or can hold more information within the 512 byte limit. + +2004-03-30 Paul Rombouts + + * src/cache.c + I've noticed that pdnsd stored rr records (of the same type) in reverse order + in the cache. + Although I don't see anything inherently wrong with that, I think it's neater to + store them in the order they are processed. + +2004-03-29 Paul Rombouts + + * src/cache.c + I've rearranged the order of the arguments of some of the functions in cache.c + to obtain a more consistent calling interface. + + * src/dns_answer.c + I've noticed that pdnsd would only add NS records to an authority section if it could + find such records matching the queried name (or the last CNAME in the answer) exactly. + However, I understand that a server should try to give NS records as close as possible + to the target name in the naming hierarchy. + I also understand that if a domain name is reported as nonexisting, or no record of + the requested type exists, it is customary to provide a SOA record, searching up the + name hierarchy if necessary. + I've tried to implement this in compose_answer(), although with some limitations. + I only look in the cache, I don't search more then three levels up, and stop before + the top level. + +2004-03-28 Paul Rombouts + + * src/cache.c,src/dns_answer.c + There were some issues with add_cache_rr_add(). + + First of all, the way it was used in rr_to_cache() (or rather not used) meant + that if an "off topic" record was added for a name that lacked an entry in the + cache, the rr set would be created with an incorrect serial number (namely zero). + I've rewritten add_cache_rr_add so that it can create new cache entries if necessary. + This simplifies the code in rr_to_cache() and ensures correct serial numbers. + + Secondly, in add_cache_rr_add() the ttl was compared with that of an existing rrset + without adjusting for the min_ttl and max_ttl options. This could lead to all the + previous records being deleted, retaining only the last one. + +2004-03-27 Paul Rombouts + + * src/dns_answer.c + In compose_answer(), if the rd (recursion desired) bit is set in the query + and the response contains a CNAME record (while a different type of record was + requested), always do a recursive query on the CNAME, even if we have already + added a record of the requested type to the response. + Failing to honor the rd bit will cause some resolver libraries to complain, + even if the answer contains a record of the requested type. + + I've slightly changed the calling interfaces of add_to_response() and add_rrset() + to make them more consistent and efficient. + + In add_rrset() I've fixed a memory leak on one of the error paths. + + In add_additional_rr(), the return value of add_rr() was not checked. + If add_rr() fails, it will free *ans, and functions higher up the calling + chain could be referencing freed memory. + + I've fixed a potential referencing of freed memory or double freeing in add_additional_a(). + If a call of add_additional_rr() fails, it will free *ans. + Previously, add_additional_rr() could be called a second time, in which case + the second call would be referencing freed memory or freeing it a second time.. + +2004-03-23 Paul Rombouts + + * configure.in, src/Makefile.in,src/pdnsd-ctl/Makefile.in,src/test/Makefile.in + Frdric L. W. Meunier has reported that configure --srcdir option (for building + in directory separate from the source directory) was broken. + Should be fixed now. + +2004-03-20 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c,src/helpers.c,src/icmp.c,src/main.c,src/netdev.c,src/ipvers.h,src/test/if_up.c,src/test/is_local_addr.c,src/test/tping.c,src/test/random.c,src/conf-parser.c + I've eliminated the global variable run_ipv6 from the code. + Enabling both the IPv4 and IPv6 protocols at the same time is not supported + in pdnsd, so the value of run_ipv6 (if it is defined) is simply !run_ipv4. + + * src/dns.c,src/test/is_local_addr.c,src/test/tping.c + It appears the option to compile pdnsd without IPv4 support (i.e. only IPv6 + support) was broken. Should be fixed now. + +2004-03-19 Paul Rombouts + + * src/cache.c + I've discovered an incorrect use of cache locks in lookup_cache(). + We only read locks in place, it is possible for purge_cent() to delete a cache + entry while another thread is trying to read it at the same time, which could + lead to trouble. I've rewritten purge_cent() so that it can be used to test + whether something needs to be purged without actually deleting anything. + If something needs to be deleted, purge_cent() will be called again with + the proper read/write locks in place, excluding access to the cache for all + other threads. + +2004-03-18 Paul Rombouts + + * src/cache.c + I've added a new function sort_rrl() for sorting the rr_l list using a merge-sort + algorithm. Usually the insertion sort used by insert_rrl() is good enough, because + new entries belong near the end most of the time. Reading entries from disk forms + an exception, though, because the rrsets in the file are completely out of order + w.r.t. timestamps, leading to quadratic time complexity of the insertion sort method. + In that case it should be faster to simply append items at the end of the rr_l list + and sort using a more efficient algorithm afterwords. + pdnsd now seems to start up noticeably faster when reading large cache files. + I've also considered using a more sophisticated data structure than a doubly linked + list, but this will add considerable complexity to the code and use more memory. + +2004-03-13 Paul Rombouts + + * src/dns_answer.c + Changed a declaration in udp_answer_thread() so that the buffer used for passing + control messages on to sendmsg() is exactly the right size, instead of an arbitrary + 512 bytes. + Also initialized the msg_flags of the struct msghdr passed on to sendmsg() to zero, + to keep Valgrind from complaining about uninitialized bytes. + +2004-03-12 Paul Rombouts + + * src/icmp.c + Fixed an incorrect call to select() in ping4(). A file descriptor set for detecting + exceptions was initialized but not passed on to select(). This would lead subsequent + code always to behave as if an IO exception had occurred. + Valgrind seems to indicate that when a poll() call times out and returns 0, + the revents field of the struct pollfd is not necessarily set. + I've changed the code to check that the return value is > 0 before examining the + revents field. + +2004-02-06 Paul Rombouts + + * src/conf-parser.c,src/conf-parser.h,src/conf-keywords.h + I've rewritten the parser for the configuration file in C from scratch. + (f)lex and yacc/bison are no longer needed to build pdnsd. + +2004-01-16 Paul Rombouts + + * src/main.c + Load the cache from disk without locking cache access because pdnsd + is still single-threaded at that point. + +2004-01-15 Paul Rombouts + + * src/cache.c,src/hash.c + Moved the responsibility for freeing the cache entries referred by + the hash buckets from destroy_cache() to free_dns_hash() (which is called + by destroy_cache()). Previously, the cache and hash tables were already + completely destroyed by the time free_dns_hash() was called, and there was + nothing left for free_dns_hash() to free. + +2004-01-14 Paul Rombouts + + * src/hash.c,src/make_hashconvtable.c + The hash conversion table is now generated at build time instead + of at run time when pdnsd is started up. + +2004-01-13 Paul Rombouts + + * src/dns.c + In add_host() fixed incorrect generation of IPV6 type of name for PTR record + due to use of && instead of & as masking operator. + +2004-01-13 Paul Rombouts + + * src/icmp.c, src/dns_answer.c + Use unsigned long instead of int error counters to reduce the danger + of wraparound. + +2004-01-06 Paul Rombouts + + * src/main.c,src/thread.c,src/thread.h,src/server.c,src/status.c,src/dns_answer.c + Initialize a global thread attribute object in main.c and use it to create all the detached + threads, instead of initializing a separate attribute object for each new thread. + +2004-01-06 Paul Rombouts + + * src/dns_answer.c + Check the return value of pthread_create() in udp_server_thread() + and tcp_server_thread() to ensure that a new answer thread has actually + been created and free resources if not. + +2004-01-04 Paul Rombouts + + * src/helpers.c,src/cache.c,src/conff.c,src/status.c + Stop writing to control socket after an error has been detected. + +2004-01-03 Paul Rombouts + + * src/pdnsd-ctl/pdnsd-ctl.c + Tried to make the error messages of pdnsd-ctl more helpful. + The complete usage description is now only printed if the 'help' command + is used. For problems with other commands a much shorter message is generated + specific for that command. + +2004-01-02 Paul Rombouts + + * src/helpers.h + Changed the definition of rhnlen(). For valid data this will make no difference, + but it may change the behaviour of pdnsd in certain error situations. + +2004-01-02 Paul Rombouts + + * src/dns.c + Optimized compress_name() some more. + +2004-01-02 Paul Rombouts + + * src/dns_answer.c + Additional code cleanup in compose_answer(). + +2004-01-01 Paul Rombouts + + * doc/pdnsd-ctl.8 + Updated the pdnsd-ctl man page. + +2003-12-31 Paul Rombouts + + * src/pdnsd-ctl/pdnsd-ctl.c + Cleaned up some code. + +2003-12-31 Paul Rombouts + + * src/status.c,src/conff.h,src/conff.c + Some further code cleanup in status.c. + Labels for server sections are no longer limited to 32 chars, + but can have arbitrary length. The string that is used to specify + new DNS-addresses with the "pdnsd-ctl server" command can now also + have arbitrary length. + +2003-12-30 Paul Rombouts + + * doc/html/doc.html + Added information about CNAME and MX resource records, that were + previously undocumented. + +2003-12-26 Paul Rombouts + + * src/dns_query.c + Removed the function p_dns_resolve_from(). This function was essentially + a call to p_recursive_query() with a dummy nocache argument. + p_recursive_query() can now be called with nocache=NULL instead. + +2003-12-26 Paul Rombouts + + * src/dns_query.c + Using a variable length array instead of an malloced buffer to hold the struct pollfd array + in p_recursive_query(). This has the potential for causing portability problems, but I + think that's unlikely because almost all the major C compilers I work with support variable + length arrays nowadays. + +2003-10-18 Paul Rombouts + + * src/helpers.h,src/helpers.c + Fixed a mistake that caused a compile error when using the --with-random-device + configuration option. + Thanks to Daniel Black for reporting this bug. + +2003-10-02 Paul Rombouts + + * conf-lex.l.in,src/conf-parse.y,src/conff.h,src/conff.c,src/dns_query.c + Made the "delegation_only" feature configurable. + +2003-09-25 Paul Rombouts + + * src/helpers.c,src/helpers.h + Added alternative implementations of strdup, strndup, stpcpy, getline and asprintf + in an effort to make the code more portable. + +2003-09-22 Paul Rombouts + + * src/helpers.c,src/conf-parse.y + Made some changes to the parser of the configuration file so that domain names + missing a dot at the end will be tolerated. + +2003-09-21 Paul Rombouts + + * src/dns_query.c + Implemented a first version of the "delegation-only" feature. + It has been "hard-coded" to work for "com" and "net" zones, + and is not yet configurable. + +2003-09-21 Paul Rombouts + + * src/dns.c + Rewrote domain_match(). Also changed the way it is used. + I believe it has a cleaner semantics now. + +2003-09-21 Paul Rombouts + + * src/dns_query.c + Changed the order of the arguments of p_exec_query() and p_recursive_query() + to make it more consistent with the other functions. + +2003-09-18 Paul Rombouts + + * src/dns_answer.c + Reordered the code in process_query() so that a buffer for an error response is + allocated only when it is actually needed. + +2003-09-17 Paul Rombouts + + * src/cache.c + Added parentheses to correct mistaken operator precedence assumption in cache.c. + !cent->flags&DF_NEGATIVE is parsed as (!(cent->flags))&DF_NEGATIVE but I think + what Thomas Moestl must have intended was !((cent->flags)&DF_NEGATIVE). + +2003-09-12 Paul Rombouts + + * src/dns_query.c + Fixed a mistake which caused the effect of the proxy_only option to be reversed. + Thanks to Andrew M. Bishop for reporting this bug. + +2003-09-11 Paul Rombouts + + * src/helpers.c + Rewrote str2rhn() and rhn2str(). + +2003-09-10 Paul Rombouts + + * src/dns.c + Rewrote read_hosts(), the function that reads /etc/hosts-style input. + I believe the parsing algorithm is more robust now. + +2003-09-09 Paul Rombouts + + * src/status.c,src/pdnsd-ctl/pdnsd-ctl.c + Fixed a bug (my fault) that caused improper passing on of flags for the + pdnsd-ctl source command. + Also reordered some of the code, so that data is validated after all of it + has been read from the control socket. This should prevent a "broken pipe" + error message if data validation fails. + Also fixed the reporting of success or failure of the pdnsd-ctl "neg" command. + +2003-09-08 Paul Rombouts + + * src/list.c + Rewrote da_grow1() and da_resize() so that they automatically allocate an array + if given a NULL argument. This makes the use of da_create() redundant in most cases. + +2003-09-08 Paul Rombouts + + * src/conf-parse.y,src/servers.c + At the suggestion of Greg Norris, I changed the code to allow server sections in the + configuration file that don't specify any IP addresses. Such a section will remain + inactive until one or more IP addresses are assigned with the control utility pdnsd-ctl. + +2003-09-04 Paul Rombouts + + * src/dns_answer.c,src/dns_query.c + Oops: in my zeal to declare variables in the smallest possible scope, I ended up + using a pointer to a struct that was out of scope. My understanding of compilers tells me + it should work out OK in practice, but it is definitely a no-no. + Used a union declared in a larger scope instead (which is ugly in another way, + but equally efficient). + Also removed a section of redundant code in udp_server_thread(). + +2003-09-01 Paul Rombouts + + * src/dns_query.c + Corrected the iteration range of a for loop in p_dns_cached_resolve(), which would + otherwise cause an array to be indexed out of bounds in the function set_flags_ttl(). + +2003-08-31 Paul Rombouts + + * src/dns_answer.c + Added cleanup handlers for freeing the resources passed on to udp_answer_thread() and + tcp_answer_thread(). This should ensure the resources are freed even if the threads get + canceled. + +2003-08-30 Paul Rombouts + + * src/cache.c + Revised large portions of code in src/cache.c, used for adding and deleting entries in + the cache. In particular, I rewrote purge_cache(), which I believe was incorrect. + I wouldn't be surprised if this was the cause of the crashed (defunct) threads that some + people were reporting. + Also fixed some memory leaks. + +2003-08-28 Paul Rombouts + + * src/cache.c + Eliminated the overhead of allocation debugging in the case that ALLOC_DEBUG is not defined. + +2003-08-24 Paul Rombouts + + * src/conf-parse.y + No longer allow certain settings of the query_method option in the configuration file + if pdnsd is not compiled with the necessary support. + Thanks to Nikolaus Rath for reporting the bug. + +2003-08-23 Paul Rombouts + + * src/netdev.c + Fixed a bug in is_local_addr() where the result of fgetc(f) is restricted to type char + before being compared to EOF, which can result in the comparison always being false. + Thanks to Gerhard Tonn for reporting the bug. + +2003-07-28 Paul Rombouts + + * doc/html/index.html,doc/html/doc.html,doc/html/dl.html,doc/pdnsd-ctl.8,contrib/README + Revised the documentation. + +2003-07-21 Paul Rombouts + + * src/main.c,src/status.c,src/icmp.c + Setting stat_pipe=0 after opening or binding the control socket fails. + This should prevent further use of the control socket if a problem with + it has been detected previously. + Also properly initialized the global variable int ping_isocket in src/icmp.c + +2003-07-13 Paul Rombouts + + * src/main.c + Polished the code in main(). + +2003-07-04 Paul Rombouts + + * src/helpers.c,src/dns_answer.c,src/dns_query.c + Eliminated the use of inet_ntoa() in favor of the more modern inet_ntop(). + inet_ntop() makes more sense in threaded code and is also recommended in + the glibc info pages. + +2003-07-03 Paul Rombouts + + * src/dns_query.c + Fixed an allocation size error (not mine) in p_exec_query(). + The erroneous size is almost always larger than necessary, so in practice this bug + just wastes memory. But there is also a possibility that the allocated buffer is too + small, which would mean trouble. + Also fixed two memory leaks on some of the error paths in p_exec_query(). + +2003-06-28 Paul Rombouts + + * acconfig.h,configure.in,src/thread.h + Extended the configuration option --with-thread-lib. + Configuring with --with-thread-lib=linuxthreads2 will cause the alternative + definition of THREAD_SIGINIT suggested by Thomas Moestl to be used. + +2003-06-27 Paul Rombouts + + * src/consts.h,src/consts.c,src/conff.c,src/conf-parse.y,src/dns_answer.c + Added two new configuration options for policies of inclusion/exclusion lists. + The new policies options are "simple_only" and "fqdn_only". + This allows me to control to which name servers pdnsd will direct queries for + simple host names. + I also polished the code a bit in report_conf_stat(), used for reporting the current configuration. + +2003-06-20 Paul Rombouts + + * acconfig.h,configure.in,src/thread.h,src/thread.c + Added a configuration option --with-thread-lib=nptl. + This causes the macro THREAD_SIGINIT to be defined as empty in src/thread.h, + and thread_sig() in src/thread.c is never used. + +2003-06-11 Paul Rombouts + + * src/thread.h + Undid the change to the definition of THREAD_SIGINIT suggested to me by + Thomas Moestl, after receiving a report of a problem with this change + from someone running SuSE 7.0. + +2003-06-06 Paul Rombouts + + * src/dns_query.c: + Discovered that I failed to preserve the semantics of Thomas Moestl's code + when I rewrote a section of code in use_server(). Fixed. + +2003-05-19 Paul Rombouts + + * src/cache.c,src/conf-lex.l.in,src/conf-parse.y,src/conff.h,src/dns_answer.c,src/dns_query.c,src/servers.c: + Merged fixes contained in patch file sent to me by Thomas Moestl with my own version. + Changing the version to 1.1.8b1 as suggested by the patch file. + +2003-02-26 Paul Rombouts + + * pdnsd-1.1.7a-par.diff: + Made one big patch file from all the changes I made up till now. + Wrote a description of the changes in a file README.par + Posted patch file on the web so others can use it. + +2003-02-24 Paul Rombouts + + * src/cache.c + Changed the code that writes the cache to disk. + Data is now written strictly sequentially, eliminating the need for fseek(). + This seems to have successfully solved the problem I had with corrupt cache files. + +2002-05-27 Paul Rombouts + + * ChangeLog: + Started experimenting with the source code. + Made many changes between 2002-05-27 and 2002-07-13. + Too lazy to maintain the ChangeLog. + +2002-01-06 Thomas Moestl + + * version: Call it 1.1.7. + +2002-01-04 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c: + Comment and debug message fixes, more assertions. + +2002-01-03 Thomas Moestl + + * src/dns.c, src/dns_answer.c, src/dns_query.c: + More harmless fixes, correct some comments and debug messages, add more + assertions. + + * NEWS, version: 1.1.7p2, correct NEWS entry. + + * src/helpers.c: + Make sure the calling thread of pdnsd_exit() terminates immediately. + +2002-01-02 Thomas Moestl + + * src/dns_answer.c, src/helpers.c, src/icmp.c: + Fix a few more harmless bugs, more paranoia. + + * src/status.c: Fix yet more, probably harmless, problems. + +2002-01-01 Thomas Moestl + + * src/dns.h, src/dns_answer.c, src/dns_query.c: + Fix a few more possible buffer size problems, and add a bunch of + assertions as last lines of defence. + +2001-12-30 Thomas Moestl + + * src/dns.c: Build fix (include error.h). + + * NEWS, version: Call it 1.1.7p1, and add a NEWS entry. + + * TODO: Reduce TODO to what actually is still needed. + + * src/dns.c, src/error.h, src/helpers.c: + Add a bunch of robustness PDNSD_ASSERT()'s. + + * src/dns_query.c: + Fix a bug which may possibly be remotely exploitable to gain access as + the user pdnsd runs as. + This was caused by a dumb single-character mistake :( + + * doc/Makefile.am, configure.in: + Avoid confusing automake 1.5 by not putting a comment into a make rule. + Fix CONFDIR passing. + + Submitted by: GoTaR + + * src/pdnsd-ctl/pdnsd-ctl.c: + Avoid crashing when the buffer contents received using the status command + are not terminated. + +2001-10-14 Thomas Moestl + + * src/rc/SuSE/pdnsd.in, THANKS: + Fix the stop case for the SuSE rc script: killproc requires the full + path of the binary as argument (reported by Bernhard Pelz). + +2001-09-23 Thomas Moestl + + * configure.in: + Revamp the OS autodetect test. OpenBSD and (hopefully) NetBSD are no longer + unsupported. + + * src/helpers.c, THANKS: + Do not try to use arc4random when compiling for NetBSD (submitted by + Thomas Stromberg). + +2001-09-10 Thomas Moestl + + * COPYING.BSD: s/REGENTS/AUTHOR/ in one place. + + * src/cache.c: + It is possible no record of the requested type is present after calling + cr_add_cent_rr_int() (when the record was marked as being local), so + check before dereferencing the pointer to the respective rrset. + Leave the record unmodified when cr_check_add() returns 0.x + +2001-07-26 Thomas Moestl + + * src/rc/RedHat/pdnsd.in: + Add a workaround for @sysconfdir@ substitutions containing ${prefix}. + Spotted by Robert Linden. + +2001-07-04 Thomas Moestl + + * src/rc/RedHat/pdnsd.in: + Add a condrestart handler to the Red Hat rc script, and do some general + cleanup. Contributed by Christian Engstler. + +2001-07-02 Thomas Moestl + + * src/error.h: + Attempt to detect a gcc that cannot yet handle ANSI variadic macros, + and work around this by using the old GCC-style variant. + + * src/conff.c: + Remove a + at the start of a line that got in when merging a diff by + hand. + + * src/servers.c: waitpid() returns a pid_t. + + * src/dns.c: + It's sizeof, not sizof. This should unbreak the IPv6 build. Also silence + some warnings with appropriate casts. + + * NEWS, version: Call it 1.1.6, and add a NEWS entry. + +2001-07-01 Thomas Moestl + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_query.c, THANKS, version, AUTHORS: + Added a modified version of Andreas Steinmetz's code for + query_port_start and query_port_range, and added him to AUTHORS and + THANKS. + +2001-06-23 Thomas Moestl + + * src/cache.c: + Fix a bogon: deleted would not be reset correctly in the first + purge_cache loop, which could cause pdnsd to loop forever when a + negative record was after a deleted rr. + +2001-06-21 Thomas Moestl + + * src/list.h: + Add (currently unused) list macros that are going to be used in future + code. + + * src/cache.c: + Fix a bogon in the rw lock code: we need to wake up a writer if there + are no readers. The old code was a leftover from a time when + SUSP_THRESH was just r_pend * x. + Fix a typo. + +2001-06-13 Thomas Moestl + + * AUTHORS: Add mention of FreeBSD code to AUTHORS. + + * src/netdev.c: + Add SIZEOF_ADDR_IFREQ (taken from FreeBSD: _SIZEOF_ADDR_IFREQ, net/if.h + rev. 1.58.2.1) and add an appropriate copyright notice. + The reason for this is that other BSDs don't have it, and we are not + supposed to use underscored macros in portable software. + +2001-06-12 Thomas Moestl + + * src/icmp.c: Fix double #inclusion of . + Noticed by Sebastian Stark. + +2001-06-08 Thomas Moestl + + * src/dns_query.c, THANKS: + Allow underscores in the query names reported back, as the comment next + to the decompress_name call already indicated (but the call gave NULL + as the uscore parameter, which disables underscores normally). + Add Michael Strder, who spotted this, to THANKS. + +2001-06-06 Thomas Moestl + + * src/servers.c, THANKS: + Fix a bug discovered by Stefan Erhardt (and add him to THANKS): the + return value of waitpid was misinterpreted. + +2001-06-04 Thomas Moestl + + * Makefile.am, file-list.base.in, version: + Bump version to 1.1.6p1; wire up COPYING.BSD so that it gets included + in RPM's and tarballs. + + * COPYING.BSD: + Add the BSD-Style copyright notice so that it can be included in binary + distributions. + +2001-06-03 Thomas Moestl + + * src/dns.c, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/status.c, NEWS, version: + Bump version to 1.1.5, and add a NEWS entry for this release. + + Miscellaneous cleanups, mainly in the status.c code; fix a bug that + could cause heap corruption (rhncpy always clobbered the whole buffer, + but only the needed space was reserved in add_rr). This should solve + the crashes some people were seeing (this bug is not an exploitable + security hole as far as I know; the respective buffer is on the heap, + as mentioned). + + * src/error.c: + Paranoia: do not use the argument to crash_msg as a format string + (crash_msg is only used with constant strings, though). + +2001-06-02 Thomas Moestl + + * src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/error.h, src/hash.c, src/helpers.c: + Correct underscore handling for SRV records, and a few comment fixes. + + * src/cache.c, src/conff.c, src/dns_query.c, src/error.h, src/helpers.h, src/status.c: + Numerous non-critical argument fixes for printf-like functions. + + * src/dns.c: Remove superfluous \n's. + + * src/conf-parse.y, src/dns_answer.c, src/status.c: + Correct some DEBUG_MSG nits, and fix two format string bugs. One of + them could allow users that are allowed to use pdnsd-ctl with the + server (when the status socket is enabled) to gain the privileges of + the user that runs (the run_as user or the user that started pdnsd on + Linux when strict_setuid is set to off) pdnsd. The status socket is + disabled by default, and if it is enabled, it's default permissions + are quite restrictive, so this isn't a problem for most. + +2001-05-30 Thomas Moestl + + * src/status.c: + Make the status permissions actually work (missed last time). + + * src/dns_answer.c, src/main.c, src/status.c, src/status.h: + Move the status socket initialization to a place where it gets executed + before any threads are started; this way, we can use umask to set the + permissions, and avoid a (in this case harmless, but anyway) race + condition. + While being there, remove obsoleted comments and places referring to + the now-socket as fifo. + +2001-05-29 Thomas Moestl + + * src/cache.c: + Replace a misuse of CF_LOCAL with DF_LOCAL. This had no effect, because + the values are the same. + +2001-05-22 Thomas Moestl + + * src/hash.c, src/helpers.c, THANKS, acconfig.h, configure.in: + Add an option for allowing underscores (_) in domain names. This + violates the RFC's if enabled (which it isn't by default). + Thanks to Eelco Vriezekolk for an initial patch. + + While being there, clean up configure.in and acconfig.c a bit. + + * src/helpers.c, src/status.c: + Add a few comments about security implications. + + * src/cache.c, src/dns_answer.c, src/dns_query.c, src/helpers.c: + Change some occurences of strcpy to strncpy. Again, no risk here, the + buffer lentgh was carefully chosen, and while the data was partially of + remote origin, it was carefully validated before entering the cache (and + thus having a chance of being used by us). + 3 occurences remain: 2 in cache.c, where we allocate a sufficient amount + of memory before (mimicking the non-portable strdup) and one where + we copy a constant and which is obviously correct. + + * src/dns.c: + Change two occurences of strcat to strncat. Again, no risk here, the + buffer lentgh was carefully chosen, the data was validated and supplied + by the starting user. + + * src/dns.c: + Change a sprintf to a snprintf and enlarge a buffer a bit. This is pure + paranoia (alrhough makes code review easier for others), because a.) the + lengths were carefully chosen so that no overrun could occur and + b.) this was locally supplied data. + +2001-05-21 Thomas Moestl + + * src/rc/RedHat/Makefile.am: + Add a missing semicolon in the RedHat rc Makefile.am (discovered by + Christian Engstler). + +2001-05-19 Thomas Moestl + + * HACKING: Remove the mostly outdated HACKING file. + + * src/debug.c: + Catch a corner case in the allocation debug helpers: realloc() with a + size of 0 is effectively a free operation. To my knowledge, this is + not done in the pdnsd sources, however. + + * src/test/test.sh: + Use the correct error function, forgotten in last commit. + + * src/test/clnt-test.sh, src/test/srv-test.sh, src/test/test.sh: + Misc small improvements in the regression test scripts, mostly + adding configuration variables and common error handlers. + Comment a little on the tests that are done in clnt-test.sh + + * src/rr_types.c: Fix a typo in a comment. + + * src/cache.c, src/debug.c, src/dns_answer.c, src/dns_query.c, src/error.h, src/hash.c, src/list.c, src/main.c, src/status.c, src/thread.c, src/thread.h: + New ANSI variadic debug macros (finally), which print a timestamp and + a thread ID now for easier debugging with many parallel queries. + It should be considered to make those inline functions instead. However, + we have the advantage that we use printf in place here and benefit + from parameter checking without specifying obscure function attributes. + + * src/rc/SuSE/.cvsignore, src/test/.cvsignore, src/rc/.cvsignore, src/rc/Debian/.cvsignore, src/rc/RedHat/.cvsignore, contrib/.cvsignore, doc/.cvsignore, src/.cvsignore, src/pdnsd-ctl/.cvsignore, .cvsignore: + Brush up the rotten (pre-autoconf!) .cvsignore files and add some where + necessary. + +2001-05-17 Thomas Moestl + + * src/rc/RedHat/Makefile.am, src/rc/RedHat/pdnsd.in, configure.in, pdnsd.spec.in: + Red Hat rc script and RPM improvements by Christian Engstler. + +2001-05-12 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c: + Fix a place missed when converting rr_info. + + * version: It's 1.1.4, finally. + + * src/rr_types.c, version: + Change some class values in the rr type structure to better values. + Bump beta version. + +2001-05-10 Thomas Moestl + + * src/cache.c, src/dns_answer.c, src/dns_query.c: + Fix a signedness bug that could cause erraneous 0 ttls to be returned. + Add some debug messages, and do some minor fixups. + +2001-05-09 Thomas Moestl + + * src/cache.c, src/rr_types.c: + Fix some bugs in the new conflict resolution code and make it more + complete. + + * src/cache.c, version: + Add conflict resolution code. This needs a bit more checking, and + the tables might still need to be tweaked. + Bump version. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/Makefile.am, src/cache.c, src/cache.h, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.c, src/consts.h, src/debug.c, src/dns.c, src/dns_answer.c, src/dns_answer.h, src/dns_query.c, src/dns_query.h, src/error.c, src/error.h, src/hash.c, src/hash.h, src/helpers.c, src/helpers.h, src/icmp.c, src/icmp.h, src/list.c, src/main.c, src/netdev.c, src/netdev.h, src/rr_types.c, src/rr_types.h, src/servers.c, src/servers.h, src/status.c, src/status.h, src/thread.c, src/thread.h, configure.in, version: + Remove the old infrastructure that theoretically could have allowed for + multiple cache subsystems. This ability was never used, and if it should, + the caching should probably be split into two layers, a higher level + common one and the actual caching backends. + src/cacheing/cache.c and src/cacheing/native/*.[ch] were repo-copied to + src/. + Substitute "conf.h" with for includes. + Purge records a little more often (when adding records, and when + retrieving from the cache). Handle cache_size properly when using + purge_cent. + Introduce some infrastructure in rr_types.[ch] for a record conflict + checker which is to be introduced shortly to enforce cache consistency + even in the purge_cache=off case. + +2001-05-04 Thomas Moestl + + * src/rc/RedHat/pdnsd.in, src/rc/SuSE/pdnsd.in, src/rc/Debian/pdnsd.in: + Revert the last commit. It breaks the rc scripts by spamming them with + make style variable expansions. + + * src/rc/SuSE/pdnsd.in, src/rc/RedHat/pdnsd.in, src/rc/Debian/pdnsd.in, AUTHORS, THANKS: + Fix a rc script bug spotted by Frank Elsner, and add him to AUTHORS and + THANKS. + +2001-05-01 Thomas Moestl + + * version: Bump version to 1.1.4p2. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/status.c: + Fix some bogons and remove some unneeded code in the pdnsd-ctl + interface. + Fix spelling and line length bugs. + +2001-04-30 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/conf-parse.y, src/status.c: + Some corrections for the authrec config file and the pdnsd-ctl noauth + support. + + * src/pdnsd-ctl/pdnsd-ctl.c: + Fix wrong argv index (using getopt changed the indices). + + * src/pdnsd-ctl/pdnsd-ctl.c, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, src/dns.h, src/dns_query.c, src/status.c, THANKS, version, AUTHORS: + Accumulated changes that should go in before 1.1.4: + - merge Andrew M. Bishop's patch that adds a server label option + - make local records authoritative for the domain by default, and add + the authrec option to change this + - add the auth keyword to the pdnsd-ctl source option to support that + - fix a bug in the conf-parse.y grammar causing a shift/reduce conflict + - sync up AUTHORS and THANKS: add Andrew M. Bishop, Kevin A. Burton and + Michael Steinl + - bump version to 1.1.4p1 + + * src/conff.c, src/main.c: + Fix two small bugs: the wrong element of argv was used for the pidfile + option, which could cause pdnsd to segfault, and C_INCLUDED was always + used in slist_add, regardless of the tp parameter. + + * src/helpers.c: + Fix a bogon discovered by Michael Steiner: the fread() return value + was tested against bytes, not the number of items. + + * src/hash.c, src/hash.h, src/cache.c: + purge_cache used to walk over the cache quite inefficiently when it was + called from add_cache. Add a lazy mode for purge_cache which uses the + rrset_l to be efficient in this special case. + Add some #ifdef'ed-out-by-default code to debug the hash function. + +2001-04-12 Thomas Moestl + + * NEWS: Add NEWS entry for 1.1.3. + + * src/dns.c, src/helpers.c, src/icmp.c, contrib/Makefile.am, contrib/README, version: + IPv6, ICMP and build fixes. It's 1.1.3 now! + + * src/debug.c, src/debug.h: Add the new debug support files. + + * src/test/clnt-test.sh, src/cache.c, src/cache.h, src/error.h, src/list.c, src/list.h, src/main.c, src/status.c, src/status.h, src/Makefile.am, src/conf-parse.y, src/conff.h, src/dns.c, src/dns_answer.c, src/dns_query.c: + Add allocation debug support. Some small cleanups before the upcoming + 1.1.3 release. + + * src/dns_query.h, src/helpers.c, src/list.c, src/conff.c, src/dns.c, src/dns_answer.c, src/dns_query.c: + Lots of small bugfixes, cleanups, style and spelling fixes. + + * src/test/clnt-test.sh: Fix nc arguments. + + * src/test/clnt-test.sh, src/test/srv-test.sh, src/test/test.sh: + Add regression test scripts. + +2001-04-11 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/servers.c, src/dns_answer.c, src/helpers.c, src/helpers.h, src/icmp.c, src/main.c: + Further cleanups and bug, style and spelling fixes. + + * configure.in: Use -g again in the CFLAGS for a while. + + * version: Beta version bump. + + * src/rc/SuSE/pdnsd.in: + killproc does not seem to take the full path, but only the process name + (which is what one would expect). + + * src/hash.c, src/netdev.c, src/rr_types.c, src/status.c, src/conf-parse.y, src/conff.c, src/helpers.c: + Misc. smaller fixes, and fixes on the new features. Also clean up style + and spelling in some places. + + * src/dns_answer.c: + Bring the glibc pthread_cleanup_push/pthread_cleanup_pop return bug + workaround into the main tree. + Without this, a return between those two macros would cause pdnsd + to crash on system using a glibc between 2.1.2 and 2.2.2 (and possibly + others). This could e.g. be cause by a TCP connect() port scan. + +2001-04-10 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c: + Minor fixes, direct error messages to stderr. + + * src/list.c, src/list.h: Add the new list implementations. + + * src/cache.c, src/cache.h, src/conf-parse.y, src/dns.c, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/helpers.h, src/conf-lex.l.in: + Introduce rhnlen and rhncpy and make use of it instead of kluged-up + strcpy/strlen in the appropriate places. + Check that incoming names contain only legal characters in + decompress_name, return RC_FORMAT otherwise (this would result in + wrong handling only, but not in a security hole). + Reorganzie compose_answer and make it more correct for multiple + questions. Get rid of the algorithm that tries to add a higher + level name server; this might be readded in another place somewhen. + Use some more da_* instead of hand-built lists. + Some style cleanups. + + * src/rc/RedHat/Makefile.am: + Add K45pdnsd links for rc6.d (reboot) and rc0.d (halt) following a + suggestion by Stas Sergeev. + +2001-04-06 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.h, src/error.h, src/helpers.c, src/helpers.h, src/main.c, src/servers.c, src/status.c, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/Makefile.am, version: + Bump alpha version; introduce a generic dynamic array type and make use + of it to ged rid of some ugly casts and redundant code. + Minor fixes. + + * src/icmp.h, src/ipvers.h, src/conff.h, src/consts.h, src/dns.h, src/dns_answer.h, src/dns_query.h, src/helpers.h: + Use macros without an underscore as first character to protect the + headers. Underscores are reserved and should not be used in the + application name space. + + * src/error.h: Add PDNSD_ASSERT, change style a little. + +2001-04-03 Thomas Moestl + + * src/hash.c, src/netdev.c, src/servers.c, src/helpers.c, src/icmp.c, src/main.c: + Another slew of small bugfixes, minor updates and small fixes. + + * src/rr_types.c, src/consts.c: + Update rr_types.c copyright date, consts.c should have a rcsid string. + + * src/rr_types.c: cvs add rr_types.c. + + * src/dns.h, src/helpers.h, src/ipvers.h, src/rr_types.h, src/status.h, src/conf-parse.y, src/conff.h: + cvs add rr_types.h that got missed before, update copyright dates, + remove some old config cruft, some minor fixups. + + * src/conff.c, src/consts.c, src/dns_answer.c, src/error.c, src/conf-lex.l.in, src/conf-parse.y: + Update copyright dates, fix some minor bugs. Update copyright dates. + cvs add missed consts.c. + +2001-03-28 Thomas Moestl + + * src/cache.c, src/hash.c, src/error.c, src/servers.c, src/dns_answer.c, version: + Bump version to 1.1.3p4 + Fix some non-critical locking issues (none of them could be fatal). + Adjust copyright dates. + + * src/hash.c: Make the hash compare case insensitive. + +2001-03-25 Thomas Moestl + + * contrib/Makefile.am: Add Id tag to Makefile.am + + * src/cache.c, src/dns_answer.c, src/icmp.c: + Some more type fixes overlooked in last commit. + + * src/conf-parse.y, src/conff.h, src/dns.h, src/dns_answer.c, src/dns_query.c, src/icmp.c: + More type cleanups. Use time_t for time specifications throughout, and + make lengths singed longs. Cast cleanup in icmp.c to fix alpha + unalinged access faults. + + * contrib/dhcp2pdnsd, contrib/pdnsd_dhcp.pl, contrib/save_ram.pl, contrib/Makefile.am, contrib/README, configure.in, version, Makefile.am: + It's 1.1.3p3 now. + Change the contrib infrastructure: there is a Makfile.am in contrib/ + now. Rename Marko Stolle's pdnsd_update.pl to pdnsd_dhcp.pl and bring + it up to date (adding the rc script and save_ram.pl). + + * src/helpers.c, src/dns.c, src/dns_query.c, AUTHORS, THANKS: + Bring in Bjoern Fischer's changes to make pdnsd conserve the case of + cached names, and add him to AUTHORS and THANKS. + + * configure.in: The gdbm backend is discontinued. + + * src/cache.c, src/hash.c: + Cleanup and small bugfixes of the cache code (esp. locking). + + * AUTHORS, THANKS, file-list.base.in, pdnsd.spec.in: + SuSE fixes by Christian Engstler. + Add him to AUTHORS, THANKS. + +2001-03-14 Thomas Moestl + + * src/dns_query.c: + Fix a bug that could cause servers that were not used in the first + parallel query not to be used at all (failure would be returned + instead). + +2001-03-13 Thomas Moestl + + * src/icmp.c: Add define for ip_p equivalent on Linux. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/dns_query.c, src/icmp.c, src/status.c, src/conf-parse.y, src/dns_answer.c, version: + Bump alpha version, more alignment fixes. All casts should be correct + now. + +2001-03-12 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, AUTHORS, THANKS: + Add the alpha fixes by P.J. Bostley, and add him to THANKS and AUTHORS. + +2001-03-10 Thomas Moestl + + * src/dns.h, src/helpers.h: + Remove prototype for removed function strtolower. + Use unit16_t and uint32_t instead of unsinged short/long for dns + protocol structures. + +2001-02-25 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/status.c, src/conf-lex.l.in, src/conf-parse.y, AUTHORS, Makefile.am, version: + Add MX and CNAME for rr sections in the config file and MX setting + for pdnsd-ctl. + Typo fixes. + + * src/netdev.c: Two more fixes. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/dns.c, src/main.c: + More small robustness fixes. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/netdev.c, src/status.c, src/status.h, src/conf-parse.y, src/helpers.c, src/main.c, configure.in, version, Makefile.am, NEWS: + A batch of robustness fixes. Move the status socket to the cache + directory. Various cleanups. + It's 1.3 now (hopefully to be released soon). + +2001-02-21 Thomas Moestl + + * src/main.c, src/conf-lex.l.in: + Fix breakage of the -mtu option and the query_method option (the parser + would not recognize constants that contained underscores). + +2001-02-20 Thomas Moestl + + * contrib/README, contrib/pdnsd_dhcp.pl, AUTHORS, Makefile.am, THANKS, file-list.base.in: + Add Marko Stolle's pdnsd_update.pl DHCP update script, add him to THANKS, + and bring a contrib/ directory in place. + +2001-02-15 Thomas Moestl + + * src/dns_answer.c, configure.in, version: + Some minor build & misc fixes. Bump version to 1.1.2a and release a + version with the spec file fixes to get proper Red Hat RPM's. + +2001-02-09 Thomas Moestl + + * NEWS: Bring NEWS up to date. + + * src/icmp.c: Do not close the socket on error. + + * pdnsd.spec.in: Add spec file fixes for man pages by Sourav K. Mandal + +2001-02-07 Thomas Moestl + + * version: It is now 1.1.2. + + * src/dns_query.c, src/main.c, Makefile.am, THANKS: + Fix a too strict length checking that could cause SERVFAIL to be returned + when the server returned NXDOMAIN. Add Markus Storm to THANKS (he has + reported this bug and supplied helpful information). + Minor tweaking in main.c. + Remove emptying of GZIP_ENV in Makefile.am (this normally contains --best). + +2001-01-27 Thomas Moestl + + * AUTHORS, THANKS: + Add Michael Wiedmann to AUTHORS and THANKS for his pdnsd-ctl.8 man page. + + * doc/Makefile.am, doc/pdnsd-ctl.8, configure.in, Makefile.am: + Add the pdnsd-ctl man page contributed by Michael Wiedmann. For this to + build in a correct way, add doc/Makefile.am and move all doc and + pdnsd.conf.sample related stuff in there. + +2001-01-25 Thomas Moestl + + * src/main.c: Removed unneeded for the non-O_NOFOLLOW case. + +2001-01-24 Thomas Moestl + + * src/main.c: + Add a fchown and a fchmod to the new non-O_NOFOLLOW case (not yet used). + + * src/conf-parse.y, src/main.c, src/status.c: Misc small fixups. + + * version: It's called 1.1.1 now. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/status.c, src/main.c: + Fix command line parsing. Add code to securely create pid files under + OSs that do not support the O_NOFOLLOW flag (those OSs are not supported + yet, though). + Fix a possible race condition in socket creation/chmod. We now create + a directory in /tmp (or whatever TEMPDIR was set) to hold the socket. + + * src/dns.c, src/dns_answer.c, src/icmp.c, src/main.c, src/status.c: + Another slew of copyright notice upgrades. + + * version, configure.in: + Bump beta revision, fix typo (missing $) in configure.in + + * src/dns.c, src/dns_answer.c, src/status.c: + Silence BSD compile time warnings. + + * configure.in: + Cleanup, add autoconf code for building pdnsd on FreeBSD-CURRENT with the + new additionally-linked libc_r. + +2001-01-16 Thomas Moestl + + * src/dns_answer.c, src/icmp.c, version: + Bump beta revision, fix a comment. Also, generate ping id's using pdnsd's + random wrappers instead of using rand() for paranoia. + +2001-01-15 Thomas Moestl + + * src/helpers.c, configure.in: Improve wording. + + * src/helpers.c: Update copyright year (forgotten in last commit). + + * src/dns_answer.c, src/helpers.c, src/main.c, acconfig.h, configure.in, version: + Bump versions. Small fixes (move socket intitializations from + udp_server_thread to init_udp_sockets to prevent warning when startup + takes long. + Make arc4random an option for a query id RNG and make it the default + on FreeBSD. + +2000-12-07 Thomas Moestl + + * src/conf-parse.y, src/main.c, version: + We are at 1.1.1p1. Removed the exec-uptest security warning printef if no + explicit user is given in the strict_setuid case (it is not needed there, + and confuses users). + +2000-11-28 Thomas Moestl + + * src/cache.c, src/helpers.c: + Converted cache locks to use condition vars and have lock contention + prevention. Added comments where not converted. + +2000-11-25 Thomas Moestl + + * AUTHORS, THANKS, pdnsd.spec.in: + Added spec file patches by Bernd Leibing and added him to AUTHORS and + THANKS. + +2000-11-21 Thomas Moestl + + * src/rc/SuSE/Makefile.am: Fixed a hopefully last SuSE rpm build bug. + + * src/rc/SuSE/Makefile.am: + Another one: allow rc.conf manipulation to fail for a clean + rpm build (SuSE only). + + * file-list.base.in: + Last-minute fix: correct filelist for rpm build to reflect the new name + for the sample configuration. + + * version: It's 1.1.0 now. + +2000-11-18 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/dns_query.c, version: + Fixed a condition where the cache code did not give up a lock. + Made the udp code use connect(). + Some small changes. + +2000-11-16 Thomas Moestl + + * version: Calling it 1.1.0b3. + +2000-11-15 Thomas Moestl + + * src/test/Makefile.am, src/pdnsd-ctl/Makefile.am, src/cache.c, src/Makefile.am, src/dns_answer.c, src/error.h, src/icmp.c, src/icmp.h, src/main.c, src/netdev.c, src/servers.c, src/thread.c, Makefile.am, configure.in, version: + Enabled new rr support by default (some resolvers don't seem to like not + supported answers - not our bug, but well). + Made some globals volatile to avoid being bitten by optimisations. + +2000-11-12 Thomas Moestl + + * TODO, version: Called it the first beta. + +2000-11-11 Thomas Moestl + + * src/cache.c, src/conf-lex.l.in, src/conf-parse.y, doc/pdnsd.conf.in: + renanmed rrneg to neg in the config file. + Misc small fixes. + pdnsd-ctl record xxx inval will now also invalidate local records. + + * src/conf-lex.l.in, src/conf-parse.y, src/dns_answer.c, src/status.c, pdnsd.spec.in: + Added --sysconfdir=/etc as argument to configure in the spec file. + Implemented the new rrneg config file section. + + * src/test/Makefile.am, src/pdnsd-ctl/Makefile.am, src/pdnsd-ctl/pdnsd-ctl.c, src/cache.c, src/cache.h, src/status.c, src/status.h, TODO: + Added the neg option to pdnsd-ctl. + + * src/cache.c, src/Makefile.am, src/conf-lex.l.in, src/consts.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/main.c, configure.in: + Assorted fixes. The new features should be stabilized by now, will + integrate the missing few features now. + Also actived the tcp server by default. + +2000-11-07 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, src/icmp.c, src/ipvers.h, THANKS, TODO: + Fixed a possible memory and socket leak reported by Erich Reitz. + Implemented udp source address discovery for FreeBSD. + + * src/dns_query.c: Part 2 of yesterdays fix. + +2000-11-06 Thomas Moestl + + * src/dns_query.c, version: + Fixed a bug reported by Erich Reitz: pdnsd could leak fd's and memory if + queries timed out. + + * src/cache.c, src/cache.h, src/Makefile.am, src/dns_answer.c, src/dns_query.c, TODO, configure.in: + Sorted out some bugs for the new neg cacheing. + +2000-11-05 Thomas Moestl + + * src/cache.c, src/dns_query.c, TODO: + Negative cacheing support is now present, but largely untested. + + * src/conff.c, TODO: + The output of pdnsd-ctl status is now complete with all currently + supported options. + + * src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/conf-lex.l.in: + Added the config file options for the nefative cacheing support. + +2000-11-04 Thomas Moestl + + * src/cache.c, src/cache.h, src/conf-parse.y, src/dns.c, src/dns_query.c, src/status.c, version: + The new cache infrastructure for negative cacheing is in place. + Using and testing it remains. + + * src/cache.c, src/cache.h: + First changes to support negative cacheing. This should not break + anything, but the cache file format will be incompatible. + + * src/main.c, src/dns_answer.c: + init_udp_socket() and init_tcp_socket() are now called after + daemonizing on FreeBSD, as bind wants to lock the fd which + can cause later calls to fail after an exit. + +2000-11-03 Thomas Moestl + + * src/dns_query.c, src/icmp.c, src/servers.c: + Minor bugfixes and repository cleanup. + +2000-11-02 Thomas Moestl + + * src/icmp.c, TODO, version: + Called it 1.0.16p4. Fixed some compatability problems with the new code; + the ipv4 implementation seems to be fairly stable, the ipv6 one needs + some testing with dest unreach messages. + + * src/icmp.c, src/main.c: + Rewrote large parts of the ping implementation to be more portable. + +2000-11-01 Thomas Moestl + + * acconfig.h, configure.in, version: + Some config fixes, version to 1.0.16p3. SOCKET_LOCKING should be + off by default, as sendmsg can block. + + * src/pdnsd-ctl/pdnsd-ctl.c, src/error.h, configure.in: + Removed -W* arguments from CFLAGS that were implied by -Wall. + + * src/thread.c, configure.in: + Added some more safety tests to configure.in, and made it give + an error on some conditions. Also made configure do poll and usleep + detectione. + + * src/cache.h, src/test/Makefile.am, src/dns_answer.c, src/error.c, src/error.h, src/helpers.c, src/main.c, src/servers.c, src/status.c, src/thread.c, src/thread.h, src/Makefile.am: + Code cleanup. Beautified some macros, and moved the thread + specific things from error.[ch] over to the new thread.[ch]. + Also introduced usleep_r which tries to be thread safe for + different Unices. + +2000-10-31 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl, src/pdnsd-ctl/pdnsd-ctl.c: + The pdnsd-ctl binary got into cvs. Fixed that. + + * src/pdnsd-ctl/pdnsd-ctl, src/cache.c, src/cache.h, src/error.c, src/helpers.c, src/main.c, version: + Another set of FreeBSD compatability patches. This seems to catch + most of the problems, and pdnsd should be useable with libc_r now. + +2000-10-30 Thomas Moestl + + * src/pdnsd-ctl/pdnsd-ctl, src/conff.c, src/dns_answer.c, src/dns_query.c, src/helpers.c, src/icmp.c, src/main.c, AUTHORS, Makefile.am, THANKS, configure.in, version: + FreeBSD fixes, mostly contributed by Roman Shterenzon. + +2000-10-25 Thomas Moestl + + * src/pdnsd-ctl/Makefile.am, src/pdnsd-ctl/pdnsd-ctl: + pdnsd-ctl was not in cvs. + + * src/dns_query.c, src/error.h, configure.in: Some fixups for 1.0.15. + +2000-10-23 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, configure.in: + Several fixes for pdnsd to work better when it receives error replys. + + * src/dns.c, src/dns_answer.c: + Fixed another memory leak on an error path in dns_answer.c and did + a pointer signedness fixup in dns.c + +2000-10-21 Thomas Moestl + + * src/dns_answer.c, AUTHORS: + Applied a patch by Paul Wagland that fixes some spelling mistakes + and some memory leaks on error paths. + + * src/dns_query.c, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_answer.c, NEWS, version: + Bug fixes. Added the randomize_recs option and turned it on + by default. + +2000-10-20 Thomas Moestl + + * src/helpers.c, src/dns.c, src/dns_query.c: + Fixes for the paranoid option to work with root servers + properly. + + * src/dns_query.c, src/dns.h, src/dns_answer.c, AUTHORS, THANKS: + Applied a patch by Paul Wagland for bind9-compatability and added + him to AUTHORS and THANKS. + +2000-10-19 Thomas Moestl + + * src/dns_answer.c, NEWS, version: + Another POLL_* fix. It is now called 1.0.14. + + * src/dns_answer.c, src/dns_query.c, src/icmp.c: BSD build fixes. + + * src/dns_query.c: + Made p_recurdive_query return immediately if a query returns + NXDOMAIN. + + * src/dns_query.c, Makefile.am: + Some bugifixes. It is now called 1.0.13. Releasing. + + * src/cache.c, src/dns_query.c, AUTHORS, NEWS: + Updated AUTHORS and NEWS. Made destroy_cache() lock the cache so + that no thread can access the cache afterwards (could lead to + crashes). + + * src/helpers.c, NEWS, THANKS: + Integrated a security fix contributed by Olaf Kirch: when + changing user IDs, pdnsd did not reinitialize the supplementary + group list, meaning that the process still had the privileges + of the supplementary groups the original user was member of. + + * src/conf-lex.l.in, src/conf-parse.y, Makefile.am, TODO, version: + Introduced the par_queries option. + + * src/dns_answer.c, src/dns_query.c, TODO: + Updated TODO, did some fixups for string handling. + + * HACKING: Added HACKING with some comments about coding style. + +2000-10-18 Thomas Moestl + + * src/dns_answer.c, src/dns_query.c, src/error.h, src/main.c, TODO: + Revieved and fixed the new dns_query.c-poll/select loops. + + * src/test/tping.c, src/dns_query.c, src/icmp.c: + Fixed the new poll/select ping support. + +2000-10-17 Thomas Moestl + + * src/dns_query.c, src/icmp.c, TODO: + Got rid of the O_NONBLOCK loop in the icmp.c ping implementation. + Beta tesing pending. + + * src/rc/Debian/Makefile.am, src/conff.c, src/conff.h, src/dns_query.c, src/dns_query.h, THANKS, TODO, acconfig.h, configure.in: + Corrected the Debian rc script (bug reported by Michael Mller). + Got rid of the nonblocking socket things in dns_query.c, and + using poll/select now. + Testing (esp. --no-poll) remains. + + * src/dns_answer.c: + Got rid of O_NONBLOCK read loops in dns_answer.c, using poll/select + now instead (after one issue about boundaries was cleared up). + +2000-10-16 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am: + The generated rc scripts do not need to be in the distribution. + + * src/conff.c, src/main.c: + Fixed a server structure members in conff.c. Only delete the socket + if we are in status pipe mode now. + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_answer.c, src/dns_query.c, acconfig.h, configure.in: + Added the --enable-tcp-subseq and --with-tcp-qtimeout configure + options, added the tcp_qtimeout conf file option, tested things. + 1.0.12 is ready for release. + +2000-10-15 Thomas Moestl + + * src/dns_answer.c, TODO, acconfig.h: + Added TCP timeouts to the answer code. Still need an option in the + conf file and documentation for that (besides beta testing). + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/dns_query.c, src/helpers.c, src/helpers.h, NEWS, TODO: + Introduced domain inclusion/exclusion lists in the server section + (new options include=, exclude=, policy=). + +2000-10-14 Thomas Moestl + + * src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_answer.c, src/main.c, NEWS, TODO, version: + Upped version, updated NEWS and TODO and implemented a process + count limit. + +2000-10-13 Thomas Moestl + + * src/rc/Debian/pdnsd.in, src/main.c: + Added the --pdnsd-user option, and made the Debian rc script + use it rather than trying to parse the config file itself. + +2000-10-11 Thomas Moestl + + * src/rc/SuSE/pdnsd, src/rc/SuSE/pdnsd.in, src/rc/RedHat/pdnsd, src/rc/RedHat/pdnsd.in, src/rc/Debian/pdnsd, src/rc/Debian/pdnsd.in, AUTHORS, THANKS, configure.in: + Added the 'configure'-able rc scripts contributed by Carsten Block + and added him to THANKS and AUTHORS. + + * src/main.c: + Added O_NOFOLLOW to the pidfile open() call (if it is defined) + to prevent users creating files as the pdnsd user (using links) + if the admin put the pidfile in a world-writeable directory + against all good advice. + This is not a bug fix! Admins were not, and are still not supposed + to put the pidfile in a directory that is writeable for untrusted + users! + +2000-10-10 Thomas Moestl + + * THANKS: Added Milan P. Stanic to THANKS. + + * src/main.c: + Fixed a missing O_WRONLY in the open() call for pidfile operation. + +2000-10-08 Thomas Moestl + + * src/Makefile.am, src/dns.c, src/dns_answer.c, configure.in, version, acconfig.h: + Released 1.0.11. + Two security fixes in dns.c and dns_answer.c, and misc. smaller issues. + + * src/Makefile.am, src/conf-parse.y, src/dns_answer.c, src/dns_query.c, src/icmp.c, src/servers.c, AUTHORS, THANKS, TODO, acconfig.h, configure.in, version: + 1.0.10 was released some time ago ;-) + This had some IPv6 fixes. + Also fixed minor bug when using SOCKET_LOCKING. + +2000-08-28 Thomas Moestl + + * src/dns_answer.c: Fixed a parameter mismatch in getsockopt() + + * Makefile.am: + Applied Sourav K. Mandal's rpm build patch to the toplevel + Makefile.am + +2000-08-27 Thomas Moestl + + * src/conf-lex.l.in, src/conf-parse.y, src/consts.h, src/servers.c, version: + Added diald support. It's now called 1.0.9. + + * src/conf-parse.y, src/conff.c, src/netdev.c, pdnsd.spec.in: + Fixed some ugly typos in conf-parse.y and netdev.c. + Since I have no further bug reports and these bugs make some + things inconvenient, I will release 1.0.9 immediately. + +2000-08-26 Thomas Moestl + + * pdnsd.spec.in: small spec fix. + + * NEWS, configure.in: Last fixups for 1.0.8. Released it. + + * Makefile.am: Set mode and owner for cache file. + + * src/conf-parse.y, src/dns_answer.c, src/netdev.c, acconfig.h, configure.in, version: + Misc fixes. Hopefully fixed the UDP socket problems under Linux SMP. + +2000-08-20 Thomas Moestl + + * src/cache.c, src/dns_answer.c, AUTHORS, THANKS, configure.in, pdnsd.spec.in, version: + Build fixes by Alexandre Nunes, spec fixes (does now set distro for + configure), first attempt at an "error in udp send"-fix, and fix + for a problem with having records for the root domain in the disk + cache file. + +2000-08-13 Thomas Moestl + + * src/netdev.h, src/servers.c, src/status.c, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/dns.c, src/dns.h, src/main.c, src/netdev.c, AUTHORS, THANKS: + Some minor fixes. Integrated the ppp device patch by Ron Yorston. + +2000-08-12 Thomas Moestl + + * configure.in: + configure.in was missing in repository. Also removed debugging + flag for build. + + * src/main.c: Made the pid file handling safe for directories. + + * src/dns.c: Part II of the last fix. + + * src/dns.c: + Fixed a nasty bug in decompress_name which would produce errors very + rarely. That was a off-by-one bug, but on the safe side (no overflow, + stopping one by too early). + + * src/main.c: + Fixed several possible problems with strncat(). None of these was + critical or involved remote data. + +2000-08-08 Thomas Moestl + + * src/main.c: Changed FreeBSD signal latency to 250 ms. + + * src/main.c, acconfig.h: + A set of last-minute FreeBSD fixes. pdnsd does now NEED linuxthreads on + BSD. + +2000-08-07 Thomas Moestl + + * version: It's now called 1.0.7. + + * src/main.c, doc/pdnsd.conf.in, Makefile.am: Misc build&BSD fixes. + + * src/conff.c, src/conf-parse.y, version: + Version set to the hopefully last beta. Fixed the proxy_only option. + +2000-08-05 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns_query.c, src/status.c, doc/pdnsd.conf.in, Makefile.am, TODO: + Added the proxy_only options. Some build fixups. + +2000-07-30 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am, src/rc/Makefile.am, src/main.c, src/status.h, AUTHORS, INSTALL, Makefile.am, TODO, version: + Many small fixups for 1.0.7. + +2000-07-29 Thomas Moestl + + * src/rc/SuSE/Makefile.am, src/rc/RedHat/Makefile.am, src/rc/Debian/Makefile.am, src/rc/README, src/status.c, src/servers.c, Makefile.am, TODO, acconfig.h: + Assorted fixes. + + * src/cache.c, src/hash.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/icmp.c, src/ipvers.h, src/servers.c, src/servers.h, src/status.c, src/status.h, src/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, AUTHORS, Makefile.am, THANKS: + Big heap of updates and fixes. Incorporated build changes from Sourav + K. Mandal and pcmcia SCHEME support by Stephan Boettcher. + +2000-07-22 Thomas Moestl + + * src/rc/Debian/pdnsd: + Applied a patch by Markus Mohr to his debian rc script, which I had + broken in some way. + +2000-07-21 Thomas Moestl + + * src/cache.c, src/main.c, src/status.c, src/status.h: + Worked on the new status socket (pdnsd-ctl) option. + + * src/Makefile.am, version: Upped version, fixed Makefile.am + + * src/cache.c, src/hash.c, src/hash.h, src/cache.h, src/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_query.c, src/dns_query.h, src/helpers.c, src/helpers.h, src/ipvers.h, src/main.c, src/status.c, src/status.h, AUTHORS, Makefile.am, THANKS, acconfig.h: + Updated AUTHORS and THANKS. Merged in patches by Sourav K. Mandal + and Lyonel Vincent. + +2000-07-20 Thomas Moestl + + * doc/pdnsd.conf: Added pdnsd.conf. Well... + + * src/dns_query.c, src/dns_query.h, doc/pdnsd.conf: + Added some ommited files. + + * src/test/Makefile.am, src/test/if_up.c, src/test/is_local_addr.c, src/test/random.c, src/test/tping.c, src/rc/SuSE/Makefile.am, src/rc/SuSE/pdnsd, src/rc/RedHat/Makefile.am, src/rc/RedHat/pdnsd, src/rc/Debian/pdnsd, src/rc/Makefile.am, src/rc/README, src/Makefile.am, src/conf-lex.l.in, src/conf-parse.y, src/conff.c, src/conff.h, src/consts.h, src/dns.c, src/dns.h, src/dns_answer.c, src/dns_answer.h, src/error.c, src/error.h, src/helpers.c, src/helpers.h, src/icmp.c, src/icmp.h, src/ipvers.h, src/main.c, src/netdev.c, src/netdev.h, src/servers.c, src/servers.h, src/status.c, src/status.h, doc/pdnsd.conf.in, Makefile.am, acconfig.h, file-list.base.in, pdnsd.spec.in: + Checked in the pdnsd files at their new locations. + + * doc/pdnsd.conf, Makefile, a-conf.sh, cache.c, cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version: + Removed the moved files. Will add the new ones soon. + +2000-07-16 Thomas Moestl + + * TODO: New tasks in TODO. + +2000-07-15 Thomas Moestl + + * TODO: + Updated TODO: Autoconf support was contributed by Sourav K. Mandal + + * conff.h, dns_query.c, ipvers.h, cache.c: + Fixed some minor bugs and a showstopper in cache.c that caused + crashes in some situations. + +2000-07-12 Thomas Moestl + + * dns_query.c, error.c, error.h, main.c: + Made pdnsd ignore SIGPIPE, which seemed to be responsible for some + crashes. + Accept (grudgingly) SOA rr's where NS ones would be The Right Thing. + +2000-07-10 Thomas Moestl + + * AUTHORS, THANKS, conff.c: + Updated AUTHORS, THANKS, and the fprintfs for the status pipe in + conff.c + + * TODO, config.h.templ, dns.h, dns_answer.c, dns_query.c, dns_query.h, main.c, version: + Added UDP queries and gave the user the choice between TCP and UDP + queries (UDP is the default now). Made the TCP server optional. + Fixed a authoritative record handling bug. Added pidfile support. + +2000-07-07 Thomas Moestl + + * doc/pdnsd.conf: + Inserted run_as="nobody"; again, it is The Right Thing and people + should use it. + + * Makefile: + The pdnsd cache directory is now created as nobody, since the + default run_as in the example pdnsd.conf is also nobody. + + * doc/pdnsd.conf: + Commented the run_as option out (people may run into permission + problems). + + * version: Upped version to 1.0.5 + + * AUTHORS, THANKS, conf.l.templ, conf.y, conff.c, conff.h, dns_answer.c, icmp.c, icmp.h, main.c, version: + Folded in the server_ip option code as contributed by Wolfgang Ocker + and extended it to IPv6. Fixed a bug in IPv4 ping in IPv6 mode. + +2000-07-06 Thomas Moestl + + * cache.c, dns_query.c: + Killed a bug which could cause crashes with more than 2 servers. + + * cache.c: Fixed a bug reported by Bert Frederiks that would break the + serve_aliases option when only one character was between official + name and alias in the /etc/hosts-style file. + +2000-07-04 Thomas Moestl + + * pdnsd-suse.spec.templ: The SuSE spec now uses the new makefile rule. + + * Makefile, THANKS, dns_query.c, helpers.c, version: + Added people to THANKS, fixed a bug that caused uppercase hosts/ + rr-section entries to be ignored in the cache, fixed the SuSE + makefile for pdnsd to run_as nobody, and other small fixups. + +2000-07-03 Thomas Moestl + + * dns_answer.c: + First change after release of 1.0.4: The questions received + are now properly written into the debug file when starting + with -g -d. + + * config.h.templ, dns_query.c: + Fixed a possible way to get around paranoid restrictions. + + * version: Set version to 1.0.4 + + * doc/pdnsd.conf: Added an entry for the paranoid option. + + * cache.c, config.h.templ, dns_answer.c, dns_query.c, ipvers.h: + Revisions and fixups. The complete code revision is now complete. + +2000-06-29 Thomas Moestl + + * dns_query.c: Overhaul. + + * dns_answer.c, dns_query.c, config.h.templ: + Code overhault continued. dns_answer.c is finished. + +2000-06-27 Thomas Moestl + + * conff.c, dns_answer.c, icmp.c, netdev.c, servers.c, status.c: + Continued code overhaul. Fixed several bugs, and simplified some + code. + + * conf.l.templ, conf.y, conff.c, ipvers.h, version: Fixups. + +2000-06-26 Thomas Moestl + + * hash.c: Revised; fixed a minor bug. + + * cache.c: Overhauled. + + * dns_query.c, error.c, helpers.c, helpers.h, ipvers.h, main.c, version, dns.c: + Manual code overhaul. Numerous small patches, greatly simplified + decompress_name(). + +2000-06-25 Thomas Moestl + + * config.h.templ: + Made the C random() RNG the default (using /dev/urandom, we suck up + too much randomness on high load). + + * error.c, error.h, icmp.c, icmp.h, main.c: + Small cleanups. Makes the testsuite compilation easier. + + * Makefile, config.h.templ: Preparing for release of 1.0.4. + +2000-06-24 Thomas Moestl + + * Makefile, THANKS, a-conf.sh, dns_answer.c, dns_query.c, error.c, version: + Fixed bugs with the paranoid option, connect() timeout handling, and + a incompatability in response handling that caused the glibc + resolver to misunderstand error messages pdnsd generated on unknown + query types. This bug, that was reported by James MacLean, could + for example cause ssh to hang some time. + + * Makefile, NEWS, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, dns.c, dns_query.c, dns_query.h, helpers.c, helpers.h, icmp.c, icmp.h, main.c: + Added the paranoid option, and modified the ping uptest so that it + works with strict_setuid. Also made strict_setuid=on the default. + 1.0.4 should be out soon. + +2000-06-23 Thomas Moestl + + * doc/pdnsd.conf: Added a run_as= line, which is sensible normally. + + * Makefile, conf.l.templ, conf.y, conff.c, conff.h, dns_answer.c, dns_answer.h, helpers.c, helpers.h, main.c, servers.c, status.c, version: + Some fixups, added the run_as and strict_setuid security options. + + * THANKS: Updated. + + * AUTHORS, ipvers.h, main.c, netdev.c, version: + Fixed some definitions for glibc2.0-users. Repaced the return at the + end of main() with _exit(). Should not build and run OK on glibc 2.0 + boxen. + Fixed a typo in netdev.c + + * ipvers.h: + Fixed a typo in ipvers.h to fix compile problems on systems without + an IPv6-supporting C library, and possible IPv6 problems using the + status pipe. + + * error.c, version: + Fixed a bug that could cause signals to be delivered to the wrong + process. + +2000-06-22 Thomas Moestl + + * version: Set version to 1.0.1. + + * cache.c, dns_answer.c, error.c, error.h, main.c, pdnsd-suse.spec.templ: + Fixed misc issues reported by Jonathan Hudson and Joachim Dorner, one + of them a real showstopper in cache.c. + + * Makefile, NEWS, README, cache.h, config.h.templ, version: + Updated things for 1.0.0 and released it finally. + + * AUTHORS, THANKS: Updated THANKS and AUTHORS + + * NEWS, a-conf.sh, cache.c, conf.l.templ, conf.y, conff.c, conff.h, dns_answer.c, dns_query.c, error.c, error.h, main.c, servers.c, status.c: + Fixed a-conf.sh and cleaned up signal handling as far as it can be + done ;-). Added the max_ttl option. + +2000-06-21 Thomas Moestl + + * dns_answer.c, error.c, error.h, main.c, servers.c, status.c: + More signal fixes. This is a real pain with LinuxThreads. + + * NEWS, cache.c, dns_answer.c, error.c, error.h, helpers.c, helpers.h, main.c, servers.c, status.c, version: + Fixups for signal handling. This is more than only a little tricky + using the linuxthreads library. This hopefully fixes the deadlocks + we had on signals. + +2000-06-13 Thomas Moestl + + * AUTHORS, THANKS: Updated credits. + + * a-conf.sh: + A primitive configure-like script intended as drop-in replacement + until autoconf support finally comes. + + * .cvsignore, Makefile, cache.c, dns_query.c, ipvers.h, servers.c, version: + Added a primitive configuration script as drop-in. Killed some bugs + and changed the recently added linkdown_kluge option following + suggestions from Daniel Smolik. + +2000-06-12 Thomas Moestl + + * dns_query.h, exec-flex.sh, Makefile, cache.c, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, dns_query.c: + Numerous cleanups and fixes. Implemented the linkdown_kluge option + as proposed by Daniel Smolik. Hope to get ready for 1.0.0 know. + +2000-06-10 Thomas Moestl + + * Makefile, NEWS, TODO, dns_answer.c: + Modified some stuff in dns_answer.c (if no nameserver for a knot in + the dns namespace is found now, its predecessors are tried now in + order to return accurate authority results). This will be paid with a + little more beta time, so the Makefile has developer switches again. + Corrected NEWS and TODO. + + * Makefile, config.h.templ, dns_answer.c, dns_query.c, dns_query.h, hash.h, helpers.c, version: + Removed some dead code, fixed some really minor bugs. Version is up + to 1.0.0p7, which is hopefully the last beta. + + * Makefile, config.h.templ: + Fixed things up for the 1.0.0 distribution version + +2000-06-06 Thomas Moestl + + * Makefile, config.h.templ, icmp.c, netdev.c: + Some minor comment fixes. + + * Makefile, TODO, main.c, version: + BSD fix in Makefile and help update. It is now called 1.0.0p6. TODO + was updated to reflect the project status. + + * dns_answer.c: + BSD & misc fixes. pdnsd runs now nicely on my FreeBSD 4.0 box. + + * dns_answer.c, ipvers.h: + Fixed IPv6 UDP dest address recovery. Also fixed a real stupid bug in + ipvers.h. + + * cache.h, dns_query.c, error.h: + Added DEBUG_MSG6 macros. Cleaned up requery handling. + + * dns.c, dns_answer.c, dns_query.c, dns_query.h: + Fixed another heap of bugs, introduced some sanity checks, no requery + on answers that have ra not set now. + +2000-06-05 Thomas Moestl + + * cache.c: + Fixed write_disk_cache. + + * cache.c, cache.h: + Fixes for rr handling. + +2000-06-04 Thomas Moestl + + * cache.c, dns_answer.c, dns_query.c: + Fixes again: some missing checks for rrset existence added. + + * cache.c, dns_answer.c, helpers.c, icmp.c: + Fixes for the new/modified code and its side effects on old code ;-) + + * ChangeLog.old, NEWS, cache.c, conf.l.templ, conf.y, conff.c, config.h.templ, dns.c, dns_answer.c, dns_query.c, error.c, hash.c, helpers.c, icmp.c, main.c, netdev.c, servers.c, status.c: + Folded the ChangeLog and NEWS of the 0.9.x tree back in and added NEWS for the + upcoming 1.0.0 release. Some compile fixes. Reorganized config.h.templ. Made + the inclusion of the rcsid strings into the executable optional. + + * cache.c, dns_answer.c: + Pile of fixes on recently added/modified code. + +2000-06-03 Thomas Moestl + + * .cvsignore, Makefile, lex.inc.h: + lex.inc.h should not be in CVS (it is automatically generated by + exec-flex.sh). It should also be deleted by 'make mclean'. + + * TODO, cache.c, dns_answer.c, dns_query.c, lex.inc.h: + rfc2181 conformance should be reached by now. Updated TODO. Bugfixing + remains. + + * doc/html/.cvsignore, doc/html/dl.html, doc/html/doc.html, doc/html/faq.html, doc/html/index.html: + Removed the html documentation from CVS. It is maintained separately. + + * doc/pdnsd.conf: + Added CVS/RCS $Id$ tag. + + * cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version, AUTHORS, INSTALL, Makefile, NEWS, README, THANKS, TODO, cache.c: + Added CVS/RCS $Id$ tags to most files, did some cleanups, introduced + the new rrset granularity caching. The new code is still much of beta, + use with care. + +2000-06-01 Thomas Moestl + + * Makefile, cache.c, hash.c, helpers.c, icmp.c, netdev.c: + Yet another set of BSD fixes (test programs do now work for me + under FreeBSD). Some other minor fixes. + + * Makefile, error.c, error.h, helpers.c, helpers.h: + Transplanted kill_pdnsd from error.c to helpers.c in order to get the + tests compiled without the thread library. + +2000-05-31 Thomas Moestl + + * pdnsd: + Ooops, executable got in. + + * Makefile, pdnsd: + Added test suite programs. + + * icmp.c, netdev.c: + All basic BSD patches have been folded in. pdnsd will now compile on + FreeBSD with (hopefully) all features. + + * dns_answer.c, Makefile: + Disabled udp targed address discovery for BSD builds (this sadly ist + OS specific at least for IPv4. Must be rewritten under BSD as it is + an RFC compatability issue under some circumstances) + + * cache.c, cache.h, conf.y, conff.c, conff.h, config.h.templ, dns_answer.c, helpers.c, icmp.c, ipvers.h, netdev.c, Makefile: + BSD include & misc build fixes. More to follow... + + * Makefile, cache.h: + BSD Fixes: Makefile should work with BSD make, sed command line, + sorted out naming clash in cache.h + + * .cvsignore: + Added ChangeLog to .cvsignore + + * THANKS, conff.c: + Fixed a bug reported by Jonathan Hudson and added him to THANKS + + * ChangeLog.old: + Added the pre-CVS ChangeLog. + + * .cvsignore, AUTHORS, COPYING, INSTALL, Makefile, NEWS, README, THANKS, TODO, cache.c, cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, doc/.cvsignore, doc/html/dl.html, doc/html/doc.html, doc/html/faq.html, doc/html/index.html, doc/pdnsd.conf, doc/txt/.cvsignore, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, lex.inc.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version: + Initial import of pdnsd-1.0.0p3 source tree into CVS. + + + * .cvsignore, AUTHORS, COPYING, INSTALL, Makefile, NEWS, README, THANKS, TODO, cache.c, cache.h, conf.l.templ, conf.y, conff.c, conff.h, config.h.templ, consts.h, dns.c, dns.h, dns_answer.c, dns_answer.h, dns_query.c, dns_query.h, doc/.cvsignore, doc/html/dl.html, doc/html/doc.html, doc/html/faq.html, doc/html/index.html, doc/pdnsd.conf, doc/txt/.cvsignore, error.c, error.h, exec-flex.sh, hash.c, hash.h, helpers.c, helpers.h, icmp.c, icmp.h, ipvers.h, lex.inc.h, main.c, netdev.c, netdev.h, pdnsd-redhat.spec.templ, pdnsd-suse.spec.templ, servers.c, servers.h, status.c, status.h, version: + New file. + diff --git a/service/src/main/jni/pdnsd/ChangeLog.old b/service/src/main/jni/pdnsd/ChangeLog.old new file mode 100644 index 0000000..0b3553e --- /dev/null +++ b/service/src/main/jni/pdnsd/ChangeLog.old @@ -0,0 +1,161 @@ +2000-02-15 + Version 0.2. First working alpha with the extended feature set. + +2000-02-16 + Version 0.3 with many bugfixes, better standard conformity and + some new features. + +2000-02-17 + Did a lint on the code, implemented soft timeouts, again bugfixes, + drastically reduced cache memory requirements, implemented local + records. + Version 0.4 released. + +2000-02-19 + Recursive query finally implemented. Version 0.5 out. + +2000-02-20 + Various bugfixes. The server now always tries to get an AA answer + if possible; this implies recursion. This is to deal better with + caching servers that may return incomplete results on * queries. + This may be a little sub-optimal since it may not take the full + effect of caching, but it is the only real possibility of getting + complete records. + Redid the deps in the makefile for now. + Version is now 0.6. + +2000-02-21 + Another set of bugfixes. Version is 0.6.1. + +2000-02-22 + Another set of bugfixes. It should stabilize by now. Answer compression + is there finally. Version is therefore up again, 0.7 by now. + +2000-02-23 + Minor bugfixes, isdn interface uptest added. The new record types as of + rfc1700 are implemented, but as a compile-time option, since normally + there is no need to waste space for them. + Version is 0.7.1. + +2000-02-24 + Version 0.7.2 with all rrs up to KX (36). #define DNS_NEW_RRS in + config.h and (re)compile if you want to use them. Delete the cache + file before using a version with this option changed! + NSAP-PTR does NOT WORK (any incoming answer containing it may cause + a format error) because it is ill-designed (see TODO). Never mind, it + is officially deprecated anyway. + The secure DNS extension record types defined in RFC 2065 (KEY, SIG, + NXT) are cached only and therefore useless. + +2000-02-26 + Version 0.8 with parallel query (and probably new bugs) introduced. + +2000-02-27 + Version 0.8.1 with minor bugfixes on the new features. + +2000-02-27 + Version 0.8.5 with authority support and the usual bugifxes. Some issues + with standard conformity were also fixed (wow, two versions a day). + +2000-02-28 + Implemented caching of non-authoritative records. This allows better + usage of other caching name servers. This may return non-authoritative + records to any non-wildcard query. If a wildcard query arrives, we + always look for authoritative records, so we do if the non-authoritative + answer does not contain at least one answering record to the query. + This fixes the sub-optimal behaviour since 0.6. + Version is therefore up to 0.8.7 (0.8.6 was also released today + containing bugfixes) + +2000-02-29 + Better thread support, avoiding deadlocks in signal handling. The cost + is unfortunately one more thread. + New option server_port in config file. + Version is 0.8.8. + +2000-03-01 + Nailed a renitent memory leak bug to the wall (shame, what a trivial + mistake). Also improved handling of error conditions slightly. + Version is 0.8.9. + +2000-03-01 + Cache code cleanup. The development of the non-parallel query code + is discontinued, although it still works. If you want serial query, + just set PAR_QUERIES to 1. + Additional information finally implemented. + We are up to 0.9. + +2000-03-02 + Fixed a DoS possibility. + More rfc compatability fixups and a smarter resolver logic. + Version 0.9.1 + +2000-03-02 + Added the source section to the config file handling. This allows to + source your /etc/hosts file. Version 0.9.2. + +2000-03-02 + Avoiding double additional records now. Version 0.9.3. + +2000-03-09 + Fixed some REALLY STUPID bugs. Uuummph, thought it was finished. + However, the number of bugs remaining is always n+1... + Anyway, we are up to 0.9.4. Hope that there are no mean bugs left. + +2000-03-14 + Added another uptest, exec. See README for details. Also grained the + cache size finer (it's now specified in kB). Version 0.9.5 + +2000-04-07 + Fixed some really stupid bugs, what else? Version is 0.9.6 + To be more exact: fixed misc bugs, cleaned up hash.c and cache.c + +2000-04-20 + Again fixed some bugs, version is now 0.9.7. + +2000-04-29 + Fixed a build problem caused by some missing #defines in glibc2.0 and a + minor bug. + Version is up to 0.9.8 + +2000-04-30 + Fixed some unclean C code and did a general C lint. Thanks to Byrial + Jensen for pointing out some issues. Now using stricter compiler flags. + Also replaced the daemon() call in main.c for improved portability. + Pre-Released 0.9.9p1 + +2000-05-03 + Released 0.9.9p2 with the changes of 0.9.9p1, Documentation updates, + bugfixes, and the Red Hat rc scripts contributed by Torben Janssen. + Also, the meaning of the command line option -v has changed. + There is a new config file option "lean_query" that is on by + default. It is an optimization, so please look in the docs when + updating whether you want it switched on or not. + Removed the long-dead serial query code from the distribution tree. + Some resolvers seem to be broken somehow in a respect that it cares + about order in which the records appear. In particular, it wants + cname to appear before other records. Ok, so it be... + + +2000-05-04 + Save all names in lower case in the rrs. + Tidying up the source tree. + The long-awaited cache structure changes have been started. Please + delete you cache files before using this new release. + +2000-05-05 + Fixed several bugs in the old and in the new source tree. + Use time_t instead of long for internal time storage for compatibility. + + +2000-05-06 + Version 0.9.10. + This fixes a bug in uptest=if. Red Hat and configuration fixes + suggested by Soeren J. Peters were included. + +2000-05-08 + Version 0.9.11 + This fixes a locally exploitable security problem (pdnsd.cache was + world-writeable). This is actually a one-line fix; for a description of + possible dangers, please refer to the html documentation. + Thanks to Alan Swanson for reporting. diff --git a/service/src/main/jni/pdnsd/INSTALL b/service/src/main/jni/pdnsd/INSTALL new file mode 100644 index 0000000..0399189 --- /dev/null +++ b/service/src/main/jni/pdnsd/INSTALL @@ -0,0 +1,190 @@ +The installation instructions are in doc/html/doc.html and doc/txt/manual.txt. +The system requirements are listed in doc/html/index.html and doc/txt/intro.txt. +I recommend using the html version. +Following are generic installation instructions for autoconf programs. +I strongly recommend to read the Installation section in the docs! + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/service/src/main/jni/pdnsd/Makefile.am b/service/src/main/jni/pdnsd/Makefile.am new file mode 100644 index 0000000..146c32a --- /dev/null +++ b/service/src/main/jni/pdnsd/Makefile.am @@ -0,0 +1,40 @@ + +SUBDIRS = src doc contrib + +EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD + +# The sample configuration is handled in doc/Makefile.am +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(cachedir)" + test -f "$(DESTDIR)$(cachedir)/pdnsd.cache" || \ + touch "$(DESTDIR)$(cachedir)/pdnsd.cache" + if test `whoami` = "root"; then \ + chown $(def_id) "$(DESTDIR)$(cachedir)/pdnsd.cache"; \ + chown $(def_id) "$(DESTDIR)$(cachedir)"; \ + fi + chmod 0640 "$(DESTDIR)$(cachedir)/pdnsd.cache" + chmod 0750 "$(DESTDIR)$(cachedir)" + +dist-hook: $(PACKAGE).spec.in + sed -e '/^%{!?distro: %define distro /c\ + %if 0%{!?distro:1}\ + %if "%{_vendor}" == "redhat"\ + %define distro RedHat\ + %else\ + %if "%{_vendor}" == "suse"\ + %define distro SuSE\ + %else\ + %if "%{_vendor}" == "SuSE"\ + %define distro SuSE\ + %endif\ + %endif\ + %endif\ + %endif' \ + -e 's:[@]PACKAGE[@]:$(PACKAGE):g' \ + -e 's:[@]VERSION[@]:$(VERSION):g' \ + -e 's:[@]fullversion[@]:$(fullversion):g' \ + -e 's:[@]packagerelease[@]:$(packagerelease):g' \ + -e 's:[@]cachedir[@]:/var/cache/$(PACKAGE):g' \ + -e 's:[@]def_id[@]:$(PACKAGE):g' \ + $(PACKAGE).spec.in > $(distdir)/$(PACKAGE).spec + diff --git a/service/src/main/jni/pdnsd/Makefile.in b/service/src/main/jni/pdnsd/Makefile.in new file mode 100644 index 0000000..a865d54 --- /dev/null +++ b/service/src/main/jni/pdnsd/Makefile.in @@ -0,0 +1,734 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = . +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/PKGBUILD.in \ + $(srcdir)/config.h.in $(srcdir)/file-list.base.in \ + $(srcdir)/pdnsd.spec.in $(top_srcdir)/configure AUTHORS \ + COPYING ChangeLog INSTALL NEWS THANKS TODO acconfig.h compile \ + depcomp install-sh missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = pdnsd.spec file-list.base PKGBUILD +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir dist dist-all distcheck +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d "$(distdir)" \ + || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr "$(distdir)"; }; } +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = src doc contrib +EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: $(am__configure_deps) $(top_srcdir)/acconfig.h + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +pdnsd.spec: $(top_builddir)/config.status $(srcdir)/pdnsd.spec.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +file-list.base: $(top_builddir)/config.status $(srcdir)/file-list.base.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +PKGBUILD: $(top_builddir)/config.status $(srcdir)/PKGBUILD.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-lzma: distdir + tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma + $(am__remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lzma*) \ + lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @$(am__cd) '$(distuninstallcheck_dir)' \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \ + ctags-recursive install-am install-data-am install-strip \ + tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am am--refresh check check-am clean clean-generic \ + ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \ + dist-hook dist-lzma dist-shar dist-tarZ dist-xz dist-zip \ + distcheck distclean distclean-generic distclean-hdr \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-data-hook install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \ + tags-recursive uninstall uninstall-am + + +# The sample configuration is handled in doc/Makefile.am +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(cachedir)" + test -f "$(DESTDIR)$(cachedir)/pdnsd.cache" || \ + touch "$(DESTDIR)$(cachedir)/pdnsd.cache" + if test `whoami` = "root"; then \ + chown $(def_id) "$(DESTDIR)$(cachedir)/pdnsd.cache"; \ + chown $(def_id) "$(DESTDIR)$(cachedir)"; \ + fi + chmod 0640 "$(DESTDIR)$(cachedir)/pdnsd.cache" + chmod 0750 "$(DESTDIR)$(cachedir)" + +dist-hook: $(PACKAGE).spec.in + sed -e '/^%{!?distro: %define distro /c\ + %if 0%{!?distro:1}\ + %if "%{_vendor}" == "redhat"\ + %define distro RedHat\ + %else\ + %if "%{_vendor}" == "suse"\ + %define distro SuSE\ + %else\ + %if "%{_vendor}" == "SuSE"\ + %define distro SuSE\ + %endif\ + %endif\ + %endif\ + %endif' \ + -e 's:[@]PACKAGE[@]:$(PACKAGE):g' \ + -e 's:[@]VERSION[@]:$(VERSION):g' \ + -e 's:[@]fullversion[@]:$(fullversion):g' \ + -e 's:[@]packagerelease[@]:$(packagerelease):g' \ + -e 's:[@]cachedir[@]:/var/cache/$(PACKAGE):g' \ + -e 's:[@]def_id[@]:$(PACKAGE):g' \ + $(PACKAGE).spec.in > $(distdir)/$(PACKAGE).spec + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/NEWS b/service/src/main/jni/pdnsd/NEWS new file mode 100644 index 0000000..9a83bba --- /dev/null +++ b/service/src/main/jni/pdnsd/NEWS @@ -0,0 +1,324 @@ +Version 1.2.9a fixes a bug in the 1.2.9 release that causes a build failure when +pdnsd is configured with --enable-strict-rfc2181. Unless you use this option to +compile pdnsd, there is no need to upgrade from 1.2.9 to 1.2.9a. + +Version 1.2.9 has support for many additional RR types, in particular those +needed for DNSSEC (though no support for the DNSSEC protocol itself yet in +pdnsd). Caching data structures are now more efficient when they only store the +most commonly used RR types. Fine-grained configurability over which RR-types +are cache-able. Pdnsd now has support for EDNS (Extension mechanisms for DNS), +although its usefulness is currently limited to enabling UDP messages larger +than 512 bytes. Defining local TXT records in the configuration file is now +supported. A new configuration option provides a fix in case the query uptest +fails due to remote servers ignoring empty queries. Several bugs have been fixed +including a UDP socket descriptor leak that affected the FreeBSD platform, and +an IPv6 port binding bug. + +Version 1.2.8 implements support for automatic discovery of root servers. +There are also some improvements in the resolver and a new default setting for +the neg_rrs_pol configuration option. + +Version 1.2.7-par fixes some security problems. It contains a fix for a +"dangling pointer" bug that could cause pdnsd to crash when it received a long +reply. It also addresses some of the issues raised in the CERT vulnerability +note VU#800113 by making the default of query_port_start equal to 1024, thereby +ensuring that source ports are randomly selected by the pdnsd resolver in the +range 1024-65535. This release also fixes problems with compiling pdnsd for the +ARM architecture and for the Darwin platform (Max OS X). There are a number of +(minor) new features. pdnsd now supports "include" files, essentially +configuration files that only contain definitions for local records. It is now +possible to define interactively, using pdnsd-ctl, any local record that can be +defined in a configuration file. + +Version 1.2.6-par has an upgraded license: GPL version 3. +A bug has been fixed which which caused pdnsd to handle NXDOMAIN replies +inefficiently when configured with neg_domain_pol=on. Also the code for the +ping test has been fixed, which was broken for 64-bit systems. A new option +randomize_servers can be used to give each server in a section of the +configuration file an equal chance of being queried. The new options reject, +reject_policy and reject_recursively make it possible to check for the presence +of certain IP addresses in the replies of name servers and to correct some types +of unwanted replies or to censor these IP addresses. +The pdnsd-ctl 'add a' and 'add aaaa' commands now allow multiple IP addresses to +be specified for the same name. There are some further improvements to pdnsd's +recursive resolver. + +Version 1.2.5-par introduces a new query method: udp_tcp. With this method a UDP +query is tried first and, if the UDP answer is truncated, the query is repeated +using TCP, which is the behaviour that seems to be recommended by the DNS +standards. There is a new configuration option use_nss, which can be turned off +to prevent lengthy timeouts and stalls in certain situations. A bug has been +fixed which could cause pdnsd to crash if debug output was generated before the +debug output stream was properly initialized. + +In version 1.2.4-par a memory leak and a minor buffer-overflow problem have been +fixed. There is now a fix for some situations that would previously cause pdnsd +to exit prematurely (such as ACPI S3 sleep or trying to attach strace to pdnsd). +Time intervals specified in the configuration file can now be expressed in +minutes, hours, days and weeks as well as seconds. Support for Apple Mac OS X +v10.4 Tiger has been improved. The "pdnsd-ctl status" command now also provides +some information about the status of the running threads. There are some further +improvements in the debugging information provided by pdnsd. +TCP-query support is now compiled in by default (but can still be disabled using +the configure option --disable-tcp-queries). + +In version 1.2.3-par the "pdnsd-ctl empty-cache" command can be provided with an +include/exclude list, allowing the user to specify a selection of names to be +removed, instead of emptying the cache completely. +Additional improvements: pdnsd should now remain responsive while executing the +"pdnsd-ctl empty-cache" command. With the query_method=tcp_udp option pdnsd will +now also try a UDP query after a TCP connection times out, which should allow +pdnsd to resolve the same names with query_method=tcp_udp as with +query_method=udp_only, although perhaps with an occasional delay. +"pdnsd-ctl config" or "pdnsd-ctl server" commands should now run without delays, +even if pdnsd is performing ping or query uptests at the time. A problem with +resolving certain names using root servers has been fixed. + +Version 1.2.2-par has a number of important portability improvements. A bug has +been fixed that prevented pdnsd from compiling successfully on some 64 bit +architectures. The code for determining endianness (most significant or least +significant byte first) should now be more portable. This release has +(experimental) support for the Darwin (Apple Mac OS X) platform. On Linux +systems, the configure script will now try to detect automatically whether the +system implements the Native POSIX Thread Library, but the method used may not +necessarily be foolproof. In addition, the debug features have been improved and +should make it easier to find out why pdnsd considers some queries or replies +malformed. + +Version 1.2.1-par has improved support for non-Linux platforms. This release has +(experimental) support for the Cygwin platform, and should also fix some +compilation glitches that have been reported by FreeBSD users. + +Version 1.2-par is a new and improved version of pdnsd! Most of the changes +effect the internal workings of pdnsd, but there are also a number of +interesting new features (well, I think they are interesting). Among the bugs +fixed are two rather nasty ones which involve the handling of NXT and NAPTR +records and which can cause pdnsd to crash or abort. The new features include a +new server availability test which can be specified with uptest=query, support +for reading the DNS configuration from resolv.conf files, a new option for +optimizing the use of root servers, a new option that makes defining local +records for reverse resolving easier, support for defining wildcard records, a +new pdnsd-ctl command for reloading the config file without restarting pdnsd, +and a new pdnsd-ctl command for dumping information about the names stored in +the cache. +The documentation has also been updated: there is now a pdnsd.conf man page. For +a more complete list of the changes I'll have to refer you to README.par and the +ChangeLog. + +Version 1.1.11a-par contains a fix for FreeBSD users that bypasses a problem +with the macro ENONET, which can cause a compilation failure when it is +undefined. Linux users will notice no difference between 1.1.11a-par and +1.1.11-par. + +Version 1.1.11-par has a rather large number of small changes, which are rather +difficult to summarize. Among the bugs fixed are a race condition in the cache +lookup code, a flaw in the code that caused a busy spin when a remote server +answered with "Not Implemented", and problems with the -4 and -6 command-line +options. Among the improvements are an alternative sorting algorithm which +should allow pdnsd to start up faster when reading a large cache file from disk, +automatic mapping of IPv4 to IPv6 addresses when running in IPv6 mode, somewhat +more efficient memory use, better compression of the replies and changes in the +parallel querying algorithm that should improve the chances of catching a reply +from a remote server. For a more complete list of the changes I'll have to +refer you to README.par and the ChangeLog. + +Version 1.1.10-par has a new parser for configuration files, completely +rewritten from scratch in C. The main advantages are: (f)lex and yacc/bison are +no longer needed to build pdnsd, more informative error messages instead of +merely "parse error", and string literals no longer need to be enclosed in +quotes in most cases. Furthermore, a bug has been fixed that caused incorrect +IPV6-type PTR records to be generated when sourcing /etc/hosts like files. +There have been other small changes, more details can be found in the ChangeLog. + +Version 1.1.9-par adds some missing pieces to the documentation (the pdnsd +manual and the man page for pdnsd-ctl). The changes to the code consist mostly +of optimizations, removal of some size limits due to fixed-size buffers, and +some cleaning up. I've also tried to make the error responses of pdnsd-ctl more +helpful. More details can be found in the ChangeLog. + +Version 1.1.8b1-par8 introduces a "delegation-only" feature that may be useful +for blocking Verisign's Sitefinder. The parser for the configuration file now +tolerates domain names missing a dot at the end. I have provided alternative +implementations for some GNU extensions that I used in an effort to make the +code more portable. In particular, the code should build on FreeBSD again. More +details can be found in the README.par file. + +Version 1.1.8b1-par7 fixing a number of bugs. I have also reworked some of the +code for adding and removing entries in the cache in an effort to improve +efficiency and stability. More details can be found in the ChangeLog. + +Version 1.1.8b1-par6 introduces some further code cleanup. In addition the +documentation has been revised. + +Version 1.1.8b1-par5 fixes a troublesome allocation size error that has been +discovered in Thomas Moestl's code. In practice this bug only wastes memory but +it could also potentially lead to memory corruption. Upgrading is +recommended. More details can be found in the ChangeLog. + +Version 1.1.8b1-par4 has been released. Due to incompatibilities between +various implementations of the pthread library on Linux systems, problems can +occur with signal handling in pdnsd. The usual symptom is failure by pdnsd to +save the cache to disk, and /var/cache/pdnsd/pdnsd.cache remaining empty. If you +experience this kind of trouble, try reconfiguring with different values for the +new --with-thread-lib option. The allowable values are described in the +documentation. + +pdnsd is no longer maintained by Thomas Moestl: I have not had time to maintain +pdnsd for quite a while now, and have been very slow to respond to issues, or +did not respond at all. It is time that I officially announce that pdnsd is no +longer actively maintained; I apologize to all those who reported bugs or asked +questions without receiving any reply. However, Paul A. Rombouts has published a +patch set against the last released version at +http://www.phys.uu.nl/~rombouts/pdnsd.html, which cleans up a lot of code fixes +many bugs. + +Version 1.1.7a fixes a reversed assertion that would cause pdnsd to terminate +if used with the ping uptest. No other changes were made. + +Version 1.1.7 fixes some problems that might be remotely exploitable to +gain access as the user pdnsd runs as (an unprivileged user by default). To do +this, an attacker needs to control a name server that is queried by pdnsd, and +send a malicious reply to such a query. Upgrading is strongly recommended! +There are also minor bug fixes and stability improvements. + +Version 1.1.6 adds the query_port_start and query_port_end options (contributed +by Andreas Steinmetz), that allow confining the ports pdnsd uses for outgoing +queries to a certain range. It also fixes numerous bugs, one of which could +cause pdnsd to hang; update is therefore recommended. + +Version 1.1.5 contains a fix for a security bug that would allow local users +that are allowed to use pdnsd-ctl on a running pdnsd server to execute +arbitrary code as the user that pdnsd runs as (or on Linux, when strict_setuid +is not enabled, as the user that started pdnsd). The danger of this is usually +quite limited; the status socket is not enabled by default, it's default +permissions do only allow the user pdnsd runs as to use the socket, +strict_setuid is enabled by default and pdnsd runs as an unprivileged user. +There is also a new configure option, --enable-underscores, that will make +pdnsd allow underscores in domain names. Furthermore, the SRV record handling +has been fixed to allow underscores in any case (this was not allowed +previously, but is required by the RFC). SOA records are not put in the +answer section any more if no answers are found (this violates the RFC's). +It may be put in the authority section in a later version. +There are also various bugfixes in this release. +Upgrade is recommended. + +Version 1.1.4 fixes various smaller bugs, and should also improve the cache +write performance especially for larger caches. There are also two new +features: servers can now be given a label (using the label server option) +which can be used to identify them for the pdnsd-ctl server command +(contributed by Andrew M. Bishop), and local records can be marked to make +the domain record authoritative in pdnsd's cache (which means that pdnsd will +assume that records that are not present in the cache for that domain are +non-existent); this is on by default now, and can be controlled using the new +authrec server option). + +Version 1.1.3 added contrib/ and had a lot of robustness fixes. +This release addresses a security hole that affects only Linux systems. Due to +a bug in glibc, pdnsd could crash during a port scan. This release contains +a workaround for this, as well as a fix for a deadlock under heavy load +conditions. It also fixes a possible problem that could be triggered by +malicious servers, and contains numerous bug fixes. +A script, contributed by Marko Stolle, makes pdnsd useful in a DHCP setup. +pdnsd also preservers the case of names in the cache, and should work much +better on alpha machines (thanks for the contributions by Bjoern Fischer +and P.J. Bostley that made this possible). New types were dded for rr +sections and pdnsd-ctl. +Upgrade is recommended. + +Version 1.1.2 has a fix for a bug that could cause SERVFAIL to be +returned when NXDOMAIN would be appropriate. The bug surfaced only when +pdnsd queried name servers with a behaviour different from BIND's in the +NXDOMAIN case, e.g. pdnsd querying another pdnsd or e.g. djbdns. + +Version 1.1.1 fixes a possible race condition in status socket creation. +This race might be used by a local attacker to change the access +permissions of a certain file in /tmp. The risk of this is probably +negligible. The default setup uses a non-privileged user, default mode +0600, and the status socket is disabled normally, so this should be +relatively safe. I don't see any possibility to exploit this, it is +more of a paranoia fix. +There are also some other minor fixes and documentation improvements. +Upgrade is recommended. + +Version 1.1.0 introduces negative cacheing, pdnsd-ctl enhancements and +a much improved FreeBSD support. The cache file format has changed from +prior releases. Some configuration defaults have changed, too. + +Version 1.0.15 is mostly a bugfix release. It also has a new option: +randomize_recs in the global section. + +Version 1.0.14 has a fix in icmp.c that will make it build properly +on FreeBSD and older Linux systems. + +Version 1.0.13 has some code cleanup, a fix for the Debian rc install, +and a security fix (contributed by Olaf Kirch): when changing +user and group id, pdnsd did not drop supplementary group IDs that +the original user was member of. + +Version 1.0.12 is a bugfix release and contains some security +enhancements. There are also inclusion/exclusion lists for servers +(new options include=, exclude=, policy= in the server +section). + +Version 1.0.11 fixes two bugs that might be used for denial-of-service +attacks, upgrading is recommended. + +Versions 1.0.9 and 1.0.10 are bugfix releases. + +Version 1.0.8 introduces special linux ppp device support contributed +by Ron Yorston, and has some bugfixes. + +Version 1.0.7 introduces autoconf support, many new config file options and +the new pdnsd-ctl run-time configuration program. + +Version 1.0.6 has another set of bugfixes, in addition to higher compile- +time configurability and UDP query support. It also contains Debian rc +scripts contributed by Markus Mohr. + +Version 1.0.5 has some bugfixes and the new "server_ip" option +contributed by Wolfgang Ocker. + +Version 1.0.4 introduces the new options run_as, strict_setuid and +paranoid. These new options are optional security enhancements. + +Versions 1.0.1, 1.0.2 and 1.0.3 are bugfix releases. + +Version 1.0.0 has a lot of changes compared to the 0.9.x tree, but much of +them "under the hood": +- IPv6 support (experimental; compile- and run-time configurable) +- FreeBSD (and such hopefully *BSD) support +- better rfc2181 compatability +- new options: + - serve_aliases in source section + - linkdown_kluge in global section + - max_ttl in global section +- cache-code reorganization, only one unified hash (of variable depth) +- Optimizations & cleanups +- Automatic deps (only interesting for developers ;-) + +Version 0.9.11 fixes a locally exploitable security hole (the cache file was +world writeable by default). Please see ChangeLog.old for details. + +Version 0.9.10 fixes some bugs and improves build on Red Hat. + +Version 0.9.9 contains the rc scripts for Red Hat Linux contributed by Torben +Janssen, in addition to code cleanups and bugfixes. +The meaning of the option -v has changed in this release. +There is also a new config file option "lean_query" that is on by default. It +is an optimization, so please look in the docs when updating whether you want +it switched on or not. + +When compiling versions after 0.9.8, you will probably get more +compiler warningsthan before. This is because the C compiler settings +have been made stricter. + +Version 0.9.8 fixes a minor bug some build problems with glibc2.0 systems. + +The versions 0.9.6 and 0.9.7 are bugfix releases. + +Version 0.9.5 introduces uptest=exec, and a modified config file syntax (cache +sizes are now specified in kB). + +Version 0.9.4 was the first to be released to the public. For information on +changes, see ChangeLog. + diff --git a/service/src/main/jni/pdnsd/PKGBUILD.in b/service/src/main/jni/pdnsd/PKGBUILD.in new file mode 100644 index 0000000..3d61765 --- /dev/null +++ b/service/src/main/jni/pdnsd/PKGBUILD.in @@ -0,0 +1,24 @@ +# Package build script for Arch Linux, +# contributed by Alexander Drozdov. + +pkgname=@PACKAGE@ +pkgver=@VERSION@ +pkgrel=@packagerelease@ +pkgdesc="pdnsd is a proxy DNS server with permanent caching (the cache contents are written to hard disk on exit) that is designed to cope with unreachable or down DNS servers." +url="http://members.home.nl/p.a.rombouts/pdnsd.html" +license="GPLv3" +depends=() +makedepends=(glibc) +conflicts=() +replaces=() +backup=() +install= +source=(http://members.home.nl/p.a.rombouts/pdnsd/releases/$pkgname-$pkgver-$pkgrel.tar.gz) +md5sums=() + +build() { + cd $startdir/src/$pkgname-$pkgver + ./configure --prefix=/usr --enable-ipv6 --sysconfdir=/etc --with-distribution=ArchLinux + make || return 1 + make DESTDIR=$startdir/pkg install +} diff --git a/service/src/main/jni/pdnsd/README b/service/src/main/jni/pdnsd/README new file mode 100644 index 0000000..7042e93 --- /dev/null +++ b/service/src/main/jni/pdnsd/README @@ -0,0 +1,22 @@ +You can find the documentation for pdnsd in the doc/ directory. The html +documentation (which I recommend) is in the doc/html/ subdirectory. +The pure text documentation (which is generated automatically from the +html documentation) is in doc/txt/. +The following documents are available: + +index.html / intro.txt Overview, system requirements +doc.html / manual.txt Building, installation and usage instructions +faq.html / faq.txt The FAQ + +Share and enjoy! + Thomas + + +For news about recent changes in pdnsd the following files may be of +interest to you: + + README.par + ChangeLog + NEWS + +Last revised: 08 July 2007 by Paul Rombouts diff --git a/service/src/main/jni/pdnsd/README.par b/service/src/main/jni/pdnsd/README.par new file mode 100644 index 0000000..ea181bc --- /dev/null +++ b/service/src/main/jni/pdnsd/README.par @@ -0,0 +1,216 @@ + pdnsd version 1.2.9a by Paul Rombouts + ===================================== + +This file describes the version of pdnsd that I maintain personally and am +making available so other people can enjoy the latest features and fixes. Thomas +Moestl no longer maintains pdnsd himself, so I am effectively the new +maintainer. This README describes the new features in version 1.2. This version +has a rather large number of internal changes and also some new features, which +I am rather pleased with, even if I say so myself. I think the changes are +significant enough to warrant increasing the minor version number from 1.1 to +1.2. The differences between my previous "official" release 1.1.11 and Thomas' +last release 1.1.7a are described in my previous README, which I have renamed +REAME.par.old. In this README I restrict myself to describing changes between +1.1.11 and 1.2. +The main difference between versions 1.2 and 1.2.1, aside from some minor +changes, is that 1.2.1 has (experimental) support for the Cygwin platform. +Version 1.2.2 has further improvements in portability and should in +particular now also compile on the Darwin (Apple Mac OS X) platform. +Version 1.2.4 has some important fixes for a memory leak, a minor buffer- +overflow problem and some situations which could cause pdnsd to exit +prematurely. Note that TCP-query support is now compiled in by default, but can +still be disabled using a configure option. +The main new feature of version 1.2.5 is the new query method "udp_tcp". +Version 1.2.6 has an updated license: GPL version 3. The main new feature of +version 1.2.6 is the "reject" option, which makes it possible to censor or +correct for unwanted IP addresses in replies. +Version 1.2.7 contains an important fix for a "dangling pointer" bug and +attempts to make pdnsd less vulnerable to the issues raised in CERT +vulnerability note VU#800113. It also contains some improvements for defining +local records interactively using the pdnsd-ctl utility. +The main new feature of version 1.2.8 is automatic discovery of root servers, +as well as some minor improvements in the resolver. +Version 1.2.9 among other things supports many addtional RR types, uses data +structures that should be more slightly more memory efficient and has support +for EDNS, which allows DNS UDP messages to be larger than 512 bytes. +Version 1.2.9a is a simple bugfix release that fixes a problem with compiling +1.2.9 after configuring with --enable-strict-rfc2181. Unless you use this option +to compile pdnsd, there is no need to upgrade from 1.2.9 to 1.2.9a. + +For instructions how to compile and install pdnsd see doc/html/doc.html or +doc/txt/manual.txt. Note that I am no longer distributing a patch w.r.t. Thomas' +version because the (compressed) patch file is barely smaller than the +(compressed) tar archive. + +Here follows a list of some of changes in version 1.2 from a user's perspective. +For a more technical description of some of the changes in the code see the ChangeLog. +For a short history about recent releases have a look at NEWS or doc/html/index.html. + +- First of all, two potentially rather nasty bugs have been fixed in the code + for the handling of NXT and NAPTR records. A response from a remote server + containing NXT records (even well-formed ones) will very likely cause pdnsd to + crash. The code for handling NAPTR records contained incorrect ASSERT + statements, which could cause pdnsd to abort in a controlled fashion, but + completely unnecessarily. + +- Sampo Lehtinen has remarked that pdnsd sometimes failed to resolve classless + reversed-delegated IP addresses, and that this has something to do with the + fact that pdnsd didn't accept '/' characters in domain names. After reading + some of the relevant RFCs I decided to remove all restrictions on the types + of characters that pdnsd accepts in domain names. Of course for most + applications, there are many characters which don't make sense in domain + names, but I feel that it is the responsibility of the client application to + reject these, not the proxy server. + +- At the suggestion of Dan Tihelka, I have expanded to the server_ip= option to + allow the name of an interface to be specified instead of an IP address. + Presently this has been tested on Linux only. Can someone running pdnsd on + *BSD tell me if the code for getting the address of an interface is different + for Linux and BSD-type systems? + +- At the suggestion of Juliusz Chroboczek I've added an new server availability + test which can be specified with uptest=query. This can be useful as an + alternative to "uptest=ping" in case the remote server does not respond to + ICMP_ECHO requests at all, which unfortunately is quite common these days. + "uptest=query" causes pdnsd to send an empty query to remote name servers. Any + well-formed response (apart from SERVFAIL) within the timeout period will be + interpreted as a sign that the server is "up". + +- Instead of specifying the IP addresses of the name servers that pdnsd should + query in a server section of the config file, you may also specify a + resolv.conf-style file. Preferably this should not be /etc/resolv.conf. If the + contents of the resolv.conf type file changes while pdnsd is running, you can + make pdnsd aware of the changes with the "pdnsd-ctl config" command, see + below. Example: + + server { + label=myisp; + file=/etc/ppp/resolv.conf; + timeout=10; + } + +- There is a new option for "server" sections in the config file: + root_server=on/off. + In case a server section contains only addresses of root servers, which + usually only give the name servers of top level domains in their reply, + setting root_server=on will enable certain optimizations. This involves using + cached information to reduce queries to the root servers, thus speeding up the + resolving of new names. + +- New option for "rr" sections in the config file: reverse=on/off. + If you want a locally defined name to resolve to a numeric address and vice + versa, you can now achieve this by setting reverse=on before defining the A + record, making it unnecessary to define a separate PTR record for the reverse + resolving. + Example: + + rr { + name = localhost; + reverse = on; + a = 127.0.0.1; + } + + has the same effect as: + + rr { + name = localhost; + a = 127.0.0.1; + } + rr { + name = 1.0.0.127.in-addr.arpa; + ptr = localhost; + } + +- In rr sections it is now possible to specify a wildcard name, i.e. a name + starting with the label *. The * in a wildcard can match one or more labels in + a queried name, but only whole labels. For example, *.mydomain will match + a.mydomain or www.a.mydomain, but not mydomain. Before you can specify an rr + section with name=*.mydomain you must define some records for mydomain, + typically NS and/or SOA records. + Example: + + rr { + name = mydomain; + ns = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = *.mydomain; + a = 192.168.1.10; + } + +- There is a slight backwards compatibility problem which involves the name= and + owner= options in rr sections. The new version does not allow you to place + owner= before name=. On the other hand, you may now freely mix the owner + option with the a,ptr,cname,mx and soa options and define as many records of + this type as you like (including zero). + +- pdnsd-ctl has three new commands: + + config: Reloads pdnsd's configuration file. This is more efficient than + restarting pdnsd, and should not cause only noticeable interruption in DNS + service. However, some types of configuration changes cannot be put into + effect this way, and you will be prompted to restart pdnsd instead. + + empty-cache: Empties the cache completely, freeing all existing entries. + In version 1.2.3 you can specify a selection of entries to delete by providing + a list of include/exclude patterns. + + dump: Prints information about all the names stored in the cached. This is + mainly useful for diagnostic purposes. + +- There is now a pdnsd.conf(5) man page, describing pdnsd's configuration file. + The man page has been generated from the html documentation using a customized + Perl script. + +- New in version 1.2.4: Time intervals in the configuration files can now be + expressed in seconds, minutes, hours, days and weeks, using the suffixes + s,m,h,d,and w. + Example: 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds. + +- Version 1.2.5 introduces a new configuration option, contributed by Jan-Marek + Glogowski, called "use_nss" which can be turned off to prevent nasty delays in + certain situations. + Besides the query methods "udp_only", "tcp_only" and "tcp_udp" you can now + also specify "udp_tcp", which more closely adheres to the behaviour + recommended by DNS standards. + +- Version 1.2.6 introduces the "randomize_servers" and "reject" options. By + setting "randomize_servers" on you can give each server in a server section an + equal chance of being queried, which is useful when resolving from root + servers, for instance. The "reject" option can be used to censor certain IP + addresses or correct for unwanted replies from servers you don't completely + trust. + +- Version 1.2.7 contains support for "include" files which can be referenced + from configuration files or read interactively using pdnsd-ctl. These files + can be used to add local definitions to the cache without reconfiguring pdnsd. + The new "pdnsd-ctl eval" command can be used to interactively define local + records that could previously only be defined in configuration files but not + with the "pdnsd-ctl add" command. + +- Version 1.2.8 contains support for automatic discovery of root servers. + Instead of supplying a complete list of IP addresses of root servers in a + server section of the configuration file, you need only enter one or two + addresses of name servers which know the names and addresses of the root + servers and set "root_server=discover". + The "neg_rrs_pol" option has a new default setting, which should allow + sensible negative caching of RRs in most situations, even if "proxy_only=on". + +- Version 1.2.9 contains support for EDNS (Extension mechanisms for DNS), which + allows UDP messages to be larger than 512 bytes. Whether pdnsd uses EDNS in + outgoing queries is determined by the configuration option "edns_query". If + pdnsd receives a query using EDNS, it will reply using EDNS regardless of the + configuration settings. + Local TXT records can now be defined in the configuration file. + If the query uptest fails due to remote servers ignoring empty queries, this + can now be remedied using the new "query_test_name" config option. + +The new features are described in greater detail in the manual doc/html/doc.html +or doc/txt/manual.txt. + +Enjoy! + +If you have any questions about my version of pdnsd, you can send these +to . Questions about the original (unmaintained) pdnsd +version should be sent to or . diff --git a/service/src/main/jni/pdnsd/README.par.old b/service/src/main/jni/pdnsd/README.par.old new file mode 100644 index 0000000..4bf0eda --- /dev/null +++ b/service/src/main/jni/pdnsd/README.par.old @@ -0,0 +1,249 @@ + pdnsd maintenance version 1.1.11-par by Paul Rombouts + ======================================================= + +This file describes the version of pdnsd that I maintain personally and am +making available so other people can enjoy the latest features and fixes. +Thomas Moestl no longer maintains pdnsd himself, so I am effectively the new +maintainer. The current version is 1.1.11-par, which has a rather large number +of small changes. Among the bugs fixed are a race condition in the cache lookup +code, a flaw in the code that caused a busy spin when a remote server answered +with "Not Implemented", and problems with the -4 and -6 command-line options. +Among the improvements are an alternative sorting algorithm which should allow +pdnsd to start up faster when reading a large cache file from disk, automatic +mapping of IPv4 to IPv6 addresses when running in IPv6 mode, somewhat more +efficient memory use, and better compression of the replies. Some of the new +features are described in the second half of this file (look for "new in version +1.1.11"). For the rest of the changes I will have to refer you to the ChangeLog. +For a short history about recent releases have a look at doc/html/index.html. + +Since version 1.1.9 I've added some missing pieces to the documentation (the +manual doc/html/doc.html,doc/txt/manual.txt and the man page doc/pdnsd-ctl.8). +Version 1.1.11 finally has a man page doc/pdnsd.8, thanks to a contribution by +Mahesh T. Pai. + +The first part of this file describes how to patch, compile, install and run +pdnsd. The second part describes some of the changes I've made to Thomas +Moestl's code. + +Unless you're using the pre-patched source archive pdnsd-1.1.11-par.tar.gz you +must first apply my patch file pdnsd-1.1.11-par.diff.gz before compiling and +installing pdnsd according to Thomas Moestl's instructions described in the the +documentation. Use a freshly untarred copy of Thomas Moestl's original version +1.1.7a source, cd into the source directory pdnsd-1.1.7a and apply the command: + +gzip -cd /pdnsd-1.1.11-par.diff.gz | patch -p2 -N -Z + +Note: I have used GNU extensions so there may be some portability issues. I have +supplied alternatives for some of the less portable functions. There should be no +problem with most Linux distributions. + +That's it! You should now be able to compile, install and run pdnsd. See the +documentation in doc/html/doc.html or doc/txt/manual.txt for more detailed +instructions. + +Some people may want change the compiler optimization flag. I use the -O2 flag, +but it might be safer to use a lower level of optimization or no optimization at +all. In that case prefix the configure command with the desired compiler flags +like this (assuming you're using a bash shell): + + CFLAGS="-O1 -g -Wall" ./configure ... + +I have added a new configuration option "--with-thread-lib=", which you +should use if you experience problems with signal handling under Linux. The +usual symptom is failure by pdnsd to save the cache to disk, and +/var/cache/pdnsd/pdnsd.cache remaining empty. If you experience this kind of +trouble, try reconfiguring with different values for the --with-thread-lib +option. The allowable values are "linuxthreads" (the default), "linuxthreads2" +(or "lt2" for short), and "nptl". I recommend that you first configure and +compile without the --with-thread-lib option, then if you experience trouble try +again with --with-thread-lib=lt2 and recompile. +If your Linux system has an implementation of the Native POSIX Thread Library, +which is the case with Red Hat 9 for instance, you should use +--with-thread-lib=nptl . +Ideally, I would like to write a configure script which automatically detects +which kind of thread library is being used on a Linux system, but I don't have a +clue yet how to do this. If you can help me with this please write to me at the +email address listed at the end of this file. + +The rest of this file describes some of the modifications I've made, but you +don't have to read it if you simply want to run pdnsd as you're used to. + + +- The main new feature I've added enables you to change the server addresses + that pdnsd uses at run-time using pdnsd-ctl. I've done this because the ISPs I + use do not specify fixed DNS server addresses, but expect their clients to use + dynamic DNS configuration (DHCP in the case of the cable connection, RFC1877 + in case of isdn). I've extended the options that can be given with the + "server" command to pdnsd-ctl, to allow IP addresses to be specified as an + additional argument after "up|down|retest". This allows me to put something + like this in my ifup-local script: + + pdnsd-ctl server isp-label up "$DNS1 $DNS2" + + For more details how to use pdnsd-ctl read the updated documentation in + the doc/html directory. There is also a manpage for pdnsd-ctl. + This was quite tricky to implement because there might be pending queries + while the addresses are being changed. It certainly was an interesting + exercise in writing multi-threaded code for me. + + +- I've implemented a feature which allowed me to specify multiple IP addresses + per server section in the configuration file. This allowed for a much more + compact configuration file (3 server sections instead of 7 in my case), + because most configuration options are identical for servers belonging to the + same ISP. It also made the output of "pdnsd-ctl status" more compact. And it + was necessary to enable a satisfactory implementation of the previous feature. + Example of the new syntax: + + ip = 123.456.789.001, 123.456.789.002, 123.456.789.003; + + At the suggestion of Greg Norris server sections no longer have to specify IP + addresses. A server section without IP addresses will remain inactive until it + is assigned one more addresses at run-time with pdnsd-ctl. + +- I've changed the implementation of dynamic arrays to make it slightly more + efficient, and improve type safety. I also got rid of several arrays of fixed + size in favor of dynamically allocated arrays. In particular, I got rid of + all occurrences of MAXPATH. I also made several static variables "automatic". + +- The output of the "status" command of pdnsd-ctl now gives more meaningful + constant names "ping|none|if|exec" instead of numbers for the "uptest" option. + I've also added some information that was previously missing. + +- I've fixed I a problem that caused pdnsd to use up a lot of CPU time and slow + down my system considerably when it received a query that took a long time to + resolve. It turned out that pdnsd can get into a "busy spin" when one of the + DNS servers pdnsd is querying refuses the connection. Apart from fixing this + bug, to speed things up additionally, I thought it would be a good idea to + mark a server down (without retesting it) after detecting errno==ECONNREFUSED. + This gives me very satisfactory performance, with the problematic server being + tried only once during every testing interval. + + New in version 1.1.11: An additional busy spin condition, triggered when a + remote server answers with "Not Implemented", has been discovered and fixed. + In case there are remaining bugs in the multiplexing code, I've added a test + that checks if the number of events reported by poll/select matches the number + of events handled by pdnsd. If not, pdnsd will log an error message and give + up. Although the bugs still need to be reported and fixed, at least this + should prevent pdnsd from wasting CPU cycles. + +- Due to a bug in Thomas' code, pdnsd tries, but fails, to remove the control + socket "pdnsd.status" before exiting. This has also been fixed. In version + 1.1.8b1-par6 I have cleaned this up some more so that pdnsd will handle + situations where it can't open or bind the control socket more gracefully. + +- I've rewritten some of the code that saves the contents of the cache to the + file "pdnsd.cache" just before pdnsd exits. This is because I noticed in my + logfiles that pdnsd occasionally had problems reading this file back at + startup. I eliminated the use of fseek() in Thomas' code. I could not find + anything that was demonstrably incorrect about his use of fseek(), but it + seemed better to me to do without it and write the file in a strictly + sequential order. Anyway, it turned out my hunch paid off: no more error + messages about "pdnsd.cache" in my logfile. + + New in version 1.1.11: I've added some new code for sorting the queue used for + purging stale cache entries. This should allow pdnsd to start up faster when + reading large cache files from disk. + +- I've extended the configuration options for policies of inclusion/exclusion + lists in server sections. The new policies options are "simple_only" and + "fqdn_only". Setting policy=simple_only will cause the server to used only for + simple hostnames if no other rule matches. On the other hand, setting + policy=fqdn_only will cause the server to be used only for fully qualified + domain names (i.e. the name has at least one dot in-between). I find these + options useful for controlling which name servers (if any) will be used by + pdnsd for simple host names. + +- I've added a new "delegation_only" option that can be used to undo the + unwanted effects of DNS "wildcards". It works roughly as the feature by the + same name in BIND. It is turned off by default. To block Verisign's + Sitefinder, add the following line to the global section of the configuration + file: + + delegation_only= com, net; + + If you find that this feature blocks some legitimate domain names, you will + probably need to add the address of a nameserver that provides good authority + information. More information can be found at + http://www.phys.uu.nl/~rombouts/pdnsd/delegationonly.html + +- It is no longer mandatory that domain names in the configuration file end in a + dot. + +- The parser for configuration files has been rewritten purely in C, so (f)lex + and yacc/bison are no longer needed to build pdnsd. + It is no longer necessary to place strings between quotes in the configuration + file, unless a string contains a special character such as whitespace, a token + that normally starts a comment, or one of the characters ",;{}". Note that + these special characters are illegal in domain names anyway. + +- New in version 1.1.11: Negating whole domains with a neg section in the + config file will result in all the subdomains being negated as well. + For example, adding the lines + + neg {name=doubleclick.com; types=domain;} + neg {name=doubleclick.net; types=domain;} + + will also negate ad.doubleclick.com, ad.fr.doubleclick.net, etc. + +- New in version 1.1.11: When running in IPv6 mode, pdnsd will now automatically + map any IPv4 addresses it reads in the config file to IPv6 addresses. + When pdnsd has been compiled with IPv6 support and runs in IPv4 mode, it will + skip IPv6 addresses with a warning message. This may result in certain server + sections becoming inactive, though. + + The -4 and -6 options should now work as advertised. + I've added two new command-line options, "-a" and "-i ". + With the -a flag pdnsd will try to detect automatically if IPv6 support is + available on a system, and fall back to IPv4 if not. The -a flag can be used + instead of -4 or -6. + The -i option can be used to specify a prefix for mapping IPv4 to IPv6 + address. The default is ::ffff.0.0.0.0. There is also a corresponding + ipv4_6_prefix= option for the config file. + +- New in version 1.1.11: I've slightly changed the way pdnsd does parallel + queries. Active queries or not canceled until we have received a useful + response from a remote name server, or all the queries have failed or timed + out. Thus the par_queries parameter is no longer the maximum number of + parallel queries, but rather the increment with which the number of parallel + queries is increased when the previous set has timed out. In the worst case + there will be pending queries to all the servers in the list of available + servers simultaneously. We may be wasting more system resources this way, but + the advantage is that we have a greater chance of catching a reply. After all, + if we wait longer anyway, why not for more servers. + I've also introduced a global timeout parameter. This is the minimum period of + time pdnsd will wait after sending the first query to a remote server before + giving up without having received a reply. The timeout options in the + configuration file are now only minimum timeout intervals. Setting the global + timeout option makes it possible to specify quite short timeout intervals in + the server sections. This will have the effect that pdnsd will start querying + additional servers fairly quickly if the first servers are slow to respond + (but will still continue to listen for responses from the first ones). This + may allow pdnsd to get an answer more quickly in certain situations. + + After receiving a reply from a remote server the server is marked up and its + time stamp is updated. This will have the effect that pdnsd doesn't bother + testing this server for availability for a period of time, and thus the + overhead caused by testing is reduced. After server timeouts, uptests are + performed by the separate server status thread, not by threads that have to + answer queries. Unresponsive servers with uptest=ping will not be marked down + immediately any more, but only after the ping test has definitely failed. + +I've also included a number of bug-fixes contained in a patch file supplied to +me by Thomas Moestl. In addition to the things I had already fixed, the +following issues are addressed: some memory leaks, dropping of root privileges +before calling uptest scripts in case pdnsd was started setuid root (which is a +bad idea anyway), passing on open fd's to uptests, integer overruns in the +status reporting code, fixing string passing from the lexer, more consistent +treatment of underscores in domain names. + +In addition to the things I've listed above, I've made various little changes to +fix minor bugs, improve efficiency or elegance, or simply to suit my my own +coding style. These changes are too numerous to list here, but some of them are +listed in the ChangeLog. Of course if you are really interested in the +nitty-gritty you can always compare the source of my version with Thomas' +original code. + +If you have any questions about the modifications I've made, you can send these +to . Questions about the original pdnsd version should +be sent to or . diff --git a/service/src/main/jni/pdnsd/THANKS b/service/src/main/jni/pdnsd/THANKS new file mode 100644 index 0000000..9c7e3bb --- /dev/null +++ b/service/src/main/jni/pdnsd/THANKS @@ -0,0 +1,66 @@ +This is a (hopefully complete) list of people I have to thank for helping me +develop and improve pdnsd: + +David G. Andersen +Andrew M. Bishop +Daniel Black +Carsten Block +Stephan Boettcher +P.J. Bostley +Rodney Brown +Kevin A. Burton +Juliusz Chroboczek +Joachim Dorner +Frank Elsner +Christian Engstler +Stefan Erhardt +Bjoern Fischer +Stefan Frster +Bert Frederiks +Mike Hammer +Jonathan Hudson +Torben Janssen +Byrial Jensen +Olaf Kirch +Nikola Kotur +Kiyo Kelvin Lee +Bernd Leibing +Patrick Loschmidt +James MacLean +Sourav K. Mandal +Fraser McCrossan +Markus Mohr +Michael Mller +Gustavo Niemeyer +Alexandre Nunes +Wolfgang Ocker +Mahesh T. Pai +Bernhard Pelz +Soenke J. Peters +Erich Reitz +Paul A. Rombouts +Brian Schroeder +Roman Shterenzon +Daniel Smolik +Milan P. Stanic +Michael Steiner +Norbert Steinl +Andreas Steinmetz +Marko Stolle +Markus Storm +Michael Strder +Thomas Stromberg +Alan Swanson +Lyonel Vincent +Eelco Vriezekolk +Paul Wagland +Sverker Wiberg +Michael Wiedmann +Ron Yorston +Nikita V. Youshchenko +Jan-Marek Glogowski +Thomas Cort +Pierre Habouzit +Dirk Armbrust +Georg Schwarz +Ashish Shukla diff --git a/service/src/main/jni/pdnsd/TODO b/service/src/main/jni/pdnsd/TODO new file mode 100644 index 0000000..f6a1650 --- /dev/null +++ b/service/src/main/jni/pdnsd/TODO @@ -0,0 +1,20 @@ +- Implement a reference counter to ensure that newly entered records are not + purged immediately (really needed?) +- Perhaps do a two-step form of recursive query: first query those servers we + have got cached, then (if unsuccessful) look the others up and query again. + The impact of this optimisation may not be very big, because all sane servers + give A records for NS records if possible. +- Test for compatibility on other Unix-like Systems other than the BSDs and + Linux; rewrite the functions in netdev.c and icmp.c for those OSs if + necessary. Also try to get compatibility for other compilers than gcc. +- Write an install rule for the Slackware start-up script. +- Update the FAQ. +- Implement DNSSEC support. Since version 1.2.9, pdnsd is able to cache the RR + types necessary for DNSSEC, but the resolver is not yet security aware. +- Implement a lookup table (hash table) for queries in progress. This would + enable a thread that is resolving a query that is already being handled by + another thread to wait for that other thread to finish and copy its result + rather than independently query remote servers. It is very common for + resolvers to resend UDP queries if they don't get a reply within a timeout + period and if the answer is not yet cached, this will result in multiple + threads duplicating each others work in the current implementation. diff --git a/service/src/main/jni/pdnsd/acconfig.h b/service/src/main/jni/pdnsd/acconfig.h new file mode 100644 index 0000000..82b3f6e --- /dev/null +++ b/service/src/main/jni/pdnsd/acconfig.h @@ -0,0 +1,187 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* ONLY EDIT acconfig.h, NEVER config.h or config.h.in! + * config.h MAY BE OVERWRITTEN BY make, config.h.in by autoheader! */ + +/* Define your Target here. Currently defined are TARGET_LINUX (any + * architecture), TARGET_BSD (experimental; tested on FreeBSD, hopefully + * works for other BSD variants) and TARGET_CYGWIN. */ +#define TARGET TARGET_LINUX + +/* change the #undef to #define if you do not want to compile with special + * ISDN support for Linux. Note that the ISDN support will not compile ok on + * unpatched kernerls earlier than 2.2.12 (if you did apply newer isdn patches, + * it may work fine). This is not on by default because it will cause compile + * problems on some systems */ +#undef ISDN_SUPPORT + +/* The following regulates the IP Protocol support. Supported types are IPv4 + * and IPv6 (aka IPng). You may enable either or both of these protocols. + * Enabling in this context means that support for the respective protocol + * will be in the binary. When running the binary, one of the protocols may + * be activated via command line switches. Note that activating both IPv4 and + * IPv6 is pointless (and will not work because two UDP and two TCP threads + * will be started that concur for ports). Because of that, it is not allowed. + * When pdnsd runs with IPv6 activated it should be able to service queries + * from IPv6 as well as from IPv4 hosts, provided that you host is configured + * properly. + * For each of the protocols there are two options: ENABLE_IPV4 and ENABLE_IPV6 + * control whether support for the respective protocol is available in the + * binary. DEFAULT_IPV4 selects which protocol is enabled on pdnsd + * startup by default. 1 means IPv4, while 0 means IPv6. If support for + * a protocol was included in the executable, you can specify command line + * parameters to activate or deactivate that protocol (the options are -4 and + * -6), but it makes more sense to use the run_ipv4=on/off option in the + * configuration file. + * Make your choice. Note that IPv6 support is experimental in pdnsd. + * In normal operation, you will currently only need IPv4. */ +#undef ENABLE_IPV4 +#define DEFAULT_IPV4 1 +#undef ENABLE_IPV6 + +/* In all pdnsd versions before 1.0.6, DNS queries were always done over + * TCP. Now, you have the choice. You can control that behaviour using + * the -m command line switch, and you can give a preset here. There + * are 3 different modes: + * UDP_ONLY: This is undoubtedly the fastest query method, because + * no TCP negotiation needs to be done. + * TCP_ONLY: This is slower than uo, but generally more secure + * against DNS spoofing. Note that some name servers on the + * internet do not support TCP queries, notably dnscache. + * TCP_UDP: TCP, then UDP. If the TCP query fails with a "connection refused"- + * error or times out, the query is retried using UDP. + * UDP_TCP: UDP, then TCP. If the UDP reply is truncated (i.e. the tc flag is set), + * the query is retried using TCP. */ +#define M_PRESET UDP_ONLY + +/* In addition to choosing the presets, you may also completely disable + * one of the protocols (TCP for preset UDP_ONLY and UDP for preset TCP_ONLY). + * This saves some executable space. */ +#undef NO_UDP_QUERIES +#undef NO_TCP_QUERIES + +/* With the following option, you can disable the TCP server functionality + * of pdnsd. Nearly no program does TCP queries, so you probably can do + * this safely and save some executable space and one thread. + * You also can turn off the TCP server at runtime with the --notcp option. */ +#undef NO_TCP_SERVER + +/* By undefining the following, you can disable the UDP source address + * discovery code. This is not recommended, but you may need it when + * running into compilation problems. */ +#undef SRC_ADDR_DISC + +/* NO_POLL specifies not to use poll(2), but select(2) instead. If you are + * unsure about what this means, just leave this as it is.*/ +#undef NO_POLL + +/* Define this for "hard" RFC 2181 compliance: this RFC states that + * implementations should discard answers whose RR sets have multiple + * different time stamps. While correct answers are generated, incorrect + * ones are normally tolerated and corrected. Full RFC compliance is + * however only achieved by deactivating this behaviour and thus being + * intolerant. */ +#undef RFC2181_ME_HARDER + +/* Define this to the device you want to use for getting random numbers. + * Leave this undefined if you wand to use the standard C library random + * function, which basically should be sufficient. + * Linux and FreeBSD have two random number devices: /dev/random and + * /dev/urandom. /dev/urandom might be less secure in some cases, but + * should still be more than sufficient. The use of /dev/random is + * discouraged, as reading from this device blocks when new random bits + * need to be gathered. */ +#undef RANDOM_DEVICE +#undef R_DEFAULT +#undef R_RANDOM +#undef R_ARC4RANDOM +/*#define RANDOM_DEVICE "/dev/urandom"*/ + +/* Designate which database manager to use for cacheing. + * default: native; others: gdbm */ +#define CACHE_DBM DBM_NATIVE + +/* This is for various debugging facilities that produce debug output and + * double-check some values. You can enable debug messages with the -g option. + * Normally, you can switch this off safely by setting the number after DEBUG + * to 0. This will increase speed (although only marginally), save space + * in the executable (only about 12kB) and some stack space per thread + * (which may be significant if you have many threads running simultaneously). + * However, it may be an aid when debugging config files. + * The only defined debug levels by now are in the range 0 - 9. + * Define this to 9 if you want hex dumps of all the queries and replies pdnsd + * receives (you must also call pdnsd with -v9 to actually see the hex dumps). + * When in doubt, leave it defined to 1. */ +#define DEBUG 1 + +/* This defines the default verbosity of informational messages you will get. + This has nothing to to with the debug option (-g), but may be set with -v + option. 0 is for normal operation, up to 3 for debugging. + Unlike the debug messages, these messages will also be written to the syslog.*/ +#define VERBOSITY 0 + +/* Redefine this if you want another hash size. + * The number of hash buckets is computed as power of two (1< sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 5 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 16 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.62])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES(OBJC)], + [define([AC_PROG_OBJC], + defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl +]) +_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl +dnl The `parallel-tests' driver may need to know about EXEEXT, so add the +dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro +dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl +]) + +dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 6 + +# AM_PROG_CC_C_O +# -------------- +# Like AC_PROG_CC_C_O, but changed for automake. +AC_DEFUN([AM_PROG_CC_C_O], +[AC_REQUIRE([AC_PROG_CC_C_O])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']` +eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o +if test "$am_t" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +dnl Make sure AC_PROG_CC is never called again, or it will override our +dnl setting of CC. +m4_define([AC_PROG_CC], + [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])]) +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 6 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check for `mkdir -p'. +AC_DEFUN([AM_PROG_MKDIR_P], +[AC_PREREQ([2.60])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, +dnl while keeping a definition of mkdir_p for backward compatibility. +dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. +dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of +dnl Makefile.ins that do not define MKDIR_P, so we do our own +dnl adjustment using top_builddir (which is defined more often than +dnl MKDIR_P). +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl +case $mkdir_p in + [[\\/$]]* | ?:[[\\/]]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 5 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + diff --git a/service/src/main/jni/pdnsd/compile b/service/src/main/jni/pdnsd/compile new file mode 100644 index 0000000..1b1d232 --- /dev/null +++ b/service/src/main/jni/pdnsd/compile @@ -0,0 +1,142 @@ +#! /bin/sh +# Wrapper for compilers which do not understand `-c -o'. + +scriptversion=2005-05-14.22 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# 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 2, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand `-c -o'. +Remove `-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file `INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; +esac + +ofile= +cfile= +eat= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as `compile cc -o foo foo.c'. + # So we strip `-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no `-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # `.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'` + +# Create the lock directory. +# Note: use `[/.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/service/src/main/jni/pdnsd/config.h b/service/src/main/jni/pdnsd/config.h new file mode 100644 index 0000000..2a0f1b9 --- /dev/null +++ b/service/src/main/jni/pdnsd/config.h @@ -0,0 +1,431 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* ONLY EDIT acconfig.h, NEVER config.h or config.h.in! + * config.h MAY BE OVERWRITTEN BY make, config.h.in by autoheader! */ + +/* Define your Target here. Currently defined are TARGET_LINUX (any + * architecture), TARGET_BSD (experimental; tested on FreeBSD, hopefully + * works for other BSD variants) and TARGET_CYGWIN. */ +#define TARGET TARGET_LINUX + +/* change the #undef to #define if you do not want to compile with special + * ISDN support for Linux. Note that the ISDN support will not compile ok on + * unpatched kernerls earlier than 2.2.12 (if you did apply newer isdn patches, + * it may work fine). This is not on by default because it will cause compile + * problems on some systems */ +/* #undef ISDN_SUPPORT */ + +/* The following regulates the IP Protocol support. Supported types are IPv4 + * and IPv6 (aka IPng). You may enable either or both of these protocols. + * Enabling in this context means that support for the respective protocol + * will be in the binary. When running the binary, one of the protocols may + * be activated via command line switches. Note that activating both IPv4 and + * IPv6 is pointless (and will not work because two UDP and two TCP threads + * will be started that concur for ports). Because of that, it is not allowed. + * When pdnsd runs with IPv6 activated it should be able to service queries + * from IPv6 as well as from IPv4 hosts, provided that you host is configured + * properly. + * For each of the protocols there are two options: ENABLE_IPV4 and ENABLE_IPV6 + * control whether support for the respective protocol is available in the + * binary. DEFAULT_IPV4 selects which protocol is enabled on pdnsd + * startup by default. 1 means IPv4, while 0 means IPv6. If support for + * a protocol was included in the executable, you can specify command line + * parameters to activate or deactivate that protocol (the options are -4 and + * -6), but it makes more sense to use the run_ipv4=on/off option in the + * configuration file. + * Make your choice. Note that IPv6 support is experimental in pdnsd. + * In normal operation, you will currently only need IPv4. */ +#define ENABLE_IPV4 1 +#define DEFAULT_IPV4 1 +#undef ENABLE_IPV6 + +/* In all pdnsd versions before 1.0.6, DNS queries were always done over + * TCP. Now, you have the choice. You can control that behaviour using + * the -m command line switch, and you can give a preset here. There + * are 3 different modes: + * UDP_ONLY: This is undoubtedly the fastest query method, because + * no TCP negotiation needs to be done. + * TCP_ONLY: This is slower than uo, but generally more secure + * against DNS spoofing. Note that some name servers on the + * internet do not support TCP queries, notably dnscache. + * TCP_UDP: TCP, then UDP. If the TCP query fails with a "connection refused"- + * error or times out, the query is retried using UDP. + * UDP_TCP: UDP, then TCP. If the UDP reply is truncated (i.e. the tc flag is set), + * the query is retried using TCP. */ +#define M_PRESET TCP_ONLY + +/* In addition to choosing the presets, you may also completely disable + * one of the protocols (TCP for preset UDP_ONLY and UDP for preset TCP_ONLY). + * This saves some executable space. */ +/* #undef NO_UDP_QUERIES */ +/* #undef NO_TCP_QUERIES */ + +/* With the following option, you can disable the TCP server functionality + * of pdnsd. Nearly no program does TCP queries, so you probably can do + * this safely and save some executable space and one thread. + * You also can turn off the TCP server at runtime with the --notcp option. */ +/* #undef NO_TCP_SERVER */ + +/* By undefining the following, you can disable the UDP source address + * discovery code. This is not recommended, but you may need it when + * running into compilation problems. */ +#define SRC_ADDR_DISC 1 + +/* NO_POLL specifies not to use poll(2), but select(2) instead. If you are + * unsure about what this means, just leave this as it is.*/ +/* #undef NO_POLL */ + +/* Define this for "hard" RFC 2181 compliance: this RFC states that + * implementations should discard answers whose RR sets have multiple + * different time stamps. While correct answers are generated, incorrect + * ones are normally tolerated and corrected. Full RFC compliance is + * however only achieved by deactivating this behaviour and thus being + * intolerant. */ +/* #undef RFC2181_ME_HARDER */ + +/* Define this to the device you want to use for getting random numbers. + * Leave this undefined if you wand to use the standard C library random + * function, which basically should be sufficient. + * Linux and FreeBSD have two random number devices: /dev/random and + * /dev/urandom. /dev/urandom might be less secure in some cases, but + * should still be more than sufficient. The use of /dev/random is + * discouraged, as reading from this device blocks when new random bits + * need to be gathered. */ +/* #undef RANDOM_DEVICE */ +#define R_DEFAULT 1 +/* #undef R_RANDOM */ +/* #undef R_ARC4RANDOM */ +/*#define RANDOM_DEVICE "/dev/urandom"*/ + +/* Designate which database manager to use for cacheing. + * default: native; others: gdbm */ +#define CACHE_DBM DBM_NATIVE + +/* This is for various debugging facilities that produce debug output and + * double-check some values. You can enable debug messages with the -g option. + * Normally, you can switch this off safely by setting the number after DEBUG + * to 0. This will increase speed (although only marginally), save space + * in the executable (only about 12kB) and some stack space per thread + * (which may be significant if you have many threads running simultaneously). + * However, it may be an aid when debugging config files. + * The only defined debug levels by now are in the range 0 - 9. + * Define this to 9 if you want hex dumps of all the queries and replies pdnsd + * receives (you must also call pdnsd with -v9 to actually see the hex dumps). + * When in doubt, leave it defined to 1. */ +#define DEBUG 1 + +/* This defines the default verbosity of informational messages you will get. + This has nothing to to with the debug option (-g), but may be set with -v + option. 0 is for normal operation, up to 3 for debugging. + Unlike the debug messages, these messages will also be written to the syslog.*/ +#define VERBOSITY 0 + +/* Redefine this if you want another hash size. + * The number of hash buckets is computed as power of two (1< and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getline' function. */ +#if defined(__aarch64__) || defined(__x86_64__) +#define HAVE_GETLINE 1 +#endif + +/* Define to 1 if you have the `getpwnam_r' function. */ +//#define HAVE_GETPWNAM_R 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define HAVE_INET_PTON 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#define HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +//#define HAVE_MEMPCPY 1 + +/* Define to 1 if you have the `mkfifo' function. */ +#define HAVE_MKFIFO 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if the system has the type `socklen_t'. */ +#define HAVE_SOCKLEN_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +#if defined(__aarch64__) || defined(__x86_64__) +#define HAVE_STPCPY 1 +#endif + +/* Define to 1 if you have the `stpncpy' function. */ +//#define HAVE_STPNCPY 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef HAVE_STRLCPY */ + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if the system has the type `struct ifreq'. */ +#define HAVE_STRUCT_IFREQ 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if the system has the type `struct in_pktinfo'. */ +#define HAVE_STRUCT_IN_PKTINFO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the `uname' function. */ +#define HAVE_UNAME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/service/src/main/jni/pdnsd/config.h.in b/service/src/main/jni/pdnsd/config.h.in new file mode 100644 index 0000000..28fb08a --- /dev/null +++ b/service/src/main/jni/pdnsd/config.h.in @@ -0,0 +1,426 @@ +/* config.h.in. Generated from configure.in by autoheader. */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* ONLY EDIT acconfig.h, NEVER config.h or config.h.in! + * config.h MAY BE OVERWRITTEN BY make, config.h.in by autoheader! */ + +/* Define your Target here. Currently defined are TARGET_LINUX (any + * architecture), TARGET_BSD (experimental; tested on FreeBSD, hopefully + * works for other BSD variants) and TARGET_CYGWIN. */ +#define TARGET TARGET_LINUX + +/* change the #undef to #define if you do not want to compile with special + * ISDN support for Linux. Note that the ISDN support will not compile ok on + * unpatched kernerls earlier than 2.2.12 (if you did apply newer isdn patches, + * it may work fine). This is not on by default because it will cause compile + * problems on some systems */ +#undef ISDN_SUPPORT + +/* The following regulates the IP Protocol support. Supported types are IPv4 + * and IPv6 (aka IPng). You may enable either or both of these protocols. + * Enabling in this context means that support for the respective protocol + * will be in the binary. When running the binary, one of the protocols may + * be activated via command line switches. Note that activating both IPv4 and + * IPv6 is pointless (and will not work because two UDP and two TCP threads + * will be started that concur for ports). Because of that, it is not allowed. + * When pdnsd runs with IPv6 activated it should be able to service queries + * from IPv6 as well as from IPv4 hosts, provided that you host is configured + * properly. + * For each of the protocols there are two options: ENABLE_IPV4 and ENABLE_IPV6 + * control whether support for the respective protocol is available in the + * binary. DEFAULT_IPV4 selects which protocol is enabled on pdnsd + * startup by default. 1 means IPv4, while 0 means IPv6. If support for + * a protocol was included in the executable, you can specify command line + * parameters to activate or deactivate that protocol (the options are -4 and + * -6), but it makes more sense to use the run_ipv4=on/off option in the + * configuration file. + * Make your choice. Note that IPv6 support is experimental in pdnsd. + * In normal operation, you will currently only need IPv4. */ +#undef ENABLE_IPV4 +#define DEFAULT_IPV4 1 +#undef ENABLE_IPV6 + +/* In all pdnsd versions before 1.0.6, DNS queries were always done over + * TCP. Now, you have the choice. You can control that behaviour using + * the -m command line switch, and you can give a preset here. There + * are 3 different modes: + * UDP_ONLY: This is undoubtedly the fastest query method, because + * no TCP negotiation needs to be done. + * TCP_ONLY: This is slower than uo, but generally more secure + * against DNS spoofing. Note that some name servers on the + * internet do not support TCP queries, notably dnscache. + * TCP_UDP: TCP, then UDP. If the TCP query fails with a "connection refused"- + * error or times out, the query is retried using UDP. + * UDP_TCP: UDP, then TCP. If the UDP reply is truncated (i.e. the tc flag is set), + * the query is retried using TCP. */ +#define M_PRESET UDP_ONLY + +/* In addition to choosing the presets, you may also completely disable + * one of the protocols (TCP for preset UDP_ONLY and UDP for preset TCP_ONLY). + * This saves some executable space. */ +#undef NO_UDP_QUERIES +#undef NO_TCP_QUERIES + +/* With the following option, you can disable the TCP server functionality + * of pdnsd. Nearly no program does TCP queries, so you probably can do + * this safely and save some executable space and one thread. + * You also can turn off the TCP server at runtime with the --notcp option. */ +#undef NO_TCP_SERVER + +/* By undefining the following, you can disable the UDP source address + * discovery code. This is not recommended, but you may need it when + * running into compilation problems. */ +#undef SRC_ADDR_DISC + +/* NO_POLL specifies not to use poll(2), but select(2) instead. If you are + * unsure about what this means, just leave this as it is.*/ +#undef NO_POLL + +/* Define this for "hard" RFC 2181 compliance: this RFC states that + * implementations should discard answers whose RR sets have multiple + * different time stamps. While correct answers are generated, incorrect + * ones are normally tolerated and corrected. Full RFC compliance is + * however only achieved by deactivating this behaviour and thus being + * intolerant. */ +#undef RFC2181_ME_HARDER + +/* Define this to the device you want to use for getting random numbers. + * Leave this undefined if you wand to use the standard C library random + * function, which basically should be sufficient. + * Linux and FreeBSD have two random number devices: /dev/random and + * /dev/urandom. /dev/urandom might be less secure in some cases, but + * should still be more than sufficient. The use of /dev/random is + * discouraged, as reading from this device blocks when new random bits + * need to be gathered. */ +#undef RANDOM_DEVICE +#undef R_DEFAULT +#undef R_RANDOM +#undef R_ARC4RANDOM +/*#define RANDOM_DEVICE "/dev/urandom"*/ + +/* Designate which database manager to use for cacheing. + * default: native; others: gdbm */ +#define CACHE_DBM DBM_NATIVE + +/* This is for various debugging facilities that produce debug output and + * double-check some values. You can enable debug messages with the -g option. + * Normally, you can switch this off safely by setting the number after DEBUG + * to 0. This will increase speed (although only marginally), save space + * in the executable (only about 12kB) and some stack space per thread + * (which may be significant if you have many threads running simultaneously). + * However, it may be an aid when debugging config files. + * The only defined debug levels by now are in the range 0 - 9. + * Define this to 9 if you want hex dumps of all the queries and replies pdnsd + * receives (you must also call pdnsd with -v9 to actually see the hex dumps). + * When in doubt, leave it defined to 1. */ +#define DEBUG 1 + +/* This defines the default verbosity of informational messages you will get. + This has nothing to to with the debug option (-g), but may be set with -v + option. 0 is for normal operation, up to 3 for debugging. + Unlike the debug messages, these messages will also be written to the syslog.*/ +#define VERBOSITY 0 + +/* Redefine this if you want another hash size. + * The number of hash buckets is computed as power of two (1< and it should be used (not on Ultrix). + */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the `asprintf' function. */ +#undef HAVE_ASPRINTF + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `getline' function. */ +#undef HAVE_GETLINE + +/* Define to 1 if you have the `getpwnam_r' function. */ +#undef HAVE_GETPWNAM_R + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#undef HAVE_LIBPTHREAD + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mempcpy' function. */ +#undef HAVE_MEMPCPY + +/* Define to 1 if you have the `mkfifo' function. */ +#undef HAVE_MKFIFO + +/* Define to 1 if you have the `nanosleep' function. */ +#undef HAVE_NANOSLEEP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if the system has the type `socklen_t'. */ +#undef HAVE_SOCKLEN_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `stpncpy' function. */ +#undef HAVE_STPNCPY + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if the system has the type `struct ifreq'. */ +#undef HAVE_STRUCT_IFREQ + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#undef HAVE_STRUCT_IN6_ADDR + +/* Define to 1 if the system has the type `struct in_pktinfo'. */ +#undef HAVE_STRUCT_IN_PKTINFO + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +#undef STACK_DIRECTION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/service/src/main/jni/pdnsd/configure b/service/src/main/jni/pdnsd/configure new file mode 100644 index 0000000..acdb4b0 --- /dev/null +++ b/service/src/main/jni/pdnsd/configure @@ -0,0 +1,11378 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.63. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell bug-autoconf@gnu.org about your system, + echo including any error possibly output before this message. + echo This can help us improve future autoconf versions. + echo Configuration will now proceed without shell functions. +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="src" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +ALLOCA +thread_CFLAGS +RANLIB +threadlib +specbuild +def_id +cachedir +distribution +packagerelease +fullversion +EGREP +GREP +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_dependency_tracking +with_distribution +with_target +with_cachedir +enable_isdn +enable_ipv4 +enable_ipv6 +enable_ipv4_startup +enable_ipv6_startup +enable_udp_queries +enable_tcp_queries +with_query_method +enable_tcp_server +enable_src_addr_disc +enable_socket_locking +enable_poll +enable_new_rrs +enable_strict_rfc2181 +with_random_device +enable_underscores +with_default_id +with_debug +with_verbosity +with_hash_buckets +enable_hash_debug +enable_rcsids +with_tcp_qtimeout +enable_tcp_subseq +with_par_queries +with_max_nameserver_ips +enable_specbuild +with_thread_lib +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { $as_echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { $as_echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + { (exit 1); exit 1; }; } ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { $as_echo "$as_me: error: working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --enable-isdn Enable ISDN support (may cause problems on + some systems; only for Linux) + --disable-ipv4 Disable IPv4 networking support + (default=enabled) + --enable-ipv6 Enable IPv6 networking support + --disable-ipv4-startup Disable IPv4 on pdnsd startup by default + (default=enabled) + --enable-ipv6-startup Enable IPV6 on pdnsd startup by default + (default=IPv4) + --disable-udp-queries Disable udp as query method. + --disable-tcp-queries Disable tcp as query method. + --disable-tcp-server Disable the TCP serving ability of pdnsd + --disable-src-addr-disc Disable the UDP source address discovery + --enable-socket-locking Enable the UDP socket locking + --disable-poll Disable poll(2) and use select(2) + (default=enabled) + --disable-new-rrs Disable new DNS RR types (obsolete, currently ignored) + --enable-strict-rfc2181 Enforce strict RFC 2181 compliance + --enable-underscores Allow _ in domain names (obsolete, currently ignored) + --enable-hash-debug Debug hash tables (warning: massive output) + --enable-rcsids Enable RCS IDs in executables (obsolete, currently ignored) + --enable-tcp-subseq Enable multiple dns querys using one + tcp connection + --enable-specbuild Only used when building pdnsd from spec files + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-distribution=distro Specify target distribution (default=Generic; + others: RedHat, SuSE, Debian, ArchLinux) + --with-target=platform Change compilation target platform (default: + autodetect; others: Linux, BSD, Cygwin) + --with-cachedir=dir Default directory for pdnsd cache + (default=/var/cache/pdnsd) + --with-query-method=qm Specify the query method (default=udponly; + others: tcponly, tcpudp, udptcp) + --with-random-device=device Specify random device other than + /dev/random; default: C Library random() PRNG; + special value arc4random for BSD C Library + arc4random function (default on FreeBSD) + --with-default-id=id Specify default uid/gid for pdnsd + (default=nobody) + --with-debug=level Specify debugging level (0 means no debug support) + --with-verbosity=level Specify default message verbosity + --with-hash-buckets=num Number of hash buckets to use (default=1024). + The number actually used is the smallest power of two + greater or equal to the number specified here. + --with-tcp-qtimeout=secs Specify default tcp query timeout (default=30) + --with-par-queries=num Specify default parallel query number (default=2) + --with-max-nameserver-ips=num Specify maximum number of IP addresses used per nameserver obtained from NS records (default=3) + --with-thread-lib=lib Specify thread library, overriding automatic detection (for Linux only). + Possible values: LinuxThreads, + LinuxThreads2 (implements a fix for newer glibcs) + or NPTL (Native POSIX Thread Library) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.63 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.63. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +package="pdnsd" +version=`cut -d - -f 1 "$srcdir"/version` +fullversion=`cat "$srcdir"/version` +packagerelease=`cut -d - -f 2- "$srcdir"/version` + +distribution="Generic" +target="autodetect" +cachedir="/var/cache/$package" +ipv4_default=1 +have_ipv4="yes" +#newrrs="yes" +query_m="udponly" +have_tcp_server="yes" +adisc="default" +slock="no"; +def_id="nobody" +#have_rcsids="no" +udp_queries="yes" +tcp_queries="yes" +tempdir="/tmp" +randomdev=default +freebsd_pthread="4" +specbuild=no +threadlib=default + +am__api_version='1.11' + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +$as_echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + +done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + { { $as_echo "$as_me:$LINENO: error: unsafe absolute working directory name" >&5 +$as_echo "$as_me: error: unsafe absolute working directory name" >&2;} + { (exit 1); exit 1; }; };; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + { { $as_echo "$as_me:$LINENO: error: unsafe srcdir value: \`$srcdir'" >&5 +$as_echo "$as_me: error: unsafe srcdir value: \`$srcdir'" >&2;} + { (exit 1); exit 1; }; };; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { $as_echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +$as_echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { $as_echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +$as_echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { $as_echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_STRIP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:$LINENO: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if test "${ac_cv_path_mkdir+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done +done +IFS=$as_save_IFS + +fi + + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + test -d ./--version && rmdir ./--version + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:$LINENO: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +mkdir_p="$MKDIR_P" +case $mkdir_p in + [\\/$]* | ?:[\\/]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_AWK+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:$LINENO: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + { { $as_echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +$as_echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE=$package + VERSION=$version + + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + + + + + +ac_config_headers="$ac_config_headers config.h" + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:$LINENO: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + fi + fi +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:$LINENO: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:$LINENO: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + if test "${ac_cv_header_minix_config_h+set}" = set; then + { $as_echo "$as_me:$LINENO: checking for minix/config.h" >&5 +$as_echo_n "checking for minix/config.h... " >&6; } +if test "${ac_cv_header_minix_config_h+set}" = set; then + $as_echo_n "(cached) " >&6 +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +$as_echo "$ac_cv_header_minix_config_h" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking minix/config.h usability" >&5 +$as_echo_n "checking minix/config.h usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking minix/config.h presence" >&5 +$as_echo_n "checking minix/config.h presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: minix/config.h: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for minix/config.h" >&5 +$as_echo_n "checking for minix/config.h... " >&6; } +if test "${ac_cv_header_minix_config_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_header_minix_config_h=$ac_header_preproc +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5 +$as_echo "$ac_cv_header_minix_config_h" >&6; } + +fi +if test "x$ac_cv_header_minix_config_h" = x""yes; then + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_SOURCE 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_1_SOURCE 2 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define _MINIX 1 +_ACEOF + + fi + + + + { $as_echo "$as_me:$LINENO: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if test "${ac_cv_safe_to_define___extensions__+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_safe_to_define___extensions__=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_safe_to_define___extensions__=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + cat >>confdefs.h <<\_ACEOF +#define __EXTENSIONS__ 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _ALL_SOURCE 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _GNU_SOURCE 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _POSIX_PTHREAD_SEMANTICS 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _TANDEM_SOURCE 1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$fullversion" +_ACEOF + + + + + +# Check whether --with-distribution was given. +if test "${with_distribution+set}" = set; then + withval=$with_distribution; distribution=$withval +fi + + + + +# Check whether --with-target was given. +if test "${with_target+set}" = set; then + withval=$with_target; target=$withval +fi + +case $target in + Linux|linux) + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_LINUX +_ACEOF + + target="Linux" + ;; + BSD| bsd) + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_BSD +_ACEOF + + target="BSD" + ;; + Cygwin|CYGWIN|cygwin) + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_CYGWIN +_ACEOF + + target="cygwin" + ;; + autodetect) + ;; + *) + { { $as_echo "$as_me:$LINENO: error: --with-target must have Linux, BSD or Cygwin as parameter." >&5 +$as_echo "$as_me: error: --with-target must have Linux, BSD or Cygwin as parameter." >&2;} + { (exit 1); exit 1; }; } + ;; +esac + + +# Check whether --enable-isdn was given. +if test "${enable_isdn+set}" = set; then + enableval=$enable_isdn; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define ISDN_SUPPORT 1 +_ACEOF + +fi + + +# Check whether --enable-ipv4 was given. +if test "${enable_ipv4+set}" = set; then + enableval=$enable_ipv4; have_ipv4=$enableval +fi + + +test $have_ipv4 = "yes" && cat >>confdefs.h <<\_ACEOF +#define ENABLE_IPV4 1 +_ACEOF + + +# Check whether --enable-ipv6 was given. +if test "${enable_ipv6+set}" = set; then + enableval=$enable_ipv6; if test $enableval = "yes" ; then + cat >>confdefs.h <<\_ACEOF +#define ENABLE_IPV6 1 +_ACEOF + + if test $have_ipv4 != "yes" ; then + ipv4_default=0 + fi + fi +fi + + +# Check whether --enable-ipv4-startup was given. +if test "${enable_ipv4_startup+set}" = set; then + enableval=$enable_ipv4_startup; if test $enableval = "yes" ; then + ipv4_default=1 + else + ipv4_default=0 + fi +fi + + +# Check whether --enable-ipv6-startup was given. +if test "${enable_ipv6_startup+set}" = set; then + enableval=$enable_ipv6_startup; if test $enableval = "yes" ; then + ipv4_default=0 + else + ipv4_default=1 + fi +fi + + +cat >>confdefs.h <<_ACEOF +#define DEFAULT_IPV4 $ipv4_default +_ACEOF + + +# Check whether --enable-udp-queries was given. +if test "${enable_udp_queries+set}" = set; then + enableval=$enable_udp_queries; udp_queries=$enableval +fi + + +# Check whether --enable-tcp-queries was given. +if test "${enable_tcp_queries+set}" = set; then + enableval=$enable_tcp_queries; tcp_queries=$enableval +fi + + + +# Check whether --with-query-method was given. +if test "${with_query_method+set}" = set; then + withval=$with_query_method; query_m=$withval +fi + +case $query_m in + udponly|UDPonly) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET UDP_ONLY +_ACEOF + + udp_queries=yes; + ;; + tcponly|TCPonly) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET TCP_ONLY +_ACEOF + + tcp_queries=yes; + ;; + tcpudp|TCPUDP) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET TCP_UDP +_ACEOF + + udp_queries=yes; + tcp_queries=yes; + ;; + udptcp|UDPTCP) + cat >>confdefs.h <<\_ACEOF +#define M_PRESET UDP_TCP +_ACEOF + + udp_queries=yes; + tcp_queries=yes; + ;; + *) + { { $as_echo "$as_me:$LINENO: error: --with-query-method must have udponly, tcponly, tcpudp or udptcp as parameter." >&5 +$as_echo "$as_me: error: --with-query-method must have udponly, tcponly, tcpudp or udptcp as parameter." >&2;} + { (exit 1); exit 1; }; } + ;; +esac + +test $udp_queries != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_UDP_QUERIES 1 +_ACEOF + +test $tcp_queries != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_TCP_QUERIES 1 +_ACEOF + + +# Check whether --enable-tcp-server was given. +if test "${enable_tcp_server+set}" = set; then + enableval=$enable_tcp_server; have_tcp_server=$enableval +fi + + +test $have_tcp_server != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_TCP_SERVER 1 +_ACEOF + + +# Check whether --enable-src-addr-disc was given. +if test "${enable_src_addr_disc+set}" = set; then + enableval=$enable_src_addr_disc; adisc=$enableval +fi + + +# Check whether --enable-socket-locking was given. +if test "${enable_socket_locking+set}" = set; then + enableval=$enable_socket_locking; slock=$enableval +fi + + +test $slock = "yes" && cat >>confdefs.h <<\_ACEOF +#define SOCKET_LOCKING 1 +_ACEOF + + +# Check whether --enable-poll was given. +if test "${enable_poll+set}" = set; then + enableval=$enable_poll; test $enableval != "yes" && cat >>confdefs.h <<\_ACEOF +#define NO_POLL 1 +_ACEOF + +fi + + +# Check whether --enable-new-rrs was given. +if test "${enable_new_rrs+set}" = set; then + enableval=$enable_new_rrs; newrrs=$enableval +fi + + +# Check whether --enable-strict-rfc2181 was given. +if test "${enable_strict_rfc2181+set}" = set; then + enableval=$enable_strict_rfc2181; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define RFC2181_ME_HARDER 1 +_ACEOF + +fi + + + +# Check whether --with-random-device was given. +if test "${with_random_device+set}" = set; then + withval=$with_random_device; randomdev=$withval +fi + + +if test "$randomdev" = arc4random ; then + cat >>confdefs.h <<\_ACEOF +#define R_ARC4RANDOM 1 +_ACEOF + +elif test "$randomdev" = random ; then + cat >>confdefs.h <<\_ACEOF +#define R_RANDOM 1 +_ACEOF + +elif test "$randomdev" = default ; then + cat >>confdefs.h <<\_ACEOF +#define R_DEFAULT 1 +_ACEOF + +else + cat >>confdefs.h <<_ACEOF +#define RANDOM_DEVICE "$randomdev" +_ACEOF + +fi + +# Check whether --enable-underscores was given. +if test "${enable_underscores+set}" = set; then + enableval=$enable_underscores; underscores=$enableval +fi + + + +# Check whether --with-default-id was given. +if test "${with_default_id+set}" = set; then + withval=$with_default_id; def_id=$withval +fi + + + + +# Check whether --with-debug was given. +if test "${with_debug+set}" = set; then + withval=$with_debug; cat >>confdefs.h <<_ACEOF +#define DEBUG $withval +_ACEOF + +fi + + + +# Check whether --with-verbosity was given. +if test "${with_verbosity+set}" = set; then + withval=$with_verbosity; cat >>confdefs.h <<_ACEOF +#define VERBOSITY $withval +_ACEOF + +fi + + + +# Check whether --with-hash-buckets was given. +if test "${with_hash_buckets+set}" = set; then + withval=$with_hash_buckets; powof2=1 + hashsz=0 + + while test $powof2 -lt "$withval" + do + powof2=`expr 2 '*' $powof2` + hashsz=`expr $hashsz '+' 1` + done + cat >>confdefs.h <<_ACEOF +#define HASH_SZ $hashsz +_ACEOF + + +fi + + +# Check whether --enable-hash-debug was given. +if test "${enable_hash_debug+set}" = set; then + enableval=$enable_hash_debug; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define DEBUG_HASH 1 +_ACEOF + +fi + + +# Check whether --enable-rcsids was given. +if test "${enable_rcsids+set}" = set; then + enableval=$enable_rcsids; have_rcsids=$enableval +fi + + + +# Check whether --with-tcp-qtimeout was given. +if test "${with_tcp_qtimeout+set}" = set; then + withval=$with_tcp_qtimeout; cat >>confdefs.h <<_ACEOF +#define TCP_TIMEOUT $withval +_ACEOF + +fi + + +# Check whether --enable-tcp-subseq was given. +if test "${enable_tcp_subseq+set}" = set; then + enableval=$enable_tcp_subseq; test $enableval = "yes" && cat >>confdefs.h <<\_ACEOF +#define TCP_SUBSEQ 1 +_ACEOF + +fi + + + +# Check whether --with-par-queries was given. +if test "${with_par_queries+set}" = set; then + withval=$with_par_queries; cat >>confdefs.h <<_ACEOF +#define PAR_QUERIES $withval +_ACEOF + +fi + + + +# Check whether --with-max-nameserver-ips was given. +if test "${with_max_nameserver_ips+set}" = set; then + withval=$with_max_nameserver_ips; cat >>confdefs.h <<_ACEOF +#define MAXNAMESERVIPS $withval +_ACEOF + +fi + + +# Check whether --enable-specbuild was given. +if test "${enable_specbuild+set}" = set; then + enableval=$enable_specbuild; specbuild=$enableval +fi + + + + + +# Check whether --with-thread-lib was given. +if test "${with_thread_lib+set}" = set; then + withval=$with_thread_lib; threadlib=$withval +fi + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc + +if test "x$CC" != xcc; then + { $as_echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5 +$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; } +else + { $as_echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5 +$as_echo_n "checking whether cc understands -c and -o together... " >&6; } +fi +set dummy $CC; ac_cc=`$as_echo "$2" | + sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if { as_var=ac_cv_prog_cc_${ac_cc}_c_o; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +# Make sure it works both with $CC and with simple cc. +# We do the test twice because some compilers refuse to overwrite an +# existing .o file with -o, though they will create one. +ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5' +rm -f conftest2.* +if { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest2.$ac_objext && { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; +then + eval ac_cv_prog_cc_${ac_cc}_c_o=yes + if test "x$CC" != xcc; then + # Test first that cc exists at all. + if { ac_try='cc -c conftest.$ac_ext >&5' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5' + rm -f conftest2.* + if { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest2.$ac_objext && { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; + then + # cc works too. + : + else + # cc exists but doesn't like -o. + eval ac_cv_prog_cc_${ac_cc}_c_o=no + fi + fi + fi +else + eval ac_cv_prog_cc_${ac_cc}_c_o=no +fi +rm -f core conftest* + +fi +if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define NO_MINUS_C_MINUS_O 1 +_ACEOF + +fi + +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o +if test "$am_t" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + +done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_RANLIB+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:$LINENO: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + +if test "$target" = "autodetect" ; then + { $as_echo "$as_me:$LINENO: checking for autodetect build target" >&5 +$as_echo_n "checking for autodetect build target... " >&6; } + uname_sys=`uname` + if test $? -ne 0 ; then + { $as_echo "$as_me:$LINENO: result: failed" >&5 +$as_echo "failed" >&6; } + { { $as_echo "$as_me:$LINENO: error: uname failed or was not found in path" >&5 +$as_echo "$as_me: error: uname failed or was not found in path" >&2;} + { (exit 1); exit 1; }; } + else + case "${uname_sys}" in + Linux) + { $as_echo "$as_me:$LINENO: result: Linux" >&5 +$as_echo "Linux" >&6; } + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_LINUX +_ACEOF + + target="Linux" + ;; + FreeBSD|NetBSD|OpenBSD|Darwin) + { $as_echo "$as_me:$LINENO: result: \"${uname_sys}\"" >&5 +$as_echo "\"${uname_sys}\"" >&6; } + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_BSD +_ACEOF + + target="BSD" + ;; + CYGWIN*) + { $as_echo "$as_me:$LINENO: result: \"${uname_sys}\"" >&5 +$as_echo "\"${uname_sys}\"" >&6; } + cat >>confdefs.h <<\_ACEOF +#define TARGET TARGET_CYGWIN +_ACEOF + + target="cygwin" + ;; + *) + { $as_echo "$as_me:$LINENO: result: failed" >&5 +$as_echo "failed" >&6; } + { { $as_echo "$as_me:$LINENO: error: Your system type could not be identified. Try setting it manually using +--with-target" >&5 +$as_echo "$as_me: error: Your system type could not be identified. Try setting it manually using +--with-target" >&2;} + { (exit 1); exit 1; }; } + ;; + esac + fi +fi + +#if test "$target" = BSD ; then +# uname_sys=`uname` +# if test "$uname_sys" = FreeBSD ; then +# AC_MSG_CHECKING([for FreeBSD version]) +# osrel=`sysctl -n kern.osreldate` +# if test $osrel -ge 500016 ; then +# AC_MSG_RESULT([5.0 (>= 500016)]) +# freebsd_pthread="5" +# else +# AC_MSG_RESULT([<=5.0 (< 500016)]) +# freebsd_pthread="4" +# fi +# fi +#fi + +if test "$adisc" = "default"; then + if test "$target" = "cygwin" ; then +# Don't do UDP source address discovery on Cygwin platform by default. + adisc="no" + else + adisc="yes" + fi +fi + +test "$adisc" = "yes" && cat >>confdefs.h <<\_ACEOF +#define SRC_ADDR_DISC 1 +_ACEOF + + + +if test "$target" = "Linux"; then +{ $as_echo "$as_me:$LINENO: checking if we can compile and link with -pthread" >&5 +$as_echo_n "checking if we can compile and link with -pthread... " >&6; } +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -pthread" +cat >conftest.$ac_ext <<_ACEOF + +#include + +void *thread_func(void *data) +{ + *((int *)data)=1; + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int val; + + if(pthread_create(&thread, NULL, thread_func, &val)) + return 1; + + if(pthread_join(thread,&retval)) + return 1; + + return (*((int *)retval)!=1); +} + +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + gcc_pthread_flag="yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + gcc_pthread_flag="no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +CFLAGS="$old_CFLAGS" +{ $as_echo "$as_me:$LINENO: result: $gcc_pthread_flag" >&5 +$as_echo "$gcc_pthread_flag" >&6; } + + if test "$gcc_pthread_flag" = yes ; then + thread_CFLAGS="-pthread" + + else + +{ $as_echo "$as_me:$LINENO: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_pthread_pthread_create=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_pthread_pthread_create=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPTHREAD 1 +_ACEOF + + LIBS="-lpthread $LIBS" + +fi + + fi +fi +if test "$target" = "BSD" -a `uname` != Darwin ; then +# if test $freebsd_pthread = 4 ; then + thread_CFLAGS="-pthread" + +# else +# AC_CHECK_LIB(c_r, pthread_create, , +# AC_MSG_ERROR([You must have libc_r installed to build/run pdnsd!])) +# fi; +fi + +if test "$target" = "Linux" -a "$threadlib" = default; then +{ $as_echo "$as_me:$LINENO: checking if this is an NPTL-based system" >&5 +$as_echo_n "checking if this is an NPTL-based system... " >&6; } +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $thread_CFLAGS" +if test "$cross_compiling" = yes; then + + { $as_echo "$as_me:$LINENO: result: couldn't run test program" >&5 +$as_echo "couldn't run test program" >&6; } + threadlib=linuxthreads + +else + cat >conftest.$ac_ext <<_ACEOF + +#include +#include +#include +#include +#include +#include + +/* All this function does is return its PID (in a roundabout way). */ +void *thread_func(void *data) +{ + *((int *)data)=getpid(); + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int err,mainpid,thrdpid; + + err=pthread_create(&thread, NULL, thread_func, &thrdpid); + if(err) { + fprintf(stderr,"pthread_create failed: %s\n",strerror(err)); + return 1; + } + err=pthread_join(thread,&retval); + if(err) { + fprintf(stderr,"pthread_join failed: %s\n",strerror(err)); + return 1; + } + mainpid=getpid(); + /* In LinuxThreads implementations, the pids of the threads will usually differ + in a non Posix-compliant way. */ + fprintf(stderr,"main pid=%d, thread pid=%d\n",mainpid,*((int *)retval)); + return (*((int *)retval)!=mainpid); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + threadlib=nptl + +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) + + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + threadlib=linuxthreads + +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +CFLAGS="$old_CFLAGS" +fi + +if test "$threadlib" = nptl -o "$threadlib" = NPTL; then + cat >>confdefs.h <<\_ACEOF +#define THREADLIB_NPTL 1 +_ACEOF + +elif test "$threadlib" = linuxthreads2 -o "$threadlib" = LinuxThreads2 -o "$threadlib" = lt2; then + cat >>confdefs.h <<\_ACEOF +#define THREADLIB_LINUXTHREADS2 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if test "${ac_cv_header_sys_wait_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_sys_wait_h=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_sys_wait_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + + + + + + + +for ac_header in fcntl.h malloc.h sys/ioctl.h sys/time.h syslog.h unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + +for ac_header in sys/types.h sys/socket.h net/if.h netinet/in.h sys/poll.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +{ $as_echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if test "${ac_cv_c_const+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_const=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_const=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const /**/ +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for pid_t" >&5 +$as_echo_n "checking for pid_t... " >&6; } +if test "${ac_cv_type_pid_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_pid_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((pid_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_pid_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +$as_echo "$ac_cv_type_pid_t" >&6; } +if test "x$ac_cv_type_pid_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for size_t" >&5 +$as_echo_n "checking for size_t... " >&6; } +if test "${ac_cv_type_size_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_size_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((size_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_size_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +$as_echo "$ac_cv_type_size_t" >&6; } +if test "x$ac_cv_type_size_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if test "${ac_cv_header_time+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_time=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_time=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } +if test "${ac_cv_struct_tm+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm tm; + int *p = &tm.tm_sec; + return !p; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_struct_tm=time.h +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_struct_tm=sys/time.h +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +$as_echo "$ac_cv_struct_tm" >&6; } +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for socklen_t" >&5 +$as_echo_n "checking for socklen_t... " >&6; } +if test "${ac_cv_type_socklen_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_socklen_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (socklen_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((socklen_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_socklen_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_socklen_t" >&5 +$as_echo "$ac_cv_type_socklen_t" >&6; } +if test "x$ac_cv_type_socklen_t" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_SOCKLEN_T 1 +_ACEOF + + +else + cat >>confdefs.h <<\_ACEOF +#define socklen_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for struct in6_addr" >&5 +$as_echo_n "checking for struct in6_addr... " >&6; } +if test "${ac_cv_type_struct_in6_addr+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_struct_in6_addr=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (struct in6_addr)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((struct in6_addr))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_struct_in6_addr=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_struct_in6_addr" >&5 +$as_echo "$ac_cv_type_struct_in6_addr" >&6; } +if test "x$ac_cv_type_struct_in6_addr" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_IN6_ADDR 1 +_ACEOF + + +fi +{ $as_echo "$as_me:$LINENO: checking for struct in_pktinfo" >&5 +$as_echo_n "checking for struct in_pktinfo... " >&6; } +if test "${ac_cv_type_struct_in_pktinfo+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_struct_in_pktinfo=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (struct in_pktinfo)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((struct in_pktinfo))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_struct_in_pktinfo=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_struct_in_pktinfo" >&5 +$as_echo "$ac_cv_type_struct_in_pktinfo" >&6; } +if test "x$ac_cv_type_struct_in_pktinfo" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_IN_PKTINFO 1 +_ACEOF + + +fi +{ $as_echo "$as_me:$LINENO: checking for struct ifreq" >&5 +$as_echo_n "checking for struct ifreq... " >&6; } +if test "${ac_cv_type_struct_ifreq+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_struct_ifreq=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof (struct ifreq)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ +if (sizeof ((struct ifreq))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_struct_ifreq=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_struct_ifreq" >&5 +$as_echo "$ac_cv_type_struct_ifreq" >&6; } +if test "x$ac_cv_type_struct_ifreq" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_IFREQ 1 +_ACEOF + + +fi + + +{ $as_echo "$as_me:$LINENO: checking for CPP C99 Variadic macro support" >&5 +$as_echo_n "checking for CPP C99 Variadic macro support... " >&6; } +cat >conftest.$ac_ext <<_ACEOF + +#define a(...) junk(0,__VA_ARGS__) +extern void junk(int i,...); +int main() +{ + a(0); + a("a"); + a(0, "a", 1); + return 0; +} + +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cpp_c99_variadic="yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + cpp_c99_variadic="no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $cpp_c99_variadic" >&5 +$as_echo "$cpp_c99_variadic" >&6; } +if test $cpp_c99_variadic = yes; then + cat >>confdefs.h <<\_ACEOF +#define CPP_C99_VARIADIC_MACROS 1 +_ACEOF + +else + if test "$GCC" != yes; then + { { $as_echo "$as_me:$LINENO: error: Compiler must support C99 or gcc variadic macros" >&5 +$as_echo "$as_me: error: Compiler must support C99 or gcc variadic macros" >&2;} + { (exit 1); exit 1; }; } + fi; +fi + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +{ $as_echo "$as_me:$LINENO: checking for working alloca.h" >&5 +$as_echo_n "checking for working alloca.h... " >&6; } +if test "${ac_cv_working_alloca_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +char *p = (char *) alloca (2 * sizeof (int)); + if (p) return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_working_alloca_h=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_working_alloca_h=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5 +$as_echo "$ac_cv_working_alloca_h" >&6; } +if test $ac_cv_working_alloca_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA_H 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for alloca" >&5 +$as_echo_n "checking for alloca... " >&6; } +if test "${ac_cv_func_alloca_works+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include +# define alloca _alloca +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +int +main () +{ +char *p = (char *) alloca (1); + if (p) return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_func_alloca_works=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_func_alloca_works=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5 +$as_echo "$ac_cv_func_alloca_works" >&6; } + +if test $ac_cv_func_alloca_works = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA 1 +_ACEOF + +else + # The SVR3 libPW and SVR4 libucb both contain incompatible functions +# that cause trouble. Some versions do not even contain alloca or +# contain a buggy version. If you still want to use their alloca, +# use ar to extract alloca.o from them instead of compiling alloca.c. + +ALLOCA=\${LIBOBJDIR}alloca.$ac_objext + +cat >>confdefs.h <<\_ACEOF +#define C_ALLOCA 1 +_ACEOF + + +{ $as_echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5 +$as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; } +if test "${ac_cv_os_cray+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if defined CRAY && ! defined CRAY2 +webecray +#else +wenotbecray +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "webecray" >/dev/null 2>&1; then + ac_cv_os_cray=yes +else + ac_cv_os_cray=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5 +$as_echo "$ac_cv_os_cray" >&6; } +if test $ac_cv_os_cray = yes; then + for ac_func in _getb67 GETB67 getb67; do + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + +cat >>confdefs.h <<_ACEOF +#define CRAY_STACKSEG_END $ac_func +_ACEOF + + break +fi + + done +fi + +{ $as_echo "$as_me:$LINENO: checking stack direction for C alloca" >&5 +$as_echo_n "checking stack direction for C alloca... " >&6; } +if test "${ac_cv_c_stack_direction+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_c_stack_direction=0 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +find_stack_direction () +{ + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; +} + +int +main () +{ + return find_stack_direction () < 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_stack_direction=1 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_stack_direction=-1 +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5 +$as_echo "$ac_cv_c_stack_direction" >&6; } + +cat >>confdefs.h <<_ACEOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +_ACEOF + + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + { $as_echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +$as_echo_n "checking whether $CC needs -traditional... " >&6; } +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +$as_echo "$ac_cv_prog_gcc_traditional" >&6; } + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +{ $as_echo "$as_me:$LINENO: checking for working memcmp" >&5 +$as_echo_n "checking for working memcmp... " >&6; } +if test "${ac_cv_func_memcmp_working+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = '\100', c1 = '\200', c2 = '\201'; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + return 1; + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + return 1; + } + return 0; + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_memcmp_working=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +$as_echo "$ac_cv_func_memcmp_working" >&6; } +test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" + ;; +esac + + +{ $as_echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if test "${ac_cv_type_signal+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_signal=int +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_signal=void +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + +for ac_func in vprintf +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +{ $as_echo "$as_me:$LINENO: checking for _doprnt" >&5 +$as_echo_n "checking for _doprnt... " >&6; } +if test "${ac_cv_func__doprnt+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef _doprnt + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char _doprnt (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub__doprnt || defined __stub____doprnt +choke me +#endif + +int +main () +{ +return _doprnt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_func__doprnt=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_func__doprnt=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +$as_echo "$ac_cv_func__doprnt" >&6; } +if test "x$ac_cv_func__doprnt" = x""yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + +{ $as_echo "$as_me:$LINENO: checking for library containing nanosleep" >&5 +$as_echo_n "checking for library containing nanosleep... " >&6; } +if test "${ac_cv_search_nanosleep+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char nanosleep (); +int +main () +{ +return nanosleep (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_nanosleep=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_nanosleep+set}" = set; then + break +fi +done +if test "${ac_cv_search_nanosleep+set}" = set; then + : +else + ac_cv_search_nanosleep=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_nanosleep" >&5 +$as_echo "$ac_cv_search_nanosleep" >&6; } +ac_res=$ac_cv_search_nanosleep +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +{ $as_echo "$as_me:$LINENO: checking for library containing socket" >&5 +$as_echo_n "checking for library containing socket... " >&6; } +if test "${ac_cv_search_socket+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +for ac_lib in '' socket; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib -lnsl $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_socket=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_socket+set}" = set; then + break +fi +done +if test "${ac_cv_search_socket+set}" = set; then + : +else + ac_cv_search_socket=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_socket" >&5 +$as_echo "$ac_cv_search_socket" >&6; } +ac_res=$ac_cv_search_socket +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +{ $as_echo "$as_me:$LINENO: checking for library containing inet_aton" >&5 +$as_echo_n "checking for library containing inet_aton... " >&6; } +if test "${ac_cv_search_inet_aton+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char inet_aton (); +int +main () +{ +return inet_aton (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_search_inet_aton=$ac_res +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext + if test "${ac_cv_search_inet_aton+set}" = set; then + break +fi +done +if test "${ac_cv_search_inet_aton+set}" = set; then + : +else + ac_cv_search_inet_aton=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_inet_aton" >&5 +$as_echo "$ac_cv_search_inet_aton" >&6; } +ac_res=$ac_cv_search_inet_aton +if test "$ac_res" != no; then + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + + + + + + + + + +for ac_func in nanosleep gettimeofday mkfifo select socket strerror uname snprintf vsnprintf +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + true +else + { { $as_echo "$as_me:$LINENO: error: One of the functions required for pdnsd were not found." >&5 +$as_echo "$as_me: error: One of the functions required for pdnsd were not found." >&2;} + { (exit 1); exit 1; }; } +fi +done + + +for ac_func in poll +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + true +else + cat >>confdefs.h <<\_ACEOF +#define NO_POLL 1 +_ACEOF + +fi +done + + + + + + + + + + + + + +for ac_func in strdup strndup stpcpy stpncpy strlcpy mempcpy getline asprintf vasprintf getpwnam_r inet_ntop inet_pton +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +ac_config_files="$ac_config_files pdnsd.spec Makefile file-list.base contrib/Makefile doc/Makefile doc/pdnsd.8 doc/pdnsd.conf.5 doc/pdnsd.conf src/Makefile src/pdnsd-ctl/Makefile src/rc/Makefile src/rc/RedHat/Makefile src/rc/RedHat/pdnsd src/rc/SuSE/Makefile src/rc/SuSE/pdnsd src/rc/Debian/Makefile src/rc/Debian/pdnsd src/rc/Slackware/Makefile src/rc/Slackware/rc.pdnsd src/rc/ArchLinux/Makefile src/rc/ArchLinux/pdnsd src/test/Makefile PKGBUILD" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.63. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTION]... [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.63, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { $as_echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { $as_echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "pdnsd.spec") CONFIG_FILES="$CONFIG_FILES pdnsd.spec" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "file-list.base") CONFIG_FILES="$CONFIG_FILES file-list.base" ;; + "contrib/Makefile") CONFIG_FILES="$CONFIG_FILES contrib/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "doc/pdnsd.8") CONFIG_FILES="$CONFIG_FILES doc/pdnsd.8" ;; + "doc/pdnsd.conf.5") CONFIG_FILES="$CONFIG_FILES doc/pdnsd.conf.5" ;; + "doc/pdnsd.conf") CONFIG_FILES="$CONFIG_FILES doc/pdnsd.conf" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/pdnsd-ctl/Makefile") CONFIG_FILES="$CONFIG_FILES src/pdnsd-ctl/Makefile" ;; + "src/rc/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/Makefile" ;; + "src/rc/RedHat/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/RedHat/Makefile" ;; + "src/rc/RedHat/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/RedHat/pdnsd" ;; + "src/rc/SuSE/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/SuSE/Makefile" ;; + "src/rc/SuSE/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/SuSE/pdnsd" ;; + "src/rc/Debian/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/Debian/Makefile" ;; + "src/rc/Debian/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/Debian/pdnsd" ;; + "src/rc/Slackware/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/Slackware/Makefile" ;; + "src/rc/Slackware/rc.pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/Slackware/rc.pdnsd" ;; + "src/rc/ArchLinux/Makefile") CONFIG_FILES="$CONFIG_FILES src/rc/ArchLinux/Makefile" ;; + "src/rc/ArchLinux/pdnsd") CONFIG_FILES="$CONFIG_FILES src/rc/ArchLinux/pdnsd" ;; + "src/test/Makefile") CONFIG_FILES="$CONFIG_FILES src/test/Makefile" ;; + "PKGBUILD") CONFIG_FILES="$CONFIG_FILES PKGBUILD" ;; + + *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + $as_echo "$as_me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr='' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 +$as_echo "$as_me: error: could not setup config files machinery" >&2;} + { (exit 1); exit 1; }; } +_ACEOF + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5 +$as_echo "$as_me: error: could not setup config headers machinery" >&2;} + { (exit 1); exit 1; }; } +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 +$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + ac_file_inputs="$ac_file_inputs '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5 +$as_echo "$as_me: error: could not create -" >&2;} + { (exit 1); exit 1; }; } + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:$LINENO: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir=$dirpart/$fdir + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + + esac +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/service/src/main/jni/pdnsd/configure.in b/service/src/main/jni/pdnsd/configure.in new file mode 100644 index 0000000..872394c --- /dev/null +++ b/service/src/main/jni/pdnsd/configure.in @@ -0,0 +1,541 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(src) + +package="pdnsd" +version=`cut -d - -f 1 "$srcdir"/version` +fullversion=`cat "$srcdir"/version` +packagerelease=`cut -d - -f 2- "$srcdir"/version` + +distribution="Generic" +target="autodetect" +cachedir="/var/cache/$package" +ipv4_default=1 +have_ipv4="yes" +#newrrs="yes" +query_m="udponly" +have_tcp_server="yes" +adisc="default" +slock="no"; +def_id="nobody" +#have_rcsids="no" +udp_queries="yes" +tcp_queries="yes" +tempdir="/tmp" +randomdev=default +freebsd_pthread="4" +specbuild=no +threadlib=default + +AM_INIT_AUTOMAKE($package, $version, [no-define]) +AM_CONFIG_HEADER(config.h) +AC_GNU_SOURCE +AC_DEFINE_UNQUOTED(VERSION,"$fullversion") +AC_SUBST(fullversion) +AC_SUBST(packagerelease) + +AC_ARG_WITH(distribution, +[ --with-distribution=distro Specify target distribution (default=Generic; + others: RedHat, SuSE, Debian, ArchLinux)], + distribution=$withval) +AC_SUBST(distribution) + +AC_ARG_WITH(target, +[ --with-target=platform Change compilation target platform (default: + autodetect; others: Linux, BSD, Cygwin)], + target=$withval) +case $target in + Linux|linux) + AC_DEFINE(TARGET, TARGET_LINUX) + target="Linux" + ;; + BSD| bsd) + AC_DEFINE(TARGET, TARGET_BSD) + target="BSD" + ;; + Cygwin|CYGWIN|cygwin) + AC_DEFINE(TARGET, TARGET_CYGWIN) + target="cygwin" + ;; + autodetect) + ;; + *) + AC_MSG_ERROR([--with-target must have Linux, BSD or Cygwin as parameter.]) + ;; +esac + +AC_ARG_ENABLE(isdn, +[ --enable-isdn Enable ISDN support (may cause problems on + some systems; only for Linux)], + test $enableval = "yes" && AC_DEFINE(ISDN_SUPPORT)) + +AC_ARG_ENABLE(ipv4, +[ --disable-ipv4 Disable IPv4 networking support + (default=enabled)], + have_ipv4=$enableval) + +test $have_ipv4 = "yes" && AC_DEFINE(ENABLE_IPV4) + +AC_ARG_ENABLE(ipv6, +[ --enable-ipv6 Enable IPv6 networking support], +[ if test $enableval = "yes" ; then + AC_DEFINE(ENABLE_IPV6) + if test $have_ipv4 != "yes" ; then + ipv4_default=0 + fi + fi]) + +AC_ARG_ENABLE(ipv4-startup, +[ --disable-ipv4-startup Disable IPv4 on pdnsd startup by default + (default=enabled)], +[ if test $enableval = "yes" ; then + ipv4_default=1 + else + ipv4_default=0 + fi]) + +AC_ARG_ENABLE(ipv6-startup, +[ --enable-ipv6-startup Enable IPV6 on pdnsd startup by default + (default=IPv4)], +[ if test $enableval = "yes" ; then + ipv4_default=0 + else + ipv4_default=1 + fi]) + +AC_DEFINE_UNQUOTED(DEFAULT_IPV4, $ipv4_default) + +AC_ARG_ENABLE(udp-queries, +[ --disable-udp-queries Disable udp as query method.], + udp_queries=$enableval) + +AC_ARG_ENABLE(tcp-queries, +[ --disable-tcp-queries Disable tcp as query method.], + tcp_queries=$enableval) + +AC_ARG_WITH(query-method, +[ --with-query-method=qm Specify the query method (default=udponly; + others: tcponly, tcpudp, udptcp)], + query_m=$withval) +case $query_m in + udponly|UDPonly) + AC_DEFINE(M_PRESET, UDP_ONLY) + udp_queries=yes; + ;; + tcponly|TCPonly) + AC_DEFINE(M_PRESET, TCP_ONLY) + tcp_queries=yes; + ;; + tcpudp|TCPUDP) + AC_DEFINE(M_PRESET, TCP_UDP) + udp_queries=yes; + tcp_queries=yes; + ;; + udptcp|UDPTCP) + AC_DEFINE(M_PRESET, UDP_TCP) + udp_queries=yes; + tcp_queries=yes; + ;; + *) + AC_MSG_ERROR([--with-query-method must have udponly, tcponly, tcpudp or udptcp as parameter.]) + ;; +esac + +test $udp_queries != "yes" && AC_DEFINE(NO_UDP_QUERIES) +test $tcp_queries != "yes" && AC_DEFINE(NO_TCP_QUERIES) + +AC_ARG_ENABLE(tcp-server, +[ --disable-tcp-server Disable the TCP serving ability of pdnsd], + have_tcp_server=$enableval) + +test $have_tcp_server != "yes" && AC_DEFINE(NO_TCP_SERVER) + +AC_ARG_ENABLE(src-addr-disc, +[ --disable-src-addr-disc Disable the UDP source address discovery], + adisc=$enableval) + +AC_ARG_ENABLE(socket-locking, +[ --enable-socket-locking Enable the UDP socket locking], + slock=$enableval) + +test $slock = "yes" && AC_DEFINE(SOCKET_LOCKING) + +AC_ARG_ENABLE(poll, +[ --disable-poll Disable poll(2) and use select(2) + (default=enabled)], + test $enableval != "yes" && AC_DEFINE(NO_POLL)) + +AC_ARG_ENABLE(new-rrs, +[ --disable-new-rrs Disable new DNS RR types (obsolete, currently ignored)], + newrrs=$enableval) + +AC_ARG_ENABLE(strict-rfc2181, +[ --enable-strict-rfc2181 Enforce strict RFC 2181 compliance], + test $enableval = "yes" && AC_DEFINE(RFC2181_ME_HARDER)) + +AC_ARG_WITH(random-device, +[ --with-random-device=device Specify random device other than + /dev/random; default: C Library random() PRNG; + special value arc4random for BSD C Library + arc4random function (default on FreeBSD)], + randomdev=$withval) + +if test "$randomdev" = arc4random ; then + AC_DEFINE(R_ARC4RANDOM) +elif test "$randomdev" = random ; then + AC_DEFINE(R_RANDOM) +elif test "$randomdev" = default ; then + AC_DEFINE(R_DEFAULT) +else + AC_DEFINE_UNQUOTED(RANDOM_DEVICE, "$randomdev") +fi + +AC_ARG_ENABLE(underscores, +[ --enable-underscores Allow _ in domain names (obsolete, currently ignored)], + underscores=$enableval) + +AC_ARG_WITH(default-id, +[ --with-default-id=id Specify default uid/gid for pdnsd + (default=nobody)], + def_id=$withval) +AC_SUBST(def_id) + +AC_ARG_WITH(debug, +[ --with-debug=level Specify debugging level (0 means no debug support)], + AC_DEFINE_UNQUOTED(DEBUG, $withval)) + +AC_ARG_WITH(verbosity, +[ --with-verbosity=level Specify default message verbosity], + AC_DEFINE_UNQUOTED(VERBOSITY, $withval)) + +AC_ARG_WITH(hash-buckets, +[ --with-hash-buckets=num Number of hash buckets to use (default=1024). + The number actually used is the smallest power of two + greater or equal to the number specified here.], + powof2=1 + hashsz=0 + + while test $powof2 -lt "$withval" + do + powof2=`expr 2 '*' $powof2` + hashsz=`expr $hashsz '+' 1` + done + AC_DEFINE_UNQUOTED(HASH_SZ, $hashsz) +) + +AC_ARG_ENABLE(hash-debug, +[ --enable-hash-debug Debug hash tables (warning: massive output)], + test $enableval = "yes" && AC_DEFINE(DEBUG_HASH)) + +AC_ARG_ENABLE(rcsids, +[ --enable-rcsids Enable RCS IDs in executables (obsolete, currently ignored)], + have_rcsids=$enableval) + +AC_ARG_WITH(tcp-qtimeout, +[ --with-tcp-qtimeout=secs Specify default tcp query timeout (default=30)], + AC_DEFINE_UNQUOTED(TCP_TIMEOUT, $withval)) + +AC_ARG_ENABLE(tcp-subseq, +[ --enable-tcp-subseq Enable multiple dns querys using one + tcp connection], + test $enableval = "yes" && AC_DEFINE(TCP_SUBSEQ)) + +AC_ARG_WITH(par-queries, +[ --with-par-queries=num Specify default parallel query number (default=2)], + AC_DEFINE_UNQUOTED(PAR_QUERIES, $withval)) + +AC_ARG_WITH(max-nameserver-ips, +[ --with-max-nameserver-ips=num Specify maximum number of IP addresses used per nameserver obtained from NS records (default=3)], + AC_DEFINE_UNQUOTED(MAXNAMESERVIPS, $withval)) + +AC_ARG_ENABLE(specbuild, +[ --enable-specbuild Only used when building pdnsd from spec files], + specbuild=$enableval) + +AC_SUBST(specbuild) + +AC_ARG_WITH(thread-lib, +[ --with-thread-lib=lib Specify thread library, overriding automatic detection (for Linux only). + Possible values: LinuxThreads, + LinuxThreads2 (implements a fix for newer glibcs) + or NPTL (Native POSIX Thread Library)], + threadlib=$withval) + +AC_SUBST(threadlib) + +dnl Checks for programs. +AC_PROG_CC +AM_PROG_CC_STDC +AM_PROG_CC_C_O +AC_PROG_INSTALL + +dnl For dbm subsystem libraries +AC_PROG_RANLIB + +if test "$target" = "autodetect" ; then + AC_MSG_CHECKING([for autodetect build target]) + uname_sys=`uname` + if test $? -ne 0 ; then + AC_MSG_RESULT([failed]) + AC_MSG_ERROR([uname failed or was not found in path]) + else + case "${uname_sys}" in + Linux) + AC_MSG_RESULT(Linux) + AC_DEFINE(TARGET, TARGET_LINUX) + target="Linux" + ;; + FreeBSD|NetBSD|OpenBSD|Darwin) + AC_MSG_RESULT("${uname_sys}") + AC_DEFINE(TARGET, TARGET_BSD) + target="BSD" + ;; + CYGWIN*) + AC_MSG_RESULT("${uname_sys}") + AC_DEFINE(TARGET, TARGET_CYGWIN) + target="cygwin" + ;; + *) + AC_MSG_RESULT([failed]) + AC_MSG_ERROR( +[Your system type could not be identified. Try setting it manually using +--with-target]) + ;; + esac + fi +fi + +#if test "$target" = BSD ; then +# uname_sys=`uname` +# if test "$uname_sys" = FreeBSD ; then +# AC_MSG_CHECKING([for FreeBSD version]) +# osrel=`sysctl -n kern.osreldate` +# if test $osrel -ge 500016 ; then +# AC_MSG_RESULT([5.0 (>= 500016)]) +# freebsd_pthread="5" +# else +# AC_MSG_RESULT([<=5.0 (< 500016)]) +# freebsd_pthread="4" +# fi +# fi +#fi + +if test "$adisc" = "default"; then + if test "$target" = "cygwin" ; then +# Don't do UDP source address discovery on Cygwin platform by default. + adisc="no" + else + adisc="yes" + fi +fi + +test "$adisc" = "yes" && AC_DEFINE(SRC_ADDR_DISC) + + +dnl Checks for libraries. +if test "$target" = "Linux"; then +AC_MSG_CHECKING([if we can compile and link with -pthread]) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -pthread" +AC_LINK_IFELSE([ +#include + +void *thread_func(void *data) +{ + *((int *)data)=1; + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int val; + + if(pthread_create(&thread, NULL, thread_func, &val)) + return 1; + + if(pthread_join(thread,&retval)) + return 1; + + return (*((int *)retval)!=1); +} +], + gcc_pthread_flag="yes", gcc_pthread_flag="no") +CFLAGS="$old_CFLAGS" +AC_MSG_RESULT([$gcc_pthread_flag]) + + if test "$gcc_pthread_flag" = yes ; then + thread_CFLAGS="-pthread" + AC_SUBST(thread_CFLAGS) + else + AC_CHECK_LIB(pthread, pthread_create) + fi +fi +if test "$target" = "BSD" -a `uname` != Darwin ; then +# if test $freebsd_pthread = 4 ; then + thread_CFLAGS="-pthread" + AC_SUBST(thread_CFLAGS) +# else +# AC_CHECK_LIB(c_r, pthread_create, , +# AC_MSG_ERROR([You must have libc_r installed to build/run pdnsd!])) +# fi; +fi + +if test "$target" = "Linux" -a "$threadlib" = default; then +AC_MSG_CHECKING([if this is an NPTL-based system]) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $thread_CFLAGS" +AC_RUN_IFELSE([ +#include +#include +#include +#include +#include +#include + +/* All this function does is return its PID (in a roundabout way). */ +void *thread_func(void *data) +{ + *((int *)data)=getpid(); + return data; +} + +int main() +{ + pthread_t thread; + void *retval; + int err,mainpid,thrdpid; + + err=pthread_create(&thread, NULL, thread_func, &thrdpid); + if(err) { + fprintf(stderr,"pthread_create failed: %s\n",strerror(err)); + return 1; + } + err=pthread_join(thread,&retval); + if(err) { + fprintf(stderr,"pthread_join failed: %s\n",strerror(err)); + return 1; + } + mainpid=getpid(); + /* In LinuxThreads implementations, the pids of the threads will usually differ + in a non Posix-compliant way. */ + fprintf(stderr,"main pid=%d, thread pid=%d\n",mainpid,*((int *)retval)); + return (*((int *)retval)!=mainpid); +} +], +[ + AC_MSG_RESULT([yes]) + threadlib=nptl +], +[ + AC_MSG_RESULT([no]) + threadlib=linuxthreads +], +[ + AC_MSG_RESULT([couldn't run test program]) + threadlib=linuxthreads +]) +CFLAGS="$old_CFLAGS" +fi + +if test "$threadlib" = nptl -o "$threadlib" = NPTL; then + AC_DEFINE(THREADLIB_NPTL) +elif test "$threadlib" = linuxthreads2 -o "$threadlib" = LinuxThreads2 -o "$threadlib" = lt2; then + AC_DEFINE(THREADLIB_LINUXTHREADS2) +fi + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h sys/time.h syslog.h unistd.h) +AC_CHECK_HEADERS(sys/types.h sys/socket.h net/if.h netinet/in.h sys/poll.h,,, +[#include +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +]) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM +AC_CHECK_TYPES(socklen_t,, AC_DEFINE(socklen_t,int),[#include +#include +#include +#include ]) +AC_CHECK_TYPES([struct in6_addr, struct in_pktinfo, struct ifreq],,,[#include +#include +#include +#include ]) + +AC_MSG_CHECKING([for CPP C99 Variadic macro support]) +AC_COMPILE_IFELSE([ +#define a(...) junk(0,__VA_ARGS__) +extern void junk(int i,...); +int main() +{ + a(0); + a("a"); + a(0, "a", 1); + return 0; +} +], + cpp_c99_variadic="yes", cpp_c99_variadic="no") +AC_MSG_RESULT([$cpp_c99_variadic]) +if test $cpp_c99_variadic = yes; then + AC_DEFINE(CPP_C99_VARIADIC_MACROS) +else + if test "$GCC" != yes; then + AC_MSG_ERROR([Compiler must support C99 or gcc variadic macros]) + fi; +fi + +dnl Checks for library functions. +AC_FUNC_ALLOCA +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_TYPE_SIGNAL +AC_FUNC_VPRINTF +AC_SEARCH_LIBS(nanosleep, rt) +AC_SEARCH_LIBS(socket, socket,,,-lnsl) +AC_SEARCH_LIBS(inet_aton, resolv) +AC_CHECK_FUNCS(nanosleep gettimeofday mkfifo select socket strerror uname snprintf vsnprintf, true, + AC_MSG_ERROR([One of the functions required for pdnsd were not found.])) +AC_CHECK_FUNCS(poll, true, AC_DEFINE(NO_POLL)) +AC_CHECK_FUNCS(strdup strndup stpcpy stpncpy strlcpy mempcpy getline asprintf vasprintf getpwnam_r inet_ntop inet_pton) + +AC_OUTPUT([ +pdnsd.spec +Makefile +file-list.base +contrib/Makefile +doc/Makefile +doc/pdnsd.8 +doc/pdnsd.conf.5 +doc/pdnsd.conf +src/Makefile +src/pdnsd-ctl/Makefile +src/rc/Makefile +src/rc/RedHat/Makefile +src/rc/RedHat/pdnsd +src/rc/SuSE/Makefile +src/rc/SuSE/pdnsd +src/rc/Debian/Makefile +src/rc/Debian/pdnsd +src/rc/Slackware/Makefile +src/rc/Slackware/rc.pdnsd +src/rc/ArchLinux/Makefile +src/rc/ArchLinux/pdnsd +src/test/Makefile +PKGBUILD +]) diff --git a/service/src/main/jni/pdnsd/contrib/Makefile.am b/service/src/main/jni/pdnsd/contrib/Makefile.am new file mode 100644 index 0000000..5bdec20 --- /dev/null +++ b/service/src/main/jni/pdnsd/contrib/Makefile.am @@ -0,0 +1,2 @@ + +EXTRA_DIST = pdnsd_dhcp.pl dhcp2pdnsd change_pdnsd_server_ip.pl README diff --git a/service/src/main/jni/pdnsd/contrib/Makefile.in b/service/src/main/jni/pdnsd/contrib/Makefile.in new file mode 100644 index 0000000..a400dfe --- /dev/null +++ b/service/src/main/jni/pdnsd/contrib/Makefile.in @@ -0,0 +1,323 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = contrib +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = pdnsd_dhcp.pl dhcp2pdnsd change_pdnsd_server_ip.pl README +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu contrib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/contrib/README b/service/src/main/jni/pdnsd/contrib/README new file mode 100644 index 0000000..19e4f44 --- /dev/null +++ b/service/src/main/jni/pdnsd/contrib/README @@ -0,0 +1,30 @@ +Last revised: 27 July 2003 by Paul Rombouts + +This directory contains user-contributed scripts for use with pdnsd. +So far there are: + +pdnsd_dhcp.pl, save_ram.pl + A perl script contributed by Marko Stolle (derived from a script by + Mike Stella) to watch a ISC DHCPD leases file and add local records for + the hosts listed there. This makes pdnsd useable in a DHCP setup. + Please look into the script for usage instructions (you will probably + also need to customize some settings there). + For details about save_ram.pl, please look into pdnsd_dhcp.pl + +dhcp2pdnsd + A rc script for pdnsd, also by Marko Stolle. You might need to change + it slightly to make it run with your distro. + +change_pdnsd_server_ip.pl + A perl script contributed by Paul Rombouts for automatically updating + the configuration file if the DNS server configuration has changed. + For instance, you could place the following line in the script + /sbin/ifup-local + + /usr/local/sbin/change_pdnsd_server_ip.pl isplabel "$DNS" /etc/pdnsd.conf + + where $DNS contains the IP addresses (in comma separated format) of the + DNS servers obtained by DHCP negotiation. The perl script only + overwrites /etc/pdnsd.conf if the DNS configuration has actually + changed, in which case the previous configuration file is saved as + /etc/pdnsd.conf.save diff --git a/service/src/main/jni/pdnsd/contrib/change_pdnsd_server_ip.pl b/service/src/main/jni/pdnsd/contrib/change_pdnsd_server_ip.pl new file mode 100644 index 0000000..1eafca0 --- /dev/null +++ b/service/src/main/jni/pdnsd/contrib/change_pdnsd_server_ip.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl -w +# +# A Perl script to change the ip addresses of dns servers +# in the pdnsd configuration file. +# +# Written by Paul A. Rombouts +# +# This file Copyright 2002, 2004 Paul A. Rombouts +# It may be distributed under the GNU Public License, version 2, or +# any higher version. See section COPYING of the GNU Public license +# for conditions under which this file may be redistributed. +# + +use strict; + +unless(@ARGV) {die "Error: no label specified.\n"} +my $label=shift; +unless(@ARGV) {die "Error: no DNS addresses specified.\n"} +my $dns_str=shift; +my $pdnsd_conf='/etc/pdnsd.conf'; +if(@ARGV) { + $pdnsd_conf=shift; + if(@ARGV) {warn "Warning: spurious arguments ignored: @ARGV\n"} +} + +#unless($label =~ /^\".*\"$/) {$label="\"$label\""} +#unless($dns_str =~ /^\".*\"$/) {$dns_str =~ s/^[\s,]*/\"/; $dns_str =~ s/[\s,]*$/\"/} +#unless($dns_str =~ /\"\s*\,\s*\"/) {$dns_str =~ s/[\s,]+/","/g} + +my @lines=(); +my $found_section=0; +my $changed=0; +my $ip_patt = qr/^((?:[^#]*?(?:\{|;))*?)(\s*ip\s*=\s*)("?[\w.:]+"?(?:\s*,\s*"?[\w.:]+"?)*)\s*;/; + +open(CONFFILE,$pdnsd_conf) or die "Can't open $pdnsd_conf: $!\n"; + +while() { + if(/^\s*server\s*\{/) { + my $sect_beg=$#lines+1; + my $sect_end; + my $found_label=0; + LOOP: { + do { + push @lines,$_; + if(/^(?:.*(?:\{|;))?\s*label\s*=\s*"?\Q$label\E"?\s*;/) { + if($found_label++) { + warn "Server section with multiple labels found.\n"; + close(CONFFILE); + exit 2; + } + } + if(/\}\s*$/) { + $sect_end=$#lines; + last LOOP; + } + } while(); + } + unless(defined($sect_end)) { + warn "Server section without proper ending found.\n"; + close(CONFFILE); + exit 2; + } + if(!$found_label) {next} + if(!($found_section++)) { + my $found_ip=0; + for(my $i=$sect_beg; $i<=$sect_end;++$i) { + if($lines[$i] =~ $ip_patt) { + my $matched=''; my $rest; + do { + $rest=$'; + if(!($found_ip++)) { + if($3 eq $dns_str) { + $matched.=$&; + } + else { + $matched.="$1$2$dns_str;"; + $changed=1; + } + } + else { + $matched.=$1; + $changed=1; + } + } while($rest =~ $ip_patt); + $lines[$i] = $matched.$rest; + } + } + if(!$found_ip) { + unless($lines[$sect_end] =~ s/\}\s*$/ ip=$dns_str;\n$&/) { + warn "Can't add ip specification to server section labeled $label.\n"; + close(CONFFILE); + exit 2; + } + $changed=1; + } + } + else { + splice @lines,$sect_beg; + $changed=1; + } + } + else {push @lines,$_} +} + +close(CONFFILE) or die "Can't close $pdnsd_conf: $!\n"; + +if(!$found_section) { + warn "No server sections labeled $label found.\n"; + exit 2; +} +elsif(!$changed) { + exit 0; +} + +rename($pdnsd_conf,"$pdnsd_conf.save") or die "Can't rename $pdnsd_conf: $!\n"; + +unless((open(CONFFILE,">$pdnsd_conf") or (warn("Can't open $pdnsd_conf for writing: $!\n"),0)) and + (print CONFFILE (@lines) or (warn("Can't write to $pdnsd_conf: $!\n"),0)) and + (close(CONFFILE) or (warn("Can't close $pdnsd_conf after writing: $!\n"),0))) { + rename("$pdnsd_conf.save",$pdnsd_conf) or die "Can't rename $pdnsd_conf.save: $!\n"; + exit 3; +} + +exit 1; diff --git a/service/src/main/jni/pdnsd/contrib/dhcp2pdnsd b/service/src/main/jni/pdnsd/contrib/dhcp2pdnsd new file mode 100644 index 0000000..73b631c --- /dev/null +++ b/service/src/main/jni/pdnsd/contrib/dhcp2pdnsd @@ -0,0 +1,45 @@ +#! /bin/sh +# +# dhcp2pdnsd Start/Stop DHCP to DNS update script +# +# chkconfig: 345 96 99 +# description: DHCP to DNS update script +# processname: dhcp2pdnsd.pl +# +# $Id: dhcp2pdnsd,v 1.1 2001/03/25 20:01:34 tmm Exp $ + +where="/usr/local/bin/" +name="pdnsd_dhcp.pl" + +# Source function library. +. /etc/rc.d/init.d/functions + +# Get config. +. /etc/sysconfig/network + +# See how we were called. +case "$1" in + start) + $where$name > /dev/null 2> /dev/null & + action "Starting DHCP to DNS update script: " /bin/true + ;; + stop) + p=`ps h -C $name | awk '{print $1}'` + [ $p -gt 0 ] 2> /dev/null && kill $p && action "Stopping DHCP to DNS update script: " /bin/true + [ $p -gt 0 ] 2> /dev/null || action "Stopping DHCP to DNS update script: " /bin/false + ;; + status) + p=`ps h -C $name | awk '{print $1}'` + [ $p -gt 0 ] 2> /dev/null && echo 'running as '$p + [ $p -gt 0 ] 2> /dev/null || echo 'not running' + ;; + restart|reload) + $0 stop + $0 start + ;; + *) + echo "Usage: dhcp2pdnsd {start|stop|status|restart|reload}" + exit 1 +esac + +exit 0 diff --git a/service/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl b/service/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl new file mode 100644 index 0000000..9cf0c87 --- /dev/null +++ b/service/src/main/jni/pdnsd/contrib/pdnsd_dhcp.pl @@ -0,0 +1,246 @@ +#!/usr/bin/perl +# $Id: pdnsd_dhcp.pl,v 1.2 2001/03/25 20:01:34 tmm Exp $ +########################################################################## +# +# Filename: pdnsd_dhcp.pl +# Description: Dynamic DNS-DHCP update script for pdnsd +# Author: Mike Stella +# Modified by: Marko Stolle +# Created: November 19, 2001 +# Last Updated: February 28, 2001 +# Email: fwd2m@gmx.de +# +########################################################################### +# +# This code is Copyright (c) 1998-2001 by Mike Stella and Marko Stolle +# +# NO WARRANTY is given for this program. If it doesn't +# work on your system, sorry. If it eats your hard drive, +# again, sorry. It works fine on mine. Good luck! +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +########################################################################### +# +# This script reads a dhcpd.leases file and dynamically updates pdnsd with +# hostname and ip information. +# +# It assumes that your DHCP server recieves hostnames from the +# clients, and that your clients offer their hostnames to the server. +# Some versions of Linux DHCP clients don't do that. I use ISC's +# DHCPD, found at http://www.isc.org - though others may work just +# fine. +# +# This version of the script updates the pdnsd database. The status +# control socket of pdnsd has to be enabled (psnsd -d -s). +# +########################################################################### +# +# 02/20/2001 - first working version +# 02/21/2001 - security patches by Thomas Moestl +# 02/22/2001 - re-read dhcpd.leases if ttl has expireds since last update +# 02/24/2001 - try to get domainname if not specified +# 02/28/2001 - randomized temporary filename +# added possibility to save some RAM (read below) +# +########################################################################### + + +# You may save some memory if you use absolute values with sysopen +# in sub update_dns and don't use tmpnam().. +# Just switch the '#' in front of the 'until sysopen' in the sub +# update_dns, check the necessary modes on your system using save_ram.pl +# and add a '#' in front of the following three lines. +# Not using the tmpnam() function may open a security breach on systems +# with not absolute trustworthy local users (Risk: a user may write a +# script which creates files with the same names as this script and block +# it that way. Unlikely because the filenames are now even without tmpnam() +# randomized and an attacker has to create a very large number of files.) + +use Fcntl; +use strict; +use POSIX qw(tmpnam); + +$|=1; + +########################################################################### +### Globals - you can change these as needed + +# Domain name +# if not changed script will try to get it from the system +my $domain_name = "domain"; + +# DHCPD lease file +my $lease_file = "/var/lib/dhcp/dhcpd.leases"; + +# path to pdnsd-ctl +my $pdnsd_ctl = "/usr/local/sbin/pdnsd-ctl"; + +# owning name server for the newly added records +my $nameserver = "localhost."; + +# TTL (Time To Live) for the new records +my $ttl = "86400"; + +# number of seconds to check the lease file for updates +my $update_freq = 30; + +my $debug = 0; + +########################################################################### +### Don't mess with anything below unless you REALLY need to modify the +### code. And if you do, please let me know, I'm always interested in +### in improving this program. + +# Make a pid file +`echo $$ > /var/run/pdnsd_update.pid`; + +my $logstr; +my $modtime = 0; +my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMP} || $ENV{TEMP}; + +use vars qw (%db); + +my $version = "1.03"; + + +########################################################################### +# Main Loop + + # try to find domainname if necessary + if ($domain_name eq "domain") { + $domain_name = `dnsdomainname`; + } + else { + $domain_name = "$domain_name\n"; + } + +while (1) { + + # check the file's last updated time, if it's been changed, update + # the DNS and save the time. Update DNS even if there a no changes on + # the leases file if ttl since last DNS update has expired. + # This will ALWAYS run once - on startup, since $modtime starts at zero. + + + my @stats = stat ($lease_file); + + + if (($stats[9] > $modtime) or (time >= $modtime+$ttl)){ + + # clear the old hash + undef %db; + + printf STDERR "updating DNS with dhcpd.leases\n"; + $modtime = time; + &read_lease_file; + &update_dns; + } + + # wait till next check time + sleep $update_freq; + +} # end main +########################################################################### + + +### write out the import file +sub update_dns { + my ($ip, $hostname, $fname); + + do { $fname = tmpnam() } + until sysopen(DNSFILE, $fname, O_WRONLY|O_CREAT|O_EXCL, 0600); +# do { $fname = "$temp_dir/d2d".int(rand(time())) } +# until sysopen(DNSFILE, $fname, 1|64|128, 0600); + + while (($hostname,$ip) = each (%db)) { + print DNSFILE "$ip $hostname.$domain_name"; + } + close DNSFILE; + + system ("$pdnsd_ctl source $fname $nameserver $ttl"); + unlink($fname); +} + + +### reads the lease file & makes a hash of what's in there. +sub read_lease_file { + + unless (open(LEASEFILE,$lease_file)) { + #`logger -t dns_update.pl error opening dhcpd lease file`; + print STDERR "Can't open lease file\n"; + return; + } + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + my $curdate = sprintf "%02d%02d%02d%02d%02d%20d%20d", + ($year+1900),($mon+1),$mday,$hour,$min,$sec; + + ## Loop here, reading from LEASEFILE + while () { + my ($ip, $hostname, $mac, $enddate,$endtime); + + if (/^\s*lease/i) { + + # find ip address + $_ =~ /^\s*lease\s+(\S+)/; + $ip = $1; + + # do the rest of the block - we're interested in hostname, + # mac address, and the lease time + while ($_ !~ /^}/) { + $_ = ; + # find hostname + if ($_ =~ /^\s*client/i) { + #chomp $_; + #chop $_; + $_ =~ /\"(.*)\"/; + $hostname = $1; + + # change spaces to dash, remove dots - microsoft + # really needs to not do this crap + $hostname =~ s/\s+/-/g; + $hostname =~ s/\.//g; + } + # get the lease end date + elsif ($_ =~ /^\s*ends/i) { + $_ =~ m/^\s*ends\s+\d\s+([^;]+);/; + $enddate = $1; + $enddate =~ s|[/: ]||g; + } + } + # lowercase it - stupid dhcp clients + $hostname =~ tr/[A-Z]/[a-z]/; + + ($debug < 1 ) || print STDERR "$hostname $ip $enddate $curdate\n"; + + # Store hostname/ip in hash - this way we can do easy dupe checking + if (($hostname ne "") and ($enddate > $curdate)) { + $db{$hostname} = $ip; + } + } + } + close LEASEFILE; +} + +### left around for testing +sub print_db { + my ($key,$value); + + while (($key,$value) = each (%db)) { + print "$key - $value\n"; + } +} + diff --git a/service/src/main/jni/pdnsd/depcomp b/service/src/main/jni/pdnsd/depcomp new file mode 100644 index 0000000..04701da --- /dev/null +++ b/service/src/main/jni/pdnsd/depcomp @@ -0,0 +1,530 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2005-07-09.11 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mecanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/service/src/main/jni/pdnsd/doc/Makefile.am b/service/src/main/jni/pdnsd/doc/Makefile.am new file mode 100644 index 0000000..b3e2198 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/Makefile.am @@ -0,0 +1,28 @@ + +man_MANS = pdnsd.8 pdnsd-ctl.8 pdnsd.conf.5 + +# Note: pdnsd-ctl.8, pdnsd.conf.5.in, dl.html and the txt docs are handled by dist-hook rule. +EXTRA_DIST = pdnsd.conf.in pdnsd.8.in \ + html/dl.html.in html/doc.html html/faq.html html/index.html \ + doc_makefile html/doc_makefile txt/doc_makefile \ + html2confman.pl html/htmlsubst.pl + +# XXX: Do not insist to set the config file owner to root to avoid breaking RPM +# builds +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" + if test `whoami` = "root"; then \ + $(INSTALL) -o 0 -g 0 -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + else \ + $(INSTALL) -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + fi + +dist-hook: doc_makefile + @$(MAKE) -f doc_makefile doc + cp -p --parents pdnsd-ctl.8 pdnsd.conf.5.in \ + html/dl.html txt/faq.txt txt/intro.txt txt/manual.txt \ + $(distdir) + +distclean-local: doc_makefile + @$(MAKE) -f doc_makefile clean + diff --git a/service/src/main/jni/pdnsd/doc/Makefile.in b/service/src/main/jni/pdnsd/doc/Makefile.in new file mode 100644 index 0000000..7f07fe6 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/Makefile.in @@ -0,0 +1,483 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = doc +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.8.in $(srcdir)/pdnsd.conf.5.in \ + $(srcdir)/pdnsd.conf.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd.8 pdnsd.conf.5 pdnsd.conf +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +man5dir = $(mandir)/man5 +am__installdirs = "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +man_MANS = pdnsd.8 pdnsd-ctl.8 pdnsd.conf.5 + +# Note: pdnsd-ctl.8, pdnsd.conf.5.in, dl.html and the txt docs are handled by dist-hook rule. +EXTRA_DIST = pdnsd.conf.in pdnsd.8.in \ + html/dl.html.in html/doc.html html/faq.html html/index.html \ + doc_makefile html/doc_makefile txt/doc_makefile \ + html2confman.pl html/htmlsubst.pl + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd.8: $(top_builddir)/config.status $(srcdir)/pdnsd.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +pdnsd.conf.5: $(top_builddir)/config.status $(srcdir)/pdnsd.conf.5.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +pdnsd.conf: $(top_builddir)/config.status $(srcdir)/pdnsd.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-man5: $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)" + @list=''; test -n "$(man5dir)" || exit 0; \ + { for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.5[a-z]*$$/p'; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ + done; } + +uninstall-man5: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man5dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.5[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + test -z "$$files" || { \ + echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(man5dir)" && rm -f $$files; } +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)" + @list=''; test -n "$(man8dir)" || exit 0; \ + { for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + test -z "$$files" || { \ + echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(man8dir)" && rm -f $$files; } +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @list='$(MANS)'; if test -n "$$list"; then \ + list=`for p in $$list; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \ + if test -n "$$list" && \ + grep 'ab help2man is required to generate this page' $$list >/dev/null; then \ + echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \ + grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \ + echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \ + echo " typically \`make maintainer-clean' will remove them" >&2; \ + exit 1; \ + else :; fi; \ + else :; fi + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile $(MANS) +installdirs: + for dir in "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man5 install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-man + +uninstall-man: uninstall-man5 uninstall-man8 + +.MAKE: install-am install-data-am install-strip + +.PHONY: all all-am check check-am clean clean-generic dist-hook \ + distclean distclean-generic distclean-local distdir dvi dvi-am \ + html html-am info info-am install install-am install-data \ + install-data-am install-data-hook install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-man5 \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am uninstall-man uninstall-man5 uninstall-man8 + + +# XXX: Do not insist to set the config file owner to root to avoid breaking RPM +# builds +install-data-hook: + $(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" + if test `whoami` = "root"; then \ + $(INSTALL) -o 0 -g 0 -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + else \ + $(INSTALL) -m 644 pdnsd.conf "$(DESTDIR)$(sysconfdir)/pdnsd.conf.sample" ; \ + fi + +dist-hook: doc_makefile + @$(MAKE) -f doc_makefile doc + cp -p --parents pdnsd-ctl.8 pdnsd.conf.5.in \ + html/dl.html txt/faq.txt txt/intro.txt txt/manual.txt \ + $(distdir) + +distclean-local: doc_makefile + @$(MAKE) -f doc_makefile clean + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/doc/doc_makefile b/service/src/main/jni/pdnsd/doc/doc_makefile new file mode 100644 index 0000000..f5e8f86 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/doc_makefile @@ -0,0 +1,38 @@ +# This file was written by Paul Rombouts. +# Because pdnsd currently has a very idiosyncratic method of building documentation +# I prefer to keep the actual build rules outside of the Makefiles an ordinary user +# would use to compile pdnsd and therefore I have put them into separate 'doc_makefile's. +# +# To rebuild pdnsd docs after you have modified something that other files depend on, +# run 'make -f doc_makefile doc' in the doc/ directory. +# This makefile is also invoked when you build a pdnsd distribution tarball +# using 'make dist' in the toplevel pdnsd source directory. +# +# If anyone thinks there is a much more elegant method for building the pdnsd docs +# using a conventional autoconf/automake process, please let me know. + +versionfile = ../version + +doc: pdnsd-ctl.8 pdnsd.conf.5.in html txt +.PHONY: pdnsd-ctl.8 doc html txt clean + +pdnsd-ctl.8: + @pver=`cat $(versionfile)` && \ + mver=`perl -e 'while(<>) {if(/^\s*\.TH(?:\s+(?:"[^"]*"|[^"\s]+)){3}\s+"pdnsd\s+([^"]*)"/) {print "$$1\n";exit 0}} \ + die "Cannot find version in $$ARGV\n"' $@` && { \ + test "$$mver" = "$$pver" || { \ + perl -p -i.makesave -e 's/^(\s*\.TH(?:\s+(?:"[^"]*"|[^"\s]+)){3}\s+"pdnsd\s+)[^"]*(")/$${1}'"$$pver"'$${2}/' $@ && \ + echo "Updated version in $@: $$mver -> $$pver"; \ + }; \ + } + +pdnsd.conf.5.in: html/doc.html html2confman.pl + perl html2confman.pl $< > $@ + +html txt: + @cd $@ && $(MAKE) -f doc_makefile + +clean: + @rm -fv pdnsd.conf.5.in + @cd html && $(MAKE) -f doc_makefile clean + @cd txt && $(MAKE) -f doc_makefile clean diff --git a/service/src/main/jni/pdnsd/doc/html/dl.html b/service/src/main/jni/pdnsd/doc/html/dl.html new file mode 100644 index 0000000..3f9c614 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html/dl.html @@ -0,0 +1,96 @@ + + + + pdnsd Download Page + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

pdnsd Download Page

+ +

The original author of pdnsd is Thomas Moestl, + but since 2003, he no longer maintains pdnsd. + However, Paul A. Rombouts has extensively revised the code and maintains + a version with many fixes and improvements at + http://members.home.nl/p.a.rombouts/pdnsd.html.
+ He has pre-patched tarballs and RPM packages available for download at this site.
+

+

+ If you are interested in the very latest code or if you want to participate in + pdnsd development, checkout the pdnsd git repository + at gitorious.org. +

+ + The most recent tarball is pdnsd-1.2.9b-par.tar.gz + (GPG signature).
+ The most recent RPM packages are: + + + + + + + + + + + + + + + + + + + + + +
Package NameSizeDescription
pdnsd-1.2.9b-par.src.rpm514kBSource RPM (binary packages can also be built directly from the tarball). +
pdnsd-1.2.9b-par_sl6.i686.rpm???i686 binary built on a Scientific Linux 6.2 system. +
pdnsd-1.2.9b-par_sl6.x86_64.rpm282kBx86_64 binary built on a Scientific Linux 6.2 system. +
+ If you want to check the signatures on these packages you will need a copy of my GPG key + which you can get here + or from a public key server. +

+ There are also Debian, + Ubuntu, + Gentoo, + Mandriva + and FreeBSD + packages, but these may not include the latest version.
+ If you are looking for other versions released by Paul Rombouts, visit this + download directory. +

+
+
+

+ Last revised: 24 Apr 2012 by Paul A. Rombouts +

+ + diff --git a/service/src/main/jni/pdnsd/doc/html/dl.html.in b/service/src/main/jni/pdnsd/doc/html/dl.html.in new file mode 100644 index 0000000..dffe2b0 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html/dl.html.in @@ -0,0 +1,96 @@ + + + + pdnsd Download Page + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

pdnsd Download Page

+ +

The original author of pdnsd is Thomas Moestl, + but since 2003, he no longer maintains pdnsd. + However, Paul A. Rombouts has extensively revised the code and maintains + a version with many fixes and improvements at + http://members.home.nl/p.a.rombouts/pdnsd.html.
+ He has pre-patched tarballs and RPM packages available for download at this site.
+

+

+ If you are interested in the very latest code or if you want to participate in + pdnsd development, checkout the pdnsd git repository + at gitorious.org. +

+ + The most recent tarball is pdnsd-$version.tar.gz + (GPG signature).
+ The most recent RPM packages are: + + + + + + + + + + + + + + + + + + + + + +
Package NameSizeDescription
pdnsd-$version.src.rpm$sizeof("$HOME/rpmbuild/SRPMS/pdnsd-$version.src.rpm")Source RPM (binary packages can also be built directly from the tarball). +
pdnsd-${version}${extver}.${arch}.rpm$sizeof("$HOME/rpmbuild/RPMS/pdnsd-${version}${extver}.${arch}.rpm")${arch} binary built on a $system. +
pdnsd-${version}${extver}.${arch2}.rpm$sizeof("$HOME/rpmbuild/RPMS/pdnsd-${version}${extver}.${arch2}.rpm")${arch2} binary built on a $system. +
+ If you want to check the signatures on these packages you will need a copy of my GPG key + which you can get here + or from a public key server. +

+ There are also Debian, + Ubuntu, + Gentoo, + Mandriva + and FreeBSD + packages, but these may not include the latest version.
+ If you are looking for other versions released by Paul Rombouts, visit this + download directory. +

+
+
+

+ Last revised: $date by Paul A. Rombouts +

+ + diff --git a/service/src/main/jni/pdnsd/doc/html/doc.html b/service/src/main/jni/pdnsd/doc/html/doc.html new file mode 100644 index 0000000..95828c5 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html/doc.html @@ -0,0 +1,2630 @@ + + + + pdnsd Documentation + + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

pdnsd Documentation

+ This is the "official" pdnsd documentation and reference written by + Thomas Moestl with revisions by + Paul A. Rombouts.
+ This manual is a part of the pdnsd package, and may be distributed in + original or modified form under terms of the GNU General Public + License, as published by the Free Software Foundation; either version + 3, or (at your option) any later version.
+ You can find a copy of the GNU GPL in the file COPYING in the source or documentation directory.
+ This manual is up-to-date for version 1.2.9b. For older documentation, please refer to the doc + directory of the respective pdnsd package.
+ If you want a quicker introduction to pdnsd, you can try some of the + HOWTOs available on the web. + For Apple Mac users, Brian Wells has published a good HOWTO at + http://web.mac.com/brianwells/main/pdnsd.html. + + +

0. Installation

+

0.1 Installing binary RPM's

+ To install a binary RPM, just do
+

rpm -i pdnsd-<version>.rpm

+ This should install pretty much everything automatically. The only thing left + for you to do is adapt your configuration file (stored in /etc/pdnsd.conf) + according to your needs (see below). + In the Red Hat and SuSE RPMs, a start script is also installed; read the section + 0.4, Start at Boot Time about that. + +
+

0.2 Building RPM's

+ It is possible to build a binary RPM from a source package using the command
+

rpmbuild --rebuild pdnsd-<version>.src.rpm

+ or alternatively from a tarball using the command
+

rpmbuild -tb pdnsd-<version>.tar.gz

+ You can do this as root, but it is safer to build a binary package first as a normal user, + and then, when all has gone well, install the resulting binary package as root as in the previous section. + How to build an RPM package without being root is described at + + http://www.ibm.com/developerworks/linux/library/l-rpm1/.

+ Several pdnsd-specific options are available when building RPM packages: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ --with isdn + + Has the same effect as --enable-isdn (see below). +
+ --without poll + + Has the same effect as --disable-poll (see below). +
+ --without nptl + + Has the same effect as --with-thread-lib=linuxthreads (see below). +
+ --with ipv6 + + Has the same effect as --enable-ipv6 (see below). +
+ --without tcpqueries + + Has the same effect as --disable-tcp-queries (see below). +
+ --without debug + + Has the same effect as --with-debug=0 (see below). +
+ --define "distro <distro>" + + Has the same effect as --with-distribution=<distro> (see below). +
+ --define "run_as_user <user>" + + Has the same effect as --with-default-id=<user> (see below).
+ For RPMs the default <user> is "pdnsd". +
+ --define "run_as_uid <uid>" + + If the user defined by the previous option does not exist when the RPM is installed, + the pre-install script will try to create a new user with numerical id <uid>. + The default is to let the system choose the numerical id at install time. +
+ --define "cachedir <dir>" + + Has the same effect as --with-cachedir=<dir> (see below). +
+ You can also configure which compiler flags will be used by setting the environment variable + CFLAGS. + Using a bash shell, you can do that on the command line like this: +  CFLAGS="-O1 -Wall" rpmbuild ...
+ This is useful if you prefer a different level of optimization, for instance. + +
+

0.3 Installing from pure sources (tar archives or git repositories)

+

0.3.1 Setting up the source code tree

+ Source code is available in the form of snapshots (tarballs) or a git repository + with the very latest development code and a (nearly) complete history of all the revisions. + Cloning a git repository is useful if you need a recent fix or feature + that is not yet contained in a main release or you want to participate in pdnsd development. + Otherwise you will probably find the tarballs more convenient because they are much more compact. +
0.3.1.1 Unpacking a tar archive
+ The pdsnsd snapshot releases come in the form of a gzip'ed tar archive. + To decompress it (using a modern tar) do
+

tar -xzf pdnsd-<version>.tar.gz

+ If your tar doesn't do this, use:
+

gzip -dc pdnsd-<version>.tar.gz | tar -xf -

+
0.3.1.2 Cloning a git repository
+ To clone a git repository you need to install, if not already installed, + the git version control system, which is available as a package in most modern Linux distributions. + Then run the command:
+

git clone git://gitorious.org/pdnsd/pdnsd.git pdnsd

+ In rare cases, if you are behind some kind of firewall, the special git protocol can't be used + and you will need to fall back to the http protocol. + See the gitorious.org website or git documentation for more information. + +

0.3.2 Configuring the source

+ Change into the pdnsd source directory and run configure. It takes the following command line + options (if you do not specify an option, defaults will be used):
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ --prefix=dir + + Specify the prefix directory. The pdnsd files are installed in subdirectories + of the prefix, the pdnsd and pdnsd-ctl executables are for example installed + in the sbin subdirectory of the prefix. The default for this is /usr/local; + you might want to set this to /usr (using --prefix=/usr). +
+ --sysconfdir=dir + + Specify the config directory. pdnsd expects its pdnsd.conf file to reside + there if the -c option is not given at startup. + The default for this is the etc subdirectory of your prefix, e.g. /usr/local/etc + if you did not specify a prefix. To set this e.g. to /etc, use --sysconfdir=/etc. +
+ --with-distribution=distro + + Specify target distribution (default=Generic; others: RedHat, SuSE, Debian)
+ See below for the effect of these settings. +
+ --with-target=platform + + Change compilation target platform (default: autodetect; others: Linux, BSD, Cygwin).
+ autodetect will attempt to detect whether you are using Linux, *BSD or Cygwin and + should normally be sufficient. If this does not work, try specifying + your system manually (for the Darwin platform (Apple Mac OS X) specify BSD here). +
+ --with-cachedir=dir + + Default directory for pdnsd cache (default=/var/cache/pdnsd)
+ This setting can be changed via config file settings when pdnsd has been built. +
+ --with-hash-buckets=num + + Number of hash buckets to use (default=1024). The default should be + sufficient for most purposes, but if you want to store a large number of names + in the cache, cache lookups may be faster if the number of hash buckets + is comparable to the number of names stored in the cache. + The number actually used is the smallest power of two + greater or equal to the number specified here. +
+ --enable-isdn + + Enable ISDN support
+ This option will work only on Linux and may cause problems with 2.0.x or + old 2.2.x kernels. You will need it for a proper if uptest + under Linux for ISDN ppp devices. +
+ --disable-ipv4 + + Disable IPv4 networking support (default=enabled) +
+ --enable-ipv6 + + Enable IPv6 networking support.
+ If your OS does support IPv6 properly, you should be able to serve also + IPv4 queries using this. Normally, this is disabled and you won't need + it. +
+ --disable-ipv4-startup + + Disable IPv4 on pdnsd startup by default (default=enabled) +
+ --enable-ipv6-startup + + Enable IPV6 on pdnsd startup by default (default=IPv4). + These options are only defaults, you can specify on + the command line or in the config files which IP version + will really be used. + Normally, you won't need to change these. +
+ --disable-udp-queries + + Disable UDP as query method. You shouldn't need to change + this. +
+ --disable-tcp-queries + + Disable TCP as query method. This only effects the querying of + name servers by pdnsd, not the ability of pdnsd to answer + TCP queries from clients. + TCP queries are slower than UDP queries, but can be more secure + against certain types of attacks and are able to handle large answers. + For normal use this can be disabled. + (Note that the default has changed: TCP-query support + is now compiled in by default, but it still depends on the run-time + options whether it is actually used.) +
+ --with-query-method=qm + + Specify the query method (default=udponly, others: tcponly, tcpudp, udptcp). + If you have enabled both UDP and TCP queries, this lets you control + which query method pdnsd will use by default. tcpudp will try TCP + first and fall back to UDP if TCP is not supported by the server; + udptcp will try UDP first and, if the answer was truncated, will repeat + the query using TCP. + udponly and tcponly should be clear. Note that this only effects + the compiled-in default; the query method can still be changed using + command-line options or options in the configuration file. +
+ --disable-tcp-server + + Disable the TCP server. + In this case pdnsd will not be able to respond to TCP queries from clients. + This may cause problems with very large answers. +
+ --disable-src-addr-disc + + Disable the UDP source address discovery.
+ You need this only if you have trouble with messages saying + "could not discover udp source address".
+ For the Cygwin target, this option is disabled by default. +
+ --disable-poll + + Disable poll(2) and use select(2) (default=enabled)
+ You will normally not need this. +
+ --disable-new-rrs + + Since version 1.2.9 this option is obsolete and ignored. + It is now possible to configure for each RR type separately whether it is + cacheable by pdnsd by editing the file src/rr_types.in. + The comments in this file explain how to do this. +
+ --enable-strict-rfc2181 + + Enforce strict RFC 2181 compliance.
+ This will cause pdnsd to reject DNS answers with incorrect + timestamp settings (multiple RRs of the same type and for the same domain with + different TTLs). Normally not needed. +
+ --enable-underscores + + This option is obsolete. Since version 1.2, pdnsd places no restrictions + on the types of characters in domain names (there are still a few restrictions + for locally defined names, though). +
+ --with-random-device=device + + Specify random device; default: C Library random() PRNG
+ pdnsd uses (pseudo-) random numbers as query IDs for security reasons + (this makes forging DNS answers more difficult). This option + controls where pdnsd gets these from. The default is the C library + random() function, which is relatively weak. + You can specify a device like /dev/urandom here if you like; pdnsd will read + random numbers from it 16-bit-wise. /dev/urandom is present under Linux and + most BSD derivates. You should not use /dev/random - it is more secure, but + may block and delay pdnsd's answers for a long time.
+ You can specify arc4random to use the BSD arc4random() + library function (default for FreeBSD target), which is considered safe.
+ You can also specify random as device to use the C Library + random() function (described above). +
+ --with-default-id=user + + Specify default user for pdnsd (default=nobody). + This is the user that will be entered for the run_as + option in the config file (see below) that will be installed during make install. + You can change this any time in your config file. +
+ --with-debug=level + + Specify debugging level. Normally you can safely switch debugging off + by setting the level to 0. This will increase speed (although only + marginally) and save space in the executable (only about 12kB). + However, more significant may be the savings in stack space, especially + if pdnsd is put under heavy load and there are many simultaneous + running threads.
+ Presently the only defined debug levels are in the range 0 - 9. + Setting the level to 9 enables hex dumps of the queries and replies + pdnsd receives and should normally not be needed. Debug output will only + be generated if you turn on special switches; it might be useful for + debugging your config files, so I recommend using the default (1). + However, if you use pdnsd under heavy load, a better strategy may be + to compile one version of pdnsd without debug support (configured with + --with-debug=0) for production use, and one version with + with debug support (e.g. --with-debug=9) + for diagnostic purposes. +
+ --with-verbosity=level + + Specify default message verbosity. The default should be ok. +
+ --enable-rcsids + + Enable RCS IDs in executables (default=disabled).
+ For personal use, there is no need to do this. If you build rpm's, it + might have advantages. +
+ --enable-tcp-subseq + + Enable subsequent tcp queries. The DNS protocol standard + requires that servers must be capable of answering multiple + subsequent queries that are sent over the same tcp connection, and that + the server may only close the connection by himself after a certain + timeout. This feature is rarely used, but may make denial-of-service + attacks easier, as it allows for an attacker to hold a connection open + a long time (although the attacker's IP is most likely revealed then). + For full standard compliance, you should use this option. + If you do not use --enable-tcp-server, is option is not honored. +
+ --with-tcp-qtimeout=secs + + Specify default tcp query timeout after which the connection is closed + if no full query has been received. The default is 30s. + You can also change this option at run time using the tcp_qtimeout + config file option. + If you do not use --enable-tcp-server, is option is not honored. +
+ --with-par-queries=num + + Specify the default number of queries that can be executed in parallel. + You can also change this option at run time using the par_queries + config file option. See the description of that option for an explanation + of what it really does.
+ The default for this option is 2. +
+ --with-max-nameserver-ips=num + + New in version 1.2.9b: + Specify the maximum number of IP addresses that can be used per nameserver obtained + from NS records (when resolving names recursively). + Just one IP address per nameserver is sufficient in the vast majority of cases + (and this was the strategy used by pdnsd in previous versions), + but in rare cases this will cause unnecessary resolve failures if the address chosen + for each nameserver happens to be unreachable while the other addresses would lead to + successful resolution.
+ The default for this option is 3. +
+ --with-thread-lib=lib + + Added by Paul Rombouts: Use this option if you experience problems with + signal handling under Linux. The usual symptom is that pdnsd fails to save + the cache to disk, and /var/cache/pdnsd/pdnsd.cache remains empty. + If you experience this kind of trouble, try reconfiguring with different values + for the --with-thread-lib option. The allowable values are + linuxthreads (or lt for short), linuxthreads2 + (or lt2 for short), and nptl. + By default the configure script tries to detect automatically whether + linuxthreads or nptl is more appropriate for your system, + but the method used is not foolproof. Look for the line: + checking if this is an NPTL-based system...
+ If the automatic test mistakenly indentifies the thread library on your system as + NPTL based, you should reconfigure with --with-thread-lib=lt and recompile. + If the result of the automatic test is "no" or if + --with-thread-lib=lt does not have the desired effect, try again using + --with-thread-lib=lt2 . +
+ Normally, you will need only --prefix, --sysconfdir and + --with-distribution. + If you specify your distribution using --with-distribution, this has the + following effects: +
    +
  • An rc script is copied in the appropriate localtion, which enables pdnsd to start + at machine boot time (see 0.4) +
  • Distribution-specific portions might be included in the generated pdnsd.spec + file (only important if you want to build rpm archives yourself). +
+ If you choose Generic, no rc script is installed, and a generic spec + file is generated.
+ + Further instructions are in the INSTALL document in the pdnsd source directory. + ./configure --help will give you a list of all supported command line + options.

+ Note added by Paul Rombouts: Some people may want change the compiler optimization flag. + I use the -O2 flag, but it might be safer to use a lower level of + optimization or no optimization at all. In that case prefix the + configure command with the desired compiler flags like this + (assuming you're using a bash shell): +

CFLAGS="-O1 -Wall" ./configure ...

+ + +
+

0.3.3 Building & installing

+ Type make in the source directory. Should work by now.
+ To install, type make install or do the installation by hand (see 0.3.4).
+ make install will do the following ($prefix is the prefix directory; see above):
+
    +
  1. copies pdnsd to $(prefix)/sbin/ +
  2. copies pdnsd-ctl to $(prefix)/sbin/ +
  3. copies docs/pdnsd.conf.sample (a sample configuration) to the pdnsd config directory. +
  4. creates your cache directory if it is not there. + After installation, you should check the file permissions and create or edit + /etc/pdnsd.conf to fit your needs (see below). + If you use the run_as option, please make sure that your + cache directory is owned by the user you specified with this option! +
+ You must be root for this installation!
+ Security notes: never make the pdnsd cache directory + writeable for untrusted users, or you will get several security holes: + the users might modify the cache contents, or plant dangerous links.
+ If you use a pidfile, you should be aware that you introduce security + problems if you place the pidfile in a directory in a NFS filesystem that + is writeable for untrusted users. Generally, the pidfile directory + (typically /var/run) should not be writeable for untrusted users. +
+

0.3.4 Manual installation

+ For a manual installation, you need to do the following steps: +
    +
  1. Copy pdnsd and pdnsd-ctl from your build directory to an appropriate location (e.g. /usr/sbin). +
  2. Copy docs/pdnsd.conf into the directory you want it to reside (/etc by default, + and change it according to your needs (see below). +
  3. Create your caching directory; default is /var/cache/pdnsd (you may change this + in your pdnsd.conf); Permissions should be at max rwxr-xr-x (if you want to + protect your cache and status socket, make it rwx------). +
+ Thats it! +
+ +

0.4 Start at boot time

+ In the src/rc folder of the pdnsd distribution are start scripts + for pdnsd designed for different Linux distros. There are scripts + for SuSE, Redhat, Debian, Arch Linux and Slackware now.
+ The start scripts are automatically installed during RPM install, and also during make install + if you specified your distro.
+ For Slackware Linux there is a start-up script contributed by Nikola Kotur, but presently + it must be installed manually. + See src/rc/README and src/rc/Slackware/rc.pdnsd for details. + +

0.4.1 SuSE Linux startup

+ rc/SuSE/pdnsd is a start script for SuSE Linux. It was tested for 6.? but should run on some + versions below. You can do make install as root in the rc/SuSE + directory to install it, or you can install manually:
+ + + + + + + +
+ manual installation +
+ For manual installation, copy rc/SuSE/pdnsd into /sbin/init.d/, go to + /sbin/init.d/rc2.d/ and create there the following two symlinks:
+ S11pdnsd to ../pdnsd (do ln -s ../pdnsd S11pdnsd in that dir)
+ K34pdnsd to ../pdnsd (do ln -s ../pdnsd K34pdnsd in that dir)
+ The numbers dictate the order different services are started and + might need to be modified. Then edit your /etc/rc.config file and + add the line START_PDNSD=yes to start pdnsd at boot time. +
+

+ If you used the make install command, START_PDNSD=yes has been + appended to your /etc/rc.config file, causing pdnsd to be started + at boot time. If you don't want that, change the yes into no. +

+ This start script was created from /sbin/init.d/skeleton by me, so the + most is copyrighted by SuSE. They put it under the GPL, however, so + the license stated in COPYING also applies to this script. + There is NO WARRANTY OF ANY KIND on these scripts. + This is no official SuSE script, and SuSE naturally does NO support + for it. +

0.4.2 Red Hat Linux startup

+ rc/Redhat/pdnsd is a start script for Red Hat Linux. It was contibuted by Torben + Janssen.
+ This was tested for 6.1 but should run on 5.0+. You can do make install as root in the + rc/Redhat directory to install it, or you can install manually:
+ + + + + + + +
+ manual installation +
+ For manual installation, copy rc/Redhat/pdnsd into /etc/rc.d/init.d/
+ Then go to /etc/rc.d/rc3.d and create there the following symlink:
+ S78pdnsd -> ../init.d/pdnsd + (do ln -f -s ../init.d/pdnsd S78pdnsd in that dir)
+ + Then go to /etc/rc.d/rc0.d and create there the following symlink:
+ K78pdnsd -> ../init.d/pdnsd + (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir)
+ + Then go to /etc/rc.d/rc6.d and create there the following symlink:
+ K78pdnsd -> ../init.d/pdnsd + (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir) +
+ This script is also covered by license stated in COPYING. + Again, there is NO WARRANTY OF ANY KIND on these scripts. + This is no offical Redhat script, and Redhat naturally does NO support + for it +
+

0.5 Notes for FreeBSD users

+ The special handling of ISDN ppp devices is only supported on Linux. It is not needed in FreeBSD, the normal + device handling also works fine with isdn4bsd devices.
+ When compiled for FreeBSD, pdnsd as a small RFC compatability issue: RFC2181 demands answers on dns querys + to be sent with the same source address the query packet went to. In seldom cases, this will not be the case, + because the kernel selects the source address depending on the interface that was used for sending the answer.
+ Setting the source address currently does not work for IPv4. I have written a kernel patch that will provide an easy way + to program this. We'll see if or when it gets commited.
+
+
+

1 Invocation

+ When invoking pdnsd, you can specify various options at the command line. Command line options + always override config file options. The various --noX options are present to override + config file options. +

+ pdnsd --help (or -h) gives you an overview of the pdnsd command line options. +

+

+ pdnsd --version (or -V for short) prints licence and version information. +

+

+ To start pdnsd as background daemon, specifiy --daemon (or -d for short) on + the command line. Diagnostic and error messages after the actual daemon start + will be printed to the syslog instead of the console. --nodaemon will disable this. +

+

+ When starting pdnsd as a daemon, the -p option may be helpful: It writes the pid + of the server process to the file of the name given as argument to this option.
+ Example: pdnsd -d -p /var/run/pdnsd.pid +

+

+ If you want to specify a configuration file other than /etc/pdnsd.conf, specify + -c or --config-file on the command line, followed by a filename. +

+

+ If pdnsd was compiled with debugging support, you may specify -g or + --debug on the command line. This will cause extra diagnostic messages to be + printed. When pdnsd runs in daemon mode, the messages will be written to the pdnsd.debug + file in your cache directory. --nodebug disables debugging. +

+

+ pdnsd -vn sets the verbosity level of pdnsd. n is normally a digit from 0 to 3, + where 0 means normal operation, while 3 will most verbose. + Level 9 can be used in combination with the --debug option for very + extensive debug information.
+ Note: The current implementation mostly ignores the verbosity level, + so you may not notice much difference between the various levels. +

+

+ The option -s or --status enables the status control socket. This is a named socket in + the cache directory called pdnsd.status. This socket allows run-time configuration of pdnsd + using the utility pdnsd-ctl. See below for more details about pdnsd-ctl. + --nostatus disables status control. + See also the configuration option status_ctl in the global section. +

+

+ The option --notcp disables the seldom needed TCP server thread, which may + save you some resources. -t or --tcp will enable it. + See also the tcp_server configuration option. +

+

+ Using the -m option, you can select the method pdnsd uses to query other name servers. + Following methods are supported (see also the query_method + configuration option):
+ -muo: pdnsd will use UDP only. This is the fastest method, and should be supported by all name servers + on the Internet.
+ -mto: pdnsd will use TCP only. TCP queries usually take longer time than UDP queries, but are more secure + against certain attacks, where an attacker tries to guess your query id and to send forged answers. TCP queries + are not supported by some name servers.
+ -mtu: pdnsd will try to use TCP, and will fall back to UDP if its connection is refused or times out.
+ -mut: New in version 1.2.5: pdnsd will try to use UDP, and will repeat the query using TCP if the UDP reply was truncated + (i.e. the tc bit is set). This is the behaviour recommended by the DNS standards.
+

+

+ The -4 option switches to IPv4 mode, providing pdnsd was compiled with IPv4 support.
+ The -6 option switches to IPv6 mode, providing pdnsd was compiled with IPv6 support.
+ The -a option is only available when pdnsd was compiled with both IPv4 and IPv6 support. + With this option, pdnsd will try to detect automatically if a system supports IPv6, and fall back to IPv4 otherwise.
+

+

+ With -i prefix or --ipv4_6_prefix=prefix you can set the prefix pdnsd uses (when running in IPv6 + mode) to map IPv4 addresses in the configuration file to IPv6 addresses. + There is also a corresponding option for the config file, see below. + Must be a valid IPv6 address. + The default is ::ffff:0.0.0.0 +

+ +

2 The configuration file

+ This section describes the layout of the configuration file and the available + configuration options. + The default location of the file is /etc/pdnsd.conf. This may be changed + with the -c command line option. + An example pdnsd.conf comes with the pdnsd distribution in the docs directory + and will be installed to /etc/ by make install. + +
+

2.1 Layout

+ The configuration file is divided into sections. Each section is prefixed with + the section name and opening curlies ({) and closed with closing curlies (}). + In each section, configuration options can be given in the form +
+ option_name=option_value; +
+ Option value may be a string literal, a number, a time specification or a constant. + In previous versions of pdnsd strings had to be enclosed + in quotes ("), but since version 1.1.10 this is no longer necessary, unless + a string contains a special character such as whitespace, a token that normally starts + a comment, or one of ",;{}\". + Since version 1.2.9 a backslash (\) inside a string is interpreted as an escape character, + so it is possible to include special characters in strings (both quoted or unquoted) + by preceding them with a backslash. Some escape sequences are in interpreted as in the C + programming language, e.g. \t becomes a tab, + \n becomes a new-line control char.
+ A time specification consists a sequence of digits followed by a one-letter suffix. + The following suffixes are recognized: + s (seconds), m (minutes), h (hours), + d (days) and w (weeks). + If the suffix is missing, seconds are assumed. + If several time specifications are concatenated, their values are added together; + e.g. 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds.
+ Some options take more than one value; in this case, the values are separated with commas.
+ If you may supply one of a set of possible values to an option, this is noted + in the documentation as + (option1|option2|option3|...)
+ The constants true|false and yes|no + are accepted as synonyms for the constants on|off.
+ Comments may be enclosed in /* and */, nested comments are possible. If the + # sign or two slashes (//) appear in the configuration file, everything from + these signs to the end of the current line is regarded as a comment and ignored.
+ There are examples for nearly all options in the sample config file. +
+

2.1.1 global Section

+ The global section specifies parameters that affect the overall behaviour of the + server. If you specify multiple global sections, the settings of those later in + the file will overwrite the earlier given values.
+ These are the possible options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ perm_cache=(number|off);
+ Switch the disk cache off or supply a maximum cache size in kB. If the disk + cache is switched off, 8 bytes will still be written to disk. + The memory cache is always 10kB larger than the file cache. + This value is 2048 (2 MB) by default. +
+ cache_dir=string;
+ Set the directory you want to keep the cache in. + The default is "/var/cache/pdnsd" + (unless pdnsd was compiled with a different default). +
+ server_port=number;
+ Set the server port. This is especially useful when you want to start the + server and are not root. Note that you may also not specify uptest=ping in + the server section as non-root.
+ The default port is 53, the RFC-standard one. Note that you should only use + non-standard ports when you only need clients on your machine to communicate + with the server; others will probably fail if the try to contact the server + on the basis of an NS record, since the A record that supplies the address for + (among others) name servers does not have a port number specification. +
+ server_ip=string;
+ or
+ interface=string;
+ Set the IP address pdnsd listens on for requests. This can be useful + when the host has several interfaces and you want pdnsd not to listen on + all interfaces. For example, it is possible to bind pdnsd to listen on + 127.0.0.2 to allow pdnsd to be a forwarder for BIND. + The default setting for this option is server_ip=any, which means that + pdnsd will listen on all of your local interfaces. + Presently you can only specify one address here; if you want pdnsd to listen on multiple + interfaces but not all you will have to specify server_ip=any + and use firewall rules to restrict access.
+ The IP address used to need quotation marks around it, but since version 1.1.10 + this is no longer necessary.
+ If pdnsd has been compiled with both IPv4 and IPv6 support, and you want to + specify an IPv6 address here, then unless pdnsd was compiled to start up in IPv6 mode + by default, you will need to use the -6 command-line option or + set run_ipv4=off first (see below) in order to ensure that the + IPv6 address is parsed correctly.
+ If pdnsd is running in IPv6 mode and you specify an IPv4 address here, + it will automatically be mapped to an IPv6 address.
+ New in version 1.2: You may also give the name of an interface + such as "lo" or "eth0" here, instead of an IP address + (this has been tested on Linux, and may or may not work on other platforms). + pdnsd will not bind to the interface name, but will look up the address of the + interface at start-up and listen on that address. If the address of the interface + changes while pdnsd is running, pdnsd will not notice that. You will need to + restart pdnsd in that case. +
+ outgoing_ip=string;
+ or
+ outside_interface=string;
+ New in version 1.2.9: + Set the IP address of the interface used by pdnsd for outgoing queries. + This can be useful when the host has several interfaces and you want pdnsd + to send outgoing queries via only one of them. + For example, if pdnsd is running on a host with one interface with IP address + 192.168.1.1 connected to the local network, and another with IP address 123.xxx.yyy.zzz + connected to the internet, you may specify server_ip=192.168.1.1 + and outgoing_ip=123.xxx.yyy.zzz to enforce that pdnsd only responds + to queries received from the local network, and only sends outgoing queries via + the interface connected to the internet.
+ The default setting for this option is any, which means that + the kernel is free to decide which interface to use. + Like with the server_ip option, you may also give the name of an + interface here, instead of an IP address. +
+ linkdown_kluge=(on|off);
+ This option enables a kluge that some people might need: when all servers are + marked down, with this option set the cache is not even used when a query is + received, and a DNS error is returned in any case. The only exception from this + is that local records (as specified in rr and source + sections are still served normally. + In general, you probably want to get cached entries even when the network is down, + so this defaults to off. +
+ max_ttl=timespec;
+ This option sets the maximum time a record is held in cache. All dns + resource records have a time to live field that says for what period of time the + record may be cached before it needs to be requeried. If this is more than the + value given with max_ttl, this time to live value is set to max_ttl. + This is done to prevent records from being cached an inappropriate long period of time, because + that is almost never a good thing to do. Default is 604800s (one week). +
+ min_ttl=timespec;
+ This option sets the minimum time a record is held in cache. All dns + resource records have a time to live field that says for what period of time the + record may be cached before it needs to be requeried. If this is less than the + value given with min_ttl, this time to live value is set to min_ttl. + Default is 120 seconds. +
+ neg_ttl=timespec;
+ This option sets the time that negatively cached records will remain valid in the + cache if no time to live can be determined. This is always the case when whole + domains are being cached negatively, and additionally when record types are cached + negatively for a domain for which no SOA record is known to pdnsd. If a SOA is present, + the ttl of the SOA is taken. +
+ neg_rrs_pol=(on|off|auth|default);
+ This sets the RR set policy for negative caching; this tells pdnsd under which circumstances + it should cache a record type negatively for a certain domain. off will + turn the negative caching of record types off, on will always add a negative + cache entry when a name server did not return a record type we asked it for, and auth + will only add such entries if the answer came from an authoritative name server for that + domain.
+ New in version 1.2.8: The default setting will add a negatively cached record + if either the answer was authoritive or the answer indicated the name server had "recursion available" + while the query explicitly requested such recursion.
+ The preset is "default" (used to be auth). +
+ neg_domain_pol=(on|off|auth);
+ This is analogue to neg_rrs_pol for whole domain negative caching. It should be safe + to set this on, because I have not seen a caching server that will falsely claim that a + domain does not exist.
+ The default is auth. +
+ run_as=string;
+ This option allows you to let pdnsd change its user and group id after operations that needed + privileges have been done. This helps minimize security risks and is therefore recommended. The + supplied string gives a user name whose user id and primary group id are taken.
+ A little more details: after reading the config file, becoming a daemon (if specified) and starting + the server status thread, the main thread changes its gid and uid, as do all newly created threads + thereafter. By taking another uid and gid, those threads run with the privileges of the + specified user. + Under Linux and FreeBSD, the server status thread runs with the original privileges only when the strict_setuid option + is set to off (see below, on by default), because these may be needed + for exec uptests. The manager thread also retains its original privileges in this case. + You should take care that the user you specify has write permissions on your cache file and + status pipe (if you need a status pipe). You should look out for error messages like "permission denied" + and "operation not permitted" to discover permission problems.
+
+ strict_setuid=(on|off);
+ When used together with the run_as option, this option lets you specify that all threads of the + program will run with the privileges of the run_as user. This provides higher security than + the normal run_as + option, but is not always possible. See the run_as option for further discussion.
+ This option is on by default.
+ Note that this option has no effect on Non-Linux systems. +
+ paranoid=(on|off);
+ Normally, pdnsd queries all servers in recursive mode (i.e. instructs servers to query other servers themselves + if possible, + and to give back answers for domains that may not be in its authority), and accepts additional records with information + for servers that are not in the authority of the queried server. This opens the possibility of so-called cache poisoning: + a malicious attacker might set up a dns server that, when queried, returns forged additional records. This way, he might + replace trusted servers with his own ones by making your dns server return bad IP addresses. This option protects + you from cache poisoning by rejecting additional records + that do not describe domains in the queried servers authority space and not doing recursive queries any more. + An exception + to this rule are the servers you specify in your config file, which are trusted.
+ The penalty is a possible performance decrease, in particular, more queries might be necessary for the same + operation.
+ You should also notice that there may be other similar security problems, which are essentially problems of + the DNS, i.e. + any "traditional" server has them (the DNS security extensions solve these problems, but are not widely + supported). + One of this vulnerabilities is that an attacker may bombard you with forged answers in hopes that one may match a + query + you have done. If you have done such a query, one in 65536 forged packets will be succesful (i.e. an average packet + count of 32768 is needed for that attack). pdnsd can use TCP for queries, + which has a slightly higher overhead, but is much less vulnerable to such attacks on sane operating systems. Also, pdnsd + chooses random query ids, so that an attacker cannot take a shortcut. If the attacker is able to listen to your network + traffic, this attack is relatively easy, though.
+ This vulnerability is not pdnsd's fault, and is possible using any conventional + name server (pdnsd is perhaps a little more secured against this type of attacks if you make it use TCP).
+ The paranoid option is off by default.
+
+ ignore_cd=(on|off);
+ New in version 1.2.8: This option lets you specify that the CD bit of a DNS query will be ignored. + Otherwise pdnsd will reply FORMERR to clients that set this bit in a query. + It is safe to enable this option, as the CD bit refers to 'Checking Disabled' + which means that the client will accept non-authenticated data.
+ This option is on by default. Turn it off if you want the old behaviour (before version 1.2.8). +
+ scheme_file=string;
+ In addition to normal uptests, you may specify that some servers shall only be queried when a certain + pcmcia-cs scheme is active (only under linux). For that, pdnsd needs to know where the file resides that + holds the pcmcia scheme information. Normally, this is either /var/lib/pcmcia/scheme or + /var/state/pcmcia/scheme. +
+ status_ctl=(on|off);
+ This has the same effect as the -s command line option: the status control is enabled when + on is specified.
+ Added by Paul Rombouts: Note that pdnsd-ctl allows run-time configuration of pdnsd, + even the IP addesses of the name servers can be changed. If you're not using pdnsd-ctl and + you want maximum security, you should not enable this option. It is disabled by default. +
+ daemon=(on|off);
+ This has the same effect as the -d command line option: the daemon mode is enabled when + on is specified.
+ Default is off. +
+ tcp_server=(on|off);
+ tcp_server=on has the same effect as the -t or --tcp + command-line option: it enables TCP serving. + Similarly, tcp_server=off is like the --notcp command-line option.
+ Default is on. +
+ pid_file=string;
+ This has the same effect as the -p command line option: you can specify a file that pdnsd + will write its pid into when it starts in daemon mode. +
+ verbosity=number;
+ This has the same effect as the -v command line option: you can set the verbosity of pdnsd's + messages with it. The argument is a number between 0 (few messages) to 3 (most messages). +
+ query_method=(tcp_only|udp_only|tcp_udp|udp_tcp);
+ This has the same effect as the -m command line option. + Read the documentation for the command line option on this. + tcp_only corresponds to the to, udp_only to the uo, + tcp_udp to the tu and udp_tcp to the ut + argument of the command line option.
+ If you use query_method=tcp_udp, it is recommended that you also set the global timeout option to at least twice the longest server timeout. +
+ run_ipv4=(on|off);
+ This has the same effect as the -4 or -6 command line option: + if on is specified, IPv4 support is enabled, and IPv6 support is disabled (if available). + If off is specified, IPv4 will be disabled and IPv6 will be enabled. + For this option to be meaningful, pdnsd needs to be compiled with support for the protocol you choose. + If pdnsd was compiled with both IPv4 and IPv6 support, and you want to include IPv6 addresses + in the configuration file, you will probably need to specify run_ipv4=off first to + ensure that the IPv6 addresses are parsed correctly. +
+ debug=(on|off);
+ This has the same effect as the -g command line option: the debugging messages are enabled when + on is specified. +
+ ctl_perms=number;
+ This option allows you to set the file permissions that the pdnsd status control socket will have. These + are the same as file permissions. The owner of the file will be the run_as user, or, if none is specified, + the user who started pdnsd. If you want to specify the permissions in octal (as usual), don't forget + the leading zero (0600 instead of 600!). To use the status control, write access is needed. The default + is 0600 (only the owner may read or write).
+ Please note that the socket is kept in the cache directory, and that the cache directory permissions + might also need to be adjusted. Please ensure that the cache directory is not writeable for untrusted + users. +
+ proc_limit=number;
+ With this option, you can set a limit on the pdnsd threads that will be active simultaneously. If + this number is exceeded, queries are queued and may be delayed some time. + See also the procq_limit option.
+ The default for this option is 40. +
+ procq_limit=number;
+ When the query thread limit proc_limit is exceeded, connection attempts to pdnsd will be queued. + With this option, you can set the maximum queue length. + If this length is also exceeded, the incoming queries will be dropped. + That means that tcp connections will be closed and udp queries will just be dropped, which + will probably cause the querying resolver to wait for an answer until it times out.
+ See also the proc_limit option. A maximum of proc_limit+procq_limit + query threads will exist at any one time (plus 3 to 6 threads that will always + be present depending on your configuration).
+ The default for this option is 60. +
+ tcp_qtimeout=timespec;
+ This option sets a timeout for tcp queries. If no full query has been received on a tcp connection + after that time has passed, the connection will be closed. The default is set using the + --with-tcp-qtimeout option to configure. +
+ par_queries=number;
+ This option used to set the maximum number of remote servers that would be queried simultaneously, + for every query that pdnsd receives.
+ Since version 1.1.11, the meaning of this option has changed slightly. + It is now the increment with which the number of parallel queries is + increased when the previous set of servers has timed out. + For example, if we have a list server1, server2, server3, etc. of available servers + and par_queries=2, then pdnsd will first send queries to server1 and server2, + and listen for responses from these servers.
+ If these servers do not send a reply within their timeout period, pdnsd will send additional + queries to server3 and server4, and listen for responses from + server1, server2, server3 and server4, and so on until a useful reply is + received or the list is exhausted.
+ In the worst case there will be pending queries to all the servers in the list of available servers. + We may be using more system resources this way (but only if the first servers in the list + are slow or unresponsive), but the advantage is that we have a greater chance of catching a reply. + After all, if we wait longer anyway, why not for more servers.
+ See also the explanation of the global timeout option below.
+ 1 or 2 are good values for this option. + The default is set at compile time using the --with-par-queries option to configure. +
+ timeout=timespec;
+ This is the global timeout parameter for dns queries. + This specifies the minimum period of time pdnsd will wait after sending the + first query to a remote server before giving up without having + received a reply. The timeout options in the configuration file are + now only minimum timeout intervals. Setting the global timeout option + makes it possible to specify quite short timeout intervals in the + server sections (see below). This will have the effect that pdnsd will start + querying additional servers fairly quickly if the first servers are + slow to respond (but will still continue to listen for responses from + the first ones). This may allow pdnsd to get an answer more quickly in + certain situations.
+ If you use query_method=tcp_udp it is recommended that + you make the global timeout at least twice as large as the largest + server timeout, otherwise pdnsd may not have time to try a UDP query + if a TCP connection times out.
+ Default value is 0. +
+ randomize_recs=(on|off);
+ If this option is turned on, pdnsd will randomly reorder the cached records of one type + when creating an answer. This supports round-robin DNS schemes and increases fail + safety for hosts with multiple IP addresses, so this is usually a good idea.
+ On by default. +
+ query_port_start=(number|none);
+ If a number is given, this defines the start of the port range used for queries of pdnsd. The + value given must be >= 1024. The purpose of this option is to aid certain firewall + configurations that are based on the source port. Please keep in mind that another application + may bind a port in that range, so a stateful firewall using target port and/or process uid may + be more effective. In case a query start port is given pdnsd uses this port as the first port of a + specified port range (see query_port_end) used for queries. + pdnsd will try to randomly select a free port from this range as local port for the query.
+ To ensure that there are enough ports for pdnsd to use, the range between query_port_start and + query_port_end should be adjusted to at least (par_queries * proc_limit). + A larger range is highly recommended for security reasons, and also because other applications may + allocate ports in that range. If possible, this range should be kept out of the space + that other applications usually use.
+ The default for this option is 1024. Together with the default value of query_port_end, + this makes it the hardest for an attacker to guess the source port used by the pdnsd resolver. + If you specify none here, pdnsd will let the kernel choose the source port, but + this may leave pdnsd more vulnerable to an attack. +
+ query_port_end=number;
+ Used if query_port_start is not none. Defines the last port of the range started by query_port_start + used for querys by pdnsd. The default is 65535, which is also the maximum legal value for this option. + For details see the description of query_port_start. +
+ delegation_only=string;
+ Added by Paul Rombouts: This option specifies a "delegation-only" zone. + This means that if pdnsd receives a query for a name that is in a + subdomain of a "delegation-only" zone but the remote name server + returns an answer with an authority section lacking any NS RRs for + subdomains of that zone, pdnsd will answer NXDOMAIN (unknown domain). + This feature can be used for undoing the undesired effects of DNS + "wildcards". Several "delegation-only" zones may be specified together. + If you specify root servers in a server section it is + important that you set root_server=on in such a section.
+ Example: +

delegation_only="com","net";

+ This feature is off by default. It is recommended that you only use + this feature if you actually need it, because there is a risk that + some legitimate names will be blocked, especially if the remote + name servers queried by pdnsd return answers with empty authority + sections. +
+ ipv4_6_prefix=string;
+ This option has the same effect as the -i command-line option. + When pdnsd runs in IPv6 mode, this option specifies the prefix pdnsd uses to convert IPv4 addresses in + the configuration file (or addresses specified with pdnsd-ctl) + to IPv6-mapped addresses. + The string must be a valid IPv6 address. Only the first 96 bits are used. + Note that this only effects the parsing of IPv4 addresses listed after this option.
+ The default is "::ffff.0.0.0.0". +
+ use_nss=(on|off);
+ If this option is turned on, pdnsd will call initgroups() to set up the group access list, + whenever pdnsd changes its user and group id (see run_as option). + There is a possible snag, though, if initgroups() uses NSS (Name Service Switch) and + NSS in turn uses DNS. In such a case you may experience lengthy timeouts and stalls. + By setting use_nss=off, you can disable the initgroups() call + (only possible in versions 1.2.5 and later).
+ This option was contributed by Jan-Marek Glogowski.
+ On by default. +
+ udpbufsize=number;
+ New in version 1.2.9: + This option sets the upper limit on the size of UDP DNS messages. The default is 1024.
+ See also the edns_query server option below. +
+ +
+

2.1.2 server Section

+ Each server section specifies a set of name servers that pdnsd should try to get + resource records or authoritative name server information from. The servers are + queried in the order of their appearance (or parallel to a limited extend). + If one fails, the next one is taken and so on.
+ You probably want to specify the dns server in your LAN, the caching dns servers + of your internet provider or even a list of root servers in one or more server sections.
+ The supported options in this section are:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ label=string;
+ Specify a label for the server section. This can be used to refer to this section + when using pdnsd-ctl, the pdnsd control utility.
+ You can give several server sections the same label, but if you want to change the addresses + of a server section (see ip option below) during run-time with + "pdnsd-ctl server label up dns1,dns2,...", + the label must be unique. +
+ ip=string;
+ Give the IP (the address, not the host name) of the server.
+ Multiple IP addresses can be given per server section. + This can be done by entering multiple lines of the form ip=string; + or a single line like this: +

ip=string,string,string;

+ IP addresses do not have to be specified in the configuration file. + A server section without IP addresses will remain inactive until it is assigned + one or more addresses with pdnsd-ctl, + the pdnsd control utility.
+ If pdnsd has been compiled with both IPv4 and IPv6 support, any IPv6 addresses you specify + here will be skipped with a warning message, unless pdnsd is running in IPv6 mode. + Thus, unless pdnsd was compiled to startup in IPv6 mode by default, you need to use the + command-line option -6 or set run_ipv4=off + first (see global section) in order to ensure + that IPv6 addresses are parsed correctly.
+ If pdnsd is running in IPv6 mode and you specify an IPv4 address here, + it will automatically be mapped to an IPv6 address. +
+ file=string;
+ New in version 1.2: This option allows you to give the name of a resolv.conf-style file. + Of the lines beginning with the nameserver keyword, the second field will be parsed as an + IP address, as if it were specified with the ip= option. The remaining lines will be ignored. + If the contents of the file changes while pdnsd is running, you can make pdnsd aware of the changes through the + use of pdnsd-ctl, the pdnsd control utility. + This is usually most conveniently done by placing the command "pdnsd-ctl config" in a script + that is automatically run whenever the DNS configuration changes.
+ For example, suppose you have a ppp client that writes the DNS configuration for your ISP to the file + /etc/ppp/resolv.conf and runs the script /etc/ppp/ip-up when a new + connection is established. One way of ensuring that pdnsd is automatically reconfigured is to + add a server section in the config file with file=/etc/ppp/resolv.conf and to + add the command "pdnsd-ctl config" to /etc/ppp/ip-up. +
+ port=number;
+ Give the port the remote name server listens on. Default is 53 (the official + dns port) +
+ uptest=(ping|none|if|dev|diald|exec|query);
+ Determine the method to check whether the server is available. Currently + defined methods are: + +
    +
  • ping: Send an ICMP_ECHO request to the server. If it doesn't respond + within the timeout, it is regarded to be unavailable until the next probe. +
  • none: The availability status is not changed, only the time stamp is updated. +
  • if: Check whether the interface (specified in the interface= option) is + existent, up and running. This currently works for all "ordinary" + network interfaces, interfaces that disappear when down (e.g. ppp?), + and additionally for Linux isdn interfaces (as of kernel 2.2). Note that + you need a /dev/isdninfo device file (major#45, minor#255), or the + isdn uptest will always fail. +
  • dev and diald: Perform an if uptest, and, if that + was succesful, additionally check whether a program is running that + has locked a given (modem-) device. The needed parameters are an interface (specified as for the if + uptest, e.g. "ppp0") and a device relative to /dev (e.g. + "modem" for /dev/modem specified using the device= option. + pdnsd will then look for a pid file for the given interface in /var/lock (e.g. + /var/run/ppp0.pid) and for a lockfile for the given device (e.g. /var/lock/LCK..modem), + and then test whether the locking process is the process that created the pid file and this process is still + alive. If this is the case, the normal if uptest is executed for the given interface.
    + The dev option is for pppd dial-on-demand, diald is the same for diald users. +
  • exec: Executes a given command in the /bin/sh shell + (as /bin/sh -c <command>) + and evaluates the result (the return code of the last command) in the shell's way of handling return codes, + i.e. 0 indicates success, all other indicate failure. The shell's process name will be + uptest_sh. The command is given with the uptest_cmd option (see below). + For secuity issues, also see that entry. +
  • query: New in version 1.2: + This works like the ping test, except it sends an (empty) DNS query to the remote server. + If the server sends a well-formed response back within the timeout period (except SERVFAIL), + it will be regarded as available. + This test is useful if a remote server does not respond to ICMP_ECHO requests at all, + which unfortunately is quite common these days. + It can also happen that a remote server is online but ignores empty DNS queries. + Then you will need the set the query_test_name option (see below). + In many cases this test will be a more reliable indicator of availability + than the ones mentioned before. +
+

+ The default value is none. +

+ NOTE: If you use on-demand dialing, use none, if, + dev, diald or exec, + since ping or query will send packets + in the specified interval and the interface will thus frequently dial! +

+ ping_timeout=number;
+ Sets the timeout for the ping test in tenths of seconds + (this unit is used for legacy reasons; actually the current implementation is + only accurate to a second).
+ The default is 600 (one minute). +
+ ping_ip=string;
+ The IP address for the ping test. The default is the IP of the name server. +
+ query_test_name=string;
+ New in version 1.2.9: + Sets the name to be queried when using uptest=query availability test. + If the string is the unquoted constant none, + an empty query is used (this the default), otherwise a query of type A will be + sent for the domain name specified here. It is not necessary for the domain name + to exist or have a record of type A in order for the uptest to succeed.
+ If the the remote server ignores empty queries, you will probably want to set + query_test_name="." (the root domain). +
+ uptest_cmd=string,string;
+ or
+ uptest_cmd=string;
+ Sets the command for the uptest=exec function to the first string. + If the second string is given, it specifies a user with whose user + id and primary group id the command is executed.
+ This is especially useful if you are executing the server as root, + but do not want the uptest to be performed with root privileges. + In fact, you should never execute the uptest as root if you can help + it.
+ If the server is running setuid or setgid, the privileges thus gained + are attempted to be dropped even before changing identity to the + specified user to prevent setuid/gid security holes (otherwise, any + user might execute commands as root if you setuid the executable).
+ Note that this is not always possible, and that pdnsd should never + be installed as setuid or setgid. + The command is executed using /bin/sh, so you should be able to use + shell builtin commands. +
+ interval=(timespec|onquery|ontimeout);
+ Sets the interval for the server up-test. The default is 900 seconds; + however, a test is forced when a query times out and the timestamp is reset then.
+ If you specify onquery instead of a timeout, the interface will be + tested before every query. This is to prevent automatically dialing + interfaces (diald/pppd or ippp) to dial on dns queries. It is intended to be + used in connection with an interface-testing uptest ;-)
+ Note that using uptest=exec, you might run into performance problems + on slow machines when you use that option. + DON'T use onquery with uptest=ping or + uptest=query, as it may cause delays if the server does not answer + (btw, it doesn't make sense anyway). + Note also that using onquery is no guarantee that the interface + will not be used. When another (reachable) dns server tells pdnsd + to query a third dns server for data, pdnsd will do that and has + no means of checking whether this will dial up the interface or not. + This however should be a rare situation.
+ New in version 1.2.3: + A third possibility is to specify interval=ontimeout. + In this case the server is not tested at startup/reconfiguration, nor at regular intervals, + but only after a DNS query to a server times out. Certain types of network problems + such as a refused connection will also cause the server to be considered unavailable. + However, once a server is declared dead it is never considered again unless it is revived using a + pdnsd-ctl config or server command. + The idea behind this option is to minimize uptests by assuming all + servers are available until there is reason to believe otherwise. +
+ interface=string;
+ The network interface (or network device, e.g. "eth0") for the uptest=if option. + Must be specified if uptest=if is given. +
+ device=string;
+ The (modem-) device that is used for the dev uptest. If you use this for a dial-on-demand + ppp uptest (together with uptest=dev), you need to enter the device you are using for your + pppd here, e.g. modem for /dev/modem.
+ Must be specified if uptest=dev is given. +
+ timeout=timespec;
+ Set the timeout for the dns query. The default is 120 seconds. You probably want to set this lower.
+ Timeouts specified in the configuration file are only treated as the + minimum period of time to wait for a reply. A queries to a remote + server are not canceled until a useful reply has been received, or all + the other queries have timed out or failed.
+ If you have also set the global timeout option, you may consider setting a fairly small value here. + See the explanation of the timeout option in the global + section for what that means. +
+ purge_cache=(on|off);
+ In every fetched dns record, there is a cache timeout given, which + specifies how long the fetched data may be cached until it needs to be + reloaded. If purge_cache is set to off, the stale records are not purged + (unless the cache size would be exceeded, in this case the oldest records are purged). + Instead, they are still served if they cannot succesfully be + updated (e.g. because all servers are down).
+ Default is off. +
+ caching=(on|off);
+ Specifies if caching shall be performed for this server at all. Default is + on. +
+ lean_query=(on|off);
+ Specifies whether to use the "lean" query mode. In this mode, only the + information actually queried from pdnsd is resolved and cached. This has + the advantage that usually less cache space is used and the query is + usually faster. In 90% of the cases, only address (A) records are needed + anyway. If switched off, pdnsd will always cache all data about a host + it can find and will specifically ask for all available records + (well, at least it is a good approximation for what it really does ;-) + This will of course increase the answer packet sizes.
+ Some buggy name servers may not deliver CNAME records when not asked for + all records. I do not know if such servers are around, but if you have + trouble resolving certain host names, try turning this option off.
+ A last note: If you use multiple pdnsd's that access each other, turning + this option on is probably a big win.
+ This on by default. +
+ edns_query=(on|off);
+ New in version 1.2.9: + Specifies whether to use EDNS (Extension mechanisms for DNS) for outgoing queries. + Currently this is only useful for allowing UDP message sizes larger than 512 bytes. + Note that setting this option on can give problems in combination with some legacy + systems or software, including, embarrassingly enough, previous versions of pdnsd.
+ The default is off, but if your network can handle UDP payloads + significantly larger than 512 bytes, the recommended value is on.
+ Note that this option only effects outgoing queries. If pdnsd receives a query using + EDNS, it will reply using EDNS regardless of the value of this option. +
+ See also the udpbufsize option above. +
+ scheme=string;
+ You can specify a pcmcia-cs scheme that is used in addition to the uptests. If you specify + a scheme here, the server this section is for will only be queries if the given scheme + is active. Shell wildcards (* and ?) are allowed in the string under their special + meanings. You need to use the scheme_file option on the global + section to make this option work. +
+ preset=(on|off);
+ This allows you to specify the initial state of a server before any uptest is performed. + on specifies that the server is regarded available. The default is on. + This is especially useful when you set uptest=none; and want to change + the status of a server only via pdnsd-ctl. +
+ proxy_only=(on|off);
+ When this option is set to on, answers given by the servers are always accepted, and no + other servers (as, for example, specified in the NS records of the query domain) are + queried. If you do not turn this option on, pdnsd will do such queries in some cases + (in particular when processing ANY queries).
+ This option is useful when you do not want pdnsd to make connections to outside servers + for some reasons (e.g. when a firewall is blocking such queries).
+ I recommend that you turn on lean_query when using this option.
+ Default is off. +
+ root_server=(on|off|discover);
+ Set this option to on if the servers specified in a section are root servers. + A root server will typically only give the name servers for the top-level domain in its reply. + Setting root_server=on will cause pdnsd to try to use cached information about + top-level domains to reduce to number of queries to root servers, making the resolving of + new names more efficient. + You can get a list of available root servers by running the command + "dig . ns".
+ This option is also necessary if you use the delegation_only option.
+ New in version 1.2.8: This option may also be set to "discover". + This will cause pdnsd to query the servers provided with the ip= option + to obtain the full list of root servers. The root-server addresses will replace the addresses + specified with the ip= option. + This will only be done once on startup, or after a "pdnsd-ctl config" command. + In this case the name servers specified with the ip= option don't have to be + root servers, they just have to know the names and addresses of the root servers. + After root-server discovery pdnsd will behave just as if root_server=on + had been specified.
+ Default is off. +
+ randomize_servers=(on|off);
+ New in version 1.2.6: Set this option to on to give each name server + in this section an equal chance of being queried. If this option is off, the name servers + are always queried starting with the first one specified. Even with this option on, the + query order is not truly random. Only the first server is selected randomly; the following + ones are queried in consecutive order, wrapping around to the beginning of the list when + the end is reached. Note that this option only effects the order within a section. The + servers in the first (active) section are always queried before those in the second one, + etc.
The default is off, but if you are resolving from root servers setting this + option on is highly recommended. If root_server=on this option also effects + the query order of the name servers for the top-level domains. +
+ reject=string;
+ New in version 1.2.6: This option can be used to make pdnsd reject replies that + contain certain IP addresses. You can specify a single IP address, which will be matched + exactly, or a range of addresses using an address/mask pair. + The mask can be specified as a simple integer, indicating the number of initial 1 bits in + the mask, or in the usual IP address notation. IP addresses may be either IPv4 or IPv6 + (provided there is sufficient support in the C libraries and support for AAAA records was + not disabled). + When addresses in the reject list are compared with those in a reply, only the bits + corresponding to those set in the netmask are significant, the rest are ignored.
+ Multiple addresses or address/mask pairs may be specified; this can be done by entering + multiple lines of the form reject=string; + or a single line like this: +

reject=string,string,string;

+ How pdnsd reacts when an address in the reply matches one in the reject list, + depends on the reject_policy option, see below. +
+ reject_policy=(fail|negate);
+ New in version 1.2.6: + This option determines what pdnsd does when an address in the reply from a name server + matches the reject list (see above). If this option is set to + fail, pdnsd will try another server, or, if there no more servers to try, + return the answer SERVFAIL. If this option is set to negate, pdnsd will + immediately return the answer NXDOMAIN (unknown domain) without querying additional + servers. The fail setting is useful if you don't always trust the servers in + this section, but do trust the servers in the following section. The negate + setting can be used to completely censor certain IP addresses. In this case you should put + the same reject list in every server section, and also set the + reject_recursively option (see below) to true.
+ The default is fail. +
+ reject_recursively=(on|off);
+ New in version 1.2.6: Normally pdnsd checks for addresses in the + reject list (see above) only when the reply comes directly from a name server + listed in the configuration file. With this option set to on, pdnsd will + also do this check for name servers that where obtained from NS records in the authority + section of a previous reply (which was incomplete and non-authoritative).
+ Default is off. +
+ policy=(included|excluded|simple_only|fqdn_only);
+ pdnsd supports inclusion/exclusion lists for server sections: with include= + and exclude= (see below) you can specify domain names for which this server + will be used or will not be used. The first match counts (i.e., the first include or + exclude rule in a server section that matches a domain name is applied, and the + search for other rules is terminated). If no rule matched a given domain name, + the policy= option determines whether this server is used for the + lookup for that domain name; when included is given, the server will + be asked, and when excluded is given, it will not. + If simple_only is given the server will be used if the name to lookup + is a simple (single-label) domain name, on the other hand if fqdn_only + is given the server will be used only for names consisting of two or more labels + (i.e. the name has at least one dot in-between).
+ If no server is available for a queried domain, pdnsd will return an error message + to the client that usually will stop the client's attempts to resolve a specific + domain from this server (the libc resolver will e.g. return an error to the application that + tried to resolve the domain if no other servers are available in the resolv.conf). + This may be of use sometimes.
+ Note: the simple_only and fqdn_only constants + were added by Paul Rombouts. + They are useful for controlling which name servers (if any) will be used by + pdnsd for resolving simple (single-label) host names. + fqdn_only used to stand for "fully qualified domain name only", but this is + actually a misnomer. The names in queries received by pdnsd are always considered to be + fully qualified. If you do not exactly understand what the options simple_only and + fqdn_only are good for, you are probably better off not using them.
+ The default for this option is included. +
+ include=string;
+ This option adds an entry to the exclusion/inclusion list. If a domain matches + the name given as string, the server is queried if this was the first matching rule + (see also the entry for policy).
+ If the given name starts with a dot, the whole subdomain + of the given name including the one of that name is matched, e.g. ".foo.bar." + will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar.
+ If it does not start in a dot, only exactly the given name (ignoring the case, of course) + will be matched (hint: if you want to include all subdomains, but not the domain of the given + name itself, place an exact-match exclude rule before the include rule, e.g: + exclude="foo.bar."; include=".foo.bar.";
+ Previous versions of pdnsd + required that names given with this and the next option ended in a dot, but since + version 1.1.8b1-par8, pdnsd automatically adds a dot at the end if it + is missing.
+ pdnsd now also accepts a more compact notation for adding several "include" entries in + one line, e.g.: +

include=".foo",".bar",".my.dom";

+
+ exclude=string;
+ This option adds an entry to the exclusion/inclusion list. If a domain matches + the name given as string, the server is not queried if this was the first matching rule + (see also the entry for policy).
+ If the given name starts with a dot, the whole subdomain + of the given name including the one of that name is matched, e.g. ".foo.bar." + will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar.
+ If it does not start in a dot, only exactly the given name (ignoring the case, of course) + will be matched (hint: if you want to exclude all subdomains, but not the domain of the given + name itself, place an exact-match include rule before the exclude rule, e.g: + include="foo.bar."; exclude=".foo.bar.";
+ pdnsd now also accepts a more compact notation for adding several "exclude" entries in + one line, e.g.: +

exclude=".foo",".bar",".my.dom";

+
+
+

2.1.3 rr Section

+ Every rr section specifies a dns resource record that is stored locally. It + allows you to specify own dns records that are served by pdnsd in a limited way. + Only A, PTR, CNAME, MX, NS and SOA records are implemented.
+ This option is intended to allow you to define RRs for 1.0.0.127.in-addr.arpa. + and localhost. (and perhaps even one or two hosts) without having to start an + extra named if your cached name servers do not serve those records. + It is NOT intended and not capable to work as a full-featured name server. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ name=string;
+ Specifies the name of the resource records, i.e. the domain name of + the resource the record describes. This option must be specified + before any a, ptr, cname, + mx, ns or soa records. + Names are interpreted as absolute domain names + (i.e. pdnsd assumes they end in the root domain). + For this and all following arguments that take domain names, you need to + specify domain names in dotted notation (example venera.isi.edu.).
+ Previous versions of pdnsd + required that domain names given in the configuration file ended in a + dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a + dot at the end if it is missing.
+ New in version 1.2: It is also possible to specify a name starting + with the label *. Such a name is called a wildcard. The * in a wildcard + can match one or more labels in a queried name, but only whole labels. + Any other * characters in a wildcard, apart from the leading one, + will only match a literal *.
+ For example, *.mydomain will match a.mydomain or www.a.mydomain, but not + mydomain. *.a*.mydomain will match www.a*.mydomain, but not www.ab.mydomain. + *a.mydomain will only match itself.
+ Before you can specify an rr section with name=*.mydomain + you must define some records for mydomain, typically NS and/or SOA records. + Example: +
+    rr {
+	name = mydomain;
+	ns = localhost;
+	soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400;
+    }
+    rr {
+	name = *.mydomain;
+	a = 192.168.1.10;
+    }
+ In this example, www.mydomain and ftp.mydomain will resolve to the numeric + address 192.168.1.10 (unless you add rr sections explicitly + specifying different addresses for www.mydomain or ftp.mydomain). + If you want mydomain also to resolve to a numeric address, + add an A record to the first rr section. +
+ ttl=timespec;
+ Specifies the ttl (time to live) for all resource records in this section after this entry. + This may be redefined. The default is 86400 seconds (=1 day). +
+ authrec=(on|off);
+ If this is turned on, pdnsd will create authoritative local records for this rr section. + This means that pdnsd flags the domain record so that records of this domain that are not + present in the cache are treated as non-existent, i.e. no other servers are queried for + that record type, and an response containing none of those records is returned. This is + most time what people want: if you add an A record for a host, and it has no AAAA record + (thus no IPv6 address), you normally don't want other name servers to be queried for it.
+ This is on by default.
+ Please note that this only has an effect if it precedes the name option! +
+ reverse=(on|off);
+ New in version 1.2: If you want a locally defined name to resolve to a numeric address + and vice versa, you can achieve this by setting reverse=on before defining the A record + (see below). The alternative is to define a separate PTR record, but you will + probably find this option much more convenient.
+ The default is off. +
+ a=string;
+ Defines an A (host address) record. The argument is an IPv4 address in dotted notation. + pdnsd will serve this address for the host name given in the name option.
+ Provided there is sufficient support in the C libraries and support for AAAA records was not + disabled, the argument string may also be an IPv6 address, in which case an AAAA record + will be defined.
+ This option be may used multiple times within an rr section, causing + multiple addresses to be defined for the name. However, if you put the different addresses + in different rr sections for the same name, the definition in the last + rr section will cancel the definitions in the previous ones. +
+ ptr=string;
+ Defines a PTR (domain name pointer) record. The argument is a host name in + dotted notation (see name). The ptr record is for resolving adresses into names. For example, if + you want the adress 127.0.0.1 to resolve into localhost, and localhost into 127.0.0.1, you need something + like the following sections:
+
+    rr {
+	name = localhost;
+	a = 127.0.0.1;
+	owner = localhost;
+	soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400;
+    }
+    rr {
+	name = 1.0.0.127.in-addr.arpa;
+	ptr = localhost;
+	owner = localhost;
+	soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400;
+    }
+ The second section is for reverse resolving and uses the ptr option. + Note that you can get the same effect by specifying only the first rr section + with reverse=on.
+ There is something special about the name in the second section: + when a resolver wants to get a host name from an internet address, + it composes an address that is built of the IP address in reverse byte order + (1.0.0.127 instead of 127.0.0.1) where each byte of the adress written + as number constitutes a sub-domain under the domain in-addr.arpa.
+ So, if you want to compose an adress for reverse resolving, take your ip in dotted notation (e.g. 1.2.3.4), + reverse the byte order (4.3.2.1) and append in-addr.arpa. (4.3.2.1.in-addr.arpa.) + Then, define an rr section giving this address as name and the domain name corresponding to + that ip in the ptr option. +
+ cname=string;
+ Defines a CNAME (canonical name) record. + The argument should be a fully-qualified host name in dotted notation (see name). + A CNAME is the DNS equivalent of an alias or symbolic link.
+ A useful application for CNAMEs is giving short, easy to remember nicknames to hosts with complicated names. + For example, you might want the name "news" to refer to your ISP's news server "nntp2.myisp.com". + Instead of adding an A record for "news" with the same address as "nntp2.myisp.com", you could + put in a CNAME pointing to "nntp2.myisp.com", so that if the IP address of the news server changes, + there is no need to update the record for "news".
+ To implement this with pdnsd, you could add the following section to your configuration file:
+
+    rr {
+	name = news;
+	cname = nntp2.myisp.com;
+	owner = localhost;
+    }
+
+ mx=string,number;
+ Defines an MX (mail exchange) record. The string is the host name of the mail server in dotted notation (see name). + The number specifies the preference level.
+ When you send mail to someone, your mail typically goes from your E-mail client to an SMTP server. + The SMTP server then checks for the MX record of the domain in the E-mail address. + For example, with joe@example.com, it would look for the MX record for example.com and find + that the name of mail server for that domain is, say, mail.example.com. + The SMTP server then gets the A record for mail.example.com, and connects to the mail server.
+ If there are multiple MX records, the SMTP server will pick one based on the preference level + (starting with the lowest preference number, working its way up).
+ Don't define MX records with pdnsd unless you know what you're doing. +
+ owner=string;
+ or
+ ns=string;
+ Defines an NS (name server) record. Specifies the name of the host which should be authoritative for the records + you defined in the rr section. This is typically the host pdnsd runs on.
+ Note: In previous versions of pdnsd this option had to be specified before + any a, ptr, cname, mx or soa entries. + In version 1.2, the restrictions on this option are same as the options just mentioned, + and it must listed after the name= option. + This can be a pain if you want to use an old config file which specifies owner= + before name= (sorry about that). + Apart from greater consistency, the advantage is that you can now specify as many NS records as you like (including zero). +
+ soa=string,string,number,timespec,timespec,timespec,timespec;
+ This defines a soa (start of authority) record. The first string is the + domain name of the server and should be equal to the name you specified as + owner.
+ The second string specifies the email address of the maintainer of the name + server. It is also specified as a domain name, so you will have to replace the + @ sign in the name with a dot (.) to get the name you have to specify here. + The next parameter (the first number) is the serial number of the record. You + should increment this number if you change the record.
+ The 4th parameter is the refresh timeout. It specifies after what amount + of time a caching server should attempt to refresh the cached record.
+ The 5th parameter specifies a time after which a caching server should attempt + to refresh the record after a refresh failure.
+ The 6th parameter defines the timeout after which a cached record expires if it + has not been refreshed.
+ The 7th parameter is the ttl that is specified in every rr and should be the + same as given with the ttl option (if you do not specify a ttl, use the default 86400). +
+ txt=string,...,string;
+ New in version 1.2.9: + Defines an TXT record. You can specify one or more strings here. +
+
+

2.1.4 neg Section

+ Every neg section specifies a dns resource record or a dns domain that should be + cached negatively locally. Queries for negatively cached records are always answered + immediatley with an error or an empty answer without querying other hosts as long + as the record is valid. The records defined with neg sections remain + valid until they are explicitely invalidated or deleted by the user using + pdnsd-ctl.
+ This is useful if a certain application asks periodically for nonexisting hosts or + RR types and you do not want a query to go out every time the cached record has + timed out. Example: Netscape Communicator will ask for the servers news and mail + on startup if unconfigured. If you do not have a dns search list for your network, + you can inhibit outgoing queries for these by specifying
+
+    neg {
+	name = news;
+	types = domain;
+    }
+    neg {
+	name = mail;
+	types = domain;
+    }
+ in your config file. If you have a search list, you have to repeat that for any + entry in your search list in addition to the entries given above!
+ In versions 1.1.11 and later, if you negate whole domains this way, all subdomains + will be negated as well. Thus if you specify
+ neg {name=example.com; types=domain;} in the + config file, this will also negate www.example.com, xxx.adserver.example.com, etc. +

+ + + + + + + + + + +
+ name=string;
+ Specifies the name of the domain for which negative cache entries are created. + This option must be specified before the types option. + Names are interpreted as absolute domain names (i.e. pdnsd + assumes they end in the root domain). + You need to specify domain names in dotted notation (example venera.isi.edu.).
+ Previous versions of pdnsd + required that domain names given in the configuration file ended in a + dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a + dot at the end if it is missing. +
+ ttl=timespec;
+ Specifies the ttl (time to live) for all resource records in this section after this entry. + This may be redefined. The default is 86400 seconds (=1 day). +
+ types=(domain|rr_type[,rr_type[,rr_type[,...]]]);
+ Specifies what is to be cached negatively: domain will cache the whole + domain negatively; alternatively, you can specify a comma-separated list of RR types + which are to be cached negatively. You may specify multiple types options, but + domain and the RR types are mutually exclusive.
+ The RR types are specified using their official names from the RFC's in capitals, + e.g. A, CNAME, NS, PTR, MX, + AAAA, ...
+ The command pdnsd-ctl list-rrtypes will give you a complete list + of those types. pdnsd-ctl is built along with pdnsd + and will be installed in the same directory as the pdnsd binary during make install. +
+
+

2.1.5 source Section

+ Every source section allows you to let pdnsd read the records from a file in an + /etc/hosts-like format. pdnsd will generate records to resolve the entries + address from its host name and vice versa for every entry in the file. This is + normally easier than defining an rr for every of your addresses, since localhost + and your other FQDNs are normally given in /etc/hosts.
+ The accepted format is as follows: The #-sign initiates a comment, the rest of + the line from the first occurence of this character on is ignored. Empty lines + are tolerated.
+ The first entry on a line (predeceded by an arbitrary number of tabs and spaces) + is the IP in dotted notation, the second entry on one line (separated by the + first by an arbitrary number of tabs and spaces) is the FQDN (fully qualified + domain name) for that ip. The rest of the line is ignored by default (in the original + /etc/hosts, it may contain information not needed by pdnsd). +

+ + + + + + + + + + + + + + + + +
+ owner=string;
+ Specifies the name of the host pdnsd runs on and that are specified in dns + answers (specifically, nameserver records). + Must be specified before any file entries.
+ Names are interpreted as absolute domain names (i.e. pdnsd + assumes they end in the root domain). + You need to specify domain names in dotted notation (example venera.isi.edu.).
+ Previous versions of pdnsd + required that domain names given in the configuration file ended in a + dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a + dot at the end if it is missing. +
+ ttl=timespec;
+ Specifies the ttl (time to live) for all resource records in this section after + this entry. This may be redefined. The default is 86400 seconds (=1 day). +
+ file=string;
+ The string specifies a file name. For every file entry in a source section, + pdnsd will try to load the given file as described above. Failure is indicated + only when the file cannot be opened, malformed entries will be ignored. +
+ serve_aliases=(on|off);
+ If this is turned on pdnsd will serve the aliases given in a hosts-style file. + These are the third entry in a line of a hosts-style file, which usually give a "short name" for the host. + This may be used to support broken clients without a proper domain-search option. + If no aliases are given in a line of the file, pdnsd behaves as without this option for this line.
+ This feature was suggested by Bert Frederiks.
+ It is off by default. +
+ authrec=(on|off);
+ If this is turned on, pdnsd will create authoritative local records with the data from the hosts file. + Please see the description of the option of the same name in the rr section for a closer description of + what this means. Please note that this only has an effect for files sourced with file options + subsequent to this option.
+ This is on by default. +
+
+

2.1.6 include Section

+ A configuration file may include other configuration files. + However, only the top-level configuration file may contain global + and server sections, + thus include files are effectively limited to sections that add local definitions to the cache.
+ Include sections currently only have one type of option, which may be given multiple times within a single section. +

+ + + + +
+ file=string;
+ The string specifies a file name. For every file option in an include section, + pdnsd will parse the given file as described above. The file may contain include sections itself, + but as a precaution pdnsd checks that a certain maximum depth is not exceeded to guard against + the possibility of infinite recursion. +
+
+

3 pdnsd-ctl

+

+ pdnsd-ctl allows you to configure pdnsd at run time. To make this work, you have to start pdnsd with the -s + option (or use the status_ctl option in the config file). You also should make sure that you + have appropriate permissions on the control socket (use the ctl_perms option to make this sure) and of your pdnsd + cache directory (pdnsd keeps its socket there). Please make sure the pdnsd cache directory is not writeable for untrusted users!

+

+ pdnsd-ctl accepts two command-line options starting with a dash.
+ -c may be used to specify the cache directory (and takes this as argument). + The default for this setting is the pdnsd default cache directory (specified at compile time). + The cache directory for pdnsd-ctl must be the same pdnsd uses!
+ -q can be used to make the output of pdnsd-ctl less verbose.

+

+ The following table lists the commands that pdnsd-ctl supports. The command must always be + the first command-line option (not starting with a dash), the arguments to the command must follow in the given order.
+ In the following table, keywords are in a normal font, while placeholders are in italics.
+ Alternatives are specified like (alt1|alt2|alt3). + Optional arguments are placed between square brackets [].

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandArgumentsDescription
helpPrint a command summary.
versionPrint version and license info.
status + Print a description of pdnsd's cache status, thread status and configuration. + Also shows which remote name servers are assumed to be available. +
server(index|label)  (up|down|retest)  [dns1[,dns2[,...]]] + Set the status of the server with the given index or label (where the given label + matches the one given with the label option in the respective server section in the config file) + to up or down, or force a retest. The index is assigned in the order of definition in + pdnsd.conf starting with 0. Use the status command to view the indices and labels. + You can specify all instead of an index or label to perform the action for all + servers registered with pdnsd. Example:
+ pdnsd-ctl server 0 retest
+ An optional third argument consisting of a list of IP addresses (separated by commas or + white-space characters) can be given. + This list will replace the previous list of addresses of name servers used by pdnsd in the + specified section of the config file. + For example in the /etc/ppp/ip-up script called by pppd you could + place the following line:
+ pdnsd-ctl server isplabel up $DNS1,$DNS2
+ If white space is used to separate addresses the list will have to be quoted. + Spurious commas and white-space characters are ignored. + The last argument may also be an empty string, in which case the existing IP addresses are + removed and the corresponding server section becomes inactive. +
recordname  (delete|invalidate) + Delete or invalidate the records of the given domain name if it is in the + cache. Invalidation means that the records are marked as timed out, and + will be reloaded if possible (if purge_cache is set to on, they will + be deleted in any case).
+ For local records (i.e., records that were given in the config file + using a rr section, records read from a hosts-style file + and records added using pdnsd-ctl), invalidation has no effect. Deletion + will work, though. Example:
+ pdnsd-ctl record localhost. delete +
sourcefn  owner  [ttl]  [(on|off)]  [noauth] + Load a hosts-style file. Works like using the pdnsd + source configuration section. + owner and ttl are used as in the source section. ttl has a default + of 900 (it does not need to be specified). The next to last argument corresponds + to the serve_aliases option, and is off by default (i.e. if it is not specified). + noauth is used to make the domains non-authoritative - please see + the description of the authrec config file options for a description of what + that means. + fn is the filename. The file must be readable by pdnsd! Example:
+ pdnsd-ctl source /etc/hosts localhost. 900 off +
adda  addr  name  [ttl]  [noauth] + Add a record of the given type to the pdnsd cache, replacing existing + records for the same name and type. The 2nd argument corresponds + to the value of the option in the rr section that is named like + the first argument: a is a record for hostname-to-address mapping, + aaaa is the same thing for IPv6 addresses, and ptr is for address-to-hostname + mapping. See the documentation for the rr section for more details. + In case of A and AAAA records, the addr argument may be a list of IP addresses, + separated by commas or white space, causing multiple addresses to be defined + for the same name. + The ttl is optional, the default is 900 seconds. + noauth is used to make the domains non-authoritative - please see + the description of the authrec config file options for a description of what + that means. + If you want no other record than the newly added in the cache, do
+ pdnsd-ctl record name delete + before adding records. This is also better when overwriting local records. Example:
+ pdnsd-ctl add a 127.0.0.1 localhost. 900 +
addaaaa  addr  name  [ttl]  [noauth]
addptr  host  name  [ttl]  [noauth]
addcname  host  name  [ttl]  [noauth]
addmx  host  name  pref  [ttl]  [noauth]
addns  host  name  [ttl]  [noauth]
negname  [type]  [ttl] + Add a negatively cached record to pdnsd's cache, replacing existing + records for the same name and type. If no type is given, the whole + domain is cached negatively. For negatively cached records, errors are + immediately returned on a query, without querying other servers first. + The ttl is optional, the default is 900 seconds.
+ You can get a list of all types you can pass to this command using + pdnsd-ctl list-rrtypes. The type is treated case-sensitive! + Example:
+ pdnsd-ctl neg foo.bar A 900
+ pdnsd-ctl neg foo.baz 900
+
config[filename] + Reload pdnsd's configuration file.
+ The config file must be owned by the uid that pdnsd had when it was + started, and be readable by pdnsd's run_as uid. If no file name is + specified, the config file used at start-up is reloaded.
+ Note that some configuration changes, like the port or IP address pdnsd listens on, + cannot be made this way and you will receive an error message. + In these cases, you will have to restart pdnsd instead. +
includefilename + Parse the given file as an include file, see the documentation on + include sections for a description + what this file may contain.
+ This command is useful for adding definitions to the cache without reconfiguring pdnsd. +
evalstring + Parse the given string as if it were part of pdnsd's configuration file. + The string should hold one or more complete configuration sections. + However, global and + server sections are not allowed, + just as in include files. + If multiple strings are given, they will be joined using newline chars + and parsed together.
+ This command is useful for adding records interactively to the cache + that cannot be defined using the "pdnsd-ctl add" command, + (e.g. soa records). +
empty-cache[[+|-]name ...] + If no arguments are provided, the cache will be completely emptied, + freeing all existing entries. + Note that this also removes local records, as defined by the config file. + To restore local records, run "pdnsd-ctl config" or + "pdnsd-ctl include filename" immediately afterwards.
+ The "pdnsd-ctl empty-cache" command now accepts additional arguments; + these are interpreted as include/exclude names. If an argument starts with a '+' + the name will be included. If an argument starts with a '-' it will be + excluded. If an argument does not begin with '+' or '-', a '+' is + assumed. If the domain name of a cache entry ends in one of the names in + the list, the first match will determine what happens. If the matching + name is to be included, the cache entry is deleted, otherwise not. + If there are no matches, the default action is not to delete. + Note that if you want to delete exactly one name and no others, you should + use "pdnsd-ctl record name delete", + this is also much more efficient.
+ Examples:
+ pdnsd-ctl empty-cache
+ This command will remove all cache entries.
+
+ pdnsd-ctl empty-cache microsoft.com msft.net
+ This will remove all entries ending in microsoft.com or msft.net.
+
+ pdnsd-ctl empty-cache -localdomain -168.192.in-addr.arpa .
+ This will remove all entries except those ending in localdomain or + 168.192.in-addr.arpa. Note that '.' is the root domain which matches any + domain name. +
dump[name] + Print information stored in the cache about name. + If name begins with a dot and is not the root domain, information about + the names in the cache ending in name (including name without + the leading dot) will be printed. + If name is not specified, information about all the names in the cache will + be printed.
+ For each RR record the time and date that this record has been added to the cache + will be printed in the form mm/dd HH:MM:SS (locally defined records are printed without a time stamp). + After that the type of record is printed with the data. For the more common types + of RR records the data will be printed in human readable form, the remaining ones in a + hexadecimal representation.
+ This command is mainly useful for diagnostic purposes.
+ Note that if you pipe the output of this command through an application that + reads only part of the output and then blocks (such as more or less), + pdnsd will not be able to add new entries to the cache until the pipe is closed. + It is preferable to capture the output in a file in such a case. +
list-rrtypes + List available rr types for the neg command. + Note that those are only used for the neg command, not for add! +
+
+
+

4 contrib/

+ The contrib directory in the pdnsd distribution contains useful user-contributed scripts.
+ So far, there are scripts contributed by Marko Stolle and Paul Rombouts that make pdnsd + usable in a DHCP setup. + Please take a look into the README file in the contrib directory for further information. +
+
+

5 Problems...

+ If you have problems with configuring or running pdnsd, be sure to read the FAQ. + If this does not help you, pdnsd crashes or you find bugs, please mail one of the authors.
+ Note added by Paul A. Rombouts: + Thomas Moestl no longer maintains the code. I have revised the code and added new features. + See README.par and the + ChangeLog in the source directory + (or /usr/share/doc/pdnsd-<version> if you have installed a binary package) + for more details. + If you have questions about my modifications, you can find my email address at the end of + README.par. +
+
+

6 Hacking

+ Here comes some information you might find useful for hacking pdnsd. +
+

6.1 Source files

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Makefile.am, configure.in, acconfig.h + autoconf/automake/autoheader scripts. Makefile.am's are in most subdirectories. +
pdnsd.spec.in + A template from which configure generates a spec file for building rpm's for various + distributions. +
version + Contains only the program version string. Needed for several templates. +
src/rc/* + rc (start-up) scripts for various linux distributions. +
src/cache.c + The pdnsd cache subsystem(s) as defined in src/cache.h. + This is the "traditional" pdnsd system which keeps the cache in memory and uses hash tables for accesses. + Sourav K. Mandal is working on a system using gdbm. +
src/pdnsd-ctl/* + Contains the code for pdnsd-ctl, a program that allows you to control pdnsd at run time. +
src/conf-lex.l.in + The lex/flex source file for the config file lexer. This is a template because there might be + inserted "%option yylineno" for proper flex support. + (obsolete, superseded by src/conf-parser.c) +
src/conf-lex.l + This is automatically generated by configure from conf-lex.l.in. It may be overwritten + in any make, so never modify this, but conf-lex.l.in instead! + (obsolete, superseded by src/conf-parser.c) +
src/conf-parse.y + The yacc/bison source of the config file parser. + (obsolete, superseded by src/conf-parser.c) +
src/conf-parser.c, src/conf-parser.h, src/conf-keywords.h + The config file parser written purely in C (versions 1.1.10-par and later). +
src/conff.c, src/conff.h + The configuration handler functions and their prototypes. The parser is called from here. +
src/consts.h + Some constants used by the parser, config file handler functions and in the server status thread, + among others. +
src/dns.c, src/dns.h + Define dns message structures, constants, and some common dns data handlers. dns.h contains gcc-specific + code (in praticular, "__attribute__((packed))"). +
src/dns_answer.c, src/dns_answer.h + Define functions that answer incoming dns queries. +
src/dns_query.c, src/dns_query.h + Define functions to manage outgoing dns queries. +
src/error.c, src/error.h + Functions for error output to stderr or the syslog, and debug output to stderr or pdnsd.debug. +
src/hash.c, src/hash.h + Contains the code for storing and looking up cache entries in the hash table. +
src/helpers.c, src/helpers.h + Define miscellaneous helper functions. +
src/icmp.c, src/icmp.h + Define a function for performing a ping test. This contains OS-specific code. +
src/main.c + Contains main(), which holds the command line parser, performs initialisations and signal handling. +
src/make_hashconvtable.c + Contains the code for the executable make_hashconvtable, which is only run once, during build time, to generate the file hashconvtable.h, used by src/hash.c (versions 1.1.10-par and later). + (obsolete since version 1.2) +
src/make_rr_types_h.pl + A perl script for generating src/rr_types.h, + a C header file containing macro definitions and tables needed for handling the + RR types known to pdnsd, from the text file src/rr_types.in. +
src/rr_types.c, src/rr_types.h, src/rr_types.in + These define tables and macros needed for handling the RR types known to pdnsd. + Since version 1.2.9, rr_types.h is an automatically generated file, + see make_rr_types_h.pl. +
src/netdev.c, src/netdev.h + Define functions for network device handling. OS-specific. +
src/servers.c, src/servers.h + Define functions for the server status thread that performs the periodical uptests. +
src/status.c, src/status.h + Define functions for the status control thread. This is pdnsd's interface to pdnsd-ctl. +
+ +
+
+
+ Copyright (C) 2000, 2001 Thomas Moestl
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2012 Paul A. Rombouts +
+

+ Last revised: 19 April 2012 by Paul A. Rombouts +

+ + diff --git a/service/src/main/jni/pdnsd/doc/html/doc_makefile b/service/src/main/jni/pdnsd/doc/html/doc_makefile new file mode 100644 index 0000000..28fb7d5 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html/doc_makefile @@ -0,0 +1,23 @@ + +versionfile = ../../version +arch = i686 +arch2 = x86_64 +extver = _sl6 +system = Scientific Linux 6.2 system + +doc: dl.html +.PHONY: doc clean + +# If the existing dl.html contains '???', then certain packages were missing +# during the previous build and dl.html needs to be built again. +ifneq ($(shell grep -F -l -e '???' dl.html),) +.PHONY: dl.html +endif + +dl.html: %.html: %.html.in htmlsubst.pl $(versionfile) + perl htmlsubst.pl version=`cat $(versionfile)` \ + baseurl='http://members.home.nl/p.a.rombouts/pdnsd/' \ + arch=$(arch) arch2=$(arch2) extver=$(extver) system="$(system)" $< > $@ + +clean: + @rm -fv dl.html diff --git a/service/src/main/jni/pdnsd/doc/html/faq.html b/service/src/main/jni/pdnsd/doc/html/faq.html new file mode 100644 index 0000000..eec6599 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html/faq.html @@ -0,0 +1,412 @@ + + + + The pdnsd FAQ + + + + + + + + + + + + + + +
+ pdnsd Homepage + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+ +

The pdnsd FAQ

+ + + + + + + + + + +
Q: + There are complete and well-tested name servers around, such as the BIND. + These do also perform caching. Why should I use pdnsd? +
A: + pdnsd does not aim to be a complete name server implementation, such as the + BIND. It is optimized for caching, and you can only specify a small subset of all + dns record types pdnsd knows in your local "zone" definitions. + This of course reduces the code size drastically, and such the memory footprint. + There are some features especially interesting for dialin networks, ordinary + (non-server) internet hosts and computers that are often not connected to + to their network, e.g. notebooks (I originally wrote this program for use + with my notebook). + These features are: +
    +
  • permanent disk cache (useful for frequent power-offs/reboots) +
  • usually smaller memory footprint (depends on cache size) (see next question) +
  • offline-detection prevents hangs (e.g. the typical hang on startup of some + Netscape Navigator versions if not dialled in) +
  • better control about timeouts (also to prevent hangs) +
  • better control over the cache +
  • better run-time control +
+
+
+ + + + + + + + + + +
Q: + When I look at the process size with ps, top, gtop, or a similar tool, I see + some processes with a total size well above 3.5 MB. This is much more than + e.g. BIND named (about 1.4 MB). Why? +
A: + Really, it is not. + pdnsd uses multithreading, not multiprocessing. That means that the processes + share most of their process space. In the LinuxThreads library + or NPTL (Native Posix Thread Libary), + which are used by pdnsd on Linux, in fact the total process address space is shared + (although the processes have different stacks, these are in one process + address space). You may check this by looking at the at the process sizes of + the pdnsd threads: all should be the same. The effective size that pdnsd + occupies is thus the size of any of the processes, not the sum of those. + So, pdnsd with empty cache occupies about 800 kB, and the maximum size + should be about the cache size plus this size (in fact, ca 5-10% more). +
+
+ + + + + + + + + + +
Q: + What do I need the status control (option -s) for? +
A: + It enables you to do some things you might or might not need. With it, you can: +
    +
  • query pdnsd's settings at runtime to debug configuration files and + see which servers are regarded to be available +
  • mark servers as available or unavailable, or force a status retest - very + handy if you want to control which servers pdnsd queries, e.g for muliple + dial-up accounts +
  • delete, invalidate or add DNS records - useful e.g. when you want to build + records for dynamically assigned IP addresses or domain names +
  • reload pdnsd's configuration file without restarting pdnsd +
  • print information about the contents of pdnsd's cache. +
+
+
+ + + + + + + + + + +
Q: + What do I need local records (rr- and source-sections in the config file) for? +
A: + Some resolver programs, e.g. nslookup, want to look up the name of the + server they are using before doing anything else. This option is for defining + a PTR record for your IP such that those programs get an answer even if the + name server you are caching is not available or does not offer these records. + By extension, you may also define A and SOA records. This allows you to build + very small zones without having to use a "big" name server. It is NOT + intended to replace such a complete server in anything but VERY small + networks. Alternatively, you may start a named on another host or on the + same host on another port and cache it with pdnsd in addition to other (more + distant) name servers. +
+ The source section allows you to let pdnsd read in your + /etc/hosts file on startup and serve its contents. This file is used by your local + resolver before it even tries the name servers and usually contains + fully-qualified domain names (FQDNs) for all of the internet addresses your host has. + If you source this file, you usually won't need any additional rr sections. Sourcing it also allows + other hosts (eg. in your local network) to access the names defined in your + hosts file. You can of course just add other hosts in your local network to the + servers hosts file, thus making them known to your server's resolver + and pdnsd (if you sourced that file). +
+ If you don't know what this answer was all about, you should just take the + source section in the sample config file that comes with pdnsd, copy it + into your config file and forget about it. +
+
+ + + + + + + + + + +
Q: + When compiling, I get an error message like
Please define __BYTE_ORDER to + be __LITTLE_ENDIAN or __BIG_ENDIAN
What's up? +
A: + Normally, this macros should be defined in your C library's header files. + There are two different methods, most C libraries support both (and pdnsd + honors both): either __BYTE_ORDER is set to __LITTLE_ENDIAN + or __BIG_ENDIAN, or __LITTLE_ENDIAN or __BIG_ENDIAN + are directly defined as macros. +
+ Linux glibc, for example, does set those macros correctly. Never mind. You just have to know + whether your machine is little-endian or big-endian, this means wheter your + machine saves the least significant byte of a word or double-word first in memory (little-endian) or + the most significant first (big-endian). + All intel x86 and Alpha machines are little-endian, for example, while SPARC + and PowerPC architectures are big-endian. + If your machine is little-endian, add the following line to your config.h: +
+ #define __BYTE_ORDER __LITTLE_ENDIAN +
+ Likewise, if your machines byte order is big-endian: +
+ #define __BYTE_ORDER __BIG_ENDIAN +
+ Pathological byte orders like pdp-endian are not yet supported really; + However, for the place the endianess is needed, __LITTLE_ENDIAN should do + (it deals only with 16 bits; for all other occurances, ntoh[sl]/hton[sl] is used). +
+
+ + + + + + + + + + +
Q: + At startup, I get a warning saying:
+ + Uptest command [...] will implicitly be executed as root +
+ What does that mean? +
A: + This warning only occurs if you use the uptest=exec option in your + configuration. It means that the uptest command is run as root + because pdnsd is running as root, and this was not explicitely specified. + The idea is that it may introduce security holes (in the programs being run) + when they run as root, and so they shouldn't do that if possible. + You can specify the user that shall run the command by appending its name + comma-separated as string to the uptest_cmd line:
+ + uptest_cmd="<your command>","<user>"; +
+ If it is correctly running as root, just append the user string "root" to + the command and the warning will not occur again. +
+
+ + + + + + + + + + +
Q: + I cannot run my uptest_cmd command as root (it says permission denied), + although the pdnsd executable is setuid root. Why? +
A: + pdnsd will drop privileges gained through setuid/setgid before executing the + uptest commands (you shouldn't set the pdnsd executable setuid/setgid anyway). + The reason is clear: if you install the pdnsd + executable as setuid root and this wouln't be done, any user could execute + shellcode with root privileges using that option! +
+
+ + + + + + + + + + +
Q: + At startup, I get an error saying:
+ + Bad config file permissions: the file must be only writeable by the user +
+ Why is that? +
A: + pdnsd has an option (uptest=exec) that allows the execution of arbitrary + shell code (for testing whether an interface is up). This must be + of course secured against unauthorized use. One of these + protection is the one that produces the error message: if you routinely run + pdnsd, e.g. at system startup, and your config file is editable for others, + someone could change it and insert shell code that is executed in the next + pdnsd run -- with your user privileges! To prevent this, pdnsd will exit if the config file is writeable + by others than the owner. + To get rid of this message, just do
+ + chmod go-w <filename> +
+ on your config + file (for the default file: chmod go-w /etc/pdnsd.conf). + You should also check that the ownership is set correct. +
+
+ + + + + + + + + + +
Q: + serve_aliases does not seem to work. +
A: + Some resolvers (e.g. of the glibc 2.1) seem sometimes not to look up unmodified names, but the names with + an entry of the search path already appended. Since pdnsd will serve short names with this + option anyway, you can delete the search an domain options from your /etc/resolv.conf. This is reported to + work in some cases. +
+
+ + + + + + + + + + +
Q: + Some queries for domains that have many records (e.g. www.gmx.de) fail mysteriously. +
A: + pdnsd versions prior to 1.1.0 had the tcp server thread disabled by default. Most resolvers + repeat their query using tcp when they receive a truncated answer (the answer is truncated + when it exceeds a length of 512 bytes). You need to recompile pdnsd with the option + --enable-tcp-server to fix this. +
+
+ + + + + + + + + + +
Q: + I am behind some kind of firewall. In the configuration file + I have only listed addresses of name servers on the local (ISP's) network, + but pdnsd is slow and DNS queries frequently time out. +
A: + In some cases pdnsd will not consider the answer of the local name server + authoritative enough, and will try to get answers from the name servers listed in the + authority section of the reply message. If pdnsd is behind a firewall that blocks the + UDP reply packets from remote name servers, pdnsd will wait in vain for a reply. + One solution is to set proxy_only=on + in the servers sections of the configuration file. + This will prevent pdnsd from querying name servers that are not listed in the configuration + file. + Another solution that can be tried is specifying + query_method=tcp_only + in the global section of the configuration file, because a firewall that blocks + UDP packets from outside might still allow outgoing TCP connections to port 53. +
+
+ + + + + + + + + + +
Q: + Is pdnsd vulnerable to DNS cache poisoning as described in + CERT vulnerability note VU#800113? +
A: + Short answer: Yes.
+ Somewhat longer answer: The problem is not so much that pdnsd's implementation is flawed + but rather that the DNS protocol currently being used is fundamentally flawed from + a security viewpoint. As long as a more secure protocol is not in place, + all that the developers of pdnsd can do is to try to tweak the current implementation + to make it as difficult as possible for an attacker to succeed.
+ From version 1.2.7 onwards, the default for the query_port_start option + is 1024, which means that the pdnsd resolver will randomly select source ports + in the range 1024-65535. (In previous versions the default was to let the kernel select + the source ports, which will often result in a more or less predictable sequence of ports.) + It also helps to use a good quality source of random numbers. On platforms where this is + supported, it is preferable to configure with --with-random-device=/dev/urandom. + There is still more that can be done to make pdnsd less vulnerable, but this remains + (as of this writing) a work in progress. +
+ Please note that pdnsd was designed for small (private) networks, and that it is generally + not recommended to let untrusted users access pdnsd. +
+ +
+
Thomas Moestl + and Paul Rombouts +
+
+

+ Last revised: 18 August 2008 by Paul Rombouts +

+ + + diff --git a/service/src/main/jni/pdnsd/doc/html/htmlsubst.pl b/service/src/main/jni/pdnsd/doc/html/htmlsubst.pl new file mode 100644 index 0000000..a9e3e9f --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html/htmlsubst.pl @@ -0,0 +1,36 @@ +#!/usr/bin/perl -w + +# Primitive ad-hoc script for updating pdnsd html doc files. +# Written by Paul Rombouts. + +use strict; +use integer; +use POSIX qw(strftime); + +my %paramvals=(); + +while(@ARGV && $ARGV[0]=~/^([^=]*)=(.*)$/) { + my $param=$1; my $val=$2; + if($param =~ /^[[:alpha:]]\w*$/) { + $paramvals{$param}=$val; + } + else {warn "Warning: invalid parameter '$param' ignored.\n"} + shift @ARGV; +} + +sub sizeof { + my($arg)=@_; + (my $str= $arg) =~ s/\$(?:([[:alpha:]]\w*)\b|\{([[:alpha:]]\w*)\})/ + defined($paramvals{$+})?$paramvals{$+}:defined($ENV{$+})?$ENV{$+}:''/eg; + my $filename=eval($str); + (-f $filename) or return '???'; + (((-s $filename)+1023)/1024).'kB'; +} + +while(<>) { + s/\$(?:date\b|\{date\})/strftime("%d %b %Y",localtime)/eg; + s/\$sizeof\(([^()]*)\)/sizeof($1)/eg; + s/\$(?:([[:alpha:]]\w*)\b|\{([[:alpha:]]\w*)\})/ + defined($paramvals{$+})?$paramvals{$+}:defined($ENV{$+})?$ENV{$+}:''/eg; + print; +} diff --git a/service/src/main/jni/pdnsd/doc/html/index.html b/service/src/main/jni/pdnsd/doc/html/index.html new file mode 100644 index 0000000..d2e426a --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html/index.html @@ -0,0 +1,686 @@ + + + + pdnsd homepage + + + + + + + + + + + + + + + +
+ About pdnsd + + pdnsd FAQ + + Documentation + + GNU GPL (pdnsd's License) + + Download Section +
+

The pdnsd Homepage

+

News

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2012-03-17Version 1.2.9a-par has been released. + Version 1.2.9a fixes a bug in the 1.2.9 release that causes a build failure when pdnsd is + configured with --enable-strict-rfc2181. + If you do not use this option to compile pdnsd, there is no need to upgrade from 1.2.9 to 1.2.9a. +
2012-02-27Version 1.2.9-par has been released. + Version 1.2.9 supports many more RR types (including those necessary for DNSSEC) and + EDNS (Extension mechanisms for DNS) to enable UDP messages larger than 512 bytes. + It also has support for defining local TXT records and has several new options and bugfixes + (including file descriptor leaks that effect FreeBSD users). +
2011-05-03The latest source code is available from a + git repository.
+ In response to frequent requests I have uploaded a git tree including the latest code + and a fairly extensive history of pdnsd development to + gitorious.org. + Anyone who wants to participate in pdnsd development is free to create a + clone repo on gitorious.org + and push his modifications there. +
2010-02-22Version 1.2.8-par has been released. + The main new feature of version 1.2.8 is automatic discovery of root servers. + Furthermore, there are some additional improvements in the resolver. +
2008-09-04Version 1.2.7-par has been released. + Foremost, this release fixes some security problems. + It contains a fix for a "dangling pointer" bug that could cause pdnsd to + crash when it received a long reply. It also addresses some of the issues + raised in the CERT + vulnerability note VU#800113 by making the default of + query_port_start equal to 1024, thereby ensuring that source + ports are randomly selected by the pdnsd resolver in the range 1024-65535. + This release also fixes problems with compiling pdnsd for the ARM architecture + and for the Darwin platform (Max OS X). +
+ There are a number of (minor) new features. + pdnsd now supports "include" files, essentially configuration files that + only contain definitions for local records. + It is now possible to define interactively, using pdnsd-ctl, + any local record that can be defined in a configuration file. +
2007-09-04Version 1.2.6-par has been released. + pdnsd's license has been upgraded to GPL version 3. + A bug has been fixed which which caused pdnsd to handle NXDOMAIN replies + inefficiently when configured with neg_domain_pol=on. The + code that implements the ping test has been fixed, which was broken for + 64-bit systems. A new option randomize_servers can be used + to give each server in a section of the configuration file an equal + chance of being queried. The new options reject, + reject_policy and reject_recursively make it + possible to check for the presence of certain IP addresses in the + replies of name servers and to avoid some types of unwanted replies. + The pdnsd-ctl 'add a' and 'add aaaa' commands + now allow multiple IP addresses to be specified for the same name. + pdnsd's ability to resolve from root servers has been improved. +
2006-09-02Version 1.2.5-par has been released. + This release introduces a new query method: udp_tcp. + With this method a UDP query is tried first and, if the UDP answer is + truncated, the query is repeated using TCP, which is the behaviour that + seems to be recommended by the DNS standards. There is a new + configuration option use_nss, which can be turned off to + prevent lengthy timeouts and stalls in certain situations. A bug has + been fixed which could cause pdnsd to crash if debug output was + generated before the debug output stream was properly initialized. +
2006-01-09Version 1.2.4-par has been released. + A memory leak and a minor buffer-overflow problem have been fixed. + There is now a fix for some situations that would previously cause pdnsd to + exit prematurely (such as ACPI S3 sleep or trying to attach strace to pdnsd). + Time intervals specified in the configuration file can now be expressed in + minutes, hours, days and weeks as well as seconds. + Support for Apple Mac OS X v10.4 Tiger has been improved. + The "pdnsd-ctl status" command now also provides some + information about the status of the running threads. + There are some further improvements in the debugging information provided by pdnsd.
+ TCP-query support is now compiled in by default (but can still be disabled using + the configure option --disable-tcp-queries). +
2005-07-11Version 1.2.3-par has been released. + New feature in this release: the "pdnsd-ctl empty-cache" command can + be provided with an include/exclude list, allowing the user to specify a + selection of names to be removed, instead of emptying the cache completely.
+ Additional improvements: pdnsd should now remain responsive while executing the + "pdnsd-ctl empty-cache" command. + With the query_method=tcp_udp option pdnsd will now also + try a UDP query after a TCP connection times out, which should allow + pdnsd to resolve the same names with query_method=tcp_udp + as with query_method=udp_only, although perhaps with an + occasional delay. + "pdnsd-ctl config" or "pdnsd-ctl server" + commands should now run without delays, even if pdnsd is performing + ping or query uptests at the time. + Some problems with resolving certain names using root servers have been fixed. +
2005-04-03Version 1.2.2-par has been released. + The main emphasis of this release is improved portability. + A bug has been fixed that prevented pdnsd from compiling successfully on some + 64 bit architectures. + This release has (experimental) support for the Darwin (Apple Mac OS X) platform. + On Linux systems, the configure script will now try to detect automatically whether + the system implements the Native POSIX Thread Library, but the method used may not + necessarily be foolproof. + In addition, the debug features have been improved and should make it easier to find out + why pdnsd considers some queries or replies malformed. +
2004-11-07Version 1.2.1-par has been released. + The main new feature of this release is improved support for non-Linux platforms.
+ This release has (experimental) support for the Cygwin platform, and should also fix + some compilation glitches that have been reported by FreeBSD users. +
2004-10-10Version 1.2-par has been released. + pdnsd is new and improved! Most of the changes effect the internal workings + of pdnsd, but there also a number of interesting new features (well, I think they are interesting).
+ Among the bugs fixed are two rather nasty ones which involve the handling of NXT and NAPTR records + and which can cause pdnsd to crash or abort.
+ The new features include a new server availability test which can be specified with uptest=query, + support for reading the DNS configuration from resolv.conf files, + a new option for optimizing the use of root servers, + a new option that makes defining local records for reverse resolving easier, + support for defining wildcard records, + a new pdnsd-ctl command for reloading the config file without restarting pdnsd, and + a new pdnsd-ctl command for dumping information about the names stored in the cache. + The documentation has also been updated: there is now a pdnsd.conf man page.
+ For a more complete list of the changes I'll have to refer you to README.par and the ChangeLog. +
2004-05-22Version 1.1.11a-par has been released. + This release contains a fix for FreeBSD users that bypasses a problem + with the macro ENONET, which can cause a compilation failure when it is undefined. + Linux users will notice no difference between 1.1.11a-par and 1.1.11-par. +
2004-05-10Version 1.1.11-par has been released. + This version has a rather large number of small changes, which are rather difficult to summarize. + Among the bugs fixed are a race condition in the cache lookup code, a + flaw in the code that caused a busy spin when a remote server answered + with "Not Implemented", and problems with the -4 and -6 command-line + options. Among the improvements are an alternative sorting algorithm + which should allow pdnsd to start up faster when reading a large cache + file from disk, automatic mapping of IPv4 to IPv6 addresses when running + in IPv6 mode, somewhat more efficient memory use, better compression of + the replies and changes in the parallel querying algorithm that should + improve the chances of catching a reply from a remote server.
+ + For a more complete list of the changes I'll have to refer you to README.par and the ChangeLog. +
2004-02-10Version 1.1.10-par has been released. + + The main new feature of this release is a new parser for configuration + files, completely rewritten from scratch in C. The main advantages are: + (f)lex and yacc/bison are no longer needed to build pdnsd, more + informative error messages instead of merely "parse error", + and string literals no longer need to be enclosed in quotes in most + cases.
Furthermore, a bug has been fixed that caused incorrect + IPV6-type PTR records to be generated when sourcing + /etc/hosts like files.
+ + There have been other small changes, more details can be found in the ChangeLog. +
2004-01-08Version 1.1.9-par has been released. + "maintenance" release by Paul Rombouts.
+ + The change of version number is not very significant; the + difference between 1.1.9-par and the previous 1.1.8b1-par8 is marginal. + However, I felt the need to simplify the numbering, because it was + becoming rather baroque.
+ + I've added some missing pieces to the documentation (the pdnsd manual and the man page for pdnsd-ctl). BTW, did you + know that it's possible to define aliases for domain names with pdnsd? I + had plans to implement such a feature when I discovered that pdnsd + already supports it. It was just poorly documented. (If want to try this + for your self, look for the new information about CNAME records under + the rr Section in the manual.)
The + changes to the code consist mostly of optimizations, removal of some + size limits due to fixed-size buffers, and some cleaning up. I've also + tried to make the error responses of pdnsd-ctl more helpful.
+ + More details can be found in the ChangeLog. +
2003-10-10Version 1.1.8b1-par8 has been released. + "maintenance" release by Paul Rombouts.
+ This version introduces a "delegation-only" feature that may be useful + for blocking Verisign's Sitefinder.
+ The parser for the configuration file now tolerates domain names missing + a dot at the end.
+ I have provided alternative implementations for some GNU extensions that I + used in an effort to make the code more portable. In particular, the + code should build on FreeBSD again.
+ More details can be found in the README.par file. +
2003-09-19Version 1.1.8b1-par7 has been released. + "maintenance" release by Paul Rombouts. Besides fixing a number of bugs I have + reworked some of the code for adding and removing entries in the cache in an + effort to improve efficiency and stability.
+ More details can be found in the ChangeLog. +
2003-07-28Version 1.1.8b1-par6 has been released. + "maintenance" release by Paul Rombouts. In addition to some further code cleanup, + the documentation has been revised. +
2003-07-10Version 1.1.8b1-par5 has been released. + A troublesome allocation size error has been discovered in Thomas Moestl's code. + In practice this bug only wastes memory but it could + also potentially lead to memory corruption. Upgrading is recommended. + More details can be found in the ChangeLog. +
2003-06-30Version 1.1.8b1-par4 has been released. + Due to incompatibilities between various implementations of + the pthread library on Linux systems, problems can occur with signal handling in + pdnsd. The usual symptom is failure by pdnsd to save the cache to disk, and + /var/cache/pdnsd/pdnsd.cache remaining empty. If you experience + this kind of trouble, try reconfiguring with different values for the new + --with-thread-lib option. The allowable values are + described in the documentation. +
2003-04-07pdnsd is no longer maintained by Thomas Moestl: + I have not had time to maintain pdnsd for quite a while now, and have been very slow to + respond to issues, or did not respond at all. It is time that I officially announce that + pdnsd is no longer actively maintained; I apologize to all those who reported bugs or + asked questions without receiving any reply. However, Paul A. Rombouts has published + a patch set against the last released version at + http://members.home.nl/p.a.rombouts/pdnsd.html, + which cleans up a lot of code fixes many bugs. +
2002-07-19Documentation update. + Please note that pdnsd should never be installed with setuid or setgid attributes, + as it is not always possible to give up all privileges due to operating system restrictions. + While this was never intended and I don't think that anybody would actually do this, the + documentation was updated to explicitely mention this to avoid misunderstandings. +
2002-01-15Version 1.1.7a has been released. + This fixes a reversed test in an assertion that would cause pdnsd to termintate when the ping uptest + was used. No other changes were made. +
2002-01-15Version 1.1.7 has been released. + This fixes some problems that might be remotely exploitable to gain access as the user pdnsd runs as + (an unprivileged user by default). To do this, an attacker needs to control a name server that is + queried by pdnsd, and send a malicious reply to such a query.
+ Upgrading is strongly recommended!
+ There are also minor bug fixes and stability improvements. +
+ +
+

About pdnsd

+ pdnsd is a proxy DNS server with permanent caching (the cache contents + are written to hard disk on exit) that is designed to cope with unreachable + or down DNS servers (for example in dial-in networking).
+ Since version 1.1.0, pdnsd supports negative caching.
+
+ It is licensed under the GNU General Public License (GPL, + also available in html and + translated into various languages.). + This, in short, means that the sources are distributed togehter with the program, and + that you are free to modify the sources and redistribute them as long as you + also license them under the GPL. You do not need to pay anything for pdnsd. + It also means that there is ABSOLUTELY NO WARRANTY for pdnsd or any part + of it. For details, please read the GPL. +

+ pdnsd can be used with applications that do DNS lookups, e.g. on startup, and + can't be configured to change that behaviour, to prevent the often minute-long + hangs (or even crashes) that result from stalled DNS queries. Some Netscape Navigator + versions for Unix, for example, expose this behaviour. +

+ pdnsd is configurable via a file and supports run-time configuration using the program pdnsd-ctl that comes + with pdnsd. This allows you to set the status flags of servers that pdnsd knows (to influence which servers + pdnsd will query), and the addition, deletion and invalidation of DNS records in pdnsd's cache. +
+ Parallel name server queries are supported. This is a technique that allows + querying several servers at the same time so that very slow or unavailable + servers will not block the answer for one timeout interval. +
+ Since version 1.0.0, pdnsd has full IPv6 support. +

+ There is also a limited support for local zone records, intended for defining + 1.0.0.127.in-addr.arpa. and localhost. , since some clients request that + information and it must be served even if the cached servers are not available + or do not serve these records. pdnsd may also read your /etc/hosts file + (this file is normally used by your local resolver and usually contains + information for localhost as well as for your machines FQDN) and serve its + contents. +

+ pdnsd was started on Linux, and has since been ported to FreeBSD (and Cygwin and Darwin). + 90% of the source code should be easily portable to POSIX- + and BSD-compatible systems, provided that those systems support the POSIX threads (pthreads). + The rest might need OS-specific rewrites. +

+ Currently, pdnsd is only compileable by gcc. This should be easy to fix, but I just + do not have documentation for other compilers. If you are not able or do not want + to use gcc, I would recommend you just try to do the minor changes. +

+

+ pdnsd must be started as root in some cases (raw sockets are needed for icmp + echoes for the option uptest=ping, and the default port is 53, this must be + >1024 to allow non-root execution). However, pdnsd can be configured to change it's user + and group id to those of a non-privileged user after opening the sockets needed for this. +

+ The server should support the full standard DNS queries following the rfcs 1034 + and 1035. As of version 1.0.0, the rfc compliance has been improved again, and pdnsd is now + believed (or hoped?) to be fully rfc-compatible. It completely follows rfc 2181 (except + for one minor issue in the FreeBSD port, see the documentation). + It does not support the + following features, of which most are marked optional, experimental or obsolete + in these rfcs: +

+
    +
  • Inverse queries +
  • Status queries +
  • Completion queries +
  • Namespaces other than IN (Internet) +
  • AXFR and IXFR queries (whole zone transfers); since pdnsd does not maintain zones, that should not violate the standard +
+ The following record types, that are extensions to the original DNS standard, are supported for caching since version 1.2.9 + (if you do not need most of them, you can disable runtime support for the unneeded ones before compiling pdnsd and save a little cache and executable space, see the source file src/rr_types.in): +
    +
  • RP (responsible person, RFC 1183) +
  • AFSDB (AFS database location, RFC 1183) +
  • X25 (X25 address, RFC 1183) +
  • ISDN (ISDN number/address, RFC 1183) +
  • RT (route through, RFC 1183) +
  • NSAP (Network Service Access Protocol address , RFC 1348) +
  • PX (X.400/RFC822 mapping information, RFC 1995) +
  • GPOS (geographic position, deprecated) +
  • AAAA (IPv6 address, RFC 1886) +
  • LOC (location, RFC 1876) +
  • EID (Nimrod EID) +
  • NIMLOC (Nimrod locator) +
  • SRV (service record, RFC 2782) +
  • ATMA (ATM address) +
  • NAPTR (URI mapping, RFC 2168) +
  • KX (key exchange, RFC 2230) +
  • CERT (Certificate record, RFC 4398) +
  • DS (Delegation Signer, RFC 4034) +
  • RRSIG (Resource Record Signature, RFC 4034) +
  • NSEC (Next Secure, RFC 4034) +
  • DNSKEY (record containing the public key for a zone, RFC 4034) +
  • NSEC3 (Next Secure version 3, RFC 5155) +
  • NSEC3PARAM (NSEC3 parameters, RFC 5155) +
+

+ Note: This list is incomplete. For the complete list see the source file src/rr_types.in. +

+ There are FreeBSD and OpenBSD ports available for pdnsd (ports/net/pdnsd for both). + Thanks go to Roman Shterenzon for the FreeBSD port Sebastian Stark for the OpenBSD one! + Thanks to Kiyo Kelvin Lee now also runs on the Cygwin platform! + Thanks goes to Rodney Brown for extending portability to the Darwin (Apple Mac OS X) platform! +

+ If you have questions left, you should take a look into the FAQ. +
+ Bugfixes, patches and compatability fixes for other OSs are very welcome! +

+

Features in detail

+

+ This section describes some of pdnsds features in detail. Most of the options are set + in the config file. For more information on the configuration file, see + the documenation page. +


+ +

Uptests

+ pdnsd provides several methods to test whether a remote DNS server should be regarded as available + (so that pdnsd can query it), in + addition to the obvious "none" test (the server is always regarded as available, + or availability is set on or off using the pdnsd-ctl utility). + These tests are: +
    +
  • ping: a given adress is ping'ed in a given interval. If it there is no response + or the host is unreachable, the server is seen to be not available (for those who don't know: + pinging is sending a certain Internet packet type to a host to which any standard-conformant + host is required to reply). +
  • if: a given network interface is tested whether it is existent, up and running. If + it is not, the server is regarded to be not available. This is especially useful for ppp and + similar interfaces. A special case test for Linux isdn (ippp*) interfaces is integrated, so that the uptests + should also work for these. +
  • dev: this is a variant of the if uptest for use with Linux dial-on-demand ppp interfaces. In addition + to performing an if-style interface uptest, it also tests whether a specified program (e.g. pppd) owns + a lock to a given (modem-) device. +
  • exec: a given shell command line is executed and the exit status of the whole command line (which + is normally the exit status of the last command) is evaluated. If it is not zero, the server is regarded + to be not available. This is a very flexible testing method with which it should be able to perform + virtually any needed test. +
  • query: New in version 1.2: + This works like the ping test, except it sends an (empty) DNS query to the remote server. + If the server sends a well-formed response back within the timeout period (except SERVFAIL), + it will be regarded as available. + This test is useful if a remote server does not respond to ICMP_ECHO requests at all, + which unfortunately is quite common these days. + In many cases this test will be a more reliable indicator of availability + than the ones mentioned above. +
+
+

Local Records ("Zones")

+ As mentioned above, there are only very basic local record types (ie the record types that you may use in record + declarations in your local configuration for records that pdnsd shall serve in addion to the cached ones). + They are organized roughly in zones but have not complete zone declarations, so I generally do not use the + term "zone" for them, but rather "local records". + These are the local record types pdnsd can understand: +
    +
  • SOA (information about the name server) +
  • A (domain-name-to-address mapping) +
  • PTR (pointer, used normally for address-to-domain-name mapping) +
  • NS (name server, generated automatically by pdnsd for any local record set) +
  • CNAME (canonical host name) +
  • MX (mail exchange for the domain) +
  • TXT (arbitrary text strings, often used for Sender Policy Framework) +
+ You can specify these records in the configuration file.
+ You may "source" a file in a format like that used in the /etc/hosts file, that means + that pdnsd reads this file, extracts addresses and domain names from it and automatically generates + A records for name to address mapping, PTR records for address to name mapping and NS records (name + server specifiation) for each entry in the file.
+ Records can also be changed dynamically at run time.
+ A script contributed by Marko Stolle makes pdnsd usable in a DHCP setup using this feature. +
+

System requirements

+ As mentioned, pdnsd currently runs under Linux, FreeBSD and Cygwin. + Other BSD flavours may or may not work (feedback is very welcome!). + The system and software requirements under Linux are: +
    +
  • Kernel version >2.2.0 +
  • glibc version >2.0.1 (aka libc6) with LinuxThreads (normally included) + or NPTL (Native Posix Thread Library, recommended).
    + Due to a bug, pdnsd 0.9.8 does not run with glibc2.1.1. This behaviour was + fixed in pdnsd 0.9.9. +
  • For IPv6: glibc>=2.1 +
+ The system requirements under FreeBSD are: +
    +
  • FreeBSD versions >=2.6 (prior ones may or may not work) +
  • For IPv6: FreeBSD >=4.0 is recommended (no idea if it runs on prior versions) +
+ + The common software requirements for all supported systems are: +
    +
  • GCC, preferably egcs-2.* or 3.* (other compilers are currently not supported; the needed patch for another compiler + should not be difficult, however) +
  • GNU or BSD make +
  • the standard commands install, grep, sed, awk, touch and which (along with the REALLY + standard ones mv, cp, ln, rm, pwd, test, echo, cat, mkdir, chown, chmod, tar). In + any standard Unix installation, this should be no problem. +
  • for hacking and building own packages, you might also need gzip, bzip2, perl and rpmbuild +
+
+

Download

+ If you want to download pdnsd, please visit the download page. +
+

Authors

+

+ pdnsd was originally written by Thomas Moestl, + but is no longer maintained by him. Paul A. Rombouts + has revised large portions of the code and has added a number of new features. + See README.par and the ChangeLog + in the source directory (or /usr/share/doc/pdnsd-<version> + if you have installed a binary package) for more details. + If you have questions about the recent modifications, you can find + the email address of the current maintainer + at the end of README.par. +

+

+ Daniel Smolik has contributed RedHat RPMs (the most recent RPMs are available here).
+ Torben Janssen contributed start scripts for Red Hat Linux.
+ Soenke J. Peters contributed patches and suggestions for Red Hat compatability.
+ Wolfgang Ocker has contributed the code and documentation for the server_ip option.
+ Markus Mohr contributed a Debian rc script.
+ Nikita V. Youschenko contributed extensions to the "if" uptest.
+ Lyonel Vincent extended the serve_aliases option to support an arbitrary number of aliases.
+ Sourav K. Mandal wrote the autoconf scripts and contributed many fixes and suggestions.
+ Stephan Boettcher contributed the SCHEME= option.
+ Ron Yorston contributed the uptest for Linux ppp dial-on-demand devices.
+ Alexandre Nunes fixed some bugs in the autoconf files.
+ Sverker Wiberg contributed fixes for IPv6.
+ Carsten Block contributed configure-able rc scripts.
+ Olaf Kirch contributed a security fix for the run_as code.
+ Paul Wagland contributed various patches for bind9-compatability and other issues.
+ Roman Shterenzon contributed patches and lots of helpful hints for FreeBSD compatability.
+ Bernd Leibing has contributed spec file fixes.
+ Michael Wiedmann has contributed the pdnsd-ctl.8 man page.
+ Marko Stolle has contributed the contrib/pdnsd_update.pl script that makes pdnsd usable in a DHCP setup.
+ P.J. Bostley has contributed patches to get pdnsd working on alpha properly.
+ Christian Engstler contributed patches for SuSE compatability.
+ Bjoern Fischer contributed code to make pdnsd leave the case of names in the cache unchanged.
+ Marko Stolle contributed the contrib/pdnsd_update.pl script that makes pdnsd usable in a DHCP setup.
+ Andrew M. Bishop contributed the support for the label server option and the pdnsd-ctl interface for using it.
+ Frank Elsner contributed rc script fixes.
+ Andreas Steinmetz contributed the code for query_port_start and query_port_end options.
+ Mahesh T. Pai contributed the pdnsd.8 man page.
+ Nikola Kotur contributed the Slackware start-up script.
+ Kiyo Kelvin Lee contributed a patch for Cygwin support.
+ Rodney Brown contributed a patch for Darwin (Apple Mac OS X) support.
+ Jan-Marek Glogowski contributed a patch implementing the use_nss option. +

+

+ Special thanks to Bert Frederiks for letting me do a late-night debugging run on his machine to + spot obscure bugs! +

+

+ Thanks to the following persons for reporting bugs and being helpful:
+ David G. Andersen,
+ Dirk Armbrust,
+ Daniel Black,
+ Kevin A. Burton,
+ Juliusz Chroboczek,
+ Joachim Dorner,
+ Stefan Erhardt,
+ Stefan Frster,
+ Mike Hammer,
+ Jonathan Hudson,
+ Dan Jacobson,
+ Byrial Jensen,
+ Patrick Loschmidt,
+ James MacLean,
+ Fraser McCrossan,
+ Michael Mller,
+ Erich Reitz,
+ Brian Schroeder,
+ Milan P. Stanic,
+ Michael Steiner,
+ Norbert Steinl,
+ Markus Storm,
+ Michael Strder,
+ Alan Swanson,
+ Eelco Vriezekolk. +

+ +

Links

+ Well, this is the obligatory link section. +
+
+ + + + + + + + +
+ + http://www.gnu.org
+ The GNU homepage +
+
+ + http://freecode.com
+ Freecode (formerly Freshmeat) - large free software index +
+
+ + http://www.freebsd.org
+ The FreeBSD project +
+
+ +
+
+
Thomas Moestl + and Paul A. Rombouts +
+
+

+ Last revised: 17 March 2012 by Paul A. Rombouts +

+ + diff --git a/service/src/main/jni/pdnsd/doc/html2confman.pl b/service/src/main/jni/pdnsd/doc/html2confman.pl new file mode 100644 index 0000000..abade11 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/html2confman.pl @@ -0,0 +1,161 @@ +#!/usr/bin/perl -w +# +# A Perl script for converting pdnsd html documentation to a man page. +# +# Written by Paul A. Rombouts +# +# This file Copyright 2004 Paul A. Rombouts +# It may be distributed under the GNU Public License, version 2, or +# any higher version. See section COPYING of the GNU Public license +# for conditions under which this file may be redistributed. +# + +use strict; +use POSIX qw(strftime); + + +while(<>) { + if(/[^<]*configuration file/i) { + last; + } +} + +exit unless defined($_); + +while(<>) { + if(/[^<]*layout/i) { + last; + } +} + +exit unless defined($_); + +(my $myname=$0) =~ s{.*/}{}; + +print <) { + if(/.*\bpdnsd-ctl\b/) { + last; + } + s{^\s*((?:<[^<>]+>)*?)[\d.]*\s*(.*)((?:<[^<>]+>)*?)(?:
)?\s*$}{.SS $1$2$3\n}i; + if(s{^\s*\s*}{.TP\n}i) {$taggedparagraph=1} + if(m{^\s*}i) {$taggedparagraph=0} + s{^\s*((?:<[^<>]+>)*?)(.*)((?:<[^<>]+>)*?)(?:
)?\s*$}{.B $1$2$3\n}i if $taggedparagraph; + s{^\s*((?:<[^<>]+>)*?or(?:<[^<>]+>)*?)(?:
)?\s*$}{$1\n.PD 0\n.TP\n.PD\n}i if $taggedparagraph; + if(s{^\s*
}{.DS L\n}i) {$displayed=1}
+    s{^\t}{        } if $displayed;
+    if(s{
\s*$}{\n.DE\n\n}i) {$displayed=0} + elsif(!$displayed) {s{^\s*}{}} + s{^\s*
  • }{.IP\n\\(bu }i; + s{
  • }{\n.IP\n\\(bu }i; + s{
      }{\n}i; + s{
    }{\n}i; + s{}{\\fB}ig; + s{}{\\fP}ig; + s{<(i|em)>}{\\fI}ig; + s{}{\\fP}ig; + unless(s{^\s*(<[^<>]+>)*(
    |

    )(<[^<>]+>)*\s*$}{\n}i) { + s{]*>(.*)

    }{\n$1\n}i; + s{^\s*
    }{.br\n}i; + s{
    \s*
    \s*$}{\n\n}i; + s{
    \s*$}{\n.br\n}i; + s{
    }{\n.br\n}i; + s{^\s*(<[^<>]+>)*\s*$}{}; + } + s{<[^<>]+>}{}g; + s{<}{<}ig; + s{>}{>}ig; + s{"}{"}ig; + s{ }{\\ }ig; + s{/var/cache/pdnsd\b}{\@cachedir\@}g; + s{(? +.UE +and was extensively revised by Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\\-par and later). +.PP +Several others have contributed to \\fBpdnsd\\fP; see files in the source or +\\fB/usr/share/doc/pdnsd/\\fP directory. +.PP +This man page was automatically generated from the html documentation for \\fBpdnsd\\fP, +using a customized Perl script written by Paul A. Rombouts. +ENDOFTRAILER + +if(defined($_)) { + while(<>) { + if(/last\s+revised/i) { + s{^\s*}{}; + s{<[^<>]+>}{}g; + s{<}{<}ig; + s{>}{>}ig; + s{"}{"}ig; + s{ }{\\ }ig; + print ".PP\n"; + print; + last; + } + } +} +exit; diff --git a/service/src/main/jni/pdnsd/doc/pdnsd-ctl.8 b/service/src/main/jni/pdnsd/doc/pdnsd-ctl.8 new file mode 100644 index 0000000..73459f3 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/pdnsd-ctl.8 @@ -0,0 +1,198 @@ +.\" This manpage has been automatically generated by docbook2man-spec +.\" from a DocBook document. docbook2man-spec can be found at: +.\" +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng . +.\" This manpage has been edited manually by Paul A. Rombouts. +.TH "PDNSD\-CTL" "8" "Sep 2008" "pdnsd 1.2.9b-par" "" +.SH NAME +\fBpdnsd\-ctl\fP \- controls pdnsd +.SH SYNOPSIS +.sp +\fBpdnsd\-ctl\fP [\fB\-c\fP \fIcachedir\fP] [\fB\-q\fP] \fIcommand\fP [\fIarguments\fP] +.SH "DESCRIPTION" +.PP +\fBpdnsd\-ctl\fP controls \fBpdnsd\fP, a proxy dns server with permanent caching. +Note that the status control socket must be enabled (by specifying an option on +the pdnsd command line or in the configuration file) before you can use +\fBpdnsd\-ctl\fP. +.PP +.TP +\fB\-c\fP \fIcachedir\fP +Set the cache directory to \fIcachedir\fP (must match pdnsd setting). +This is only necessary if the directory differs from the default specified +at compile time. +.TP +\fB\-q\fP +Be quiet unless output is specified by the command or something goes wrong. +.SH "COMMANDS" +.TP +\fBhelp\fP\ \ \ [no arguments] + +Print a command summary. +.TP +\fBversion\fP\ [no arguments] + +Print version and license info. +.TP +\fBstatus\fP\ [no arguments] + +Print a description of pdnsd's cache status, thread status and configuration. +Also shows which remote name servers are assumed to be available. +.TP +\fBserver\fP\ (\fIindex\fP|\fIlabel\fP) (\fBup\fP|\fBdown\fP|\fBretest\fP) [\fIdns1\fP[,\fIdns2\fP[,...]]] + +Set the status of the servers with the given index or label to up or down, or +force a retest. The index is assigned in the order of definition in pdnsd.conf +starting with 0. Use the status command to view the indexes. You can specify all +instead of an index to perform the action for all servers registered with pdnsd. +.IP +An optional third argument can be given consisting of a list of IP addresses +separated by commas or white-space characters. This list will replace the +addresses of name servers used by pdnsd for the given server section. This +feature is useful for run-time configuration of pdnsd with dynamic DNS data in +scripts called by ppp or DHCP clients. The last argument may also be an empty +string, which causes existing IP addresses to be removed and the corresponding +server section to become inactive. +.TP +\fBrecord\fP\ \fIname\fP (\fBdelete\fP|\fBinvalidate\fP) + +Delete or invalidate the records of the given domain name if it is in the cache. +Invalidation means that the records are marked as timed out, and will be +reloaded if possible. For local records (i.e., records that were given in the +config file using a rr section, records read from a hosts-style file and records +added using pdnsd-ctl), invalidation has no effect. Deletion will work, though. +.TP +\fBsource\fP\ \fIfn\fP \fIowner\fP [\fIttl\fP] [(\fBon\fP|\fBoff\fP)] [\fBnoauth\fP] + +Load a hosts-style file. Works like using the pdnsd source configuration section. +Owner and ttl are used as in the source section. ttl has a default +of 900 (it does not need to be specified). The next to last argument corresponds +to the serve_aliases option, and is off by default. +\fBnoauth\fP is used to make the domains non-authoritative +(this is similar to setting authrec=off in the config file, +please consult the +.BR pdnsd.conf (5) +man page for what that means). +fn is the name of the file, which must be readable by pdnsd. +.TP +\fBadd\fP\ \ \ \ \fBa\fP \fIaddr\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBaaaa\fP \fIaddr\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBptr\fP \fIhost\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBcname\fP \fIhost\fP \fIname\fP [\fIttl\fP] [\fBnoauth\fP] +.TP +\fBadd\fP\ \ \ \ \fBmx\fP \fIhost\fP \fIname\fP \fIpref\fP [\fIttl\fP] [\fBnoauth\fP] + +Add a record of the given type to the pdnsd cache, replacing existing +records for the same name and type. The 2nd argument corresponds +to the value of the option in the rr section that is named like +the first argument. The addr argument may be a list of IP addresses, +separated by commas or white space. +The ttl is optional, the default is 900 seconds. +\fBnoauth\fP is used to make the domains non-authoritative +(this is similar to setting authrec=off in the config file, +please consult the +.BR pdnsd.conf (5) +man page for what that means). +If you want no other record than the newly added in the cache, do +\fBpdnsd\-ctl\fP\ \fBrecord\fP\ \fIname\fP\ \fBdelete\fP +before adding records. +.TP +\fBneg\fP\ \ \ \ \fIname\fP [\fItype\fP] [\fIttl\fP] + +Add a negatively cached record to pdnsd's cache, replacing existing +records for the same name and type. If no type is given, the whole +domain is cached negatively. For negatively cached records, errors are +immediately returned on a query, without querying other servers first. +The ttl is optional, the default is 900 seconds. +.TP +\fBconfig\fP\ \fIfilename\fP + +Reload pdnsd's configuration file. +.br +The config file must be owned by the uid that pdnsd had when it was started, +and be readable by pdnsd's run_as uid. +If no file name is specified, the config file used at start-up is reloaded. +Note that some configuration changes, like the port or IP address pdnsd listens on, +cannot be made this way and you will receive an error message. +In these cases, you will have to restart pdnsd instead. +.TP +\fBinclude\fP\ \fIfilename\fP + +Parse an include file. +.br +The include file may contain the same +type of sections as a config file, expect for global and server +sections, which are not allowed. This command can be used to add data +to the cache without reconfiguring pdnsd. +.TP +\fBeval\fP\ \ \ \fIstring\fP + +Parse a string as if part of an include file. +.br +The string should hold one or more complete configuration sections, +but no global and server sections, which are not allowed. +If multiple strings are given, they will be joined using newline chars +and parsed together. +.TP +\fBempty\-cache\fP\ [[+|-]\fIname\fP ...] + +Delete all entries in the cache matching include/exclude rules. +.br +If no arguments are provided, the cache is completely emptied, +freeing all existing entries. +Note that this also removes local records, as defined by the config file. +To restore local records, run "pdnsd-ctl\ config" immediately afterwards. +.br +If one or more arguments are provided, these are interpreted as +include/exclude names. If an argument starts with a '+' the name is to +be included. If an argument starts with a '-' it is to be excluded. +If an argument does not begin with '+' or '-', a '+' is assumed. +If the domain name of a cache entry ends in one of the names in the +list, the first match will determine what happens. If the matching name +is to be included, the cache entry is deleted, otherwise it remains. +If there are no matches, the default action is not to delete. +.TP +\fBdump\fP\ \ \ [\fIname\fP] + +Print information stored in the cache about \fIname\fP. +If \fIname\fP begins with a dot and is not the root domain, information +about the names in the cache ending in \fIname\fP (including \fIname\fP without +the leading dot) will be printed. +If \fIname\fP is not specified, information about all the names in the cache +will be printed. +.TP +\fBlist\-rrtypes\fP [no arguments] + +List available rr types for the neg command. Note that those are only +used for the neg command, not for add! +.SH "BUGS" +.PP +If you pipe the output of \fBdump\fP command through an application that +reads only part of the output and then blocks (such as more or less), +pdnsd threads trying to add new entries to the cache will be suspended +until the pipe is closed. +It is preferable to capture the output in a file in such a case. +.br +Report any remaining bugs to the authors. +.SH "AUTHORS" +.PP +Thomas Moestl +.UR + +.UE +.br +Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\-par and later) +.PP +Last revised: 04 Sep 2008 by Paul A. Rombouts. +.SH "SEE ALSO" +.PP +.BR pdnsd (8), +.BR pdnsd.conf (5) diff --git a/service/src/main/jni/pdnsd/doc/pdnsd.8.in b/service/src/main/jni/pdnsd/doc/pdnsd.8.in new file mode 100644 index 0000000..ba4330a --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/pdnsd.8.in @@ -0,0 +1,326 @@ +.TH PDNSD 8 "Jul 2007" "pdnsd @fullversion@" "System Administration Commands" + +.SH NAME +\fBpdnsd\fP \- dns proxy daemon + +.SH SYNOPSIS + +\fBpdnsd\fP [\-h] [\-V] [\-s] [\-d] [\-g] [\-t] [\-p \fIfile\fR] [\-v\fIn\fR] [\-m\fIxx\fR] [\-c \fIfile\fR] [\-4] [\-6] [\-a] +.PP +This man page is an extract of the documentation of \fBpdnsd\fP. +For complete, current documentation, refer to the HTML (or plain text) +documentation (which you can find in the \fBdoc/\fP subdirectory of the +source or in a standard documentation directory, typically +\fB/usr/share/doc/pdnsd/\fP if you are using a binary package). + +.SH DESCRIPTION +.PP +\fBpdnsd\fP is a IPv6 capable proxy domain name server (DNS) which +saves the contents of its DNS cache to the disk on exit. + +.SH OPTIONS + +.RS +.TP +.B \-4 +enables IPv4 support. IPv6 support is automatically +disabled (should it be available). On by default. +.TP +.B \-6 +enables IPv6 support. IPv4 support is automatically +disabled (should it be available). Off by default. +.TP +.B \-a +With this option, pdnsd will try to detect automatically if +the system supports IPv6, and fall back to IPv4 otherwise. +.TP +.BR \-V " or " \-\-version +Print version information and exit. +.TP +\fB\-c\fP \fIFILE\fP or \fB\-\-config\-file=\fP\fIFILE\fP +specifies that configuration is to be read from \fIFILE\fP. +Default is \fB@sysconfdir@/pdnsd.conf\fP. +.TP +.BR \-d " or " \-\-daemon +Start \fBpdnsd\fP in daemon mode (as a background process). +.TP +.BR \-g " or " \-\-debug +Print some debug messages on the console or to the file +\fBpdnsd.debug\fP in your cache directory (in daemon mode). +.TP +.BR \-h " or " \-\-help +Print an option summary and exit. +.TP +\fB\-i\fP \fIPREFIX\fP or \fB\-\-ipv4_6_prefix=\fP\fIPREFIX\fP +specifies the prefix pdnsd uses (when running in IPv6 mode) to map IPv4 +addresses in the configuration file to IPv6 addresses. Must be a valid IPv6 +address. Default is ::ffff:0.0.0.0 +.TP +.B \-p \fIFILE\fP +writes the pid the server runs as to the specified filename. Works +only in daemon mode. +.TP +.B \-\-pdnsd\-user +Print the user \fBpdnsd\fP will run as and exit. +.TP +.BR \-s " or " \-\-status +enables the status control socket. Either this option should be passed +to the command line or \fBstatus_ctl=on;\fP should be specified in the +config file if you want to use +.BR pdnsd\-ctl (8) +to control \fBpdnsd\fP at runtime. +.TP +.BR \-t " or " \-\-tcp +enables the TCP server thread. \fBpdnsd\fP will then serve TCP and UDP +queries. +.TP +.BI \-v n +sets the verbosity of \fBpdnsd\fP. \fIn\fP is a numeric argument +between 0 (normal operation) to 3 (many messages for debugging). +.TP +.BI \-m xx +sets the query method \fBpdnsd\fP +uses. Possible values for \fIxx\fP are: +.IP +.B uo +\- pdnsd will use UDP only. This is the fastest method, and should +be supported by all name servers on the Internet. + +.IP +.B to +\- pdnsd will use TCP only. TCP queries usually take more time than +UDP queries, but are more secure against certain attacks, where an +attacker tries to guess your query id and to send forged answers. TCP +queries are not supported by some name servers. + +.IP +.B tu +\- pdnsd will try to use TCP, and will fall back to UDP if its +connection is refused or times out. + +.IP +.B ut +\- pdnsd will try to use UDP, and will repeat the query using TCP +if the UDP reply was truncated (i.e. the tc bit is set). +This is the behaviour recommended by the DNS standards. + +.PP +Additionally, "no" can be prepended to the \-\-status, \-\-daemon, \-\-debug +and \-\-tcp options (e.g. \-\-notcp) to reverse their effect. +.RE + +.SH USAGE +.PP +\fBpdnsd\fP is usually run from a startup script. For \fBpdnsd\fP to +work, You need to:- + +.IP +1. Tell your system to use \fBpdnsd\fP as the primary DNS server by +modifying \fB/etc/resolv.conf\fP. + +.IP +2. Tell \fBpdnsd\fP to use an authentic source for DNS records, by +including the IP addresses of one or more DNS servers, usually your +ISP's DNS servers, in \fB@sysconfdir@/pdnsd.conf\fP. +.PP +For this, put the following line in your \fB/etc/resolv.conf\fP +.PP +.RS +nameserver 127.0.0.X +.RE +.PP +where X can be any number. (I use 3). Comment out all other +entries. You should put the same value in the server_ip= line in +\fBglobal\fP section of \fB@sysconfdir@/pdnsd.conf\fP. +.br +If you want to use \fBpdnsd\fP as the DNS server for a small local network, +you should use the IP address or name of the interface connected to +this network instead of 127.0.0.X. +.RE + +.PP +To tell \fBpdnsd\fP where to get DNS information from, add the +following lines in \fB@sysconfdir@/pdnsd.conf\fP:- + +.PP +.RS +server { +.br + label= "myisp"; + ip=123.456.789.001,123.456.789.002; + proxy_only=on; + timeout=10; +.br +} +.RE +.PP +Note the opening and closing braces. Add more such \fBserver\fP +sections for each set of DNS servers you want \fBpdnsd\fP to query. +Of course the configuration options shown here are just examples. +More examples can be found in \fB@sysconfdir@/pdnsd.conf.sample\fP +or the pdnsd.conf in the documentation directory. +See the +.BR pdnsd.conf (5) +man page for all the possible options and their exact meaning. +.PP +If you use a dial up connection, remember that ppp scripts usually +replace \fB/etc/resolv.conf\fP when connection with the ISP is +established. You need to configure ppp (or whatever you use to +establish a connection) so that \fB/etc/resolv.conf\fP is not replaced +every time a connection is established. Read the documentation for the +scripts run when your network comes up. +.PP +If you use pppconfig, specify `none' in the `nameservers' option in +the `advanced' tab. If you use multiple ISPs, you should do this for +each connection/account. +.PP +If you use multiple ISPs, you should tell \fBpdnsd\fP which DNS servers +have become available by calling \fBpdnsd\-ctl\fP, the \fBpdnsd\fP +control utility, in a script (e.g. \fB/etc/ppp/ip\-up\fP when you use pppd) +that is run when the connection is established. +If the addresses of the DNS servers are obtained through some type of +dynamic configuration protocol (e.g. pppd with the usepeerdns +option or a DHCP client), you can pass the DNS server addresses as an extra +argument to \fBpdnsd\-ctl\fP to configure \fBpdnsd\fP at run time. +See the +.BR pdnsd\-ctl (8) +man page for details. + +.SH FILES + +\fB@sysconfdir@/pdnsd.conf\fP is the pdnsd configuration file. +The file format and configuration options are described in the +.BR pdnsd.conf (5) +man page. You can find examples of almost all options in +\fB@sysconfdir@/pdnsd.conf.sample\fP. +.PP +\fB@cachedir@/pdnsd.cache\fP +.PP +\fB@cachedir@/pdnsd.status\fP is the status control socket, which must be +enabled before you can use \fBpdnsd\-ctl\fP. +.PP +\fB/etc/init.d/pdnsd\fP (the name and location of the start-up script +may be different depending on your distribution.) +.PP +\fB/etc/resolv.conf\fP +.PP +\fB/etc/defaults/pdnsd\fP contains additional parameters or options +which may be passed to pdnsd at boot time. This saves the hassle of +fiddling with initscripts (not available on all distributions). + +.SH BUGS +.PP +The verbosity option +.BI -v n +presently does not seem to have much effect on the amount of debug output. +.br +Report any remaining bugs to the authors. + +.SH CONFORMING TO +.PP +\fBpdnsd\fP should comply with RFCs 1034 and 1035. As of version +1.0.0, RFC compliance has been improved and pdnsd is now believed (or +hoped?) to be fully RFC compatible. It completely follows RFC 2181 +(except for one minor issue in the FreeBSD port, see the +documentation). +.PP +It does \fINOT\fP support the following features, of which most are +marked optional, experimental or obsolete in these RFCs: + + +.IP +\(bu Inverse queries +.IP +\(bu Status queries +.IP +\(bu Completion queries +.IP +\(bu Namespaces other than IN (Internet) +.IP +\(bu AXFR and IXFR queries (whole zone transfers); since pdnsd does not maintain zones, that should not violate the standard + +.PP +The following record types, that are extensions to the original DNS +standard, are supported if given as options at compile time. (if you +do not need them, you do not need to compile support for them into +pdnsd and save cache and executable space): + +.IP +\(bu RP (responsible person, RFC 1183) +.IP +\(bu AFSDB (AFS database location, RFC 1183) +.IP +\(bu X25 (X25 address, RFC 1183) +.IP +\(bu ISDN (ISDN number/address, RFC 1183) +.IP +\(bu RT (route through, RFC 1183) +.IP +\(bu NSAP (Network Service Access Protocol address , RFC 1348) +.IP +\(bu PX (X.400/RFC822 mapping information, RFC 1995) +.IP +\(bu GPOS (geographic position, deprecated) +.IP +\(bu AAAA (IPv6 address, RFC 1886) +.IP +\(bu LOC (location, RFC 1876) +.IP +\(bu EID (Nimrod EID) +.IP +\(bu NIMLOC (Nimrod locator) +.IP +\(bu SRV (service record, RFC 2782) +.IP +\(bu ATMA (ATM address) +.IP +\(bu NAPTR (URI mapping, RFC 2168) +.IP +\(bu KX (key exchange, RFC 2230) + +.SH SEE ALSO +.PP +.BR pdnsd\-ctl (8), +.BR pdnsd.conf (5), +.BR pppconfig (8), +.BR resolv.conf (5) +.PP +More documentation is available in the \fBdoc/\fP subdirectory of the source, +or in \fB/usr/share/doc/pdnsd/\fP if you are using a binary package. + +.SH AUTHORS + +\fBpdnsd\fP was originally written by Thomas Moestl, +.UR +, +.UE +and was extensively revised by Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\-par and later). +.PP +Several others have contributed to \fBpdnsd\fP; see files in the +source or \fB/usr/share/doc/pdnsd/\fP directory. +.PP +This man page was written by Mahesh T. Pai +.UR + +.UE +using the documents in \fB/usr/share/docs/pdnsd/\fP directory for Debian, +but can be used on other distributions too. +.PP +Last revised: 22 Jul 2007 by Paul A. Rombouts. + +.SH COPYRIGHT + +.PP +This man page is a part of the pdnsd package, and may be distributed +in original or modified form under terms of the GNU General Public +License, as published by the Free Software Foundation; either version +3, or (at your option) any later version. + +.PP +You can find a copy of the GNU GPL in the file \fBCOPYING\fP in the source +or the \fB/usr/share/common\-licenses/\fP directory if you are using a +Debian system. diff --git a/service/src/main/jni/pdnsd/doc/pdnsd.conf.5.in b/service/src/main/jni/pdnsd/doc/pdnsd.conf.5.in new file mode 100644 index 0000000..801b535 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/pdnsd.conf.5.in @@ -0,0 +1,1328 @@ +.\" Generated automatically from the html documentation by html2confman.pl +.\" +.\" Manpage for pdnsd.conf (pdnsd configuration file) +.\" +.\" Copyright (C) 2000, 2001 Thomas Moestl +.\" Copyright (C) 2003, 2004, 2005, 2006, 2007 Paul A. Rombouts +.\" +.\" This manual is a part of the pdnsd package, and may be distributed in +.\" original or modified form under terms of the GNU General Public +.\" License, as published by the Free Software Foundation; either version +.\" 3, or (at your option) any later version. +.\" You can find a copy of the GNU GPL in the file COPYING in the source +.\" or documentation directory. +.\" +.TH PDNSD.CONF 5 "Apr 2012" "pdnsd @fullversion@" +.SH NAME +pdnsd.conf \- The configuration file for pdnsd +.hw config +.SH DESCRIPTION +.PP +This manual page describes the layout of the +.BR pdnsd (8) +configuration file and the available configuration options. +The default location of the file is @sysconfdir@/pdnsd.conf. This may be changed +with the \fB-c\fP command line option. +An example pdnsd.conf comes with the pdnsd distribution in the documentation directory +or in @sysconfdir@/pdnsd.conf.sample. +.SH "FILE FORMAT" +.PP +The configuration file is divided into sections. Each section is prefixed with +the section name and opening curlies ({) and closed with closing curlies (}). +In each section, configuration options can be given in the form + +\fIoption_name\fP=\fIoption_value\fP; + +Option value may be a string literal, a number, a time specification or a constant. +In previous versions of pdnsd strings had to be enclosed +in quotes ("), but since version 1.1.10 this is no longer necessary, unless +a string contains a special character such as whitespace, a token that normally starts +a comment, or one of ",;{}\". +Since version 1.2.9 a backslash (\) inside a string is interpreted as an escape character, +so it is possible to include special characters in strings (both quoted or unquoted) +by preceding them with a backslash. Some escape sequences are in interpreted as in the C +programming language, e.g. \t becomes a tab, +\n becomes a new-line control char. +.br +A time specification consists a sequence of digits followed by a one-letter suffix. +The following suffixes are recognized: +s (seconds), m (minutes), h (hours), +d (days) and w (weeks). +If the suffix is missing, seconds are assumed. +If several time specifications are concatenated, their values are added together; +e.g. 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds. +.br +Some options take more than one value; in this case, the values are separated with commas. +.br +If you may supply one of a set of possible values to an option, this is noted +in the documentation as +(option1|option2|option3|...) +.br +The constants true|false and yes|no +are accepted as synonyms for the constants on|off. +.br +Comments may be enclosed in /* and */, nested comments are possible. If the +# sign or two slashes (//) appear in the configuration file, everything from +these signs to the end of the current line is regarded as a comment and ignored. +.br +There are examples for nearly all options in the sample config file. + +.SS global Section +The global section specifies parameters that affect the overall behaviour of the +server. If you specify multiple global sections, the settings of those later in +the file will overwrite the earlier given values. +.br +These are the possible options: + +.TP +.B perm_cache=(\fInumber\fP|off); +Switch the disk cache off or supply a maximum cache size in kB. If the disk +cache is switched off, 8 bytes will still be written to disk. +The memory cache is always 10kB larger than the file cache. +This value is 2048 (2 MB) by default. +.TP +.B cache_dir=\fIstring\fP; +Set the directory you want to keep the cache in. +The default is "@cachedir@" +(unless pdnsd was compiled with a different default). +.TP +.B server_port=\fInumber\fP; +Set the server port. This is especially useful when you want to start the +server and are not root. Note that you may also not specify uptest=ping in +the server section as non-root. +.br +The default port is 53, the RFC-standard one. Note that you should only use +non-standard ports when you only need clients on your machine to communicate +with the server; others will probably fail if the try to contact the server +on the basis of an NS record, since the A record that supplies the address for +(among others) name servers does not have a port number specification. +.TP +.B server_ip=\fIstring\fP; +or +.PD 0 +.TP +.PD +.B interface=\fIstring\fP; +Set the IP address pdnsd listens on for requests. This can be useful +when the host has several interfaces and you want pdnsd not to listen on +all interfaces. For example, it is possible to bind pdnsd to listen on +127.0.0.2 to allow pdnsd to be a forwarder for BIND. +The default setting for this option is server_ip=any, which means that +pdnsd will listen on all of your local interfaces. +Presently you can only specify one address here; if you want pdnsd to listen on multiple +interfaces but not all you will have to specify server_ip=any +and use firewall rules to restrict access. +.br +The IP address used to need quotation marks around it, but since version 1.1.10 +this is no longer necessary. +.br +If pdnsd has been compiled with both IPv4 and IPv6 support, and you want to +specify an IPv6 address here, then unless pdnsd was compiled to start up in IPv6 mode +by default, you will need to use the \-6 command-line option or +set run_ipv4=off first (see below) in order to ensure that the +IPv6 address is parsed correctly. +.br +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, +it will automatically be mapped to an IPv6 address. +.br +\fINew in version 1.2:\fP You may also give the name of an interface +such as "lo" or "eth0" here, instead of an IP address +(this has been tested on Linux, and may or may not work on other platforms). +pdnsd will not bind to the interface name, but will look up the address of the +interface at start-up and listen on that address. If the address of the interface +changes while pdnsd is running, pdnsd will not notice that. You will need to +restart pdnsd in that case. +.TP +.B outgoing_ip=\fIstring\fP; +or +.PD 0 +.TP +.PD +.B outside_interface=\fIstring\fP; +\fINew in version 1.2.9:\fP +Set the IP address of the interface used by pdnsd for outgoing queries. +This can be useful when the host has several interfaces and you want pdnsd +to send outgoing queries via only one of them. +For example, if pdnsd is running on a host with one interface with IP address +192.168.1.1 connected to the local network, and another with IP address 123.xxx.yyy.zzz +connected to the internet, you may specify server_ip=192.168.1.1 +and outgoing_ip=123.xxx.yyy.zzz to enforce that pdnsd only responds +to queries received from the local network, and only sends outgoing queries via +the interface connected to the internet. +.br +The default setting for this option is any, which means that +the kernel is free to decide which interface to use. +Like with the server_ip option, you may also give the name of an +interface here, instead of an IP address. +.TP +.B linkdown_kluge=(on|off); +This option enables a kluge that some people might need: when all servers are +marked down, with this option set the cache is not even used when a query is +received, and a DNS error is returned in any case. The only exception from this +is that local records (as specified in rr and source +sections are still served normally. +In general, you probably want to get cached entries even when the network is down, +so this defaults to off. +.TP +.B max_ttl=\fItimespec\fP; +This option sets the maximum time a record is held in cache. All dns +resource records have a time to live field that says for what period of time the +record may be cached before it needs to be requeried. If this is more than the +value given with max_ttl, this time to live value is set to max_ttl. +This is done to prevent records from being cached an inappropriate long period of time, because +that is almost never a good thing to do. Default is 604800s (one week). +.TP +.B min_ttl=\fItimespec\fP; +This option sets the minimum time a record is held in cache. All dns +resource records have a time to live field that says for what period of time the +record may be cached before it needs to be requeried. If this is less than the +value given with min_ttl, this time to live value is set to min_ttl. +Default is 120 seconds. +.TP +.B neg_ttl=\fItimespec\fP; +This option sets the time that negatively cached records will remain valid in the +cache if no time to live can be determined. This is always the case when whole +domains are being cached negatively, and additionally when record types are cached +negatively for a domain for which no SOA record is known to pdnsd. If a SOA is present, +the ttl of the SOA is taken. +.TP +.B neg_rrs_pol=(on|off|auth|default); +This sets the RR set policy for negative caching; this tells pdnsd under which circumstances +it should cache a record type negatively for a certain domain. off will +turn the negative caching of record types off, on will always add a negative +cache entry when a name server did not return a record type we asked it for, and auth +will only add such entries if the answer came from an authoritative name server for that +domain. +.br +\fINew in version 1.2.8:\fP The default setting will add a negatively cached record +if either the answer was authoritive or the answer indicated the name server had "recursion available" +while the query explicitly requested such recursion. +.br +The preset is "default" (used to be auth). +.TP +.B neg_domain_pol=(on|off|auth); +This is analogue to neg_rrs_pol for whole domain negative caching. It should be safe +to set this on, because I have not seen a caching server that will falsely claim that a +domain does not exist. +.br +The default is auth. +.TP +.B run_as=\fIstring\fP; +This option allows you to let pdnsd change its user and group id after operations that needed +privileges have been done. This helps minimize security risks and is therefore recommended. The +supplied string gives a user name whose user id and primary group id are taken. +.br +A little more details: after reading the config file, becoming a daemon (if specified) and starting +the server status thread, the main thread changes its gid and uid, as do all newly created threads +thereafter. By taking another uid and gid, those threads run with the privileges of the +specified user. +Under Linux and FreeBSD, the server status thread runs with the original privileges only when the strict_setuid option +is set to off (see below, on by default), because these may be needed +for exec uptests. The manager thread also retains its original privileges in this case. +You should take care that the user you specify has write permissions on your cache file and +status pipe (if you need a status pipe). You should look out for error messages like "permission denied" +and "operation not permitted" to discover permission problems. +.br +.TP +.B strict_setuid=(on|off); +When used together with the run_as option, this option lets you specify that all threads of the +program will run with the privileges of the run_as user. This provides higher security than +the normal run_as +option, but is not always possible. See the run_as option for further discussion. +.br +This option is on by default. +.br +Note that this option has no effect on Non-Linux systems. +.TP +.B paranoid=(on|off); +Normally, pdnsd queries all servers in recursive mode (i.e. instructs servers to query other servers themselves +if possible, +and to give back answers for domains that may not be in its authority), and accepts additional records with information +for servers that are not in the authority of the queried server. This opens the possibility of so-called cache poisoning: +a malicious attacker might set up a dns server that, when queried, returns forged additional records. This way, he might +replace trusted servers with his own ones by making your dns server return bad IP addresses. This option protects +you from cache poisoning by rejecting additional records +that do not describe domains in the queried servers authority space and not doing recursive queries any more. +An exception +to this rule are the servers you specify in your config file, which are trusted. +.br +The penalty is a possible performance decrease, in particular, more queries might be necessary for the same +operation. +.br +You should also notice that there may be other similar security problems, which are essentially problems of +the DNS, i.e. +any "traditional" server has them (the DNS security extensions solve these problems, but are not widely +supported). +One of this vulnerabilities is that an attacker may bombard you with forged answers in hopes that one may match a +query +you have done. If you have done such a query, one in 65536 forged packets will be succesful (i.e. an average packet +count of 32768 is needed for that attack). pdnsd can use TCP for queries, +which has a slightly higher overhead, but is much less vulnerable to such attacks on sane operating systems. Also, pdnsd +chooses random query ids, so that an attacker cannot take a shortcut. If the attacker is able to listen to your network +traffic, this attack is relatively easy, though. +.br +This vulnerability is not pdnsd's fault, and is possible using any conventional +name server (pdnsd is perhaps a little more secured against this type of attacks if you make it use TCP). +.br +The paranoid option is off by default. +.br +.TP +.B ignore_cd=(on|off); +\fINew in version 1.2.8:\fP This option lets you specify that the CD bit of a DNS query will be ignored. +Otherwise pdnsd will reply FORMERR to clients that set this bit in a query. +It is safe to enable this option, as the CD bit refers to 'Checking Disabled' +which means that the client will accept non-authenticated data. +.br +This option is on by default. Turn it off if you want the old behaviour (before version 1.2.8). +.TP +.B scheme_file=\fIstring\fP; +In addition to normal uptests, you may specify that some servers shall only be queried when a certain +pcmcia-cs scheme is active (only under linux). For that, pdnsd needs to know where the file resides that +holds the pcmcia scheme information. Normally, this is either /var/lib/pcmcia/scheme or +/var/state/pcmcia/scheme. +.TP +.B status_ctl=(on|off); +This has the same effect as the \-s command line option: the status control is enabled when +on is specified. +.br +\fIAdded by Paul Rombouts\fP: Note that pdnsd\-ctl allows run-time configuration of pdnsd, +even the IP addesses of the name servers can be changed. If you're not using pdnsd\-ctl and +you want maximum security, you should not enable this option. It is disabled by default. +.TP +.B daemon=(on|off); +This has the same effect as the \-d command line option: the daemon mode is enabled when +on is specified. +.br +Default is off. +.TP +.B tcp_server=(on|off); +tcp_server=on has the same effect as the \-t or \-\-tcp +command-line option: it enables TCP serving. +Similarly, tcp_server=off is like the \-\-notcp command-line option. +.br +Default is on. +.TP +.B pid_file=\fIstring\fP; +This has the same effect as the \-p command line option: you can specify a file that pdnsd +will write its pid into when it starts in daemon mode. +.TP +.B verbosity=\fInumber\fP; +This has the same effect as the \-v command line option: you can set the verbosity of pdnsd's +messages with it. The argument is a number between 0 (few messages) to 3 (most messages). +.TP +.B query_method=(tcp_only|udp_only|tcp_udp|udp_tcp); +This has the same effect as the \-m command line option. +Read the documentation for the command line option on this. +tcp_only corresponds to the to, udp_only to the uo, +tcp_udp to the tu and udp_tcp to the ut +argument of the command line option. +.br +If you use query_method=tcp_udp, it is recommended that you also set the global timeout option to at least twice the longest server timeout. +.TP +.B run_ipv4=(on|off); +This has the same effect as the \-4 or \-6 command line option: +if on is specified, IPv4 support is enabled, and IPv6 support is disabled (if available). +If off is specified, IPv4 will be disabled and IPv6 will be enabled. +For this option to be meaningful, pdnsd needs to be compiled with support for the protocol you choose. +If pdnsd was compiled with both IPv4 and IPv6 support, and you want to include IPv6 addresses +in the configuration file, you will probably need to specify run_ipv4=off first to +ensure that the IPv6 addresses are parsed correctly. +.TP +.B debug=(on|off); +This has the same effect as the \-g command line option: the debugging messages are enabled when +on is specified. +.TP +.B ctl_perms=\fInumber\fP; +This option allows you to set the file permissions that the pdnsd status control socket will have. These +are the same as file permissions. The owner of the file will be the run_as user, or, if none is specified, +the user who started pdnsd. If you want to specify the permissions in octal (as usual), don't forget +the leading zero (0600 instead of 600!). To use the status control, write access is needed. The default +is 0600 (only the owner may read or write). +.br +Please note that the socket is kept in the cache directory, and that the cache directory permissions +might also need to be adjusted. Please ensure that the cache directory is not writeable for untrusted +users. +.TP +.B proc_limit=\fInumber\fP; +With this option, you can set a limit on the pdnsd threads that will be active simultaneously. If +this number is exceeded, queries are queued and may be delayed some time. +See also the procq_limit option. +.br +The default for this option is 40. +.TP +.B procq_limit=\fInumber\fP; +When the query thread limit proc_limit is exceeded, connection attempts to pdnsd will be queued. +With this option, you can set the maximum queue length. +If this length is also exceeded, the incoming queries will be dropped. +That means that tcp connections will be closed and udp queries will just be dropped, which +will probably cause the querying resolver to wait for an answer until it times out. +.br +See also the proc_limit option. A maximum of proc_limit+procq_limit +query threads will exist at any one time (plus 3 to 6 threads that will always +be present depending on your configuration). +.br +The default for this option is 60. +.TP +.B tcp_qtimeout=\fItimespec\fP; +This option sets a timeout for tcp queries. If no full query has been received on a tcp connection +after that time has passed, the connection will be closed. The default is set using the +\-\-with\-tcp\-qtimeout option to configure. +.TP +.B par_queries=\fInumber\fP; +This option used to set the maximum number of remote servers that would be queried simultaneously, +for every query that pdnsd receives. +.br +Since version 1.1.11, the meaning of this option has changed slightly. +It is now the increment with which the number of parallel queries is +increased when the previous set of servers has timed out. +For example, if we have a list \fIserver1, server2, server3,\fP etc. of available servers +and par_queries=2, then pdnsd will first send queries to \fIserver1\fP and \fIserver2\fP, +and listen for responses from these servers. +.br +If these servers do not send a reply within their timeout period, pdnsd will send additional +queries to \fIserver3\fP and \fIserver4\fP, and listen for responses from +\fIserver1, server2, server3\fP and \fIserver4\fP, and so on until a useful reply is +received or the list is exhausted. +.br +In the worst case there will be pending queries to all the servers in the list of available servers. +We may be using more system resources this way (but only if the first servers in the list +are slow or unresponsive), but the advantage is that we have a greater chance of catching a reply. +After all, if we wait longer anyway, why not for more servers. +.br +See also the explanation of the global timeout option below. +.br +1 or 2 are good values for this option. +The default is set at compile time using the \-\-with\-par\-queries option to configure. +.TP +.B timeout=\fItimespec\fP; +This is the global timeout parameter for dns queries. +This specifies the minimum period of time pdnsd will wait after sending the +first query to a remote server before giving up without having +received a reply. The timeout options in the configuration file are +now only minimum timeout intervals. Setting the global timeout option +makes it possible to specify quite short timeout intervals in the +server sections (see below). This will have the effect that pdnsd will start +querying additional servers fairly quickly if the first servers are +slow to respond (but will still continue to listen for responses from +the first ones). This may allow pdnsd to get an answer more quickly in +certain situations. +.br +If you use query_method=tcp_udp it is recommended that +you make the global timeout at least twice as large as the largest +server timeout, otherwise pdnsd may not have time to try a UDP query +if a TCP connection times out. +.br +Default value is 0. +.TP +.B randomize_recs=(on|off); +If this option is turned on, pdnsd will randomly reorder the cached records of one type +when creating an answer. This supports round-robin DNS schemes and increases fail +safety for hosts with multiple IP addresses, so this is usually a good idea. +.br +On by default. +.TP +.B query_port_start=(\fInumber\fP|none); +If a number is given, this defines the start of the port range used for queries of pdnsd. The +value given must be >= 1024. The purpose of this option is to aid certain firewall +configurations that are based on the source port. Please keep in mind that another application +may bind a port in that range, so a stateful firewall using target port and/or process uid may +be more effective. In case a query start port is given pdnsd uses this port as the first port of a +specified port range (see query_port_end) used for queries. +pdnsd will try to randomly select a free port from this range as local port for the query. +.br +To ensure that there are enough ports for pdnsd to use, the range between query_port_start and +query_port_end should be adjusted to at least (par_queries * proc_limit). +A larger range is highly recommended for security reasons, and also because other applications may +allocate ports in that range. If possible, this range should be kept out of the space +that other applications usually use. +.br +The default for this option is 1024. Together with the default value of query_port_end, +this makes it the hardest for an attacker to guess the source port used by the pdnsd resolver. +If you specify none here, pdnsd will let the kernel choose the source port, but +this may leave pdnsd more vulnerable to an attack. +.TP +.B query_port_end=\fInumber\fP; +Used if query_port_start is not none. Defines the last port of the range started by query_port_start +used for querys by pdnsd. The default is 65535, which is also the maximum legal value for this option. +For details see the description of query_port_start. +.TP +.B delegation_only=\fIstring\fP; +\fIAdded by Paul Rombouts\fP: This option specifies a "delegation-only" zone. +This means that if pdnsd receives a query for a name that is in a +subdomain of a "delegation-only" zone but the remote name server +returns an answer with an authority section lacking any NS RRs for +subdomains of that zone, pdnsd will answer NXDOMAIN (unknown domain). +This feature can be used for undoing the undesired effects of DNS +"wildcards". Several "delegation-only" zones may be specified together. +If you specify root servers in a server section it is +important that you set root_server=on in such a section. +.br +Example: + +delegation_only="com","net"; + +This feature is off by default. It is recommended that you only use +this feature if you actually need it, because there is a risk that +some legitimate names will be blocked, especially if the remote +name servers queried by pdnsd return answers with empty authority +sections. +.TP +.B ipv4_6_prefix=\fIstring\fP; +This option has the same effect as the \-i command-line option. +When pdnsd runs in IPv6 mode, this option specifies the prefix pdnsd uses to convert IPv4 addresses in +the configuration file (or addresses specified with pdnsd\-ctl) +to IPv6-mapped addresses. +The string must be a valid IPv6 address. Only the first 96 bits are used. +Note that this only effects the parsing of IPv4 addresses listed after this option. +.br +The default is "::ffff.0.0.0.0". +.TP +.B use_nss=(on|off); +If this option is turned on, pdnsd will call initgroups() to set up the group access list, +whenever pdnsd changes its user and group id (see run_as option). +There is a possible snag, though, if initgroups() uses NSS (Name Service Switch) and +NSS in turn uses DNS. In such a case you may experience lengthy timeouts and stalls. +By setting use_nss=off, you can disable the initgroups() call +(only possible in versions 1.2.5 and later). +.br +This option was contributed by Jan-Marek Glogowski. +.br +On by default. +.TP +.B udpbufsize=\fInumber\fP; +\fINew in version 1.2.9:\fP +This option sets the upper limit on the size of UDP DNS messages. The default is 1024. +.br +See also the edns_query server option below. + +.SS server Section +Each server section specifies a set of name servers that pdnsd should try to get +resource records or authoritative name server information from. The servers are +queried in the order of their appearance (or parallel to a limited extend). +If one fails, the next one is taken and so on. +.br +You probably want to specify the dns server in your LAN, the caching dns servers +of your internet provider or even a list of root servers in one or more server sections. +.br +The supported options in this section are: + +.TP +.B label=\fIstring\fP; +Specify a label for the server section. This can be used to refer to this section +when using pdnsd\-ctl, the pdnsd control utility. +.br +You can give several server sections the same label, but if you want to change the addresses +of a server section (see \fBip\fP option below) during run-time with +"pdnsd\-ctl\ server\ \fIlabel\fP\ up\ \fIdns1\fP,\fIdns2\fP,...", +the label must be unique. +.TP +.B ip=\fIstring\fP; +Give the IP (the address, \fInot\fP the host name) of the server. +.br +Multiple IP addresses can be given per server section. +This can be done by entering multiple lines of the form ip=\fIstring\fP; +or a single line like this: + +ip=\fIstring\fP,\fIstring\fP,\fIstring\fP; + +IP addresses do not have to be specified in the configuration file. +A server section without IP addresses will remain inactive until it is assigned +one or more addresses with pdnsd\-ctl, +the pdnsd control utility. +.br +If pdnsd has been compiled with both IPv4 and IPv6 support, any IPv6 addresses you specify +here will be skipped with a warning message, unless pdnsd is running in IPv6 mode. +Thus, unless pdnsd was compiled to startup in IPv6 mode by default, you need to use the +command-line option \-6 or set run_ipv4=off +first (see global section) in order to ensure +that IPv6 addresses are parsed correctly. +.br +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, +it will automatically be mapped to an IPv6 address. +.TP +.B file=\fIstring\fP; +\fINew in version 1.2:\fP This option allows you to give the name of a resolv.conf-style file. +Of the lines beginning with the nameserver keyword, the second field will be parsed as an +IP address, as if it were specified with the ip= option. The remaining lines will be ignored. +If the contents of the file changes while pdnsd is running, you can make pdnsd aware of the changes through the +use of pdnsd\-ctl, the pdnsd control utility. +This is usually most conveniently done by placing the command "pdnsd\-ctl\ config" in a script +that is automatically run whenever the DNS configuration changes. +.br +For example, suppose you have a ppp client that writes the DNS configuration for your ISP to the file +/etc/ppp/resolv.conf and runs the script /etc/ppp/ip-up when a new +connection is established. One way of ensuring that pdnsd is automatically reconfigured is to +add a server section in the config file with file=/etc/ppp/resolv.conf and to +add the command "pdnsd\-ctl\ config" to /etc/ppp/ip-up. +.TP +.B port=\fInumber\fP; +Give the port the remote name server listens on. Default is 53 (the official +dns port) +.TP +.B uptest=(ping|none|if|dev|diald|exec|query); +Determine the method to check whether the server is available. Currently +defined methods are: +.IP +\(bu \fBping\fP: Send an ICMP_ECHO request to the server. If it doesn't respond +within the timeout, it is regarded to be unavailable until the next probe. +.IP +\(bu \fBnone\fP: The availability status is not changed, only the time stamp is updated. +.IP +\(bu \fBif\fP: Check whether the interface (specified in the interface= option) is +existent, up and running. This currently works for all "ordinary" +network interfaces, interfaces that disappear when down (e.g. ppp?), +and additionally for Linux isdn interfaces (as of kernel 2.2). Note that +you need a /dev/isdninfo device file (major#45, minor#255), or the +isdn uptest will always fail. +.IP +\(bu \fBdev\fP and \fBdiald\fP: Perform an if uptest, and, if that +was succesful, additionally check whether a program is running that +has locked a given (modem-) device. The needed parameters are an interface (specified as for the if +uptest, e.g. "ppp0") and a device relative to /dev (e.g. +"modem" for /dev/modem specified using the device= option. +pdnsd will then look for a pid file for the given interface in /var/lock (e.g. +/var/run/ppp0.pid) and for a lockfile for the given device (e.g. /var/lock/LCK..modem), +and then test whether the locking process is the process that created the pid file and this process is still +alive. If this is the case, the normal if uptest is executed for the given interface. +.br +The dev option is for pppd dial-on-demand, diald is the same for diald users. +.IP +\(bu \fBexec\fP: Executes a given command in the /bin/sh shell +(as /bin/sh \-c ) +and evaluates the result (the return code of the last command) in the shell's way of handling return codes, +i.e. 0 indicates success, all other indicate failure. The shell's process name will be +uptest_sh. The command is given with the uptest_cmd option (see below). +For secuity issues, also see that entry. +.IP +\(bu \fBquery\fP: \fINew in version 1.2:\fP +This works like the ping test, except it sends an (empty) DNS query to the remote server. +If the server sends a well-formed response back within the timeout period (except SERVFAIL), +it will be regarded as available. +This test is useful if a remote server does not respond to ICMP_ECHO requests at all, +which unfortunately is quite common these days. +It can also happen that a remote server is online but ignores empty DNS queries. +Then you will need the set the query_test_name option (see below). +In many cases this test will be a more reliable indicator of availability +than the ones mentioned before. + +The default value is \fBnone\fP. + +\fBNOTE\fP: If you use on-demand dialing, use none, if, +dev, diald or exec, +since ping or query will send packets +in the specified interval and the interface will thus frequently dial! +.TP +.B ping_timeout=\fInumber\fP; +Sets the timeout for the ping test in tenths of seconds +(this unit is used for legacy reasons; actually the current implementation is +only accurate to a second). +.br +The default is 600 (one minute). +.TP +.B ping_ip=\fIstring\fP; +The IP address for the ping test. The default is the IP of the name server. +.TP +.B query_test_name=\fIstring\fP; +\fINew in version 1.2.9:\fP +Sets the name to be queried when using uptest=query availability test. +If the string is the unquoted constant none, +an empty query is used (this the default), otherwise a query of type A will be +sent for the domain name specified here. It is not necessary for the domain name +to exist or have a record of type A in order for the uptest to succeed. +.br +If the the remote server ignores empty queries, you will probably want to set +query_test_name="." (the root domain). +.TP +.B uptest_cmd=\fIstring\fP,\fIstring\fP; +or +.PD 0 +.TP +.PD +.B uptest_cmd=\fIstring\fP; +Sets the command for the uptest=exec function to the first string. +If the second string is given, it specifies a user with whose user +id and primary group id the command is executed. +.br +This is especially useful if you are executing the server as root, +but do not want the uptest to be performed with root privileges. +In fact, you should never execute the uptest as root if you can help +it. +.br +If the server is running setuid or setgid, the privileges thus gained +are attempted to be dropped even before changing identity to the +specified user to prevent setuid/gid security holes (otherwise, any +user might execute commands as root if you setuid the executable). +.br +\fBNote that this is not always possible, and that pdnsd should never +be installed as setuid or setgid.\fP +The command is executed using /bin/sh, so you should be able to use +shell builtin commands. +.TP +.B interval=(\fItimespec\fP|onquery|ontimeout); +Sets the interval for the server up-test. The default is 900 seconds; +however, a test is forced when a query times out and the timestamp is reset then. +.br +If you specify onquery instead of a timeout, the interface will be +tested before every query. This is to prevent automatically dialing +interfaces (diald/pppd or ippp) to dial on dns queries. It is intended to be +used in connection with an interface-testing uptest ;\-) +.br +Note that using uptest=exec, you might run into performance problems +on slow machines when you use that option. +DON'T use onquery with uptest=ping or +uptest=query, as it may cause delays if the server does not answer +(btw, it doesn't make sense anyway). +Note also that using onquery is no guarantee that the interface +will not be used. When another (reachable) dns server tells pdnsd +to query a third dns server for data, pdnsd will do that and has +no means of checking whether this will dial up the interface or not. +This however should be a rare situation. +.br +\fINew in version 1.2.3:\fP +A third possibility is to specify interval=ontimeout. +In this case the server is not tested at startup/reconfiguration, nor at regular intervals, +but only after a DNS query to a server times out. Certain types of network problems +such as a refused connection will also cause the server to be considered unavailable. +However, once a server is declared dead it is never considered again unless it is revived using a +pdnsd\-ctl config or server command. +The idea behind this option is to minimize uptests by assuming all +servers are available until there is reason to believe otherwise. +.TP +.B interface=\fIstring\fP; +The network interface (or network device, e.g. "eth0") for the uptest=if option. +Must be specified if uptest=if is given. +.TP +.B device=\fIstring\fP; +The (modem-) device that is used for the dev uptest. If you use this for a dial-on-demand +ppp uptest (together with uptest=dev), you need to enter the device you are using for your +pppd here, e.g. modem for /dev/modem. +.br +Must be specified if uptest=dev is given. +.TP +.B timeout=\fItimespec\fP; +Set the timeout for the dns query. The default is 120 seconds. You probably want to set this lower. +.br +Timeouts specified in the configuration file are only treated as the +minimum period of time to wait for a reply. A queries to a remote +server are not canceled until a useful reply has been received, or all +the other queries have timed out or failed. +.br +If you have also set the global timeout option, you may consider setting a fairly small value here. +See the explanation of the timeout option in the global +section for what that means. +.TP +.B purge_cache=(on|off); +In every fetched dns record, there is a cache timeout given, which +specifies how long the fetched data may be cached until it needs to be +reloaded. If purge_cache is set to off, the stale records are not purged +(unless the cache size would be exceeded, in this case the oldest records are purged). +Instead, they are still served if they cannot succesfully be +updated (e.g. because all servers are down). +.br +Default is off. +.TP +.B caching=(on|off); +Specifies if caching shall be performed for this server at all. Default is +on. +.TP +.B lean_query=(on|off); +Specifies whether to use the "lean" query mode. In this mode, only the +information actually queried from pdnsd is resolved and cached. This has +the advantage that usually less cache space is used and the query is +usually faster. In 90% of the cases, only address (A) records are needed +anyway. If switched off, pdnsd will always cache all data about a host +it can find and will specifically ask for all available records +(well, at least it is a good approximation for what it really does ;\-) +This will of course increase the answer packet sizes. +.br +Some buggy name servers may not deliver CNAME records when not asked for +all records. I do not know if such servers are around, but if you have +trouble resolving certain host names, try turning this option off. +.br +A last note: If you use multiple pdnsd's that access each other, turning +this option on is probably a big win. +.br +This on by default. +.TP +.B edns_query=(on|off); +\fINew in version 1.2.9:\fP +Specifies whether to use EDNS (Extension mechanisms for DNS) for outgoing queries. +Currently this is only useful for allowing UDP message sizes larger than 512 bytes. +Note that setting this option on can give problems in combination with some legacy +systems or software, including, embarrassingly enough, previous versions of pdnsd. +.br +The default is off, but if your network can handle UDP payloads +significantly larger than 512 bytes, the recommended value is on. +.br +Note that this option only effects outgoing queries. If pdnsd receives a query using +EDNS, it will reply using EDNS regardless of the value of this option. + +See also the udpbufsize option above. +.TP +.B scheme=\fIstring\fP; +You can specify a pcmcia-cs scheme that is used in addition to the uptests. If you specify +a scheme here, the server this section is for will only be queries if the given scheme +is active. Shell wildcards (* and ?) are allowed in the string under their special +meanings. You need to use the scheme_file option on the global +section to make this option work. +.TP +.B preset=(on|off); +This allows you to specify the initial state of a server before any uptest is performed. +on specifies that the server is regarded available. The default is on. +This is especially useful when you set uptest=none; and want to change +the status of a server only via pdnsd\-ctl. +.TP +.B proxy_only=(on|off); +When this option is set to on, answers given by the servers are always accepted, and no +other servers (as, for example, specified in the NS records of the query domain) are +queried. If you do not turn this option on, pdnsd will do such queries in some cases +(in particular when processing ANY queries). +.br +This option is useful when you do not want pdnsd to make connections to outside servers +for some reasons (e.g. when a firewall is blocking such queries). +.br +I recommend that you turn on lean_query when using this option. +.br +Default is off. +.TP +.B root_server=(on|off|discover); +Set this option to on if the servers specified in a section are root servers. +A root server will typically only give the name servers for the top-level domain in its reply. +Setting root_server=on will cause pdnsd to try to use cached information about +top-level domains to reduce to number of queries to root servers, making the resolving of +new names more efficient. +You can get a list of available root servers by running the command +"dig\ .\ ns". +.br +This option is also necessary if you use the delegation_only option. +.br +\fINew in version 1.2.8:\fP This option may also be set to "discover". +This will cause pdnsd to query the servers provided with the ip= option +to obtain the full list of root servers. The root-server addresses will replace the addresses +specified with the ip= option. +This will only be done once on startup, or after a "pdnsd\-ctl\ config" command. +In this case the name servers specified with the ip= option don't have to be +root servers, they just have to know the names and addresses of the root servers. +After root-server discovery pdnsd will behave just as if root_server=on +had been specified. +.br +Default is off. +.TP +.B randomize_servers=(on|off); +\fINew in version 1.2.6:\fP Set this option to on to give each name server +in this section an equal chance of being queried. If this option is off, the name servers +are always queried starting with the first one specified. Even with this option on, the +query order is not truly random. Only the first server is selected randomly; the following +ones are queried in consecutive order, wrapping around to the beginning of the list when +the end is reached. Note that this option only effects the order within a section. The +servers in the first (active) section are always queried before those in the second one, +etc. +.br + The default is off, but if you are resolving from root servers setting this +option on is highly recommended. If root_server=on this option also effects +the query order of the name servers for the top-level domains. +.TP +.B reject=\fIstring\fP; +\fINew in version 1.2.6:\fP This option can be used to make pdnsd reject replies that +contain certain IP addresses. You can specify a single IP address, which will be matched +exactly, or a range of addresses using an address/mask pair. +The mask can be specified as a simple integer, indicating the number of initial 1 bits in +the mask, or in the usual IP address notation. IP addresses may be either IPv4 or IPv6 +(provided there is sufficient support in the C libraries and support for AAAA records was +not disabled). +When addresses in the reject list are compared with those in a reply, only the bits +corresponding to those set in the netmask are significant, the rest are ignored. +.br +Multiple addresses or address/mask pairs may be specified; this can be done by entering +multiple lines of the form reject=\fIstring\fP; +or a single line like this: + +reject=\fIstring\fP,\fIstring\fP,\fIstring\fP; + +How pdnsd reacts when an address in the reply matches one in the reject list, +depends on the reject_policy option, see below. +.TP +.B reject_policy=(fail|negate); +\fINew in version 1.2.6:\fP +This option determines what pdnsd does when an address in the reply from a name server +matches the reject list (see above). If this option is set to +fail, pdnsd will try another server, or, if there no more servers to try, +return the answer SERVFAIL. If this option is set to negate, pdnsd will +immediately return the answer NXDOMAIN (unknown domain) without querying additional +servers. The fail setting is useful if you don't always trust the servers in +this section, but do trust the servers in the following section. The negate +setting can be used to completely censor certain IP addresses. In this case you should put +the same reject list in every server section, and also set the +reject_recursively option (see below) to true. +.br +The default is fail. +.TP +.B reject_recursively=(on|off); +\fINew in version 1.2.6:\fP Normally pdnsd checks for addresses in the +reject list (see above) only when the reply comes directly from a name server +listed in the configuration file. With this option set to on, pdnsd will +also do this check for name servers that where obtained from NS records in the authority +section of a previous reply (which was incomplete and non-authoritative). +.br +Default is off. +.TP +.B policy=(included|excluded|simple_only|fqdn_only); +pdnsd supports inclusion/exclusion lists for server sections: with include= +and exclude= (see below) you can specify domain names for which this server +will be used or will not be used. The first match counts (i.e., the first include or +exclude rule in a server section that matches a domain name is applied, and the +search for other rules is terminated). If no rule matched a given domain name, +the policy= option determines whether this server is used for the +lookup for that domain name; when included is given, the server will +be asked, and when excluded is given, it will not. +If simple_only is given the server will be used if the name to lookup +is a simple (single-label) domain name, on the other hand if fqdn_only +is given the server will be used only for names consisting of two or more labels +(i.e. the name has at least one dot in-between). +.br +If no server is available for a queried domain, pdnsd will return an error message +to the client that usually will stop the client's attempts to resolve a specific +domain from this server (the libc resolver will e.g. return an error to the application that +tried to resolve the domain if no other servers are available in the resolv.conf). +This may be of use sometimes. +.br +\fINote\fP: the simple_only and fqdn_only constants +were added by Paul Rombouts. +They are useful for controlling which name servers (if any) will be used by +pdnsd for resolving simple (single-label) host names. +fqdn_only used to stand for "fully qualified domain name only", but this is +actually a misnomer. The names in queries received by pdnsd are always considered to be +fully qualified. If you do not exactly understand what the options simple_only and +fqdn_only are good for, you are probably better off not using them. +.br +The default for this option is included. +.TP +.B include=\fIstring\fP; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is queried if this was the first matching rule +(see also the entry for policy). +.br +If the given name starts with a dot, the whole subdomain +of the given name including the one of that name is matched, e.g. ".foo.bar." +will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +.br +If it does not start in a dot, only exactly the given name (ignoring the case, of course) +will be matched (hint: if you want to include all subdomains, but not the domain of the given +name itself, place an exact-match exclude rule before the include rule, e.g: +exclude="foo.bar."; include=".foo.bar."; +.br +Previous versions of pdnsd +required that names given with this and the next option ended in a dot, but since +version 1.1.8b1-par8, pdnsd automatically adds a dot at the end if it +is missing. +.br +pdnsd now also accepts a more compact notation for adding several "include" entries in +one line, e.g.: + +include=".foo",".bar",".my.dom"; + +.TP +.B exclude=\fIstring\fP; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is not queried if this was the first matching rule +(see also the entry for policy). +.br +If the given name starts with a dot, the whole subdomain +of the given name including the one of that name is matched, e.g. ".foo.bar." +will match the domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +.br +If it does not start in a dot, only exactly the given name (ignoring the case, of course) +will be matched (hint: if you want to exclude all subdomains, but not the domain of the given +name itself, place an exact-match include rule before the exclude rule, e.g: +include="foo.bar."; exclude=".foo.bar."; +.br +pdnsd now also accepts a more compact notation for adding several "exclude" entries in +one line, e.g.: + +exclude=".foo",".bar",".my.dom"; + + +.SS rr Section +Every rr section specifies a dns resource record that is stored locally. It +allows you to specify own dns records that are served by pdnsd in a limited way. +Only A, PTR, CNAME, MX, NS and SOA records are implemented. +.br +This option is intended to allow you to define RRs for 1.0.0.127.in-addr.arpa. +and localhost. (and perhaps even one or two hosts) without having to start an +extra named if your cached name servers do not serve those records. +It is \fBNOT\fP intended and not capable to work as a full-featured name server. + +.TP +.B name=\fIstring\fP; +Specifies the name of the resource records, i.e. the domain name of +the resource the record describes. This option must be specified +before any a, ptr, cname, +mx, ns or soa records. +Names are interpreted as absolute domain names +(i.e. pdnsd assumes they end in the root domain). +For this and all following arguments that take domain names, you need to +specify domain names in dotted notation (example venera.isi.edu.). +.br +Previous versions of pdnsd +required that domain names given in the configuration file ended in a +dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a +dot at the end if it is missing. +.br +\fINew in version 1.2:\fP It is also possible to specify a name starting +with the label *. Such a name is called a wildcard. The * in a wildcard +can match one or more labels in a queried name, but only whole labels. +Any other * characters in a wildcard, apart from the leading one, +will only match a literal *. +.br +For example, *.mydomain will match a.mydomain or www.a.mydomain, but not +mydomain. *.a*.mydomain will match www.a*.mydomain, but not www.ab.mydomain. +*a.mydomain will only match itself. +.br +Before you can specify an rr section with name=*.mydomain +you must define some records for mydomain, typically NS and/or SOA records. +Example: +.DS L + + rr { + name = mydomain; + ns = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = *.mydomain; + a = 192.168.1.10; + } +.DE + +In this example, www.mydomain and ftp.mydomain will resolve to the numeric +address 192.168.1.10 (unless you add rr sections explicitly +specifying different addresses for www.mydomain or ftp.mydomain). +If you want mydomain also to resolve to a numeric address, +add an A record to the first rr section. +.TP +.B ttl=\fItimespec\fP; +Specifies the ttl (time to live) for all resource records in this section after this entry. +This may be redefined. The default is 86400 seconds (=1 day). +.TP +.B authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records for this rr section. +This means that pdnsd flags the domain record so that records of this domain that are not +present in the cache are treated as non-existent, i.e. no other servers are queried for +that record type, and an response containing none of those records is returned. This is +most time what people want: if you add an A record for a host, and it has no AAAA record +(thus no IPv6 address), you normally don't want other name servers to be queried for it. +.br +This is on by default. +.br +Please note that this only has an effect if it precedes the name option! +.TP +.B reverse=(on|off); +\fINew in version 1.2:\fP If you want a locally defined name to resolve to a numeric address +and vice versa, you can achieve this by setting reverse=on before defining the A record +(see below). The alternative is to define a separate PTR record, but you will +probably find this option much more convenient. +.br +The default is off. +.TP +.B a=\fIstring\fP; +Defines an A (host address) record. The argument is an IPv4 address in dotted notation. +pdnsd will serve this address for the host name given in the name option. +.br +Provided there is sufficient support in the C libraries and support for AAAA records was not +disabled, the argument string may also be an IPv6 address, in which case an AAAA record +will be defined. +.br +This option be may used multiple times within an rr section, causing +multiple addresses to be defined for the name. However, if you put the different addresses +in different rr sections for the same name, the definition in the last +rr section will cancel the definitions in the previous ones. +.TP +.B ptr=\fIstring\fP; +Defines a PTR (domain name pointer) record. The argument is a host name in +dotted notation (see name). The ptr record is for resolving adresses into names. For example, if +you want the adress 127.0.0.1 to resolve into localhost, and localhost into 127.0.0.1, you need something +like the following sections: +.br +.DS L + + rr { + name = localhost; + a = 127.0.0.1; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = 1.0.0.127.in-addr.arpa; + ptr = localhost; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } +.DE + +The second section is for reverse resolving and uses the ptr option. +Note that you can get the same effect by specifying only the first rr section +with reverse=on. +.br +There is something special about the name in the second section: +when a resolver wants to get a host name from an internet address, +it composes an address that is built of the IP address in reverse byte order +(1.0.0.127 instead of 127.0.0.1) where each byte of the adress written +as number constitutes a sub-domain under the domain in-addr.arpa. +.br +So, if you want to compose an adress for reverse resolving, take your ip in dotted notation (e.g. 1.2.3.4), +reverse the byte order (4.3.2.1) and append in-addr.arpa. (4.3.2.1.in-addr.arpa.) +Then, define an rr section giving this address as name and the domain name corresponding to +that ip in the ptr option. +.TP +.B cname=\fIstring\fP; +Defines a CNAME (canonical name) record. +The argument should be a fully-qualified host name in dotted notation (see name). +A CNAME is the DNS equivalent of an alias or symbolic link. +.br +A useful application for CNAMEs is giving short, easy to remember nicknames to hosts with complicated names. +For example, you might want the name "news" to refer to your ISP's news server "nntp2.myisp.com". +Instead of adding an A record for "news" with the same address as "nntp2.myisp.com", you could +put in a CNAME pointing to "nntp2.myisp.com", so that if the IP address of the news server changes, +there is no need to update the record for "news". +.br +To implement this with pdnsd, you could add the following section to your configuration file: +.br +.DS L + + rr { + name = news; + cname = nntp2.myisp.com; + owner = localhost; + } +.DE + +.TP +.B mx=\fIstring\fP,\fInumber\fP; +Defines an MX (mail exchange) record. The string is the host name of the mail server in dotted notation (see name). +The number specifies the preference level. +.br +When you send mail to someone, your mail typically goes from your E-mail client to an SMTP server. +The SMTP server then checks for the MX record of the domain in the E-mail address. +For example, with joe@example.com, it would look for the MX record for example.com and find +that the name of mail server for that domain is, say, mail.example.com. +The SMTP server then gets the A record for mail.example.com, and connects to the mail server. +.br +If there are multiple MX records, the SMTP server will pick one based on the preference level +(starting with the lowest preference number, working its way up). +.br +Don't define MX records with pdnsd unless you know what you're doing. +.TP +.B owner=\fIstring\fP; +or +.PD 0 +.TP +.PD +.B ns=\fIstring\fP; +Defines an NS (name server) record. Specifies the name of the host which should be authoritative for the records +you defined in the rr section. This is typically the host pdnsd runs on. +.br +\fINote:\fP In previous versions of pdnsd this option had to be specified before +any a, ptr, cname, mx or soa entries. +In version 1.2, the restrictions on this option are same as the options just mentioned, +and it must listed after the name= option. +This can be a pain if you want to use an old config file which specifies owner= +before name= (sorry about that). +Apart from greater consistency, the advantage is that you can now specify as many NS records as you like (including zero). +.TP +.B soa=\fIstring\fP,\fIstring\fP,\fInumber\fP,\fItimespec\fP,\fItimespec\fP,\fItimespec\fP,\fItimespec\fP; +This defines a soa (start of authority) record. The first string is the +domain name of the server and should be equal to the name you specified as +owner. +.br +The second string specifies the email address of the maintainer of the name +server. It is also specified as a domain name, so you will have to replace the +@ sign in the name with a dot (.) to get the name you have to specify here. +The next parameter (the first number) is the serial number of the record. You +should increment this number if you change the record. +.br +The 4th parameter is the refresh timeout. It specifies after what amount +of time a caching server should attempt to refresh the cached record. +.br +The 5th parameter specifies a time after which a caching server should attempt +to refresh the record after a refresh failure. +.br +The 6th parameter defines the timeout after which a cached record expires if it +has not been refreshed. +.br +The 7th parameter is the ttl that is specified in every rr and should be the +same as given with the ttl option (if you do not specify a ttl, use the default 86400). +.TP +.B txt=\fIstring\fP,...,\fIstring\fP; +\fINew in version 1.2.9:\fP +Defines an TXT record. You can specify one or more strings here. + +.SS neg Section +Every neg section specifies a dns resource record or a dns domain that should be +cached negatively locally. Queries for negatively cached records are always answered +immediatley with an error or an empty answer without querying other hosts as long +as the record is valid. The records defined with neg sections remain +valid until they are explicitely invalidated or deleted by the user using +pdnsd\-ctl. +.br +This is useful if a certain application asks periodically for nonexisting hosts or +RR types and you do not want a query to go out every time the cached record has +timed out. Example: Netscape Communicator will ask for the servers news and mail +on startup if unconfigured. If you do not have a dns search list for your network, +you can inhibit outgoing queries for these by specifying +.br +.DS L + + neg { + name = news; + types = domain; + } + neg { + name = mail; + types = domain; + } +.DE + +in your config file. If you have a search list, you have to repeat that for any +entry in your search list in addition to the entries given above! +.br +In versions 1.1.11 and later, if you negate whole domains this way, all subdomains +will be negated as well. Thus if you specify +.br +neg {name=example.com; types=domain;} in the +config file, this will also negate www.example.com, xxx.adserver.example.com, etc. + +.TP +.B name=\fIstring\fP; +Specifies the name of the domain for which negative cache entries are created. +This option must be specified before the types option. +Names are interpreted as absolute domain names (i.e. pdnsd +assumes they end in the root domain). +You need to specify domain names in dotted notation (example venera.isi.edu.). +.br +Previous versions of pdnsd +required that domain names given in the configuration file ended in a +dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a +dot at the end if it is missing. +.TP +.B ttl=\fItimespec\fP; +Specifies the ttl (time to live) for all resource records in this section after this entry. +This may be redefined. The default is 86400 seconds (=1 day). +.TP +.B types=(domain|\fIrr_type\fP[,\fIrr_type\fP[,\fIrr_type\fP[,...]]]); +Specifies what is to be cached negatively: domain will cache the whole +domain negatively; alternatively, you can specify a comma-separated list of RR types +which are to be cached negatively. You may specify multiple types options, but +domain and the RR types are mutually exclusive. +.br +The RR types are specified using their official names from the RFC's in capitals, +e.g. A, CNAME, NS, PTR, MX, +AAAA, ... +.br +The command pdnsd\-ctl\ list\-rrtypes will give you a complete list +of those types. pdnsd\-ctl is built along with pdnsd +and will be installed in the same directory as the pdnsd binary during make install. + +.SS source Section +Every source section allows you to let pdnsd read the records from a file in an +/etc/hosts-like format. pdnsd will generate records to resolve the entries +address from its host name and vice versa for every entry in the file. This is +normally easier than defining an rr for every of your addresses, since localhost +and your other FQDNs are normally given in /etc/hosts. +.br +The accepted format is as follows: The #\-sign initiates a comment, the rest of +the line from the first occurence of this character on is ignored. Empty lines +are tolerated. +.br +The first entry on a line (predeceded by an arbitrary number of tabs and spaces) +is the IP in dotted notation, the second entry on one line (separated by the +first by an arbitrary number of tabs and spaces) is the FQDN (fully qualified +domain name) for that ip. The rest of the line is ignored by default (in the original +/etc/hosts, it may contain information not needed by pdnsd). + +.TP +.B owner=\fIstring\fP; +Specifies the name of the host pdnsd runs on and that are specified in dns +answers (specifically, nameserver records). +Must be specified before any file entries. +.br +Names are interpreted as absolute domain names (i.e. pdnsd +assumes they end in the root domain). +You need to specify domain names in dotted notation (example venera.isi.edu.). +.br +Previous versions of pdnsd +required that domain names given in the configuration file ended in a +dot, but since version 1.1.8b1-par8, pdnsd automatically assumes a +dot at the end if it is missing. +.TP +.B ttl=\fItimespec\fP; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). +.TP +.B file=\fIstring\fP; +The string specifies a file name. For every file entry in a source section, +pdnsd will try to load the given file as described above. Failure is indicated +only when the file cannot be opened, malformed entries will be ignored. +.TP +.B serve_aliases=(on|off); +If this is turned on pdnsd will serve the aliases given in a hosts-style file. +These are the third entry in a line of a hosts-style file, which usually give a "short name" for the host. +This may be used to support broken clients without a proper domain-search option. +If no aliases are given in a line of the file, pdnsd behaves as without this option for this line. +.br +This feature was suggested by Bert Frederiks. +.br +It is off by default. +.TP +.B authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records with the data from the hosts file. +Please see the description of the option of the same name in the rr section for a closer description of +what this means. Please note that this only has an effect for files sourced with file options +subsequent to this option. +.br +This is on by default. + +.SS include Section +A configuration file may include other configuration files. +However, only the top-level configuration file may contain global +and server sections, +thus include files are effectively limited to sections that add local definitions to the cache. +.br +Include sections currently only have one type of option, which may be given multiple times within a single section. + +.TP +.B file=\fIstring\fP; +The string specifies a file name. For every file option in an include section, +pdnsd will parse the given file as described above. The file may contain include sections itself, +but as a precaution pdnsd checks that a certain maximum depth is not exceeded to guard against +the possibility of infinite recursion. + +.SH "VERSION" +.PP +This man page is correct for version @fullversion@ of pdnsd. +.SH "SEE ALSO" +.PP +.BR pdnsd (8), +.BR pdnsd\-ctl (8) +.PP +More documentation is available in the \fBdoc/\fP subdirectory of the source, +or in \fB/usr/share/doc/pdnsd/\fP if you are using a binary package. + +.SH AUTHORS + +\fBpdnsd\fP was originally written by Thomas Moestl +.UR + +.UE +and was extensively revised by Paul A. Rombouts +.UR + +.UE +(for versions 1.1.8b1\-par and later). +.PP +Several others have contributed to \fBpdnsd\fP; see files in the source or +\fB/usr/share/doc/pdnsd/\fP directory. +.PP +This man page was automatically generated from the html documentation for \fBpdnsd\fP, +using a customized Perl script written by Paul A. Rombouts. +.PP +Last revised: 19 April 2012 by Paul A. Rombouts diff --git a/service/src/main/jni/pdnsd/doc/pdnsd.conf.in b/service/src/main/jni/pdnsd/doc/pdnsd.conf.in new file mode 100644 index 0000000..e348eb0 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/pdnsd.conf.in @@ -0,0 +1,143 @@ +// Sample pdnsd configuration file. Must be customized to obtain a working pdnsd setup! +// Read the pdnsd.conf(5) manpage for an explanation of the options. +// Add or remove '#' in front of options you want to disable or enable, respectively. +// Remove '/*' and '*/' to enable complete sections. + +global { + perm_cache=1024; + cache_dir="@cachedir@"; +# pid_file = /var/run/pdnsd.pid; + run_as="@def_id@"; + server_ip = 127.0.0.1; # Use eth0 here if you want to allow other + # machines on your network to query pdnsd. + status_ctl = on; +# paranoid=on; # This option reduces the chance of cache poisoning + # but may make pdnsd less efficient, unfortunately. + query_method=udp_tcp; + min_ttl=15m; # Retain cached entries at least 15 minutes. + max_ttl=1w; # One week. + timeout=10; # Global timeout option (10 seconds). + neg_domain_pol=on; + udpbufsize=1024; # Upper limit on the size of UDP messages. +} + +# The following section is most appropriate if you have a fixed connection to +# the Internet and an ISP which provides good DNS servers. +server { + label= "myisp"; + ip = 192.168.0.1; # Put your ISP's DNS-server address(es) here. +# proxy_only=on; # Do not query any name servers beside your ISP's. + # This may be necessary if you are behind some + # kind of firewall and cannot receive replies + # from outside name servers. + timeout=4; # Server timeout; this may be much shorter + # that the global timeout option. + uptest=if; # Test if the network interface is active. + interface=eth0; # The name of the interface to check. + interval=10m; # Check every 10 minutes. + purge_cache=off; # Keep stale cache entries in case the ISP's + # DNS servers go offline. + edns_query=yes; # Use EDNS for outgoing queries to allow UDP messages + # larger than 512 bytes. May cause trouble with some + # legacy systems. +# exclude=.thepiratebay.org, # If your ISP censors certain names, you may +# .thepiratebay.se, # want to exclude them here, and provide an +# .piratebay.org, # alternative server section below that will +# .piratebay.se; # successfully resolve the names. +} + +/* +# The following section is more appropriate for dial-up connections. +# Read about how to use pdnsd-ctl for dynamic configuration in the documentation. +server { + label= "dialup"; + file = "/etc/ppp/resolv.conf"; # Preferably do not use /etc/resolv.conf + proxy_only=on; + timeout=4; + uptest=if; + interface = ppp0; + interval=10; # Check the interface every 10 seconds. + purge_cache=off; + preset=off; +} +*/ + +/* +# The servers provided by OpenDNS are fast, but they do not reply with +# NXDOMAIN for non-existant domains, instead they supply you with an +# address of one of their search engines. They also lie about the addresses of +# of the search engines of google, microsoft and yahoo. +# If you do not like this behaviour the "reject" option may be useful. +server { + label = "opendns"; + ip = 208.67.222.222, 208.67.220.220; + reject = 208.69.32.0/24, # You may need to add additional address ranges + 208.69.34.0/24, # here if the addresses of their search engines + 208.67.219.0/24; # change. + reject_policy = fail; # If you do not provide any alternative server + # sections, like the following root-server + # example, "negate" may be more appropriate here. + timeout = 4; + uptest = ping; # Test availability using ICMP echo requests. + ping_timeout = 100; # ping test will time out after 10 seconds. + interval = 15m; # Test every 15 minutes. + preset = off; +} +*/ + +/* +# This section is meant for resolving from root servers. +server { + label = "root-servers"; + root_server = discover; # Query the name servers listed below + # to obtain a full list of root servers. + randomize_servers = on; # Give every root server an equal chance + # of being queried. + ip = 198.41.0.4, # This list will be expanded to the full + 192.228.79.201; # list on start up. + timeout = 5; + uptest = query; # Test availability using empty DNS queries. +# query_test_name = .; # To be used if remote servers ignore empty queries. + interval = 30m; # Test every half hour. + ping_timeout = 300; # Test should time out after 30 seconds. + purge_cache = off; +# edns_query = yes; # Use EDNS for outgoing queries to allow UDP messages + # larger than 512 bytes. May cause trouble with some + # legacy systems. + exclude = .localdomain; + policy = included; + preset = off; +} +*/ + +source { + owner=localhost; +# serve_aliases=on; + file="/etc/hosts"; +} + +/* +include {file="/etc/pdnsd.include";} # Read additional definitions from /etc/pdnsd.include. +*/ + +rr { + name=localhost; + reverse=on; + a=127.0.0.1; + owner=localhost; + soa=localhost,root.localhost,42,86400,900,86400,86400; +} + +/* +neg { + name=doubleclick.net; + types=domain; # This will also block xxx.doubleclick.net, etc. +} +*/ + +/* +neg { + name=bad.server.com; # Badly behaved server you don't want to connect to. + types=A,AAAA; +} +*/ diff --git a/service/src/main/jni/pdnsd/doc/txt/doc_makefile b/service/src/main/jni/pdnsd/doc/txt/doc_makefile new file mode 100644 index 0000000..57aa7c1 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/txt/doc_makefile @@ -0,0 +1,21 @@ +# Makefile for converting pdnsd html documentation to text files. +# This file was based on a Makefile originally written by Thomas Moestl +# and adapted by Paul Rombouts. + + +HTML2TXT=w3m -dump -cols 80 -T text/html + +doc: intro.txt manual.txt faq.txt +.PHONY: doc clean + +intro.txt: ../html/index.html + sed -e 's///-->/g' ../html/index.html | $(HTML2TXT) | sed -e 's/[[:blank:]]\+$$//' > intro.txt + +manual.txt: ../html/doc.html + sed -e 's///-->/g' ../html/doc.html | $(HTML2TXT) | sed -e 's/[[:blank:]]\+$$//' > manual.txt + +faq.txt: ../html/faq.html + sed -e 's///-->/g' ../html/faq.html | $(HTML2TXT) | sed -e 's/[[:blank:]]\+$$//' > faq.txt + +clean: + @rm -fv intro.txt manual.txt faq.txt diff --git a/service/src/main/jni/pdnsd/doc/txt/faq.txt b/service/src/main/jni/pdnsd/doc/txt/faq.txt new file mode 100644 index 0000000..0263161 --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/txt/faq.txt @@ -0,0 +1,227 @@ +The pdnsd FAQ + +Q: There are complete and well-tested name servers around, such as the BIND. + These do also perform caching. Why should I use pdnsd? + + pdnsd does not aim to be a complete name server implementation, such as the + BIND. It is optimized for caching, and you can only specify a small subset + of all dns record types pdnsd knows in your local "zone" definitions. This + of course reduces the code size drastically, and such the memory footprint. + There are some features especially interesting for dialin networks, + ordinary (non-server) internet hosts and computers that are often not + connected to to their network, e.g. notebooks (I originally wrote this + program for use with my notebook). These features are: +A: + * permanent disk cache (useful for frequent power-offs/reboots) + * usually smaller memory footprint (depends on cache size) (see next + question) + * offline-detection prevents hangs (e.g. the typical hang on startup of + some Netscape Navigator versions if not dialled in) + * better control about timeouts (also to prevent hangs) + * better control over the cache + * better run-time control + +------------------------------------------------------------------------------- + + When I look at the process size with ps, top, gtop, or a similar tool, I +Q: see some processes with a total size well above 3.5 MB. This is much more + than e.g. BIND named (about 1.4 MB). Why? + + Really, it is not. pdnsd uses multithreading, not multiprocessing. That + means that the processes share most of their process space. In the + LinuxThreads library or NPTL (Native Posix Thread Libary), which are used + by pdnsd on Linux, in fact the total process address space is shared +A: (although the processes have different stacks, these are in one process + address space). You may check this by looking at the at the process sizes + of the pdnsd threads: all should be the same. The effective size that pdnsd + occupies is thus the size of any of the processes, not the sum of those. + So, pdnsd with empty cache occupies about 800 kB, and the maximum size + should be about the cache size plus this size (in fact, ca 5-10% more). + +------------------------------------------------------------------------------- + +Q: What do I need the status control (option -s) for? + + It enables you to do some things you might or might not need. With it, you + can: + + * query pdnsd's settings at runtime to debug configuration files and see + which servers are regarded to be available +A: * mark servers as available or unavailable, or force a status retest - + very handy if you want to control which servers pdnsd queries, e.g for + muliple dial-up accounts + * delete, invalidate or add DNS records - useful e.g. when you want to + build records for dynamically assigned IP addresses or domain names + * reload pdnsd's configuration file without restarting pdnsd + * print information about the contents of pdnsd's cache. + +------------------------------------------------------------------------------- + +Q: What do I need local records (rr- and source-sections in the config file) + for? + + Some resolver programs, e.g. nslookup, want to look up the name of the + server they are using before doing anything else. This option is for + defining a PTR record for your IP such that those programs get an answer + even if the name server you are caching is not available or does not offer + these records. By extension, you may also define A and SOA records. This + allows you to build very small zones without having to use a "big" name + server. It is NOT intended to replace such a complete server in anything + but VERY small networks. Alternatively, you may start a named on another + host or on the same host on another port and cache it with pdnsd in + addition to other (more distant) name servers. +A: The source section allows you to let pdnsd read in your /etc/hosts file on + startup and serve its contents. This file is used by your local resolver + before it even tries the name servers and usually contains fully-qualified + domain names (FQDNs) for all of the internet addresses your host has. If + you source this file, you usually won't need any additional rr sections. + Sourcing it also allows other hosts (eg. in your local network) to access + the names defined in your hosts file. You can of course just add other + hosts in your local network to the servers hosts file, thus making them + known to your server's resolver and pdnsd (if you sourced that file). + If you don't know what this answer was all about, you should just take the + source section in the sample config file that comes with pdnsd, copy it + into your config file and forget about it. + +------------------------------------------------------------------------------- + + When compiling, I get an error message like +Q: Please define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN + What's up? + + Normally, this macros should be defined in your C library's header files. + There are two different methods, most C libraries support both (and pdnsd + honors both): either __BYTE_ORDER is set to __LITTLE_ENDIAN or + __BIG_ENDIAN, or __LITTLE_ENDIAN or __BIG_ENDIAN are directly defined as + macros. + Linux glibc, for example, does set those macros correctly. Never mind. You + just have to know whether your machine is little-endian or big-endian, this + means wheter your machine saves the least significant byte of a word or + double-word first in memory (little-endian) or the most significant first +A: (big-endian). All intel x86 and Alpha machines are little-endian, for + example, while SPARC and PowerPC architectures are big-endian. If your + machine is little-endian, add the following line to your config.h: + #define __BYTE_ORDER __LITTLE_ENDIAN + Likewise, if your machines byte order is big-endian: + #define __BYTE_ORDER __BIG_ENDIAN + Pathological byte orders like pdp-endian are not yet supported really; + However, for the place the endianess is needed, __LITTLE_ENDIAN should do + (it deals only with 16 bits; for all other occurances, ntoh[sl]/hton[sl] is + used). + +------------------------------------------------------------------------------- + + At startup, I get a warning saying: +Q: Uptest command [...] will implicitly be executed as root + What does that mean? + + This warning only occurs if you use the uptest=exec option in your + configuration. It means that the uptest command is run as root because + pdnsd is running as root, and this was not explicitely specified. The idea + is that it may introduce security holes (in the programs being run) when +A: they run as root, and so they shouldn't do that if possible. You can + specify the user that shall run the command by appending its name + comma-separated as string to the uptest_cmd line: + uptest_cmd="",""; + If it is correctly running as root, just append the user string "root" to + the command and the warning will not occur again. + +------------------------------------------------------------------------------- + +Q: I cannot run my uptest_cmd command as root (it says permission denied), + although the pdnsd executable is setuid root. Why? + + pdnsd will drop privileges gained through setuid/setgid before executing + the uptest commands (you shouldn't set the pdnsd executable setuid/setgid +A: anyway). The reason is clear: if you install the pdnsd executable as setuid + root and this wouln't be done, any user could execute shellcode with root + privileges using that option! + +------------------------------------------------------------------------------- + + At startup, I get an error saying: +Q: Bad config file permissions: the file must be only writeable by the user + Why is that? + + pdnsd has an option (uptest=exec) that allows the execution of arbitrary + shell code (for testing whether an interface is up). This must be of course + secured against unauthorized use. One of these protection is the one that + produces the error message: if you routinely run pdnsd, e.g. at system + startup, and your config file is editable for others, someone could change +A: it and insert shell code that is executed in the next pdnsd run -- with + your user privileges! To prevent this, pdnsd will exit if the config file + is writeable by others than the owner. To get rid of this message, just do + chmod go-w + on your config file (for the default file: chmod go-w /etc/pdnsd.conf). You + should also check that the ownership is set correct. + +------------------------------------------------------------------------------- + +Q: serve_aliases does not seem to work. + + Some resolvers (e.g. of the glibc 2.1) seem sometimes not to look up + unmodified names, but the names with an entry of the search path already +A: appended. Since pdnsd will serve short names with this option anyway, you + can delete the search an domain options from your /etc/resolv.conf. This is + reported to work in some cases. + +------------------------------------------------------------------------------- + +Q: Some queries for domains that have many records (e.g. www.gmx.de) fail + mysteriously. + + pdnsd versions prior to 1.1.0 had the tcp server thread disabled by + default. Most resolvers repeat their query using tcp when they receive a +A: truncated answer (the answer is truncated when it exceeds a length of 512 + bytes). You need to recompile pdnsd with the option --enable-tcp-server to + fix this. + +------------------------------------------------------------------------------- + + I am behind some kind of firewall. In the configuration file I have only +Q: listed addresses of name servers on the local (ISP's) network, but pdnsd is + slow and DNS queries frequently time out. + + In some cases pdnsd will not consider the answer of the local name server + authoritative enough, and will try to get answers from the name servers + listed in the authority section of the reply message. If pdnsd is behind a + firewall that blocks the UDP reply packets from remote name servers, pdnsd + will wait in vain for a reply. One solution is to set proxy_only=on in the +A: servers sections of the configuration file. This will prevent pdnsd from + querying name servers that are not listed in the configuration file. + Another solution that can be tried is specifying query_method=tcp_only in + the global section of the configuration file, because a firewall that + blocks UDP packets from outside might still allow outgoing TCP connections + to port 53. + +------------------------------------------------------------------------------- + +Q: Is pdnsd vulnerable to DNS cache poisoning as described in CERT + vulnerability note VU#800113? + + Short answer: Yes. + Somewhat longer answer: The problem is not so much that pdnsd's + implementation is flawed but rather that the DNS protocol currently being + used is fundamentally flawed from a security viewpoint. As long as a more + secure protocol is not in place, all that the developers of pdnsd can do is + to try to tweak the current implementation to make it as difficult as + possible for an attacker to succeed. + From version 1.2.7 onwards, the default for the query_port_start option is +A: 1024, which means that the pdnsd resolver will randomly select source ports + in the range 1024-65535. (In previous versions the default was to let the + kernel select the source ports, which will often result in a more or less + predictable sequence of ports.) It also helps to use a good quality source + of random numbers. On platforms where this is supported, it is preferable + to configure with --with-random-device=/dev/urandom. There is still more + that can be done to make pdnsd less vulnerable, but this remains (as of + this writing) a work in progress. + Please note that pdnsd was designed for small (private) networks, and that + it is generally not recommended to let untrusted users access pdnsd. + +------------------------------------------------------------------------------- + +Thomas Moestl and Paul Rombouts + + +Last revised: 18 August 2008 by Paul Rombouts + diff --git a/service/src/main/jni/pdnsd/doc/txt/intro.txt b/service/src/main/jni/pdnsd/doc/txt/intro.txt new file mode 100644 index 0000000..db1c89e --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/txt/intro.txt @@ -0,0 +1,305 @@ +------------------------------------------------------------------------------- + +About pdnsd + +pdnsd is a proxy DNS server with permanent caching (the cache contents are +written to hard disk on exit) that is designed to cope with unreachable or down +DNS servers (for example in dial-in networking). +Since version 1.1.0, pdnsd supports negative caching. + +It is licensed under the GNU General Public License (GPL). This, in short, +means that the sources are distributed togehter with the program, and that you +are free to modify the sources and redistribute them as long as you also +license them under the GPL. You do not need to pay anything for pdnsd. It also +means that there is ABSOLUTELY NO WARRANTY for pdnsd or any part of it. For +details, please read the GPL. + +pdnsd can be used with applications that do DNS lookups, e.g. on startup, and +can't be configured to change that behaviour, to prevent the often minute-long +hangs (or even crashes) that result from stalled DNS queries. Some Netscape +Navigator versions for Unix, for example, expose this behaviour. + +pdnsd is configurable via a file and supports run-time configuration using the +program pdnsd-ctl that comes with pdnsd. This allows you to set the status +flags of servers that pdnsd knows (to influence which servers pdnsd will +query), and the addition, deletion and invalidation of DNS records in pdnsd's +cache. +Parallel name server queries are supported. This is a technique that allows +querying several servers at the same time so that very slow or unavailable +servers will not block the answer for one timeout interval. +Since version 1.0.0, pdnsd has full IPv6 support. + +There is also a limited support for local zone records, intended for defining +1.0.0.127.in-addr.arpa. and localhost. , since some clients request that +information and it must be served even if the cached servers are not available +or do not serve these records. pdnsd may also read your /etc/hosts file (this +file is normally used by your local resolver and usually contains information +for localhost as well as for your machines FQDN) and serve its contents. + +pdnsd was started on Linux, and has since been ported to FreeBSD (and Cygwin +and Darwin). 90% of the source code should be easily portable to POSIX- and +BSD-compatible systems, provided that those systems support the POSIX threads +(pthreads). The rest might need OS-specific rewrites. + +Currently, pdnsd is only compileable by gcc. This should be easy to fix, but I +just do not have documentation for other compilers. If you are not able or do +not want to use gcc, I would recommend you just try to do the minor changes. + +pdnsd must be started as root in some cases (raw sockets are needed for icmp +echoes for the option uptest=ping, and the default port is 53, this must be > +1024 to allow non-root execution). However, pdnsd can be configured to change +it's user and group id to those of a non-privileged user after opening the +sockets needed for this. + +The server should support the full standard DNS queries following the rfcs 1034 +and 1035. As of version 1.0.0, the rfc compliance has been improved again, and +pdnsd is now believed (or hoped?) to be fully rfc-compatible. It completely +follows rfc 2181 (except for one minor issue in the FreeBSD port, see the +documentation). It does not support the following features, of which most are +marked optional, experimental or obsolete in these rfcs: + + * Inverse queries + * Status queries + * Completion queries + * Namespaces other than IN (Internet) + * AXFR and IXFR queries (whole zone transfers); since pdnsd does not maintain + zones, that should not violate the standard + +The following record types, that are extensions to the original DNS standard, +are supported for caching since version 1.2.9 (if you do not need most of them, +you can disable runtime support for the unneeded ones before compiling pdnsd +and save a little cache and executable space, see the source file src/ +rr_types.in): + + * RP (responsible person, RFC 1183) + * AFSDB (AFS database location, RFC 1183) + * X25 (X25 address, RFC 1183) + * ISDN (ISDN number/address, RFC 1183) + * RT (route through, RFC 1183) + * NSAP (Network Service Access Protocol address , RFC 1348) + * PX (X.400/RFC822 mapping information, RFC 1995) + * GPOS (geographic position, deprecated) + * AAAA (IPv6 address, RFC 1886) + * LOC (location, RFC 1876) + * EID (Nimrod EID) + * NIMLOC (Nimrod locator) + * SRV (service record, RFC 2782) + * ATMA (ATM address) + * NAPTR (URI mapping, RFC 2168) + * KX (key exchange, RFC 2230) + * CERT (Certificate record, RFC 4398) + * DS (Delegation Signer, RFC 4034) + * RRSIG (Resource Record Signature, RFC 4034) + * NSEC (Next Secure, RFC 4034) + * DNSKEY (record containing the public key for a zone, RFC 4034) + * NSEC3 (Next Secure version 3, RFC 5155) + * NSEC3PARAM (NSEC3 parameters, RFC 5155) + +Note: This list is incomplete. For the complete list see the source file src/ +rr_types.in. + +There are FreeBSD and OpenBSD ports available for pdnsd (ports/net/pdnsd for +both). Thanks go to Roman Shterenzon for the FreeBSD port Sebastian Stark for +the OpenBSD one! Thanks to Kiyo Kelvin Lee now also runs on the Cygwin +platform! Thanks goes to Rodney Brown for extending portability to the Darwin +(Apple Mac OS X) platform! + +If you have questions left, you should take a look into the FAQ. +Bugfixes, patches and compatability fixes for other OSs are very welcome! + +Features in detail + +This section describes some of pdnsds features in detail. Most of the options +are set in the config file. For more information on the configuration file, see +the documenation page. + + +Uptests + +pdnsd provides several methods to test whether a remote DNS server should be +regarded as available (so that pdnsd can query it), in addition to the obvious +"none" test (the server is always regarded as available, or availability is set +on or off using the pdnsd-ctl utility). These tests are: + + * ping: a given adress is ping'ed in a given interval. If it there is no + response or the host is unreachable, the server is seen to be not available + (for those who don't know: pinging is sending a certain Internet packet + type to a host to which any standard-conformant host is required to reply). + * if: a given network interface is tested whether it is existent, up and + running. If it is not, the server is regarded to be not available. This is + especially useful for ppp and similar interfaces. A special case test for + Linux isdn (ippp*) interfaces is integrated, so that the uptests should + also work for these. + * dev: this is a variant of the if uptest for use with Linux dial-on-demand + ppp interfaces. In addition to performing an if-style interface uptest, it + also tests whether a specified program (e.g. pppd) owns a lock to a given + (modem-) device. + * exec: a given shell command line is executed and the exit status of the + whole command line (which is normally the exit status of the last command) + is evaluated. If it is not zero, the server is regarded to be not + available. This is a very flexible testing method with which it should be + able to perform virtually any needed test. + * query: New in version 1.2: This works like the ping test, except it sends + an (empty) DNS query to the remote server. If the server sends a + well-formed response back within the timeout period (except SERVFAIL), it + will be regarded as available. This test is useful if a remote server does + not respond to ICMP_ECHO requests at all, which unfortunately is quite + common these days. In many cases this test will be a more reliable + indicator of availability than the ones mentioned above. + + +Local Records ("Zones") + +As mentioned above, there are only very basic local record types (ie the record +types that you may use in record declarations in your local configuration for +records that pdnsd shall serve in addion to the cached ones). They are +organized roughly in zones but have not complete zone declarations, so I +generally do not use the term "zone" for them, but rather "local records". +These are the local record types pdnsd can understand: + + * SOA (information about the name server) + * A (domain-name-to-address mapping) + * PTR (pointer, used normally for address-to-domain-name mapping) + * NS (name server, generated automatically by pdnsd for any local record set) + * CNAME (canonical host name) + * MX (mail exchange for the domain) + * TXT (arbitrary text strings, often used for Sender Policy Framework) + +You can specify these records in the configuration file. +You may "source" a file in a format like that used in the /etc/hosts file, that +means that pdnsd reads this file, extracts addresses and domain names from it +and automatically generates A records for name to address mapping, PTR records +for address to name mapping and NS records (name server specifiation) for each +entry in the file. +Records can also be changed dynamically at run time. +A script contributed by Marko Stolle makes pdnsd usable in a DHCP setup using +this feature. + +System requirements + +As mentioned, pdnsd currently runs under Linux, FreeBSD and Cygwin. Other BSD +flavours may or may not work (feedback is very welcome!). The system and +software requirements under Linux are: + + * Kernel version >2.2.0 + * glibc version >2.0.1 (aka libc6) with LinuxThreads (normally included) or + NPTL (Native Posix Thread Library, recommended). + Due to a bug, pdnsd 0.9.8 does not run with glibc2.1.1. This behaviour was + fixed in pdnsd 0.9.9. + * For IPv6: glibc>=2.1 + +The system requirements under FreeBSD are: + + * FreeBSD versions >=2.6 (prior ones may or may not work) + * For IPv6: FreeBSD >=4.0 is recommended (no idea if it runs on prior + versions) + +The common software requirements for all supported systems are: + + * GCC, preferably egcs-2.* or 3.* (other compilers are currently not + supported; the needed patch for another compiler should not be difficult, + however) + * GNU or BSD make + * the standard commands install, grep, sed, awk, touch and which (along with + the REALLY standard ones mv, cp, ln, rm, pwd, test, echo, cat, mkdir, + chown, chmod, tar). In any standard Unix installation, this should be no + problem. + * for hacking and building own packages, you might also need gzip, bzip2, + perl and rpmbuild + + +Download + +If you want to download pdnsd, please visit the download page. + +Authors + +pdnsd was originally written by Thomas Moestl, but is no longer maintained by +him. Paul A. Rombouts has revised large portions of the code and has added a +number of new features. See README.par and the ChangeLog in the source +directory (or /usr/share/doc/pdnsd- if you have installed a binary +package) for more details. If you have questions about the recent +modifications, you can find the email address of the current maintainer at the +end of README.par. + +Daniel Smolik has contributed RedHat RPMs (the most recent RPMs are available +here). +Torben Janssen contributed start scripts for Red Hat Linux. +Soenke J. Peters contributed patches and suggestions for Red Hat compatability. +Wolfgang Ocker has contributed the code and documentation for the server_ip +option. +Markus Mohr contributed a Debian rc script. +Nikita V. Youschenko contributed extensions to the "if" uptest. +Lyonel Vincent extended the serve_aliases option to support an arbitrary number +of aliases. +Sourav K. Mandal wrote the autoconf scripts and contributed many fixes and +suggestions. +Stephan Boettcher contributed the SCHEME= option. +Ron Yorston contributed the uptest for Linux ppp dial-on-demand devices. +Alexandre Nunes fixed some bugs in the autoconf files. +Sverker Wiberg contributed fixes for IPv6. +Carsten Block contributed configure-able rc scripts. +Olaf Kirch contributed a security fix for the run_as code. +Paul Wagland contributed various patches for bind9-compatability and other +issues. +Roman Shterenzon contributed patches and lots of helpful hints for FreeBSD +compatability. +Bernd Leibing has contributed spec file fixes. +Michael Wiedmann has contributed the pdnsd-ctl.8 man page. +Marko Stolle has contributed the contrib/pdnsd_update.pl script that makes +pdnsd usable in a DHCP setup. +P.J. Bostley has contributed patches to get pdnsd working on alpha properly. +Christian Engstler contributed patches for SuSE compatability. +Bjoern Fischer contributed code to make pdnsd leave the case of names in the +cache unchanged. +Marko Stolle contributed the contrib/pdnsd_update.pl script that makes pdnsd +usable in a DHCP setup. +Andrew M. Bishop contributed the support for the label server option and the +pdnsd-ctl interface for using it. +Frank Elsner contributed rc script fixes. +Andreas Steinmetz contributed the code for query_port_start and query_port_end +options. +Mahesh T. Pai contributed the pdnsd.8 man page. +Nikola Kotur contributed the Slackware start-up script. +Kiyo Kelvin Lee contributed a patch for Cygwin support. +Rodney Brown contributed a patch for Darwin (Apple Mac OS X) support. +Jan-Marek Glogowski contributed a patch implementing the use_nss option. + +Special thanks to Bert Frederiks for letting me do a late-night debugging run +on his machine to spot obscure bugs! + +Thanks to the following persons for reporting bugs and being helpful: +David G. Andersen, +Dirk Armbrust, +Daniel Black, +Kevin A. Burton, +Juliusz Chroboczek, +Joachim Dorner, +Stefan Erhardt, +Stefan F?rster, +Mike Hammer, +Jonathan Hudson, +Dan Jacobson, +Byrial Jensen, +Patrick Loschmidt, +James MacLean, +Fraser McCrossan, +Michael M?ller, +Erich Reitz, +Brian Schroeder, +Milan P. Stanic, +Michael Steiner, +Norbert Steinl, +Markus Storm, +Michael Str?der, +Alan Swanson, +Eelco Vriezekolk. + + +------------------------------------------------------------------------------- + +Thomas Moestl and Paul A. Rombouts + + +Last revised: 17 March 2012 by Paul A. Rombouts + diff --git a/service/src/main/jni/pdnsd/doc/txt/manual.txt b/service/src/main/jni/pdnsd/doc/txt/manual.txt new file mode 100644 index 0000000..c7e3e9d --- /dev/null +++ b/service/src/main/jni/pdnsd/doc/txt/manual.txt @@ -0,0 +1,2017 @@ + pdnsd Documentation + +This is the "official" pdnsd documentation and reference written by Thomas +Moestl with revisions by Paul A. Rombouts. +This manual is a part of the pdnsd package, and may be distributed in original +or modified form under terms of the GNU General Public License, as published by +the Free Software Foundation; either version 3, or (at your option) any later +version. +You can find a copy of the GNU GPL in the file COPYING in the source or +documentation directory. +This manual is up-to-date for version 1.2.9b. For older documentation, please +refer to the doc directory of the respective pdnsd package. +If you want a quicker introduction to pdnsd, you can try some of the HOWTOs +available on the web. For Apple Mac users, Brian Wells has published a good +HOWTO at http://web.mac.com/brianwells/main/pdnsd.html. + +0. Installation + +0.1 Installing binary RPM's + +To install a binary RPM, just do + +rpm -i pdnsd-.rpm + +This should install pretty much everything automatically. The only thing left +for you to do is adapt your configuration file (stored in /etc/pdnsd.conf) +according to your needs (see below). In the Red Hat and SuSE RPMs, a start +script is also installed; read the section 0.4, Start at Boot Time about that. + +0.2 Building RPM's + +It is possible to build a binary RPM from a source package using the command + +rpmbuild --rebuild pdnsd-.src.rpm + +or alternatively from a tarball using the command + +rpmbuild -tb pdnsd-.tar.gz + +You can do this as root, but it is safer to build a binary package first as a +normal user, and then, when all has gone well, install the resulting binary +package as root as in the previous section. How to build an RPM package without +being root is described at http://www.ibm.com/developerworks/linux/library/ +l-rpm1/. + +Several pdnsd-specific options are available when building RPM packages: + +--with isdn Has the same effect as --enable-isdn (see below). + +--without poll Has the same effect as --disable-poll (see below). + +--without nptl Has the same effect as --with-thread-lib=linuxthreads ( + see below). + +--with ipv6 Has the same effect as --enable-ipv6 (see below). + +--without tcpqueries Has the same effect as --disable-tcp-queries (see below + ). + +--without debug Has the same effect as --with-debug=0 (see below). + +--define "distro < Has the same effect as --with-distribution= ( +distro>" see below). + +--define "run_as_user Has the same effect as --with-default-id= (see +" below). + For RPMs the default is "pdnsd". + + If the user defined by the previous option does not +--define "run_as_uid < exist when the RPM is installed, the pre-install script +uid>" will try to create a new user with numerical id . + The default is to let the system choose the numerical + id at install time. + +--define "cachedir < Has the same effect as --with-cachedir= (see below +dir>" ). + +You can also configure which compiler flags will be used by setting the +environment variable CFLAGS. Using a bash shell, you can do that on the command +line like this: CFLAGS="-O1 -Wall" rpmbuild ... +This is useful if you prefer a different level of optimization, for instance. + +0.3 Installing from pure sources (tar archives or git repositories) + +0.3.1 Setting up the source code tree + +Source code is available in the form of snapshots (tarballs) or a git +repository with the very latest development code and a (nearly) complete +history of all the revisions. Cloning a git repository is useful if you need a +recent fix or feature that is not yet contained in a main release or you want +to participate in pdnsd development. Otherwise you will probably find the +tarballs more convenient because they are much more compact. + +0.3.1.1 Unpacking a tar archive + +The pdsnsd snapshot releases come in the form of a gzip'ed tar archive. To +decompress it (using a modern tar) do + +tar -xzf pdnsd-.tar.gz + +If your tar doesn't do this, use: + +gzip -dc pdnsd-.tar.gz | tar -xf - + +0.3.1.2 Cloning a git repository + +To clone a git repository you need to install, if not already installed, the +git version control system, which is available as a package in most modern +Linux distributions. Then run the command: + +git clone git://gitorious.org/pdnsd/pdnsd.git pdnsd + +In rare cases, if you are behind some kind of firewall, the special git +protocol can't be used and you will need to fall back to the http protocol. See +the gitorious.org website or git documentation for more information. + +0.3.2 Configuring the source + +Change into the pdnsd source directory and run configure. It takes the +following command line options (if you do not specify an option, defaults will +be used): + + Specify the prefix directory. The pdnsd files are + installed in subdirectories of the prefix, the +--prefix=dir pdnsd and pdnsd-ctl executables are for example + installed in the sbin subdirectory of the prefix. + The default for this is /usr/local; you might want + to set this to /usr (using --prefix=/usr). + + Specify the config directory. pdnsd expects its + pdnsd.conf file to reside there if the -c option is +--sysconfdir=dir not given at startup. The default for this is the + etc subdirectory of your prefix, e.g. /usr/local/ + etc if you did not specify a prefix. To set this + e.g. to /etc, use --sysconfdir=/etc. + +--with-distribution= Specify target distribution (default=Generic; +distro others: RedHat, SuSE, Debian) + See below for the effect of these settings. + + Change compilation target platform (default: + autodetect; others: Linux, BSD, Cygwin). + autodetect will attempt to detect whether you are +--with-target=platform using Linux, *BSD or Cygwin and should normally be + sufficient. If this does not work, try specifying + your system manually (for the Darwin platform + (Apple Mac OS X) specify BSD here). + + Default directory for pdnsd cache (default=/var/ +--with-cachedir=dir cache/pdnsd) + This setting can be changed via config file + settings when pdnsd has been built. + + Number of hash buckets to use (default=1024). The + default should be sufficient for most purposes, but + if you want to store a large number of names in the +--with-hash-buckets=num cache, cache lookups may be faster if the number of + hash buckets is comparable to the number of names + stored in the cache. The number actually used is + the smallest power of two greater or equal to the + number specified here. + + Enable ISDN support + This option will work only on Linux and may cause +--enable-isdn problems with 2.0.x or old 2.2.x kernels. You will + need it for a proper if uptest under Linux for ISDN + ppp devices. + +--disable-ipv4 Disable IPv4 networking support (default=enabled) + + Enable IPv6 networking support. +--enable-ipv6 If your OS does support IPv6 properly, you should + be able to serve also IPv4 queries using this. + Normally, this is disabled and you won't need it. + +--disable-ipv4-startup Disable IPv4 on pdnsd startup by default (default= + enabled) + + Enable IPV6 on pdnsd startup by default (default= + IPv4). These options are only defaults, you can +--enable-ipv6-startup specify on the command line or in the config files + which IP version will really be used. Normally, you + won't need to change these. + +--disable-udp-queries Disable UDP as query method. You shouldn't need to + change this. + + Disable TCP as query method. This only effects the + querying of name servers by pdnsd, not the ability + of pdnsd to answer TCP queries from clients. TCP + queries are slower than UDP queries, but can be +--disable-tcp-queries more secure against certain types of attacks and + are able to handle large answers. For normal use + this can be disabled. (Note that the default has + changed: TCP-query support is now compiled in by + default, but it still depends on the run-time + options whether it is actually used.) + + Specify the query method (default=udponly, others: + tcponly, tcpudp, udptcp). If you have enabled both + UDP and TCP queries, this lets you control which + query method pdnsd will use by default. tcpudp will + try TCP first and fall back to UDP if TCP is not +--with-query-method=qm supported by the server; udptcp will try UDP first + and, if the answer was truncated, will repeat the + query using TCP. udponly and tcponly should be + clear. Note that this only effects the compiled-in + default; the query method can still be changed + using command-line options or options in the + configuration file. + + Disable the TCP server. In this case pdnsd will not +--disable-tcp-server be able to respond to TCP queries from clients. + This may cause problems with very large answers. + + Disable the UDP source address discovery. + You need this only if you have trouble with +--disable-src-addr-disc messages saying "could not discover udp source + address". + For the Cygwin target, this option is disabled by + default. + +--disable-poll Disable poll(2) and use select(2) (default=enabled) + You will normally not need this. + + Since version 1.2.9 this option is obsolete and + ignored. It is now possible to configure for each +--disable-new-rrs RR type separately whether it is cacheable by pdnsd + by editing the file src/rr_types.in. The comments + in this file explain how to do this. + + Enforce strict RFC 2181 compliance. + This will cause pdnsd to reject DNS answers with +--enable-strict-rfc2181 incorrect timestamp settings (multiple RRs of the + same type and for the same domain with different + TTLs). Normally not needed. + + This option is obsolete. Since version 1.2, pdnsd +--enable-underscores places no restrictions on the types of characters + in domain names (there are still a few restrictions + for locally defined names, though). + + Specify random device; default: C Library random() + PRNG + pdnsd uses (pseudo-) random numbers as query IDs + for security reasons (this makes forging DNS + answers more difficult). This option controls where + pdnsd gets these from. The default is the C library + random() function, which is relatively weak. You + can specify a device like /dev/urandom here if you +--with-random-device= like; pdnsd will read random numbers from it +device 16-bit-wise. /dev/urandom is present under Linux + and most BSD derivates. You should not use /dev/ + random - it is more secure, but may block and delay + pdnsd's answers for a long time. + You can specify arc4random to use the BSD + arc4random() library function (default for FreeBSD + target), which is considered safe. + You can also specify random as device to use the C + Library random() function (described above). + + Specify default user for pdnsd (default=nobody). + This is the user that will be entered for the +--with-default-id=user run_as option in the config file (see below) that + will be installed during make install. You can + change this any time in your config file. + + Specify debugging level. Normally you can safely + switch debugging off by setting the level to 0. + This will increase speed (although only marginally) + and save space in the executable (only about 12kB). + However, more significant may be the savings in + stack space, especially if pdnsd is put under heavy + load and there are many simultaneous running + threads. + Presently the only defined debug levels are in the + range 0 - 9. Setting the level to 9 enables hex +--with-debug=level dumps of the queries and replies pdnsd receives and + should normally not be needed. Debug output will + only be generated if you turn on special switches; + it might be useful for debugging your config files, + so I recommend using the default (1). However, if + you use pdnsd under heavy load, a better strategy + may be to compile one version of pdnsd without + debug support (configured with --with-debug=0) for + production use, and one version with with debug + support (e.g. --with-debug=9) for diagnostic + purposes. + +--with-verbosity=level Specify default message verbosity. The default + should be ok. + + Enable RCS IDs in executables (default=disabled). +--enable-rcsids For personal use, there is no need to do this. If + you build rpm's, it might have advantages. + + Enable subsequent tcp queries. The DNS protocol + standard requires that servers must be capable of + answering multiple subsequent queries that are sent + over the same tcp connection, and that the server + may only close the connection by himself after a + certain timeout. This feature is rarely used, but +--enable-tcp-subseq may make denial-of-service attacks easier, as it + allows for an attacker to hold a connection open a + long time (although the attacker's IP is most + likely revealed then). For full standard + compliance, you should use this option. If you do + not use --enable-tcp-server, is option is not + honored. + + Specify default tcp query timeout after which the + connection is closed if no full query has been +--with-tcp-qtimeout=secs received. The default is 30s. You can also change + this option at run time using the tcp_qtimeout + config file option. If you do not use + --enable-tcp-server, is option is not honored. + + Specify the default number of queries that can be + executed in parallel. You can also change this +--with-par-queries=num option at run time using the par_queries config + file option. See the description of that option for + an explanation of what it really does. + The default for this option is 2. + + New in version 1.2.9b: Specify the maximum number + of IP addresses that can be used per nameserver + obtained from NS records (when resolving names + recursively). Just one IP address per nameserver is + sufficient in the vast majority of cases (and this +--with-max-nameserver-ips was the strategy used by pdnsd in previous +=num versions), but in rare cases this will cause + unnecessary resolve failures if the address chosen + for each nameserver happens to be unreachable while + the other addresses would lead to successful + resolution. + The default for this option is 3. + + Added by Paul Rombouts: Use this option if you + experience problems with signal handling under + Linux. The usual symptom is that pdnsd fails to + save the cache to disk, and /var/cache/pdnsd/ + pdnsd.cache remains empty. If you experience this + kind of trouble, try reconfiguring with different + values for the --with-thread-lib option. The + allowable values are linuxthreads (or lt for + short), linuxthreads2 (or lt2 for short), and nptl. + By default the configure script tries to detect +--with-thread-lib=lib automatically whether linuxthreads or nptl is more + appropriate for your system, but the method used is + not foolproof. Look for the line: checking if this + is an NPTL-based system... + If the automatic test mistakenly indentifies the + thread library on your system as NPTL based, you + should reconfigure with --with-thread-lib=lt and + recompile. If the result of the automatic test is + "no" or if --with-thread-lib=lt does not have the + desired effect, try again using --with-thread-lib= + lt2 . + +Normally, you will need only --prefix, --sysconfdir and --with-distribution. If +you specify your distribution using --with-distribution, this has the following +effects: + + * An rc script is copied in the appropriate localtion, which enables pdnsd to + start at machine boot time (see 0.4) + * Distribution-specific portions might be included in the generated + pdnsd.spec file (only important if you want to build rpm archives + yourself). + +If you choose Generic, no rc script is installed, and a generic spec file is +generated. +Further instructions are in the INSTALL document in the pdnsd source directory. +./configure --help will give you a list of all supported command line options. + +Note added by Paul Rombouts: Some people may want change the compiler +optimization flag. I use the -O2 flag, but it might be safer to use a lower +level of optimization or no optimization at all. In that case prefix the +configure command with the desired compiler flags like this (assuming you're +using a bash shell): + +CFLAGS="-O1 -Wall" ./configure ... + + +0.3.3 Building & installing + +Type make in the source directory. Should work by now. +To install, type make install or do the installation by hand (see 0.3.4). +make install will do the following ($prefix is the prefix directory; see +above): + + 1. copies pdnsd to $(prefix)/sbin/ + 2. copies pdnsd-ctl to $(prefix)/sbin/ + 3. copies docs/pdnsd.conf.sample (a sample configuration) to the pdnsd config + directory. + 4. creates your cache directory if it is not there. After installation, you + should check the file permissions and create or edit /etc/pdnsd.conf to fit + your needs (see below). If you use the run_as option, please make sure that + your cache directory is owned by the user you specified with this option! + +You must be root for this installation! +Security notes: never make the pdnsd cache directory writeable for untrusted +users, or you will get several security holes: the users might modify the cache +contents, or plant dangerous links. +If you use a pidfile, you should be aware that you introduce security problems +if you place the pidfile in a directory in a NFS filesystem that is writeable +for untrusted users. Generally, the pidfile directory (typically /var/run) +should not be writeable for untrusted users. + +0.3.4 Manual installation + +For a manual installation, you need to do the following steps: + + 1. Copy pdnsd and pdnsd-ctl from your build directory to an appropriate + location (e.g. /usr/sbin). + 2. Copy docs/pdnsd.conf into the directory you want it to reside (/etc by + default, and change it according to your needs (see below). + 3. Create your caching directory; default is /var/cache/pdnsd (you may change + this in your pdnsd.conf); Permissions should be at max rwxr-xr-x (if you + want to protect your cache and status socket, make it rwx------). + +Thats it! + +0.4 Start at boot time + +In the src/rc folder of the pdnsd distribution are start scripts for pdnsd +designed for different Linux distros. There are scripts for SuSE, Redhat, +Debian, Arch Linux and Slackware now. +The start scripts are automatically installed during RPM install, and also +during make install if you specified your distro. +For Slackware Linux there is a start-up script contributed by Nikola Kotur, but +presently it must be installed manually. See src/rc/README and src/rc/Slackware +/rc.pdnsd for details. + +0.4.1 SuSE Linux startup + +rc/SuSE/pdnsd is a start script for SuSE Linux. It was tested for 6.? but +should run on some versions below. You can do make install as root in the rc/ +SuSE directory to install it, or you can install manually: + +manual installation + +For manual installation, copy rc/SuSE/pdnsd into /sbin/init.d/, go to /sbin/ +init.d/rc2.d/ and create there the following two symlinks: +S11pdnsd to ../pdnsd (do ln -s ../pdnsd S11pdnsd in that dir) +K34pdnsd to ../pdnsd (do ln -s ../pdnsd K34pdnsd in that dir) +The numbers dictate the order different services are started and might need to +be modified. Then edit your /etc/rc.config file and add the line START_PDNSD= +yes to start pdnsd at boot time. + +If you used the make install command, START_PDNSD=yes has been appended to your +/etc/rc.config file, causing pdnsd to be started at boot time. If you don't +want that, change the yes into no. + +This start script was created from /sbin/init.d/skeleton by me, so the most is +copyrighted by SuSE. They put it under the GPL, however, so the license stated +in COPYING also applies to this script. There is NO WARRANTY OF ANY KIND on +these scripts. This is no official SuSE script, and SuSE naturally does NO +support for it. + +0.4.2 Red Hat Linux startup + +rc/Redhat/pdnsd is a start script for Red Hat Linux. It was contibuted by +Torben Janssen. +This was tested for 6.1 but should run on 5.0+. You can do make install as root +in the rc/Redhat directory to install it, or you can install manually: + +manual installation + +For manual installation, copy rc/Redhat/pdnsd into /etc/rc.d/init.d/ +Then go to /etc/rc.d/rc3.d and create there the following symlink: +S78pdnsd -> ../init.d/pdnsd (do ln -f -s ../init.d/pdnsd S78pdnsd in that dir) +Then go to /etc/rc.d/rc0.d and create there the following symlink: +K78pdnsd -> ../init.d/pdnsd (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir) +Then go to /etc/rc.d/rc6.d and create there the following symlink: +K78pdnsd -> ../init.d/pdnsd (do ln -f -s ../init.d/pdnsd K78pdnsd in that dir) + +This script is also covered by license stated in COPYING. Again, there is NO +WARRANTY OF ANY KIND on these scripts. This is no offical Redhat script, and +Redhat naturally does NO support for it + +0.5 Notes for FreeBSD users + +The special handling of ISDN ppp devices is only supported on Linux. It is not +needed in FreeBSD, the normal device handling also works fine with isdn4bsd +devices. +When compiled for FreeBSD, pdnsd as a small RFC compatability issue: RFC2181 +demands answers on dns querys to be sent with the same source address the query +packet went to. In seldom cases, this will not be the case, because the kernel +selects the source address depending on the interface that was used for sending +the answer. +Setting the source address currently does not work for IPv4. I have written a +kernel patch that will provide an easy way to program this. We'll see if or +when it gets commited. + + + +1 Invocation + +When invoking pdnsd, you can specify various options at the command line. +Command line options always override config file options. The various --noX +options are present to override config file options. + +pdnsd --help (or -h) gives you an overview of the pdnsd command line options. + +pdnsd --version (or -V for short) prints licence and version information. + +To start pdnsd as background daemon, specifiy --daemon (or -d for short) on the +command line. Diagnostic and error messages after the actual daemon start will +be printed to the syslog instead of the console. --nodaemon will disable this. + +When starting pdnsd as a daemon, the -p option may be helpful: It writes the +pid of the server process to the file of the name given as argument to this +option. +Example: pdnsd -d -p /var/run/pdnsd.pid + +If you want to specify a configuration file other than /etc/pdnsd.conf, specify +-c or --config-file on the command line, followed by a filename. + +If pdnsd was compiled with debugging support, you may specify -g or --debug on +the command line. This will cause extra diagnostic messages to be printed. When +pdnsd runs in daemon mode, the messages will be written to the pdnsd.debug file +in your cache directory. --nodebug disables debugging. + +pdnsd -vn sets the verbosity level of pdnsd. n is normally a digit from 0 to 3, +where 0 means normal operation, while 3 will most verbose. Level 9 can be used +in combination with the --debug option for very extensive debug information. +Note: The current implementation mostly ignores the verbosity level, so you may +not notice much difference between the various levels. + +The option -s or --status enables the status control socket. This is a named +socket in the cache directory called pdnsd.status. This socket allows run-time +configuration of pdnsd using the utility pdnsd-ctl. See below for more details +about pdnsd-ctl. --nostatus disables status control. See also the configuration +option status_ctl in the global section. + +The option --notcp disables the seldom needed TCP server thread, which may save +you some resources. -t or --tcp will enable it. See also the tcp_server +configuration option. + +Using the -m option, you can select the method pdnsd uses to query other name +servers. Following methods are supported (see also the query_method +configuration option): +-muo: pdnsd will use UDP only. This is the fastest method, and should be +supported by all name servers on the Internet. +-mto: pdnsd will use TCP only. TCP queries usually take longer time than UDP +queries, but are more secure against certain attacks, where an attacker tries +to guess your query id and to send forged answers. TCP queries are not +supported by some name servers. +-mtu: pdnsd will try to use TCP, and will fall back to UDP if its connection is +refused or times out. +-mut: New in version 1.2.5: pdnsd will try to use UDP, and will repeat the +query using TCP if the UDP reply was truncated (i.e. the tc bit is set). This +is the behaviour recommended by the DNS standards. + +The -4 option switches to IPv4 mode, providing pdnsd was compiled with IPv4 +support. +The -6 option switches to IPv6 mode, providing pdnsd was compiled with IPv6 +support. +The -a option is only available when pdnsd was compiled with both IPv4 and IPv6 +support. With this option, pdnsd will try to detect automatically if a system +supports IPv6, and fall back to IPv4 otherwise. + +With -i prefix or --ipv4_6_prefix=prefix you can set the prefix pdnsd uses +(when running in IPv6 mode) to map IPv4 addresses in the configuration file to +IPv6 addresses. There is also a corresponding option for the config file, see +below. Must be a valid IPv6 address. The default is ::ffff:0.0.0.0 + +2 The configuration file + +This section describes the layout of the configuration file and the available +configuration options. The default location of the file is /etc/pdnsd.conf. +This may be changed with the -c command line option. An example pdnsd.conf +comes with the pdnsd distribution in the docs directory and will be installed +to /etc/ by make install. + +2.1 Layout + +The configuration file is divided into sections. Each section is prefixed with +the section name and opening curlies ({) and closed with closing curlies (}). +In each section, configuration options can be given in the form +option_name=option_value; +Option value may be a string literal, a number, a time specification or a +constant. In previous versions of pdnsd strings had to be enclosed in quotes +("), but since version 1.1.10 this is no longer necessary, unless a string +contains a special character such as whitespace, a token that normally starts a +comment, or one of ",;{}\". Since version 1.2.9 a backslash (\) inside a string +is interpreted as an escape character, so it is possible to include special +characters in strings (both quoted or unquoted) by preceding them with a +backslash. Some escape sequences are in interpreted as in the C programming +language, e.g. \t becomes a tab, \n becomes a new-line control char. +A time specification consists a sequence of digits followed by a one-letter +suffix. The following suffixes are recognized: s (seconds), m (minutes), h +(hours), d (days) and w (weeks). If the suffix is missing, seconds are assumed. +If several time specifications are concatenated, their values are added +together; e.g. 2h30m is interpreted as 2*60*60 + 30*60 = 9000 seconds. +Some options take more than one value; in this case, the values are separated +with commas. +If you may supply one of a set of possible values to an option, this is noted +in the documentation as (option1|option2|option3|...) +The constants true|false and yes|no are accepted as synonyms for the constants +on|off. +Comments may be enclosed in /* and */, nested comments are possible. If the # +sign or two slashes (//) appear in the configuration file, everything from +these signs to the end of the current line is regarded as a comment and +ignored. +There are examples for nearly all options in the sample config file. + +2.1.1 global Section + +The global section specifies parameters that affect the overall behaviour of +the server. If you specify multiple global sections, the settings of those +later in the file will overwrite the earlier given values. +These are the possible options: + + +perm_cache=(number|off); +Switch the disk cache off or supply a maximum cache size in kB. If the disk +cache is switched off, 8 bytes will still be written to disk. The memory cache +is always 10kB larger than the file cache. This value is 2048 (2 MB) by +default. + +cache_dir=string; +Set the directory you want to keep the cache in. The default is "/var/cache/ +pdnsd" (unless pdnsd was compiled with a different default). + +server_port=number; +Set the server port. This is especially useful when you want to start the +server and are not root. Note that you may also not specify uptest=ping in the +server section as non-root. +The default port is 53, the RFC-standard one. Note that you should only use +non-standard ports when you only need clients on your machine to communicate +with the server; others will probably fail if the try to contact the server on +the basis of an NS record, since the A record that supplies the address for +(among others) name servers does not have a port number specification. + +server_ip=string; +or +interface=string; +Set the IP address pdnsd listens on for requests. This can be useful when the +host has several interfaces and you want pdnsd not to listen on all interfaces. +For example, it is possible to bind pdnsd to listen on 127.0.0.2 to allow pdnsd +to be a forwarder for BIND. The default setting for this option is server_ip= +any, which means that pdnsd will listen on all of your local interfaces. +Presently you can only specify one address here; if you want pdnsd to listen on +multiple interfaces but not all you will have to specify server_ip=any and use +firewall rules to restrict access. +The IP address used to need quotation marks around it, but since version 1.1.10 +this is no longer necessary. +If pdnsd has been compiled with both IPv4 and IPv6 support, and you want to +specify an IPv6 address here, then unless pdnsd was compiled to start up in +IPv6 mode by default, you will need to use the -6 command-line option or set +run_ipv4=off first (see below) in order to ensure that the IPv6 address is +parsed correctly. +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, it will +automatically be mapped to an IPv6 address. +New in version 1.2: You may also give the name of an interface such as "lo" or +"eth0" here, instead of an IP address (this has been tested on Linux, and may +or may not work on other platforms). pdnsd will not bind to the interface name, +but will look up the address of the interface at start-up and listen on that +address. If the address of the interface changes while pdnsd is running, pdnsd +will not notice that. You will need to restart pdnsd in that case. + +outgoing_ip=string; +or +outside_interface=string; +New in version 1.2.9: Set the IP address of the interface used by pdnsd for +outgoing queries. This can be useful when the host has several interfaces and +you want pdnsd to send outgoing queries via only one of them. For example, if +pdnsd is running on a host with one interface with IP address 192.168.1.1 +connected to the local network, and another with IP address 123.xxx.yyy.zzz +connected to the internet, you may specify server_ip=192.168.1.1 and +outgoing_ip=123.xxx.yyy.zzz to enforce that pdnsd only responds to queries +received from the local network, and only sends outgoing queries via the +interface connected to the internet. +The default setting for this option is any, which means that the kernel is free +to decide which interface to use. Like with the server_ip option, you may also +give the name of an interface here, instead of an IP address. + +linkdown_kluge=(on|off); +This option enables a kluge that some people might need: when all servers are +marked down, with this option set the cache is not even used when a query is +received, and a DNS error is returned in any case. The only exception from this +is that local records (as specified in rr and source sections are still served +normally. In general, you probably want to get cached entries even when the +network is down, so this defaults to off. + +max_ttl=timespec; +This option sets the maximum time a record is held in cache. All dns resource +records have a time to live field that says for what period of time the record +may be cached before it needs to be requeried. If this is more than the value +given with max_ttl, this time to live value is set to max_ttl. This is done to +prevent records from being cached an inappropriate long period of time, because +that is almost never a good thing to do. Default is 604800s (one week). + +min_ttl=timespec; +This option sets the minimum time a record is held in cache. All dns resource +records have a time to live field that says for what period of time the record +may be cached before it needs to be requeried. If this is less than the value +given with min_ttl, this time to live value is set to min_ttl. Default is 120 +seconds. + +neg_ttl=timespec; +This option sets the time that negatively cached records will remain valid in +the cache if no time to live can be determined. This is always the case when +whole domains are being cached negatively, and additionally when record types +are cached negatively for a domain for which no SOA record is known to pdnsd. +If a SOA is present, the ttl of the SOA is taken. + +neg_rrs_pol=(on|off|auth|default); +This sets the RR set policy for negative caching; this tells pdnsd under which +circumstances it should cache a record type negatively for a certain domain. +off will turn the negative caching of record types off, on will always add a +negative cache entry when a name server did not return a record type we asked +it for, and auth will only add such entries if the answer came from an +authoritative name server for that domain. +New in version 1.2.8: The default setting will add a negatively cached record +if either the answer was authoritive or the answer indicated the name server +had "recursion available" while the query explicitly requested such recursion. +The preset is "default" (used to be auth). + +neg_domain_pol=(on|off|auth); +This is analogue to neg_rrs_pol for whole domain negative caching. It should be +safe to set this on, because I have not seen a caching server that will falsely +claim that a domain does not exist. +The default is auth. + +run_as=string; +This option allows you to let pdnsd change its user and group id after +operations that needed privileges have been done. This helps minimize security +risks and is therefore recommended. The supplied string gives a user name whose +user id and primary group id are taken. +A little more details: after reading the config file, becoming a daemon (if +specified) and starting the server status thread, the main thread changes its +gid and uid, as do all newly created threads thereafter. By taking another uid +and gid, those threads run with the privileges of the specified user. Under +Linux and FreeBSD, the server status thread runs with the original privileges +only when the strict_setuid option is set to off (see below, on by default), +because these may be needed for exec uptests. The manager thread also retains +its original privileges in this case. You should take care that the user you +specify has write permissions on your cache file and status pipe (if you need a +status pipe). You should look out for error messages like "permission denied" +and "operation not permitted" to discover permission problems. + +strict_setuid=(on|off); +When used together with the run_as option, this option lets you specify that +all threads of the program will run with the privileges of the run_as user. +This provides higher security than the normal run_as option, but is not always +possible. See the run_as option for further discussion. +This option is on by default. +Note that this option has no effect on Non-Linux systems. + +paranoid=(on|off); +Normally, pdnsd queries all servers in recursive mode (i.e. instructs servers +to query other servers themselves if possible, and to give back answers for +domains that may not be in its authority), and accepts additional records with +information for servers that are not in the authority of the queried server. +This opens the possibility of so-called cache poisoning: a malicious attacker +might set up a dns server that, when queried, returns forged additional +records. This way, he might replace trusted servers with his own ones by making +your dns server return bad IP addresses. This option protects you from cache +poisoning by rejecting additional records that do not describe domains in the +queried servers authority space and not doing recursive queries any more. An +exception to this rule are the servers you specify in your config file, which +are trusted. +The penalty is a possible performance decrease, in particular, more queries +might be necessary for the same operation. +You should also notice that there may be other similar security problems, which +are essentially problems of the DNS, i.e. any "traditional" server has them +(the DNS security extensions solve these problems, but are not widely +supported). One of this vulnerabilities is that an attacker may bombard you +with forged answers in hopes that one may match a query you have done. If you +have done such a query, one in 65536 forged packets will be succesful (i.e. an +average packet count of 32768 is needed for that attack). pdnsd can use TCP for +queries, which has a slightly higher overhead, but is much less vulnerable to +such attacks on sane operating systems. Also, pdnsd chooses random query ids, +so that an attacker cannot take a shortcut. If the attacker is able to listen +to your network traffic, this attack is relatively easy, though. +This vulnerability is not pdnsd's fault, and is possible using any conventional +name server (pdnsd is perhaps a little more secured against this type of +attacks if you make it use TCP). +The paranoid option is off by default. + +ignore_cd=(on|off); +New in version 1.2.8: This option lets you specify that the CD bit of a DNS +query will be ignored. Otherwise pdnsd will reply FORMERR to clients that set +this bit in a query. It is safe to enable this option, as the CD bit refers to +'Checking Disabled' which means that the client will accept non-authenticated +data. +This option is on by default. Turn it off if you want the old behaviour (before +version 1.2.8). + +scheme_file=string; +In addition to normal uptests, you may specify that some servers shall only be +queried when a certain pcmcia-cs scheme is active (only under linux). For that, +pdnsd needs to know where the file resides that holds the pcmcia scheme +information. Normally, this is either /var/lib/pcmcia/scheme or /var/state/ +pcmcia/scheme. + +status_ctl=(on|off); +This has the same effect as the -s command line option: the status control is +enabled when on is specified. +Added by Paul Rombouts: Note that pdnsd-ctl allows run-time configuration of +pdnsd, even the IP addesses of the name servers can be changed. If you're not +using pdnsd-ctl and you want maximum security, you should not enable this +option. It is disabled by default. + +daemon=(on|off); +This has the same effect as the -d command line option: the daemon mode is +enabled when on is specified. +Default is off. + +tcp_server=(on|off); +tcp_server=on has the same effect as the -t or --tcp command-line option: it +enables TCP serving. Similarly, tcp_server=off is like the --notcp command-line +option. +Default is on. + +pid_file=string; +This has the same effect as the -p command line option: you can specify a file +that pdnsd will write its pid into when it starts in daemon mode. + +verbosity=number; +This has the same effect as the -v command line option: you can set the +verbosity of pdnsd's messages with it. The argument is a number between 0 (few +messages) to 3 (most messages). + +query_method=(tcp_only|udp_only|tcp_udp|udp_tcp); +This has the same effect as the -m command line option. Read the documentation +for the command line option on this. tcp_only corresponds to the to, udp_only +to the uo, tcp_udp to the tu and udp_tcp to the ut argument of the command line +option. +If you use query_method=tcp_udp, it is recommended that you also set the global +timeout option to at least twice the longest server timeout. + +run_ipv4=(on|off); +This has the same effect as the -4 or -6 command line option: if on is +specified, IPv4 support is enabled, and IPv6 support is disabled (if +available). If off is specified, IPv4 will be disabled and IPv6 will be +enabled. For this option to be meaningful, pdnsd needs to be compiled with +support for the protocol you choose. If pdnsd was compiled with both IPv4 and +IPv6 support, and you want to include IPv6 addresses in the configuration file, +you will probably need to specify run_ipv4=off first to ensure that the IPv6 +addresses are parsed correctly. + +debug=(on|off); +This has the same effect as the -g command line option: the debugging messages +are enabled when on is specified. + +ctl_perms=number; +This option allows you to set the file permissions that the pdnsd status +control socket will have. These are the same as file permissions. The owner of +the file will be the run_as user, or, if none is specified, the user who +started pdnsd. If you want to specify the permissions in octal (as usual), +don't forget the leading zero (0600 instead of 600!). To use the status +control, write access is needed. The default is 0600 (only the owner may read +or write). +Please note that the socket is kept in the cache directory, and that the cache +directory permissions might also need to be adjusted. Please ensure that the +cache directory is not writeable for untrusted users. + +proc_limit=number; +With this option, you can set a limit on the pdnsd threads that will be active +simultaneously. If this number is exceeded, queries are queued and may be +delayed some time. See also the procq_limit option. +The default for this option is 40. + +procq_limit=number; +When the query thread limit proc_limit is exceeded, connection attempts to +pdnsd will be queued. With this option, you can set the maximum queue length. +If this length is also exceeded, the incoming queries will be dropped. That +means that tcp connections will be closed and udp queries will just be dropped, +which will probably cause the querying resolver to wait for an answer until it +times out. +See also the proc_limit option. A maximum of proc_limit+procq_limit query +threads will exist at any one time (plus 3 to 6 threads that will always be +present depending on your configuration). +The default for this option is 60. + +tcp_qtimeout=timespec; +This option sets a timeout for tcp queries. If no full query has been received +on a tcp connection after that time has passed, the connection will be closed. +The default is set using the --with-tcp-qtimeout option to configure. + +par_queries=number; +This option used to set the maximum number of remote servers that would be +queried simultaneously, for every query that pdnsd receives. +Since version 1.1.11, the meaning of this option has changed slightly. It is +now the increment with which the number of parallel queries is increased when +the previous set of servers has timed out. For example, if we have a list +server1, server2, server3, etc. of available servers and par_queries=2, then +pdnsd will first send queries to server1 and server2, and listen for responses +from these servers. +If these servers do not send a reply within their timeout period, pdnsd will +send additional queries to server3 and server4, and listen for responses from +server1, server2, server3 and server4, and so on until a useful reply is +received or the list is exhausted. +In the worst case there will be pending queries to all the servers in the list +of available servers. We may be using more system resources this way (but only +if the first servers in the list are slow or unresponsive), but the advantage +is that we have a greater chance of catching a reply. After all, if we wait +longer anyway, why not for more servers. +See also the explanation of the global timeout option below. +1 or 2 are good values for this option. The default is set at compile time +using the --with-par-queries option to configure. + +timeout=timespec; +This is the global timeout parameter for dns queries. This specifies the +minimum period of time pdnsd will wait after sending the first query to a +remote server before giving up without having received a reply. The timeout +options in the configuration file are now only minimum timeout intervals. +Setting the global timeout option makes it possible to specify quite short +timeout intervals in the server sections (see below). This will have the effect +that pdnsd will start querying additional servers fairly quickly if the first +servers are slow to respond (but will still continue to listen for responses +from the first ones). This may allow pdnsd to get an answer more quickly in +certain situations. +If you use query_method=tcp_udp it is recommended that you make the global +timeout at least twice as large as the largest server timeout, otherwise pdnsd +may not have time to try a UDP query if a TCP connection times out. +Default value is 0. + +randomize_recs=(on|off); +If this option is turned on, pdnsd will randomly reorder the cached records of +one type when creating an answer. This supports round-robin DNS schemes and +increases fail safety for hosts with multiple IP addresses, so this is usually +a good idea. +On by default. + +query_port_start=(number|none); +If a number is given, this defines the start of the port range used for queries +of pdnsd. The value given must be >= 1024. The purpose of this option is to aid +certain firewall configurations that are based on the source port. Please keep +in mind that another application may bind a port in that range, so a stateful +firewall using target port and/or process uid may be more effective. In case a +query start port is given pdnsd uses this port as the first port of a specified +port range (see query_port_end) used for queries. pdnsd will try to randomly +select a free port from this range as local port for the query. +To ensure that there are enough ports for pdnsd to use, the range between +query_port_start and query_port_end should be adjusted to at least (par_queries +* proc_limit). A larger range is highly recommended for security reasons, and +also because other applications may allocate ports in that range. If possible, +this range should be kept out of the space that other applications usually use. +The default for this option is 1024. Together with the default value of +query_port_end, this makes it the hardest for an attacker to guess the source +port used by the pdnsd resolver. If you specify none here, pdnsd will let the +kernel choose the source port, but this may leave pdnsd more vulnerable to an +attack. + +query_port_end=number; +Used if query_port_start is not none. Defines the last port of the range +started by query_port_start used for querys by pdnsd. The default is 65535, +which is also the maximum legal value for this option. For details see the +description of query_port_start. + +delegation_only=string; +Added by Paul Rombouts: This option specifies a "delegation-only" zone. This +means that if pdnsd receives a query for a name that is in a subdomain of a +"delegation-only" zone but the remote name server returns an answer with an +authority section lacking any NS RRs for subdomains of that zone, pdnsd will +answer NXDOMAIN (unknown domain). This feature can be used for undoing the +undesired effects of DNS "wildcards". Several "delegation-only" zones may be +specified together. If you specify root servers in a server section it is +important that you set root_server=on in such a section. +Example: + +delegation_only="com","net"; + +This feature is off by default. It is recommended that you only use this +feature if you actually need it, because there is a risk that some legitimate +names will be blocked, especially if the remote name servers queried by pdnsd +return answers with empty authority sections. + +ipv4_6_prefix=string; +This option has the same effect as the -i command-line option. When pdnsd runs +in IPv6 mode, this option specifies the prefix pdnsd uses to convert IPv4 +addresses in the configuration file (or addresses specified with pdnsd-ctl) to +IPv6-mapped addresses. The string must be a valid IPv6 address. Only the first +96 bits are used. Note that this only effects the parsing of IPv4 addresses +listed after this option. +The default is "::ffff.0.0.0.0". + +use_nss=(on|off); +If this option is turned on, pdnsd will call initgroups() to set up the group +access list, whenever pdnsd changes its user and group id (see run_as option). +There is a possible snag, though, if initgroups() uses NSS (Name Service +Switch) and NSS in turn uses DNS. In such a case you may experience lengthy +timeouts and stalls. By setting use_nss=off, you can disable the initgroups() +call (only possible in versions 1.2.5 and later). +This option was contributed by Jan-Marek Glogowski. +On by default. + +udpbufsize=number; +New in version 1.2.9: This option sets the upper limit on the size of UDP DNS +messages. The default is 1024. +See also the edns_query server option below. + + +2.1.2 server Section + +Each server section specifies a set of name servers that pdnsd should try to +get resource records or authoritative name server information from. The servers +are queried in the order of their appearance (or parallel to a limited extend). +If one fails, the next one is taken and so on. +You probably want to specify the dns server in your LAN, the caching dns +servers of your internet provider or even a list of root servers in one or more +server sections. +The supported options in this section are: + + +label=string; +Specify a label for the server section. This can be used to refer to this +section when using pdnsd-ctl, the pdnsd control utility. +You can give several server sections the same label, but if you want to change +the addresses of a server section (see ip option below) during run-time with +"pdnsd-ctl server label up dns1,dns2,...", the label must be unique. + +ip=string; +Give the IP (the address, not the host name) of the server. +Multiple IP addresses can be given per server section. This can be done by +entering multiple lines of the form ip=string; or a single line like this: + +ip=string,string,string; + +IP addresses do not have to be specified in the configuration file. A server +section without IP addresses will remain inactive until it is assigned one or +more addresses with pdnsd-ctl, the pdnsd control utility. +If pdnsd has been compiled with both IPv4 and IPv6 support, any IPv6 addresses +you specify here will be skipped with a warning message, unless pdnsd is +running in IPv6 mode. Thus, unless pdnsd was compiled to startup in IPv6 mode +by default, you need to use the command-line option -6 or set run_ipv4=off +first (see global section) in order to ensure that IPv6 addresses are parsed +correctly. +If pdnsd is running in IPv6 mode and you specify an IPv4 address here, it will +automatically be mapped to an IPv6 address. + +file=string; +New in version 1.2: This option allows you to give the name of a +resolv.conf-style file. Of the lines beginning with the nameserver keyword, the +second field will be parsed as an IP address, as if it were specified with the +ip= option. The remaining lines will be ignored. If the contents of the file +changes while pdnsd is running, you can make pdnsd aware of the changes through +the use of pdnsd-ctl, the pdnsd control utility. This is usually most +conveniently done by placing the command "pdnsd-ctl config" in a script that is +automatically run whenever the DNS configuration changes. +For example, suppose you have a ppp client that writes the DNS configuration +for your ISP to the file /etc/ppp/resolv.conf and runs the script /etc/ppp/ +ip-up when a new connection is established. One way of ensuring that pdnsd is +automatically reconfigured is to add a server section in the config file with +file=/etc/ppp/resolv.conf and to add the command "pdnsd-ctl config" to /etc/ppp +/ip-up. + +port=number; +Give the port the remote name server listens on. Default is 53 (the official +dns port) + +uptest=(ping|none|if|dev|diald|exec|query); +Determine the method to check whether the server is available. Currently +defined methods are: + + * ping: Send an ICMP_ECHO request to the server. If it doesn't respond within + the timeout, it is regarded to be unavailable until the next probe. + * none: The availability status is not changed, only the time stamp is + updated. + * if: Check whether the interface (specified in the interface= option) is + existent, up and running. This currently works for all "ordinary" network + interfaces, interfaces that disappear when down (e.g. ppp?), and + additionally for Linux isdn interfaces (as of kernel 2.2). Note that you + need a /dev/isdninfo device file (major#45, minor#255), or the isdn uptest + will always fail. + * dev and diald: Perform an if uptest, and, if that was succesful, + additionally check whether a program is running that has locked a given + (modem-) device. The needed parameters are an interface (specified as for + the if uptest, e.g. "ppp0") and a device relative to /dev (e.g. "modem" for + /dev/modem specified using the device= option. pdnsd will then look for a + pid file for the given interface in /var/lock (e.g. /var/run/ppp0.pid) and + for a lockfile for the given device (e.g. /var/lock/LCK..modem), and then + test whether the locking process is the process that created the pid file + and this process is still alive. If this is the case, the normal if uptest + is executed for the given interface. + The dev option is for pppd dial-on-demand, diald is the same for diald + users. + * exec: Executes a given command in the /bin/sh shell (as /bin/sh -c + ) and evaluates the result (the return code of the last command) + in the shell's way of handling return codes, i.e. 0 indicates success, all + other indicate failure. The shell's process name will be uptest_sh. The + command is given with the uptest_cmd option (see below). For secuity + issues, also see that entry. + * query: New in version 1.2: This works like the ping test, except it sends + an (empty) DNS query to the remote server. If the server sends a + well-formed response back within the timeout period (except SERVFAIL), it + will be regarded as available. This test is useful if a remote server does + not respond to ICMP_ECHO requests at all, which unfortunately is quite + common these days. It can also happen that a remote server is online but + ignores empty DNS queries. Then you will need the set the query_test_name + option (see below). In many cases this test will be a more reliable + indicator of availability than the ones mentioned before. + +The default value is none. + +NOTE: If you use on-demand dialing, use none, if, dev, diald or exec, since +ping or query will send packets in the specified interval and the interface +will thus frequently dial! + +ping_timeout=number; +Sets the timeout for the ping test in tenths of seconds (this unit is used for +legacy reasons; actually the current implementation is only accurate to a +second). +The default is 600 (one minute). + +ping_ip=string; +The IP address for the ping test. The default is the IP of the name server. + +query_test_name=string; +New in version 1.2.9: Sets the name to be queried when using uptest=query +availability test. If the string is the unquoted constant none, an empty query +is used (this the default), otherwise a query of type A will be sent for the +domain name specified here. It is not necessary for the domain name to exist or +have a record of type A in order for the uptest to succeed. +If the the remote server ignores empty queries, you will probably want to set +query_test_name="." (the root domain). + +uptest_cmd=string,string; +or +uptest_cmd=string; +Sets the command for the uptest=exec function to the first string. If the +second string is given, it specifies a user with whose user id and primary +group id the command is executed. +This is especially useful if you are executing the server as root, but do not +want the uptest to be performed with root privileges. In fact, you should never +execute the uptest as root if you can help it. +If the server is running setuid or setgid, the privileges thus gained are +attempted to be dropped even before changing identity to the specified user to +prevent setuid/gid security holes (otherwise, any user might execute commands +as root if you setuid the executable). +Note that this is not always possible, and that pdnsd should never be installed +as setuid or setgid. The command is executed using /bin/sh, so you should be +able to use shell builtin commands. + +interval=(timespec|onquery|ontimeout); +Sets the interval for the server up-test. The default is 900 seconds; however, +a test is forced when a query times out and the timestamp is reset then. +If you specify onquery instead of a timeout, the interface will be tested +before every query. This is to prevent automatically dialing interfaces (diald/ +pppd or ippp) to dial on dns queries. It is intended to be used in connection +with an interface-testing uptest ;-) +Note that using uptest=exec, you might run into performance problems on slow +machines when you use that option. DON'T use onquery with uptest=ping or uptest +=query, as it may cause delays if the server does not answer (btw, it doesn't +make sense anyway). Note also that using onquery is no guarantee that the +interface will not be used. When another (reachable) dns server tells pdnsd to +query a third dns server for data, pdnsd will do that and has no means of +checking whether this will dial up the interface or not. This however should be +a rare situation. +New in version 1.2.3: A third possibility is to specify interval=ontimeout. In +this case the server is not tested at startup/reconfiguration, nor at regular +intervals, but only after a DNS query to a server times out. Certain types of +network problems such as a refused connection will also cause the server to be +considered unavailable. However, once a server is declared dead it is never +considered again unless it is revived using a pdnsd-ctl config or server +command. The idea behind this option is to minimize uptests by assuming all +servers are available until there is reason to believe otherwise. + +interface=string; +The network interface (or network device, e.g. "eth0") for the uptest=if +option. Must be specified if uptest=if is given. + +device=string; +The (modem-) device that is used for the dev uptest. If you use this for a +dial-on-demand ppp uptest (together with uptest=dev), you need to enter the +device you are using for your pppd here, e.g. modem for /dev/modem. +Must be specified if uptest=dev is given. + +timeout=timespec; +Set the timeout for the dns query. The default is 120 seconds. You probably +want to set this lower. +Timeouts specified in the configuration file are only treated as the minimum +period of time to wait for a reply. A queries to a remote server are not +canceled until a useful reply has been received, or all the other queries have +timed out or failed. +If you have also set the global timeout option, you may consider setting a +fairly small value here. See the explanation of the timeout option in the +global section for what that means. + +purge_cache=(on|off); +In every fetched dns record, there is a cache timeout given, which specifies +how long the fetched data may be cached until it needs to be reloaded. If +purge_cache is set to off, the stale records are not purged (unless the cache +size would be exceeded, in this case the oldest records are purged). Instead, +they are still served if they cannot succesfully be updated (e.g. because all +servers are down). +Default is off. + +caching=(on|off); +Specifies if caching shall be performed for this server at all. Default is on. + +lean_query=(on|off); +Specifies whether to use the "lean" query mode. In this mode, only the +information actually queried from pdnsd is resolved and cached. This has the +advantage that usually less cache space is used and the query is usually +faster. In 90% of the cases, only address (A) records are needed anyway. If +switched off, pdnsd will always cache all data about a host it can find and +will specifically ask for all available records (well, at least it is a good +approximation for what it really does ;-) This will of course increase the +answer packet sizes. +Some buggy name servers may not deliver CNAME records when not asked for all +records. I do not know if such servers are around, but if you have trouble +resolving certain host names, try turning this option off. +A last note: If you use multiple pdnsd's that access each other, turning this +option on is probably a big win. +This on by default. + +edns_query=(on|off); +New in version 1.2.9: Specifies whether to use EDNS (Extension mechanisms for +DNS) for outgoing queries. Currently this is only useful for allowing UDP +message sizes larger than 512 bytes. Note that setting this option on can give +problems in combination with some legacy systems or software, including, +embarrassingly enough, previous versions of pdnsd. +The default is off, but if your network can handle UDP payloads significantly +larger than 512 bytes, the recommended value is on. +Note that this option only effects outgoing queries. If pdnsd receives a query +using EDNS, it will reply using EDNS regardless of the value of this option. +See also the udpbufsize option above. + +scheme=string; +You can specify a pcmcia-cs scheme that is used in addition to the uptests. If +you specify a scheme here, the server this section is for will only be queries +if the given scheme is active. Shell wildcards (* and ?) are allowed in the +string under their special meanings. You need to use the scheme_file option on +the global section to make this option work. + +preset=(on|off); +This allows you to specify the initial state of a server before any uptest is +performed. on specifies that the server is regarded available. The default is +on. This is especially useful when you set uptest=none; and want to change the +status of a server only via pdnsd-ctl. + +proxy_only=(on|off); +When this option is set to on, answers given by the servers are always +accepted, and no other servers (as, for example, specified in the NS records of +the query domain) are queried. If you do not turn this option on, pdnsd will do +such queries in some cases (in particular when processing ANY queries). +This option is useful when you do not want pdnsd to make connections to outside +servers for some reasons (e.g. when a firewall is blocking such queries). +I recommend that you turn on lean_query when using this option. +Default is off. + +root_server=(on|off|discover); +Set this option to on if the servers specified in a section are root servers. A +root server will typically only give the name servers for the top-level domain +in its reply. Setting root_server=on will cause pdnsd to try to use cached +information about top-level domains to reduce to number of queries to root +servers, making the resolving of new names more efficient. You can get a list +of available root servers by running the command "dig . ns". +This option is also necessary if you use the delegation_only option. +New in version 1.2.8: This option may also be set to "discover". This will +cause pdnsd to query the servers provided with the ip= option to obtain the +full list of root servers. The root-server addresses will replace the addresses +specified with the ip= option. This will only be done once on startup, or after +a "pdnsd-ctl config" command. In this case the name servers specified with the +ip= option don't have to be root servers, they just have to know the names and +addresses of the root servers. After root-server discovery pdnsd will behave +just as if root_server=on had been specified. +Default is off. + +randomize_servers=(on|off); +New in version 1.2.6: Set this option to on to give each name server in this +section an equal chance of being queried. If this option is off, the name +servers are always queried starting with the first one specified. Even with +this option on, the query order is not truly random. Only the first server is +selected randomly; the following ones are queried in consecutive order, +wrapping around to the beginning of the list when the end is reached. Note that +this option only effects the order within a section. The servers in the first +(active) section are always queried before those in the second one, etc. +The default is off, but if you are resolving from root servers setting this +option on is highly recommended. If root_server=on this option also effects the +query order of the name servers for the top-level domains. + +reject=string; +New in version 1.2.6: This option can be used to make pdnsd reject replies that +contain certain IP addresses. You can specify a single IP address, which will +be matched exactly, or a range of addresses using an address/mask pair. The +mask can be specified as a simple integer, indicating the number of initial 1 +bits in the mask, or in the usual IP address notation. IP addresses may be +either IPv4 or IPv6 (provided there is sufficient support in the C libraries +and support for AAAA records was not disabled). When addresses in the reject +list are compared with those in a reply, only the bits corresponding to those +set in the netmask are significant, the rest are ignored. +Multiple addresses or address/mask pairs may be specified; this can be done by +entering multiple lines of the form reject=string; or a single line like this: + +reject=string,string,string; + +How pdnsd reacts when an address in the reply matches one in the reject list, +depends on the reject_policy option, see below. + +reject_policy=(fail|negate); +New in version 1.2.6: This option determines what pdnsd does when an address in +the reply from a name server matches the reject list (see above). If this +option is set to fail, pdnsd will try another server, or, if there no more +servers to try, return the answer SERVFAIL. If this option is set to negate, +pdnsd will immediately return the answer NXDOMAIN (unknown domain) without +querying additional servers. The fail setting is useful if you don't always +trust the servers in this section, but do trust the servers in the following +section. The negate setting can be used to completely censor certain IP +addresses. In this case you should put the same reject list in every server +section, and also set the reject_recursively option (see below) to true. +The default is fail. + +reject_recursively=(on|off); +New in version 1.2.6: Normally pdnsd checks for addresses in the reject list +(see above) only when the reply comes directly from a name server listed in the +configuration file. With this option set to on, pdnsd will also do this check +for name servers that where obtained from NS records in the authority section +of a previous reply (which was incomplete and non-authoritative). +Default is off. + +policy=(included|excluded|simple_only|fqdn_only); +pdnsd supports inclusion/exclusion lists for server sections: with include= and +exclude= (see below) you can specify domain names for which this server will be +used or will not be used. The first match counts (i.e., the first include or +exclude rule in a server section that matches a domain name is applied, and the +search for other rules is terminated). If no rule matched a given domain name, +the policy= option determines whether this server is used for the lookup for +that domain name; when included is given, the server will be asked, and when +excluded is given, it will not. If simple_only is given the server will be used +if the name to lookup is a simple (single-label) domain name, on the other hand +if fqdn_only is given the server will be used only for names consisting of two +or more labels (i.e. the name has at least one dot in-between). +If no server is available for a queried domain, pdnsd will return an error +message to the client that usually will stop the client's attempts to resolve a +specific domain from this server (the libc resolver will e.g. return an error +to the application that tried to resolve the domain if no other servers are +available in the resolv.conf). This may be of use sometimes. +Note: the simple_only and fqdn_only constants were added by Paul Rombouts. They +are useful for controlling which name servers (if any) will be used by pdnsd +for resolving simple (single-label) host names. fqdn_only used to stand for +"fully qualified domain name only", but this is actually a misnomer. The names +in queries received by pdnsd are always considered to be fully qualified. If +you do not exactly understand what the options simple_only and fqdn_only are +good for, you are probably better off not using them. +The default for this option is included. + +include=string; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is queried if this was the first matching +rule (see also the entry for policy). +If the given name starts with a dot, the whole subdomain of the given name +including the one of that name is matched, e.g. ".foo.bar." will match the +domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +If it does not start in a dot, only exactly the given name (ignoring the case, +of course) will be matched (hint: if you want to include all subdomains, but +not the domain of the given name itself, place an exact-match exclude rule +before the include rule, e.g: exclude="foo.bar."; include=".foo.bar."; +Previous versions of pdnsd required that names given with this and the next +option ended in a dot, but since version 1.1.8b1-par8, pdnsd automatically adds +a dot at the end if it is missing. +pdnsd now also accepts a more compact notation for adding several "include" +entries in one line, e.g.: + +include=".foo",".bar",".my.dom"; + +exclude=string; +This option adds an entry to the exclusion/inclusion list. If a domain matches +the name given as string, the server is not queried if this was the first +matching rule (see also the entry for policy). +If the given name starts with a dot, the whole subdomain of the given name +including the one of that name is matched, e.g. ".foo.bar." will match the +domain names a.foo.bar., a.b.c.foo.bar. and foo.bar. +If it does not start in a dot, only exactly the given name (ignoring the case, +of course) will be matched (hint: if you want to exclude all subdomains, but +not the domain of the given name itself, place an exact-match include rule +before the exclude rule, e.g: include="foo.bar."; exclude=".foo.bar."; +pdnsd now also accepts a more compact notation for adding several "exclude" +entries in one line, e.g.: + +exclude=".foo",".bar",".my.dom"; + + +2.1.3 rr Section + +Every rr section specifies a dns resource record that is stored locally. It +allows you to specify own dns records that are served by pdnsd in a limited +way. Only A, PTR, CNAME, MX, NS and SOA records are implemented. +This option is intended to allow you to define RRs for 1.0.0.127.in-addr.arpa. +and localhost. (and perhaps even one or two hosts) without having to start an +extra named if your cached name servers do not serve those records. It is NOT +intended and not capable to work as a full-featured name server. + + +name=string; +Specifies the name of the resource records, i.e. the domain name of the +resource the record describes. This option must be specified before any a, ptr, +cname, mx, ns or soa records. Names are interpreted as absolute domain names +(i.e. pdnsd assumes they end in the root domain). For this and all following +arguments that take domain names, you need to specify domain names in dotted +notation (example venera.isi.edu.). +Previous versions of pdnsd required that domain names given in the +configuration file ended in a dot, but since version 1.1.8b1-par8, pdnsd +automatically assumes a dot at the end if it is missing. +New in version 1.2: It is also possible to specify a name starting with the +label *. Such a name is called a wildcard. The * in a wildcard can match one or +more labels in a queried name, but only whole labels. Any other * characters in +a wildcard, apart from the leading one, will only match a literal *. +For example, *.mydomain will match a.mydomain or www.a.mydomain, but not +mydomain. *.a*.mydomain will match www.a*.mydomain, but not www.ab.mydomain. +*a.mydomain will only match itself. +Before you can specify an rr section with name=*.mydomain you must define some +records for mydomain, typically NS and/or SOA records. Example: + + rr { + name = mydomain; + ns = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = *.mydomain; + a = 192.168.1.10; + } + +In this example, www.mydomain and ftp.mydomain will resolve to the numeric +address 192.168.1.10 (unless you add rr sections explicitly specifying +different addresses for www.mydomain or ftp.mydomain). If you want mydomain +also to resolve to a numeric address, add an A record to the first rr section. + +ttl=timespec; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). + +authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records for this rr +section. This means that pdnsd flags the domain record so that records of this +domain that are not present in the cache are treated as non-existent, i.e. no +other servers are queried for that record type, and an response containing none +of those records is returned. This is most time what people want: if you add an +A record for a host, and it has no AAAA record (thus no IPv6 address), you +normally don't want other name servers to be queried for it. +This is on by default. +Please note that this only has an effect if it precedes the name option! + +reverse=(on|off); +New in version 1.2: If you want a locally defined name to resolve to a numeric +address and vice versa, you can achieve this by setting reverse=on before +defining the A record (see below). The alternative is to define a separate PTR +record, but you will probably find this option much more convenient. +The default is off. + +a=string; +Defines an A (host address) record. The argument is an IPv4 address in dotted +notation. pdnsd will serve this address for the host name given in the name +option. +Provided there is sufficient support in the C libraries and support for AAAA +records was not disabled, the argument string may also be an IPv6 address, in +which case an AAAA record will be defined. +This option be may used multiple times within an rr section, causing multiple +addresses to be defined for the name. However, if you put the different +addresses in different rr sections for the same name, the definition in the +last rr section will cancel the definitions in the previous ones. + +ptr=string; +Defines a PTR (domain name pointer) record. The argument is a host name in +dotted notation (see name). The ptr record is for resolving adresses into +names. For example, if you want the adress 127.0.0.1 to resolve into localhost, +and localhost into 127.0.0.1, you need something like the following sections: + + rr { + name = localhost; + a = 127.0.0.1; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + rr { + name = 1.0.0.127.in-addr.arpa; + ptr = localhost; + owner = localhost; + soa = localhost, root.localhost, 42, 86400, 900, 86400, 86400; + } + +The second section is for reverse resolving and uses the ptr option. Note that +you can get the same effect by specifying only the first rr section with +reverse=on. +There is something special about the name in the second section: when a +resolver wants to get a host name from an internet address, it composes an +address that is built of the IP address in reverse byte order (1.0.0.127 +instead of 127.0.0.1) where each byte of the adress written as number +constitutes a sub-domain under the domain in-addr.arpa. +So, if you want to compose an adress for reverse resolving, take your ip in +dotted notation (e.g. 1.2.3.4), reverse the byte order (4.3.2.1) and append +in-addr.arpa. (4.3.2.1.in-addr.arpa.) Then, define an rr section giving this +address as name and the domain name corresponding to that ip in the ptr option. + +cname=string; +Defines a CNAME (canonical name) record. The argument should be a +fully-qualified host name in dotted notation (see name). A CNAME is the DNS +equivalent of an alias or symbolic link. +A useful application for CNAMEs is giving short, easy to remember nicknames to +hosts with complicated names. For example, you might want the name "news" to +refer to your ISP's news server "nntp2.myisp.com". Instead of adding an A +record for "news" with the same address as "nntp2.myisp.com", you could put in +a CNAME pointing to "nntp2.myisp.com", so that if the IP address of the news +server changes, there is no need to update the record for "news". +To implement this with pdnsd, you could add the following section to your +configuration file: + + rr { + name = news; + cname = nntp2.myisp.com; + owner = localhost; + } + +mx=string,number; +Defines an MX (mail exchange) record. The string is the host name of the mail +server in dotted notation (see name). The number specifies the preference +level. +When you send mail to someone, your mail typically goes from your E-mail client +to an SMTP server. The SMTP server then checks for the MX record of the domain +in the E-mail address. For example, with joe@example.com, it would look for the +MX record for example.com and find that the name of mail server for that domain +is, say, mail.example.com. The SMTP server then gets the A record for +mail.example.com, and connects to the mail server. +If there are multiple MX records, the SMTP server will pick one based on the +preference level (starting with the lowest preference number, working its way +up). +Don't define MX records with pdnsd unless you know what you're doing. + +owner=string; +or +ns=string; +Defines an NS (name server) record. Specifies the name of the host which should +be authoritative for the records you defined in the rr section. This is +typically the host pdnsd runs on. +Note: In previous versions of pdnsd this option had to be specified before any +a, ptr, cname, mx or soa entries. In version 1.2, the restrictions on this +option are same as the options just mentioned, and it must listed after the +name= option. This can be a pain if you want to use an old config file which +specifies owner= before name= (sorry about that). Apart from greater +consistency, the advantage is that you can now specify as many NS records as +you like (including zero). + +soa=string,string,number,timespec,timespec,timespec,timespec; +This defines a soa (start of authority) record. The first string is the domain +name of the server and should be equal to the name you specified as owner. +The second string specifies the email address of the maintainer of the name +server. It is also specified as a domain name, so you will have to replace the +@ sign in the name with a dot (.) to get the name you have to specify here. The +next parameter (the first number) is the serial number of the record. You +should increment this number if you change the record. +The 4th parameter is the refresh timeout. It specifies after what amount of +time a caching server should attempt to refresh the cached record. +The 5th parameter specifies a time after which a caching server should attempt +to refresh the record after a refresh failure. +The 6th parameter defines the timeout after which a cached record expires if it +has not been refreshed. +The 7th parameter is the ttl that is specified in every rr and should be the +same as given with the ttl option (if you do not specify a ttl, use the default +86400). + +txt=string,...,string; +New in version 1.2.9: Defines an TXT record. You can specify one or more +strings here. + + +2.1.4 neg Section + +Every neg section specifies a dns resource record or a dns domain that should +be cached negatively locally. Queries for negatively cached records are always +answered immediatley with an error or an empty answer without querying other +hosts as long as the record is valid. The records defined with neg sections +remain valid until they are explicitely invalidated or deleted by the user +using pdnsd-ctl. +This is useful if a certain application asks periodically for nonexisting hosts +or RR types and you do not want a query to go out every time the cached record +has timed out. Example: Netscape Communicator will ask for the servers news and +mail on startup if unconfigured. If you do not have a dns search list for your +network, you can inhibit outgoing queries for these by specifying + + neg { + name = news; + types = domain; + } + neg { + name = mail; + types = domain; + } + +in your config file. If you have a search list, you have to repeat that for any +entry in your search list in addition to the entries given above! +In versions 1.1.11 and later, if you negate whole domains this way, all +subdomains will be negated as well. Thus if you specify +neg {name=example.com; types=domain;} in the config file, this will also negate +www.example.com, xxx.adserver.example.com, etc. + + +name=string; +Specifies the name of the domain for which negative cache entries are created. +This option must be specified before the types option. Names are interpreted as +absolute domain names (i.e. pdnsd assumes they end in the root domain). You +need to specify domain names in dotted notation (example venera.isi.edu.). +Previous versions of pdnsd required that domain names given in the +configuration file ended in a dot, but since version 1.1.8b1-par8, pdnsd +automatically assumes a dot at the end if it is missing. + +ttl=timespec; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). + +types=(domain|rr_type[,rr_type[,rr_type[,...]]]); +Specifies what is to be cached negatively: domain will cache the whole domain +negatively; alternatively, you can specify a comma-separated list of RR types +which are to be cached negatively. You may specify multiple types options, but +domain and the RR types are mutually exclusive. +The RR types are specified using their official names from the RFC's in +capitals, e.g. A, CNAME, NS, PTR, MX, AAAA, ... +The command pdnsd-ctl list-rrtypes will give you a complete list of those +types. pdnsd-ctl is built along with pdnsd and will be installed in the same +directory as the pdnsd binary during make install. + + +2.1.5 source Section + +Every source section allows you to let pdnsd read the records from a file in an +/etc/hosts-like format. pdnsd will generate records to resolve the entries +address from its host name and vice versa for every entry in the file. This is +normally easier than defining an rr for every of your addresses, since +localhost and your other FQDNs are normally given in /etc/hosts. +The accepted format is as follows: The #-sign initiates a comment, the rest of +the line from the first occurence of this character on is ignored. Empty lines +are tolerated. +The first entry on a line (predeceded by an arbitrary number of tabs and +spaces) is the IP in dotted notation, the second entry on one line (separated +by the first by an arbitrary number of tabs and spaces) is the FQDN (fully +qualified domain name) for that ip. The rest of the line is ignored by default +(in the original /etc/hosts, it may contain information not needed by pdnsd). + + +owner=string; +Specifies the name of the host pdnsd runs on and that are specified in dns +answers (specifically, nameserver records). Must be specified before any file +entries. +Names are interpreted as absolute domain names (i.e. pdnsd assumes they end in +the root domain). You need to specify domain names in dotted notation (example +venera.isi.edu.). +Previous versions of pdnsd required that domain names given in the +configuration file ended in a dot, but since version 1.1.8b1-par8, pdnsd +automatically assumes a dot at the end if it is missing. + +ttl=timespec; +Specifies the ttl (time to live) for all resource records in this section after +this entry. This may be redefined. The default is 86400 seconds (=1 day). + +file=string; +The string specifies a file name. For every file entry in a source section, +pdnsd will try to load the given file as described above. Failure is indicated +only when the file cannot be opened, malformed entries will be ignored. + +serve_aliases=(on|off); +If this is turned on pdnsd will serve the aliases given in a hosts-style file. +These are the third entry in a line of a hosts-style file, which usually give a +"short name" for the host. This may be used to support broken clients without a +proper domain-search option. If no aliases are given in a line of the file, +pdnsd behaves as without this option for this line. +This feature was suggested by Bert Frederiks. +It is off by default. + +authrec=(on|off); +If this is turned on, pdnsd will create authoritative local records with the +data from the hosts file. Please see the description of the option of the same +name in the rr section for a closer description of what this means. Please note +that this only has an effect for files sourced with file options subsequent to +this option. +This is on by default. + + +2.1.6 include Section + +A configuration file may include other configuration files. However, only the +top-level configuration file may contain global and server sections, thus +include files are effectively limited to sections that add local definitions to +the cache. +Include sections currently only have one type of option, which may be given +multiple times within a single section. + + +file=string; +The string specifies a file name. For every file option in an include section, +pdnsd will parse the given file as described above. The file may contain +include sections itself, but as a precaution pdnsd checks that a certain +maximum depth is not exceeded to guard against the possibility of infinite +recursion. + + +3 pdnsd-ctl + +pdnsd-ctl allows you to configure pdnsd at run time. To make this work, you +have to start pdnsd with the -s option (or use the status_ctl option in the +config file). You also should make sure that you have appropriate permissions +on the control socket (use the ctl_perms option to make this sure) and of your +pdnsd cache directory (pdnsd keeps its socket there). Please make sure the +pdnsd cache directory is not writeable for untrusted users! + +pdnsd-ctl accepts two command-line options starting with a dash. +-c may be used to specify the cache directory (and takes this as argument). The +default for this setting is the pdnsd default cache directory (specified at +compile time). The cache directory for pdnsd-ctl must be the same pdnsd uses! +-q can be used to make the output of pdnsd-ctl less verbose. + +The following table lists the commands that pdnsd-ctl supports. The command +must always be the first command-line option (not starting with a dash), the +arguments to the command must follow in the given order. +In the following table, keywords are in a normal font, while placeholders are +in italics. +Alternatives are specified like (alt1|alt2|alt3). Optional arguments are placed +between square brackets []. + +Command Arguments Description + +help Print a command summary. + +version Print version and license info. + +status Print a description of pdnsd's cache status, thread status + and configuration. Also shows which remote name servers + are assumed to be available. + +server (index|label) (up| Set the status of the server with the given index or label + down|retest) [dns1[, (where the given label matches the one given with the + dns2[,...]]] label option in the respective server section in the + config file) to up or down, or force a retest. The index + is assigned in the order of definition in pdnsd.conf + starting with 0. Use the status command to view the + indices and labels. You can specify all instead of an + index or label to perform the action for all servers + registered with pdnsd. Example: + pdnsd-ctl server 0 retest + An optional third argument consisting of a list of IP + addresses (separated by commas or white-space characters) + can be given. This list will replace the previous list of + addresses of name servers used by pdnsd in the specified + section of the config file. For example in the /etc/ppp/ + ip-up script called by pppd you could place the following + line: + pdnsd-ctl server isplabel up $DNS1,$DNS2 + If white space is used to separate addresses the list will + have to be quoted. Spurious commas and white-space + characters are ignored. The last argument may also be an + empty string, in which case the existing IP addresses are + removed and the corresponding server section becomes + inactive. + +record name (delete| Delete or invalidate the records of the given domain name + invalidate) if it is in the cache. Invalidation means that the records + are marked as timed out, and will be reloaded if possible + (if purge_cache is set to on, they will be deleted in any + case). + For local records (i.e., records that were given in the + config file using a rr section, records read from a + hosts-style file and records added using pdnsd-ctl), + invalidation has no effect. Deletion will work, though. + Example: + pdnsd-ctl record localhost. delete + +source fn owner [ttl] [(on Load a hosts-style file. Works like using the pdnsd source + |off)] [noauth] configuration section. owner and ttl are used as in the + source section. ttl has a default of 900 (it does not need + to be specified). The next to last argument corresponds to + the serve_aliases option, and is off by default (i.e. if + it is not specified). noauth is used to make the domains + non-authoritative - please see the description of the + authrec config file options for a description of what that + means. fn is the filename. The file must be readable by + pdnsd! Example: + pdnsd-ctl source /etc/hosts localhost. 900 off + +add a addr name [ttl] + [noauth] + Add a record of the given type to the pdnsd cache, + replacing existing records for the same name and type. The +add aaaa addr name [ttl 2nd argument corresponds to the value of the option in the + ] [noauth] rr section that is named like the first argument: a is a + record for hostname-to-address mapping, aaaa is the same + thing for IPv6 addresses, and ptr is for +add ptr host name [ttl] address-to-hostname mapping. See the documentation for the + [noauth] rr section for more details. In case of A and AAAA + records, the addr argument may be a list of IP addresses, + separated by commas or white space, causing multiple +add cname host name [ addresses to be defined for the same name. The ttl is + ttl] [noauth] optional, the default is 900 seconds. noauth is used to + make the domains non-authoritative - please see the + description of the authrec config file options for a +add mx host name pref description of what that means. If you want no other + [ttl] [noauth] record than the newly added in the cache, do + pdnsd-ctl record name delete before adding records. This + is also better when overwriting local records. Example: +add ns host name [ttl] pdnsd-ctl add a 127.0.0.1 localhost. 900 + [noauth] + + +neg name [type] [ttl] Add a negatively cached record to pdnsd's cache, replacing + existing records for the same name and type. If no type is + given, the whole domain is cached negatively. For + negatively cached records, errors are immediately returned + on a query, without querying other servers first. The ttl + is optional, the default is 900 seconds. + You can get a list of all types you can pass to this + command using pdnsd-ctl list-rrtypes. The type is treated + case-sensitive! Example: + pdnsd-ctl neg foo.bar A 900 + pdnsd-ctl neg foo.baz 900 + +config [filename] Reload pdnsd's configuration file. + The config file must be owned by the uid that pdnsd had + when it was started, and be readable by pdnsd's run_as + uid. If no file name is specified, the config file used at + start-up is reloaded. + Note that some configuration changes, like the port or IP + address pdnsd listens on, cannot be made this way and you + will receive an error message. In these cases, you will + have to restart pdnsd instead. + +include filename Parse the given file as an include file, see the + documentation on include sections for a description what + this file may contain. + This command is useful for adding definitions to the cache + without reconfiguring pdnsd. + +eval string Parse the given string as if it were part of pdnsd's + configuration file. The string should hold one or more + complete configuration sections. However, global and + server sections are not allowed, just as in include files. + If multiple strings are given, they will be joined using + newline chars and parsed together. + This command is useful for adding records interactively to + the cache that cannot be defined using the "pdnsd-ctl add" + command, (e.g. soa records). + +empty-cache [[+|-]name ...] If no arguments are provided, the cache will be completely + emptied, freeing all existing entries. Note that this also + removes local records, as defined by the config file. To + restore local records, run "pdnsd-ctl config" or + "pdnsd-ctl include filename" immediately afterwards. + The "pdnsd-ctl empty-cache" command now accepts additional + arguments; these are interpreted as include/exclude names. + If an argument starts with a '+' the name will be + included. If an argument starts with a '-' it will be + excluded. If an argument does not begin with '+' or '-', a + '+' is assumed. If the domain name of a cache entry ends + in one of the names in the list, the first match will + determine what happens. If the matching name is to be + included, the cache entry is deleted, otherwise not. If + there are no matches, the default action is not to delete. + Note that if you want to delete exactly one name and no + others, you should use "pdnsd-ctl record name delete", + this is also much more efficient. + Examples: + pdnsd-ctl empty-cache + This command will remove all cache entries. + + pdnsd-ctl empty-cache microsoft.com msft.net + This will remove all entries ending in microsoft.com or + msft.net. + + pdnsd-ctl empty-cache -localdomain -168.192.in-addr.arpa . + This will remove all entries except those ending in + localdomain or 168.192.in-addr.arpa. Note that '.' is the + root domain which matches any domain name. + +dump [name] Print information stored in the cache about name. If name + begins with a dot and is not the root domain, information + about the names in the cache ending in name (including + name without the leading dot) will be printed. If name is + not specified, information about all the names in the + cache will be printed. + For each RR record the time and date that this record has + been added to the cache will be printed in the form mm/dd + HH:MM:SS (locally defined records are printed without a + time stamp). After that the type of record is printed with + the data. For the more common types of RR records the data + will be printed in human readable form, the remaining ones + in a hexadecimal representation. + This command is mainly useful for diagnostic purposes. + Note that if you pipe the output of this command through + an application that reads only part of the output and then + blocks (such as more or less), pdnsd will not be able to + add new entries to the cache until the pipe is closed. It + is preferable to capture the output in a file in such a + case. + +list-rrtypes List available rr types for the neg command. Note that + those are only used for the neg command, not for add! + + + +4 contrib/ + +The contrib directory in the pdnsd distribution contains useful +user-contributed scripts. +So far, there are scripts contributed by Marko Stolle and Paul Rombouts that +make pdnsd usable in a DHCP setup. Please take a look into the README file in +the contrib directory for further information. + + +5 Problems... + +If you have problems with configuring or running pdnsd, be sure to read the FAQ +. If this does not help you, pdnsd crashes or you find bugs, please mail one of +the authors. +Note added by Paul A. Rombouts: Thomas Moestl no longer maintains the code. I +have revised the code and added new features. See README.par and the ChangeLog +in the source directory (or /usr/share/doc/pdnsd- if you have +installed a binary package) for more details. If you have questions about my +modifications, you can find my email address at the end of README.par. + + +6 Hacking + +Here comes some information you might find useful for hacking pdnsd. + +6.1 Source files + +Makefile.am, autoconf/automake/autoheader scripts. Makefile.am's are in +configure.in, most subdirectories. +acconfig.h + +pdnsd.spec.in A template from which configure generates a spec file for + building rpm's for various distributions. + +version Contains only the program version string. Needed for + several templates. + +src/rc/* rc (start-up) scripts for various linux distributions. + + The pdnsd cache subsystem(s) as defined in src/cache.h. +src/cache.c This is the "traditional" pdnsd system which keeps the + cache in memory and uses hash tables for accesses. Sourav + K. Mandal is working on a system using gdbm. + +src/pdnsd-ctl/* Contains the code for pdnsd-ctl, a program that allows you + to control pdnsd at run time. + + The lex/flex source file for the config file lexer. This is +src/conf-lex.l.in a template because there might be inserted "%option + yylineno" for proper flex support. (obsolete, superseded by + src/conf-parser.c) + + This is automatically generated by configure from +src/conf-lex.l conf-lex.l.in. It may be overwritten in any make, so never + modify this, but conf-lex.l.in instead! (obsolete, + superseded by src/conf-parser.c) + +src/conf-parse.y The yacc/bison source of the config file parser. (obsolete, + superseded by src/conf-parser.c) + +src/conf-parser.c, The config file parser written purely in C (versions +src/conf-parser.h, 1.1.10-par and later). +src/conf-keywords.h + +src/conff.c, src/ The configuration handler functions and their prototypes. +conff.h The parser is called from here. + +src/consts.h Some constants used by the parser, config file handler + functions and in the server status thread, among others. + + Define dns message structures, constants, and some common +src/dns.c, src/dns.h dns data handlers. dns.h contains gcc-specific code (in + praticular, "__attribute__((packed))"). + +src/dns_answer.c, Define functions that answer incoming dns queries. +src/dns_answer.h + +src/dns_query.c, src Define functions to manage outgoing dns queries. +/dns_query.h + +src/error.c, src/ Functions for error output to stderr or the syslog, and +error.h debug output to stderr or pdnsd.debug. + +src/hash.c, src/ Contains the code for storing and looking up cache entries +hash.h in the hash table. + +src/helpers.c, src/ Define miscellaneous helper functions. +helpers.h + +src/icmp.c, src/ Define a function for performing a ping test. This contains +icmp.h OS-specific code. + +src/main.c Contains main(), which holds the command line parser, + performs initialisations and signal handling. + + Contains the code for the executable make_hashconvtable, +src/ which is only run once, during build time, to generate the +make_hashconvtable.c file hashconvtable.h, used by src/hash.c (versions + 1.1.10-par and later). (obsolete since version 1.2) + + A perl script for generating src/rr_types.h, a C header +src/ file containing macro definitions and tables needed for +make_rr_types_h.pl handling the RR types known to pdnsd, from the text file + src/rr_types.in. + +src/rr_types.c, src/ These define tables and macros needed for handling the RR +rr_types.h, src/ types known to pdnsd. Since version 1.2.9, rr_types.h is an +rr_types.in automatically generated file, see make_rr_types_h.pl. + +src/netdev.c, src/ Define functions for network device handling. OS-specific. +netdev.h + +src/servers.c, src/ Define functions for the server status thread that performs +servers.h the periodical uptests. + +src/status.c, src/ Define functions for the status control thread. This is +status.h pdnsd's interface to pdnsd-ctl. + + +------------------------------------------------------------------------------- + +Copyright (C) 2000, 2001 Thomas Moestl +Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2012 Paul A. Rombouts + +Last revised: 19 April 2012 by Paul A. Rombouts + diff --git a/service/src/main/jni/pdnsd/file-list.base.in b/service/src/main/jni/pdnsd/file-list.base.in new file mode 100644 index 0000000..409ba33 --- /dev/null +++ b/service/src/main/jni/pdnsd/file-list.base.in @@ -0,0 +1,5 @@ +%defattr(-,root,root) +%doc AUTHORS THANKS COPYING COPYING.BSD ChangeLog ChangeLog.old INSTALL NEWS README README.par README.par.old TODO +%config /etc/pdnsd.conf.sample +%attr(750, @def_id@, @def_id@) %dir @cachedir@ +%attr(640, @def_id@, @def_id@) %config @cachedir@/pdnsd.cache diff --git a/service/src/main/jni/pdnsd/install-sh b/service/src/main/jni/pdnsd/install-sh new file mode 100644 index 0000000..4d4a951 --- /dev/null +++ b/service/src/main/jni/pdnsd/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-05-14.22 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/service/src/main/jni/pdnsd/missing b/service/src/main/jni/pdnsd/missing new file mode 100644 index 0000000..894e786 --- /dev/null +++ b/service/src/main/jni/pdnsd/missing @@ -0,0 +1,360 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2005-06-08.21 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# 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 2, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case "$1" in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/service/src/main/jni/pdnsd/pdnsd.spec.in b/service/src/main/jni/pdnsd/pdnsd.spec.in new file mode 100644 index 0000000..6af6cde --- /dev/null +++ b/service/src/main/jni/pdnsd/pdnsd.spec.in @@ -0,0 +1,244 @@ +# rpmbuild spec file for pdnsd. +# with modifications by Paul Rombouts. + +# Supported rpmbuild --define and --with options include: +# +# --with isdn Configure with --enable-isdn. +# +# --without poll Configure with --disable-poll +# +# --without nptl Configure with --with-thread-lib=linuxthreads. +# +# --with ipv6 Configure with --enable-ipv6. +# +# --without tcpqueries Configure with --disable-tcp-queries. +# +# --without debug Configure with --with-debug=0. +# +# --define "distro " Configure with --with-distribution=. +# +# --define "run_as_user " Configure with --with-default-id=. +# For RPMs the default is "@def_id@". +# +# --define "run_as_uid " If the user defined by the previous option does not exist +# when the RPM is installed, the pre-install script will try +# to create a new user with numerical id . +# +# --define "cachedir " Configure with --with-cachedir=. +# + +%{!?distro: %define distro @distribution@} + +# The default run_as ID to use +%{!?run_as_user: %define run_as_user @def_id@} +# By default, if a new run_as_user is to be created, we let +# useradd choose the numerical uid, unless run_as_uid is defined. +#define run_as_uid 96 +%{!?cachedir: %define cachedir @cachedir@} +%define conffile %{_sysconfdir}/pdnsd.conf + +Summary: A caching dns proxy for small networks or dialin accounts +Name: @PACKAGE@ +Version: @VERSION@ +Release: @packagerelease@ +License: GPLv3 +Group: Daemons +Source: http://members.home.nl/p.a.rombouts/pdnsd/releases/%{name}-%{version}-%{release}.tar.gz +URL: http://members.home.nl/p.a.rombouts/pdnsd.html +Vendor: Paul A. Rombouts +Packager: Paul A. Rombouts +Prefix: %{_prefix} +BuildRoot: %{_tmppath}/%{name}-%{version}-root + +%description +pdnsd is a proxy DNS daemon with permanent (disk-)cache and the ability +to serve local records. It is designed to detect network outages or hangups +and to prevent DNS-dependent applications like Netscape Navigator from hanging. + +The original author of pdnsd is Thomas Moestl, but pdnsd is no longer maintained +by him. This is an extensively revised version by Paul A. Rombouts. +For a description of the changes see http://members.home.nl/p.a.rombouts/pdnsd.html +and the file README.par in %{_docdir}/%{name}-%{version} + +%{!?distro:You can specify the target distribution when you build the source RPM. For instance, if you're building for a Red Hat system call rpmbuild with:} +%{!?distro: --define "distro RedHat"} +%{?distro:This package was built for a %{distro} distribution.} +%{!?_with_isdn:It's possible to rebuild the source RPM with isdn support using the rpmbuild option:} +%{!?_with_isdn: --with isdn} +%{?_with_isdn:This package was built with isdn support enabled.} +%{!?_with_ipv6:It's possible to rebuild the source RPM with ipv6 support using the rpmbuild option:} +%{!?_with_ipv6: --with ipv6} +%{?_with_ipv6:This package was built with ipv6 support.} +%{?_without_poll:This package was built with the select(2) function instead of poll(2).} + +%prep +%setup + +%build +CFLAGS="${CFLAGS:-$RPM_OPT_FLAGS -Wall}" ./configure \ + --prefix=%{_prefix} --sysconfdir=%{_sysconfdir} --mandir=%{_mandir} \ + --with-cachedir="%{cachedir}" \ + %{?distro:--with-distribution=%{distro}} --enable-specbuild \ + --with-default-id=%{run_as_user} \ + %{?_with_isdn:--enable-isdn} \ + %{?_without_poll:--disable-poll} \ + %{?_without_nptl:--with-thread-lib=linuxthreads} \ + %{?_with_ipv6:--enable-ipv6} \ + %{?_without_tcpqueries:--disable-tcp-queries} \ + %{?_without_debug:--with-debug=0} + +make + +%install +%if "%{run_as_user}" != "nobody" +[ "$(id -un)" != root ] || +id -u %{run_as_user} > /dev/null 2>&1 || +/usr/sbin/useradd -c "Proxy DNS daemon" %{?run_as_uid:-u %{run_as_uid}} \ + -s /sbin/nologin -r -d "%{cachedir}" %{run_as_user} || { + set +x + echo "Cannot create user \"%{run_as_user}\"%{?run_as_uid: with uid=%{run_as_uid}}" + echo "Please select another numerical uid and rebuild with --define \"run_as_uid uid\"" + echo "or create a user named \"%{run_as_user}\" by hand and try again." + exit 1 +} +%endif + +rm -rf "$RPM_BUILD_ROOT" +make DESTDIR="$RPM_BUILD_ROOT" install +cp -f file-list.base file-list +find doc contrib -not -type d -not -iname '*makefile' -not -name '*.am' \ + -not -name '*.in' -not -path 'doc/*.pl' | +sed -e 's/^/%doc --parents /' >> file-list +CURDIR=$PWD; cd "$RPM_BUILD_ROOT" +find . -not -type d '(' -not -name 'pdnsd.conf*' -or -name 'pdnsd.conf.[1-9]*' ')' \ + -not -path '.%{_docdir}/*' -not -path './var/*' | +sed -e 's/^\.// + \:/man:{ + /\.gz$/!s/$/.gz/ + }' >> "$CURDIR/file-list" + +%clean +rm -rf "$RPM_BUILD_ROOT" +#rm -rf %{_builddir}/%{name}-%{srcver} + +%files -f file-list + +%pre +# First stop any running pdnsd daemons +%if "%{distro}" == "SuSE" +/sbin/init.d/pdnsd stop >/dev/null 2>&1 +%endif +%if "%{distro}" == "RedHat" +if [ -f /var/lock/subsys/pdnsd ]; then + if /sbin/pidof pdnsd > /dev/null; then + /sbin/service pdnsd stop >/dev/null 2>&1 + if [ "$1" -ge 2 ]; then touch /var/lock/subsys/pdnsd; fi + else + rm -f /var/lock/subsys/pdnsd + fi +fi +%endif + +%if "%{run_as_user}" != "nobody" +# Add the "pdnsd" user +id -u %{run_as_user} > /dev/null 2>&1 || +/usr/sbin/useradd -c "Proxy DNS daemon" %{?run_as_uid:-u %{run_as_uid}} \ + -s /sbin/nologin -r -d "%{cachedir}" %{run_as_user} || { + echo "Cannot create user \"%{run_as_user}\"%{?run_as_uid: with uid=%{run_as_uid}}" + echo "Please create a user named \"%{run_as_user}\" by hand and try again." + exit 1 +} +[ "$(id -gn %{run_as_user})" = %{run_as_user} ] || { + echo "user \"%{run_as_user}\" does not have an corresponding group called \"%{run_as_user}\"" + echo "Please change the initial group of user \"%{run_as_user}\" to \"%{run_as_user}\" and try again." + exit 1 +} + +if [ -f "%{conffile}" ] && + grep -v -e '^[[:blank:]]*\(#\|\/\/\)' "%{conffile}" | + grep -q -e '\ "%{conffile}" && + echo "In %{conffile} runs_as=\"nobody\" has been replaced by run_as=\"%{run_as_user}\"" +fi +%endif + +if [ -f "%{cachedir}/pdnsd.cache" ]; then + chown -c %{run_as_user}:%{run_as_user} "%{cachedir}/pdnsd.cache" +fi + +%post +%if "%{distro}" == "SuSE" +if [ -w /etc/rc.config ]; then + grep "START_PDNSD" /etc/rc.config > /dev/null + if [ $? -ne 0 ] ; then + echo -e \ +"\n\n#\n# Set to yes to start pdnsd at boot time\n#\nSTART_PDNSD=yes" \ +>> /etc/rc.config + fi +fi +%endif +%if "%{distro}" == "RedHat" +if [ "$1" = 1 ]; then + /sbin/chkconfig --add pdnsd +fi +%endif + +%preun +%if "%{distro}" == "RedHat" +if [ "$1" = 0 ]; then + /sbin/service pdnsd stop >/dev/null 2>&1 + /sbin/chkconfig --del pdnsd +fi +%endif + +%postun +%if "%{distro}" == "RedHat" +if [ "$1" -ge 1 ]; then + /sbin/service pdnsd condrestart >/dev/null 2>&1 +fi +%endif + +%changelog +* Tue Jan 31 2012 Paul A. Rombouts +- Prevent makefiles and perl scripts from being installed + in the documentation directory. +* Sat Jan 28 2012 Paul A. Rombouts +- Update the (Source) URLs. +* Sat Aug 4 2007 Paul Rombouts +- License is now GPL version 3 +* Fri Mar 24 2006 Paul Rombouts +- Instead of using a fixed default value for run_as_uid, + I let useradd choose the uid if run_as_uid is undefined. +* Thu Dec 29 2005 Paul Rombouts +- TCP-query support is now compiled in by default, + but can be disabled using "--without tcpqueries". +* Sun Jul 20 2003 Paul Rombouts +- Changed default run_as ID from "nobody" to "pdnsd" +* Fri Jun 20 2003 Paul Rombouts +- Added configuration option for NPTL. +* Sat Jun 07 2003 Paul Rombouts +- Added automatic definition of distro using _vendor macro. +* Thu May 22 2003 Paul Rombouts +- Ensured that modification times of acconfig.h and configure.in + are not changed by patching to avoid unwanted reconfigure during make phase. +* Tue May 20 2003 Paul Rombouts +- Applied my customized patch file. See READ.par for details. +* Sun May 16 2001 Thomas Moestl +- Make use of chkconfig for Red Hat (patch by Christian Engstler) +* Sun Mar 25 2001 Thomas Moestl +- Merged SuSE fixes by Christian Engstler +* Fri Feb 09 2001 Thomas Moestl +- Merged in a spec fix for mapage inclusion contributed by Sourav K. + Mandal +* Sun Nov 26 2000 Thomas Moestl +- Added some patches contributed by Bernd Leibing +* Tue Aug 15 2000 Thomas Moestl +- Added the distro for configure +* Tue Jul 11 2000 Sourav K. Mandal +- autoconf/automake modifications diff --git a/service/src/main/jni/pdnsd/src/Makefile.am b/service/src/main/jni/pdnsd/src/Makefile.am new file mode 100644 index 0000000..052022d --- /dev/null +++ b/service/src/main/jni/pdnsd/src/Makefile.am @@ -0,0 +1,24 @@ + +sbin_PROGRAMS = pdnsd + +pdnsd_CFLAGS = $(thread_CFLAGS) + +pdnsd_SOURCES = conf-parser.c conff.c consts.c debug.c dns.c dns_answer.c \ + dns_query.c error.c helpers.c icmp.c list.c main.c netdev.c rr_types.c \ + status.c servers.c thread.c cache.c hash.c conf-parser.h \ + conf-keywords.h conff.h consts.h debug.h dns.h dns_answer.h \ + dns_query.h error.h helpers.h icmp.h ipvers.h list.h netdev.h \ + rr_types.h servers.h status.h thread.h cache.h hash.h pdnsd_assert.h \ + freebsd_netinet_ip_icmp.h + +EXTRA_DIST = make_rr_types_h.pl rr_types.in + +## Try to do this last + +SUBDIRS = . pdnsd-ctl rc test + +$(pdnsd_OBJECTS): rr_types.h + +rr_types.h: make_rr_types_h.pl rr_types.in + perl make_rr_types_h.pl rr_types.in > rr_types.h + diff --git a/service/src/main/jni/pdnsd/src/Makefile.in b/service/src/main/jni/pdnsd/src/Makefile.in new file mode 100644 index 0000000..1cc64f3 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/Makefile.in @@ -0,0 +1,921 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +sbin_PROGRAMS = pdnsd$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +am_pdnsd_OBJECTS = pdnsd-conf-parser.$(OBJEXT) pdnsd-conff.$(OBJEXT) \ + pdnsd-consts.$(OBJEXT) pdnsd-debug.$(OBJEXT) \ + pdnsd-dns.$(OBJEXT) pdnsd-dns_answer.$(OBJEXT) \ + pdnsd-dns_query.$(OBJEXT) pdnsd-error.$(OBJEXT) \ + pdnsd-helpers.$(OBJEXT) pdnsd-icmp.$(OBJEXT) \ + pdnsd-list.$(OBJEXT) pdnsd-main.$(OBJEXT) \ + pdnsd-netdev.$(OBJEXT) pdnsd-rr_types.$(OBJEXT) \ + pdnsd-status.$(OBJEXT) pdnsd-servers.$(OBJEXT) \ + pdnsd-thread.$(OBJEXT) pdnsd-cache.$(OBJEXT) \ + pdnsd-hash.$(OBJEXT) +pdnsd_OBJECTS = $(am_pdnsd_OBJECTS) +pdnsd_LDADD = $(LDADD) +pdnsd_LINK = $(CCLD) $(pdnsd_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(pdnsd_SOURCES) +DIST_SOURCES = $(pdnsd_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +pdnsd_CFLAGS = $(thread_CFLAGS) +pdnsd_SOURCES = conf-parser.c conff.c consts.c debug.c dns.c dns_answer.c \ + dns_query.c error.c helpers.c icmp.c list.c main.c netdev.c rr_types.c \ + status.c servers.c thread.c cache.c hash.c conf-parser.h \ + conf-keywords.h conff.h consts.h debug.h dns.h dns_answer.h \ + dns_query.h error.h helpers.h icmp.h ipvers.h list.h netdev.h \ + rr_types.h servers.h status.h thread.h cache.h hash.h pdnsd_assert.h \ + freebsd_netinet_ip_icmp.h + +EXTRA_DIST = make_rr_types_h.pl rr_types.in +SUBDIRS = . pdnsd-ctl rc test +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) +pdnsd$(EXEEXT): $(pdnsd_OBJECTS) $(pdnsd_DEPENDENCIES) + @rm -f pdnsd$(EXEEXT) + $(pdnsd_LINK) $(pdnsd_OBJECTS) $(pdnsd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-cache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-conf-parser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-conff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-consts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-debug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-dns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-dns_answer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-dns_query.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-error.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-hash.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-helpers.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-icmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-netdev.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-rr_types.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-servers.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-status.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-thread.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +pdnsd-conf-parser.o: conf-parser.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conf-parser.o -MD -MP -MF $(DEPDIR)/pdnsd-conf-parser.Tpo -c -o pdnsd-conf-parser.o `test -f 'conf-parser.c' || echo '$(srcdir)/'`conf-parser.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conf-parser.Tpo $(DEPDIR)/pdnsd-conf-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conf-parser.c' object='pdnsd-conf-parser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conf-parser.o `test -f 'conf-parser.c' || echo '$(srcdir)/'`conf-parser.c + +pdnsd-conf-parser.obj: conf-parser.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conf-parser.obj -MD -MP -MF $(DEPDIR)/pdnsd-conf-parser.Tpo -c -o pdnsd-conf-parser.obj `if test -f 'conf-parser.c'; then $(CYGPATH_W) 'conf-parser.c'; else $(CYGPATH_W) '$(srcdir)/conf-parser.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conf-parser.Tpo $(DEPDIR)/pdnsd-conf-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conf-parser.c' object='pdnsd-conf-parser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conf-parser.obj `if test -f 'conf-parser.c'; then $(CYGPATH_W) 'conf-parser.c'; else $(CYGPATH_W) '$(srcdir)/conf-parser.c'; fi` + +pdnsd-conff.o: conff.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conff.o -MD -MP -MF $(DEPDIR)/pdnsd-conff.Tpo -c -o pdnsd-conff.o `test -f 'conff.c' || echo '$(srcdir)/'`conff.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conff.Tpo $(DEPDIR)/pdnsd-conff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conff.c' object='pdnsd-conff.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conff.o `test -f 'conff.c' || echo '$(srcdir)/'`conff.c + +pdnsd-conff.obj: conff.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-conff.obj -MD -MP -MF $(DEPDIR)/pdnsd-conff.Tpo -c -o pdnsd-conff.obj `if test -f 'conff.c'; then $(CYGPATH_W) 'conff.c'; else $(CYGPATH_W) '$(srcdir)/conff.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-conff.Tpo $(DEPDIR)/pdnsd-conff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='conff.c' object='pdnsd-conff.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-conff.obj `if test -f 'conff.c'; then $(CYGPATH_W) 'conff.c'; else $(CYGPATH_W) '$(srcdir)/conff.c'; fi` + +pdnsd-consts.o: consts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-consts.o -MD -MP -MF $(DEPDIR)/pdnsd-consts.Tpo -c -o pdnsd-consts.o `test -f 'consts.c' || echo '$(srcdir)/'`consts.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-consts.Tpo $(DEPDIR)/pdnsd-consts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='consts.c' object='pdnsd-consts.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-consts.o `test -f 'consts.c' || echo '$(srcdir)/'`consts.c + +pdnsd-consts.obj: consts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-consts.obj -MD -MP -MF $(DEPDIR)/pdnsd-consts.Tpo -c -o pdnsd-consts.obj `if test -f 'consts.c'; then $(CYGPATH_W) 'consts.c'; else $(CYGPATH_W) '$(srcdir)/consts.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-consts.Tpo $(DEPDIR)/pdnsd-consts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='consts.c' object='pdnsd-consts.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-consts.obj `if test -f 'consts.c'; then $(CYGPATH_W) 'consts.c'; else $(CYGPATH_W) '$(srcdir)/consts.c'; fi` + +pdnsd-debug.o: debug.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-debug.o -MD -MP -MF $(DEPDIR)/pdnsd-debug.Tpo -c -o pdnsd-debug.o `test -f 'debug.c' || echo '$(srcdir)/'`debug.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-debug.Tpo $(DEPDIR)/pdnsd-debug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='debug.c' object='pdnsd-debug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-debug.o `test -f 'debug.c' || echo '$(srcdir)/'`debug.c + +pdnsd-debug.obj: debug.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-debug.obj -MD -MP -MF $(DEPDIR)/pdnsd-debug.Tpo -c -o pdnsd-debug.obj `if test -f 'debug.c'; then $(CYGPATH_W) 'debug.c'; else $(CYGPATH_W) '$(srcdir)/debug.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-debug.Tpo $(DEPDIR)/pdnsd-debug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='debug.c' object='pdnsd-debug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-debug.obj `if test -f 'debug.c'; then $(CYGPATH_W) 'debug.c'; else $(CYGPATH_W) '$(srcdir)/debug.c'; fi` + +pdnsd-dns.o: dns.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns.o -MD -MP -MF $(DEPDIR)/pdnsd-dns.Tpo -c -o pdnsd-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns.Tpo $(DEPDIR)/pdnsd-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns.c' object='pdnsd-dns.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c + +pdnsd-dns.obj: dns.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns.obj -MD -MP -MF $(DEPDIR)/pdnsd-dns.Tpo -c -o pdnsd-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns.Tpo $(DEPDIR)/pdnsd-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns.c' object='pdnsd-dns.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` + +pdnsd-dns_answer.o: dns_answer.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_answer.o -MD -MP -MF $(DEPDIR)/pdnsd-dns_answer.Tpo -c -o pdnsd-dns_answer.o `test -f 'dns_answer.c' || echo '$(srcdir)/'`dns_answer.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_answer.Tpo $(DEPDIR)/pdnsd-dns_answer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_answer.c' object='pdnsd-dns_answer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_answer.o `test -f 'dns_answer.c' || echo '$(srcdir)/'`dns_answer.c + +pdnsd-dns_answer.obj: dns_answer.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_answer.obj -MD -MP -MF $(DEPDIR)/pdnsd-dns_answer.Tpo -c -o pdnsd-dns_answer.obj `if test -f 'dns_answer.c'; then $(CYGPATH_W) 'dns_answer.c'; else $(CYGPATH_W) '$(srcdir)/dns_answer.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_answer.Tpo $(DEPDIR)/pdnsd-dns_answer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_answer.c' object='pdnsd-dns_answer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_answer.obj `if test -f 'dns_answer.c'; then $(CYGPATH_W) 'dns_answer.c'; else $(CYGPATH_W) '$(srcdir)/dns_answer.c'; fi` + +pdnsd-dns_query.o: dns_query.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_query.o -MD -MP -MF $(DEPDIR)/pdnsd-dns_query.Tpo -c -o pdnsd-dns_query.o `test -f 'dns_query.c' || echo '$(srcdir)/'`dns_query.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_query.Tpo $(DEPDIR)/pdnsd-dns_query.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_query.c' object='pdnsd-dns_query.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_query.o `test -f 'dns_query.c' || echo '$(srcdir)/'`dns_query.c + +pdnsd-dns_query.obj: dns_query.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-dns_query.obj -MD -MP -MF $(DEPDIR)/pdnsd-dns_query.Tpo -c -o pdnsd-dns_query.obj `if test -f 'dns_query.c'; then $(CYGPATH_W) 'dns_query.c'; else $(CYGPATH_W) '$(srcdir)/dns_query.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-dns_query.Tpo $(DEPDIR)/pdnsd-dns_query.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dns_query.c' object='pdnsd-dns_query.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-dns_query.obj `if test -f 'dns_query.c'; then $(CYGPATH_W) 'dns_query.c'; else $(CYGPATH_W) '$(srcdir)/dns_query.c'; fi` + +pdnsd-error.o: error.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-error.o -MD -MP -MF $(DEPDIR)/pdnsd-error.Tpo -c -o pdnsd-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-error.Tpo $(DEPDIR)/pdnsd-error.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error.c' object='pdnsd-error.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c + +pdnsd-error.obj: error.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-error.obj -MD -MP -MF $(DEPDIR)/pdnsd-error.Tpo -c -o pdnsd-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-error.Tpo $(DEPDIR)/pdnsd-error.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error.c' object='pdnsd-error.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi` + +pdnsd-helpers.o: helpers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-helpers.o -MD -MP -MF $(DEPDIR)/pdnsd-helpers.Tpo -c -o pdnsd-helpers.o `test -f 'helpers.c' || echo '$(srcdir)/'`helpers.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-helpers.Tpo $(DEPDIR)/pdnsd-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='helpers.c' object='pdnsd-helpers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-helpers.o `test -f 'helpers.c' || echo '$(srcdir)/'`helpers.c + +pdnsd-helpers.obj: helpers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-helpers.obj -MD -MP -MF $(DEPDIR)/pdnsd-helpers.Tpo -c -o pdnsd-helpers.obj `if test -f 'helpers.c'; then $(CYGPATH_W) 'helpers.c'; else $(CYGPATH_W) '$(srcdir)/helpers.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-helpers.Tpo $(DEPDIR)/pdnsd-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='helpers.c' object='pdnsd-helpers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-helpers.obj `if test -f 'helpers.c'; then $(CYGPATH_W) 'helpers.c'; else $(CYGPATH_W) '$(srcdir)/helpers.c'; fi` + +pdnsd-icmp.o: icmp.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-icmp.o -MD -MP -MF $(DEPDIR)/pdnsd-icmp.Tpo -c -o pdnsd-icmp.o `test -f 'icmp.c' || echo '$(srcdir)/'`icmp.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-icmp.Tpo $(DEPDIR)/pdnsd-icmp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='icmp.c' object='pdnsd-icmp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-icmp.o `test -f 'icmp.c' || echo '$(srcdir)/'`icmp.c + +pdnsd-icmp.obj: icmp.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-icmp.obj -MD -MP -MF $(DEPDIR)/pdnsd-icmp.Tpo -c -o pdnsd-icmp.obj `if test -f 'icmp.c'; then $(CYGPATH_W) 'icmp.c'; else $(CYGPATH_W) '$(srcdir)/icmp.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-icmp.Tpo $(DEPDIR)/pdnsd-icmp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='icmp.c' object='pdnsd-icmp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-icmp.obj `if test -f 'icmp.c'; then $(CYGPATH_W) 'icmp.c'; else $(CYGPATH_W) '$(srcdir)/icmp.c'; fi` + +pdnsd-list.o: list.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-list.o -MD -MP -MF $(DEPDIR)/pdnsd-list.Tpo -c -o pdnsd-list.o `test -f 'list.c' || echo '$(srcdir)/'`list.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-list.Tpo $(DEPDIR)/pdnsd-list.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='list.c' object='pdnsd-list.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-list.o `test -f 'list.c' || echo '$(srcdir)/'`list.c + +pdnsd-list.obj: list.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-list.obj -MD -MP -MF $(DEPDIR)/pdnsd-list.Tpo -c -o pdnsd-list.obj `if test -f 'list.c'; then $(CYGPATH_W) 'list.c'; else $(CYGPATH_W) '$(srcdir)/list.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-list.Tpo $(DEPDIR)/pdnsd-list.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='list.c' object='pdnsd-list.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-list.obj `if test -f 'list.c'; then $(CYGPATH_W) 'list.c'; else $(CYGPATH_W) '$(srcdir)/list.c'; fi` + +pdnsd-main.o: main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-main.o -MD -MP -MF $(DEPDIR)/pdnsd-main.Tpo -c -o pdnsd-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-main.Tpo $(DEPDIR)/pdnsd-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='pdnsd-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c + +pdnsd-main.obj: main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-main.obj -MD -MP -MF $(DEPDIR)/pdnsd-main.Tpo -c -o pdnsd-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-main.Tpo $(DEPDIR)/pdnsd-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='pdnsd-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` + +pdnsd-netdev.o: netdev.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-netdev.o -MD -MP -MF $(DEPDIR)/pdnsd-netdev.Tpo -c -o pdnsd-netdev.o `test -f 'netdev.c' || echo '$(srcdir)/'`netdev.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-netdev.Tpo $(DEPDIR)/pdnsd-netdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='netdev.c' object='pdnsd-netdev.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-netdev.o `test -f 'netdev.c' || echo '$(srcdir)/'`netdev.c + +pdnsd-netdev.obj: netdev.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-netdev.obj -MD -MP -MF $(DEPDIR)/pdnsd-netdev.Tpo -c -o pdnsd-netdev.obj `if test -f 'netdev.c'; then $(CYGPATH_W) 'netdev.c'; else $(CYGPATH_W) '$(srcdir)/netdev.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-netdev.Tpo $(DEPDIR)/pdnsd-netdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='netdev.c' object='pdnsd-netdev.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-netdev.obj `if test -f 'netdev.c'; then $(CYGPATH_W) 'netdev.c'; else $(CYGPATH_W) '$(srcdir)/netdev.c'; fi` + +pdnsd-rr_types.o: rr_types.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-rr_types.o -MD -MP -MF $(DEPDIR)/pdnsd-rr_types.Tpo -c -o pdnsd-rr_types.o `test -f 'rr_types.c' || echo '$(srcdir)/'`rr_types.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-rr_types.Tpo $(DEPDIR)/pdnsd-rr_types.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rr_types.c' object='pdnsd-rr_types.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-rr_types.o `test -f 'rr_types.c' || echo '$(srcdir)/'`rr_types.c + +pdnsd-rr_types.obj: rr_types.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-rr_types.obj -MD -MP -MF $(DEPDIR)/pdnsd-rr_types.Tpo -c -o pdnsd-rr_types.obj `if test -f 'rr_types.c'; then $(CYGPATH_W) 'rr_types.c'; else $(CYGPATH_W) '$(srcdir)/rr_types.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-rr_types.Tpo $(DEPDIR)/pdnsd-rr_types.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rr_types.c' object='pdnsd-rr_types.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-rr_types.obj `if test -f 'rr_types.c'; then $(CYGPATH_W) 'rr_types.c'; else $(CYGPATH_W) '$(srcdir)/rr_types.c'; fi` + +pdnsd-status.o: status.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-status.o -MD -MP -MF $(DEPDIR)/pdnsd-status.Tpo -c -o pdnsd-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-status.Tpo $(DEPDIR)/pdnsd-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='status.c' object='pdnsd-status.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c + +pdnsd-status.obj: status.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-status.obj -MD -MP -MF $(DEPDIR)/pdnsd-status.Tpo -c -o pdnsd-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-status.Tpo $(DEPDIR)/pdnsd-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='status.c' object='pdnsd-status.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` + +pdnsd-servers.o: servers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-servers.o -MD -MP -MF $(DEPDIR)/pdnsd-servers.Tpo -c -o pdnsd-servers.o `test -f 'servers.c' || echo '$(srcdir)/'`servers.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-servers.Tpo $(DEPDIR)/pdnsd-servers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='servers.c' object='pdnsd-servers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-servers.o `test -f 'servers.c' || echo '$(srcdir)/'`servers.c + +pdnsd-servers.obj: servers.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-servers.obj -MD -MP -MF $(DEPDIR)/pdnsd-servers.Tpo -c -o pdnsd-servers.obj `if test -f 'servers.c'; then $(CYGPATH_W) 'servers.c'; else $(CYGPATH_W) '$(srcdir)/servers.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-servers.Tpo $(DEPDIR)/pdnsd-servers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='servers.c' object='pdnsd-servers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-servers.obj `if test -f 'servers.c'; then $(CYGPATH_W) 'servers.c'; else $(CYGPATH_W) '$(srcdir)/servers.c'; fi` + +pdnsd-thread.o: thread.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-thread.o -MD -MP -MF $(DEPDIR)/pdnsd-thread.Tpo -c -o pdnsd-thread.o `test -f 'thread.c' || echo '$(srcdir)/'`thread.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-thread.Tpo $(DEPDIR)/pdnsd-thread.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='thread.c' object='pdnsd-thread.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-thread.o `test -f 'thread.c' || echo '$(srcdir)/'`thread.c + +pdnsd-thread.obj: thread.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-thread.obj -MD -MP -MF $(DEPDIR)/pdnsd-thread.Tpo -c -o pdnsd-thread.obj `if test -f 'thread.c'; then $(CYGPATH_W) 'thread.c'; else $(CYGPATH_W) '$(srcdir)/thread.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-thread.Tpo $(DEPDIR)/pdnsd-thread.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='thread.c' object='pdnsd-thread.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-thread.obj `if test -f 'thread.c'; then $(CYGPATH_W) 'thread.c'; else $(CYGPATH_W) '$(srcdir)/thread.c'; fi` + +pdnsd-cache.o: cache.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-cache.o -MD -MP -MF $(DEPDIR)/pdnsd-cache.Tpo -c -o pdnsd-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-cache.Tpo $(DEPDIR)/pdnsd-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cache.c' object='pdnsd-cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c + +pdnsd-cache.obj: cache.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-cache.obj -MD -MP -MF $(DEPDIR)/pdnsd-cache.Tpo -c -o pdnsd-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-cache.Tpo $(DEPDIR)/pdnsd-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cache.c' object='pdnsd-cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` + +pdnsd-hash.o: hash.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-hash.o -MD -MP -MF $(DEPDIR)/pdnsd-hash.Tpo -c -o pdnsd-hash.o `test -f 'hash.c' || echo '$(srcdir)/'`hash.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-hash.Tpo $(DEPDIR)/pdnsd-hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hash.c' object='pdnsd-hash.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-hash.o `test -f 'hash.c' || echo '$(srcdir)/'`hash.c + +pdnsd-hash.obj: hash.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -MT pdnsd-hash.obj -MD -MP -MF $(DEPDIR)/pdnsd-hash.Tpo -c -o pdnsd-hash.obj `if test -f 'hash.c'; then $(CYGPATH_W) 'hash.c'; else $(CYGPATH_W) '$(srcdir)/hash.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdnsd-hash.Tpo $(DEPDIR)/pdnsd-hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hash.c' object='pdnsd-hash.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pdnsd_CFLAGS) $(CFLAGS) -c -o pdnsd-hash.obj `if test -f 'hash.c'; then $(CYGPATH_W) 'hash.c'; else $(CYGPATH_W) '$(srcdir)/hash.c'; fi` + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-sbinPROGRAMS + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ + install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic \ + clean-sbinPROGRAMS ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ + ps ps-am tags tags-recursive uninstall uninstall-am \ + uninstall-sbinPROGRAMS + + +$(pdnsd_OBJECTS): rr_types.h + +rr_types.h: make_rr_types_h.pl rr_types.in + perl make_rr_types_h.pl rr_types.in > rr_types.h + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/cache.c b/service/src/main/jni/pdnsd/src/cache.c new file mode 100644 index 0000000..4fbb7fb --- /dev/null +++ b/service/src/main/jni/pdnsd/src/cache.c @@ -0,0 +1,2723 @@ +/* cache.c - Keep the dns caches. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2005, 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cache.h" +#include "hash.h" +#include "conff.h" +#include "helpers.h" +#include "dns.h" +#include "error.h" +#include "debug.h" +#include "thread.h" +#include "ipvers.h" + + +/* A version identifier to prevent reading incompatible cache files */ +static const char cachverid[] = {'p','d','1','3'}; + +/* CACHE STRUCTURE CHANGES IN PDNSD 1.0.0 + * Prior to version 1.0.0, the cache was managed at domain granularity (all records of a domain were handled as a unit), + * which was suboptimal after the lean query feature and the additional record management were included. + * From 1.0.0 on, the cache management was switched to act with RR set granularity. The API of the cache handlers was + * slightly modified, in particular the rr_bucket_t was modified and some parameter list were changed. The cache + * file format had to be changed and is incompatible now. This means that post-1.0.0p1 versions will not read the cache + * files of older versions and vice versa. In addition, cache files from 1.0.0p5 on are incompatible to those of 1.0.0p1 + * to 1.0.0p4. Better delete them before upgrading. + * The "cent" lists common to old versions have vanished; the only access point to the cent's is the hash. + * However, there are now double linked rrset lists. Thus, rrs can be acces through the hash or through the rrset lists. + * The rrset list entries need some additional entries to manage the deletion from rrs lists as well as from the cents. + * + * Nearly all cache functions had to be changed significantly or even to be rewritten for that. Expect some beta time + * because of that. + * There are bonuses visible to the users resulting from this changes however: more consistent cache handling (under + * some circumstances, rrs could be in the cache more than once) and reduced memory requirements, as no rr needs + * to have stored its oname any more. There are more pointers however, and in some cases (CNAMES) the memory require- + * ments for some records may increase. The total should be lower, however. + * + * RRSET_L LIST STRUCTURE: + * The rrset_l rrset list is a simple double-linked list. The oldest entries are at the first positions, the list is sorted + * by age in descending order. Search is done only on insert. + * The rationale for this form is: + * - the purging operation needs to be fast (this way, the first records are the oldest and can easily be purged) + * - the append operation is common and needs to be fast (in normal operation, an appended record was just retrieved + * and therefore is the newest, so it can be appended at the end of the list without search. Only in the case of + * reading a disk cache file, searches are necessary) + * The rrset list is excusively used for purging purposes. + * + * THE DISK CACHE FILES: + * The disk cache file consists of cent's, i.e. structures for every known hostnames with a header and rrs attached to it. + * Therefore, the rr's are not ordered by their age and a search must be performed to insert the into the rr_l in the + * right positions. This operations has some costs (although not all too much), but the other way (rrs stored in order + * of their age and the cent headers separated from them), the rrs would need to be attached to the cent headers, which + * would be even more costly, also in means of disk space. + * + * CHANGES AFTER 1.0.0p1 + * In 1.0.0p5, the cache granularity was changed from rr level to rr set level. This was done because rfc2181 demands + * rr set consistency constraints on rr set level and if we are doing so we can as well save space (and eliminate some + * error-prone algorithms). + * + * CHANGES FOR 1.1.0p1 + * In this version, negative caching support was introduced. Following things were changed for that: + * - new members ts, ttl and flags in dns_cent_t and dns_file_t + * - new caching flag CF_NEGATIVE + * - all functions must accept and deal correctly with empty cents with DF_NEGATIVE set. + * - all functions must accept and deal correctly with empty rrsets with CF_NEGATIVE set. + */ + + +/* + * This is the size the memory cache may exceed the size of the permanent cache. + */ +#define MCSZ 10240 + +/* Some structs used for storing cache entries in a file. */ +typedef struct { + unsigned short rdlen; +/* data (with length rdlen) follows here;*/ +} rr_fbucket_t; + +typedef struct { + unsigned char tp; /* RR type */ + unsigned char num_rr; /* Number of records in RR set. */ + unsigned short flags; /* Flags for RR set. */ + time_t ttl; + time_t ts; +} __attribute__((packed)) +rr_fset_t; + +#if NRRTOT>255 +#warning "Number of cache-able RR types is greater than 255. This can give problems when saving the cache to file." +#endif + +typedef struct { + unsigned char qlen; /* Length of the domain name which follows after the struct. */ + unsigned char num_rrs; /* Number of RR-sets. */ + unsigned short flags; /* Flags for the whole cent. */ + unsigned char c_ns,c_soa; /* Number of trailing name elements in qname to use to find NS or SOA + records to add to the authority section of a response. */ + /* ttl and ts follow but only for negatively cached domains. */ + /* qname (with length qlen) follows here. */ +} __attribute__((packed)) +dns_file_t; + + +/* TTL and timestamp for negatively cached domains. */ +typedef struct { + time_t ttl; + time_t ts; +} __attribute__((packed)) +dom_fttlts_t; + +/* + * This has two modes: Normally, we have rrset, cent and idx filled in; + * for negatively cached cents, we have rrset set to NULL and idx set to -1. + */ +typedef struct rr_lent_s { + struct rr_lent_s *next; + struct rr_lent_s *prev; + rr_set_t *rrset; + dns_cent_t *cent; + int idx; /* This is the array index, not the type of the RR-set. */ +} rr_lent_t; + + +static rr_lent_t *rrset_l=NULL; +static rr_lent_t *rrset_l_tail=NULL; + +/* + * We do not count the hash table sizes here. Those are very small compared + * to the cache entries. + */ +static volatile long cache_size=0; +static volatile long ent_num=0; + +static volatile int cache_w_lock=0; +static volatile int cache_r_lock=0; + +pthread_mutex_t lock_mutex = PTHREAD_MUTEX_INITIALIZER; +/* + * These are condition variables for lock coordination, so that normal lock + * routines do not need to loop. Basically, a process wanting to acquire a lock + * tries first to lock, and if the lock is busy, sleeps on one of the conds. + * If the r lock count has gone to zero one process sleeping on the rw cond + * will be awankened. + * If the rw lock is lifted, either all threads waiting on the r lock or one + * thread waiting on the rw lock is/are awakened. This is determined by policy. + */ +pthread_cond_t rw_cond = PTHREAD_COND_INITIALIZER; +pthread_cond_t r_cond = PTHREAD_COND_INITIALIZER; + +/* This is to suspend the r lock to avoid lock contention by reading threads */ +static volatile int r_pend=0; +static volatile int rw_pend=0; +static volatile int r_susp=0; + +/* This threshold is used to temporarily suspend r locking to give rw locking + * a chance. */ +#define SUSP_THRESH(r_pend) (r_pend/2+2) + +/* + * This is set to 1 once the lock is intialized. This must happen before we get + * multiple threads. + */ +volatile short int use_cache_lock=0; + +/* + This is set to 0 while cache is read from disk. + This must be set to 1 before we start adding new entries. +*/ +static short int insert_sort=1; + + +#ifdef ALLOC_DEBUG +#define cache_free(ptr) { if (dbg) pdnsd_free(ptr); else free(ptr); } +#define cache_malloc(sz) ((dbg)?(pdnsd_malloc(sz)):(malloc(sz))) +#define cache_calloc(n,sz) ((dbg)?(pdnsd_calloc(n,sz)):(calloc(n,sz))) +#define cache_realloc(ptr,sz) ((dbg)?(pdnsd_realloc(ptr,sz)):(realloc(ptr,sz))) +#else +#define cache_free(ptr) {free(ptr);} +#define cache_malloc(sz) (malloc(sz)) +#define cache_calloc(n,sz) (calloc(n,sz)) +#define cache_realloc(ptr,sz) (realloc(ptr,sz)) +#endif + + +/* + * Prototypes for internal use + */ +static void purge_cache(long sz, int lazy); +static void del_cache_ent(dns_cent_t *cent,dns_hash_loc_t *loc); +static void remove_rrl(rr_lent_t *le DBGPARAM); + +/* + * Locking functions. + */ + +/* + * Lock/unlock cache for reading. Concurrent reads are allowed, while writes are forbidden. + * DO NOT MIX THE LOCK TYPES UP WHEN LOCKING/UNLOCKING! + * + * We use a mutex to lock the access to the locks ;-). + * This is because we do not allow read and write to interfere (for which a normal mutex would be + * fine), but we also want to allow concurrent reads. + * We use condition variables, and readlock contention protection. + */ +static void lock_cache_r(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + r_pend++; + while(((rw_pend>SUSP_THRESH(r_pend))?(r_susp=1):r_susp) || cache_w_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + pthread_cond_wait(&r_cond, &lock_mutex); + } + cache_r_lock++; + r_pend--; + pthread_mutex_unlock(&lock_mutex); +} + +static void unlock_cache_r(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + if (cache_r_lock>0) + cache_r_lock--; + /* wakeup threads waiting to write */ + if (!cache_r_lock) + pthread_cond_signal(&rw_cond); + pthread_mutex_unlock(&lock_mutex); +} + +/* + * Lock/unlock cache for reading and writing. Concurrent reads and writes are forbidden. + * Do this only if you actually modify the cache. + * DO NOT MIX THE LOCK TYPES UP WHEN LOCKING/UNLOCKING! + * (cant say it often enough) + */ +static void lock_cache_rw(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + rw_pend++; + while(cache_w_lock || cache_r_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + pthread_cond_wait(&rw_cond, &lock_mutex); + } + cache_w_lock=1; + rw_pend--; + pthread_mutex_unlock(&lock_mutex); +} + +/* Lock cache for reading and writing, or time out after tm seconds. */ +static int timedlock_cache_rw(int tm) +{ + int retval=0; + struct timeval now; + struct timespec timeout; + + if (!use_cache_lock) + return 0; + pthread_mutex_lock(&lock_mutex); + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + tm; + timeout.tv_nsec = now.tv_usec * 1000; + rw_pend++; + while(cache_w_lock || cache_r_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + if(pthread_cond_timedwait(&rw_cond, &lock_mutex, &timeout) == ETIMEDOUT) + goto cleanup_return; + } + cache_w_lock=1; + retval=1; + cleanup_return: + rw_pend--; + pthread_mutex_unlock(&lock_mutex); + return retval; +} + +static void unlock_cache_rw(void) +{ + if (!use_cache_lock) + return; + pthread_mutex_lock(&lock_mutex); + cache_w_lock=0; + /* always reset r suspension (r locking code will set it again) */ + r_susp=0; + /* wakeup threads waiting to read or write */ + if (r_pend==0 || rw_pend>SUSP_THRESH(r_pend)) + pthread_cond_signal(&rw_cond); /* schedule another rw proc */ + else + pthread_cond_broadcast(&r_cond); /* let 'em all read */ + pthread_mutex_unlock(&lock_mutex); +} + + +/* + If there are other threads waiting to read from or write to + the cache, give up the read/write lock on the cache to give another + thread a chance; then try to get the lock back again. + This can be called regularly during a process that takes + a lot of processor time but has low priority, in order to improve + overall responsiveness. +*/ +static void yield_lock_cache_rw() +{ + if (!use_cache_lock || (!r_pend && !rw_pend)) + return; + + /* Give up the lock */ + pthread_mutex_lock(&lock_mutex); + cache_w_lock=0; + /* always reset r suspension (r locking code will set it again) */ + r_susp=0; + /* wakeup threads waiting to read or write */ + if (r_pend==0 || rw_pend>SUSP_THRESH(r_pend)) + pthread_cond_signal(&rw_cond); /* schedule another rw proc */ + else + pthread_cond_broadcast(&r_cond); /* let 'em all read */ + pthread_mutex_unlock(&lock_mutex); + + usleep_r(1000); + + /* Now try to get the lock back again */ + pthread_mutex_lock(&lock_mutex); + rw_pend++; + while(cache_w_lock || cache_r_lock) { + /* This will unlock the mutex while sleeping and relock it before exit */ + pthread_cond_wait(&rw_cond, &lock_mutex); + } + cache_w_lock=1; + rw_pend--; + pthread_mutex_unlock(&lock_mutex); +} + +/* These are a special version of the ordinary read lock functions. The lock "soft" to avoid deadlocks: they will give up + * after a certain number of bad trials. You have to check the exit status though. + * To avoid blocking mutexes, we cannot use condition variables here. Never mind, these are only used on + * exit. */ +static int softlock_cache_r(void) +{ + if (!use_cache_lock) + return 0; + { + int lk=0,tr=0; + + for(;;) { + if (!softlock_mutex(&lock_mutex)) + return 0; + if(!cache_w_lock) { + lk=1; + cache_r_lock++; + } + pthread_mutex_unlock(&lock_mutex); + if (lk) break; + if (++tr>=SOFTLOCK_MAXTRIES) + return 0; + usleep_r(1000); /*give contol back to the scheduler instead of hammering the lock close*/ + } + } + return 1; +} + +/* On unlocking, we do not wake others. We are about to exit! */ +static int softunlock_cache_r(void) +{ + if (!use_cache_lock) + return 0; + if (!softlock_mutex(&lock_mutex)) + return 0; + if (cache_r_lock>0) + cache_r_lock--; + pthread_mutex_unlock(&lock_mutex); + return 1; +} + +static int softlock_cache_rw(void) +{ + if (!use_cache_lock) + return 0; + { + int lk=0,tr=0; + + for(;;) { + if (!softlock_mutex(&lock_mutex)) + return 0; + if (!(cache_w_lock || cache_r_lock)) { + lk=1; + cache_w_lock=1; + } + pthread_mutex_unlock(&lock_mutex); + if(lk) break; + if (++tr>=SOFTLOCK_MAXTRIES) + return 0; + usleep_r(1000); /*give contol back to the scheduler instead of hammering the lock close*/ + } + } + return 1; +} + +static int softunlock_cache_rw(void) +{ + if (!use_cache_lock) + return 0; + if (!softlock_mutex(&lock_mutex)) + return 0; + cache_w_lock=0; + pthread_mutex_unlock(&lock_mutex); + return 1; +} + +/* + * Serial numbers: Serial numbers are used when additional records are added to the cache: serial numbers are unique to each + * query, so we can determine whether data was added by the query just executed (records can coexist) or not (records must + * be replaced). A serial of 0 is special and will not be used by any query. All records added added authoritatively (as + * chunk) or read from a file can have no query in process and therefore have serial 0, which is != any other serial. + */ +#if 0 +unsigned long l_serial=1; + +unsigned long get_serial() +{ + unsigned long rv; + lock_cache_rw(); + rv=l_serial++; + unlock_cache_rw(); + return rv; +} +#endif + +/* + * Cache/cent handlers + */ + +/* Initialize the cache. Call only once. */ +#if 0 +void init_cache() +{ + mk_hash_ctable(); + mk_dns_hash(); +} +#endif + +/* Initialize the cache lock. Call only once. */ +/* This is now defined as an inline function in cache.h */ +#if 0 +void init_cache_lock() +{ + + use_cache_lock=1; +} +#endif + +/* Empty the cache, freeing all entries that match the include/exclude list. */ +int empty_cache(slist_array sla) +{ + int i; + + /* Wait at most 60 seconds to obtain a lock. */ + if(!timedlock_cache_rw(60)) + return 0; + + for(i=0; ; ) { + if(sla) + free_dns_hash_selected(i,sla); + else + free_dns_hash_bucket(i); + if(++i>=HASH_NUM_BUCKETS) + break; + /* Give another thread a chance */ + yield_lock_cache_rw(); + } + + unlock_cache_rw(); + return 1; +} + +/* Delete the cache. Call only once */ +void destroy_cache() +{ + /* lock the cache, in case that any thread is still accessing. */ + if(!softlock_cache_rw()) { + log_error("Lock failed; could not destroy cache on exit."); + return; + } + free_dns_hash(); +#if DEBUG>0 + if(ent_num || cache_size) { + DEBUG_MSG("After destroying cache, %ld entries (%ld bytes) remaining.\n",ent_num,cache_size); + } +#endif + +#if 0 +#if (TARGET!=TARGET_LINUX) + /* under Linux, this frees no resources but may hang on a crash */ + pthread_mutex_destroy(&lock_mutex); + pthread_cond_destroy(&rw_cond); + pthread_cond_destroy(&r_cond); +#endif +#endif +} + +/* Make a flag value for a dns_cent_t (dns cache entry) from a server record */ +/* Now defined as inline function in cache.h */ +#if 0 +unsigned int mk_flag_val(servparm_t *server) +{ + unsigned int fl=0; + if (!server->purge_cache) + fl|=CF_NOPURGE; + if (server->nocache) + fl|=CF_NOCACHE; + if (server->rootserver) + fl|=CF_ROOTSERV; + return fl; +} +#endif + +/* Initialize a dns cache record (dns_cent_t) with the query name (in + * transport format), a flag value, a timestamp indicating + * the time the query was done, and a TTL. The timestamp and TTL + * are only used if DF_NEGATIVE is set in the flags. Otherwise, + * the timestamps of the individual records are used. DF_NEGATIVE + * is used for whole-domain negative caching. + * By convention, ttl and ts should be set to 0, unless the + * DF_NEGATIVE bit is set. */ +int init_cent(dns_cent_t *cent, const unsigned char *qname, time_t ttl, time_t ts, unsigned flags DBGPARAM) +{ + int i; + size_t namesz=rhnlen(qname); + + cent->qname=cache_malloc(namesz); + if (cent->qname == NULL) + return 0; + memcpy(cent->qname,qname,namesz); + cent->cs=sizeof(dns_cent_t)+namesz; + cent->num_rrs=0; + cent->flags=flags; + if(flags&DF_NEGATIVE) { + cent->neg.lent=NULL; + cent->neg.ttl=ttl; + cent->neg.ts=ts; + } + else { + for(i=0; irr.rrmu[i]=NULL; + cent->rr.rrext=NULL; + } + cent->c_ns=cundef; + cent->c_soa=cundef; + return 1; +} + +/* + * Create a rr record holder using the given values. + */ +static rr_bucket_t *create_rr(unsigned dlen, void *data DBGPARAM) +{ + rr_bucket_t *rrb; + rrb=(rr_bucket_t *)cache_malloc(sizeof(rr_bucket_t)+dlen); + if (rrb == NULL) + return NULL; + rrb->next=NULL; + + rrb->rdlen=dlen; + memcpy(rrb->data,data,dlen); + return rrb; +} + +/* + * Adds an empty rrset_t with the requested data to a cent. This is exactly what you need to + * do to create a negatively cached cent. + */ +static int add_cent_rrset_by_index(dns_cent_t *cent, unsigned int idx, time_t ttl, time_t ts, unsigned flags DBGPARAM) +{ + rr_set_t **rrext, **rrsetpa, *rrset; + + /* If we add a rrset, even a negative one, the domain is not negative any more. */ + if (cent->flags&DF_NEGATIVE) { + int i; + /* need to remove the cent from the lent list. */ + if (cent->neg.lent) + remove_rrl(cent->neg.lent DBGARG); + cent->flags &= ~DF_NEGATIVE; + for(i=0; irr.rrmu[i]=NULL; + cent->rr.rrext=NULL; + } + + if(idx < NRRMU) + rrsetpa = ¢->rr.rrmu[idx]; + else { + idx -= NRRMU; + PDNSD_ASSERT(idx < NRREXT, "add_cent_rrset_by_index: rr-set index out of range"); + rrext = cent->rr.rrext; + if(!rrext) { + int i; + cent->rr.rrext = rrext = cache_malloc(sizeof(rr_set_t*)*NRREXT); + if(!rrext) + return 0; + for(i=0; ics += sizeof(rr_set_t*)*NRREXT; + } + rrsetpa = &rrext[idx]; + } + +#if 0 + if(*rrsetpa) del_rrset(*rrsetpa); +#endif + *rrsetpa = rrset = cache_malloc(sizeof(rr_set_t)); + if (!rrset) + return 0; + rrset->lent=NULL; + rrset->ttl=ttl; + rrset->ts=ts; + rrset->flags=flags; + rrset->rrs=NULL; + cent->cs += sizeof(rr_set_t); + ++cent->num_rrs; + return 1; +} + +int add_cent_rrset_by_type(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags DBGPARAM) +{ + int tpi = type - T_MIN; + + PDNSD_ASSERT(tpi>=0 && tpinext=rrset->rrs; + rrset->rrs=rr; + } + else { + /* append at the end */ + rr->next=(*rtail)->next; + (*rtail)->next=rr; + } + if(rtail) *rtail=rr; + cent->cs += sizeof(rr_bucket_t)+rr->rdlen; +#if DEBUG>0 + if(debug_p) { + rrset = RRARR_INDEX(cent,idx); + if (rrset->flags&CF_NEGATIVE) { + char cflagstr[CFLAGSTRLEN]; + DEBUG_MSG("Tried to add rr to a rrset with CF_NEGATIVE set! flags=%s\n",cflags2str(rrset->flags,cflagstr)); + } + } +#endif + return 1; + + cleanup_return: + free_rr(*rr); + free(rr); + return 0; +} + + +/* Add an rr to a cache entry, giving the ttl, the data length, the rr type + * and a pointer to the data. A record is allocated, and the data is copied into + * it. Do this for all rrs in a cache entry. + * The return value will be 1 in case of success, or 0 in case of a memory allocation + * problem. + */ +int add_cent_rr(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags, + unsigned dlen, void *data DBGPARAM) +{ + int tpi; + unsigned int idx; + rr_set_t *rrset; + rr_bucket_t *rtail, *rrb; + + if ((cent->flags&DF_LOCAL) && !(flags&CF_LOCAL)) + return 1; /* ignore. Local has precedence. */ + + tpi = type - T_MIN; + PDNSD_ASSERT(tpi>=0 && tpittl) + /* The ttl timestamps should be identical. + In case they are not, we will use the smallest one. */ + rrset->ttl= ttl; + + /* OK, some stupid nameservers feel inclined to return the same address twice. Grmbl... */ + rrb=rrset->rrs; + while (rrb) { + if (rrb->rdlen==dlen && memcmp(rrb->data,data,dlen)==0) + return 1; + rtail=rrb; + rrb=rrb->next; + } + } + return add_cent_rr_int(cent,idx,ttl,ts,flags,dlen,data,&rtail DBGARG); +} + +/* Free a complete rrset including all memory. Returns the size of the memory freed */ +int del_rrset(rr_set_t *rrs DBGPARAM) +{ + int rv=sizeof(rr_set_t); + rr_bucket_t *rrb,*rrn; + + if(rrs->lent) remove_rrl(rrs->lent DBGARG); + rrb=rrs->rrs; + while (rrb) { + rv+=sizeof(rr_bucket_t)+rrb->rdlen; + rrn=rrb->next; + free_rr(*rrb); + cache_free(rrb); + rrb=rrn; + } + cache_free(rrs); + return rv; +} + +/* Remove a complete rrset from a cent, freeing the memory. + The second argument should be an RR-set array index, not an RR type! + Returns the size of the memory freed */ +static int del_cent_rrset_by_index(dns_cent_t *cent, int i DBGPARAM) +{ + int rv=0; + rr_set_t **rrspa = RRARR_INDEX_PA_TESTEXT(cent,i); + + if(rrspa) { + rr_set_t *rrs = *rrspa; + if(rrs) { + rv= del_rrset(rrs DBGARG); + *rrspa=NULL; + --cent->num_rrs; + cent->cs -= rv; + cent->flags &= ~DF_AUTH; + } + } + return rv; +} + +#if 0 +static int del_cent_rrset_by_type(dns_cent_t *cent, int type DBGPARAM) +{ + return del_cent_rrset_by_index(cent, rrlkuptab[type-T_MIN] DBGARG); +} +#endif + +#if 0 +/* Free the pointers contained in an rr record. If the rr record is on the heap, + * don't forget to delete itself. This is done extra mainly for extensibility + * -- This is not here any more. The definition is actually an empty macro in + * cache.h. + */ +void free_rr(rr_bucket_t rr) +{ +} +#endif + +/* Free all data referred by a cache entry. */ +void free_cent(dns_cent_t *cent DBGPARAM) +{ + cache_free(cent->qname); + if(cent->flags&DF_NEGATIVE) { + if(cent->neg.lent) + remove_rrl(cent->neg.lent DBGARG); + } + else { + int i; + for (i=0; irr.rrmu[i]; + if (rrs) del_rrset(rrs DBG0); + } + { + rr_set_t **rrext = cent->rr.rrext; + if(rrext) { + for(i=0; iflags&DF_NEGATIVE)) { + for (i=0; irr.rrmu[i]; + if (rrs) { + cent->cs -= del_rrset(rrs DBG0); + /* cent->rr.rrmu[i]=NULL; */ + } + } + { + rr_set_t **rrext = cent->rr.rrext; + if(rrext) { + for(i=0; ics -= del_rrset(rrs DBG0); + } + cache_free(rrext); + /* cent->rr.rrext=NULL; */ + cent->cs -= sizeof(rr_set_t*)*NRREXT; + } + } + cent->num_rrs=0; + cent->flags |= DF_NEGATIVE; + cent->neg.lent=NULL; + } + + cent->neg.ttl=ttl; + cent->neg.ts=ts; +} + +inline static time_t get_rrlent_ts(rr_lent_t *le) +{ + return (le->rrset)?(le->rrset->ts):(le->cent->neg.ts); +} + +/* insert a rrset into the rr_l list. This modifies the rr_set_t if rrs is not NULL! + * The rrset address needs to be constant afterwards. + * idx is the internally used RR-set index, not the RR type! + * Call with locks applied. */ +static int insert_rrl(rr_set_t *rrs, dns_cent_t *cent, int idx) +{ + time_t ts; + rr_lent_t *le,*ne; + + /* No need to add local records to the rr_l list, because purge_cache() ignores them anyway. */ + if((rrs && (rrs->flags&CF_LOCAL)) || (cent->flags&DF_LOCAL)) + return 1; + + if (!(ne=malloc(sizeof(rr_lent_t)))) + return 0; + ne->rrset=rrs; + ne->cent=cent; + ne->idx=idx; + ne->next=NULL; + ne->prev=NULL; + + if(insert_sort) { + /* Since the append at the and is a very common case (and we want this case to be fast), we search back-to-forth. + * Since rr_l is a list and we don't really have fast access to all elements, we do not perform an advanced algorithm + * like binary search.*/ + ts=get_rrlent_ts(ne); + le=rrset_l_tail; + while (le) { + if (ts>=get_rrlent_ts(le)) goto found; + le=le->prev; + } + /* not found, so it needs to be inserted at the start of the list. */ + ne->next=rrset_l; + if (rrset_l) + rrset_l->prev=ne; + else + rrset_l_tail=ne; + rrset_l=ne; + goto finish; + found: + ne->next=le->next; + ne->prev=le; + if (le->next) + le->next->prev=ne; + else + rrset_l_tail=ne; + le->next=ne; + finish:; + } + else { + /* simply append at the end, sorting will be done later with a more efficient algorithm. */ + ne->prev=rrset_l_tail; + if(rrset_l_tail) + rrset_l_tail->next=ne; + else + rrset_l=ne; + rrset_l_tail=ne; + } + + if (rrs) + rrs->lent=ne; + else + cent->neg.lent=ne; + + return 1; +} + +/* Remove a rr from the rr_l list. Call with locks applied. */ +static void remove_rrl(rr_lent_t *le DBGPARAM) +{ + rr_lent_t *next=le->next,*prev=le->prev; + if (next) + next->prev=prev; + else + rrset_l_tail=prev; + if (prev) + prev->next=next; + else + rrset_l=next; + cache_free(le); +} + + +/* Merge two sorted rr_l lists to make a larger sorted list. + The lists are sorted according to increasing time-stamp. + The back links are ignored, these must be fixed using a separate pass. +*/ +static rr_lent_t *listmerge(rr_lent_t *p, rr_lent_t *q) +{ + + if(!p) + return q; + else if(!q) + return p; + else { + rr_lent_t *l=NULL, **s= &l; + + for(;;) { + if(get_rrlent_ts(p) <= get_rrlent_ts(q)) { + *s= p; + s= &p->next; + p= *s; + if(!p) { + *s= q; + break; + } + } + else { /* get_rrlent_ts(p) > get_rrlent_ts(q) */ + *s= q; + s= &q->next; + q= *s; + if(!q) { + *s= p; + break; + } + } + } + + return l; + } +} + +/* Sort the rr_l list using merge sort, which can be more efficient than insertion sort used by rr_insert(). + This algorithm is adapted from the GNU C++ STL implementation for list containers. + Call with locks applied. + Written by Paul Rombouts. +*/ +static void sort_rrl() +{ + /* Do nothing unless the list has length >= 2. */ + if(rrset_l && rrset_l->next) { + /* First sort the list ignoring the back links, these will be fixed later. */ +# define NTMPSORT 32 + /* Because we use an array of fixed length, the length of the list we can sort + is bounded by pow(2,NTMPSORT)-1. */ + rr_lent_t *tmp[NTMPSORT]; /* tmp[i] will either be NULL or point to a sorted list of length pow(2,i). */ + rr_lent_t **fill= tmp, **end=tmp+NTMPSORT, **counter; + rr_lent_t *rem= rrset_l, *carry; + + do { + carry=rem; rem=rem->next; + carry->next=NULL; + for(counter = tmp; counter!=fill && *counter!=NULL; ++counter) { + carry=listmerge(*counter,carry); + *counter=NULL; + } + + PDNSD_ASSERT(counter!=end, "sort_rrl: tmp array overflowed"); + + *counter=carry; + + if(counter==fill) ++fill; + } + while(rem); + + /* Merge together all the remaining list fragments contained in array tmp. */ + carry= tmp[0]; + counter= tmp; + while(++counter!=fill) + carry=listmerge(*counter,carry); + + rrset_l= carry; + + { + /* Restore the backward links. */ + rr_lent_t *p,*q=NULL; + for(p=rrset_l; p; p=p->next) {p->prev=q; q=p;} + rrset_l_tail=q; + } + } +} + + +/* Copy a rr_bucket_t into newly allocated memory */ +inline static rr_bucket_t *copy_rr(rr_bucket_t *rr DBGPARAM) +{ + rr_bucket_t *rrn; + rrn=cache_malloc(sizeof(rr_bucket_t)+rr->rdlen); + if (rrn == NULL) + return NULL; + memcpy(rrn,rr,sizeof(rr_bucket_t)+rr->rdlen); + rrn->next=NULL; + return rrn; +} + + +/* Copy an RR set into newly allocated memory */ +static rr_set_t *copy_rrset(rr_set_t *rrset DBGPARAM) +{ + rr_set_t *rrsc=cache_malloc(sizeof(rr_set_t)); + rr_bucket_t *rr,**rrp; + if (rrsc) { + *rrsc=*rrset; + rrsc->lent=NULL; + rrp=&rrsc->rrs; + rr=rrset->rrs; + while(rr) { + rr_bucket_t *rrc=copy_rr(rr DBGARG); + *rrp=rrc; + if (!rrc) goto cleanup_return; + rrp=&rrc->next; + rr=rr->next; + } + } + return rrsc; + +cleanup_return: + del_rrset(rrsc DBG0); + return NULL; +} + + +/* Copy a cache entry into newly allocated memory */ +dns_cent_t *copy_cent(dns_cent_t *cent DBGPARAM) +{ + dns_cent_t *copy; + + /* + * We do not debug cache internals with it, as mallocs seem to be + * "lost" when they enter the cache for a longer time. + */ + if (!(copy=cache_malloc(sizeof(dns_cent_t)))) + return NULL; + + { + /* copy the name */ + size_t namesz=rhnlen(cent->qname); + if (!(copy->qname=cache_malloc(namesz))) + goto free_return_null; + + memcpy(copy->qname,cent->qname,namesz); + } + copy->cs= cent->cs; + copy->num_rrs= cent->num_rrs; + copy->flags= cent->flags; + copy->c_ns = cent->c_ns; + copy->c_soa= cent->c_soa; + if(cent->flags&DF_NEGATIVE) { + copy->neg.lent=NULL; + copy->neg.ttl= cent->neg.ttl; + copy->neg.ts = cent->neg.ts; + } + else { + int i, ilim; + for (i=0; irr.rrmu[i]=NULL; + copy->rr.rrext=NULL; + + ilim = NRRMU; + if(cent->rr.rrext) { + rr_set_t **rrextc; + ilim = NRRTOT; + copy->rr.rrext = rrextc = cache_malloc(sizeof(rr_set_t*)*NRREXT); + if(!rrextc) goto free_cent_return_null; + + for (i=0; ilent=NULL; + rrp=&rrsc->rrs; + rr=rrset->rrs; + while(rr) { + rr_bucket_t *rrc=copy_rr(rr DBGARG); + *rrp=rrc; + if (!rrc) goto free_cent_return_null; + rrp=&rrc->next; + rr=rr->next; + } + } + } + } + return copy; + + free_cent_return_null: + free_cent(copy DBGARG); + free_return_null: + cache_free(copy); + return NULL; +} + +/* + * Remove all timed out entries of the RR set with the given index. + * idx is the internally used RR-set index, not the RR type! + * Follow some rules based on flags etc. + * This will either delete the whole rrset, or will leave it as a whole (RFC2181 seems to + * go in that direction) + * This was pretty large once upon a time ;-), but now, since we operate in rrsets, was + * shrunk drastically. + * If test is zero and the record is in the cache, we need rw-locks applied. + * If test is nonzero, nothing will actually be deleted. + * Substracts the size of the freed memory from cache_size (if test is zero). + * Returns 1 if the rrset has been (or would have been) deleted. + */ +static int purge_rrset(dns_cent_t *cent, int idx, int test) +{ + rr_set_t *rrs= RRARR_INDEX_TESTEXT(cent,idx); + if (rrs && !(rrs->flags&CF_NOPURGE || rrs->flags&CF_LOCAL) && timedout(rrs)) { + /* well, it must go. */ + if(!test) + cache_size -= del_cent_rrset_by_index(cent,idx DBG0); + return 1; + } + return 0; +} + +/* + Remove all timed out entries of alls RR sets of a cache entry. + The test flag works the same as in purge_rrset(). + Substracts the size of the freed memory from cache_size, just as purge_rrset(). + *numrrsrem is set to the number of remaining RR sets (or the number that would have remained). + Returns the number of items (RR sets or RR set arrays) that have been (or would have been) deleted. +*/ +static int purge_all_rrsets(dns_cent_t *cent, int test, int *numrrsrem) +{ + int rv=0, numrrs=0, numrrext=0; + + if(!(cent->flags&DF_NEGATIVE)) { + int i, ilim= RRARR_LEN(cent); + for(i=0; iflags&CF_NOPURGE || rrs->flags&CF_LOCAL) && timedout(rrs)) { + /* well, it must go. */ + if(!test) + cache_size -= del_cent_rrset_by_index(cent, i DBG0); + ++rv; + } + else { + ++numrrs; + if(i>=NRRMU) ++numrrext; + } + } + } + + /* If the array of less frequently used RRs has become empty, free it. */ + if(cent->rr.rrext && numrrext==0) { + if(!test) { + cache_free(cent->rr.rrext); + cent->rr.rrext=NULL; + cent->cs -= sizeof(rr_set_t*)*NRREXT; + cache_size -= sizeof(rr_set_t*)*NRREXT; + } + ++rv; + } + } + + if(numrrsrem) *numrrsrem=numrrs; + return rv; +} + + +/* + * Purge a cent, deleting timed-out rrs (following the constraints noted in "purge_rrset"). + * Since the cent may actually become empty and be deleted, you may not use it after this call until + * you refetch its address from the hash (if it is still there). + * If test is zero and the record is in the cache, we need rw-locks applied. + * If test is nonzero, nothing will actually be deleted. + * Substracts the size of the freed memory from cache_size (if test is zero). + * If delete is nonzero and the cent was purged empty and no longer needed, it is removed from the cache. + * Returns -1 if the cent was (or would have been) completely removed, + * otherwise returns the number of items that were (or would have been) deleted. + */ +static int purge_cent(dns_cent_t *cent, int delete, int test) +{ + int npurge, numrrs; + + npurge = purge_all_rrsets(cent,test, &numrrs); + + /* If the cache entry was purged empty, delete it from the cache. */ + if (delete && numrrs==0 + && (!(cent->flags&DF_NEGATIVE) || + (!(cent->flags&DF_LOCAL) && timedout_nxdom(cent)))) + { + if(!test) + del_cache_ent(cent,NULL); /* this will subtract the cent's left size from cache_size */ + return -1; + } + + if(!(cent->flags&DF_LOCAL)) { + /* Set stale references to NS or SOA records back to undefined. */ + unsigned scnt=rhnsegcnt(cent->qname); + if(cent->c_ns!=cundef) { + rr_set_t *rrset=NULL; + if(cent->c_ns==scnt) + rrset=getrrset_NS(cent); + else if(cent->c_nsqname,scnt-cent->c_ns),NULL); + if(ce) rrset=getrrset_NS(ce); + } + if(!rrset || !rrset->rrs || (!(rrset->flags&CF_LOCAL) && timedout(rrset))) { + if(!test) + cent->c_ns=cundef; + ++npurge; + } + } + if(cent->c_soa!=cundef) { + rr_set_t *rrset=NULL; + if(cent->c_soa==scnt) + rrset=getrrset_SOA(cent); + else if(cent->c_soaqname,scnt-cent->c_soa),NULL); + if(ce) rrset=getrrset_SOA(ce); + } + if(!rrset || !rrset->rrs || (!(rrset->flags&CF_LOCAL) && timedout(rrset))) { + if(!test) + cent->c_soa=cundef; + ++npurge; + } + } + } + + return npurge; +} + +/* + * Bring cache to a size below or equal the cache size limit (sz). There are two strategies: + * - for cached sets with CF_NOPURGE not set: delete if timed out + * - additional: delete oldest sets. + */ +static void purge_cache(long sz, int lazy) +{ + rr_lent_t *le; + + /* Walk the cache list from the oldest entries to the newest, deleting timed-out + * records. + * XXX: We walk the list a second time if this did not free up enough space - this + * should be done better. */ + le=rrset_l; + while (le && (!lazy || cache_size>sz)) { + /* Note by Paul Rombouts: + * If data integrity is ensured, at most one node is removed from the rrset_l + * per iteration, and this node is the one referenced by le. */ + rr_lent_t *next=le->next; + if (!((le->rrset && (le->rrset->flags&CF_LOCAL)) || + (le->cent->flags&DF_LOCAL))) { + dns_cent_t *ce = le->cent; + if (le->rrset) + purge_rrset(ce, le->idx,0); + /* Side effect: if purge_rrset called del_cent_rrset then le has been freed. + * ce, however, is still guaranteed to be valid. */ + if (ce->num_rrs==0 && (!(ce->flags&DF_NEGATIVE) || + (!(ce->flags&DF_LOCAL) && timedout_nxdom(ce)))) + del_cache_ent(ce,NULL); + } + le=next; + } + if (cache_size<=sz) + return; + + /* we are still above the desired cache size. Well, delete records from the oldest to + * the newest. This is the case where nopurge records are deleted anyway. Only local + * records are kept in any case.*/ + if(!insert_sort) { + sort_rrl(); + insert_sort=1; /* use insertion sort from now on */ + } + + le=rrset_l; + while (le && cache_size>sz) { + rr_lent_t *next=le->next; + if (!((le->rrset && (le->rrset->flags&CF_LOCAL)) || + (le->cent->flags&DF_LOCAL))) { + dns_cent_t *ce = le->cent; + if (le->rrset) + cache_size -= del_cent_rrset_by_index(ce, le->idx DBG0); + /* this will also delete negative cache entries */ + if (ce->num_rrs==0) + del_cache_ent(ce,NULL); + } + le=next; + } +} + +#define log_warn_read_error(f,item) \ + log_warn("%s encountered while reading %s from disk cache file.", \ + ferror(f)?"Error":feof(f)?"EOF":"Incomplete item",item) + +/* + * Load cache from disk and rebuild the hash tables. + */ +void read_disk_cache() +{ + /* The locks are done when we add items. */ + dns_cent_t ce; + int dtsz=512; + unsigned char *data; + unsigned long cnt; + FILE *f; + + char path[strlen(global.cache_dir)+sizeof("/pdnsd.cache")]; + + stpcpy(stpcpy(path,global.cache_dir),"/pdnsd.cache"); + + if (!(f=fopen(path,"r"))) { + log_warn("Could not open disk cache file %s: %s",path,strerror(errno)); + return; + } + + if (!(data = malloc(dtsz))) { + goto fclose_exit; + } + + /* Don't use insertion sort while reading caches entries from disk, because this can be + noticeably inefficient with large cache files. + Entries are simply appended at the end of the rr_l list. + The rr_l list is sorted using a more efficient merge sort after we are done reading. + */ + insert_sort=0; + + { + unsigned nb; + char buf[sizeof(cachverid)]; + + /* check cache version identifier */ + nb=fread(buf,1,sizeof(cachverid),f); + if (nb!=sizeof(cachverid)) { + /* Don't complain about empty files */ + if(nb!=0 || !feof(f)) { + log_warn_read_error(f,"cache version identifier"); + } + goto free_data_fclose; + } + if(memcmp(buf,cachverid,sizeof(cachverid))) { + log_warn("Cache file %s ignored because of incompatible version identifier",path); + goto free_data_fclose; + } + } + + if (fread(&cnt,sizeof(cnt),1,f)!=1) { + log_warn_read_error(f,"entry count"); + goto free_data_fclose; + } + + for(;cnt>0;--cnt) { + dns_file_t fe; + dom_fttlts_t fttlts = {0,0}; + unsigned char nb[256]; + unsigned num_rrs; + unsigned char prevtp; + if (fread(&fe,sizeof(fe),1,f)!=1) { + log_warn_read_error(f,"cache entry header"); + goto free_data_fclose; + } + if(fe.flags&DF_NEGATIVE) { + if (fread(&fttlts,sizeof(fttlts),1,f)!=1) { + log_warn_read_error(f,"cache TTL and timestamp"); + goto free_data_fclose; + } + } + if (fe.qlen) { + int i; + /* Because of its type qlen should be <=255. */ + if (fread(nb,fe.qlen,1,f)!=1) { + log_warn_read_error(f,"domain name"); + goto free_data_fclose; + } + for(i=0;i63 || (i += lb+1)>fe.qlen) { + log_warn("Invalid domain name encountered while reading disk cache file."); + goto free_data_fclose; + } + } + } + nb[fe.qlen]='\0'; + if (!init_cent(&ce, nb, fttlts.ttl, fttlts.ts, fe.flags DBG0)) { + goto free_data_fclose_exit; + } + ce.c_ns=fe.c_ns; ce.c_soa=fe.c_soa; + + /* now, read the rr's */ + prevtp=0; + for (num_rrs=fe.num_rrs;num_rrs;--num_rrs) { + rr_fset_t sh; + unsigned num_rr; + if (fread(&sh,sizeof(sh),1,f)!=1) { + log_warn_read_error(f,"rr header"); + goto free_cent_data_fclose; + } + if(PDNSD_NOT_CACHED_TYPE(sh.tp)) { + log_warn("Invalid rr type encountered while reading disk cache file."); + goto free_data_fclose; + } + if(sh.tp<=prevtp) { + log_warn("Unexpected rr type encountered (not in strict ascending order) while reading disk cache file."); + goto free_data_fclose; + } + prevtp=sh.tp; + /* Add the rrset header in any case (needed for negative caching) */ + if(!add_cent_rrset_by_type(&ce, sh.tp, sh.ttl, sh.ts, sh.flags DBG0)) { + goto free_cent_data_fclose_exit; + } + for (num_rr=sh.num_rr;num_rr;--num_rr) { + rr_fbucket_t rr; + if (fread(&rr,sizeof(rr),1,f)!=1) { + log_warn_read_error(f,"rr data length"); + goto free_cent_data_fclose; + } + if (rr.rdlen>dtsz) { + unsigned char *tmp; + dtsz=rr.rdlen; + tmp=realloc(data,dtsz); + if (!tmp) { + goto free_cent_data_fclose_exit; + } + data=tmp; + } + if (rr.rdlen && fread(data,rr.rdlen,1,f)!=1) { + log_warn_read_error(f,"rr data"); + goto free_cent_data_fclose; + } + if (!add_cent_rr(&ce,sh.tp,sh.ttl,sh.ts,sh.flags,rr.rdlen,data DBG0)) { + goto free_cent_data_fclose_exit; + } + } + } + add_cache(&ce); + free_cent(&ce DBG0); + } +#ifdef DEBUG_HASH + free(data); + fclose(f); + dumphash(); + goto sort_return; +#else + goto free_data_fclose; +#endif + + free_cent_data_fclose: + free_cent(&ce DBG0); + free_data_fclose: + free(data); + fclose(f); +#ifdef DEBUG_HASH + sort_return: +#endif + /* Do we need read/write locks to sort the rr_l list? + As long as at most one thread is sorting, it is OK for the other threads + to read the cache, providing they do not add or delete anything. + */ + lock_cache_r(); + if(!insert_sort) { + sort_rrl(); + insert_sort=1; + } + unlock_cache_r(); + return; + + free_cent_data_fclose_exit: + free_cent(&ce DBG0); + free_data_fclose_exit: + free(data); + fclose_exit: + fclose(f); + log_error("Out of memory in reading cache file. Exiting."); + pdnsd_exit(); +} + +/* write an rr to the file f */ +static int write_rrset(int tp, rr_set_t *rrs, FILE *f) +{ + rr_bucket_t *rr; + rr_fset_t sh; + rr_fbucket_t rf; + unsigned num_rr; + + sh.tp=tp; + + num_rr=0; + for(rr=rrs->rrs; rr && num_rr<255; rr=rr->next) ++num_rr; + sh.num_rr=num_rr; + sh.flags=rrs->flags; + sh.ttl=rrs->ttl; + sh.ts=rrs->ts; + + if (fwrite(&sh,sizeof(sh),1,f)!=1) { + log_error("Error while writing rr header to disk cache: %s", strerror(errno)); + return 0; + } + + rr=rrs->rrs; + for(; num_rr; --num_rr) { + rf.rdlen=rr->rdlen; + if (fwrite(&rf,sizeof(rf),1,f)!=1 || (rf.rdlen && fwrite((rr->data),rf.rdlen,1,f)!=1)) { + log_error("Error while writing rr data to disk cache: %s", strerror(errno)); + return 0; + } + rr=rr->next; + } + + return 1; +} + + +/* + * Write cache to disk on termination. The hash table is lost and needs to be regenerated + * on reload. + * + * The locks are not very fine grained here, but I don't think this needs fixing as this routine + * is only called on exit. + * + */ +void write_disk_cache() +{ + int j, jlim; + dns_cent_t *le; + unsigned long en=0; + dns_hash_pos_t pos; + FILE *f; + unsigned long num_rrs_errs=0; +# define MAX_NUM_RRS_ERRS 10 + + char path[strlen(global.cache_dir)+sizeof("/pdnsd.cache")]; + + stpcpy(stpcpy(path,global.cache_dir),"/pdnsd.cache"); + + DEBUG_MSG("Writing cache to %s\n",path); + + if (!softlock_cache_rw()) { + goto lock_failed; + } + /* purge cache down to allowed size*/ + purge_cache((long)global.perm_cache*1024, 0); + if (!softunlock_cache_rw()) { + goto lock_failed; + } + + if (!softlock_cache_r()) { + goto lock_failed; + } + + if (!(f=fopen(path,"w"))) { + log_warn("Could not open disk cache file %s: %s",path,strerror(errno)); + goto softunlock_return; + } + + /* Write the cache version identifier */ + if (fwrite(cachverid,sizeof(cachverid),1,f)!=1) { + log_error("Error while writing cache version identifier to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + + for (le=fetch_first(&pos); le; le=fetch_next(&pos)) { + /* count the rr's */ + if(le->flags&DF_NEGATIVE) { + if(!(le->flags&DF_LOCAL)) + ++en; + } + else { + jlim= RRARR_LEN(le); + for (j=0; jflags&CF_LOCAL)) { + ++en; + break; + } + } + } + } + if (fwrite(&en,sizeof(en),1,f)!=1) { + log_error("Error while writing entry count to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + + for (le=fetch_first(&pos); le; le=fetch_next(&pos)) { + /* now, write the rr's */ + if(le->flags&DF_NEGATIVE) { + if(!(le->flags&DF_LOCAL)) + goto write_rrs; + } + else { + jlim= RRARR_LEN(le); + for (j=0; jflags&CF_LOCAL)) { + goto write_rrs; + } + } + } + continue; + write_rrs: + { + dns_file_t df; + int num_rrs; + const unsigned short *iterlist; + df.qlen=rhnlen(le->qname)-1; /* Don't include the null byte at the end */ + df.num_rrs=0; + df.flags=le->flags; + df.c_ns=le->c_ns; df.c_soa=le->c_soa; + num_rrs=0; + jlim=RRARR_LEN(le); + for (j=0; jflags&CF_LOCAL)) + ++df.num_rrs; + } + } + if(num_rrs!=le->num_rrs && ++num_rrs_errs<=MAX_NUM_RRS_ERRS) { + unsigned char buf[DNSNAMEBUFSIZE]; + log_warn("Counted %d rr record types for %s but cached counter=%d", + num_rrs,rhn2str(le->qname,buf,sizeof(buf)),le->num_rrs); + } + if (fwrite(&df,sizeof(df),1,f)!=1) { + log_error("Error while writing cache entry header to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + if(le->flags&DF_NEGATIVE) { + dom_fttlts_t fttlts= {le->neg.ttl,le->neg.ts}; + if (fwrite(&fttlts,sizeof(fttlts),1,f)!=1) { + log_error("Error while writing cache TTL and timestamp to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + } + if (df.qlen && fwrite(le->qname,df.qlen,1,f)!=1) { + log_error("Error while writing domain name to disk cache: %s", strerror(errno)); + goto fclose_unlock; + } + + jlim= NRRITERLIST(le); + iterlist= RRITERLIST(le); + for (j=0; jflags&CF_LOCAL)) { + if(!write_rrset(tp,rrset,f)) + goto fclose_unlock; + } + } + } + } + if(fclose(f)) { + log_error("Could not close cache file %s after writing cache: %s", path,strerror(errno)); + } + softunlock_cache_r(); + DEBUG_MSG("Finished writing cache to disk.\n"); + return; + + fclose_unlock: + fclose(f); + softunlock_return: + softunlock_cache_r(); + return; + + lock_failed: + crash_msg("Lock failed; could not write disk cache."); +} + +/* + * Conflict Resolution. + * The first function is the actual checker; the latter two are wrappers for the respective + * function for convenience only. + * + * We check for conflicts by checking the new data rrset by rrset against the cent. + * This is not bad when considering that new records are hopefully consistent; if they are not, + * we might end up deleteing too much of the old data, which is probably added back through the + * new query, though. + * Having checked additions rrset by rrset, we are at least sure that the resulting record is OK. + * cr_check_add returns 1 if the addition is OK, 0 otherwise. + * This is for records that are already in the cache! + * + * idx is the internally used RR-set index, not the RR type! + */ +static int cr_check_add(dns_cent_t *cent, int idx, time_t ttl, time_t ts, unsigned flags) +{ + time_t nttl; + const struct rr_infos *rri; + + if (flags & CF_NEGATIVE) + return 1; /* no constraints here. */ + + nttl = 0; + rri = &rr_info[idx]; + + if (!(flags & CF_LOCAL)) { + int i, ilim, ncf; + + if(cent->flags & DF_LOCAL) + return 0; /* Local has precedence. */ + + ncf = 0; ilim = RRARR_LEN(cent); + for (i = 0; i < ilim; ++i) { + rr_set_t *rrs= RRARR_INDEX(cent,i); + /* Should be symmetric; check both ways anyway. */ + if (rrs && !(rrs->flags & CF_NEGATIVE) && + ((rri->class & rr_info[i].excludes) || + (rri->excludes & rr_info[i].class))) + { + time_t rttl; + if (rrs->flags & CF_LOCAL) + return 0; /* old was authoritative. */ + ++ncf; + rttl = rrs->ttl + rrs->ts - time(NULL); + if(rttl > 0) nttl += rttl; + } + } + if (ncf == 0) /* no conflicts */ + return 1; + /* Medium ttl of conflicting records */ + nttl /= ncf; + } + if ((flags & CF_LOCAL) || ttl > nttl) { + int i, ilim= RRARR_LEN(cent); + + /* Remove the old records, so that the new one can be added. */ + for (i = 0; i < ilim; ++i) { + rr_set_t *rrs= RRARR_INDEX(cent,i); + /* Should be symmetric; check both ways anyway. */ + if (rrs && !(rrs->flags & CF_NEGATIVE) && + ((rri->class & rr_info[i].excludes) || + (rri->excludes & rr_info[i].class))) { + del_cent_rrset_by_index(cent, i DBG0); + } + } + return 1; + } + /* old records precede */ + return 0; +} + + +inline static void adjust_ttl(rr_set_t *rrset) +{ + if (rrset->flags&CF_NOCACHE) { + rrset->flags &= ~CF_NOCACHE; + rrset->ttl=0; + } + else { + time_t min_ttl= global.min_ttl, neg_ttl=global.neg_ttl; + if((rrset->flags&CF_NEGATIVE) && neg_ttlttlttl=min_ttl; + else { + time_t max_ttl= global.max_ttl; + if(rrset->ttl>max_ttl) + rrset->ttl=max_ttl; + } + } +} + + +/* Only use for negatively cached domains, thus only + if the DF_NEGATIVE bit is set! */ +inline static void adjust_dom_ttl(dns_cent_t *cent) +{ + if (cent->flags&DF_NOCACHE) { + cent->flags &= ~DF_NOCACHE; + cent->neg.ttl=0; + } + else { + time_t min_ttl= global.min_ttl, neg_ttl=global.neg_ttl; + if(/* (cent->flags&DF_NEGATIVE) && */ neg_ttlneg.ttlneg.ttl=min_ttl; + else { + time_t max_ttl= global.max_ttl; + if(cent->neg.ttl>max_ttl) + cent->neg.ttl=max_ttl; + } + } +} + +/* + * Add a ready built dns_cent_t to the hashes, purge if necessary to not exceed cache size + * limits, and add the entries to the hashes. + * As memory is already reserved for the rrs, we only need to wrap up the dns_cent_t and + * alloc memory for it. + * New entries are appended, so we easiliy know the oldest for purging. For fast acces, + * we use hashes instead of ordered storage. + * + * This does not free the argument, and it uses a copy of it, so the caller must do free_cent() + * on it. + * + * The new entries rr sets replace the old ones, i.e. old rr sets with the same key are deleted + * before the new ones are added. + */ +void add_cache(dns_cent_t *cent) +{ + dns_cent_t *ce; + dns_hash_loc_t loc; + int i,ilim; + + lock_cache_rw(); + retry: + if (!(ce=dns_lookup(cent->qname,&loc))) { + /* if the new entry doesn't contain any information, + don't try to add it to the cache because purge_cache() will not + be able to get rid of it. + */ + if(cent->num_rrs==0 && !(cent->flags&DF_NEGATIVE)) + goto purge_cache_return; + + if(!(ce=copy_cent(cent DBG0))) + goto warn_unlock_cache_return; + + if(!(ce->flags&DF_NEGATIVE)) { + ilim= RRARR_LEN(ce); + /* Add the rrs to the rr list */ + for (i=0; iflags&DF_NEGATIVE) { + /* the new entry is negative. So, we need to delete the whole cent, + * and then generate a new one. */ + ilim= RRARR_LEN(ce); + for (i=0; iflags&CF_LOCAL) { + goto unlock_cache_return; /* Do not clobber local records */ + } + } + del_cache_ent(ce,&loc); + goto retry; + } + purge_cent(ce, 0,0); + /* We have a record; add the rrsets replacing old ones */ + cache_size-=ce->cs; + + ilim= RRARR_LEN(cent); + for (i=0; iflags&CF_LOCAL) && (cerrs->flags&CF_LOCAL)) || + ((centrrs->flags&CF_ADDITIONAL) && (!(cerrs->flags&CF_ADDITIONAL) || + (!(centrrs->flags&CF_ROOTSERV) && + (cerrs->flags&CF_ROOTSERV))) && + !timedout(cerrs))))) + { + rr_bucket_t *rr,*rtail; + + del_cent_rrset_by_index(ce,i DBG0); + + if (!cr_check_add(ce, i, centrrs->ttl, centrrs->ts, centrrs->flags)) + continue; /* the new record has been deleted as a conflict resolution measure. */ + + /* pre-initialize a rrset_t for the case we have a negative cached + * rrset, in which case no further rrs will be added. */ + if (!add_cent_rrset_by_index(ce, i, centrrs->ttl, centrrs->ts, centrrs->flags DBG0)) { + goto addsize_unlock_cache_return; + } + rtail=NULL; + for (rr=centrrs->rrs; rr; rr=rr->next) { + if (!add_cent_rr_int(ce,i,centrrs->ttl, centrrs->ts, centrrs->flags, + rr->rdlen, rr->data, &rtail DBG0)) + { + /* cleanup this entry */ + goto cleanup_cent_unlock_cache_return; + } + } + cerrs= RRARR_INDEX(ce,i); + adjust_ttl(cerrs); + if (!insert_rrl(cerrs,ce,i)) { + goto cleanup_cent_unlock_cache_return; + } + } + } + } + ce->flags |= (cent->flags&(DF_AUTH|DF_WILD)); + if(cent->c_ns!=cundef && (ce->c_ns==cundef || ce->c_nsc_ns)) + ce->c_ns=cent->c_ns; + if(cent->c_soa!=cundef && (ce->c_soa==cundef || ce->c_soac_soa)) + ce->c_soa=cent->c_soa; + } + + cache_size += ce->cs; + purge_cache_return: + purge_cache((long)global.perm_cache*1024+MCSZ, 1); + goto unlock_cache_return; + + cleanup_cent_unlock_cache_return: + del_cent_rrset_by_index(ce, i DBG0); + addsize_unlock_cache_return: + cache_size += ce->cs; + goto warn_unlock_cache_return; + + free_cent_unlock_cache_return: + free_cent(ce DBG0); + pdnsd_free(ce); + warn_unlock_cache_return: + log_warn("Out of cache memory."); + unlock_cache_return: + unlock_cache_rw(); +} + +/* + Convert A (and AAAA) records in a ready built cache entry to PTR records suitable for reverse resolving + of numeric addresses and add them to the cache. +*/ +int add_reverse_cache(dns_cent_t * cent) +{ + int tp=T_A; + rr_set_t *rrset= getrrset_A(cent); + + for(;;) { + if(rrset) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + dns_cent_t ce; + unsigned char buf[DNSNAMEBUFSIZE],rhn[DNSNAMEBUFSIZE]; + if(!a2ptrstr((pdnsd_ca *)(rr->data),tp,buf) || !str2rhn(buf,rhn)) + return 0; + if(!init_cent(&ce, rhn, 0, 0, cent->flags DBG0)) + return 0; + if(!add_cent_rr(&ce,T_PTR,rrset->ttl,rrset->ts,rrset->flags,rhnlen(cent->qname),cent->qname DBG0)) { + free_cent(&ce DBG0); + return 0; + } +#ifdef RRMUINDEX_NS + ce.rr.rrmu[RRMUINDEX_NS]=cent->rr.rrmu[RRMUINDEX_NS]; +#endif +#ifdef RRMUINDEX_SOA + ce.rr.rrmu[RRMUINDEX_SOA]=cent->rr.rrmu[RRMUINDEX_SOA]; +#endif + add_cache(&ce); +#ifdef RRMUINDEX_NS + ce.rr.rrmu[RRMUINDEX_NS]=NULL; +#endif +#ifdef RRMUINDEX_SOA + ce.rr.rrmu[RRMUINDEX_SOA]=NULL; +#endif + free_cent(&ce DBG0); + } + } +#if ALLOW_LOCAL_AAAA + if(tp==T_AAAA) + break; + tp=T_AAAA; + rrset= getrrset_AAAA(cent); +#else + break; +#endif + } + return 1; +} + + +/* + Delete a cent from the cache. Call with write locks applied. + Does not delete corresponding entry in hash table, call del_cache_ent() + or del_cache() for that. +*/ +void del_cent(dns_cent_t *cent) +{ + cache_size -= cent->cs; + + /* free the data referred by the cent and the cent itself */ + free_cent(cent DBG0); + free(cent); + + --ent_num; +} + +/* + * Delete a cent from the cache. Call with write locks applied. + */ +static void del_cache_ent(dns_cent_t *cent,dns_hash_loc_t *loc) +{ + dns_cent_t *data; + + /* Delete from the hash */ + if(loc) + data=del_dns_hash_ent(loc); + else + data=del_dns_hash(cent->qname); + if(!data) { + log_warn("Cache entry not found by del_dns_hash() in %s, line %d",__FILE__,__LINE__); + } + else if(data!=cent) { + log_warn("pointer returned by del_dns_hash() does not match cache entry in %s, line %d",__FILE__,__LINE__); + } + del_cent(cent); +} + +/* Delete a cached record. Performs locking. Call this from the outside, NOT del_cache_ent */ +void del_cache(const unsigned char *name) +{ + dns_cent_t *cent; + + lock_cache_rw(); + if ((cent=del_dns_hash(name))) { + del_cent(cent); + } + unlock_cache_rw(); +} + + +/* Invalidate a record by resetting the fetch time to 0. This means that it will be refreshed + * if possible (and will only be served when purge_cache=off;) */ +void invalidate_record(const unsigned char *name) +{ + dns_cent_t *ce; + int i, ilim; + + lock_cache_rw(); + if ((ce=dns_lookup(name,NULL))) { + if(!(ce->flags&DF_NEGATIVE)) { + ilim= RRARR_LEN(ce); + for (i=0; its=0; + rrs->flags &= ~CF_AUTH; + } + } + } + else { + /* set the cent time to 0 (for the case that this was negative) */ + ce->neg.ts=0; + } + ce->flags &= ~DF_AUTH; + } + unlock_cache_rw(); +} + + +/* + Set flags of the cache entry with the specified name. + Don't use this to set the DF_NEGATIVE flag, or you will + risk leaving the cache in an inconsistent state. + Returns 0 if the cache entry cannot be found, otherwise 1. + */ +int set_cent_flags(const unsigned char *name, unsigned flags) +{ + dns_cent_t *ret; + lock_cache_rw(); + ret=dns_lookup(name,NULL); + if (ret) { + ret->flags |= flags; + } + unlock_cache_rw(); + return ret!=NULL; +} + +unsigned char *getlocalowner(unsigned char *name,int tp) +{ + unsigned char *ret=NULL; + dns_cent_t *ce; + unsigned lb; + + lock_cache_r(); + if((lb = *name)) { + while(name += lb+1, lb = *name) { + if((ce=dns_lookup(name,NULL))) { + if(!(ce->flags&DF_LOCAL)) + break; + if(have_rr(ce,tp)) { + ret=name; + break; + } + } + } + } + unlock_cache_r(); + + return ret; +} + + +/* Lookup an entry in the cache using name (in length byte - string notation). + * For thread safety, a copy must be returned, so delete it after use, by first doing + * free_cent to remove the rrs and then by freeing the returned pointer. + * If wild is nonzero, and name can't be found in the cache, lookup_cache() + * will search up the name hierarchy for a record with the DF_NEGATIVE or DF_WILD flag set. + */ +dns_cent_t *lookup_cache(const unsigned char *name, int *wild) +{ + int purge=0; + dns_cent_t *ret; + + /* First try with only read access to the cache. */ + lock_cache_r(); + ret=dns_lookup(name,NULL); + if(wild) { + *wild=0; + if(!ret) { + const unsigned char *nm=name; + unsigned lb=*nm; + if(lb) { + while(nm += lb+1, lb = *nm) { + if ((ret=dns_lookup(nm,NULL))) { + if(ret->flags&DF_NEGATIVE) + /* use this entry */ + *wild=w_neg; + else if(ret->flags&DF_WILD) { + unsigned char buf[DNSNAMEBUFSIZE]; + buf[0]=1; buf[1]='*'; + /* When we get here, at least one element of name + has been removed, so assuming name is not longer + than DNSNAMEBUFSIZE bytes, the remainder is guaranteed to + fit into DNSNAMEBUFSIZE-2 bytes */ + rhncpy(&buf[2],nm); + ret=dns_lookup(buf,NULL); + if(ret) + *wild=w_wild; + } + else if(ret->flags&DF_LOCAL) + *wild=w_locnerr; + else + ret=NULL; + break; + } + } + } + } + } + if (ret) { + if(!(purge=purge_cent(ret, 1,1))) /* test only, don't remove anything yet! */ + ret=copy_cent(ret DBG1); + } + unlock_cache_r(); + + if(purge) { + /* we need exclusive read and write access before we delete anything. */ + lock_cache_rw(); + ret=dns_lookup(name,NULL); + if(wild) { + *wild=0; + if(!ret) { + const unsigned char *nm=name; + unsigned lb=*nm; + if(lb) { + while(nm += lb+1, lb = *nm) { + if ((ret=dns_lookup(nm,NULL))) { + if(ret->flags&DF_NEGATIVE) + /* use this entry */ + *wild=w_neg; + else if(ret->flags&DF_WILD) { + unsigned char buf[DNSNAMEBUFSIZE]; + buf[0]=1; buf[1]='*'; + rhncpy(&buf[2],nm); + ret=dns_lookup(buf,NULL); + if(ret) + *wild=w_wild; + } + else if(ret->flags&DF_LOCAL) + *wild=w_locnerr; + else + ret=NULL; + break; + } + } + } + } + } + if (ret) { + if(purge_cent(ret, 1,0)<0) + ret=NULL; + else + ret=copy_cent(ret DBG1); + } + unlock_cache_rw(); + } + + return ret; +} + +/* lookup_cache_local_rrset() check if there is locally defined RR set of a specific RR type + for name, and if so, returns a copy of the RR set. After use, the copy should be cleaned + up using del_rrset(). + This is potentially much more efficient than using lookup_cache(), if the name is likely + to have a cache entry, but unlikely to have locally defined RR sets. +*/ +rr_set_t *lookup_cache_local_rrset(const unsigned char *name, int type) +{ + rr_set_t *ret=NULL; + dns_cent_t *cent; + + lock_cache_r(); + cent= dns_lookup(name,NULL); + if(cent) { + rr_set_t *rrset=getrrset(cent,type); + if(rrset && (rrset->flags&CF_LOCAL)) { + ret= copy_rrset(rrset); + } + } + unlock_cache_r(); + + return ret; +} + + +#if 0 +/* Add an rr to an existing cache entry or create a new entry if necessary. + * The rr is treated with the precedence of an additional or off-topic record, ie. regularly retrieved + * have precedence. + * You cannot add a negative additional record. Makes no sense anyway. */ +int add_cache_rr_add(const unsigned char *name, int tp, time_t ttl, time_t ts, unsigned flags, unsigned dlen, void *data, unsigned long serial) +{ + dns_hash_loc_t loc; + dns_cent_t *ret; + rr_set_t *rrs; + int rv=0; + + lock_cache_rw(); + if (!(ret=dns_lookup(name,&loc))) { + if (!(ret=cache_malloc(sizeof(dns_cent_t)))) + goto unlock_return; + if(!init_cent(ret, name, 0, 0, 0 DBG0)) { + pdnsd_free(ret); + goto unlock_return; + } + if(!add_dns_hash(ret,&loc)) { + free_cent(ret DBG0); + pdnsd_free(ret); + goto unlock_return; + } + ++ent_num; + } + else { + /* purge the record. */ + purge_cent(ret,0,0); + cache_size-=ret->cs; + } + rrs=getrrset(ret,tp); + if (rrs && + ((rrs->flags&CF_NEGATIVE && !(rrs->flags&CF_LOCAL)) || + (rrs->flags&CF_NOPURGE && timedout(rrs)) || + (rrs->flags&CF_ADDITIONAL && rrs->serial!=serial) || + (rrs->serial==serial && rrs->ttl!=(ttlglobal.max_ttl?global.max_ttl:ttl))))) { + del_cent_rrset_by_type(ret,tp DBG0); + rrs=NULL; + } + if (rrs==NULL || rrs->serial==serial) { + if (cr_check_add(ret,rrlkuptab[tp-T_MIN],ttl,ts,flags)) { + if (add_cent_rr(ret,tp,ttl,ts,flags,dlen,data,serial DBG0)) { + rr_set_t *rrsnew; + if (!rrs && (rrsnew=getrrset(ret,tp)) && !insert_rrl(rrsnew,ret,rrlkuptab[tp-T_MIN])) { + del_cent_rrset_by_type(ret,tp DBG0); + } + else { + cache_size+=ret->cs; + purge_cent(ret,1,0); + rv=1; + goto unlock_return; + } + } + } + } else { + rv=1; + } + cache_size+=ret->cs; + + unlock_return: + unlock_cache_rw(); + return rv; +} +#endif + +/* Report the cache status to the file descriptor f, for the status fifo (see status.c) */ +int report_cache_stat(int f) +{ + /* Cache size and entry counters are volatile (and even the entries + in the global struct can change), so make copies to get consistent data. + Even better would be to use locks, but that could be rather costly. */ + long csz= cache_size, en= ent_num; + long pc= global.perm_cache; + long mc= pc*1024+MCSZ; + + fsprintf_or_return(f,"\nCache status:\n=============\n"); + fsprintf_or_return(f,"%ld kB maximum disk cache size.\n",pc); + fsprintf_or_return(f,"%ld of %ld bytes (%.3g%%) memory cache used in %ld entries" + " (avg %.5g bytes/entry).\n", + csz, mc, (((double)csz)/mc)*100, en, + ((double)csz)/en); + return 0; +} + + +#define timestamp2str(ts,now,buf) \ +{ \ + struct tm tstm; \ + if(!((ts) && localtime_r(&(ts), &tstm) && \ + strftime(buf, sizeof(buf), \ + ((ts)<=(now) && (now)-(ts)<365*24*60*60/2)?" %m/%d %T":"%Y/%m/%d %T", \ + &tstm)>0)) \ + strcpy(buf," "); \ +} + +/* Dump contents of a cache entry to file descriptor fd. + Returns 1 on success, -1 if there is an IO error. +*/ +static int dump_cent(int fd, dns_cent_t *cent) +{ + time_t now; + char tstr[sizeof "2000/12/31 23:59:59"],dbuf[1024]; + + fsprintf_or_return(fd,"%s\n",rhn2str(cent->qname,ucharp dbuf,sizeof(dbuf))); + now=time(NULL); + + if(cent->flags&DF_NEGATIVE) { + timestamp2str(cent->neg.ts,now,tstr); + fsprintf_or_return(fd,"%s (domain negated)\n",tstr); + } + else { + int i, n= NRRITERLIST(cent); + const unsigned short *iterlist= RRITERLIST(cent); + for(i=0; its,now,tstr); + if(rrset->flags&CF_NEGATIVE) { + fsprintf_or_return(fd,"%s %-7s (negated)\n",tstr,rrnames[tp-T_MIN]); + } + else { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + switch (tp) { + case T_CNAME: + case T_MB: + case T_MD: + case T_MF: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + rhn2str((unsigned char *)(rr->data),ucharp dbuf,sizeof(dbuf)); + break; +#if IS_CACHED_MINFO || IS_CACHED_RP +#if IS_CACHED_MINFO + case T_MINFO: +#endif +#if IS_CACHED_RP + case T_RP: +#endif + { + unsigned char *p=(unsigned char *)(rr->data); + int n; + rhn2str(p,ucharp dbuf,sizeof(dbuf)); + n=strlen(dbuf); + dbuf[n++] = ' '; + if(n>=sizeof(dbuf)) + goto hex_dump; + rhn2str(skiprhn(p),ucharp dbuf+n,sizeof(dbuf)-n); + } + break; +#endif + case T_MX: +#if IS_CACHED_AFSDB + case T_AFSDB: +#endif +#if IS_CACHED_RT + case T_RT: +#endif +#if IS_CACHED_KX + case T_KX: +#endif + { + unsigned char *p=(unsigned char *)(rr->data); + unsigned pref; + int n; + GETINT16(pref,p); + n=sprintf(dbuf,"%u ",pref); + if(n<0) goto hex_dump; + rhn2str(p,ucharp dbuf+n,sizeof(dbuf)-n); + } + break; + case T_SOA: + { + unsigned char *p=(unsigned char *)(rr->data); + char *q; + int n,rem; + uint32_t serial,refresh,retry,expire,minimum; + rhn2str(p,ucharp dbuf,sizeof(dbuf)); + n=strlen(dbuf); + dbuf[n++] = ' '; + if(n>=sizeof(dbuf)) + goto hex_dump; + q=dbuf+n; + rem=sizeof(dbuf)-n; + p=skiprhn(p); + rhn2str(p,ucharp q,rem); + n=strlen(q); + q[n++] = ' '; + if(n>=rem) + goto hex_dump; + q += n; + rem -= n; + p=skiprhn(p); + GETINT32(serial,p); + GETINT32(refresh,p); + GETINT32(retry,p); + GETINT32(expire,p); + GETINT32(minimum,p); + n=snprintf(q,rem,"%lu %lu %lu %lu %lu", + (unsigned long)serial,(unsigned long)refresh, + (unsigned long)retry,(unsigned long)expire, + (unsigned long)minimum); + if(n<0 || n>=rem) + goto hex_dump; + } + break; +#if IS_CACHED_HINFO || IS_CACHED_TXT || IS_CACHED_SPF +#if IS_CACHED_HINFO + case T_HINFO: +#endif +#if IS_CACHED_TXT + case T_TXT: +#endif +#if IS_CACHED_SPF + case T_SPF: +#endif + { + /* TXT records are not necessarily validated + before they are stored in the cache, so + we need to be careful. */ + unsigned char *p=(unsigned char *)(rr->data); + char *q=dbuf; + int j=0,n,rem=sizeof(dbuf); + while(jrdlen) { + unsigned lb; + if(rem<3) + goto hex_dump; + if(j) { + *q++ = ' '; + --rem; + } + *q++ = '"'; + --rem; + lb=*p++; + if((j += lb+1)>rr->rdlen) + goto hex_dump; + n=escapestr(charp p,lb,q,rem); + if(n<0 || n+1>=rem) + goto hex_dump; + q += n; + *q++ = '"'; + rem -= n+1; + p += lb; + } + *q=0; + } + break; +#endif +#if IS_CACHED_PX + case T_PX: + { + unsigned char *p=(unsigned char *)(rr->data); + char *q; + unsigned pref; + int n,rem; + GETINT16(pref,p); + n=sprintf(dbuf,"%u ",pref); + if(n<0) goto hex_dump; + q=dbuf+n; + rem=sizeof(dbuf)-n; + rhn2str(p,ucharp q,rem); + n=strlen(q); + q[n++] = ' '; + if(n>=rem) + goto hex_dump; + rhn2str(skiprhn(p),ucharp q+n,rem-n); + } + break; +#endif +#if IS_CACHED_SRV + case T_SRV: + { + unsigned char *p=(unsigned char *)(rr->data); + unsigned priority,weight,port; + int n; + GETINT16(priority,p); + GETINT16(weight,p); + GETINT16(port,p); + n=sprintf(dbuf,"%u %u %u ",priority,weight,port); + if(n<0) goto hex_dump; + rhn2str(p,ucharp dbuf+n,sizeof(dbuf)-n); + } + break; +#endif +#if IS_CACHED_NXT + case T_NXT: + { + unsigned char *p=(unsigned char *)(rr->data); + int n,rlen; + rhn2str(p,ucharp dbuf,sizeof(dbuf)); + n=strlen(dbuf); + dbuf[n++] = ' '; + if(n>=sizeof(dbuf)) + goto hex_dump; + rlen=rhnlen(p); + hexdump(p+rlen,rr->rdlen-rlen,dbuf+n,sizeof(dbuf)-n); + } + break; +#endif +#if IS_CACHED_NAPTR + case T_NAPTR: + { + unsigned char *p=(unsigned char *)(rr->data); + char *q; + unsigned order,pref; + int n,rem,j; + GETINT16(order,p); + GETINT16(pref,p); + n=sprintf(dbuf,"%u %u ",order,pref); + if(n<0) goto hex_dump; + q=dbuf+n; + rem=sizeof(dbuf)-n; + for (j=0;j<3;++j) { + unsigned lb; + if(rem<2) + goto hex_dump; + *q++ = '"'; + --rem; + lb=*p++; + n=escapestr(charp p,lb,q,rem); + if(n<0 || n+2>=rem) + goto hex_dump; + q += n; + *q++ = '"'; + *q++ = ' '; + rem -= n+2; + p += lb; + } + rhn2str(p,ucharp q,rem); + } + break; +#endif +#if IS_CACHED_LOC + case T_LOC: + /* Binary data length has not necessarily been validated */ + if(rr->rdlen!=16) + goto hex_dump; + if(!loc2str(rr->data,dbuf,sizeof(dbuf))) + goto hex_dump; + break; +#endif + case T_A: + if (!inet_ntop(AF_INET,rr->data,dbuf,sizeof(dbuf))) + goto hex_dump; + break; +#if IS_CACHED_AAAA && defined(AF_INET6) + case T_AAAA: + if (!inet_ntop(AF_INET6,rr->data,dbuf,sizeof(dbuf))) + goto hex_dump; + break; +#endif + default: + hex_dump: + hexdump(rr->data,rr->rdlen,dbuf,sizeof(dbuf)); + } + fsprintf_or_return(fd,"%s %-7s %s\n",tstr,rrnames[tp-T_MIN],dbuf); + } + } + } + } + } + fsprintf_or_return(fd,"\n"); + return 1; +} + +/* Dump cache contents to file descriptor fd. + If name is not null, restricts information to that name, + otherwise dumps information about all names found in the cache. + Returns 1 on success, 0 if the name is not found, -1 is there is an IO error. + Mainly for debugging purposes. +*/ +int dump_cache(int fd, const unsigned char *name, int exact) +{ + int rv=0; + lock_cache_r(); + if(name && exact) { + dns_cent_t *cent=dns_lookup(name,NULL); + if(cent) + rv=dump_cent(fd,cent); + } + else { + dns_cent_t *cent; + dns_hash_pos_t pos; + for (cent=fetch_first(&pos); cent; cent=fetch_next(&pos)) { + unsigned int nrem; + if(!name || (domain_match(name,cent->qname,&nrem,NULL),nrem==0)) + if((rv=dump_cent(fd,cent))<0) + break; + } + } + unlock_cache_r(); + return rv; +} + + +#if DEBUG>0 + +/* Added by Paul Rombouts: This is only used in debug messages. */ +const char cflgnames[NCFLAGS*3]={'N','E','G','L','O','C','A','U','T','N','O','C','A','D','D','N','O','P','R','T','S'}; +const char dflgnames[NDFLAGS*3]={'N','E','G','L','O','C','A','U','T','N','O','C','W','L','D'}; + +char *flags2str(unsigned flags,char *buf,int nflags,const char *flgnames) +{ + char *p=buf; + int i,nflgchars=3*nflags; + for(i=0;ibuf) *p++='|'; + p=mempcpy(p,&flgnames[i],3); + } + flags >>= 1; + } + if(p==buf) + *p++='0'; + *p=0; + return buf; +} +#endif diff --git a/service/src/main/jni/pdnsd/src/cache.h b/service/src/main/jni/pdnsd/src/cache.h new file mode 100644 index 0000000..5056dec --- /dev/null +++ b/service/src/main/jni/pdnsd/src/cache.h @@ -0,0 +1,306 @@ +/* cache.h - Definitions for the dns cache + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2003, 2004, 2005, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _CACHE_H_ +#define _CACHE_H_ + +#include +#include "ipvers.h" +#include +#include "list.h" +#include "dns.h" +#include "conff.h" + +struct rr_lent_s; + +/* + * These values are converted to host byte order. the data is _not_. + */ +typedef struct rr_b_s { + struct rr_b_s *next; /* this is the next pointer in the dns_cent_t list. */ + unsigned rdlen; +#if ALLOW_LOCAL_AAAA || defined(ENABLE_IPV6) + struct in6_addr data[0]; /* dummy for alignment */ +#else + struct in_addr data[0]; +#endif +} rr_bucket_t; + +typedef struct { + struct rr_lent_s *lent; /* this points to the list entry */ + time_t ttl; + time_t ts; + unsigned short flags; + rr_bucket_t *rrs; +} rr_set_t; + + +typedef struct { + unsigned char *qname; /* Name of the domain in length byte - string notation. */ + size_t cs; /* Size of the cache entry, including RR sets. */ + unsigned short num_rrs; /* The number of RR sets. When this decreases to 0, the cent is deleted. */ + unsigned short flags; /* Flags for the whole domain. */ + union { + struct { /* Fields used only for negatively cached domains. */ + struct rr_lent_s *lent; /* list entry for the whole cent. */ + time_t ttl; /* TTL for negative caching. */ + time_t ts; /* Timestamp. */ + } neg; + struct { /* Fields used only for domains that actually exist. */ + rr_set_t *(rrmu[NRRMU]); /* The most used records. + Use the the value obtained from rrlkuptab[] as index. */ + rr_set_t **rrext; /* Pointer (may be NULL) to an array of size NNRREXT storing the + less frequently used records. */ + } rr; + }; + unsigned char c_ns,c_soa; /* Number of trailing name elements in qname to use to find NS or SOA + records to add to the authority section of a response. */ +} dns_cent_t; + +/* This value is used to represent an undefined c_ns or c_soa field. */ +#define cundef 0xff + +/* + * the flag values for RR sets in the cache + */ +#define CF_NEGATIVE 1 /* this one is for per-RRset negative caching*/ +#define CF_LOCAL 2 /* Local zone entry */ +#define CF_AUTH 4 /* authoritative record */ +#define CF_NOCACHE 8 /* Only hold for the cache latency time period, then purge. + * Not really written to cache, but used by add_cache. */ +#define CF_ADDITIONAL 16 /* This was fetched as an additional or "off-topic" record. */ +#define CF_NOPURGE 32 /* Do not purge this record */ +#define CF_ROOTSERV 64 /* This record was directly obtained from a root server */ + +#define CFF_NOINHERIT (CF_LOCAL|CF_AUTH|CF_ADDITIONAL|CF_ROOTSERV) /* not to be inherited on requery */ + +/* + * the flag values for whole domains in the cache + */ +#define DF_NEGATIVE 1 /* this one is for whole-domain negative caching (created on NXDOMAIN)*/ +#define DF_LOCAL 2 /* local record (in conj. with DF_NEGATIVE) */ +#define DF_AUTH 4 /* authoritative record */ +#define DF_NOCACHE 8 /* Only hold for the cache latency time period, then purge. + * Only used for negatively cached domains. + * Not really written to cache, but used by add_cache. */ +#define DF_WILD 16 /* subdomains of this domain have wildcard records */ + +/* #define DFF_NOINHERIT (DF_NEGATIVE) */ /* not to be inherited on requery */ + +enum {w_wild=1, w_neg, w_locnerr}; /* Used to distinguish different types of wildcard records. */ + +#if DEBUG>0 +#define NCFLAGS 7 +#define NDFLAGS 5 +#define CFLAGSTRLEN (NCFLAGS*4) +#define DFLAGSTRLEN (NDFLAGS*4) +extern const char cflgnames[]; +extern const char dflgnames[]; +char *flags2str(unsigned flags,char *buf,int nflags,const char *flgnames); +#define cflags2str(flags,buf) flags2str(flags,buf,NCFLAGS,cflgnames) +#define dflags2str(flags,buf) flags2str(flags,buf,NDFLAGS,dflgnames) +#endif + +/* + * This is the time in secs any record remains at least in the cache before it is purged. + * (exception is that the cache is full) + */ +#define CACHE_LAT 120 +#define CLAT_ADJ(ttl) ((ttl)ts+CLAT_ADJ((rrset)->ttl)neg.ts+CLAT_ADJ((cent)->neg.ttl)purge_cache) + fl|=CF_NOPURGE; + if (server->nocache) + fl|=CF_NOCACHE; + if (server->rootserver) + fl|=CF_ROOTSERV; + return fl; +} + +int init_cent(dns_cent_t *cent, const unsigned char *qname, time_t ttl, time_t ts, unsigned flags DBGPARAM); +int add_cent_rrset_by_type(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags DBGPARAM); +int add_cent_rr(dns_cent_t *cent, int type, time_t ttl, time_t ts, unsigned flags,unsigned dlen, void *data DBGPARAM); +int del_rrset(rr_set_t *rrs DBGPARAM); +void free_cent(dns_cent_t *cent DBGPARAM); +void free_cent0(void *ptr); +void negate_cent(dns_cent_t *cent, time_t ttl, time_t ts); +void del_cent(dns_cent_t *cent); + +/* Because this is empty by now, it is defined as an empty macro to save overhead.*/ +/*void free_rr(rr_bucket_t cent);*/ +#define free_rr(x) + +dns_cent_t *copy_cent(dns_cent_t *cent DBGPARAM); + +#if 0 +unsigned long get_serial(void); +#endif + +/* Get pointer to rrset given cache entry and rr type value. */ +inline static rr_set_t *getrrset(dns_cent_t *cent, int type) + __attribute__((always_inline)); +inline static rr_set_t *getrrset(dns_cent_t *cent, int type) +{ + if(!(cent->flags&DF_NEGATIVE)) { + int tpi= type - T_MIN; + + if(tpi>=0 && tpirr.rrmu[idx]; + else { + idx -= NRRMU; + if(idx < NRREXT) { + rr_set_t **rrext= cent->rr.rrext; + if(rrext) + return rrext[idx]; + } + } + } + } + + return NULL; +} + +/* This version of getrrset is slightly more efficient, + but also more dangerous, because it performs less checks. + It is safe to use if T_MIN <= type <= T_MAX and cent + is not negative. +*/ +inline static rr_set_t *getrrset_eff(dns_cent_t *cent, int type) + __attribute__((always_inline)); +inline static rr_set_t *getrrset_eff(dns_cent_t *cent, int type) +{ + unsigned int idx = rrlkuptab[type-T_MIN]; + if(idx < NRRMU) + return cent->rr.rrmu[idx]; + else { + idx -= NRRMU; + if(idx < NRREXT) { + rr_set_t **rrext= cent->rr.rrext; + if(rrext) + return rrext[idx]; + } + } + + return NULL; +} + + +/* have_rr() tests whether a cache entry has at least one record of a given type. + Only use if T_MIN <= type <=T_MAX +*/ +inline static int have_rr(dns_cent_t *cent, int type) + __attribute__((always_inline)); +inline static int have_rr(dns_cent_t *cent, int type) +{ + rr_set_t *rrset; + return !(cent->flags&DF_NEGATIVE) && (rrset=getrrset_eff(cent, type)) && rrset->rrs; +} + +/* Some quick and dirty and hopefully fast macros. */ +#define PDNSD_NOT_CACHED_TYPE(type) ((type)T_MAX || rrlkuptab[(type)-T_MIN]>=NRRTOT) + +/* This is useful for iterating over all the RR types in a cache entry in strict ascending order. */ +#define NRRITERLIST(cent) ((cent)->flags&DF_NEGATIVE?0:(cent)->rr.rrext?NRRTOT:NRRMU) +#define RRITERLIST(cent) ((cent)->flags&DF_NEGATIVE?NULL:(cent)->rr.rrext?rrcachiterlist:rrmuiterlist) + +/* The following macros use array indices as arguments, not RR type values! */ +#define GET_RRSMU(cent,i) (!((cent)->flags&DF_NEGATIVE)?(cent)->rr.rrmu[i]:NULL) +#define GET_RRSEXT(cent,i) (!((cent)->flags&DF_NEGATIVE) && (cent)->rr.rrext?(cent)->rr.rrext[i]:NULL) +#define HAVE_RRMU(cent,i) (!((cent)->flags&DF_NEGATIVE) && (cent)->rr.rrmu[i] && (cent)->rr.rrmu[i]->rrs) +#define HAVE_RREXT(cent,i) (!((cent)->flags&DF_NEGATIVE) && (cent)->rr.rrext && (cent)->rr.rrext[i] && (cent)->rr.rrext[i]->rrs) + +#define RRARR_LEN(cent) ((cent)->flags&DF_NEGATIVE?0:(cent)->rr.rrext?NRRTOT:NRRMU) + +/* This allows us to index the RR-set arrays in a cache entry as if they formed one contiguous array. */ +#define RRARR_INDEX_TESTEXT(cent,i) ((cent)->flags&DF_NEGATIVE?NULL:(i)rr.rrmu[i]:(cent)->rr.rrext?(cent)->rr.rrext[(i)-NRRMU]:NULL) +/* This gets the address where the pointer to an RR-set is stored in a cache entry, + given the cache entry and an RR-set index. + Address may be NULL if no storage space for the type has been allocated. */ +#define RRARR_INDEX_PA_TESTEXT(cent,i) ((cent)->flags&DF_NEGATIVE?NULL:(i)rr.rrmu[i]:(cent)->rr.rrext?&(cent)->rr.rrext[(i)-NRRMU]:NULL) + +/* The following macros should only be used if 0 <= i < RRARR_LEN(cent) ! */ +#define RRARR_INDEX(cent,i) ((i)rr.rrmu[i]:(cent)->rr.rrext[(i)-NRRMU]) +#define RRARR_INDEX_PA(cent,i) ((i)rr.rrmu[i]:&(cent)->rr.rrext[(i)-NRRMU]) + +#endif diff --git a/service/src/main/jni/pdnsd/src/conf-keywords.h b/service/src/main/jni/pdnsd/src/conf-keywords.h new file mode 100644 index 0000000..2bcdacf --- /dev/null +++ b/service/src/main/jni/pdnsd/src/conf-keywords.h @@ -0,0 +1,238 @@ +/* conf-keywords.h - Tables used by parser of configuration file. + Based on information previously contained in conf-lex.y and conf-parse.y + + Copyright (C) 2004,2005,2006,2007,2008,2009,2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +enum { + ERROR, + + GLOBAL, + SERVER, + RR, + NEG, + SOURCE, + INCLUDE_F, + + PERM_CACHE, + CACHE_DIR, + SERVER_PORT, + SERVER_IP, + OUTGOING_IP, + SCHEME_FILE, + LINKDOWN_KLUGE, + MAX_TTL, + MIN_TTL, + RUN_AS, + STRICT_SETUID, + USE_NSS, + PARANOID, + IGNORE_CD, + STATUS_CTL, + DAEMON, + C_TCP_SERVER, + PID_FILE, + C_VERBOSITY, + C_QUERY_METHOD, + RUN_IPV4, + IPV4_6_PREFIX, + C_DEBUG, + C_CTL_PERMS, + C_PROC_LIMIT, + C_PROCQ_LIMIT, + TCP_QTIMEOUT, + C_PAR_QUERIES, + C_RAND_RECS, + NEG_TTL, + NEG_RRS_POL, + NEG_DOMAIN_POL, + QUERY_PORT_START, + QUERY_PORT_END, + UDP_BUFSIZE, + DELEGATION_ONLY, + + IP, + PORT, + SCHEME, + UPTEST, + TIMEOUT, + PING_TIMEOUT, + PING_IP, + UPTEST_CMD, + QUERY_TEST_NAME, + INTERVAL, + INTERFACE, + DEVICE, + PURGE_CACHE, + CACHING, + LEAN_QUERY, + EDNS_QUERY, + PRESET, + PROXY_ONLY, + ROOT_SERVER, + RANDOMIZE_SERVERS, + INCLUDE, + EXCLUDE, + POLICY, + REJECTLIST, + REJECTPOLICY, + REJECTRECURSIVELY, + LABEL, + + A, + PTR, + MX, + SOA, + CNAME, + TXT, + SPF, + NAME, + OWNER, + TTL, + TYPES, + FILET, + SERVE_ALIASES, + AUTHREC, + REVERSE +}; + + +/* Table for looking up section headers. Order alphabetically! */ +static const namevalue_t section_headers[]= { + {"global", GLOBAL}, + {"include", INCLUDE_F}, + {"neg", NEG}, + {"rr", RR}, + {"server", SERVER}, + {"source", SOURCE} +}; + +/* Table for looking up global options. Order alphabetically! */ +static const namevalue_t global_options[]= { + {"cache_dir", CACHE_DIR}, + {"ctl_perms", C_CTL_PERMS}, + {"daemon", DAEMON}, + {"debug", C_DEBUG}, + {"delegation_only", DELEGATION_ONLY}, + {"ignore_cd", IGNORE_CD}, + {"interface", SERVER_IP}, + {"ipv4_6_prefix", IPV4_6_PREFIX}, + {"linkdown_kluge", LINKDOWN_KLUGE}, + {"max_ttl", MAX_TTL}, + {"min_ttl", MIN_TTL}, + {"neg_domain_pol", NEG_DOMAIN_POL}, + {"neg_rrs_pol", NEG_RRS_POL}, + {"neg_ttl", NEG_TTL}, + {"outgoing_ip", OUTGOING_IP}, + {"outside_interface", OUTGOING_IP}, + {"par_queries", C_PAR_QUERIES}, + {"paranoid", PARANOID}, + {"perm_cache", PERM_CACHE}, + {"pid_file", PID_FILE}, + {"proc_limit", C_PROC_LIMIT}, + {"procq_limit", C_PROCQ_LIMIT}, + {"query_method", C_QUERY_METHOD}, + {"query_port_end", QUERY_PORT_END}, + {"query_port_start", QUERY_PORT_START}, + {"randomize_recs", C_RAND_RECS}, + {"run_as", RUN_AS}, + {"run_ipv4", RUN_IPV4}, + {"scheme_file", SCHEME_FILE}, + {"server_ip", SERVER_IP}, + {"server_port", SERVER_PORT}, + {"status_ctl", STATUS_CTL}, + {"strict_setuid", STRICT_SETUID}, + {"tcp_qtimeout", TCP_QTIMEOUT}, + {"tcp_server", C_TCP_SERVER}, + {"timeout", TIMEOUT}, + {"udpbufsize", UDP_BUFSIZE}, + {"use_nss", USE_NSS}, + {"verbosity", C_VERBOSITY} +}; + +/* Table for looking up server options. Order alphabetically! */ +static const namevalue_t server_options[]= { + {"caching", CACHING}, + {"device", DEVICE}, + {"edns_query", EDNS_QUERY}, + {"exclude", EXCLUDE}, + {"file", FILET}, + {"include", INCLUDE}, + {"interface", INTERFACE}, + {"interval", INTERVAL}, + {"ip", IP}, + {"label", LABEL}, + {"lean_query", LEAN_QUERY}, + {"ping_ip", PING_IP}, + {"ping_timeout", PING_TIMEOUT}, + {"policy", POLICY}, + {"port", PORT}, + {"preset", PRESET}, + {"proxy_only", PROXY_ONLY}, + {"purge_cache", PURGE_CACHE}, + {"query_test_name", QUERY_TEST_NAME}, + {"randomize_servers", RANDOMIZE_SERVERS}, + {"reject", REJECTLIST}, + {"reject_policy", REJECTPOLICY}, + {"reject_recursively", REJECTRECURSIVELY}, + {"root_server", ROOT_SERVER}, + {"scheme", SCHEME}, + {"timeout", TIMEOUT}, + {"uptest", UPTEST}, + {"uptest_cmd", UPTEST_CMD} +}; + +/* Table for looking up rr options. Order alphabetically! */ +static const namevalue_t rr_options[]= { + {"a", A}, + {"authrec", AUTHREC}, + {"cname", CNAME}, + {"mx", MX}, + {"name", NAME}, + {"ns", OWNER}, + {"owner", OWNER}, + {"ptr", PTR}, + {"reverse", REVERSE}, + {"soa", SOA}, + {"spf", SPF}, + {"ttl", TTL}, + {"txt", TXT} +}; + +/* Table for looking up source options. Order alphabetically! */ +static const namevalue_t source_options[]= { + {"authrec", AUTHREC}, + {"file", FILET}, + {"ns", OWNER}, + {"owner", OWNER}, + {"serve_aliases", SERVE_ALIASES}, + {"ttl", TTL} +}; + +/* Table for looking up include options. Order alphabetically! */ +static const namevalue_t include_options[]= { + {"file", FILET} +}; + +/* Table for looking up neg options. Order alphabetically! */ +static const namevalue_t neg_options[]= { + {"name", NAME}, + {"ttl", TTL}, + {"types", TYPES} +}; diff --git a/service/src/main/jni/pdnsd/src/conf-parser.c b/service/src/main/jni/pdnsd/src/conf-parser.c new file mode 100644 index 0000000..9cf9180 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/conf-parser.c @@ -0,0 +1,2118 @@ +/* conf-parser.c - Parser for pdnsd config files. + Based on the files conf-lex.l and conf-parse.y written by + Thomas Moestl. + This version was rewritten in C from scratch by Paul A. Rombouts + and doesn't require (f)lex or yacc/bison. + + Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011 Paul A. Rombouts. + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include "ipvers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(HAVE_STRUCT_IFREQ) +#include +#endif +#include "conff.h" +#include "consts.h" +#include "cache.h" +#include "dns.h" +#include "helpers.h" +#include "rr_types.h" +#include "netdev.h" +#include "conf-keywords.h" +#include "conf-parser.h" + + +/* Check that include files are not nested deeper than MAXINCLUDEDEPTH, + as a precaution against infinite recursion. */ +#define MAXINCLUDEDEPTH 100 + +static char *report_error (const char *conftype, unsigned linenr, const char *msg) +{ + char *retval; + if(linenr) { + if(asprintf(&retval, "Error in %s (line %u): %s",conftype,linenr,msg)<0) + retval=NULL; + } + else { + if(asprintf(&retval, "Error in %s: %s",conftype,msg)<0) + retval=NULL; + } + + return retval; +} + +static char *report_errorf (const char *conftype, unsigned linenr, const char *frm,...) printfunc(3, 4); +static char *report_errorf (const char *conftype, unsigned linenr, const char *frm,...) +{ + char *msg,*retval; int mlen; + va_list va; + va_start(va,frm); + mlen=vasprintf(&msg,frm,va); + va_end(va); + if(mlen<0) return NULL; + retval=report_error(conftype,linenr,msg); + free(msg); + return retval; +} + +/* return pointer to next character in linebuffer after skipping blanks and comments */ +static char* getnextp(char **buf, size_t *n, FILE* in, char *p, unsigned *linenr, char **errstr) +{ + if(!p) goto nextline; + tryagain: + if(!*p) { + nextline: + do { + if(!in || getline(buf,n,in)<0) { + *errstr=NULL; + return NULL; + } + ++*linenr; + p=*buf; + } while(!*p); + } + if(isspace(*p)) { + ++p; goto tryagain; + } + if(*p=='#') { + skip_rest_of_line: + if(*linenr) + goto nextline; + else { + p=strchr(p,'\n'); + if(p) { + ++p; + goto tryagain; + } + else + goto nextline; + } + } + if(*p=='/') { + if(*(p+1)=='/') + goto skip_rest_of_line; + if(*(p+1)=='*') { + int lev=1; + p +=2; + for(;;) { + while(*p) { + if(*p=='/' && *(p+1)=='*') { + ++lev; + p +=2; + continue; + } + else if(*p=='*' && *(p+1)=='/') { + p +=2; + if(--lev==0) goto tryagain; + continue; + } + ++p; + } + if(!in || getline(buf,n,in)<0) { + *errstr="comment without closing */"; + return NULL; + } + ++*linenr; + p=*buf; + } + } + } + + return p; +} + +static char translescapedchar(char c) +{ + switch(c) { + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + } + return c; +} + +/* Scan a buffer for a string and copy the decoded (i.e. unescaped) version into + another buffer. + + A string either begins after and ends before a double-quote ("), + or simply consists of a sequence of "non-special" characters, + starting at the current position. + A back-slash (\) acts as an escape character, preventing any character + following it from terminating the string. Thus, for example, + back-slash double-quote (\") may be used to include double-quotes + in a string. + A number of escape sequences are interpreted as in C, e.g. + \t, \n, \r yield control-chars as in C. + + char **curp should point to the position in the buffer where + the scanning should begin. It will be updated to point + to the first character past the scanned string. + + char *outbuf is used to store the decoded string. + size_t outbufsz should be the size of outbuf. + + The return value is the length of the decoded string, unless an error occurs, + in which case -1 is returned and *errstr is assigned an error message. + The returned length may be larger than outbufsz, in which case the buffer + is filled with only the first outbufsz chars of the string. +*/ +static int scan_string(char **curp,char *outbuf, unsigned outbufsz, char **errstr) +{ + char *cur=*curp; + unsigned i=0; + + if(*cur=='"') { + /* Double-quoted string. */ + ++cur; /* Skip opening quote. */ + for(;; ++i,++cur) { + if(!*cur) goto noclosingquote; + if(*cur=='"') break; + if(*cur=='\\') { + if(!*++cur) goto nofollowingchar; + if(i=sizeof(buf)) \ + {CLEANUP_HANDLERS; goto string_too_long;} \ + } +# define REPORT_ERROR(msg) (*errstr=report_error(conftype,linenr,msg)) +# if !defined(CPP_C99_VARIADIC_MACROS) + /* GNU C Macro Varargs style. */ +# define REPORT_ERRORF(args...) (*errstr=report_errorf(conftype,linenr,args)) +#else + /* ANSI C99 style. */ +# define REPORT_ERRORF(...) (*errstr=report_errorf(conftype,linenr,__VA_ARGS__)) +# endif +# define PARSERROR {CLEANUP_HANDLERS; goto free_linebuf_return;} +# define OUTOFMEMERROR {CLEANUP_HANDLERS; goto out_of_memory;} +# define CLEANUP_GOTO(lab) {CLEANUP_HANDLERS; goto lab;} + + *errstr=NULL; + if(in) { + linebuf=malloc(buflen); + if(!linebuf) { + /* If malloc() just failed, allocating space for an error message is unlikely to succeed. */ + return 0; + } + if(global) + conftype="config file"; + else + conftype="include file"; + } + else + conftype="config string"; + + p=prestr; + while((p=getnextp(&linebuf,&buflen,in,p,&linenr,&getnextperr))) { + if(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + sechdr=lookup_keyword(ps,len,section_headers); + if(!sechdr) { + REPORT_ERRORF("invalid section header: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='{') goto expected_bropen; + ++p; + SKIP_BLANKS(p); + + switch(sechdr) { + case GLOBAL: + if(!global) { + REPORT_ERROR(in?"global section not allowed in include file": + "global section not allowed in eval string"); + PARSERROR; + } + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,global_options); + if(!option) { + REPORT_ERRORF("invalid option for global section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + pdnsd_a *ipaddrp; + + case PERM_CACHE: + if (isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_OFF) { + global->perm_cache=0; + } + else + goto bad_perm_cache_option; + } + else if(isdigit(*p)) { + global->perm_cache=strtol(p,&p,0); + } + else { + bad_perm_cache_option: + REPORT_ERROR("bad qualifier in perm_cache= option."); + PARSERROR; + } + break; + + case CACHE_DIR: + SCAN_STRING(p,strbuf,len); + STRNDUP(global->cache_dir,strbuf,len); + break; + + case SERVER_PORT: + SCAN_UNSIGNED_NUM(global->port,p,"server_port option") + break; + + case OUTGOING_IP: + ipaddrp= &global->out_a; + goto scan_ip_or_interface; + + case SERVER_IP: + ipaddrp= &global->a; + scan_ip_or_interface: + SCAN_STRING(p,strbuf,len); + { + const char *err; + if ((err=parse_ip(strbuf,ipaddrp))) { +#if defined(HAVE_STRUCT_IFREQ) && defined(IFNAMSIZ) && defined(SIOCGIFADDR) + if(!strcmp(err,"bad IP address") && lenipv4= ((struct sockaddr_in *)&req.ifr_addr)->sin_addr; +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 + ipaddrp->ipv6= ((struct sockaddr_in6 *)&req.ifr_addr)->sin6_addr; +# endif + close(fd); + } + else { + REPORT_ERRORF("Failed to get IP address of %s: %s",req.ifr_name,strerror(errno)); + if(fd!=-1) close(fd); + PARSERROR; + } + } + else +#endif + { + REPORT_ERRORF("%s for the %s= option.",err,option==SERVER_IP?"server_ip":"outgoing_ip"); + PARSERROR; + } + } + } + break; + + case SCHEME_FILE: + SCAN_STRING(p,strbuf,len); + STRNDUP(global->scheme_file, strbuf,len); + break; + + case LINKDOWN_KLUGE: + ASSIGN_ON_OFF(global->lndown_kluge,p,C_ON,"bad qualifier in linkdown_kluge= option."); + break; + + case MAX_TTL: + SCAN_TIMESECS(global->max_ttl,p,"max_ttl option"); + break; + + case MIN_TTL: + SCAN_TIMESECS(global->min_ttl,p,"min_ttl option"); + break; + + case RUN_AS: + SCAN_STRING(p,strbuf,len); + STRNCP(global->run_as, strbuf,len, "run_as"); + break; + + case STRICT_SETUID: + ASSIGN_ON_OFF(global->strict_suid, p,C_ON,"bad qualifier in strict_setuid= option."); + break; + + case USE_NSS: + ASSIGN_ON_OFF(global->use_nss, p,C_ON,"bad qualifier in use_nss= option."); + break; + + case PARANOID: + ASSIGN_ON_OFF(global->paranoid, p,C_ON,"bad qualifier in paranoid= option."); + break; + + case IGNORE_CD: { + int ignore_cd; + ASSIGN_ON_OFF(ignore_cd, p,C_ON,"bad qualifier in ignore_cd= option."); + fprintf(stderr, "Warning: ignore_cd option in configuration file is obsolete and currently has no effect.\n"); + } + break; + + case STATUS_CTL: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in status_pipe= option."); + if(!cmdline.stat_pipe) global->stat_pipe=(cnst==C_ON); + } + break; + + case DAEMON: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in daemon= option."); + if(!cmdline.daemon) global->daemon=(cnst==C_ON); + } + break; + + case C_TCP_SERVER: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in tcp_server= option."); + if(!cmdline.notcp) { + global->notcp=(cnst==C_OFF); +#ifdef NO_TCP_SERVER + if(!global->notcp) { + REPORT_ERROR("pdnsd was compiled without TCP server support. tcp_server=on is not allowed."); + PARSERROR; + } +#endif + } + } + break; + + case PID_FILE: + SCAN_STRING(p,strbuf,len); + if(!cmdline.pidfile) {STRNDUP(global->pidfile,strbuf,len);} + break; + + case C_VERBOSITY: { + int val; + SCAN_UNSIGNED_NUM(val,p,"verbosity option"); + if(!cmdline.verbosity) global->verbosity=val; + } + break; + + case C_QUERY_METHOD: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==TCP_ONLY || cnst==UDP_ONLY || cnst==TCP_UDP || cnst==UDP_TCP,"bad qualifier in query_method= option."); +#ifdef NO_TCP_QUERIES + if (cnst==TCP_ONLY) { + REPORT_ERROR("the tcp_only option is only available when pdnsd is compiled with TCP support."); + PARSERROR; + } + else +#endif +#ifdef NO_UDP_QUERIES + if (cnst==UDP_ONLY) { + REPORT_ERROR("the udp_only option is only available when pdnsd is compiled with UDP support."); + PARSERROR; + } + else +#endif +#if defined(NO_TCP_QUERIES) || defined(NO_UDP_QUERIES) + if (cnst==TCP_UDP) { + REPORT_ERROR("the tcp_udp option is only available when pdnsd is compiled with both TCP and UDP support."); + PARSERROR; + } + else if (cnst==UDP_TCP) { + REPORT_ERROR("the udp_tcp option is only available when pdnsd is compiled with both TCP and UDP support."); + PARSERROR; + } + else +#endif + if(!cmdline.query_method) global->query_method=cnst; + } + break; + + case RUN_IPV4: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF,"bad qualifier in run_ipv4= option."); +#ifndef ENABLE_IPV4 + if(cnst==C_ON) { + REPORT_ERROR("You can only set run_ipv4=on when pdnsd is compiled with IPv4 support."); + PARSERROR; + } +#endif +#ifndef ENABLE_IPV6 + if(cnst==C_OFF) { + REPORT_ERROR("You can only set run_ipv4=off when pdnsd is compiled with IPv6 support."); + PARSERROR; + } +#endif +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(!cmdlineipv) { + run_ipv4=(cnst==C_ON); cmdlineipv=-1; + } + else if(cmdlineipv<0 && run_ipv4!=(cnst==C_ON)) { + REPORT_ERROR(cmdlineipv==-1? + "IPv4/IPv6 conflict: you are trying to set run_ipv4 to a value that conflicts with a previous run_ipv4 setting.": + "You must set the run_ipv4 option before specifying IP addresses."); + PARSERROR; + } +#endif + } + break; + + case IPV4_6_PREFIX: + SCAN_STRING(p,strbuf,len); +#ifdef ENABLE_IPV6 + if(!cmdline.prefix) { + if(inet_pton(AF_INET6,strbuf,&global->ipv4_6_prefix)<=0) { + REPORT_ERROR("ipv4_6_prefix: argument not a valid IPv6 address."); + PARSERROR; + } + } +#else + fprintf(stderr,"pdnsd was compiled without IPv6 support. ipv4_6_prefix option in config file will be ignored.\n"); +#endif + break; + + case C_DEBUG: { + int cnst; + ASSIGN_CONST(cnst, p,cnst==C_ON || cnst==C_OFF ,"bad qualifier in debug= option."); + if(!cmdline.debug) { + global->debug=(cnst==C_ON); +#if !DEBUG + if(global->debug) + fprintf(stderr,"pdnsd was compiled without debugging support. debug=on has no effect.\n"); +#endif + } + } + break; + + case C_CTL_PERMS: + SCAN_UNSIGNED_NUM(global->ctl_perms, p,"ctl_perms option"); + break; + + case C_PROC_LIMIT: + SCAN_UNSIGNED_NUM(global->proc_limit, p,"proc_limit option"); + break; + + case C_PROCQ_LIMIT: + SCAN_UNSIGNED_NUM(global->procq_limit, p,"procq_limit option"); + break; + + case TCP_QTIMEOUT: + SCAN_TIMESECS(global->tcp_qtimeout, p,"tcp_qtimeout option"); + break; + + case TIMEOUT: + SCAN_TIMESECS(global->timeout, p,"global timeout option"); + break; + + case C_PAR_QUERIES: { + int val; + SCAN_UNSIGNED_NUM(val, p,"par_queries option"); + if(val<=0) { + REPORT_ERROR("bad value for par_queries."); + PARSERROR; + } else { + global->par_queries=val; + } + } + break; + + case C_RAND_RECS: + ASSIGN_ON_OFF(global->rnd_recs, p,C_ON,"bad qualifier in randomize_recs= option."); + break; + + case NEG_TTL: + SCAN_TIMESECS(global->neg_ttl, p,"neg_ttl option"); + break; + + case NEG_RRS_POL: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF || cnst==C_DEFAULT || cnst==C_AUTH, + "bad qualifier in neg_rrs_pol= option."); + global->neg_rrs_pol=cnst; + } + break; + + case NEG_DOMAIN_POL: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF || cnst==C_AUTH,"bad qualifier in neg_domain_pol= option."); + global->neg_domain_pol=cnst; + } + break; + + case QUERY_PORT_START: { + int val; + if(isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_NONE) + val=-1; + else + goto bad_port_start_option; + } + else if(isdigit(*p)) { + val=strtol(p,&p,0); + if(val>65535) { + REPORT_ERROR("value for query_port_start out of range."); + PARSERROR; + } + else if(val<1024) + fprintf(stderr,"Warning: query_port_start=%i but source ports <1204 can only be used as root.\n", + val); + } + else { + bad_port_start_option: + REPORT_ERROR("bad qualifier in query_port_start= option."); + PARSERROR; + } + global->query_port_start=val; + } + break; + + case QUERY_PORT_END: { + int val; + SCAN_UNSIGNED_NUM(val,p,"query_port_end option"); + if(val>65535) { + REPORT_ERROR("value for query_port_end out of range."); + PARSERROR; + } + global->query_port_end=val; + } + break; + + case UDP_BUFSIZE: { + int val; + SCAN_UNSIGNED_NUM(val,p,"udpbufsize"); + if(val<512 || val>65535-(20+8)) { + REPORT_ERROR("value for udpbufsize out of range."); + PARSERROR; + } + global->udpbufsize=val; + } + break; + + case DELEGATION_ONLY: + SCAN_STRING_LIST(&global->deleg_only_zones,p,strbuf,len,zone_add) + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + + if(*p!='}') goto expected_closing_brace; + if (global->query_port_end < global->query_port_start) { + REPORT_ERROR("query_port_end may not be smaller than query_port_start."); + PARSERROR; + } + break; + + case SERVER: { + servparm_t server; + + if(!servers) { + REPORT_ERROR(in?"server section not allowed in include file": + "server section not allowed in eval string"); + PARSERROR; + } + + server=serv_presets; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER (free_servparm(&server)) + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,server_options); + if(!option) { + REPORT_ERRORF("invalid option for server section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') CLEANUP_GOTO(expected_equals); + ++p; + SKIP_BLANKS(p); + + switch(option) { + case IP: + SCAN_STRING_LIST(&server.atup_a,p,strbuf,len,addr_add_); + break; + + case FILET: + SCAN_STRING(p,strbuf,len); + { + char *errmsg; + if (!read_resolv_conf(strbuf, &server.atup_a, &errmsg)) { + if(errmsg) {REPORT_ERROR(errmsg); free(errmsg);} + else *errstr=NULL; + PARSERROR; + } + } + break; + + case PORT: + SCAN_UNSIGNED_NUM(server.port,p,"port option"); + break; + + case SCHEME: + SCAN_STRING(p,strbuf,len); + STRNCP(server.scheme, strbuf,len, "scheme"); + break; + + case UPTEST: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_PING || cnst==C_NONE || cnst==C_IF || cnst==C_EXEC || cnst==C_DEV || cnst==C_DIALD || cnst==C_QUERY,"bad qualifier in uptest= option."); + server.uptest=cnst; + } + break; + + case TIMEOUT: + SCAN_TIMESECS(server.timeout,p,"timeout option"); + break; + + case PING_TIMEOUT: + SCAN_UNSIGNED_NUM(server.ping_timeout,p,"ping_timeout option"); + break; + + case PING_IP: + SCAN_STRING(p,strbuf,len); + { + const char *err; + if ((err=parse_ip(strbuf,&server.ping_a))) { + REPORT_ERRORF("%s for the ping_ip= option.",err); + PARSERROR; + } + } + break; + + case UPTEST_CMD: + SCAN_STRING(p,strbuf,len); + STRNDUP(server.uptest_cmd, strbuf,len); + SKIP_BLANKS(p); + if(*p==',') { + ++p; + SKIP_BLANKS(p); + SCAN_STRING(p,strbuf,len); + STRNCP(server.uptest_usr, strbuf,len, "second argument of uptest_cmd"); + } + break; + + case QUERY_TEST_NAME: + if(isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + if(*p!='.' && *p!='-') { + cnst=lookup_const(ps,len); + if(cnst==C_NONE) { + if(server.query_test_name) + free(server.query_test_name); + server.query_test_name=NULL; + break; + } + } + p=ps; /* reset current char pointer and try again. */ + } + { + unsigned char tname[DNSNAMEBUFSIZE], *copy; + unsigned sz; + + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,tname); + sz=rhnlen(tname); + copy= malloc(sz); + if(!copy) { + OUTOFMEMERROR; + } + memcpy(copy,tname,sz); + if(server.query_test_name) + free(server.query_test_name); + server.query_test_name=copy; + } + break; + + case INTERVAL: + if(isalpha(*p)) { + int cnst; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_ONQUERY) { + server.interval=-1; + } + else if(cnst==C_ONTIMEOUT) { + server.interval=-2; + } + else { + goto bad_interval_option; + } + } + else if(isdigit(*p)) { + char *err; + server.interval=strtotime(p,&p,&err); + if(err) { + REPORT_ERRORF("bad time specification in interval= option: %s",err); + PARSERROR; + } + } + else { + bad_interval_option: + REPORT_ERROR("bad qualifier in interval= option."); + PARSERROR; + } + break; + + case INTERFACE: + SCAN_STRING(p,strbuf,len); + STRNCP(server.interface, strbuf,len, "interface"); + break; + + case DEVICE: + SCAN_STRING(p,strbuf,len); + STRNCP(server.device, strbuf,len, "device"); + break; + + case PURGE_CACHE: + ASSIGN_ON_OFF(server.purge_cache,p,C_ON,"bad qualifier in purge_cache= option."); + break; + + case CACHING: + ASSIGN_ON_OFF(server.nocache,p,C_OFF,"bad qualifier in caching= option."); + break; + + case LEAN_QUERY: + ASSIGN_ON_OFF(server.lean_query,p,C_ON,"bad qualifier in lean_query= option."); + break; + + case EDNS_QUERY: + ASSIGN_ON_OFF(server.edns_query,p,C_ON,"bad qualifier in edns_query= option."); + break; + + case PRESET: + ASSIGN_ON_OFF(server.preset,p,C_ON,"bad qualifier in preset= option."); + break; + + case PROXY_ONLY: + ASSIGN_ON_OFF(server.is_proxy,p,C_ON,"bad qualifier in proxy_only= option."); + break; + + case ROOT_SERVER: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF || cnst==C_DISCOVER,"bad qualifier in root_server= option."); + server.rootserver= (cnst==C_DISCOVER? 2: cnst==C_ON); + } + break; + + case RANDOMIZE_SERVERS: + ASSIGN_ON_OFF(server.rand_servers,p,C_ON,"bad qualifier in randomize_servers= option."); + break; + + case POLICY: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_INCLUDED || cnst==C_EXCLUDED || cnst==C_SIMPLE_ONLY || cnst==C_FQDN_ONLY,"bad qualifier in policy= option."); + server.policy=cnst; + } + break; + + case INCLUDE: + SCAN_STRING_LIST(&server.alist,p,strbuf,len,include_list_add) + break; + + case EXCLUDE: + SCAN_STRING_LIST(&server.alist,p,strbuf,len,exclude_list_add) + break; + + case REJECTLIST: + SCAN_STRING_LIST(&server,p,strbuf,len,reject_add_); + break; + + case REJECTPOLICY: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_FAIL || cnst==C_NEGATE,"bad qualifier in reject_policy= option."); + server.rejectpolicy=cnst; + } + break; + + case REJECTRECURSIVELY: + ASSIGN_ON_OFF(server.rejectrecursively,p,C_ON,"bad qualifier in reject_recursively= option."); + break; + + case LABEL: + SCAN_STRING(p,strbuf,len); + STRNDUP(server.label,strbuf,len); + break; + + default: /* we should never get here */ + CLEANUP_GOTO(internal_parse_error); + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') CLEANUP_GOTO(expected_semicolon); + ++p; + SKIP_BLANKS(p); + } + + if(*p!='}') CLEANUP_GOTO(expected_closing_brace); + if (server.uptest==C_EXEC) { + if (!server.uptest_cmd) { + REPORT_ERROR("you must specify uptest_cmd if you specify uptest=exec!"); + PARSERROR; + } + } + if (server.is_proxy && server.rootserver) { + REPORT_ERROR("A server may not be specified as both a proxy and a root-server."); + PARSERROR; + } + if(server.rootserver && (server.policy==C_SIMPLE_ONLY || server.policy==C_FQDN_ONLY)) + fprintf(stderr,"Warning: using policy=%s with a root-server usually makes no sense.", + const_name(server.policy)); + if (DA_NEL(server.atup_a)) { + check_localaddrs(&server); + if(!DA_NEL(server.atup_a)) { + REPORT_ERROR("Server section contains only local IP addresses.\n" + "Bind pdnsd to a different local IP address or specify different port numbers" + " in global section and server section if you want pdnsd to query servers on" + " the same machine."); + PARSERROR; + } + } + { + int j,n=DA_NEL(server.atup_a); + for(j=0;jis_up=server.preset; + /* A negative test interval means don't test at startup or reconfig. */ + if(server.interval<0) at->i_ts=time(NULL); + } + } + if(server.interval==-1) global->onquery=1; + + if (!(*servers=DA_GROW1_F(*servers,(void(*)(void*))free_servparm))) { + OUTOFMEMERROR; + } + DA_LAST(*servers)= server; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER + } + break; + + case RR: { + /* Initialize c_cent to all zeros. + Then it should be safe to call free_cent() on it, even before calling init_cent(). */ + dns_cent_t c_cent={0}; + time_t c_ttl=86400; + unsigned c_flags=DF_LOCAL; + unsigned char reverse=0; + +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER (free_cent(&c_cent DBG0)) + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,rr_options); + if(!option) { + REPORT_ERRORF("invalid option for rr section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') CLEANUP_GOTO(expected_equals); + ++p; + SKIP_BLANKS(p); + + switch(option) { + int tp; const char *tpname; + case NAME: { + unsigned char c_name[DNSNAMEBUFSIZE]; + if (c_cent.qname) { + REPORT_ERROR("You may specify only one name in a rr section."); + PARSERROR; + } + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_name); + if (!init_cent(&c_cent, c_name, 0, 0, c_flags DBG0)) + goto out_of_memory; + } + break; + + case TTL: + SCAN_TIMESECS(c_ttl,p, "ttl option"); + break; + + case AUTHREC: { + int cnst; + if (c_cent.qname) { + REPORT_ERROR("The authrec= option has no effect unless it precedes name= in a rr section."); + PARSERROR; + } + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF,"Bad qualifier in authrec= option."); + c_flags=(cnst==C_ON)?DF_LOCAL:0; + } + break; + + case REVERSE: + ASSIGN_ON_OFF(reverse,p,C_ON,"bad qualifier in reverse= option."); + break; + + case A: { + unsigned int sz; + pdnsd_ca c_a; + + if (!c_cent.qname) + goto no_name_spec; + SCAN_STRING(p,strbuf,len); + if (inet_aton(strbuf,&c_a.ipv4)) { + tp=T_A; + sz=sizeof(struct in_addr); + } + else +#if ALLOW_LOCAL_AAAA + if (inet_pton(AF_INET6,strbuf,&c_a.ipv6)>0) { + tp=T_AAAA; + sz=sizeof(struct in6_addr); + } + else +#endif + { + REPORT_ERROR("bad IP address in a= option."); + PARSERROR; + } + + if(!add_cent_rr(&c_cent,tp,c_ttl,0,CF_LOCAL,sz,&c_a DBG0)) + goto add_rr_failed; + } + break; + + case OWNER: + tp=T_NS; + goto scan_name; + case CNAME: + tp=T_CNAME; + goto scan_name; + case PTR: + tp=T_PTR; + scan_name: + { + unsigned char c_name[DNSNAMEBUFSIZE]; + + if (!c_cent.qname) + goto no_name_spec; + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_name); + if(!add_cent_rr(&c_cent,tp,c_ttl,0,CF_LOCAL,rhnlen(c_name),c_name DBG0)) + goto add_rr_failed; + } + break; + + case MX: { + unsigned char *cp; + unsigned pref; + unsigned char c_mx[2+DNSNAMEBUFSIZE]; + + if (!c_cent.qname) + goto no_name_spec; + cp=c_mx+2; + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,cp); + SKIP_COMMA(p,"missing second argument (preference level) of mx= option"); + SCAN_UNSIGNED_NUM(pref,p,"second argument of mx= option"); + cp=c_mx; + PUTINT16(pref,cp); + if(!add_cent_rr(&c_cent,T_MX,c_ttl,0,CF_LOCAL,2+rhnlen(cp),c_mx DBG0)) + goto add_rr_failed; + } + break; + + case SOA: { + unsigned int blen,rlen; + unsigned char *bp; + uint32_t val; + unsigned char buf[2*DNSNAMEBUFSIZE+20]; + + if (!c_cent.qname) + goto no_name_spec; + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,buf); + rlen=rhnlen(buf); + blen=rlen; + bp=buf+rlen; + SKIP_COMMA(p,"missing 2nd argument of soa= option"); + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,bp); + rlen=rhnlen(bp); + blen += rlen; + bp += rlen; + SKIP_COMMA(p,"missing 3rd argument of soa= option"); + SCAN_UNSIGNED_NUM(val,p,"3rd argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 4th argument of soa= option"); + SCAN_TIMESECS(val,p,"4th argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 5th argument of soa= option"); + SCAN_TIMESECS(val,p,"5th argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 6th argument of soa= option"); + SCAN_TIMESECS(val,p,"6th argument of soa= option"); + PUTINT32(val,bp); + SKIP_COMMA(p,"missing 7th argument of soa= option"); + SCAN_TIMESECS(val,p,"7th argument of soa= option"); + PUTINT32(val,bp); + blen += 20; + if(!add_cent_rr(&c_cent,T_SOA,c_ttl,0,CF_LOCAL,blen,buf DBG0)) + goto add_rr_failed; + } + break; + case SPF: +#if IS_CACHED_SPF + tp=T_SPF; tpname="spf"; + goto define_txt_rr; +#else + REPORT_ERROR("Missing support for caching SPF records in rr section"); + PARSERROR; +#endif + case TXT: +#if IS_CACHED_TXT + tp=T_TXT; tpname="txt"; +#else + REPORT_ERROR("Missing support for caching TXT records in rr section"); + PARSERROR; +#endif +#if IS_CACHED_TXT || IS_CACHED_SPF +#if IS_CACHED_SPF + define_txt_rr: +#endif + { + unsigned char *rbuf; + unsigned sz,allocsz; + int rv; + + if (!c_cent.qname) + goto no_name_spec; + rbuf=NULL; + sz=allocsz=0; +# undef CLEANUP_HANDLER2 +# define CLEANUP_HANDLER2 (free(rbuf)) + + for(;;) { + unsigned char *newbuf,*cp; + unsigned newsz=sz+256; + int n; + if(newsz>allocsz) { + allocsz += 512; + newbuf=realloc(rbuf,allocsz); + if(!newbuf) { + OUTOFMEMERROR; + } + rbuf=newbuf; + } + cp = rbuf+sz; + n=scan_string(&p, charp (cp+1), 255, &scanstrerr); + if(n==-1) { + REPORT_ERRORF("%s in %s= option", scanstrerr, tpname); + PARSERROR; + } + if(n>255) { + REPORT_ERRORF("string longer than 255 bytes in %s= option", tpname); + PARSERROR; + } + *cp=n; + sz += n+1; + if(sz>0xffff) { + REPORT_ERRORF("data exceeds maximum size (65535 bytes) in %s= option", tpname); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!=',') break; + ++p; + SKIP_BLANKS(p); + } + rv=add_cent_rr(&c_cent,tp,c_ttl,0,CF_LOCAL,sz,rbuf DBG0); + CLEANUP_HANDLER2; +# undef CLEANUP_HANDLER2 +# define CLEANUP_HANDLER2 + if(!rv) + goto add_rr_failed; + } + break; +#endif + default: /* we should never get here */ + CLEANUP_GOTO(internal_parse_error); + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') CLEANUP_GOTO(expected_semicolon); + ++p; + SKIP_BLANKS(p); + } + + if(*p!='}') CLEANUP_GOTO(expected_closing_brace); + if (!c_cent.qname) + goto no_name_spec; + if(c_cent.qname[0]==1 && c_cent.qname[1]=='*') { + /* Wild card record. Set the DF_WILD flag for the name with '*.' removed. */ + if(!set_cent_flags(&c_cent.qname[2],DF_WILD)) { + unsigned char buf[DNSNAMEBUFSIZE]; + rhn2str(c_cent.qname,buf,sizeof(buf)); + REPORT_ERRORF("You must define some records for '%s'" + " before you can define records for the wildcard name '%s'", + &buf[2],buf); + PARSERROR; + } + } + + add_cache(&c_cent); + if(reverse) { + if(!add_reverse_cache(&c_cent)) { + REPORT_ERROR("Can't convert IP address in a= option" + " into form suitable for reverse resolving."); + PARSERROR; + } + } + CLEANUP_HANDLER; + break; + + add_rr_failed: + OUTOFMEMERROR; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER + } + + case SOURCE: { + unsigned char c_owner[DNSNAMEBUFSIZE]; + time_t c_ttl; + unsigned c_flags; + unsigned char c_aliases; + + c_owner[0]='\0'; + c_ttl=86400; + c_flags=DF_LOCAL; + c_aliases=0; + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,source_options); + if(!option) { + REPORT_ERRORF("invalid option for source section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + case OWNER: + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_owner); + break; + + case TTL: + SCAN_TIMESECS(c_ttl,p,"ttl option"); + break; + + case FILET: + if (!c_owner[0]) { + REPORT_ERROR("you must specify owner before file= in source records."); + PARSERROR; + } + SCAN_STRING(p,strbuf,len); + { + char *errmsg; + if (!read_hosts(strbuf, c_owner, c_ttl, c_flags, c_aliases, &errmsg)) { + if(errmsg) { REPORT_ERROR(errmsg); free(errmsg); } + else *errstr=NULL; + PARSERROR; + } + } + break; + + case SERVE_ALIASES: + ASSIGN_ON_OFF(c_aliases,p,C_ON,"Bad qualifier in serve_aliases= option."); + break; + + case AUTHREC: { + int cnst; + ASSIGN_CONST(cnst,p,cnst==C_ON || cnst==C_OFF,"Bad qualifier in authrec= option."); + c_flags=(cnst==C_ON)?DF_LOCAL:0; + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + } + break; + + case INCLUDE_F: { + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,include_options); + if(!option) { + REPORT_ERRORF("invalid option for include section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + case FILET: + if(includedepth>=MAXINCLUDEDEPTH) { + REPORT_ERRORF("maximum include depth (%d) exceeded.",MAXINCLUDEDEPTH); + PARSERROR; + } + SCAN_STRING(p,strbuf,len); + { + char *errmsg; + if (!read_config_file(strbuf, NULL, NULL, includedepth+1, &errmsg)) { + if(errmsg) { + if(linenr) { + if(asprintf(errstr, "In file %s included at line %u:\n%s",strbuf,linenr,errmsg)<0) + *errstr=NULL; + } + else { + if(asprintf(errstr, "In file %s:\n%s",strbuf,errmsg)<0) + *errstr=NULL; + } + free(errmsg); + } + else + *errstr=NULL; + PARSERROR; + } + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + } + break; + + case NEG: { + unsigned char c_name[DNSNAMEBUFSIZE]; + time_t c_ttl; + unsigned char htp,hdtp; + + htp=0; + hdtp=0; + c_name[0]='\0'; + c_ttl=86400; + + while(isalpha(*p)) { + SCAN_ALPHANUM(ps,p,len); + option=lookup_keyword(ps,len,neg_options); + if(!option) { + REPORT_ERRORF("invalid option for neg section: %.*s",(int)len,ps); + PARSERROR; + } + SKIP_BLANKS(p); + if(*p!='=') goto expected_equals; + ++p; + SKIP_BLANKS(p); + + switch(option) { + case NAME: + SCAN_STRING(p,strbuf,len); + PARSESTR2RHN(ucharp strbuf,len,c_name); + break; + + case TTL: + SCAN_TIMESECS(c_ttl,p, "ttl option"); + break; + + case TYPES: + if (!c_name[0]) { + REPORT_ERROR("you must specify a name before the types= option."); + PARSERROR; + } + if (isalpha(*p)) { + int cnst; + dns_cent_t c_cent /* ={0} */; + SCAN_ALPHANUM(ps,p,len); + cnst=lookup_const(ps,len); + if(cnst==C_DOMAIN) { + if (htp) { + REPORT_ERROR("You may not specify types=domain together with other types!"); + PARSERROR; + } + hdtp=1; + if (!init_cent(&c_cent, c_name, c_ttl, 0, DF_LOCAL|DF_NEGATIVE DBG0)) + goto out_of_memory; + } + else if(cnst==0) { + if (hdtp) { + REPORT_ERROR("You may not specify types=domain together with other types!"); + PARSERROR; + } + htp=1; + if (!init_cent(&c_cent, c_name, 0, 0, 0 DBG0)) + goto out_of_memory; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER (free_cent(&c_cent DBG0)) + for(;;) { + { + TEMPSTRNCPY(buf,ps,len); + cnst=rr_tp_byname(buf); + } + if(cnst==-1) { + REPORT_ERRORF("unrecognized rr type '%.*s' used as argument for types= option.",(int)len,ps); + PARSERROR; + } + if(PDNSD_NOT_CACHED_TYPE(cnst)) { + REPORT_ERRORF("illegal rr type '%.*s' used as argument for types= option.",(int)len,ps); + PARSERROR; + } + if (!getrrset_eff(&c_cent,cnst) && !add_cent_rrset_by_type(&c_cent,cnst,c_ttl,0,CF_LOCAL|CF_NEGATIVE DBG0)) { + OUTOFMEMERROR; + } + SKIP_BLANKS(p); + if(*p!=',') break; + ++p; + SKIP_BLANKS(p); + if (!isalpha(*p)) + {CLEANUP_GOTO(bad_types_option);} + SCAN_ALPHANUM(ps,p,len); + } + } + else + goto bad_types_option; + + add_cache(&c_cent); + CLEANUP_HANDLER; +# undef CLEANUP_HANDLER +# define CLEANUP_HANDLER + } + else { + bad_types_option: + REPORT_ERROR("Bad argument for types= option."); + PARSERROR; + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(option) */ + + SKIP_BLANKS(p); + if(*p!=';') goto expected_semicolon; + ++p; + SKIP_BLANKS(p); + } + } + break; + + default: /* we should never get here */ + goto internal_parse_error; + } /* end of switch(sechdr) */ + + if(*p!='}') goto expected_closing_brace; + ++p; + } + else { + REPORT_ERROR("expected section header"); + PARSERROR; + } + } + + if(!in || feof(in)) { + if(getnextperr) { + REPORT_ERROR(getnextperr); + PARSERROR; + } + retval=1; /* success */ + } + else + goto input_error; + + goto free_linebuf_return; + + expected_bropen: + REPORT_ERROR("expected opening brace after section name"); + PARSERROR; + + expected_closing_brace: + REPORT_ERROR("expected beginning of new option or closing brace"); + PARSERROR; + + expected_equals: + REPORT_ERROR("expected equals sign after option name"); + PARSERROR; + + expected_semicolon: + REPORT_ERROR("too many arguments to option or missing semicolon"); + PARSERROR; + + string_err: + REPORT_ERROR(scanstrerr); + PARSERROR; + + string_too_long: + REPORT_ERROR("string length exceeds buffer size"); + PARSERROR; + + no_name_spec: + REPORT_ERROR("you must specify a name before a,ptr,cname,mx,ns(owner) and soa records."); + PARSERROR; + + internal_parse_error: + if(asprintf(errstr,"Internal inconsistency detected while parsing line %u of %s.\n" + "Please consider reporting this error to one of the maintainers.\n",linenr,conftype)<0) + *errstr=NULL; + PARSERROR; + + out_of_memory: + /* If malloc() just failed, allocating space for an error message is unlikely to succeed. */ + *errstr=NULL; + PARSERROR; + + unexpected_eof: + if(!in || feof(in)) { + REPORT_ERROR(getnextperr?getnextperr:in?"unexpected end of file":"unexpected end of input string"); + } + else + input_error: { + if(asprintf(errstr,"Error while reading config file: %s",strerror(errno))<0) + *errstr=NULL; + } + + free_linebuf_return: + free(linebuf); + return retval; + +#undef SKIP_BLANKS +#undef SCAN_STRING +#undef REPORT_ERROR +#undef REPORT_ERRORF +#undef PARSERROR +#undef OUTOFMEMERROR +#undef CLEANUP_GOTO +} + + +/* Convert a string representation of an IP address into a binary format. */ +static const char* parse_ip(const char *ipstr, pdnsd_a *a) +{ +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(!cmdlineipv) cmdlineipv=-2; +#endif + { + if(!strcmp(ipstr,"any")) { +#ifdef ENABLE_IPV4 + if (run_ipv4) + a->ipv4.s_addr=INADDR_ANY; +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 + a->ipv6=in6addr_any; +#endif + } + else if(!str2pdnsd_a(ipstr,a)) { +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(run_ipv4 && inet_pton(AF_INET6,ipstr,&a->ipv6)>0) { + return "You should set run_ipv4=off or use the command-line option -6" + " before specifying an IPv6 address"; + } +#endif + return "bad IP address"; + } + } + return NULL; +} + +/* Add an IP address to the list of name servers. */ +static const char *addr_add(atup_array *ata, const char *ipstr) +{ + atup_t *at; + pdnsd_a addr; + +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(!cmdlineipv) cmdlineipv=-2; +#endif + { + if(!str2pdnsd_a(ipstr,&addr)) { +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + if(run_ipv4 && inet_pton(AF_INET6,ipstr,&addr.ipv6)>0) { + fprintf(stderr,"IPv6 address \"%s\" in config file ignored while running in IPv4 mode.\n",ipstr); + return NULL; + } +#endif + return "bad IP address"; + } + } + + if (!(*ata=DA_GROW1(*ata))) { + return "out of memory!"; + } + at=&DA_LAST(*ata); + SET_PDNSD_A2(&at->a, &addr); + at->is_up=0; + at->i_ts=0; + return NULL; +} + + +/* Helper functions for making netmasks */ +inline static uint32_t mk_netmask4(int len) +{ + uint32_t m; + + if(len<=0) + return 0; + + m= ~(uint32_t)0; + return (len<32)? htonl(m<<(32-len)): m; +} + +#if ALLOW_LOCAL_AAAA +inline static void mk_netmask6(struct in6_addr *m, int len) +{ + uint32_t *ma = (uint32_t *)m; + ma[0] = mk_netmask4(len); + ma[1] = mk_netmask4(len -= 32); + ma[2] = mk_netmask4(len -= 32); + ma[3] = mk_netmask4(len -= 32); +} +#endif + +/* Add an IP address/mask to the reject lists. */ +static const char *reject_add(servparm_t *serv, const char *ipstr) +{ + char *slash=strchr(ipstr,'/'); int mlen=0; + + if(slash) { + *slash++=0; + + if(*slash && isdigit(*slash)) { + char *endptr; + int l = strtol(slash,&endptr,10); + if(!*endptr) { + mlen=l; + slash=NULL; + } + } + } + else + mlen=128; /* Works for both IPv4 and IPv6 */ + + { + addr4maskpair_t am; + + am.mask.s_addr = mk_netmask4(mlen); + if(inet_aton(ipstr,&am.a) && (!slash || inet_aton(slash,&am.mask))) { + if(!(serv->reject_a4=DA_GROW1(serv->reject_a4))) + return "out of memory!"; + + DA_LAST(serv->reject_a4) = am; + return NULL; + } + } +#if ALLOW_LOCAL_AAAA + { + addr6maskpair_t am; + + mk_netmask6(&am.mask,mlen); + if(inet_pton(AF_INET6,ipstr,&am.a)>0 && (!slash || inet_pton(AF_INET6,slash,&am.mask)>0)) { + if(!(serv->reject_a6=DA_GROW1(serv->reject_a6))) + return "out of memory!"; + + DA_LAST(serv->reject_a6) = am; + return NULL; + } + } +#endif + + return "bad IP address"; +} + +/* Try to avoid the possibility that pdnsd will query itself. */ +static void check_localaddrs(servparm_t *serv) +{ + if(serv->port == global.port) { + atup_array ata=serv->atup_a; + int i,j=0,n=DA_NEL(ata); + for(i=0;ia))) { + char buf[ADDRSTR_MAXLEN]; + fprintf(stderr,"Local name-server address \"%s\" ignored in config file.\n", + pdnsd_a2str(PDNSD_A2_TO_A(&at->a),buf,ADDRSTR_MAXLEN)); + continue; + } + } + else { + if(equiv_inaddr2(&global.a,&at->a)) { + char buf[ADDRSTR_MAXLEN]; + fprintf(stderr,"Ignoring name-server address \"%s\" in config file (identical to server_ip address).\n", + pdnsd_a2str(PDNSD_A2_TO_A(&at->a),buf,ADDRSTR_MAXLEN)); + continue; + } + } + if(jatup_a=DA_RESIZE(ata,j); + } +} + +/* Read the name server addresses from a resolv.conf-style file. */ +static int read_resolv_conf(const char *fn, atup_array *ata, char **errstr) +{ + int rv=0; + FILE *f; + char *buf; + size_t buflen=256; + unsigned linenr=0; + + if (!(f=fopen(fn,"r"))) { + if(asprintf(errstr, "Failed to open %s: %s", fn, strerror(errno))<0) + *errstr=NULL; + return 0; + } + buf=malloc(buflen); + if(!buf) { + *errstr=NULL; + goto fclose_return; + } + while(getline(&buf,&buflen,f)>=0) { + size_t len; + char *p,*ps; + ++linenr; + p=buf; + for(;; ++p) { + if(!*p) goto nextline; + if(!isspace(*p)) break; + } + ps=p; + do { + if(!*++p) goto nextline; + } while(!isspace(*p)); + len=p-ps; + if(len==strlitlen("nameserver") && !strncmp(ps,"nameserver",len)) { + const char *errmsg; + do { + if(!*++p) goto nextline; + } while (isspace(*p)); + ps=p; + do { + ++p; + } while(*p && !isspace(*p)); + len=p-ps; + { + TEMPSTRNCPY(ipstr,ps,len); + errmsg=addr_add(ata, ipstr); + } + if(errmsg) { + if(asprintf(errstr, "%s in line %u of file %s", errmsg,linenr,fn)<0) + *errstr=NULL; + goto cleanup_return; + } + } + nextline:; + } + if (feof(f)) + rv=1; + else if(asprintf(errstr, "Failed to read %s: %s", fn, strerror(errno))<0) + *errstr=NULL; + cleanup_return: + free(buf); + fclose_return: + fclose(f); + return rv; +} + +static const char *slist_add(slist_array *sla, const char *nm, unsigned int len, int tp) +{ + slist_t *sl; + int exact=1; + const char *err; + size_t sz; + unsigned char rhn[DNSNAMEBUFSIZE]; + + if (len>1 && *nm=='.') { + exact=0; + ++nm; + --len; + } + if((err=parsestr2rhn(ucharp nm,len,rhn))) + return err; + sz=rhnlen(rhn); + if (!(*sla=DA_GROW1_F(*sla,free_slist_domain))) { + return "out of memory!"; + } + sl=&DA_LAST(*sla); + + sl->exact=exact; + sl->rule=tp; + if (!(sl->domain=malloc(sz))) + return "out of memory!"; + memcpy(sl->domain,rhn,sz); + return NULL; +} + +static const char *zone_add(zone_array *za, const char *zone, unsigned int len) +{ + zone_t z; + const char *err; + size_t sz; + unsigned char rhn[DNSNAMEBUFSIZE]; + + if((err=parsestr2rhn(ucharp zone,len,rhn))) + return err; + sz=rhnlen(rhn); + if(!(*za=DA_GROW1_F(*za,free_zone)) || !(DA_LAST(*za)=z=malloc(sz))) + return "out of memory!"; + memcpy(z,rhn,sz); + return NULL; +} + diff --git a/service/src/main/jni/pdnsd/src/conf-parser.h b/service/src/main/jni/pdnsd/src/conf-parser.h new file mode 100644 index 0000000..d3a3e99 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/conf-parser.h @@ -0,0 +1,29 @@ +/* conf-parser.h - definitions for parser of pdnsd config files. + The parser was rewritten in C from scratch and doesn't require (f)lex + or yacc/bison. + + Copyright (C) 2004,2008 Paul A. Rombouts. + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef CONF_PARSER_H +#define CONF_PARSER_H + +int confparse(FILE* in, char *prestr, globparm_t *global, servparm_array *servers, int includedepth, char **errstr); + +#endif /* CONF_PARSER_H */ diff --git a/service/src/main/jni/pdnsd/src/conff.c b/service/src/main/jni/pdnsd/src/conff.c new file mode 100644 index 0000000..8b8e143 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/conff.c @@ -0,0 +1,543 @@ +/* conff.c - Maintain configuration information + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipvers.h" +#include "conff.h" +#include "consts.h" +#include "helpers.h" +#include "conf-parser.h" +#include "servers.h" +#include "icmp.h" + + +globparm_t global={ + /* perm_cache: */ 2048, + /* cache_dir: */ NULL, + /* pidfile: */ NULL, + /* port: */ 53, + /* a: */ PDNSD_A_INITIALIZER, + /* out_a: */ PDNSD_A_INITIALIZER, +#ifdef ENABLE_IPV6 + /* ipv4_6_prefix: */ IN6ADDR_ANY_INIT, +#endif + /* max_ttl: */ 604800, + /* min_ttl: */ 120, + /* neg_ttl: */ 900, + /* neg_rrs_pol: */ C_DEFAULT, + /* neg_domain_pol: */ C_AUTH, + /* verbosity: */ VERBOSITY, + /* run_as: */ "", + /* daemon: */ 0, + /* debug: */ 0, + /* stat_pipe: */ 0, + /* notcp: */ 0, + /* strict_suid: */ 1, + /* use_nss: */ 1, + /* paranoid: */ 0, + /* lndown_kluge: */ 0, + /* onquery: */ 0, + /* rnd_recs: */ 1, + /* ctl_perms: */ 0600, + /* scheme_file: */ NULL, + /* proc_limit: */ 40, + /* procq_limit: */ 60, + /* tcp_qtimeout: */ TCP_TIMEOUT, + /* timeout: */ 0, + /* par_queries: */ PAR_QUERIES, + /* query_method: */ M_PRESET, + /* query_port_start: */ 1024, + /* query_port_end: */ 65535, + /* udpbufsize: */ 1024, + /* deleg_only_zones: */ NULL +}; + +servparm_t serv_presets={ + /* port: */ 53, + /* uptest: */ C_NONE, + /* timeout: */ 120, + /* interval: */ 900, + /* ping_timeout: */ 600, + /* scheme: */ "", + /* uptest_cmd: */ NULL, + /* uptest_usr: */ "", + /* interface: */ "", + /* device: */ "", + /* query_test_name: */ NULL, + /* label: */ NULL, + /* purge_cache: */ 0, + /* nocache: */ 0, + /* lean_query: */ 1, + /* edns_query: */ 0, + /* is_proxy: */ 0, + /* rootserver: */ 0, + /* rand_servers: */ 0, + /* preset: */ 1, + /* rejectrecursively: */0, + /* rejectpolicy: */ C_FAIL, + /* policy: */ C_INCLUDED, + /* alist: */ NULL, + /* atup_a: */ NULL, + /* reject_a4: */ NULL, +#if ALLOW_LOCAL_AAAA + /* reject_a6: */ NULL, +#endif + /* ping_a: */ PDNSD_A_INITIALIZER +}; + +servparm_array servers=NULL; + +static void free_zones(zone_array za); +static void free_server_data(servparm_array sa); +static int report_server_stat(int f,int i); + + +/* + * Read a configuration file, saving the results in a (separate) global section and servers array, + * and the cache. + * + * char *nm should contain the name of the file to read. If it is NULL, the name of the config file + * read during startup is used. + * + * globparm_t *global should point to a struct which will be used to store the data of the + * global section(s). If it is NULL, no global sections are allowed in the + * file. + * + * servparm_array *servers should point to a dynamic array which will be grown to store the data + * of the server sections. If it is NULL, no server sections are allowed + * in the file. + * + * char **errstr is used to return a possible error message. + * In case of failure, *errstr will refer to a newly allocated string. + * + * read_config_file returns 1 on success, 0 on failure. + */ +int read_config_file(const char *nm, globparm_t *global, servparm_array *servers, int includedepth, char **errstr) +{ + int retval=0; + const char *conftype= (global?"config":"include"); + FILE *in; + + if (nm==NULL) + nm=conf_file; + + if (!(in=fopen(nm,"r"))) { + if(asprintf(errstr,"Error: Could not open %s file %s: %s",conftype,nm,strerror(errno))<0) + *errstr=NULL; + return 0; + } + + retval=confparse(in,NULL,global,servers,includedepth,errstr); + if(fclose(in) && retval) { + if(asprintf(errstr,"Error: Could not close %s file %s: %s", + conftype,nm,strerror(errno))<0) + *errstr=NULL; + return 0; + } + if(retval && servers && !DA_NEL(*servers)) { + if(asprintf(errstr,"Error: no server sections defined in config file %s",nm)<0) + *errstr=NULL; + return 0; + } + return retval; +} + +/* + * Re-Read the configuration file. + * Return 1 on success, 0 on failure. + * In case of failure, the old configuration will be unchanged (although the cache may not) and + * **errstr will refer to a newly allocated string containing an error message. + */ +int reload_config_file(const char *nm, char **errstr) +{ + globparm_t global_new; + servparm_array servers_new; + + global_new=global; + global_new.cache_dir=NULL; + global_new.pidfile=NULL; + global_new.scheme_file=NULL; + global_new.deleg_only_zones=NULL; + global_new.onquery=0; + servers_new=NULL; + if(read_config_file(nm,&global_new,&servers_new,0,errstr)) { + if(global_new.cache_dir && strcmp(global_new.cache_dir,global.cache_dir)) { + *errstr=strdup("Cannot reload config file: the specified cache_dir directory has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.pidfile && (!global.pidfile || strcmp(global_new.pidfile,global.pidfile))) { + *errstr=strdup("Cannot reload config file: the specified pid_file has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.scheme_file && strcmp(global_new.scheme_file,global.scheme_file)) { + *errstr=strdup("Cannot reload config file: the specified scheme_file has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.port!=global.port) { + *errstr=strdup("Cannot reload config file: the specified server_port has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(!ADDR_EQUIV(&global_new.a,&global.a)) { + *errstr=strdup("Cannot reload config file: the specified interface address (server_ip) has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } +#ifdef ENABLE_IPV6 + if(!IN6_ARE_ADDR_EQUAL(&global_new.ipv4_6_prefix,&global.ipv4_6_prefix)) { + *errstr=strdup("Cannot reload config file: the specified ipv4_6_prefix has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } +#endif + if(strcmp(global_new.run_as,global.run_as)) { + *errstr=strdup("Cannot reload config file: the specified run_as id has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.daemon!=global.daemon) { + *errstr=strdup("Cannot reload config file: the daemon option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.debug!=global.debug) { + *errstr=strdup("Cannot reload config file: the debug option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.stat_pipe!=global.stat_pipe) { + *errstr=strdup("Cannot reload config file: the status_ctl option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.notcp!=global.notcp) { + *errstr=strdup("Cannot reload config file: the tcp_server option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.strict_suid!=global.strict_suid) { + *errstr=strdup("Cannot reload config file: the strict_setuid option has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(global_new.ctl_perms!=global.ctl_perms) { + *errstr=strdup("Cannot reload config file: the specified ctl_perms has changed.\n" + "Try restarting pdnsd instead."); + goto cleanup_return; + } + if(ping_isocket==-1 +#ifdef ENABLE_IPV6 + && ping6_isocket==-1 +#endif + ) { + int i,n=DA_NEL(servers_new); + for (i=0;idomain); +} + +void free_slist_array(slist_array sla) +{ + int j,m=DA_NEL(sla); + for(j=0;juptest_cmd); + free(serv->query_test_name); + free(serv->label); + da_free(serv->atup_a); + free_slist_array(serv->alist); + da_free(serv->reject_a4); +#if ALLOW_LOCAL_AAAA + da_free(serv->reject_a6); +#endif +} + +static void free_server_data(servparm_array sa) +{ + int i,n=DA_NEL(sa); + for(i=0;ireject_a4!=NULL || (s)->reject_a6!=NULL) +#else +#define serv_has_rejectlist(s) ((s)->reject_a4!=NULL) +#endif + + +/* Report the current status of server i to the file descriptor f. + Call with locks applied. +*/ +static int report_server_stat(int f,int i) +{ + servparm_t *st=&DA_INDEX(servers,i); + int j,m; + + fsprintf_or_return(f,"Server %i:\n------\n",i); + fsprintf_or_return(f,"\tlabel: %s\n",st->label?st->label:"(none)"); + m=DA_NEL(st->atup_a); + if(st->rootserver>1 && m) + fsprintf_or_return(f,"\tThe following name servers will be used for discovery of rootservers only:\n"); + for(j=0;jatup_a,j); + {char buf[ADDRSTR_MAXLEN]; + fsprintf_or_return(f,"\tip: %s\n",pdnsd_a2str(PDNSD_A2_TO_A(&at->a),buf,ADDRSTR_MAXLEN));} + fsprintf_or_return(f,"\tserver assumed available: %s\n",at->is_up?"yes":"no"); + } + fsprintf_or_return(f,"\tport: %hu\n",st->port); + fsprintf_or_return(f,"\tuptest: %s\n",const_name(st->uptest)); + fsprintf_or_return(f,"\ttimeout: %li\n",(long)st->timeout); + if(st->interval>0) { + fsprintf_or_return(f,"\tuptest interval: %li\n",(long)st->interval); + } else { + fsprintf_or_return(f,"\tuptest interval: %s\n", + st->interval==-1?"onquery": + st->interval==-2?"ontimeout": + "(never retest)"); + } + fsprintf_or_return(f,"\tping timeout: %li\n",(long)st->ping_timeout); + {char buf[ADDRSTR_MAXLEN]; + fsprintf_or_return(f,"\tping ip: %s\n",is_inaddr_any(&st->ping_a)?"(using server ip)":pdnsd_a2str(&st->ping_a,buf,ADDRSTR_MAXLEN));} + if(st->interface[0]) { + fsprintf_or_return(f,"\tinterface: %s\n",st->interface); + } + if(st->device[0]) { + fsprintf_or_return(f,"\tdevice (for special Linux ppp device support): %s\n",st->device); + } + if(st->uptest_cmd) { + fsprintf_or_return(f,"\tuptest command: %s\n",st->uptest_cmd); + fsprintf_or_return(f,"\tuptest user: %s\n",st->uptest_usr[0]?st->uptest_usr:"(process owner)"); + } + if(st->query_test_name) { + unsigned char nmbuf[DNSNAMEBUFSIZE]; + fsprintf_or_return(f,"\tname used in query uptest: %s\n", + rhn2str(st->query_test_name,nmbuf,sizeof(nmbuf))); + } + if (st->scheme[0]) { + fsprintf_or_return(f,"\tscheme: %s\n", st->scheme); + } + fsprintf_or_return(f,"\tforce cache purging: %s\n",st->purge_cache?"on":"off"); + fsprintf_or_return(f,"\tserver is cached: %s\n",st->nocache?"off":"on"); + fsprintf_or_return(f,"\tlean query: %s\n",st->lean_query?"on":"off"); + fsprintf_or_return(f,"\tUse EDNS in outgoing queries: %s\n",st->edns_query?"on":"off"); + fsprintf_or_return(f,"\tUse only proxy?: %s\n",st->is_proxy?"on":"off"); + fsprintf_or_return(f,"\tAssumed root server: %s\n",st->rootserver?(st->rootserver==1?"yes":"discover"):"no"); + fsprintf_or_return(f,"\tRandomize server query order: %s\n",st->rand_servers?"yes":"no"); + fsprintf_or_return(f,"\tDefault policy: %s\n",const_name(st->policy)); + fsprintf_or_return(f,"\tPolicies:%s\n", st->alist?"":" (none)"); + for (j=0;jalist);++j) { + slist_t *sl=&DA_INDEX(st->alist,j); + unsigned char buf[DNSNAMEBUFSIZE]; + fsprintf_or_return(f,"\t\t%s: %s%s\n", + sl->rule==C_INCLUDED?"include":"exclude", + sl->exact?"":".", + rhn2str(sl->domain,buf,sizeof(buf))); + } + if(serv_has_rejectlist(st)) { + fsprintf_or_return(f,"\tAddresses which should be rejected in replies:\n"); + m=DA_NEL(st->reject_a4); + for (j=0;jreject_a4,j); + char abuf[ADDRSTR_MAXLEN],mbuf[ADDRSTR_MAXLEN]; + fsprintf_or_return(f,"\t\t%s/%s\n",inet_ntop(AF_INET,&am->a,abuf,sizeof(abuf)), + inet_ntop(AF_INET,&am->mask,mbuf,sizeof(mbuf))); + } +#if ALLOW_LOCAL_AAAA + m=DA_NEL(st->reject_a6); + for (j=0;jreject_a6,j); + char abuf[INET6_ADDRSTRLEN],mbuf[INET6_ADDRSTRLEN]; + fsprintf_or_return(f,"\t\t%s/%s\n",inet_ntop(AF_INET6,&am->a,abuf,sizeof(abuf)), + inet_ntop(AF_INET6,&am->mask,mbuf,sizeof(mbuf))); + } +#endif + fsprintf_or_return(f,"\tReject policy: %s\n",const_name(st->rejectpolicy)); + fsprintf_or_return(f,"\tReject recursively: %s\n",st->rejectrecursively?"yes":"no"); + } + return 0; +} diff --git a/service/src/main/jni/pdnsd/src/conff.h b/service/src/main/jni/pdnsd/src/conff.h new file mode 100644 index 0000000..a07b156 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/conff.h @@ -0,0 +1,190 @@ +/* conff.h - Definitions for configuration management. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef CONFF_H +#define CONFF_H + +/* XXX should use the system defined ones. */ +/* #define MAXPATH 1024 */ +/* #define MAXIFNAME 31 */ + +#include +#include +#include +#include +#include +#include "ipvers.h" +#include "list.h" + +/* From main.c */ +#if DEBUG>0 +extern short int debug_p; +#else +#define debug_p 0 +#endif +extern short int stat_pipe; +extern pthread_t main_thrid; +extern uid_t init_uid; +extern char *conf_file; + +/* ----------- */ + +typedef DYNAMIC_ARRAY(pdnsd_a) *addr_array; +typedef DYNAMIC_ARRAY(pdnsd_a2) *addr2_array; + +typedef struct { + time_t i_ts; + char is_up; + pdnsd_a2 a; +} atup_t; +typedef DYNAMIC_ARRAY(atup_t) *atup_array; + +typedef struct { + unsigned char *domain; + short exact; + short rule; +} slist_t; +typedef DYNAMIC_ARRAY(slist_t) *slist_array; + +typedef struct { + struct in_addr a,mask; +} addr4maskpair_t; + +typedef DYNAMIC_ARRAY(addr4maskpair_t) *a4_array; + +#if ALLOW_LOCAL_AAAA +typedef struct { + struct in6_addr a,mask; +} addr6maskpair_t; + +typedef DYNAMIC_ARRAY(addr6maskpair_t) *a6_array; +#endif + +typedef struct { + unsigned short port; + short uptest; + time_t timeout; + time_t interval; + time_t ping_timeout; + char scheme[32]; + char *uptest_cmd; + char uptest_usr[21]; + char interface[IFNAMSIZ]; + char device[IFNAMSIZ]; + unsigned char *query_test_name; + char *label; + char purge_cache; + char nocache; + char lean_query; + char edns_query; + char is_proxy; + char rootserver; + char rand_servers; + char preset; + char rejectrecursively; + short rejectpolicy; + short policy; + slist_array alist; + atup_array atup_a; + a4_array reject_a4; +#if ALLOW_LOCAL_AAAA + a6_array reject_a6; +#endif + pdnsd_a ping_a; +} servparm_t; +typedef DYNAMIC_ARRAY(servparm_t) *servparm_array; + +typedef unsigned char *zone_t; +typedef DYNAMIC_ARRAY(zone_t) *zone_array; + +typedef struct { + long perm_cache; + char *cache_dir; + char *pidfile; + int port; + pdnsd_a a; + pdnsd_a out_a; +#ifdef ENABLE_IPV6 + struct in6_addr ipv4_6_prefix; +#endif + time_t max_ttl; + time_t min_ttl; + time_t neg_ttl; + short neg_rrs_pol; + short neg_domain_pol; + short verbosity; + char run_as[21]; + char daemon; + char debug; + char stat_pipe; + char notcp; + char strict_suid; + char use_nss; + char paranoid; + char lndown_kluge; + char onquery; + char rnd_recs; + int ctl_perms; + char *scheme_file; + int proc_limit; + int procq_limit; + time_t tcp_qtimeout; + time_t timeout; + int par_queries; + int query_method; + int query_port_start; + int query_port_end; + int udpbufsize; + zone_array deleg_only_zones; +} globparm_t; + +typedef struct { + char +#ifdef ENABLE_IPV6 + prefix, +#endif + pidfile, + verbosity, + pdnsduser, + daemon, + debug, + stat_pipe, + notcp, + query_method; +} cmdlineflags_t; + +extern globparm_t global; +extern cmdlineflags_t cmdline; +extern servparm_t serv_presets; + +extern servparm_array servers; + +int read_config_file(const char *nm, globparm_t *global, servparm_array *servers, int includedepth, char **errstr); +int reload_config_file(const char *nm, char **errstr); +void free_zone(void *ptr); +void free_slist_domain(void *ptr); +void free_slist_array(slist_array sla); +void free_servparm(servparm_t *serv); + +int report_conf_stat(int f); +#endif diff --git a/service/src/main/jni/pdnsd/src/consts.c b/service/src/main/jni/pdnsd/src/consts.c new file mode 100644 index 0000000..c875a09 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/consts.c @@ -0,0 +1,133 @@ +/* consts.c - Common config constants & handling + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2005, 2006, 2007, 2009 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include "consts.h" +#include "rr_types.h" + + +/* Order alphabetically!! */ +static const namevalue_t const_dic[]={ + {"auth", C_AUTH}, + {"default", C_DEFAULT}, + {"dev", C_DEV}, + {"diald", C_DIALD}, + {"discover", C_DISCOVER}, + {"domain", C_DOMAIN}, + {"excluded", C_EXCLUDED}, + {"exec", C_EXEC}, + {"fail", C_FAIL}, + {"false", C_OFF}, + {"fqdn_only", C_FQDN_ONLY}, + {"if", C_IF}, + {"included", C_INCLUDED}, + {"negate", C_NEGATE}, + {"no", C_OFF}, + {"none", C_NONE}, + {"off", C_OFF}, + {"on", C_ON}, + {"onquery", C_ONQUERY}, + {"ontimeout", C_ONTIMEOUT}, + {"ping", C_PING}, + {"query", C_QUERY}, + {"simple_only", C_SIMPLE_ONLY}, + {"tcp_only", TCP_ONLY}, + {"tcp_udp", TCP_UDP}, + {"true", C_ON}, + {"udp_only", UDP_ONLY}, + {"udp_tcp", UDP_TCP}, + {"yes", C_ON} +}; + +/* Added by Paul Rombouts */ +static const char *const const_names[]={ + "error", + "on", + "off", + "default", + "discover", + "none", + "if", + "exec", + "ping", + "query", + "onquery", + "ontimeout", + "udp_only", + "tcp_only", + "tcp_udp", + "udp_tcp", + "dev", + "diald", + "included", + "excluded", + "simple_only", + "fqdn_only", + "auth", + "domain", + "fail", + "negate" +}; + +/* compare two strings. + The first one is given as pointer to a char array of length len (which + should not contain any null chars), + the second one as a pointer to a null terminated char array. +*/ +inline static int keyncmp(const char *key1, int len, const char *key2) +{ + int cmp=strncmp(key1,key2,len); + if(cmp) return cmp; + return -(int)((unsigned char)(key2[len])); +} + +int binsearch_keyword(const char *name, int len, const namevalue_t dic[], int range) +{ + int i=0,j=range; + + while(i0) + i=k+1; + else + return dic[k].val; + } + + return 0; +} + + +int lookup_const(const char *name, int len) +{ + return binsearch_keyword(name,len,const_dic,sizeof(const_dic)/sizeof(namevalue_t)); +} + +/* Added by Paul Rombouts */ +const char *const_name(int c) +{ + return (c>=0 && c. +*/ + + +#ifndef CONSTS_H +#define CONSTS_H + +#include + +#define C_RRTOFFS 64 + +enum { + C_ERR, + C_ON, + C_OFF, + C_DEFAULT, + C_DISCOVER, + C_NONE, + C_IF, + C_EXEC, + C_PING, + C_QUERY, + C_ONQUERY, + C_ONTIMEOUT, + UDP_ONLY, + TCP_ONLY, + TCP_UDP, + UDP_TCP, + C_DEV, + C_DIALD, + C_INCLUDED, + C_EXCLUDED, + C_SIMPLE_ONLY, + C_FQDN_ONLY, + C_AUTH, + C_DOMAIN, + C_FAIL, + C_NEGATE +}; + +typedef struct { + const char *name; + int val; +} namevalue_t; + +int binsearch_keyword(const char *name, int len, const namevalue_t dic[], int range); +int lookup_const(const char *name, int len); +const char *const_name(int c); /* Added by Paul Rombouts */ + +#endif diff --git a/service/src/main/jni/pdnsd/src/debug.c b/service/src/main/jni/pdnsd/src/debug.c new file mode 100644 index 0000000..dd7f244 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/debug.c @@ -0,0 +1,64 @@ +/* debug.c - Various debugging facilities + * Copyright (C) 2001 Thomas Moestl + * + * This file is part of the pdnsd package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include "helpers.h" +#include "error.h" + + +/* + * This is indeed very primitive (it does not track allocation failures + * and so on). It should be expanded some time. + */ +#ifdef ALLOC_DEBUG +void *DBGcalloc(size_t n, size_t sz, char *file, int line) +{ + DEBUG_MSG("+ calloc, %s:%d\n", file, line); + return calloc(n, sz); +} + +void *DBGmalloc(size_t sz, char *file, int line) +{ + DEBUG_MSG("+ malloc, %s:%d\n", file, line); + return malloc(sz); +} + +void *DBGrealloc(void *ptr, size_t sz, char *file, int line) +{ + if (ptr == NULL && sz != 0) + DEBUG_MSG("+ realloc, %s:%d\n", file, line); + if (ptr != NULL && sz == 0) + DEBUG_MSG("- realloc(0), %s:%d\n", file, line); + return realloc(ptr, sz); +} +void DBGfree(void *ptr, char *file, int line) +{ + DEBUG_MSG("- free, %s:%d\n", file, line); + free(ptr); +} +#endif diff --git a/service/src/main/jni/pdnsd/src/debug.h b/service/src/main/jni/pdnsd/src/debug.h new file mode 100644 index 0000000..02f0e5d --- /dev/null +++ b/service/src/main/jni/pdnsd/src/debug.h @@ -0,0 +1,52 @@ +/* debug.h - Various debugging facilities + * Copyright (C) 2001 Thomas Moestl + * + * This file is part of the pdnsd package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef DEBUG_H +#define DEBUG_H + +/* + * A hand-rolled alloc debug factility, because most available libraries have + * problems with at least one thread implementation. + */ +#ifdef ALLOC_DEBUG +void *DBGcalloc(size_t n, size_t sz, char *file, int line); +void *DBGmalloc(size_t sz, char *file, int line); +void *DBGrealloc(void *ptr, size_t sz, char *file, int line); +void DBGfree(void *ptr, char *file, int line); + +#define pdnsd_calloc(n,sz) DBGcalloc(n,sz,__FILE__,__LINE__) +#define pdnsd_malloc(sz) DBGmalloc(sz,__FILE__,__LINE__) +#define pdnsd_realloc(ptr,sz) DBGrealloc(ptr,sz,__FILE__,__LINE__) +#define pdnsd_free(ptr) DBGfree(ptr,__FILE__,__LINE__) +#else +#define pdnsd_calloc calloc +#define pdnsd_malloc malloc +#define pdnsd_realloc realloc +#define pdnsd_free free +#endif + +#endif /* def DEBUG_H */ diff --git a/service/src/main/jni/pdnsd/src/dns.c b/service/src/main/jni/pdnsd/src/dns.c new file mode 100644 index 0000000..a02d7fd --- /dev/null +++ b/service/src/main/jni/pdnsd/src/dns.c @@ -0,0 +1,617 @@ +/* dns.c - Declarations for dns handling and generic dns functions + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include "error.h" +#include "helpers.h" +#include "dns.h" + + +/* Decompress a name record, taking the whole message as msg, returning its results in tgt + * (which should be able hold at least DNSNAMEBUFSIZE chars), + * taking sz as the remaining msg size (it is returned decremented by the name length, ready for further use) and + * a source pointer (it is returned pointing to the location after the name). msgsize is the size of the whole message, + * len is the total name length. + * msg and msgsz are needed for decompression (see rfc1035). The returned data is decompressed, but still in the + * rr name form (length byte - string of that length, terminated by a 0 length byte). + * + * Returned is a dns return code, with one exception: RC_TRUNC, as defined in dns.h, indicates that the message is + * truncated at the name (which needs a special return code, as it might or might not be fatal). + */ +int decompress_name(unsigned char *msg, size_t msgsz, unsigned char **src, size_t *sz, unsigned char *tgt, unsigned int *len) +{ + unsigned int lb,offs; + unsigned int hops=0,tpos=0; + unsigned char *lptr=*src; + size_t oldsz=*sz; + size_t newsz=oldsz; + + if (newsz==0) + goto name_outside_data; + if (lptr-msg>=msgsz) + goto name_outside_msg; + + for(;;) { + newsz--; + lb=*lptr++; + + if(lb>0x3f) { + if (lb<0xc0) /* The two highest bits must be either 00 or 11 */ + goto unsupported_lbl_bits; + if (newsz==0) + goto name_outside_data; + if (lptr-msg>=msgsz) + goto name_outside_msg; + newsz--; + offs=((lb&0x3f)<<8)|(*lptr); + if (offs>=msgsz) + goto offset_outside_msg; + lptr=msg+offs; + goto jumped; + } + tgt[tpos++]=lb; + if (lb==0) + break; + + if (newsz<=lb) + goto name_outside_data; + if (lptr+lb-msg>=msgsz) + goto name_outside_msg; + if (tpos+lb>DNSNAMEBUFSIZE-1) /* terminating null byte has to follow */ + goto name_buf_full; + newsz -= lb; + do { + /* if (!*lptr || *lptr=='.') + return RC_FORMAT; */ + tgt[tpos++]=*lptr++; + } while(--lb); + } + goto return_OK; + + jumped: + ++hops; + for(;;) { + lb=*lptr++; + + while(lb>0x3f) { + if (lb<0xc0) /* The two highest bits must be either 00 or 11 */ + goto unsupported_lbl_bits; + if (lptr-msg>=msgsz) + goto name_outside_msg; + if (++hops>255) + goto too_many_hops; + offs=((lb&0x3f)<<8)|(*lptr); + if (offs>=msgsz) + goto offset_outside_msg; + lptr=msg+offs; + lb=*lptr++; + } + tgt[tpos++]=lb; + if (lb==0) + break; + + if (lptr+lb-msg>=msgsz) + goto name_outside_msg; + if(tpos+lb>DNSNAMEBUFSIZE-1) /* terminating null byte has to follow */ + goto name_buf_full; + do { + /* if (!*lptr || *lptr=='.') + return RC_FORMAT; */ + tgt[tpos++]=*lptr++; + } while(--lb); + } + return_OK: + *src += oldsz-newsz; + *sz = newsz; + if(len) *len=tpos; + return RC_OK; + + name_outside_data: + DEBUG_MSG("decompress_name: compressed name extends outside data field.\n"); + return RC_TRUNC; + + name_outside_msg: + DEBUG_MSG("decompress_name: compressed name extends outside message.\n"); + return RC_FORMAT; + + unsupported_lbl_bits: + DEBUG_MSG(lb==0x41?"decompress_name: Bit-string labels not supported.\n": + "decompress_name: unsupported label type.\n"); + return RC_FORMAT; + + offset_outside_msg: + DEBUG_MSG("decompress_name: offset points outside message.\n"); + return RC_FORMAT; + + name_buf_full: + DEBUG_MSG("decompress_name: decompressed name larger than %u bytes.\n", DNSNAMEBUFSIZE); + return RC_FORMAT; + + too_many_hops: + DEBUG_MSG("decompress_name: too many offsets in compressed name.\n"); + return RC_FORMAT; +} + +#if 0 +/* Compare two names (ordinary C-strings) back-to-forth and return the longest match. + The comparison is done at name granularity. + The return value is the length of the match in name elements. + *os (*od) is set to the offset in the domain name ms (md) of the match. + */ +int domain_name_match(const unsigned char *ms, const unsigned char *md, int *os, int *od) +{ + int i,j,k=0,offs,offd; + + offs=i=strlen(ms); offd=j=strlen(md); + if(i && ms[i-1]=='.') --offs; + if(j && md[j-1]=='.') --offd; + + if(i==0 || (i==1 && *ms=='.') || j==0 || (j==1 && *md=='.')) + /* Special case: root domain */ + ; + else { + --i; if(ms[i]=='.') --i; + --j; if(md[j]=='.') --j; + while(tolower(ms[i]) == tolower(md[j])) { + if(ms[i]=='.') { + ++k; + offs=i+1; offd=j+1; + } + if(i==0 || j==0) { + if((i==0 || ms[i-1]=='.') && (j==0 || md[j-1]=='.')) { + ++k; + offs=i; offd=j; + } + break; + } + --i; --j; + } + } + if(os) *os=offs; + if(od) *od=offd; + return k; +} +#endif + +/* Compare the names (in length byte-string notation) back-to-forth and return the longest match. + The comparison is done at name granularity. + The return value is the length of the match in name elements. + *os (*od) is set to the offset in the domain name ms (md) of the match. + */ +unsigned int domain_match(const unsigned char *ms, const unsigned char *md, unsigned int *os, unsigned int *od) +{ + unsigned int i,j,k,n,ns=0,nd=0,offs,offd; + unsigned char lb,ls[128],ld[128]; + + /* first collect all length bytes */ + i=0; + while((lb=ms[i])) { + PDNSD_ASSERT(ns<128, "domain_match: too many name segments"); + ls[ns++]=lb; + i += ((unsigned)lb)+1; + } + + j=0; + while((lb=md[j])) { + PDNSD_ASSERT(nd<128, "domain_match: too many name segments"); + ld[nd++]=lb; + j += ((unsigned)lb)+1; + } + + n=ns; if(n>nd) n=nd; + + for(k=1; offs=i,offd=j,k<=n; ++k) { + lb=ls[ns-k]; + if(lb!=ld[nd-k]) goto mismatch; + for(;lb;--lb) + if(tolower(ms[--i]) != tolower(md[--j])) goto mismatch; + --i; --j; + } + mismatch: + + if(os) *os=offs; + if(od) *od=offd; + return k-1; +} + +/* compress the domain name in in and put the result (of maximum length of rhnlen(in)) and + * fill cb with compression information for further strings.*cb may be NULL initially. + * offs is the offset the generated string will be placed in the packet. + * retval: 0 - error, otherwise length + * When done, just free() cb (if it is NULL, free will behave correctly). + * It is guaranteed (and insured by assertions) that the output is smaller or equal in + * size to the input. + */ +unsigned int compress_name(unsigned char *in, unsigned char *out, unsigned int offs, dlist *cb) +{ + compel_t *ci; + unsigned int longest=0,lrem=0,coffs=0; + unsigned int rl=0; + unsigned ilen = rhnlen(in); + unsigned short add=1; + + PDNSD_ASSERT(ilen<=DNSNAMEBUFSIZE, "compress_name: name too long"); + + /* part 1: compression */ + for (ci=dlist_first(*cb); ci; ci=dlist_next(ci)) { + unsigned int rv,rem,to; + if ((rv=domain_match(in, ci->s, &rem,&to))>longest) { + /* + * This has some not obvious implications that should be noted: If a + * domain name as saved in the list has been compressed, we only can + * index the non-compressed part. We rely here that the first occurence + * can't be compressed. So we take the first occurence of a given length. + * This works perfectly, but watch it if you change something. + */ + unsigned int newoffs= ci->index + to; + /* Only use if the offset is not too large. */ + if(newoffs<=0x3fff) { + longest=rv; + lrem=rem; + coffs= newoffs; + } + } + } + if (longest>0) { + PDNSD_ASSERT(lrem+2 <= ilen, "compress_name: length increased"); + memcpy(out, in,lrem); + out[lrem]=0xc0|((coffs&0x3f00)>>8); + out[lrem+1]=coffs&0xff; + rl=lrem+2; + add= lrem!=0; + } + else { + memcpy(out,in,ilen); + rl=ilen; + } + + /* part 2: addition to the cache structure */ + if (add) { + if (!(*cb=dlist_grow(*cb,sizeof(compel_t)+ilen))) + return 0; + ci=dlist_last(*cb); + ci->index=offs; + memcpy(ci->s,in,ilen); + } + return rl; +} + +/* Convert a numeric IP address into a domain name representation + (C string) suitable for PTR records. + buf is assumed to be at least DNSNAMEBUFSIZE bytes in size. +*/ +int a2ptrstr(pdnsd_ca *a, int tp, unsigned char *buf) +{ + if(tp==T_A) { + unsigned char *p=(unsigned char *)&a->ipv4.s_addr; + int n=snprintf(charp buf,DNSNAMEBUFSIZE,"%u.%u.%u.%u.in-addr.arpa.",p[3],p[2],p[1],p[0]); + if(n<0 || n>=DNSNAMEBUFSIZE) + return 0; + } + else +#if ALLOW_LOCAL_AAAA + if(tp==T_AAAA) { + unsigned char *p=(unsigned char *)&a->ipv6; + int i,offs=0; + for (i=15;i>=0;--i) { + unsigned char bt=p[i]; + int n=snprintf(charp(buf+offs), DNSNAMEBUFSIZE-offs,"%x.%x.",bt&0xf,(bt>>4)&0xf); + if(n<0) return 0; + offs+=n; + if(offs>=DNSNAMEBUFSIZE) return 0; + } + if(!strncp(charp(buf+offs),"ip6.arpa.",DNSNAMEBUFSIZE-offs)) + return 0; + } + else +#endif + return 0; + return 1; +} + +/* + * Add records for a host as read from a hosts-style file. + * Returns 1 on success, 0 in an out of memory condition, and -1 when there was a problem with + * the record data. + */ +static int add_host(unsigned char *pn, unsigned char *rns, pdnsd_ca *a, int tp, int a_sz, time_t ttl, unsigned flags, int reverse) +{ + dns_cent_t ce; + + if (!init_cent(&ce, pn, 0, 0, flags DBG0)) + return 0; + if (!add_cent_rr(&ce,tp,ttl,0,CF_LOCAL,a_sz,a DBG0)) + goto free_cent_return0; + if (!add_cent_rr(&ce,T_NS,ttl,0,CF_LOCAL,rhnlen(rns),rns DBG0)) + goto free_cent_return0; + add_cache(&ce); + free_cent(&ce DBG0); + if (reverse) { + unsigned char b2[DNSNAMEBUFSIZE],rhn[DNSNAMEBUFSIZE]; + if(!a2ptrstr(a,tp,b2)) + return -1; + if (!str2rhn(b2,rhn)) + return -1; + if (!init_cent(&ce, rhn, 0, 0, flags DBG0)) + return 0; + if (!add_cent_rr(&ce,T_PTR,ttl,0,CF_LOCAL,rhnlen(pn),pn DBG0)) + goto free_cent_return0; + if (!add_cent_rr(&ce,T_NS,ttl,0,CF_LOCAL,rhnlen(rns),rns DBG0)) + goto free_cent_return0; + add_cache(&ce); + free_cent(&ce DBG0); + } + return 1; + + free_cent_return0: + free_cent(&ce DBG0); + return 0; +} + +/* + * Read a file in /etc/hosts-format and add generate rrs for it. + * Errors are largely ignored so that we can skip entries we do not understand + * (but others possibly do). + */ +int read_hosts(const char *fn, unsigned char *rns, time_t ttl, unsigned flags, int aliases, char **errstr) +{ + int rv=0; + FILE *f; + char *buf; + size_t buflen=256; + + if (!(f=fopen(fn,"r"))) { + if(asprintf(errstr, "Failed to source %s: %s", fn, strerror(errno))<0) *errstr=NULL; + return 0; + } + buf=malloc(buflen); + if(!buf) { + *errstr=NULL; + goto fclose_return; + } + while(getline(&buf,&buflen,f)>=0) { + unsigned int len; + unsigned char *p,*pn,*pi; + unsigned char rhn[DNSNAMEBUFSIZE]; + int tp,sz; + pdnsd_ca a; + + p= ucharp strchr(buf,'#'); + if(p) *p=0; + p= ucharp buf; + for(;;) { + if(!*p) goto nextline; + if(!isspace(*p)) break; + ++p; + } + pi=p; + do { + if(!*++p) goto nextline; + } while(!isspace(*p)); + *p=0; + do { + if(!*++p) goto nextline; + } while (isspace(*p)); + pn=p; + do { + ++p; + } while(*p && !isspace(*p)); + len=p-pn; + if (parsestr2rhn(pn,len,rhn)!=NULL) + continue; + if (inet_aton(charp pi,&a.ipv4)) { + tp=T_A; + sz=sizeof(struct in_addr); + } else { +#if ALLOW_LOCAL_AAAA /* We don't read them otherwise, as the C library may not be able to to that.*/ + if (inet_pton(AF_INET6,charp pi,&a.ipv6)>0) { + tp=T_AAAA; + sz=sizeof(struct in6_addr); + } else +#endif + continue; + } + { + int res=add_host(rhn, rns, &a, tp,sz, ttl, flags, 1); + if(res==0) { + *errstr= NULL; + goto cleanup_return; + } + else if(res<0) + continue; + } + if(aliases) { + for(;;) { + for(;;) { + if(!*p) goto nextline; + if(!isspace(*p)) break; + ++p; + } + pn=p; + do { + ++p; + } while(*p && !isspace(*p)); + len=p-pn; + if (parsestr2rhn(pn,len,rhn)!=NULL) + break; + if (add_host(rhn, rns, &a, tp,sz, ttl, flags, 0) == 0) { + *errstr= NULL; + goto cleanup_return; + } + } + } + nextline:; + } + if (feof(f)) + rv=1; + else if(asprintf(errstr, "Failed to source %s: %s", fn, strerror(errno))<0) *errstr=NULL; + cleanup_return: + free(buf); + fclose_return: + fclose(f); + return rv; +} + + +/* Get the name of an RR type given its value. */ +const char *getrrtpname(int tp) +{ + return tp>=T_MIN && tp<=T_MAX? rrnames[tp-T_MIN]: "[unknown]"; +} + +#if DEBUG>0 +/* + * Const decoders for debugging display + */ +static const char *const c_names[C_NUM] = {"IN","CS","CH","HS"}; +static const char *const qt_names[QT_NUM]={"IXFR","AXFR","MAILB","MAILA","*"}; + +const char *get_cname(int id) +{ + if (id>=C_MIN && id<=C_MAX) + return c_names[id-C_MIN]; + if (id==QC_ALL) + return "*"; + return "[unknown]"; +} + +const char *get_tname(int id) +{ + if (id>=T_MIN && id<=T_MAX) + return rrnames[id-T_MIN]; + else if (id>=QT_MIN && id<=QT_MAX) + return qt_names[id-QT_MIN]; + return "[unknown]"; +} + + +#define NRC 17 +static const char *const e_names[NRC]={ + "no error", + "query format error", + "server failed", + "non-existent domain", + "not supported", + "query refused", + "name exists when it should not", + "RR set exists when it should not", + "RR set that should exist does not", + "server not authoritative for zone", + "name not contained in zone", + "11", + "12", + "13", + "14", + "15", + "bad OPT version" +}; + +const char *get_ename(int id) +{ + if (id>=0 && idaa) + p=mempcpy(p, " AA", 3); + if (hdr->tc) + p=mempcpy(p, " TC", 3); + if (hdr->rd) + p=mempcpy(p, " RD", 3); + if (hdr->ra) + p=mempcpy(p, " RA", 3); + if (hdr->z) + p=mempcpy(p, " Z", 2); + if (hdr->ad) + p=mempcpy(p, " AD", 3); + if (hdr->cd) + p=mempcpy(p, " CD", 3); + *p=0; + + return buf; +} + +#endif + + +#if DEBUG>=9 +/* Based on debug code contributed by Kiyo Kelvin Lee. */ + +void debug_dump_dns_msg(void *data, size_t len) +{ + unsigned char *udata = (unsigned char *)data; +# define dmpchksz 16 + char buf[dmpchksz*4+2]; + size_t i, j, k, l; + + DEBUG_MSG("pointer=%p len=%lu\n", udata, (unsigned long)len); + + for (i = 0; i < len; i += dmpchksz) { + char *cp = buf; + k = l = i + dmpchksz; + if(k > len) k = len; + for (j = i; j < k; ++j) { + int n = sprintf(cp, "%02x ", udata[j]); + cp += n; + } + for (; j < l; ++j) { + *cp++ = ' '; + *cp++ = ' '; + *cp++ = ' '; + } + *cp++ = ' '; + for (j = i; j < k; ++j) { + *cp++ = isprint(udata[j]) ? udata[j] : '.'; + } + PDNSD_ASSERT(cp < buf + sizeof(buf), "debug_dump_dns_msg: line buffer overflowed"); + *cp = '\0'; + DEBUG_MSG("%s\n", buf); + } + + if(len >= sizeof(dns_hdr_t)) { + dns_hdr_t *hdr = (dns_hdr_t *)data; + + DEBUG_MSG( + "id=%04x qr=%x opcode=%x aa=%x tc=%x rd=%x " + "ra=%x z=%x ad=%x cd=%x rcode=%x\n", + ntohs(hdr->id), hdr->qr, hdr->opcode, hdr->aa, hdr->tc, hdr->rd, + hdr->ra, hdr->z, hdr->ad, hdr->cd, hdr->rcode); + DEBUG_MSG( + "qdcount=%04x ancount=%04x nscount=%04x arcount=%04x\n", + ntohs(hdr->qdcount), ntohs(hdr->ancount), ntohs(hdr->nscount), ntohs(hdr->arcount)); + } +} +#endif diff --git a/service/src/main/jni/pdnsd/src/dns.h b/service/src/main/jni/pdnsd/src/dns.h new file mode 100644 index 0000000..0f6a4ac --- /dev/null +++ b/service/src/main/jni/pdnsd/src/dns.h @@ -0,0 +1,309 @@ +/* dns.h - Declarations for dns handling and generic dns functions + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef DNS_H +#define DNS_H + +#include +#include +#include +#include +#include +#include +#include "rr_types.h" +#include "list.h" +#include "ipvers.h" + +#if (TARGET==TARGET_BSD) +# if !defined(__BIG_ENDIAN) +# if defined(BIG_ENDIAN) +# define __BIG_ENDIAN BIG_ENDIAN +# elif defined(_BIG_ENDIAN) +# define __BIG_ENDIAN _BIG_ENDIAN +# endif +# endif +# if !defined(__LITTLE_ENDIAN) +# if defined(LITTLE_ENDIAN) +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# elif defined(_LITTLE_ENDIAN) +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# endif +# endif +# if !defined(__BYTE_ORDER) +# if defined(BYTE_ORDER) +# define __BYTE_ORDER BYTE_ORDER +# elif defined(_BYTE_ORDER) +# define __BYTE_ORDER _BYTE_ORDER +# endif +# endif +#endif + +/* Deal with byte orders */ +#ifndef __BYTE_ORDER +# if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) +# error Fuzzy endianness system! Both __LITTLE_ENDIAN and __BIG_ENDIAN have been defined! +# endif +# if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) +# error Strange Endianness-less system! Neither __LITTLE_ENDIAN nor __BIG_ENDIAN has been defined! +# endif +# if defined(__LITTLE_ENDIAN) +# define __BYTE_ORDER __LITTLE_ENDIAN +# elif defined(__BIG_ENDIAN) +# define __BYTE_ORDER __BIG_ENDIAN +# endif +#endif + +/* special rr type codes for queries */ +#define QT_MIN 251 +#define QT_IXFR 251 +#define QT_AXFR 252 +#define QT_MAILB 253 +#define QT_MAILA 254 +#define QT_ALL 255 +#define QT_MAX 255 +#define QT_NUM 5 + +/* rr classes */ +#define C_MIN 1 +#define C_IN 1 +#define C_CS 2 +#define C_CH 3 +#define C_HS 4 +#define C_MAX 4 +#define C_NUM 4 + +/* special classes for queries */ +#define QC_ALL 255 + +/* status codes */ +#define RC_OK 0 +#define RC_FORMAT 1 +#define RC_SERVFAIL 2 +#define RC_NAMEERR 3 +#define RC_NOTSUPP 4 +#define RC_REFUSED 5 +#define RC_BADVERS 16 + +/* + * special internal retvals + */ +#define RC_NOTCACHED 0xfffa +#define RC_CACHED 0xfffb +#define RC_STALE 0xfffc +#define RC_TCPREFUSED 0xfffd +#define RC_TRUNC 0xfffe +#define RC_FATALERR 0xffff + +/* query/response */ +#define QR_QUERY 0 +#define QR_RESP 1 + +/*opcodes */ +#define OP_QUERY 0 +#define OP_IQUERY 1 +#define OP_STATUS 2 + +#if 0 +typedef struct { + /* the name is the first field. It has variable length, so it can't be put in the struct */ + uint16_t type; + uint16_t class; + uint32_t ttl; + uint16_t rdlength; + /* rdata follows */ +} __attribute__((packed)) rr_hdr_t; + +#define sizeof_rr_hdr_t (sizeof rr_hdr_t) +#else + +/* We will not actually use the rr_hdr_t type, only its size: + sizeof(rr_hdr_t) = 2 + 2 + 4 + 2 */ +#define sizeof_rr_hdr_t 10 +#endif + +#define sizeof_opt_pseudo_rr (1+sizeof_rr_hdr_t) + +#if 0 +typedef struct { + /* The server name and maintainer mailbox are the first two fields. It has variable length, */ + /* so they can't be put in the struct */ + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; +} __attribute__((packed)) soa_r_t; + + +typedef struct { +/* char qname[];*/ + uint16_t qtype; + uint16_t qclass; +} __attribute__((packed)) std_query_t; +#endif + + +typedef struct { + uint16_t id; +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int rd:1; + unsigned int tc:1; + unsigned int aa:1; + unsigned int opcode:4; + unsigned int qr:1; + unsigned int rcode:4; + unsigned int cd:1; + unsigned int ad:1; + unsigned int z :1; + unsigned int ra:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned int qr:1; + unsigned int opcode:4; + unsigned int aa:1; + unsigned int tc:1; + unsigned int rd:1; + unsigned int ra:1; + unsigned int z :1; + unsigned int ad:1; + unsigned int cd:1; + unsigned int rcode:4; +#else +# error "Please define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN" +#endif + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} __attribute__((packed)) dns_hdr_t; + + +/* A structure that can also be used for DNS messages over TCP. */ +typedef struct { +#ifndef NO_TCP_QUERIES + uint16_t len; +#endif + dns_hdr_t hdr; +} __attribute__((packed)) dns_msg_t; + +#ifdef NO_TCP_QUERIES +# define dnsmsghdroffset 0 +#else +# define dnsmsghdroffset 2 +#endif + + +/* Structure for storing EDNS (Extension mechanisms for DNS) information. */ +typedef struct { + unsigned short udpsize; + unsigned short rcode; + unsigned short version; + unsigned char do_flg; +} edns_info_t; + + +/* Macros to retrieve or store integer data that is not necessarily aligned. + Also takes care of network to host byte order. + The pointer cp is advanced and should be of type void* or char*. + These are actually adapted versions of the NS_GET16 and NS_GET32 + macros in the arpa/nameser.h include file in the BIND 9 source. +*/ + +#define GETINT16(s,cp) do { \ + register uint16_t t_s; \ + register const unsigned char *t_cp = (const unsigned char *)(cp); \ + t_s = (uint16_t)*t_cp++ << 8; \ + t_s |= (uint16_t)*t_cp++; \ + (s) = t_s; \ + (cp) = (void *)t_cp; \ +} while (0) + +#define GETINT32(l,cp) do { \ + register uint32_t t_l; \ + register const unsigned char *t_cp = (const unsigned char *)(cp); \ + t_l = (uint32_t)*t_cp++ << 24; \ + t_l |= (uint32_t)*t_cp++ << 16; \ + t_l |= (uint32_t)*t_cp++ << 8; \ + t_l |= (uint32_t)*t_cp++; \ + (l) = t_l; \ + (cp) = (void *)t_cp; \ +} while (0) + +#define PUTINT16(s,cp) do { \ + register uint16_t t_s = (uint16_t)(s); \ + register unsigned char *t_cp = (unsigned char *)(cp); \ + *t_cp++ = t_s >> 8; \ + *t_cp++ = t_s; \ + (cp) = (void *)t_cp; \ +} while (0) + +#define PUTINT32(l,cp) do { \ + register uint32_t t_l = (uint32_t)(l); \ + register unsigned char *t_cp = (unsigned char *)(cp); \ + *t_cp++ = t_l >> 24; \ + *t_cp++ = t_l >> 16; \ + *t_cp++ = t_l >> 8; \ + *t_cp++ = t_l; \ + (cp) = (void *)t_cp; \ +} while (0) + + +/* Size (number of bytes) of buffers used to hold domain names. */ +#define DNSNAMEBUFSIZE 256 + +/* Recursion depth. */ +#define MAX_HOPS 20 + +/* + * Types for compression buffers. + */ +typedef struct { + unsigned int index; + unsigned char s[0]; +} compel_t; + + + +int decompress_name(unsigned char *msg, size_t msgsz, unsigned char **src, size_t *sz, unsigned char *tgt, unsigned int *len); +/* int domain_name_match(const unsigned char *ms, const unsigned char *md, int *os, int *od); */ +unsigned int domain_match(const unsigned char *ms, const unsigned char *md, unsigned int *os, unsigned int *od); +unsigned int compress_name(unsigned char *in, unsigned char *out, unsigned int offs, dlist *cb); +int a2ptrstr(pdnsd_ca *a, int tp, unsigned char *buf); +int read_hosts(const char *fn, unsigned char *rns, time_t ttl, unsigned flags, int aliases, char **errstr); + +const char *getrrtpname(int tp); +#if DEBUG>0 +const char *get_cname(int id); +const char *get_tname(int id); +const char *get_ename(int id); +#define DNSFLAGSMAXSTRSIZE (7*3+1) +char *dnsflags2str(dns_hdr_t *hdr, char *buf); +#endif + +#if DEBUG>=9 +void debug_dump_dns_msg(void *data, size_t len); +#define DEBUG_DUMP_DNS_MSG(d,l) {if(debug_p && global.verbosity>=9) debug_dump_dns_msg(d,l);} +#else +#define DEBUG_DUMP_DNS_MSG(d,l) +#endif + +#endif diff --git a/service/src/main/jni/pdnsd/src/dns_answer.c b/service/src/main/jni/pdnsd/src/dns_answer.c new file mode 100644 index 0000000..6a2a5b5 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/dns_answer.c @@ -0,0 +1,2170 @@ +/* dns_answer.c - Receive and process incoming dns queries. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +/* + * STANDARD CONFORMITY + * + * There are several standard conformity issues noted in the comments. + * Some additional comments: + * + * I always set RA but I ignore RD largely (in everything but CNAME recursion), + * not because it is not supported, but because I _always_ do a recursive + * resolve in order to be able to cache the results. + */ + +#include +#include "ipvers.h" +#include +#include +#include +#ifdef HAVE_SYS_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread.h" +#include "list.h" +#include "dns.h" +#include "dns_answer.h" +#include "dns_query.h" +#include "helpers.h" +#include "cache.h" +#include "error.h" +#include "debug.h" + + +/* + * This is for error handling to prevent spewing the log files. + * Maximums of different message types are set. + * Races do not really matter here, so no locks. + */ +#define TCP_MAX_ERRS 10 +#define UDP_MAX_ERRS 10 +#define MEM_MAX_ERRS 10 +#define THRD_MAX_ERRS 10 +#define MISC_MAX_ERRS 10 +static volatile unsigned long da_tcp_errs=0; +static volatile unsigned long da_udp_errs=0; +static volatile unsigned long da_mem_errs=0; +static volatile unsigned long da_thrd_errs=0; +#if DEBUG>0 +static volatile unsigned long da_misc_errs=0; +#endif +static volatile int procs=0; /* active query processes */ +static volatile int qprocs=0; /* queued query processes */ +static volatile unsigned long dropped=0,spawned=0; +static volatile unsigned thrid_cnt=0; +static pthread_mutex_t proc_lock = PTHREAD_MUTEX_INITIALIZER; + +#ifdef SOCKET_LOCKING +static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +typedef union { +#ifdef ENABLE_IPV4 +# if (TARGET==TARGET_LINUX) + struct in_pktinfo pi4; +# else + struct in_addr ai4; +# endif +#endif +#ifdef ENABLE_IPV6 + struct in6_pktinfo pi6; +#endif +} pkt_info_t; + + +typedef struct { + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } addr; + + pkt_info_t pi; + + int sock; + int proto; + size_t len; + unsigned char buf[0]; /* Actual size determined by global.udpbufsize */ +} udp_buf_t; + + +/* ALLOCINITIALSIZE should be at least sizeof(dns_msg_t) = 2+12 */ +#define ALLOCINITIALSIZE 256 +/* This mask corresponds to a chunk size of 128 bytes. */ +#define ALLOCCHUNKSIZEMASK ((size_t)0x7f) + +typedef struct { + unsigned short qtype; + unsigned short qclass; + unsigned char query[0]; +} dns_queryel_t; + + +#define S_ANSWER 1 +#define S_AUTHORITY 2 +#define S_ADDITIONAL 3 + +typedef struct { + unsigned short tp,dlen; + unsigned char nm[0]; + /* unsigned char data[0]; */ +} sva_t; + + +/* + * Mark an additional record as added to avoid double records. + */ +static int sva_add(dlist *sva, const unsigned char *rhn, unsigned short tp, unsigned short dlen, void* data) +{ + if (sva) { + size_t rlen=rhnlen(rhn); + sva_t *st; + if (!(*sva=dlist_grow(*sva,sizeof(sva_t)+rlen+dlen))) { + return 0; + } + st=dlist_last(*sva); + st->tp=tp; + st->dlen=dlen; + memcpy(mempcpy(st->nm,rhn,rlen),data,dlen); + } + return 1; +} + +/* ans_ttl computes the ttl value to return to the client. + This is the ttl value stored in the cache entry minus the time + the cache entry has lived in the cache. + Local cache entries are an exception, they never "age". +*/ +inline static time_t ans_ttl(rr_set_t *rrset, time_t queryts) +{ + time_t ttl= rrset->ttl; + + if (!(rrset->flags&CF_LOCAL)) { + time_t tpassed= queryts - rrset->ts; + if(tpassed<0) tpassed=0; + ttl -= tpassed; + if(ttl<0) ttl=0; + } + return ttl; +} + +/* follow_cname_chain takes a cache entry and a buffer (must be at least DNSNAMEBUFSIZE bytes), + and copies the name indicated by the first cname record in the cache entry. + The name is returned in length-byte string notation. + follow_cname_chain returns 1 if a cname record is found, otherwise 0. +*/ +inline static int follow_cname_chain(dns_cent_t *c, unsigned char *name) +{ + rr_set_t *rrset=getrrset_CNAME(c); + rr_bucket_t *rr; + if (!rrset || !(rr=rrset->rrs)) + return 0; + PDNSD_ASSERT(rr->rdlen <= DNSNAMEBUFSIZE, "follow_cname_chain: record too long"); + memcpy(name,rr->data,rr->rdlen); + return 1; +} + + +/* + * Add data from a rr_bucket_t (as in cache) into a dns message in ans. Ans is grown + * to fit, sz is the old size of the packet (it is modified so at the end of the procedure + * it is the new size), type is the rr type and ltime is the time in seconds the record is + * old. + * cb is the buffer used for message compression. *cb should be NULL when you call compress_name + * or add_to_response the first time. + * It gets filled with a pointer to compression information that can be reused in subsequent calls + * to add_to_response. + * sect is the section (S_ANSWER, S_AUTHORITY or S_ADDITIONAL) in which the record + * belongs logically. Note that you still have to add the rrs in the right order (answer rrs first, + * then authority and last additional). + */ +static int add_rr(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned char *rrn, unsigned short type, uint32_t ttl, + unsigned int dlen, void *data, char section, unsigned *udp, dlist *cb) +{ + size_t osz= *sz; + unsigned int ilen,blen,rdlen; + unsigned char *rrht; + + { + unsigned int nlen; + unsigned char nbuf[DNSNAMEBUFSIZE]; + + if (!(nlen=compress_name(rrn,nbuf,*sz,cb))) + return 0; + + /* This buffer is usually over-allocated due to compression. + Never mind, just a few bytes, and the buffer is freed soon. */ + { + size_t newsz= dnsmsghdroffset + *sz + nlen + sizeof_rr_hdr_t + dlen; + if(newsz > *allocsz) { + /* Need to allocate more space. + To avoid frequent reallocs, we allocate + a multiple of a certain chunk size. */ + size_t newallocsz= (newsz+ALLOCCHUNKSIZEMASK)&(~ALLOCCHUNKSIZEMASK); + dns_msg_t *newans=(dns_msg_t *)pdnsd_realloc(*ans,newallocsz); + if (!newans) + return 0; + *ans=newans; + *allocsz=newallocsz; + } + } + memcpy((unsigned char *)(&(*ans)->hdr)+ *sz, nbuf, nlen); + *sz += nlen; + } + + /* the rr header will be filled in later. Just reserve some space for it. */ + rrht= ((unsigned char *)(&(*ans)->hdr)) + *sz; + *sz += sizeof_rr_hdr_t; + + switch (type) { + case T_CNAME: + case T_MB: + case T_MD: + case T_MF: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + if (!(rdlen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + PDNSD_ASSERT(rdlen <= dlen, "T_CNAME/T_MB/...: got longer"); + *sz+=rdlen; + break; +#if IS_CACHED_MINFO || IS_CACHED_RP +#if IS_CACHED_MINFO + case T_MINFO: +#endif +#if IS_CACHED_RP + case T_RP: +#endif + if (!(rdlen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + *sz+=rdlen; + ilen=rhnlen((unsigned char *)data); + PDNSD_ASSERT(rdlen <= ilen, "T_MINFO/T_RP: got longer"); + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen+=blen; + PDNSD_ASSERT(rdlen <= dlen, "T_MINFO/T_RP: got longer"); + *sz+=blen; + break; +#endif + case T_MX: +#if IS_CACHED_AFSDB + case T_AFSDB: +#endif +#if IS_CACHED_RT + case T_RT: +#endif +#if IS_CACHED_KX + case T_KX: +#endif + PDNSD_ASSERT(dlen > 2, "T_MX/T_AFSDB/...: rr botch"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),(unsigned char *)data,2); + *sz+=2; + if (!(blen=compress_name(((unsigned char *)data)+2, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=2+blen; + PDNSD_ASSERT(rdlen <= dlen, "T_MX/T_AFSDB/...: got longer"); + *sz+=blen; + break; + case T_SOA: + if (!(rdlen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + *sz+=rdlen; + ilen=rhnlen((unsigned char *)data); + PDNSD_ASSERT(rdlen <= ilen, "T_SOA: got longer"); + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen+=blen; + *sz+=blen; + ilen+=rhnlen(((unsigned char *)data)+ilen); + PDNSD_ASSERT(rdlen <= ilen, "T_SOA: got longer"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data)+ilen,20); + rdlen+=20; + PDNSD_ASSERT(rdlen <= dlen, "T_SOA: rr botch"); + *sz+=20; + break; +#if IS_CACHED_PX + case T_PX: + PDNSD_ASSERT(dlen > 2, "T_PX: rr botch"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),(unsigned char *)data,2); + *sz+=2; + ilen=2; + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=2+blen; + *sz+=blen; + ilen+=rhnlen(((unsigned char *)data)+ilen); + PDNSD_ASSERT(rdlen <= ilen, "T_PX: got longer"); + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen+=blen; + PDNSD_ASSERT(rdlen <= dlen, "T_PX: got longer"); + *sz+=blen; + break; +#endif +#if IS_CACHED_SRV + case T_SRV: + PDNSD_ASSERT(dlen > 6, "T_SRV: rr botch"); + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),(unsigned char *)data,6); + *sz+=6; + if (!(blen=compress_name(((unsigned char *)data)+6, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=6+blen; + PDNSD_ASSERT(rdlen <= dlen, "T_SRV: got longer"); + *sz+=blen; + break; +#endif +#if IS_CACHED_NXT + case T_NXT: + if (!(blen=compress_name(((unsigned char *)data), ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=blen; + *sz+=blen; + ilen=rhnlen((unsigned char *)data); + PDNSD_ASSERT(rdlen <= ilen, "T_NXT: got longer"); + PDNSD_ASSERT(dlen >= ilen, "T_NXT: rr botch"); + if (dlen > ilen) { + unsigned int wlen = dlen - ilen; + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data)+ilen,wlen); + *sz+=wlen; + rdlen+=wlen; + } + break; +#endif +#if IS_CACHED_NAPTR + case T_NAPTR: + PDNSD_ASSERT(dlen > 4, "T_NAPTR: rr botch"); + ilen=4; + { + int j; + for (j=0;j<3;j++) { + ilen += ((unsigned)*(((unsigned char *)data)+ilen)) + 1; + PDNSD_ASSERT(dlen > ilen, "T_NAPTR: rr botch 2"); + } + } + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data),ilen); + (*sz)+=ilen; + + if (!(blen=compress_name(((unsigned char *)data)+ilen, ((unsigned char *)(&(*ans)->hdr))+(*sz),*sz,cb))) + return 0; + rdlen=ilen+blen; + PDNSD_ASSERT(rdlen <= dlen, "T_NAPTR: got longer"); + *sz+=blen; + break; +#endif + default: + memcpy(((unsigned char *)(&(*ans)->hdr))+(*sz),((unsigned char *)data),dlen); + rdlen=dlen; + *sz+=dlen; + } + + if (udp && *sz>*udp && section==S_ADDITIONAL) /* only add the record if we do not increase the length over 512 */ + *sz=osz; /* (or possibly more if the request used EDNS) in additionals for udp answer. */ + else { + PUTINT16(type,rrht); + PUTINT16(C_IN,rrht); + PUTINT32(ttl,rrht); + PUTINT16(rdlen,rrht); + + switch (section) { + case S_ANSWER: + (*ans)->hdr.ancount=htons(ntohs((*ans)->hdr.ancount)+1); + break; + case S_AUTHORITY: + (*ans)->hdr.nscount=htons(ntohs((*ans)->hdr.nscount)+1); + break; + case S_ADDITIONAL: + (*ans)->hdr.arcount=htons(ntohs((*ans)->hdr.arcount)+1); + break; + } + } + + return 1; +} + +/* Add an OPT pseudo RR containing EDNS info. + Can only be added to the additional section! +*/ +int add_opt_pseudo_rr(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned short udpsize, unsigned short rcode, + unsigned short ednsver, unsigned short Zflags) +{ + unsigned char *ptr; + size_t newsz= dnsmsghdroffset + *sz + sizeof_opt_pseudo_rr; + if(newsz > *allocsz) { + /* Need to allocate more space. + To avoid frequent reallocs, we allocate + a multiple of a certain chunk size. */ + size_t newallocsz= (newsz+ALLOCCHUNKSIZEMASK)&(~ALLOCCHUNKSIZEMASK); + dns_msg_t *newans=(dns_msg_t *)pdnsd_realloc(*ans,newallocsz); + if (!newans) + return 0; + *ans=newans; + *allocsz=newallocsz; + } + + ptr= ((unsigned char *)(&(*ans)->hdr)) + *sz; + *ptr++ = 0; /* Empty name */ + PUTINT16(T_OPT,ptr); /* type field */ + PUTINT16(udpsize,ptr); /* class field */ + *ptr++ = rcode>>4; /* 4 byte TTL field */ + *ptr++ = ednsver; + PUTINT16(Zflags,ptr); + PUTINT16(0,ptr); /* rdlen field */ + /* Empty RDATA. */ + + *sz += sizeof_opt_pseudo_rr; + /* Increment arcount field in dns header. */ + (*ans)->hdr.arcount = htons(ntohs((*ans)->hdr.arcount)+1); + return 1; +} + +/* Remove the last entry in the additional section, + assuming it is an OPT pseudo RR of fixed size. + Returns the new message size if successful, or + zero if an inconsistency is detected. +*/ +size_t remove_opt_pseudo_rr(dns_msg_t *ans, size_t sz) +{ + uint16_t acnt=ntohs(ans->hdr.arcount), type; + unsigned char *ptr; + /* First do some sanity checks. */ + if(!(acnt>0 && sz >= sizeof(dns_hdr_t)+sizeof_opt_pseudo_rr)) + return 0; + sz -= sizeof_opt_pseudo_rr; + ptr= ((unsigned char *)(&ans->hdr)) + sz; + if(*ptr++) + return 0; /* Name must be empty. */ + GETINT16(type,ptr); + if(type!=T_OPT) + return 0; /* RR type must be OPT. */ + /* Decrement arcount field in dns header. */ + ans->hdr.arcount = htons(acnt-1); + return sz; +} + +typedef struct rre_s { + unsigned short tp; + unsigned short tsz; /* Size of tnm field */ + uint32_t ttl; /* ttl of the record in the answer (if tp==T_NS or T_SOA) */ + unsigned char tnm[0]; /* Name for the domain a record refers to */ + /* unsigned char nm[0]; */ /* Name of the domain the record is for (if tp==T_NS or T_SOA) */ +} rr_ext_t; + + +/* types for the tp field */ +/* #define RRETP_NS T_NS */ /* For name server: add to authority, add address to additional. */ +/* #define RRETP_SOA T_SOA */ /* For SOA record: add to authority. */ +#define RRETP_ADD 0 /* For other records: add the address of buf to additional */ + +static int add_ar(dlist *ar,unsigned short tp, unsigned short tsz,void *tnm,unsigned char *nm, uint32_t ttl) +{ + rr_ext_t *re; + unsigned char *p; + size_t nmsz=0,size=sizeof(rr_ext_t)+tsz; + if(tp==T_NS || tp==T_SOA) { + nmsz=rhnlen(nm); + size += nmsz; + } + if (!(*ar=dlist_grow(*ar,size))) + return 0; + re=dlist_last(*ar); + re->tp=tp; + re->tsz=tsz; + re->ttl=ttl; + p=mempcpy(re->tnm,tnm,tsz); + if(tp==T_NS || tp==T_SOA) { + memcpy(p,nm,nmsz); + } + return 1; +} + + +/* Select a random rr record from a list. */ +inline static rr_bucket_t *randrr(rr_bucket_t *rrb) +{ + rr_bucket_t *rr; + unsigned cnt=0; + + /* In order to have an equal chance for each record to be selected, we have to count first. */ + for(rr=rrb; rr; rr=rr->next) ++cnt; + + /* We do not use the pdnsd random functions (these might use /dev/urandom if the user is paranoid, + * and we do not need any good PRNG here). */ + if(cnt) for(cnt=random()%cnt; cnt; --cnt) rrb=rrb->next; + + return rrb; +} + +#if IS_CACHED_SRV +#define AR_NUM 6 +#else +#define AR_NUM 5 +#endif +static const int ar_recs[AR_NUM]={T_NS, T_MD, T_MF, T_MB, T_MX +#if IS_CACHED_SRV + ,T_SRV +#endif +}; +/* offsets from record data start to server name */ +static const int ar_offs[AR_NUM]={0,0,0,0,2 +#if IS_CACHED_SRV + ,6 +#endif +}; + +/* This adds an rrset, optionally randomizing the first element it adds. + * if that is done, all rrs after the randomized one appear in order, starting from + * that one and wrapping over if needed. */ +static int add_rrset(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned char *rrn, unsigned tp, time_t queryts, + dns_cent_t *cached, unsigned *udp, dlist *cb, dlist *sva, dlist *ar) +{ + rr_set_t *crrset=getrrset(cached,tp); + + if (crrset && crrset->rrs) { + rr_bucket_t *b; + rr_bucket_t *first=NULL; /* Initialized to inhibit compiler warning */ + int i; + short rnd_recs=global.rnd_recs; + + b=crrset->rrs; + if (rnd_recs) b=first=randrr(crrset->rrs); + + while (b) { + if (!add_rr(ans, sz, allocsz, rrn, tp, ans_ttl(crrset,queryts), + b->rdlen, b->data, S_ANSWER, udp, cb)) + return 0; + if (tp==T_NS || tp==T_A || tp==T_AAAA) { + /* mark it as added */ + if (!sva_add(sva,rrn,tp,b->rdlen,b->data)) + return 0; + } + /* Mark for additional address records. XXX: this should be a more effective algorithm; at least the list is small */ + for (i=0;irdlen-ar_offs[i],((unsigned char *)(b->data))+ar_offs[i], + ucharp "", 0)) + return 0; + break; + } + } + b=b->next; + if (rnd_recs) { + if(!b) b=crrset->rrs; /* wraparound */ + if(b==first) break; + } + } + } + return 1; +} + +/* + * Add the fitting elements of the cached record to the message in ans, where ans + * is grown to fit, sz is the size of the packet and is modified to be the new size. + * The query is in qe. + * cb is the buffer used for message compression. *cb should be NULL if you call add_to_response + * the first time. It gets filled with a pointer to compression information that can be + * reused in subsequent calls to add_to_response. + */ +static int add_to_response(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned char *rrn, unsigned qtype, time_t queryts, + dns_cent_t *cached, unsigned *udp, dlist *cb, dlist *sva, dlist *ar) +{ + /* First of all, unless we have records of qtype, add cnames. + Well, actually, there should be at max one cname. */ + if (qtype!=T_CNAME && qtype!=QT_ALL && !(qtype>=T_MIN && qtype<=T_MAX && have_rr(cached,qtype))) + if (!add_rrset(ans, sz, allocsz, rrn, T_CNAME, queryts, cached, udp, cb, sva, ar)) + return 0; + + /* We need no switch for qclass, since we already have filtered packets we cannot understand */ + if (qtype==QT_AXFR || qtype==QT_IXFR) { + /* I do not know what to do in this case. Since we do not maintain zones (and since we are + no master server, so it is not our task), I just return an error message. If anyone + knows how to do this better, please notify me. + Anyway, this feature is rarely used in client communication, and there is no need for + other name servers to ask pdnsd. Btw: many bind servers reject an ?XFR query for security + reasons. */ + return 0; + } else if (qtype==QT_MAILB) { + if (!add_rrset(ans, sz, allocsz, rrn, T_MB, queryts, cached, udp, cb, sva, ar)) + return 0; + if (!add_rrset(ans, sz, allocsz, rrn, T_MG, queryts, cached, udp, cb, sva, ar)) + return 0; + if (!add_rrset(ans, sz, allocsz, rrn, T_MR, queryts, cached, udp, cb, sva, ar)) + return 0; + } else if (qtype==QT_MAILA) { + if (!add_rrset(ans, sz, allocsz, rrn, T_MD, queryts, cached, udp, cb, sva, ar)) + return 0; + if (!add_rrset(ans, sz, allocsz, rrn, T_MF, queryts, cached, udp, cb, sva, ar)) + return 0; + } else if (qtype==QT_ALL) { + int i, n= NRRITERLIST(cached); + const unsigned short *iterlist= RRITERLIST(cached); + for (i=0; i=T_MIN && qtype<=T_MAX) { + if (!add_rrset(ans, sz, allocsz, rrn, qtype, queryts, cached, udp, cb, sva, ar)) + return 0; + } else /* Shouldn't get here. */ + return 0; +#if 0 + if (!ntohs((*ans)->hdr.ancount)) { + /* Add a SOA if we have one and no other records are present in the answer. + * This is to aid caches so that they have a ttl. */ + if (!add_rrset(ans, sz, allocsz, rrn, T_SOA , queryts, cached, udp, cb, sva, ar)) + return 0; + } +#endif + return 1; +} + +/* + * Add an additional + */ +static int add_additional_rr(dns_msg_t **ans, size_t *rlen, size_t *allocsz, + unsigned char *rhn, unsigned tp, time_t ttl, + unsigned dlen, void *data, int sect, unsigned *udp, dlist *cb, dlist *sva) +{ + sva_t *st; + + /* Check if already added; no double additionals */ + for (st=dlist_first(*sva); st; st=dlist_next(st)) { + if (st->tp==tp && rhnicmp(st->nm,rhn) && st->dlen==dlen && + (memcmp(skiprhn(st->nm),data, dlen)==0)) + { + return 1; + } + } + /* add_rr will do nothing when udp!=NULL and sz>*udp. */ + if(!add_rr(ans, rlen, allocsz, rhn, tp, ttl, dlen, data, sect, udp, cb)) + return 0; + /* mark it as added */ + if (!sva_add(sva,rhn,tp,dlen,data)) + return 0; + + return 1; +} + +/* + * Add one or more additionals from an rr bucket. + */ +static int add_additional_rrs(dns_msg_t **ans, size_t *rlen, size_t *allocsz, + unsigned char *rhn, unsigned tp, time_t ttl, + rr_bucket_t *rrb, int sect, unsigned *udp, dlist *cb, dlist *sva) +{ + rr_bucket_t *rr; + rr_bucket_t *first=NULL; /* Initialized to inhibit compiler warning */ + short rnd_recs=global.rnd_recs; + + rr=rrb; + if (rnd_recs) rr=first=randrr(rrb); + + while(rr) { + if (!add_additional_rr(ans, rlen, allocsz, rhn, tp, ttl, rr->rdlen,rr->data, sect, udp, cb, sva)) + return 0; + rr=rr->next; + if (rnd_recs) { + if(!rr) rr=rrb; /* wraparound */ + if(rr==first) break; + } + } + return 1; +} + +/* + * The code below actually handles A and AAAA additionals. + */ +static int add_additional_a(dns_msg_t **ans, size_t *rlen, size_t *allocsz, + unsigned char *rhn, time_t queryts, + unsigned *udp, dlist *cb, dlist *sva) +{ + dns_cent_t *ae; + int retval = 1; + + if ((ae=lookup_cache(rhn,NULL))) { + rr_set_t *rrset; rr_bucket_t *rr; + rrset=getrrset_A(ae); + if (rrset && (rr=rrset->rrs)) + if (!add_additional_rrs(ans, rlen, allocsz, + rhn, T_A, ans_ttl(rrset,queryts), + rr, S_ADDITIONAL, udp, cb, sva)) + retval = 0; + +#if IS_CACHED_AAAA + if(retval) { + rrset=getrrset_AAAA(ae); + if (rrset && (rr=rrset->rrs)) + if (!add_additional_rrs(ans, rlen, allocsz, + rhn, T_AAAA, ans_ttl(rrset,queryts), + rr, S_ADDITIONAL, udp, cb, sva)) + retval = 0; + } +#endif + free_cent(ae DBG1); + pdnsd_free(ae); + } + return retval; +} + +/* + * Compose an answer message for the decoded query in ql, hdr is the header of the dns request + * rlen is set to be the answer length. + * If udp is not NULL, *udp indicates the max length the dns response may have. + */ +static dns_msg_t *compose_answer(llist *ql, dns_hdr_t *hdr, size_t *rlen, edns_info_t *ednsinfo, unsigned *udp, int *rcodep) +{ + unsigned short rcode=RC_OK, aa=1; + dlist cb=NULL; + dlist sva=NULL; + dlist ar=NULL; + time_t queryts=time(NULL); + dns_queryel_t *qe; + dns_msg_t *ans; + size_t allocsz= ALLOCINITIALSIZE; + dns_cent_t *cached; + + ans=(dns_msg_t *)pdnsd_malloc(allocsz); + if (!ans) + goto return_ans; + ans->hdr.id=hdr->id; + ans->hdr.qr=QR_RESP; + ans->hdr.opcode=OP_QUERY; + ans->hdr.aa=0; + ans->hdr.tc=0; /* If tc is needed, it is set when the response is sent in udp_answer_thread. */ + ans->hdr.rd=hdr->rd; + ans->hdr.ra=1; + ans->hdr.z=0; + ans->hdr.ad=0; + ans->hdr.cd=0; + ans->hdr.rcode=rcode; + ans->hdr.qdcount=0; /* this is first filled in and will be modified */ + ans->hdr.ancount=0; + ans->hdr.nscount=0; + ans->hdr.arcount=0; + + *rlen=sizeof(dns_hdr_t); + /* first, add the query to the response */ + for (qe=llist_first(ql); qe; qe=llist_next(qe)) { + unsigned int qclen; + size_t newsz= dnsmsghdroffset + *rlen + rhnlen(qe->query) + 4; + if(newsz > allocsz) { + /* Need to allocate more space. + To avoid frequent reallocs, we allocate + a multiple of a certain chunk size. */ + size_t newallocsz= (newsz+ALLOCCHUNKSIZEMASK)&(~ALLOCCHUNKSIZEMASK); + dns_msg_t *newans=(dns_msg_t *)pdnsd_realloc(ans,newallocsz); + if (!newans) + goto error_ans; + ans=newans; + allocsz=newallocsz; + } + + { + unsigned char *p = ((unsigned char *)&ans->hdr) + *rlen; + /* the first name occurrence will not be compressed, + but the offset needs to be stored for future compressions */ + if (!(qclen=compress_name(qe->query,p,*rlen,&cb))) + goto error_ans; + p += qclen; + PUTINT16(qe->qtype,p); + PUTINT16(qe->qclass,p); + } + *rlen += qclen+4; + ans->hdr.qdcount=htons(ntohs(ans->hdr.qdcount)+1); + } + + /* Barf if we get a query we cannot answer */ + for (qe=llist_first(ql); qe; qe=llist_next(qe)) { + if ((PDNSD_NOT_CACHED_TYPE(qe->qtype) && + (qe->qtype!=QT_MAILB && qe->qtype!=QT_MAILA && qe->qtype!=QT_ALL)) || + (qe->qclass!=C_IN && qe->qclass!=QC_ALL)) + { + DEBUG_MSG("Unsupported QTYPE or QCLASS.\n"); + ans->hdr.rcode=rcode=RC_NOTSUPP; + goto cleanup_return; + } + } + + /* second, the answer section */ + for (qe=llist_first(ql); qe; qe=llist_next(qe)) { + int hops; + unsigned char qname[DNSNAMEBUFSIZE]; + + rhncpy(qname,qe->query); + /* look if we have a cached copy. otherwise, perform a nameserver query. Same with timeout */ + hops=MAX_HOPS; + do { + int rc; + unsigned char c_soa=cundef; + if ((rc=dns_cached_resolve(qname,qe->qtype, &cached, MAX_HOPS,queryts,&c_soa))!=RC_OK) { + ans->hdr.rcode=rcode=rc; + if(rc==RC_NAMEERR) { + if(c_soa!=cundef) { + /* Try to add a SOA record to the authority section. */ + unsigned scnt=rhnsegcnt(qname); + if(c_soaflags&CF_NEGATIVE)) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + if (!add_rr(&ans,rlen,&allocsz,cached->qname,T_SOA,ans_ttl(rrset,queryts), + rr->rdlen,rr->data,S_AUTHORITY,udp,&cb)) + goto error_cached; + } + } + free_cent(cached DBG1); + pdnsd_free(cached); + } + } + + /* Possibly add an OPT pseudo-RR to the additional section. */ + if(ednsinfo) { + if(!add_opt_pseudo_rr(&ans, rlen, &allocsz, global.udpbufsize, rcode, 0,0)) + goto error_ans; + } + } + goto cleanup_return; + } + if(!(cached->flags&DF_LOCAL)) + aa=0; + + if (!add_to_response(&ans,rlen,&allocsz,qname,qe->qtype,queryts,cached,udp,&cb,&sva,&ar)) + goto error_cached; + if (hdr->rd && qe->qtype!=T_CNAME && qe->qtype!=QT_ALL && + !(qe->qtype>=T_MIN && qe->qtype<=T_MAX && have_rr(cached,qe->qtype)) && + follow_cname_chain(cached,qname)) + /* The rd bit is set and the response does not contain records of the requested type, + * but the response does contain a cname, so repeat the inquiry with the cname. + * add_to_response() has already added the cname to the response. + * Because of follow_cname_chain(), qname now contains the last cname in the chain. */ + ; + else { + /* maintain a list (ar) for authority records: We will add every name server that was + listed as authoritative in a reply we received (and only those) to this list. + This list will be used to fill the authority and additional sections of our own reply. + We only do this for the last record in a cname chain, to prevent answer bloat. */ + rr_set_t *rrset; + int rretp=T_NS; + if((qe->qtype>=T_MIN && qe->qtype<=T_MAX && !have_rr(cached,qe->qtype)) || + (qe->qtype==QT_MAILB && !have_rr_MB(cached) && !have_rr_MG(cached) && !have_rr_MR(cached)) || + (qe->qtype==QT_MAILA && !have_rr_MD(cached) && !have_rr_MF(cached))) + { + /* no record of requested type in the answer section. */ + rretp=T_SOA; + } + rrset=getrrset(cached,rretp); + if(rrset && (rrset->flags&CF_NEGATIVE)) + rrset=NULL; + if(!rrset) { + /* Try to find a name server higher up the hierarchy . + */ + dns_cent_t *prev=cached; + unsigned scnt=rhnsegcnt(prev->qname); + unsigned tcnt=(rretp==T_NS?prev->c_ns:prev->c_soa); + if((cached=lookup_cache((tcnt!=cundef && tcntqname,scnt-tcnt):prev->qname,NULL))) { + rrset=getrrset(cached,rretp); + if(rrset && (rrset->flags&CF_NEGATIVE)) + rrset=NULL; + } + if(!rrset && (prev->flags&DF_LOCAL)) { + unsigned char *nm=getlocalowner(prev->qname,rretp); + if(nm) { + if(cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + if((cached=lookup_cache(nm,NULL))) + rrset=getrrset(cached,rretp); + } + } + free_cent(prev DBG1); + pdnsd_free(prev); + } + if (rrset) { + rr_bucket_t *rr; + for (rr=rrset->rrs; rr; rr=rr->next) { + if (!add_ar(&ar, rretp, rr->rdlen,rr->data, cached->qname, + ans_ttl(rrset,queryts))) + goto error_cached; + } + } + hops=0; /* this will break the loop */ + } + if(cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + } while (--hops>=0); + } + + { + rr_ext_t *rre; + /* Add the authority section */ + for (rre=dlist_first(ar); rre; rre=dlist_next(rre)) { + if (rre->tp == T_NS || rre->tp == T_SOA) { + unsigned char *nm = rre->tnm + rre->tsz; + if (!add_additional_rr(&ans, rlen, &allocsz, + nm, rre->tp, rre->ttl, rre->tsz, rre->tnm, + S_AUTHORITY, udp, &cb, &sva)) + { + goto error_ans; + } + } + } + + /* Add the additional section, but only if we stay within the UDP buffer limit. */ + /* If a pseudo RR doesn't fit, nothing else will. */ + if(!(udp && *rlen+sizeof_opt_pseudo_rr>*udp)) { + + /* Possibly add an OPT pseudo-RR to the additional section. */ + if(ednsinfo) { + if(!add_opt_pseudo_rr(&ans, rlen, &allocsz, global.udpbufsize, rcode, 0,0)) + goto error_ans; + } + + /* now add the name server addresses */ + for (rre=dlist_first(ar); rre; rre=dlist_next(rre)) { + if (rre->tp == T_NS || rre->tp == RRETP_ADD) { + if (!add_additional_a(&ans, rlen, &allocsz, + rre->tnm, queryts, udp, &cb, &sva)) + goto error_ans; + } + } + } + } + if (aa) + ans->hdr.aa=1; + goto cleanup_return; + + /* You may not like goto's, but here we avoid lots of code duplication. */ +error_cached: + free_cent(cached DBG1); + pdnsd_free(cached); +error_ans: + pdnsd_free(ans); + ans=NULL; +cleanup_return: + dlist_free(ar); + dlist_free(sva); + dlist_free(cb); +return_ans: + if(rcodep) *rcodep=rcode; + return ans; +} + +/* + * Decode the query (the query messgage is in data and rlen bytes long) into a dlist. + * XXX: data needs to be aligned. + * The return value can be RC_OK or RC_TRUNC, in which case the (partially) constructed list is + * returned in qp, or something else (RC_FORMAT or RC_SERVFAIL), in which case no list is returned. + * + * *ptrrem will be assigned the address just after the questions sections in the message, and *lenrem + * the remaining message length after the questions section. These values are only meaningful if the + * return value is RC_OK. + */ +static int decode_query(unsigned char *data, size_t rlen, unsigned char **ptrrem, size_t *lenrem, llist *qp) +{ + int i,res=RC_OK; + dns_hdr_t *hdr=(dns_hdr_t *)data; /* aligned, so no prob. */ + unsigned char *ptr=(unsigned char *)(hdr+1); + size_t sz= rlen - sizeof(dns_hdr_t); + uint16_t qdcount=ntohs(hdr->qdcount); + + llist_init(qp); + for (i=0; iqtype,ptr); + GETINT16(qe->qclass,ptr); + sz-=4; + memcpy(qe->query,qbuf,qlen); + } + + if(ptrrem) *ptrrem=ptr; + if(lenrem) *lenrem=sz; + return res; +} + + +/* Scan the additional section of a query message for an OPT pseudo RR. + data and rlen are as in decode_query(). Note in particular that data needs to be aligned! + ptr should point the beginning of the additional section, sz should contain the + length of this remaining part of the message and numrr the number of resource records in the section. + *numopt is incremented with the number of OPT RRs found (should be at most one). + + Note that a return value of RC_OK means the additional section was parsed without errors, not that + an OPT pseudo RR was found! Check the value of *numopt for the latter. + + The structure pointed to by ep is filled with the information of the first OPT pseudo RR found, + but only if *numopt was set to zero before the call. +*/ +static int decode_query_additional(unsigned char *data, size_t rlen, unsigned char *ptr, size_t sz, int numrr, + int *numopt, edns_info_t *ep) +{ + int i, res; + + for (i=0; i0 + if(nmbuf[0]!=0) { + DEBUG_MSG("decode_query_additional: name in OPT record not empty!\n"); + } +#endif + ep->udpsize= class; + ep->rcode= ((uint16_t)ttlp[0]<<4) | ((dns_hdr_t *)data)->rcode; + ep->version= ttlp[1]; + ep->do_flg= (ttlp[2]>>7)&1; +#if DEBUG>0 + if(debug_p) { + unsigned int Zflags= ((uint16_t)ttlp[2]<<8) | ttlp[3]; + if(Zflags & 0x7fff) { + DEBUG_MSG("decode_query_additional: Z field contains unknown nonzero bits (%04x).\n", + Zflags); + } + if(rdlen) { + DEBUG_MSG("decode_query_additional: RDATA field in OPT record not empty!\n"); + } + } +#endif + } + else { + DEBUG_MSG("decode_query_additional: ingnoring surplus OPT record.\n"); + } + } + else { + DEBUG_MSG("decode_query_additional: ignoring record of type %s (%d).\n", + getrrtpname(type), type); + } + + /* Skip RDATA field. */ + sz -= rdlen; + ptr += rdlen; + } + + return RC_OK; +} + +/* Make a dns error reply message + * Id is the query id and still in network order. + * op is the opcode to fill in, rescode - name says it all. + */ +static void mk_error_reply(unsigned short id, unsigned short opcode,unsigned short rescode,dns_hdr_t *rep) +{ + rep->id=id; + rep->qr=QR_RESP; + rep->opcode=opcode; + rep->aa=0; + rep->tc=0; + rep->rd=0; + rep->ra=1; + rep->z=0; + rep->ad=0; + rep->cd=0; + rep->rcode=rescode; + rep->qdcount=0; + rep->ancount=0; + rep->nscount=0; + rep->arcount=0; +} + +/* + * Analyze and answer the query in data. The answer is returned. rlen is at call the query length and at + * return the length of the answer. You have to free the answer after sending it. + */ +static dns_msg_t *process_query(unsigned char *data, size_t *rlenp, unsigned *udp, int *rcodep) +{ + size_t rlen= *rlenp; + int res; + dns_hdr_t *hdr; + llist ql; + dns_msg_t *ans; + edns_info_t ednsinfo= {0}, *ednsinfop= NULL; + + DEBUG_MSG("Received query (msg len=%u).\n", (unsigned int)rlen); + DEBUG_DUMP_DNS_MSG(data, rlen); + + /* + * We will ignore all records that come with a query, except for the actual query records, + * and possible OPT pseudo RRs in the addtional section. + * We will send back the query in the response. We will reject all non-queries, and + * some not supported thingies. + * If anyone notices behaviour that is not in standard conformance, please notify me! + */ + hdr=(dns_hdr_t *)data; + if (rlen<2) { + DEBUG_MSG("Message too short.\n"); + return NULL; /* message too short: no id provided. */ + } + if (rlenqr!=QR_QUERY) { + DEBUG_MSG("The QR bit indicates this is a response, not a query.\n"); + return NULL; /* RFC says: discard */ + } + if (hdr->opcode!=OP_QUERY) { + DEBUG_MSG("Not a standard query (opcode=%u).\n",hdr->opcode); + res=RC_NOTSUPP; + goto error_reply; + } +#if DEBUG>0 + if(debug_p) { + char flgsbuf[DNSFLAGSMAXSTRSIZE]; + dnsflags2str(hdr, flgsbuf); + if(flgsbuf[0]) { + DEBUG_MSG("Flags:%s\n", flgsbuf); + } + } +#endif + if (hdr->z!=0) { + DEBUG_MSG("Malformed query (nonzero Z bit).\n"); + res=RC_FORMAT; + goto error_reply; + } + if (hdr->rcode!=RC_OK) { + DEBUG_MSG("Bad rcode(%u).\n",hdr->rcode); + return NULL; /* discard (may cause error storms) */ + } + + if (hdr->ancount) { + DEBUG_MSG("Query has a non-empty answer section!\n"); + res=RC_FORMAT; + goto error_reply; + } + + if (hdr->nscount) { + DEBUG_MSG("Query has a non-empty authority section!\n"); + res=RC_FORMAT; + goto error_reply; + } + +#if 0 + /* The following only makes sense if we completely disallow + Extension Mechanisms for DNS (RFC 2671). */ + if (hdr->arcount) { + DEBUG_MSG("Query has a non-empty additional section!\n"); + res=RC_FORMAT; + goto error_reply; + } +#endif + { + unsigned char *ptr; + size_t sz; + uint16_t arcount; + res=decode_query(data,rlen,&ptr,&sz,&ql); + if(res!=RC_OK) { + if(res==RC_TRUNC) { + if(!hdr->tc || llist_isempty(&ql)) { + res=RC_FORMAT; + goto free_ql_error_reply; + } + } + else + goto error_reply; + } + + if ((arcount=ntohs(hdr->arcount))) { + int numoptrr= 0; + DEBUG_MSG("Query has a non-empty additional section: " + "checking for OPT pseudo-RR.\n"); + if(res==RC_TRUNC) { + DEBUG_MSG("Additional section cannot be read due to truncation!\n"); + res=RC_FORMAT; + goto free_ql_error_reply; + } + res=decode_query_additional(data,rlen,ptr,sz,arcount, &numoptrr, &ednsinfo); + if(!(res==RC_OK || (res==RC_TRUNC && hdr->tc))) { + res=RC_FORMAT; + goto free_ql_error_reply; + } + if(numoptrr) { +#if DEBUG>0 + if(numoptrr!=1) { + DEBUG_MSG("Additional section in query contains %d OPT pseudo-RRs!\n", numoptrr); + } +#endif + if(ednsinfo.version!=0) { + DEBUG_MSG("Query contains unsupported EDNS version %d!\n", ednsinfo.version); + res=RC_BADVERS; + goto free_ql_error_reply; + } + if(ednsinfo.rcode!=0) { + DEBUG_MSG("Query contains non-zero EDNS rcode (%d)!\n", ednsinfo.rcode); + res=RC_FORMAT; + goto free_ql_error_reply; + } + DEBUG_MSG("Query contains OPT pseudosection: EDNS udp size = %u, flag DO=%u\n", + ednsinfo.udpsize, ednsinfo.do_flg); + ednsinfop = &ednsinfo; + if(udp && ednsinfo.udpsize>UDP_BUFSIZE) { + unsigned udpbufsize = global.udpbufsize; + if(udpbufsize > ednsinfo.udpsize) + udpbufsize = ednsinfo.udpsize; + *udp = udpbufsize; + } + } + } + } + +#if DEBUG>0 + if (debug_p) { + if(!llist_isempty(&ql)) { + dns_queryel_t *qe; + DEBUG_MSG("Questions are:\n"); + for (qe=llist_first(&ql); qe; qe=llist_next(qe)) { + DEBUG_RHN_MSG("\tqc=%s (%u), qt=%s (%u), query=\"%s\"\n", + get_cname(qe->qclass),qe->qclass,get_tname(qe->qtype),qe->qtype,RHN2STR(qe->query)); + } + } + else { + DEBUG_MSG("Query contains no questions.\n"); + } + } +#endif + + if (llist_isempty(&ql)) { + res=RC_FORMAT; + goto error_reply; + } + if (!(ans=compose_answer(&ql, hdr, rlenp, ednsinfop, udp, rcodep))) { + /* An out of memory condition or similar could cause NULL output. Send failure notification */ + res=RC_SERVFAIL; + goto free_ql_error_reply; + } + llist_free(&ql); + return ans; + + free_ql_error_reply: + llist_free(&ql); + error_reply: + *rlenp=sizeof(dns_hdr_t); + { + size_t allocsz = sizeof(dns_msg_t); + if(res&~0xf) + allocsz += sizeof_opt_pseudo_rr; + ans= (dns_msg_t *)pdnsd_malloc(allocsz); + if (ans) { + mk_error_reply(hdr->id,rlen>=3?hdr->opcode:OP_QUERY,res,&ans->hdr); + if(res&~0xf) + add_opt_pseudo_rr(&ans,rlenp,&allocsz, + global.udpbufsize,res,0,0); + } + else if (++da_mem_errs<=MEM_MAX_ERRS) { + log_error("Out of memory in query processing."); + } + } + if(rcodep) *rcodep= res; + return ans; +} + +/* + * Called by *_answer_thread exit handler to clean up process count. + */ +inline static void decrease_procs() +{ + + pthread_mutex_lock(&proc_lock); + procs--; + qprocs--; + pthread_mutex_unlock(&proc_lock); +} + +static void udp_answer_thread_cleanup(void *data) +{ + pdnsd_free(data); + decrease_procs(); +} + +/* + * A thread opened to answer a query transmitted via udp. Data is a pointer to the structure udp_buf_t that + * contains the received data and various other parameters. + * After the query is answered, the thread terminates + * XXX: data must point to a correctly aligned buffer + */ +static void *udp_answer_thread(void *data) +{ + struct msghdr msg; + struct iovec v; + struct cmsghdr *cmsg; +#if defined(SRC_ADDR_DISC) + char ctrl[CMSG_SPACE(sizeof(pkt_info_t))]; +#endif + size_t rlen=((udp_buf_t *)data)->len; + unsigned udpmaxrespsize = UDP_BUFSIZE; + /* XXX: process_query is assigned to this, this mallocs, so this points to aligned memory */ + dns_msg_t *resp; + int rcode; + unsigned thrid; + pthread_cleanup_push(udp_answer_thread_cleanup, data); + THREAD_SIGINIT; + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + for(;;) { + pthread_mutex_lock(&proc_lock); + if (procs0 + if(debug_p) { + int err; + if ((err=pthread_setspecific(thrid_key, &thrid)) != 0) { + if(++da_misc_errs<=MISC_MAX_ERRS) + log_error("pthread_setspecific failed: %s",strerror(err)); + /* pdnsd_exit(); */ + } + } +#endif + + if (!(resp=process_query(((udp_buf_t *)data)->buf,&rlen,&udpmaxrespsize,&rcode))) { + /* + * A return value of NULL is a fatal error that prohibits even the sending of an error message. + * logging is already done. Just exit the thread now. + */ + pthread_exit(NULL); /* data freed by cleanup handler */ + } + pthread_cleanup_push(free, resp); + if (rlen>udpmaxrespsize) { + rlen=udpmaxrespsize; + resp->hdr.tc=1; /*set truncated bit*/ + } + DEBUG_MSG("Outbound msg len %li, tc=%u, rc=\"%s\"\n",(long)rlen,resp->hdr.tc,get_ename(rcode)); + + v.iov_base=(char *)&resp->hdr; + v.iov_len=rlen; + msg.msg_iov=&v; + msg.msg_iovlen=1; +#if (TARGET!=TARGET_CYGWIN) +#if defined(SRC_ADDR_DISC) + msg.msg_control=ctrl; + msg.msg_controllen=sizeof(ctrl); +#else + msg.msg_control=NULL; + msg.msg_controllen=0; +#endif + msg.msg_flags=0; /* to avoid warning message by Valgrind */ +#endif + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + + msg.msg_name=&((udp_buf_t *)data)->addr.sin4; + msg.msg_namelen=sizeof(struct sockaddr_in); +# if defined(SRC_ADDR_DISC) +# if (TARGET==TARGET_LINUX) + ((udp_buf_t *)data)->pi.pi4.ipi_spec_dst=((udp_buf_t *)data)->pi.pi4.ipi_addr; + cmsg=CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_pktinfo)); + cmsg->cmsg_level=SOL_IP; + cmsg->cmsg_type=IP_PKTINFO; + memcpy(CMSG_DATA(cmsg),&((udp_buf_t *)data)->pi.pi4,sizeof(struct in_pktinfo)); + msg.msg_controllen=CMSG_SPACE(sizeof(struct in_pktinfo)); +# else + cmsg=CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_addr)); + cmsg->cmsg_level=IPPROTO_IP; + cmsg->cmsg_type=IP_RECVDSTADDR; + memcpy(CMSG_DATA(cmsg),&((udp_buf_t *)data)->pi.ai4,sizeof(struct in_addr)); + msg.msg_controllen=CMSG_SPACE(sizeof(struct in_addr)); +# endif +# endif +# if DEBUG>0 + { + char buf[ADDRSTR_MAXLEN]; + + DEBUG_MSG("Answering to: %s", inet_ntop(AF_INET,&((udp_buf_t *)data)->addr.sin4.sin_addr,buf,ADDRSTR_MAXLEN)); +# if defined(SRC_ADDR_DISC) +# if (TARGET==TARGET_LINUX) + DEBUG_MSGC(", source address: %s\n", inet_ntop(AF_INET,&((udp_buf_t *)data)->pi.pi4.ipi_spec_dst,buf,ADDRSTR_MAXLEN)); +# else + DEBUG_MSGC(", source address: %s\n", inet_ntop(AF_INET,&((udp_buf_t *)data)->pi.ai4,buf,ADDRSTR_MAXLEN)); +# endif +# else + DEBUG_MSGC("\n"); +# endif + } +# endif /* DEBUG */ + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + + msg.msg_name=&((udp_buf_t *)data)->addr.sin6; + msg.msg_namelen=sizeof(struct sockaddr_in6); +# if defined(SRC_ADDR_DISC) + cmsg=CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len=CMSG_LEN(sizeof(struct in6_pktinfo)); + cmsg->cmsg_level=SOL_IPV6; + cmsg->cmsg_type=IPV6_PKTINFO; + memcpy(CMSG_DATA(cmsg),&((udp_buf_t *)data)->pi.pi6,sizeof(struct in6_pktinfo)); + msg.msg_controllen=CMSG_SPACE(sizeof(struct in6_pktinfo)); +# endif +# if DEBUG>0 + { + char buf[ADDRSTR_MAXLEN]; + + DEBUG_MSG("Answering to: %s", inet_ntop(AF_INET6,&((udp_buf_t *)data)->addr.sin6.sin6_addr,buf,ADDRSTR_MAXLEN)); +# if defined(SRC_ADDR_DISC) + DEBUG_MSGC(", source address: %s\n", inet_ntop(AF_INET6,&((udp_buf_t *)data)->pi.pi6.ipi6_addr,buf,ADDRSTR_MAXLEN)); +# else + DEBUG_MSGC("\n"); +# endif + } +# endif /* DEBUG */ + } +#endif + + /* Lock the socket, and clear the error flag before dropping the lock */ +#ifdef SOCKET_LOCKING + pthread_mutex_lock(&s_lock); +#endif + if (sendmsg(((udp_buf_t *)data)->sock,&msg,0)<0) { +#ifdef SOCKET_LOCKING + pthread_mutex_unlock(&s_lock); +#endif + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("Error in udp send: %s",strerror(errno)); + } + } else { + int tmp; + socklen_t sl=sizeof(tmp); + getsockopt(((udp_buf_t *)data)->sock, SOL_SOCKET, SO_ERROR, &tmp, &sl); +#ifdef SOCKET_LOCKING + pthread_mutex_unlock(&s_lock); +#endif + } + + pthread_cleanup_pop(1); /* free(resp) */ + pthread_cleanup_pop(1); /* free(data) */ + return NULL; +} + +int init_udp_socket() +{ + int sock; + int so=1; + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } sin; + socklen_t sinl; + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + if ((sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1) { + log_error("Could not open udp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin4,0,sizeof(struct sockaddr_in)); + sin.sin4.sin_family=AF_INET; + sin.sin4.sin_port=htons(global.port); + sin.sin4.sin_addr=global.a.ipv4; + SET_SOCKA_LEN4(sin.sin4); + sinl=sizeof(struct sockaddr_in); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + if ((sock=socket(PF_INET6,SOCK_DGRAM,IPPROTO_UDP))==-1) { + log_error("Could not open udp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin6,0,sizeof(struct sockaddr_in6)); + sin.sin6.sin6_family=AF_INET6; + sin.sin6.sin6_port=htons(global.port); + sin.sin6.sin6_flowinfo=IPV6_FLOWINFO; + sin.sin6.sin6_addr=global.a.ipv6; + SET_SOCKA_LEN6(sin.sin6); + sinl=sizeof(struct sockaddr_in6); + } +#endif + +#ifdef SRC_ADDR_DISC +# if (TARGET!=TARGET_LINUX) + if (run_ipv4) { +# endif + /* The following must be set on any case because it also applies for IPv4 packets sent to + * ipv6 addresses. */ +# if (TARGET==TARGET_LINUX ) + if (setsockopt(sock,SOL_IP,IP_PKTINFO,&so,sizeof(so))!=0) { +# else + if (setsockopt(sock,IPPROTO_IP,IP_RECVDSTADDR,&so,sizeof(so))!=0) { +# endif + log_error("Could not set options on udp socket: %s",strerror(errno)); + close(sock); + return -1; + } +# if (TARGET!=TARGET_LINUX) + } +# endif + +# ifdef ENABLE_IPV6 + if (!run_ipv4) { + if (setsockopt(sock,SOL_IPV6,IPV6_RECVPKTINFO,&so,sizeof(so))!=0) { + log_error("Could not set options on udp socket: %s",strerror(errno)); + close(sock); + return -1; + } + } +# endif +#endif + if (bind(sock,(struct sockaddr *)&sin,sinl)!=0) { + log_error("Could not bind to udp socket: %s",strerror(errno)); + close(sock); + return -1; + } + return sock; +} + +/* + * Listen on the specified port for udp packets and answer them (each in a new thread to be nonblocking) + * This was changed to support sending UDP packets with exactly the same source address as they were coming + * to us, as required by rfc2181. Although this is a sensible requirement, it is slightly more difficult + * and may introduce portability issues. + */ +void *udp_server_thread(void *dummy) +{ + int sock; + ssize_t qlen; + pthread_t pt; + udp_buf_t *buf; + struct msghdr msg; + struct iovec v; + struct cmsghdr *cmsg; + char ctrl[512]; +#if defined(ENABLE_IPV6) && (TARGET==TARGET_LINUX) + struct in_pktinfo sip; +#endif + /* (void)dummy; */ /* To inhibit "unused variable" warning */ + + THREAD_SIGINIT; + + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + sock=udp_socket; + + while (1) { + int udpbufsize= global.udpbufsize; + if (!(buf=(udp_buf_t *)pdnsd_calloc(1,sizeof(udp_buf_t)+udpbufsize))) { + if (++da_mem_errs<=MEM_MAX_ERRS) { + log_error("Out of memory in request handling."); + } + break; + } + + buf->sock=sock; + + v.iov_base=(char *)buf->buf; + v.iov_len=udpbufsize; + msg.msg_iov=&v; + msg.msg_iovlen=1; +#if (TARGET!=TARGET_CYGWIN) + msg.msg_control=ctrl; + msg.msg_controllen=sizeof(ctrl); +#endif + +#if defined(SRC_ADDR_DISC) +# ifdef ENABLE_IPV4 + if (run_ipv4) { + msg.msg_name=&buf->addr.sin4; + msg.msg_namelen=sizeof(struct sockaddr_in); + if ((qlen=recvmsg(sock,&msg,0))>=0) { + cmsg=CMSG_FIRSTHDR(&msg); + while(cmsg) { +# if (TARGET==TARGET_LINUX) + if (cmsg->cmsg_level==SOL_IP && cmsg->cmsg_type==IP_PKTINFO) { + memcpy(&buf->pi.pi4,CMSG_DATA(cmsg),sizeof(struct in_pktinfo)); + break; + } +# else + if (cmsg->cmsg_level==IPPROTO_IP && cmsg->cmsg_type==IP_RECVDSTADDR) { + memcpy(&buf->pi.ai4,CMSG_DATA(cmsg),sizeof(buf->pi.ai4)); + break; + } +# endif + cmsg=CMSG_NXTHDR(&msg,cmsg); + } + if (!cmsg) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("Could not discover udp destination address"); + } + goto free_buf_continue; + } + } else if (errno!=EINTR) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("error in UDP recv: %s", strerror(errno)); + } + } + } +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 { + msg.msg_name=&buf->addr.sin6; + msg.msg_namelen=sizeof(struct sockaddr_in6); + if ((qlen=recvmsg(sock,&msg,0))>=0) { + cmsg=CMSG_FIRSTHDR(&msg); + while(cmsg) { + if (cmsg->cmsg_level==SOL_IPV6 && cmsg->cmsg_type==IPV6_PKTINFO) { + memcpy(&buf->pi.pi6,CMSG_DATA(cmsg),sizeof(struct in6_pktinfo)); + break; + } + cmsg=CMSG_NXTHDR(&msg,cmsg); + } + if (!cmsg) { + /* We might have an IPv4 Packet incoming on our IPv6 port, so we also have to + * check for IPv4 sender addresses */ + cmsg=CMSG_FIRSTHDR(&msg); + while(cmsg) { +# if (TARGET==TARGET_LINUX) + if (cmsg->cmsg_level==SOL_IP && cmsg->cmsg_type==IP_PKTINFO) { + memcpy(&sip,CMSG_DATA(cmsg),sizeof(sip)); + IPV6_MAPIPV4(&sip.ipi_addr,&buf->pi.pi6.ipi6_addr); + buf->pi.pi6.ipi6_ifindex=sip.ipi_ifindex; + break; + } + /* FIXME: What about BSD? probably ok, but... */ +# endif + cmsg=CMSG_NXTHDR(&msg,cmsg); + } + if (!cmsg) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("Could not discover udp destination address"); + } + goto free_buf_continue; + } + } + } else if (errno!=EINTR) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("error in UDP recv: %s", strerror(errno)); + } + } + } +# endif +#else /* !SRC_ADDR_DISC */ +# ifdef ENABLE_IPV4 + if (run_ipv4) { + msg.msg_name=&buf->addr.sin4; + msg.msg_namelen=sizeof(struct sockaddr_in); + } +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 { + msg.msg_name=&buf->addr.sin6; + msg.msg_namelen=sizeof(struct sockaddr_in6); + } +# endif + qlen=recvmsg(sock,&msg,0); + if (qlen<0 && errno!=EINTR) { + if (++da_udp_errs<=UDP_MAX_ERRS) { + log_error("error in UDP recv: %s", strerror(errno)); + } + } +#endif /* SRC_ADDR_DISC */ + + if (qlen>=0) { + pthread_mutex_lock(&proc_lock); + if (qprocslen=qlen; + err=pthread_create(&pt,&attr_detached,udp_answer_thread,(void *)buf); + if(err==0) + continue; + if(++da_thrd_errs<=THRD_MAX_ERRS) + log_warn("pthread_create failed: %s",strerror(err)); + /* If thread creation failed, free resources associated with it. */ + pthread_mutex_lock(&proc_lock); + --qprocs; --spawned; + } + ++dropped; + pthread_mutex_unlock(&proc_lock); + } + free_buf_continue: + pdnsd_free(buf); + usleep_r(50000); + } + + udp_socket=-1; + close(sock); + udps_thrid=main_thrid; + if (tcp_socket==-1) + pdnsd_exit(); + return NULL; +} + +#ifndef NO_TCP_SERVER + +static void tcp_answer_thread_cleanup(void *csock) +{ + close(*((int *)csock)); + pdnsd_free(csock); + decrease_procs(); +} + +/* + * Process a dns query via tcp. The argument is a pointer to the socket. + */ +static void *tcp_answer_thread(void *csock) +{ + /* XXX: This should be OK, the original must be (and is) aligned */ + int sock=*((int *)csock); + unsigned thrid; + + pthread_cleanup_push(tcp_answer_thread_cleanup, csock); + THREAD_SIGINIT; + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + for(;;) { + pthread_mutex_lock(&proc_lock); + if (procs0 + if(debug_p) { + int err; + if ((err=pthread_setspecific(thrid_key, &thrid)) != 0) { + if(++da_misc_errs<=MISC_MAX_ERRS) + log_error("pthread_setspecific failed: %s",strerror(err)); + /* pdnsd_exit(); */ + } + } +#endif +#ifdef TCP_SUBSEQ + + /* rfc1035 says we should process multiple queries in succession, so we are looping until + * the socket is closed by the other side or by tcp timeout. + * This in fact makes DoSing easier. If that is your concern, you should disable pdnsd's + * TCP server.*/ + for(;;) +#endif + { + int rlen,olen; + size_t nlen; + unsigned char *buf; + dns_msg_t *resp; + +#ifdef NO_POLL + fd_set fds; + struct timeval tv; + FD_ZERO(&fds); + PDNSD_ASSERT(sock=2) { /* We need the id to send a valid reply. */ + dns_msg_t err; + mk_error_reply(((dns_hdr_t*)buf)->id, + olen>=3?((dns_hdr_t*)buf)->opcode:OP_QUERY, + RC_FORMAT, + &err.hdr); + err.len=htons(sizeof(dns_hdr_t)); + write_all(sock,&err,sizeof(err)); /* error anyway. */ + } + pthread_exit(NULL); /* buf freed and socket closed by cleanup handlers */ + } + olen += rv; + } + nlen=rlen; + if (!(resp=process_query(buf,&nlen,NULL,NULL))) { + /* + * A return value of NULL is a fatal error that prohibits even the sending of an error message. + * logging is already done. Just exit the thread now. + */ + pthread_exit(NULL); + } + pthread_cleanup_pop(1); /* free(buf) */ + pthread_cleanup_push(free,resp); + { + int err; size_t rsize; + resp->len=htons(nlen); + rsize=dnsmsghdroffset+nlen; + if ((err=write_all(sock,resp,rsize))!=rsize) { + DEBUG_MSG("Error while writing to TCP client: %s\n",err==-1?strerror(errno):"unknown error"); + pthread_exit(NULL); /* resp is freed and socket is closed by cleanup handlers */ + } + } + pthread_cleanup_pop(1); /* free(resp) */ + } + + /* socket is closed by cleanup handler */ + pthread_cleanup_pop(1); + return NULL; +} + +int init_tcp_socket() +{ + int sock; + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } sin; + socklen_t sinl; + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + if ((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) { + log_error("Could not open tcp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin4,0,sizeof(struct sockaddr_in)); + sin.sin4.sin_family=AF_INET; + sin.sin4.sin_port=htons(global.port); + sin.sin4.sin_addr=global.a.ipv4; + SET_SOCKA_LEN4(sin.sin4); + sinl=sizeof(struct sockaddr_in); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + if ((sock=socket(PF_INET6,SOCK_STREAM,IPPROTO_TCP))==-1) { + log_error("Could not open tcp socket: %s",strerror(errno)); + return -1; + } + memset(&sin.sin6,0,sizeof(struct sockaddr_in6)); + sin.sin6.sin6_family=AF_INET6; + sin.sin6.sin6_port=htons(global.port); + sin.sin6.sin6_flowinfo=IPV6_FLOWINFO; + sin.sin6.sin6_addr=global.a.ipv6; + SET_SOCKA_LEN6(sin.sin6); + sinl=sizeof(struct sockaddr_in6); + } +#endif + { + int so=1; + /* The SO_REUSEADDR socket option tells the kernel that even if this port + is busy (in the TIME_WAIT state), go ahead and reuse it anyway. If it + is busy, but with another state, we should get an address already in + use error. It is useful if pdnsd is shut down, and then restarted right + away while sockets are still active on its port. There is a slight risk + though. If unexpected data comes in, it may confuse pdnsd, but while + this is possible, it is not likely. + */ + if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&so,sizeof(so))) + log_warn("Could not set options on tcp socket: %s",strerror(errno)); + } + if (bind(sock,(struct sockaddr *)&sin,sinl)) { + log_error("Could not bind tcp socket: %s",strerror(errno)); + close(sock); + return -1; + } + return sock; +} + +/* + * Listen on the specified port for tcp connects and answer them (each in a new thread to be nonblocking) + */ +void *tcp_server_thread(void *p) +{ + int sock; + pthread_t pt; + int *csock; + + /* (void)p; */ /* To inhibit "unused variable" warning */ + + THREAD_SIGINIT; + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + sock=tcp_socket; + + if (listen(sock,5)) { + if (++da_tcp_errs<=TCP_MAX_ERRS) { + log_error("Could not listen on tcp socket: %s",strerror(errno)); + } + goto close_sock_return; + } + + while (1) { + if (!(csock=(int *)pdnsd_malloc(sizeof(int)))) { + if (++da_mem_errs<=MEM_MAX_ERRS) { + log_error("Out of memory in request handling."); + } + break; + } + if ((*csock=accept(sock,NULL,0))==-1) { + if (errno!=EINTR && ++da_tcp_errs<=TCP_MAX_ERRS) { + log_error("tcp accept failed: %s",strerror(errno)); + } + } else { + /* + * With creating a new thread, we follow recommendations + * in rfc1035 not to block + */ + pthread_mutex_lock(&proc_lock); + if (qprocs. +*/ + + +#ifndef DNS_ANSWER_H +#define DNS_ANSWER_H + +#include + +/* --- from main.c */ +extern pthread_t main_thrid,servstat_thrid,statsock_thrid,tcps_thrid,udps_thrid; +extern volatile int tcp_socket; +extern volatile int udp_socket; +/* --- */ + +int init_udp_socket(void); +int init_tcp_socket(void); +void start_dns_servers(void); +int report_thread_stat(int f); + +#endif diff --git a/service/src/main/jni/pdnsd/src/dns_query.c b/service/src/main/jni/pdnsd/src/dns_query.c new file mode 100644 index 0000000..0b6c9c0 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/dns_query.c @@ -0,0 +1,3798 @@ +/* dns_query.c - Execute outgoing dns queries and write entries to cache + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#ifdef HAVE_SYS_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "consts.h" +#include "ipvers.h" +#include "dns_query.h" +#include "cache.h" +#include "dns.h" +#include "conff.h" +#include "servers.h" +#include "helpers.h" +#include "netdev.h" +#include "error.h" +#include "debug.h" + + +#if defined(NO_TCP_QUERIES) && M_PRESET!=UDP_ONLY +# error "You may not define NO_TCP_QUERIES when M_PRESET is not set to UDP_ONLY" +#endif +#if defined(NO_UDP_QUERIES) && M_PRESET!=TCP_ONLY +# error "You may not define NO_UDP_QUERIES when M_PRESET is not set to TCP_ONLY" +#endif + +/* data type to hold lists of IP addresses (both v4 and v6) + The allocated size should be: + sizeof(rejectlist_t) + na4*sizeof(addr4maskpair_t) + na6*sizeof(addr6maskpair_t) +*/ +typedef struct rejectlist_s { + struct rejectlist_s *next; + short policy; + short inherit; + int na4; +#if ALLOW_LOCAL_AAAA + int na6; + addr6maskpair_t rdata[0]; /* dummy array for alignment */ +#else + addr4maskpair_t rdata[0]; +#endif +} rejectlist_t; + +/* --- structures and state constants for parallel query */ +typedef struct { + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } a; +#ifdef ENABLE_IPV6 + struct in_addr a4fallback; +#endif + time_t timeout; + unsigned short flags; + short state; + short qm; + char nocache; + char auth_serv; + char lean_query; + char edns_query; + char needs_testing; + char trusted; + char aa; + char tc; + char ra; + char failed; + const unsigned char *nsdomain; + rejectlist_t *rejectlist; + /* internal state for p_exec_query */ + int sock; +#if 0 + dns_cent_t nent; + dns_cent_t servent; +#endif + unsigned short transl; + unsigned short recvl; +#ifndef NO_TCP_QUERIES + int iolen; /* number of bytes written or read up to now */ +#endif + dns_msg_t *msg; + dns_hdr_t *recvbuf; + unsigned short myrid; + int s_errno; +} query_stat_t; +typedef DYNAMIC_ARRAY(query_stat_t) *query_stat_array; + +/* Some macros for handling data in reject lists + Perhaps we should use inline functions instead of macros. +*/ +#define have_rejectlist(st) ((st)->rejectlist!=NULL) +#define inherit_rejectlist(st) ((st)->rejectlist && (st)->rejectlist->inherit) +#define reject_policy(st) ((st)->rejectlist->policy) +#define nreject_a4(st) ((st)->rejectlist->na4) +#if ALLOW_LOCAL_AAAA +#define nreject_a6(st) ((st)->rejectlist->na6) +#define rejectlist_a6(st) ((addr6maskpair_t *)(st)->rejectlist->rdata) +#define rejectlist_a4(st) ((addr4maskpair_t *)(rejectlist_a6(st)+nreject_a6(st))) +#else +#define rejectlist_a4(st) ((addr4maskpair_t *)(st)->rejectlist->rdata) +#endif + +#define QS_INITIAL 0 /* This is the initial state. Set this before starting. */ + +#define QS_TCPINITIAL 1 /* Start a TCP query. */ +#define QS_TCPWRITE 2 /* Waiting to write data. */ +#define QS_TCPREAD 3 /* Waiting to read data. */ + +#define QS_UDPINITIAL 4 /* Start a UDP query */ +#define QS_UDPRECEIVE 5 /* UDP query transmitted, waiting for response. */ + +#define QS_QUERY_CASES case QS_TCPINITIAL: case QS_TCPWRITE: case QS_TCPREAD: case QS_UDPINITIAL: case QS_UDPRECEIVE + +#define QS_CANCELED 7 /* query was started, but canceled before completion */ +#define QS_DONE 8 /* done, resources freed, result is in stat_t */ + + +/* Events to be polled/selected for */ +#define QS_WRITE_CASES case QS_TCPWRITE +#define QS_READ_CASES case QS_TCPREAD: case QS_UDPRECEIVE + +/* + * This is for error handling to prevent spewing the log files. + * Races do not really matter here, so no locks. + */ +#define MAXPOLLERRS 10 +static volatile unsigned long poll_errs=0; + +#define SOCK_ADDR(p) ((struct sockaddr *) &(p)->a) + +#ifdef SIN_LEN +#undef SIN_LEN +#endif + +#define SIN_LEN SEL_IPVER(sizeof(struct sockaddr_in),sizeof(struct sockaddr_in6)) +#define PDNSD_A(p) SEL_IPVER(((pdnsd_a *) &(p)->a.sin4.sin_addr),((pdnsd_a *) &(p)->a.sin6.sin6_addr)) + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +typedef DYNAMIC_ARRAY(dns_cent_t) *dns_cent_array; + + +/* + * Take the data from an RR and add it to an array of cache entries. + * The return value will be RC_OK in case of success, + * RC_SERVFAIL in case there is a problem with inconsistent ttl timestamps + * or RC_FATALERR in case of a memory allocation failure. + */ +static int rr_to_cache(dns_cent_array *centa, unsigned char *oname, int tp, time_t ttl, + unsigned dlen, void *data, unsigned flags, time_t queryts) +{ + int i,n; + dns_cent_t *cent; + + n=DA_NEL(*centa); + for(i=0;iqname,oname)) { + int retval=RC_OK; + /* We already have an entry in the array for this name. add_cent_rr is sufficient. + However, make sure there are no double records. This is done by add_cent_rr */ +#ifdef RFC2181_ME_HARDER + rr_set_t *rrset= getrrset(cent,tp); + if (rrset && rrset->ttl!=ttl) + retval= RC_SERVFAIL; +#endif + return add_cent_rr(cent,tp,ttl,queryts,flags,dlen,data DBG1)? retval: RC_FATALERR; + } + } + + /* Add a new entry to the array for this name. */ + if (!(*centa=DA_GROW1_F(*centa,free_cent0))) + return RC_FATALERR; + cent=&DA_LAST(*centa); + if (!init_cent(cent,oname, 0, 0, 0 DBG1)) { + *centa=DA_RESIZE(*centa,n); + return RC_FATALERR; + } + return add_cent_rr(cent,tp,ttl,queryts,flags,dlen,data DBG1)? RC_OK: RC_FATALERR; +} + +/* + * Takes a pointer (ptr) to a buffer with recnum rrs,decodes them and enters + * them into an array of cache entries. *ptr is modified to point after the last + * rr, and *lcnt is decremented by the size of the rrs. + * + * *numopt is incremented with the number of OPT pseudo RRs found (should be at most one). + * The structure pointed to by ep is filled with the information of the first OPT pseudo RR found, + * but only if *numopt was set to zero before the call. + * + * The return value will be either RC_OK (which indicates success), + * or one of the failure codes RC_FORMAT, RC_TRUNC, RC_SERVFAIL or RC_FATALERR + * (the latter indicates a memory allocation failure). +*/ +static int rrs2cent(unsigned char *msg, size_t msgsz, unsigned char **ptr, size_t *lcnt, int recnum, + unsigned flags, time_t queryts, dns_cent_array *centa, int *numopt, edns_info_t *ep) +{ + int rc, retval=RC_OK; + int i; + uint16_t type,class; uint32_t ttl; uint16_t rdlength; + + for (i=0;i0 + if(oname[0]!=0) { + DEBUG_MSG("rrs2cent: name in OPT record not empty!\n"); + } +#endif + ep->udpsize= class; + ep->rcode= ((uint16_t)ttlp[0]<<4) | ((dns_hdr_t *)msg)->rcode; + ep->version= ttlp[1]; + ep->do_flg= (ttlp[2]>>7)&1; +#if DEBUG>0 + if(debug_p) { + unsigned int Zflags= ((uint16_t)ttlp[2]<<8) | ttlp[3]; + if(Zflags & 0x7fff) { + DEBUG_MSG("rrs2cent: Z field contains unknown nonzero bits (%04x).\n", + Zflags); + } + } + if(rdlength) { + DEBUG_MSG("rrs2cent: RDATA field in OPT record not empty!\n"); + } +#endif + } + else { + DEBUG_MSG("rrs2cent: ingnoring surplus OPT record.\n"); + } + } + else if (!(PDNSD_NOT_CACHED_TYPE(type) || class!=C_IN)) { + /* Some types contain names that may be compressed, so these need to be processed. + * The other records are taken as they are. */ + + size_t blcnt=rdlength; + unsigned char *bptr=*ptr; /* make backup for decompression, because rdlength is the + authoritative record length and pointer and size will be + modified by decompress_name. */ + unsigned char *nptr; + unsigned int slen; + + switch (type) { + case T_A: + /* Validate types we use internally */ + if(rdlength!=4) goto invalid_length; + goto default_case; + + case T_CNAME: + case T_MB: + case T_MD: + case T_MF: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + { + unsigned char db[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, db, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, len, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; + +#if IS_CACHED_MINFO || IS_CACHED_RP +#if IS_CACHED_MINFO + case T_MINFO: +#endif +#if IS_CACHED_RP + case T_RP: +#endif + { + unsigned char db[DNSNAMEBUFSIZE+DNSNAMEBUFSIZE]; + nptr=db; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* PDNSD_ASSERT(len + DNSNAMEBUFSIZE <= sizeof(db), "T_MINFO/T_RP: buffer limit reached"); */ + nptr+=len; + slen=len; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif + case T_MX: +#if IS_CACHED_AFSDB + case T_AFSDB: +#endif +#if IS_CACHED_RT + case T_RT: +#endif +#if IS_CACHED_KX + case T_KX: +#endif + { + unsigned char db[2+DNSNAMEBUFSIZE]; + if (blcnt<2) + goto record_too_short; + memcpy(db,bptr,2); /* copy the preference field*/ + blcnt-=2; + bptr+=2; + nptr=db+2; + slen=2; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; + + case T_SOA: + { + unsigned char db[DNSNAMEBUFSIZE+DNSNAMEBUFSIZE+20]; + nptr=db; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* PDNSD_ASSERT(len + DNSNAMEBUFSIZE <= sizeof(db), "T_SOA: buffer limit reached"); */ + nptr+=len; + slen=len; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + nptr+=len; + slen+=len; + /* PDNSD_ASSERT(slen + 20 <= sizeof(db), "T_SOA: buffer limit reached"); */ + if (blcnt<20) + goto record_too_short; + memcpy(nptr,bptr,20); /*copy the rest of the SOA record*/ + blcnt-=20; + slen+=20; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#if IS_CACHED_AAAA + case T_AAAA: + /* Validate types we use internally */ + if(rdlength!=16) goto invalid_length; + goto default_case; +#endif +#if IS_CACHED_PX + case T_PX: + { + unsigned char db[2+DNSNAMEBUFSIZE+DNSNAMEBUFSIZE]; + if (blcnt<2) + goto record_too_short; + memcpy(db,bptr,2); /* copy the preference field*/ + blcnt-=2; + bptr+=2; + nptr=db+2; + slen=2; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* PDNSD_ASSERT(len + DNSNAMEBUFSIZE <= sizeof(db), "T_PX: buffer limit reached"); */ + nptr+=len; + slen+=len; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /* nptr+=len; */ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_SRV + case T_SRV: + { + unsigned char db[6+DNSNAMEBUFSIZE]; + if (blcnt<6) + goto record_too_short; + memcpy(db,bptr,6); + blcnt-=6; + bptr+=6; + nptr=db+6; + slen=6; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_NXT + case T_NXT: + { + unsigned char db[1040]; + nptr=db; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + nptr+=len; + slen=len+blcnt; + if (slen > sizeof(db)) + goto buffer_overflow; + memcpy(nptr,bptr,blcnt); + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_NAPTR + case T_NAPTR: + { + int j; + unsigned char db[4 + 3*256 + DNSNAMEBUFSIZE]; + nptr=db; + /* + * After the preference field, three text strings follow, the maximum length being 255 + * characters for each (this is ensured by the type of *bptr), plus one length byte for + * each, so 3 * 256 = 786 in total. In addition, the name below is up to DNSNAMEBUFSIZE characters + * in size, and the preference field is another 4 bytes in size, so the total length + * that can be taken up is 1028 characters. This means that the whole record will always + * fit into db. + */ + len=4; /* also copy the preference field*/ + for (j=0;j<3;j++) { + if (len>=blcnt) + goto record_too_short; + len += ((unsigned)bptr[len])+1; + } + if(len>blcnt) + goto record_too_short; + memcpy(nptr,bptr,len); + blcnt-=len; + bptr+=len; + nptr+=len; + slen=len; + + /* PDNSD_ASSERT(slen+DNSNAMEBUFSIZE <= sizeof(db), "T_NAPTR: buffer limit reached (name)"); */ + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nptr, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + /*nptr+=len;*/ + slen+=len; + if (blcnt!=0) + goto trailing_junk; + if ((rc=rr_to_cache(centa, oname, type, ttl, slen, db, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_IPSECKEY + case T_IPSECKEY: + { + unsigned gwtp; + /* An IPSECKEY record can contain a domain name, so we do some sanity checks just to be sure. */ + if(blcnt<3) goto record_too_short; + gwtp= bptr[1]; + blcnt -= 3; + bptr += 3; + switch(gwtp) { + case 0: goto default_case; + case 1: /* There should be enough room for IPv4 address. */ + if(blcnt<4) goto record_too_short; + goto default_case; + case 2: /* There should be enough room for IPv6 address. */ + if(blcnt<16) goto record_too_short; + goto default_case; + case 3: /* Check that domain name is not compressed. */ + if(isnormalencdomname(bptr,blcnt)) goto default_case; + /* It appears the name is compressed even though RFC 4025 + says it shouldn't be. For the sake of flexibility, we + try to decompress it anyway. */ + { + unsigned char *rbuf, nmbuf[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nmbuf, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + slen=3+len+blcnt; + rbuf=malloc(slen); + if(!rbuf) return RC_FATALERR; + nptr=mempcpy(rbuf,*ptr,3); + nptr=mempcpy(nptr,nmbuf,len); + memcpy(nptr,bptr,blcnt); + rc=rr_to_cache(centa, oname, type, ttl, slen, rbuf, flags,queryts); + free(rbuf); + if(rc!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; + default: + DEBUG_MSG("rrs2cent: %s record contains unsupported gateway type (%u).\n",getrrtpname(type),gwtp); + return RC_FORMAT; + } + } + break; +#endif +#if IS_CACHED_RRSIG + case T_RRSIG: + /* An RRSIG record contains a domain name, so we do some sanity checks just to be sure. */ + if(blcnt<18) goto record_too_short; + blcnt -= 18; + bptr += 18; + if(isnormalencdomname(bptr,blcnt)) goto default_case; + /* It appears the name is compressed even though RFC 4034 + says it shouldn't be. For the sake of flexibility, we + try to decompress it anyway. */ + { + unsigned char *rbuf, nmbuf[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nmbuf, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + slen=18+len+blcnt; + rbuf=malloc(slen); + if(!rbuf) return RC_FATALERR; + nptr=mempcpy(rbuf,*ptr,18); + nptr=mempcpy(nptr,nmbuf,len); + memcpy(nptr,bptr,blcnt); + rc=rr_to_cache(centa, oname, type, ttl, slen, rbuf, flags,queryts); + free(rbuf); + if(rc!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif +#if IS_CACHED_NSEC + case T_NSEC: + /* An NSEC record contains a domain name, so we do some sanity checks just to be sure. */ + if(isnormalencdomname(bptr,blcnt)) goto default_case; + /* It appears the name is compressed even though RFC 4034 + says it shouldn't be. For the sake of flexibility, we + try to decompress it anyway. */ + { + unsigned char *rbuf, nmbuf[DNSNAMEBUFSIZE]; + if ((rc=decompress_name(msg, msgsz, &bptr, &blcnt, nmbuf, &len))!=RC_OK) + return rc==RC_TRUNC?RC_FORMAT:rc; + slen=len+blcnt; + rbuf=malloc(slen); + if(!rbuf) return RC_FATALERR; + nptr=mempcpy(rbuf,nmbuf,len); + memcpy(nptr,bptr,blcnt); + rc=rr_to_cache(centa, oname, type, ttl, slen, rbuf, flags,queryts); + free(rbuf); + if(rc!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + break; +#endif + default: + default_case: + if ((rc=rr_to_cache(centa, oname, type, ttl, rdlength, *ptr, flags,queryts))!=RC_OK) { + if(rc==RC_FATALERR) + return rc; + retval=rc; + } + } + } + else { + /* skip otherwise */ + DEBUG_MSG("rrs2cent: ignoring record of type %s (%d), class %s (%d).\n", + getrrtpname(type), type, + class==C_IN?"IN":"[unknown]", class); + } + + *lcnt -= rdlength; + *ptr += rdlength; + } + return retval; + + trailing_junk: + DEBUG_MSG("rrs2cent: %s record has trailing junk.\n",getrrtpname(type)); + return RC_FORMAT; + + record_too_short: + DEBUG_MSG("rrs2cent: %s record too short.\n",getrrtpname(type)); + return RC_FORMAT; + + buffer_overflow: + DEBUG_MSG("rrs2cent: buffer too small to process %s record.\n",getrrtpname(type)); + return RC_FORMAT; + + invalid_length: + DEBUG_MSG("rrs2cent: %s record has length %u.\n",getrrtpname(type),rdlength); + return RC_FORMAT; +} + +/* + * Try to bind the socket to a port in the given port range. Returns 1 on success, or 0 on failure. + */ +static int bind_socket(int s) +{ + int query_port_start=global.query_port_start,query_port_end=global.query_port_end; + + /* + * -1, as a special value for query_port_start, denotes that we let the kernel select + * a port when we first use the socket, which used to be the default. + */ + if (query_port_start >= 0) { + union { +#ifdef ENABLE_IPV4 + struct sockaddr_in sin4; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sin6; +#endif + } sin; + socklen_t sinl; + int prt, pstart, range = query_port_end-query_port_start+1, m=0xffff; + unsigned try1,try2, maxtry2; + + if (range<=0 || range>0x10000) { + log_warn("Illegal port range in %s line %d, dropping query!\n",__FILE__,__LINE__); + return 0; + } + if(range<=0x8000) { + /* Find the smallest power of 2 >= range. */ + for(m=1; m= range. */ + for(try1=0;;) { + prt= get_rand16()&m; + if(prt=0x10000) { + log_warn("Cannot get random number < range" + " after %d tries in %s line %d," + " bad random number generator?\n", + try1,__FILE__,__LINE__); + return 0; + } + } + prt += query_port_start; + + for(pstart=prt;;) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + memset(&sin.sin4,0,sizeof(struct sockaddr_in)); + sin.sin4.sin_family=AF_INET; + sin.sin4.sin_port=htons(prt); + sin.sin4.sin_addr=global.out_a.ipv4; + SET_SOCKA_LEN4(sin.sin4); + sinl=sizeof(struct sockaddr_in); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + memset(&sin.sin6,0,sizeof(struct sockaddr_in6)); + sin.sin6.sin6_family=AF_INET6; + sin.sin6.sin6_port=htons(prt); + sin.sin6.sin6_flowinfo=IPV6_FLOWINFO; + sin.sin6.sin6_addr=global.out_a.ipv6; + SET_SOCKA_LEN6(sin.sin6); + sinl=sizeof(struct sockaddr_in6); + } +#endif + if (bind(s,(struct sockaddr *)&sin,sinl)==-1) { + if (errno!=EADDRINUSE && + errno!=EADDRNOTAVAIL) { /* EADDRNOTAVAIL should not happen here... */ + log_warn("Could not bind to socket: %s\n", strerror(errno)); + return 0; + } + /* If the address is in use, we continue. */ + } else + goto done; + + if(++try2>=maxtry2) { + /* It is possible we missed the free ports by chance, + try scanning the whole range. */ + if (++prt>query_port_end) + prt=query_port_start; + if (prt==pstart) { + /* Wrapped around, scanned the whole range. Give up. */ + log_warn("Out of ports in the range" + " %d-%d, dropping query!\n", + query_port_start,query_port_end); + return 0; + } + } + else /* Try new random number */ + break; + } + } + } +done: + return 1; +} + + +inline static void *realloc_or_cleanup(void *ptr,size_t size) +{ + void *retval=pdnsd_realloc(ptr,size); + if(!retval) + pdnsd_free(ptr); + return retval; +} + +#if defined(NO_TCP_QUERIES) +# define USE_UDP(st) 1 +#elif defined(NO_UDP_QUERIES) +# define USE_UDP(st) 0 +#else /* !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) */ +# define USE_UDP(st) ((st)->qm==UDP_ONLY || (st)->qm==UDP_TCP) + +/* These functions will be used in case a TCP query might fail and we want to try again using UDP. */ + +# define tentative_tcp_query(st) ((st)->qm==TCP_UDP && ((st)->state==QS_TCPWRITE || ((st)->state==QS_TCPREAD && (st)->iolen==0))) + +inline static void switch_to_udp(query_stat_t *st) +{ + st->qm=UDP_ONLY; + st->myrid=get_rand16(); + st->msg->hdr.id=htons(st->myrid); + st->state=QS_UDPINITIAL; + /* st->failed=0; */ +} + +/* This function will be used in case a UDP reply was truncated and we want to try again using TCP. */ + +inline static void switch_to_tcp(query_stat_t *st) +{ + /* PDNSD_ASSERT(st->state==QS_INITIAL || st->state==QS_DONE || st->state==QS_CANCELED, + "Attempt to switch to TCP while a query is in progress."); */ + st->qm=TCP_ONLY; + st->state=QS_INITIAL; + st->failed=0; +} +#endif + + +/* ------ following is the parallel query code. + * It has been observed that a whole lot of name servers are just damn lame, with response time + * of about 1 min. If that slow one is by chance the first server we try, serializing the tries is quite + * sub-optimal. Also when doing serial queries, the timeout values given in the config will add up, which + * is not the Right Thing. Now that serial queries are in place, this is still true for CNAME recursion, + * and for recursion in quest for the holy AA, but not totally for querying multiple servers. + * The impact on network bandwith should be only marginal (given todays bandwith). + * + * The actual strategy is to do (max) PAR_QUERIES parallel queries, and, if these time out or fail, do again + * that number of queries, until we are successful or there are no more servers to query. + * Since the memory footprint of a thread is considerably large on some systems, and because we have better + * control, we will do the parallel queries multiplexed in one thread. + */ + +/* The query state machine that is called from p_exec_query. This is called once for initialization (state + * QS_TCPINITIAL or QS_UDPINITIAL is preset), and the state that it gives back may either be state QS_DONE, + * in which case it must return a return code other than -1 and is called no more for this server + * (except perhaps in UDP mode if TCP failed). If p_query_sm returns -1, then the state machine is in a read + * or write state, and a function higher up the calling chain can setup a poll() or select() together with st->sock. + * If that poll/select is succesful for that socket, p_exec_query is called again and will hand over to p_query_sm. + * So, you can assume that read(), write() and recvfrom() will not block at the start of a state handling when you + * have returned -1 (which means "call again") as last step of the last state handling. */ +static int p_query_sm(query_stat_t *st) +{ + int retval=RC_SERVFAIL,rv; + +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + tryagain: +#endif + switch (st->state){ + /* TCP query code */ +#ifndef NO_TCP_QUERIES + case QS_TCPINITIAL: + if ((st->sock=socket(PDNSD_PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) { + DEBUG_MSG("Could not open socket: %s\n", strerror(errno)); + break; + } + /* sin4 or sin6 is intialized, hopefully. */ + + /* maybe bind */ + if (!bind_socket(st->sock)) { + close(st->sock); + break; + } + + /* transmit query by tcp*/ + /* make the socket non-blocking */ + { + int oldflags = fcntl(st->sock, F_GETFL, 0); + if (oldflags == -1 || fcntl(st->sock,F_SETFL,oldflags|O_NONBLOCK)==-1) { + DEBUG_PDNSDA_MSG("fcntl error while trying to make socket to %s non-blocking: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + } + st->iolen=0; +#ifdef ENABLE_IPV6 + retry_tcp_connect: +#endif + if (connect(st->sock,SOCK_ADDR(st),SIN_LEN)==-1) { + if (errno==EINPROGRESS || errno==EPIPE) { + st->state=QS_TCPWRITE; + /* st->event=QEV_WRITE; */ /* wait for writability; the connect is then done */ + return -1; + } else if (errno==ECONNREFUSED) { + st->s_errno=errno; + DEBUG_PDNSDA_MSG("TCP connection refused by %s\n", PDNSDA2STR(PDNSD_A(st))); + close(st->sock); + goto tcp_failed; /* We may want to try again using UDP */ + } else { + /* Since immediate connect() errors do not cost any time, we do not try to switch the + * server status to offline */ +#ifdef ENABLE_IPV6 + /* if IPv6 connectivity is for some reason unavailable, perhaps the + IPv4 fallback address can still be reached. */ + if(!run_ipv4 && (errno==ENETUNREACH || errno==ENETDOWN) + && st->a4fallback.s_addr!=INADDR_ANY) + { +#if DEBUG>0 + char abuf[ADDRSTR_MAXLEN]; + DEBUG_PDNSDA_MSG("Connecting to %s failed: %s, retrying with IPv4 address %s\n", + PDNSDA2STR(PDNSD_A(st)),strerror(errno), + inet_ntop(AF_INET,&st->a4fallback,abuf,sizeof(abuf))); +#endif + IPV6_MAPIPV4(&st->a4fallback,&st->a.sin6.sin6_addr); + st->a4fallback.s_addr=INADDR_ANY; + goto retry_tcp_connect; + } +#endif + DEBUG_PDNSDA_MSG("Error while connecting to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + } + st->state=QS_TCPWRITE; + /* st->event=QEV_WRITE; */ + /* fall through in case of not EINPROGRESS */ + case QS_TCPWRITE: + { + int rem= dnsmsghdroffset + st->transl - st->iolen; + if(rem>0) { + rv=write(st->sock,((unsigned char*)st->msg)+st->iolen,rem); + if(rv==-1) { + if(errno==EWOULDBLOCK) + return -1; + st->s_errno=errno; + close(st->sock); + if (st->iolen==0 && + (st->s_errno==ECONNREFUSED || st->s_errno==ECONNRESET || + st->s_errno==EPIPE)) + { + /* This error may be delayed from connect() */ + DEBUG_PDNSDA_MSG("TCP connection to %s failed: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(st->s_errno)); + goto tcp_failed; /* We may want to try again using UDP */ + } + DEBUG_PDNSDA_MSG("Error while sending data to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(st->s_errno)); + break; + } + st->iolen += rv; + if(rvstate=QS_TCPREAD; + st->iolen=0; + /* st->event=QEV_READ; */ + /* fall through */ + case QS_TCPREAD: + if(st->iolen==0) { + uint16_t recvl_net; + rv=read(st->sock,&recvl_net,sizeof(recvl_net)); + if(rv==-1 && errno==EWOULDBLOCK) + return -1; + if(rv!=sizeof(recvl_net)) + goto error_receiv_data; + st->iolen=rv; + st->recvl=ntohs(recvl_net); + if(!(st->recvbuf=(dns_hdr_t *)realloc_or_cleanup(st->recvbuf,st->recvl))) { + close(st->sock); + DEBUG_MSG("Out of memory in query.\n"); + retval=RC_FATALERR; + break; + } + } + { + int offset=st->iolen-sizeof(uint16_t); + int rem=st->recvl-offset; + if(rem>0) { + rv=read(st->sock,((unsigned char*)st->recvbuf)+offset,rem); + if(rv==-1) { + if(errno==EWOULDBLOCK) + return -1; + goto error_receiv_data; + } + if(rv==0) + goto error_receiv_data; /* unexpected EOF */ + st->iolen += rv; + if(rvsock); + st->state=QS_DONE; + return RC_OK; + error_receiv_data: + if(rv==-1) st->s_errno=errno; + DEBUG_PDNSDA_MSG("Error while receiving data from %s: %s\n", PDNSDA2STR(PDNSD_A(st)), + rv==-1?strerror(errno):(rv==0 && st->iolen==0)?"no data":"incomplete data"); + close(st->sock); + tcp_failed: +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if(st->qm==TCP_UDP) { + switch_to_udp(st); + DEBUG_PDNSDA_MSG("TCP query to %s failed. Trying to use UDP.\n", PDNSDA2STR(PDNSD_A(st))); + goto tryagain; + } +#endif + break; +#endif + +#ifndef NO_UDP_QUERIES + /* UDP query code */ + case QS_UDPINITIAL: + if ((st->sock=socket(PDNSD_PF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1) { + DEBUG_MSG("Could not open socket: %s\n", strerror(errno)); + break; + } + + /* maybe bind */ + if (!bind_socket(st->sock)) { + close(st->sock); + break; + } + + /* connect */ +#ifdef ENABLE_IPV6 + retry_udp_connect: +#endif + if (connect(st->sock,SOCK_ADDR(st),SIN_LEN)==-1) { + if (errno==ECONNREFUSED) st->s_errno=errno; +#ifdef ENABLE_IPV6 + /* if IPv6 connectivity is for some reason unavailable, perhaps the + IPv4 fallback address can still be reached. */ + else if(!run_ipv4 && (errno==ENETUNREACH || errno==ENETDOWN) + && st->a4fallback.s_addr!=INADDR_ANY) + { +#if DEBUG>0 + char abuf[ADDRSTR_MAXLEN]; + DEBUG_PDNSDA_MSG("Connecting to %s failed: %s, retrying with IPv4 address %s\n", + PDNSDA2STR(PDNSD_A(st)),strerror(errno), + inet_ntop(AF_INET,&st->a4fallback,abuf,sizeof(abuf))); +#endif + IPV6_MAPIPV4(&st->a4fallback,&st->a.sin6.sin6_addr); + st->a4fallback.s_addr=INADDR_ANY; + goto retry_udp_connect; + } +#endif + DEBUG_PDNSDA_MSG("Error while connecting to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + + /* transmit query by udp*/ + /* send will hopefully not block on a freshly opened socket (the buffer + * must be empty) */ + if (send(st->sock,&st->msg->hdr,st->transl,0)==-1) { + st->s_errno=errno; + DEBUG_PDNSDA_MSG("Error while sending data to %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + st->state=QS_UDPRECEIVE; + /* st->event=QEV_READ; */ + return -1; + case QS_UDPRECEIVE: + { + int udpbufsize= (st->edns_query?global.udpbufsize:UDP_BUFSIZE); + if(!(st->recvbuf=(dns_hdr_t *)realloc_or_cleanup(st->recvbuf,udpbufsize))) { + close(st->sock); + DEBUG_MSG("Out of memory in query.\n"); + retval=RC_FATALERR; + break; + } + if ((rv=recv(st->sock,st->recvbuf,udpbufsize,0))==-1) { + st->s_errno=errno; + DEBUG_PDNSDA_MSG("Error while receiving data from %s: %s\n", PDNSDA2STR(PDNSD_A(st)),strerror(errno)); + close(st->sock); + break; + } + st->recvl=rv; + if (st->recvlrecvbuf->id)!=st->myrid) { + DEBUG_MSG("Bad answer received. Ignoring it.\n"); + /* no need to care about timeouts here. That is done at an upper layer. */ + st->state=QS_UDPRECEIVE; + /* st->event=QEV_READ; */ + return -1; + } + close(st->sock); + st->state=QS_DONE; + return RC_OK; + } +#endif + } + + /* If we get here, something has gone wrong. */ + st->state=QS_DONE; + return retval; /* should be either RC_SERVFAIL or RC_FATALERR */ +} + +static dns_cent_t *lookup_cent_array(dns_cent_array ca, const unsigned char *nm) +{ + int i,n=DA_NEL(ca); + for(i=0;iqname,nm)) + return ce; + } + return NULL; +} + +/* Extract the minimum ttl field from the SOA record stored in an rr bucket. */ +static time_t soa_minimum(rr_bucket_t *rrs) +{ + uint32_t minimum; + unsigned char *p=(unsigned char *)(rrs->data); + + /* Skip owner and maintainer. Lengths are validated in cache. */ + p=skiprhn(skiprhn(p)); + /* Skip serial, refresh, retry, expire fields. */ + p += 4*sizeof(uint32_t); + GETINT32(minimum,p); + return minimum; +} + +/* + * The function that will actually execute a query. It takes a state structure in st. + * st->state must be set to QS_INITIAL before calling. + * This may return one of the RC_* codes, where RC_OK indicates success, the other + * RC codes indicate the appropriate errors. -1 is the return value that indicates that + * you should call p_exec_query again with the same state for the result until you get + * a return value >0. Alternatively, call p_cancel_query to cancel it. + * Timeouts are already handled by this function. + * Any records that the query has yielded and that are not a direct answer to the query + * (i.e. are records for other domains) are added to the cache, while the direct answers + * are returned in ent. + * All ns records, to whomever they might belong, are additionally returned in the ns list. + * Free it when done. + * This function calls another query state machine function that supports TCP and UDP. + * + * If you want to tell me that this function has a truly ugly coding style, ah, well... + * You are right, somehow, but I feel it is conceptually elegant ;-) + */ +static int p_exec_query(dns_cent_t **entp, const unsigned char *name, int thint, + query_stat_t *st, dlist *ns, unsigned char *c_soa) +{ + int rv,rcode; + unsigned short rd; + + switch (st->state){ + case QS_INITIAL: { + size_t transl,allocsz; + unsigned int rrnlen=0; + + allocsz= sizeof(dns_msg_t); + if(name) { + rrnlen=rhnlen(name); + allocsz += rrnlen+4; + if(st->edns_query) + allocsz += sizeof_opt_pseudo_rr; + } + st->msg=(dns_msg_t *)pdnsd_malloc(allocsz); + if (!st->msg) { + st->state=QS_DONE; + return RC_FATALERR; /* unrecoverable error */ + } + st->myrid=get_rand16(); + st->msg->hdr.id=htons(st->myrid); + st->msg->hdr.qr=QR_QUERY; + st->msg->hdr.opcode=OP_QUERY; + st->msg->hdr.aa=0; + st->msg->hdr.tc=0; + st->msg->hdr.rd=(name && st->trusted); + st->msg->hdr.ra=0; + st->msg->hdr.z=0; + st->msg->hdr.ad=0; + st->msg->hdr.cd=0; + st->msg->hdr.rcode=RC_OK; + st->msg->hdr.qdcount=htons(name!=NULL); + st->msg->hdr.ancount=0; + st->msg->hdr.nscount=0; + st->msg->hdr.arcount=0; + + transl= sizeof(dns_hdr_t); + if(name) { + unsigned char *p = mempcpy((unsigned char *)(&st->msg->hdr+1),name,rrnlen); + unsigned short qtype=(st->lean_query?thint:QT_ALL); + PUTINT16(qtype,p); + PUTINT16(C_IN,p); + transl += rrnlen+4; + if(st->edns_query) + add_opt_pseudo_rr(&st->msg,&transl,&allocsz, + global.udpbufsize,RC_OK,0,0); + } + st->transl=transl; +#ifndef NO_TCP_QUERIES + st->msg->len=htons(st->transl); +#endif + st->recvbuf=NULL; + st->state=(USE_UDP(st)?QS_UDPINITIAL:QS_TCPINITIAL); + /* fall through */ + } + QS_QUERY_CASES: + tryagain: + rv=p_query_sm(st); + if (rv==-1) { + return -1; + } + if (rv!=RC_OK) { + pdnsd_free(st->msg); + pdnsd_free(st->recvbuf); + st->state=QS_DONE; + if(st->needs_testing) { + switch(st->s_errno) { + case ENETUNREACH: /* network unreachable */ + case EHOSTUNREACH: /* host unreachable */ + case ENOPROTOOPT: /* protocol unreachable */ + case ECONNREFUSED: /* port unreachable */ + case ENETDOWN: /* network down */ + case EHOSTDOWN: /* host down */ +#ifdef ENONET + case ENONET: /* machine not on the network */ +#endif + /* Mark this server as down for a period of time */ + sched_server_test(PDNSD_A(st),1,0); + st->needs_testing=0; + } + } + return rv; + } + /* rv==RC_OK */ + DEBUG_PDNSDA_MSG("Received reply from %s (msg len=%u).\n", PDNSDA2STR(PDNSD_A(st)), st->recvl); + DEBUG_DUMP_DNS_MSG(st->recvbuf, st->recvl); + + /* Basic sanity checks */ + if (st->recvlrecvbuf->id); + if (recvid!=st->myrid) { + DEBUG_MSG("ID mismatch: expected %04x, got %04x!\n", st->myrid, recvid); + goto discard_reply; + } + } + if (st->recvbuf->qr!=QR_RESP) { + DEBUG_MSG("The QR bit indicates this is a query, not a response!\n"); + goto discard_reply; + } + if (st->recvbuf->opcode!=OP_QUERY) { + DEBUG_MSG("Not a reply to a standard query (opcode=%u).\n",st->recvbuf->opcode); + goto discard_reply; + } + + rcode=st->recvbuf->rcode; +#if DEBUG>0 + { + char flgsbuf[DNSFLAGSMAXSTRSIZE]; + DEBUG_MSG("rcode=%u (%s), flags:%s\n", rcode, get_ename(rcode), dnsflags2str(st->recvbuf, flgsbuf)); + } +#endif + if (st->recvbuf->z!=0) { + DEBUG_MSG("Malformed response (nonzero Z bit).\n"); + goto discard_reply; + } + + if(st->needs_testing) { + /* We got an answer from this server, so don't bother with up tests for a while. */ + sched_server_test(PDNSD_A(st),1,1); + st->needs_testing=0; + } + + rv=rcode; + if(rcode==RC_OK || rcode==RC_NAMEERR) { + /* success or at least no requery is needed */ + st->state=QS_DONE; + break; + } + else if (entp) { + if(rcode==RC_SERVFAIL || rcode==RC_NOTSUPP || rcode==RC_REFUSED) { + if (st->msg->hdr.rd && !st->recvbuf->ra) { + /* seems as if we have got no recursion available. + We will have to do it by ourselves (sigh...) */ + DEBUG_PDNSDA_MSG("Server %s returned error code: %s." + " Maybe does not support recursive query?" + " Querying non-recursively.\n", + PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + st->msg->hdr.rd=0; + goto resetstate_tryagain; + } + else if(rcode!=RC_SERVFAIL && st->edns_query && st->msg->hdr.arcount) + goto try_withoutedns; + else if (st->recvbuf->ancount && st->auth_serv==2) { + /* The name server returned a failure code, + but the answer section is not empty, + and the answer is from a server lower down the call chain. + Use this answer tentatively (it may be the + best we can get), but remember the failure. */ + DEBUG_PDNSDA_MSG("Server %s returned error code: %s," + " but the answer section is not empty." + " Using the answer tentatively.\n", + PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + st->failed=3; + st->state=QS_DONE; + break; + } + } + else if(rcode==RC_FORMAT && st->edns_query && st->msg->hdr.arcount) + try_withoutedns: { + size_t transl; + /* Perhaps the remote server barfs when the query + contains an OPT RR in the additional section. + Try again with an empty addtional section. */ + DEBUG_PDNSDA_MSG("Server %s returned error code: %s." + " Maybe cannot handle EDNS?" + " Querying with empty additional section.\n", + PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + transl=remove_opt_pseudo_rr(st->msg,st->transl); + if(transl!=0 && st->msg->hdr.arcount==0) { + st->transl=transl; +#ifndef NO_TCP_QUERIES + st->msg->len=htons(st->transl); +#endif + st->edns_query=0; + resetstate_tryagain: + st->myrid=get_rand16(); + st->msg->hdr.id=htons(st->myrid); + st->state=(USE_UDP(st)?QS_UDPINITIAL:QS_TCPINITIAL); + goto tryagain; + } + else { + DEBUG_PDNSDA_MSG("Internal error: could not remove additional section from query" + " to server %s\n", PDNSDA2STR(PDNSD_A(st))); + } + } + } + + discard_reply: + /* report failure */ + pdnsd_free(st->msg); + pdnsd_free(st->recvbuf); + /*close(st->sock);*/ + st->state=QS_DONE; +#if DEBUG>0 + if(entp) { + DEBUG_PDNSDA_MSG("Discarding reply from server %s\n", PDNSDA2STR(PDNSD_A(st))); + } +#endif + if (rv!=RC_OK) + return rv; + + return RC_SERVFAIL; /* mock error code */ + + default: /* we shouldn't get here */ + st->state=QS_DONE; + return RC_SERVFAIL; /* mock error code */ + } + + /* If we reach this code, we have successfully received an answer, + * because we have returned error codes on errors or -1 on AGAIN conditions. + * So we *should* have a usable dns record in recvbuf by now. + */ + rd= st->msg->hdr.rd; /* Save the 'Recursion Desired' bit of the query. */ + pdnsd_free(st->msg); + if(entp) { + time_t queryts=time(NULL); + size_t lcnt= ((size_t)st->recvl) - sizeof(dns_hdr_t); + unsigned char *rrp=(unsigned char *)(st->recvbuf+1); + dns_cent_array secs[3]={NULL,NULL,NULL}; +# define ans_sec secs[0] +# define auth_sec secs[1] +# define add_sec secs[2] + unsigned short qtype,flags,aa,neg_ans=0,reject_ans=0,num_ns=0; + int numoptrr; + edns_info_t ednsinfo= {0}; + + if (ntohs(st->recvbuf->qdcount)!=1) { + DEBUG_PDNSDA_MSG("Bad number of query records in answer from %s\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + /* check & skip the query record. */ + { + unsigned char nbuf[DNSNAMEBUFSIZE]; + if ((rv=decompress_name((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, nbuf, NULL))!=RC_OK) { + DEBUG_PDNSDA_MSG("Cannot decompress QNAME in answer from %s\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + if(!rhnicmp(nbuf,name)) { + DEBUG_PDNSDA_MSG("Answer from %s does not match query.\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + } + + qtype=(st->lean_query?thint:QT_ALL); + if (lcnt<4) { + DEBUG_PDNSDA_MSG("Format error in reply from %s (message truncated in qtype or qclass).\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; /* mock error code */ + goto free_recvbuf_return; + } + { + unsigned short qt,qc; + GETINT16(qt,rrp); + GETINT16(qc,rrp); + if(qt!=qtype) { + DEBUG_PDNSDA_MSG("qtype in answer (%u) from %s does not match expected qtype (%u).\n", + qt,PDNSDA2STR(PDNSD_A(st)),qtype); + rv=RC_SERVFAIL; + goto free_recvbuf_return; + } + } + lcnt-=4; + + st->aa= (st->recvbuf->aa && !st->failed); + st->tc= st->recvbuf->tc; + st->ra= (rd && st->recvbuf->ra); + + /* Don't flag cache entries from a truncated reply as authoritative. */ + aa= (st->aa && !st->tc); + flags=st->flags; + if (aa) flags|=CF_AUTH; + + + /* Initialize a dns_cent_t in the array for the answer section */ + if (!(ans_sec=DA_GROW1(ans_sec))) { + rv=RC_FATALERR; /* unrecoverable error */ + goto free_recvbuf_return; + } + /* By marking DF_AUTH, we mean authoritative AND complete. */ + if (!init_cent(&DA_INDEX(ans_sec,0), name, 0, 0, (aa && qtype==QT_ALL)?DF_AUTH:0 DBG1)) { + rv=RC_FATALERR; /* unrecoverable error */ + goto free_centarrays_recvbuf_return; + } + + /* Now read the answer, authority and additional sections, + storing the results in the arrays ans_sec,auth_sec and add_sec. + */ + numoptrr=0; + rv=rrs2cent((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, ntohs(st->recvbuf->ancount), + flags, queryts, &ans_sec, &numoptrr, &ednsinfo); +#if DEBUG>0 + if(numoptrr!=0) { + DEBUG_MSG("Answer section in reply contains %d OPT pseudo-RRs!\n", numoptrr); + } +#endif + numoptrr=0; + if(rv==RC_OK) { + uint16_t nscount=ntohs(st->recvbuf->nscount); + if (nscount) { + rv=rrs2cent((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, nscount, + flags|CF_ADDITIONAL, queryts, &auth_sec, &numoptrr, &ednsinfo); +#if DEBUG>0 + if(numoptrr!=0) { + DEBUG_MSG("Authority section in reply contains %d OPT pseudo-RRs!\n", numoptrr); + } +#endif + } + } + + numoptrr=0; + if(rv==RC_OK) { + uint16_t arcount=ntohs(st->recvbuf->arcount); + if (arcount) { + rv=rrs2cent((unsigned char *)st->recvbuf, st->recvl, &rrp, &lcnt, arcount, + flags|CF_ADDITIONAL, queryts, &add_sec, &numoptrr, &ednsinfo); + if(numoptrr!=0) { +#if DEBUG>0 + if(numoptrr!=1) { + DEBUG_MSG("Additional section in reply contains %d OPT pseudo-RRs!\n", numoptrr); + } + DEBUG_PDNSDA_MSG("Reply from %s contains OPT pseudosection: EDNS version = %u, udp size = %u, flag DO=%u\n", + PDNSDA2STR(PDNSD_A(st)), ednsinfo.version, ednsinfo.udpsize, ednsinfo.do_flg); +#endif + if(rcode!=ednsinfo.rcode) { + DEBUG_PDNSDA_MSG("Reply from %s contains unexpected EDNS rcode %u (%s)!\n", + PDNSDA2STR(PDNSD_A(st)), ednsinfo.rcode, get_ename(ednsinfo.rcode)); + rcode=ednsinfo.rcode; + /* Mark as failed, but use answer tentatively. */ + if(!st->failed) st->failed=1; + } + } + } + } + + if(!(rv==RC_OK || (rv==RC_TRUNC && st->recvbuf->tc))) { + DEBUG_PDNSDA_MSG(rv==RC_FORMAT?"Format error in reply from %s.\n": + rv==RC_TRUNC?"Format error in reply from %s (message unexpectedly truncated).\n": + rv==RC_SERVFAIL?"Inconsistent timestamps in reply from %s.\n": + "Out of memory while processing reply from %s.\n", + PDNSDA2STR(PDNSD_A(st))); + if(rv==RC_SERVFAIL) { + /* Inconsistent ttl timestamps and we are + enforcing strict RFC 2181 compliance. + Mark as failed, but use answer tentatively. */ + if(!st->failed) st->failed=1; + } + else { + if(rv!=RC_FATALERR) rv=RC_SERVFAIL; + goto free_ent_centarrays_recvbuf_return; + } + } + + { + /* Remember references to NS and SOA records in the answer or authority section + so that we can add this information to our own reply. */ + int i,n=DA_NEL(ans_sec); + for(i=0;iqname); + + if(getrrset_NS(cent)) + cent->c_ns=scnt; + if(getrrset_SOA(cent)) + cent->c_soa=scnt; + + if((qtype>=QT_MIN && qtype<=QT_MAX) || + (/* (qtype>=T_MIN && qtype<=T_MAX) && */ getrrset(cent,qtype)) || + (n==1 && cent->num_rrs==0)) + { + /* Match this name with names in the authority section */ + int j,m=DA_NEL(auth_sec); + for(j=0;jqname,cent->qname, &rem, NULL); + if(rem==0 && + /* Don't accept records for the root domain from name servers + that were not listed in the configuration file. */ + (ml || st->auth_serv!=2)) { + if(getrrset_NS(ce)) { + if(cent->c_ns==cundef || cent->c_nsc_ns=ml; + } + if(getrrset_SOA(ce)) { + if(cent->c_soa==cundef || cent->c_soac_soa=ml; + } + } + } + } + } + } + + /* Check whether the answer contains an IP address that should be rejected. */ + if(have_rejectlist(st)) { + int i; + int na4=nreject_a4(st); + addr4maskpair_t *a4arr=rejectlist_a4(st); +#if ALLOW_LOCAL_AAAA + int na6=nreject_a6(st); + addr6maskpair_t *a6arr=rejectlist_a6(st); +#endif + /* Check addresses in the answer, authority and additional sections. */ + for(i=0;i<3;++i) { + dns_cent_array sec=secs[i]; + int j,nce=DA_NEL(sec); + for(j=0;jrrs; rr; rr=rr->next) { + struct in_addr *a=(struct in_addr *)(rr->data); + int k; + for(k=0;ka,&am->mask)) { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE]; char abuf[ADDRSTR_MAXLEN]; + DEBUG_PDNSDA_MSG("Rejecting answer from server %s because it contains an A record" + " for \"%s\" with an address in the reject list: %s\n", + PDNSDA2STR(PDNSD_A(st)), + rhn2str(cent->qname,nmbuf,sizeof(nmbuf)), + inet_ntop(AF_INET,a,abuf,sizeof(abuf))); +#endif + reject_ans=1; goto rejectlist_scan_done; + } + } + } + } +#if ALLOW_LOCAL_AAAA + rrset=getrrset_AAAA(cent); + if(rrset && na6) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + struct in6_addr *a=(struct in6_addr *)(rr->data); + int k; + for(k=0;ka,&am->mask)) { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE]; char abuf[INET6_ADDRSTRLEN]; + DEBUG_PDNSDA_MSG("Rejecting answer from server %s because it contains an AAAA record" + " for \"%s\" with an address in the reject list: %s\n", + PDNSDA2STR(PDNSD_A(st)), + rhn2str(cent->qname,nmbuf,sizeof(nmbuf)), + inet_ntop(AF_INET6,a,abuf,sizeof(abuf))); +#endif + reject_ans=1; goto rejectlist_scan_done; + } + } + } + } +#endif + } + } + rejectlist_scan_done:; + } + + /* negative caching for domains */ + if (rcode==RC_NAMEERR) { + DEBUG_PDNSDA_MSG("Server %s returned error code: %s\n", PDNSDA2STR(PDNSD_A(st)),get_ename(rcode)); + name_error: + neg_ans=1; + { + /* We did not get what we wanted. Cache according to policy */ + dns_cent_t *ent=&DA_INDEX(ans_sec,0); + int neg_domain_pol=global.neg_domain_pol; + if (neg_domain_pol==C_ON || (neg_domain_pol==C_AUTH && st->aa)) { + time_t ttl=global.neg_ttl; + + /* Try to find a SOA record that came with the reply. + */ + if(ent->c_soa!=cundef) { + unsigned scnt=rhnsegcnt(name); + dns_cent_t *cent; + if(ent->c_soac_soa)))) { + rr_set_t *rrset=getrrset_SOA(cent); + if (rrset && rrset->rrs) { + time_t min=soa_minimum(rrset->rrs); + ttl=rrset->ttl; + if(ttl>min) + ttl=min; + } + } + } + DEBUG_RHN_MSG("Caching domain %s negative with ttl %li\n",RHN2STR(name),(long)ttl); + negate_cent(ent,ttl,queryts); + if(st->nocache) ent->flags |= DF_NOCACHE; + goto cleanup_return_OK; + } else { + if(c_soa) *c_soa=ent->c_soa; + free_cent(ent DBG1); + rv=RC_NAMEERR; + goto add_additional; + } + } + } + + if(reject_ans) { + if(reject_policy(st)==C_NEGATE && st->failed<=1) + goto name_error; + else { + rv=RC_SERVFAIL; + goto free_ent_centarrays_recvbuf_return; + } + } + + if(global.deleg_only_zones && st->auth_serv<3) { /* st->auth_serv==3 means this server is a root-server. */ + int missingdelegation,authcnt; + /* The deleg_only_zones data may change due to runtime reconfiguration, + therefore use locks. */ + lock_server_data(); + missingdelegation=0; authcnt=0; + { + int i,n=DA_NEL(global.deleg_only_zones); unsigned rem,zrem; + for(i=0;iqname,DA_INDEX(global.deleg_only_zones,l),&rem,&zrem) && zrem==0) { + if(rem) break; + else goto try_next_auth; + } + } + goto delegation_OK; + } + try_next_auth:; + } + } +#if DEBUG>0 + { + unsigned char nmbuf[DNSNAMEBUFSIZE],zbuf[DNSNAMEBUFSIZE]; + DEBUG_PDNSDA_MSG(authcnt?"%s is in %s zone, but no delegation found in answer returned by server %s\n" + :"%s is in %s zone, but no authority information provided by server %s\n", + rhn2str(name,nmbuf,sizeof(nmbuf)), rhn2str(DA_INDEX(global.deleg_only_zones,i),zbuf,sizeof(zbuf)), + PDNSDA2STR(PDNSD_A(st))); + } +#endif + missingdelegation=1; + } + delegation_OK:; + } + unlock_server_data(); + + if(missingdelegation) { + if(authcnt && st->failed<=1) { + /* Treat this as a nonexistant name. */ + goto name_error; + } + else if(st->auth_serv<2) { + /* If this is one of the servers obtained from the list + pdnsd was configured with, treat this as a failure. + Hopefully one of the other servers in the list will + return a non-empty authority section. + */ + rv=RC_SERVFAIL; + goto free_ent_centarrays_recvbuf_return; + } + } + } + + { + /* Negative caching of rr sets */ + dns_cent_t *ent=&DA_INDEX(ans_sec,0); + + if(!ent->num_rrs) neg_ans=1; + + if (thint>=T_MIN && thint<=T_MAX && !getrrset(ent,thint) && !st->tc && st->failed<=1) { + /* We did not get what we wanted. Cache according to policy */ + int neg_rrs_pol=global.neg_rrs_pol; + if (neg_rrs_pol==C_ON || (neg_rrs_pol==C_AUTH && aa) || + (neg_rrs_pol==C_DEFAULT && (aa || st->ra))) + { + time_t ttl=global.neg_ttl; + rr_set_t *rrset=getrrset_SOA(ent); + dns_cent_t *cent; + unsigned scnt; + /* If we received a SOA, we should take the ttl of that record. */ + if ((rrset && rrset->rrs) || + /* Try to find a SOA record higher up the hierarchy that came with the reply. */ + ((cent=lookup_cent_array(auth_sec, + (ent->c_soa!=cundef && ent->c_soa<(scnt=rhnsegcnt(name)))? + skipsegs(name,scnt-ent->c_soa): + name)) && + (rrset=getrrset_SOA(cent)) && rrset->rrs)) + { + time_t min=soa_minimum(rrset->rrs); + ttl=rrset->ttl; + if(ttl>min) + ttl=min; + } + DEBUG_RHN_MSG("Caching type %s for domain %s negative with ttl %li\n",getrrtpname(thint),RHN2STR(name),(long)ttl); + if (!add_cent_rrset_by_type(ent, thint, ttl, queryts, CF_NEGATIVE|flags DBG1)) { + rv=RC_FATALERR; + goto free_ent_centarrays_recvbuf_return; + } + } + } + } + + if (st->failed<=1) { + /* The domain names of all name servers found in the answer and authority sections are placed in *ns, + which is automatically grown. */ + /* dns_cent_array secs[2]={ans_sec,auth_sec}; */ + int i; + for(i=0;i<2;++i) { + dns_cent_array sec=secs[i]; + int j,n=DA_NEL(sec); + for(j=0;jqname) || st->auth_serv!=2) && + /* Don't accept possibly poisoning nameserver entries in paranoid mode */ + (st->trusted || !st->nsdomain || (domain_match(st->nsdomain, cent->qname, &rem,NULL),rem==0)) && + /* The following test is actually redundant and should never fail. */ + *(cent->qname)!=0xff) + { + /* Some nameservers obviously choose to send SOA records instead of NS ones. + * Although I think that this is poor behaviour, we'll have to work around that. */ + static const unsigned short nstypes[2]={T_NS,T_SOA}; + int k; + for(k=0;k<2;++k) { + rr_set_t *rrset=getrrset(cent,nstypes[k]); + if(rrset) { + rr_bucket_t *rr; + unsigned short first=1; + for(rr=rrset->rrs; rr; rr=rr->next) { + size_t sz1,sz2; + unsigned char *p; + /* Skip duplicate records */ + for(p=dlist_first(*ns); p; p=dlist_next(p)) { + if(rhnicmp(*p==0xff?p+1:skiprhn(p),(unsigned char *)(rr->data))) + goto next_nsr; + } + /* add to the nameserver list. + Here we use a little compression trick: if + the first byte of a name is 0xff, this means + repeat the previous name. + */ + sz1= (first?rhnlen(cent->qname):1); + sz2=rhnlen((unsigned char *)(rr->data)); + if (!(*ns=dlist_grow(*ns,sz1+sz2))) { + rv=RC_FATALERR; + goto free_ent_centarrays_recvbuf_return; + } + p=dlist_last(*ns); + if(first) { + first=0; + p=mempcpy(p,cent->qname,sz1); + } + else + *p++ = 0xff; /* 0xff means 'idem' */ + /* This will only copy the first name, which is the NS */ + memcpy(p,(unsigned char *)(rr->data),sz2); + ++num_ns; + next_nsr:; + } + } + } + } + } + } + } + cleanup_return_OK: + if(st->failed && neg_ans && num_ns==0) { + DEBUG_PDNSDA_MSG("Answer from server %s does not contain usable records.\n", + PDNSDA2STR(PDNSD_A(st))); + rv=RC_SERVFAIL; + goto free_ns_ent_centarrays_recvbuf_return; + } + if(!(*entp=malloc(sizeof(dns_cent_t)))) { + rv=RC_FATALERR; + goto free_ns_ent_centarrays_recvbuf_return; + } + **entp=DA_INDEX(ans_sec,0); + rv=RC_OK; + add_additional: + if (!st->failed && !reject_ans) { + /* Add the additional RRs to the cache. */ + /* dns_cent_array secs[3]={ans_sec,auth_sec,add_sec}; */ + int i; +#if DEBUG>0 + if(debug_p && neg_ans) { + int j,n=DA_NEL(ans_sec); + for(j=1; jqname) || st->auth_serv!=2) { + unsigned int rem; + if(st->trusted || !st->nsdomain || (domain_match(st->nsdomain, cent->qname, &rem, NULL),rem==0)) + add_cache(cent); + else { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE],nsbuf[DNSNAMEBUFSIZE]; + DEBUG_MSG("Record for %s not in nsdomain %s; dropped.\n", + rhn2str(cent->qname,nmbuf,sizeof(nmbuf)),rhn2str(st->nsdomain,nsbuf,sizeof(nsbuf))); +#endif + } + } + else { +#if DEBUG>0 + static const char *const secname[3]={"answer","authority","additional"}; + DEBUG_PDNSDA_MSG("Record(s) for root domain in %s section from %s dropped.\n", secname[i],PDNSDA2STR(PDNSD_A(st))); +#endif + } + } + } + } + goto free_centarrays_recvbuf_return; + + free_ns_ent_centarrays_recvbuf_return: + dlist_free(*ns); *ns=NULL; + free_ent_centarrays_recvbuf_return: + if(DA_NEL(ans_sec)>=1) free_cent(&DA_INDEX(ans_sec,0) DBG1); + free_centarrays_recvbuf_return: + { + /* dns_cent_array secs[3]={ans_sec,auth_sec,add_sec}; */ + int i; + for(i=0;i<3;++i) { + dns_cent_array sec=secs[i]; + int j,n=DA_NEL(sec); + /* The first entry in the answer section is treated separately, so skip that one. */ + for(j= !i; jrecvbuf); + return rv; +} + +/* + * Cancel a query, freeing all resources. Any query state is valid as input (this may even be called + * if a call to p_exec_query already returned error or success) + */ +static void p_cancel_query(query_stat_t *st) +{ + switch (st->state) { + QS_WRITE_CASES: + QS_READ_CASES: + close(st->sock); + /* fall through */ + case QS_TCPINITIAL: + case QS_UDPINITIAL: + pdnsd_free(st->recvbuf); + pdnsd_free(st->msg); + } + if(st->state!=QS_INITIAL && st->state!=QS_DONE) + st->state=QS_CANCELED; +} + +#if 0 +/* + * Initialize a query_serv_t (server list for parallel query) + * This is there for historical reasons only. + */ +inline static void init_qserv(query_stat_array *q) +{ + *q=NULL; +} +#endif + +/* + * Add a server entry to a query_serv_t + * Note: only a reference to nsdomain is copied, not the name itself. + * Be sure to free the q-list before freeing the name. + */ +static int add_qserv(query_stat_array *q, pdnsd_a2 *a, int port, time_t timeout, unsigned flags, + char nocache, char lean_query, char edns_query, char auth_s, char needs_testing, char trusted, + const unsigned char *nsdomain, rejectlist_t *rejectlist) +{ + query_stat_t *qs; + + if ((*q=DA_GROW1(*q))==NULL) { + DEBUG_MSG("Out of memory in add_qserv()\n"); + return 0; + } + + qs=&DA_LAST(*q); +#ifdef ENABLE_IPV4 + if (run_ipv4) { + memset(&qs->a.sin4,0,sizeof(qs->a.sin4)); + qs->a.sin4.sin_family=AF_INET; + qs->a.sin4.sin_port=htons(port); + qs->a.sin4.sin_addr=a->ipv4; + SET_SOCKA_LEN4(qs->a.sin4); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + memset(&qs->a.sin6,0,sizeof(qs->a.sin6)); + qs->a.sin6.sin6_family=AF_INET6; + qs->a.sin6.sin6_port=htons(port); + qs->a.sin6.sin6_flowinfo=IPV6_FLOWINFO; + qs->a.sin6.sin6_addr=a->ipv6; + SET_SOCKA_LEN6(qs->a.sin6); + + qs->a4fallback=a->ipv4; + } +#endif + qs->timeout=timeout; + qs->flags=flags; + qs->nocache=nocache; + qs->auth_serv=auth_s; + qs->lean_query=lean_query; + qs->edns_query=edns_query; + qs->needs_testing=needs_testing; + qs->trusted=trusted; + qs->aa=0; + qs->tc=0; + qs->ra=0; + qs->failed=0; + qs->nsdomain=nsdomain; /* Note: only a reference is copied, not the name itself! */ + qs->rejectlist=rejectlist; + + qs->state=QS_INITIAL; + qs->qm=global.query_method; + qs->s_errno=0; + return 1; +} + +/* Test whether two pdnsd_a2 addresses are the same. */ +inline __attribute__((always_inline)) +static int same_inaddr2_2(pdnsd_a2 *a, pdnsd_a2 *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) && + a->ipv4.s_addr==b->ipv4.s_addr ); +} + +/* This can be used to check whether a server address was already used in a + previous query_stat_t entry. */ +inline static int query_stat_same_inaddr2(query_stat_t *qs, pdnsd_a2 *b) +{ + return SEL_IPVER( qs->a.sin4.sin_addr.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&qs->a.sin6.sin6_addr,&b->ipv6) && + qs->a4fallback.s_addr==b->ipv4.s_addr ); +} + + +/* + * Free resources used by a query_serv_t + * There for historical reasons only. + */ +inline static void del_qserv(query_stat_array q) +{ + da_free(q); +} + +struct qstatnode_s { + query_stat_array qa; + struct qstatnode_s *next; +}; +typedef struct qstatnode_s qstatnode_t; + +struct qhintnode_s { + const unsigned char *nm; + int tp; + struct qhintnode_s *next; +}; +/* typedef struct qhintnode_s qhintnode_t; */ /* Already defined in dns_query.h */ + +static int auth_ok(query_stat_array q, const unsigned char *name, int thint, dns_cent_t *ent, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, + query_stat_t *qse, dlist ns, query_stat_array *serv); +static int p_dns_cached_resolve(query_stat_array q, const unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa); +static int simple_dns_cached_resolve(atup_array atup_a, int port, char edns_query, time_t timeout, + const unsigned char *name, int thint, dns_cent_t **cachedp); + + +/* + * Performs a semi-parallel query on the servers in q. PAR_QUERIES are executed parallel at a time. + * name is the query name in dns protocol format (number.string etc), + * ent is the dns_cent_t that will be filled. + * hops is the number of recursions left. + * qslist should refer to a list of server arrays used higher up in the calling chain. This way we can + * avoid name servers that have already been tried for this name. + * qhlist should refer to a list of names that we are trying to resolve higher up in the calling chain. + * These names should be avoided further down the chain, or we risk getting caught in a wasteful cycle. + * thint is a hint on the requested query type used to decide whether an aa record must be fetched + * or a non-authoritative answer will be enough. + * + * nocache is needed because we add AA records to the cache. If the nocache flag is set, we do not + * take the original values for the record, but flags=0 and ttl=0 (but only if we do not already have + * a cached record for that set). These settings cause the record be purged on the next cache addition. + * It will also not be used again. + * + * The return value of p_recursive_query() has the same meaning as that of p_dns_cached_resolve() + * (see below). + */ +static int p_recursive_query(query_stat_array q, const unsigned char *name, int thint, dns_cent_t **entp, + int *nocache, int hops, qstatnode_t *qslist, qhintnode_t *qhlist, + unsigned char *c_soa) +{ + dns_cent_t *ent,*entsave=NULL; + int i,j,k; + int rv=RC_SERVFAIL; + int qualval=0; + query_stat_t *qse=NULL; /* Initialized to inhibit compiler warning */ + dlist ns=NULL,nssave=NULL; + query_stat_array serv=NULL,servsave=NULL; + +# define W_AUTHOK 8 +# define W_NOTFAILED 2 +# define W_NOTTRUNC 1 +# define NOTFAILMASK 6 +# define GOODQUAL (W_AUTHOK+3*W_NOTFAILED) +# define save_query_result(ent,qs,ns,serv,authok) \ + { \ + int qval = authok*W_AUTHOK + (3-qs->failed)*W_NOTFAILED + (!qs->tc)*W_NOTTRUNC; \ + if(entsave && qval>qualval) { \ + /* Free the old copy, because the new result is better. */ \ + free_cent(entsave DBG1); \ + pdnsd_free(entsave); \ + entsave=NULL; \ + del_qserv(servsave); \ + dlist_free(nssave); \ + } \ + if(!entsave) { \ + entsave=ent; \ + servsave=serv; \ + /* The serv array contains references to data within the ns list, \ + so we need to save a copy of the ns list as well! */ \ + if(DA_NEL(serv)>0) nssave=ns; else {nssave=NULL;dlist_free(ns);} \ + qualval=qval; \ + qse=qs; \ + } \ + else { \ + /* We already have a copy, free the present one. */ \ + free_cent(ent DBG1); \ + pdnsd_free(ent); \ + del_qserv(serv); \ + dlist_free(ns); \ + } \ + serv=NULL; \ + ns=NULL; \ + } + + { + time_t ts0=time(NULL),global_timeout=global.timeout; + int dc=0,mc=0,nq=DA_NEL(q),parqueries=global.par_queries; + + for (j=0; jnq) mc=nq; + + /* First, call p_exec_query once for each parallel set to initialize. + * Then, as long as not all have the state QS_DONE or we have a timeout, + * build a poll/select set for all active queries and call them accordingly. */ + for (i=dc;i=j) { + /* The below should not happen any more, but may once again + * (immediate success) */ + DEBUG_PDNSDA_MSG("Sending query to %s\n", PDNSDA2STR(PDNSD_A(qs))); + retryquery: + rv=p_exec_query(&ent, name, thint, qs,&ns,c_soa); + if (rv==RC_OK) { + int authok; + DEBUG_PDNSDA_MSG("Query to %s succeeded.\n", PDNSDA2STR(PDNSD_A(qs))); + if((authok=auth_ok(q, name, thint, ent, hops, qslist, qhlist, qs, ns, &serv))) { + if(authok>=0) { + if(!qs->failed +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + && !(qs->qm==UDP_TCP && qs->tc) +#endif + ) + { + qse=qs; + mc=i; /* No need to cancel queries beyond i */ + goto done; + } + } + else { + mc=i; /* No need to cancel queries beyond i */ + goto free_ent_return_failed; + } + } + /* We do not have a satisfactory answer. + However, we will save a copy in case none of the other + servers in the q list give a satisfactory answer either. + */ + save_query_result(ent,qs,ns,serv,authok); +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if(qs->qm==UDP_TCP && qs->tc) { + switch_to_tcp(qs); + DEBUG_PDNSDA_MSG("Reply from %s was truncated. Trying again using TCP.\n", + PDNSDA2STR(PDNSD_A(qs))); + goto retryquery; + } +#endif + } + else if (rv==RC_NAMEERR || rv==RC_FATALERR) { + mc=i; /* No need to cancel queries beyond i */ + goto done; + } + } + if (qs->state==QS_DONE && i==dc) + dc++; + } + if (dcstate!=QS_DONE) { + if (i>=j && qs->timeout>maxto) + maxto=qs->timeout; +#ifdef NO_POLL + if (qs->sock>maxfd) { + maxfd=qs->sock; + PDNSD_ASSERT(maxfdstate) { + QS_READ_CASES: + FD_SET(qs->sock,&reads); + break; + QS_WRITE_CASES: + FD_SET(qs->sock,&writes); + break; + } +#else + polls[pc].fd=qs->sock; + switch (qs->state) { + QS_READ_CASES: + polls[pc].events=POLLIN; + break; + QS_WRITE_CASES: + polls[pc].events=POLLOUT; + break; + default: + polls[pc].events=0; + } +#endif + pc++; + } + } + if (pc==0) { + /* In this case, ALL are done and we do not need to cancel any + * query. */ + dc=mc; + break; + } + now=time(NULL); + maxto -= now-ts; + if (mc==nq) { +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + /* Don't use the global timeout if there are TCP queries + we might want to retry using UDP. */ + for (i=j;imaxto) maxto=globto; + } +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + skip_globto:; +#endif + } +#ifdef NO_POLL + tv.tv_sec=(maxto>0)?maxto:0; + tv.tv_usec=0; + nevents=select(maxfd+1,&reads,&writes,NULL,&tv); +#else + nevents=poll(polls,pc,(maxto>0)?(maxto*1000):0); +#endif + if (nevents<0) { + /* if(errno==EINTR) + continue; */ + log_warn("poll/select failed: %s",strerror(errno)); + goto done; + } + if (nevents==0) { + /* We have timed out. Mark the unresponsive servers so that we can consider + them for retesting later on. We will continue to listen for replies from + these servers as long as we have additional servers to try. */ + for (i=j;istate!=QS_DONE && qs->needs_testing) + qs->needs_testing=2; +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if (tentative_tcp_query(qs)) { + /* We timed out while waiting for a TCP connection. + Try again using UDP. + */ + close(qs->sock); + switch_to_udp(qs); + DEBUG_PDNSDA_MSG("TCP connection to %s timed out. Trying to use UDP.\n", + PDNSDA2STR(PDNSD_A(qs))); + + rv=p_exec_query(&ent, name, thint, qs,&ns,c_soa); + /* In the unlikely case of immediate success */ + if (rv==RC_OK) { + int authok; + DEBUG_PDNSDA_MSG("Query to %s succeeded.\n", PDNSDA2STR(PDNSD_A(qs))); + if((authok=auth_ok(q, name, thint, ent, hops, qslist, qhlist, qs, ns, &serv))) { + if(authok>=0) { + if(!qs->failed) { + qse=qs; + goto done; + } + } + else + goto free_ent_return_failed; + } + save_query_result(ent,qs,ns,serv,authok); + } + else if (rv==RC_NAMEERR || rv==RC_FATALERR) { + goto done; + } + ++nevents; + } +#endif + } +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if (mc==nq) { + /* We will not try additional servers, but we might want to try again + using UDP instead of TCP + */ + if(nevents && (time(NULL)-ts0)state!=QS_DONE) { + int srv_event=0; + /* This detection may seem suboptimal, but normally, we have at most 2-3 parallel + * queries, and anything else would be higher overhead, */ +#ifdef NO_POLL + switch (qs->state) { + QS_READ_CASES: + srv_event=FD_ISSET(qs->sock,&reads); + break; + QS_WRITE_CASES: + srv_event=FD_ISSET(qs->sock,&writes); + break; + } +#else + do { + PDNSD_ASSERT(icsock); + /* + * In case of an error, reenter the state machine + * to catch it. + */ + switch (qs->state) { + QS_READ_CASES: + srv_event=polls[k].revents&(POLLIN|POLLERR|POLLHUP|POLLNVAL); + break; + QS_WRITE_CASES: + srv_event=polls[k].revents&(POLLOUT|POLLERR|POLLHUP|POLLNVAL); + break; + } +#endif + if (srv_event) { + --nevents; + retryquery2: + rv=p_exec_query(&ent, name, thint, qs,&ns,c_soa); + if (rv==RC_OK) { + int authok; + DEBUG_PDNSDA_MSG("Query to %s succeeded.\n", PDNSDA2STR(PDNSD_A(qs))); + if((authok=auth_ok(q, name, thint, ent, hops, qslist, qhlist, qs, ns, &serv))) { + if(authok>=0) { + if(!qs->failed +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + && !(qs->qm==UDP_TCP && qs->tc) +#endif + ) + { + qse=qs; + goto done; + } + } + else + goto free_ent_return_failed; + } + save_query_result(ent,qs,ns,serv,authok); +#if !defined(NO_TCP_QUERIES) && !defined(NO_UDP_QUERIES) + if(qs->qm==UDP_TCP && qs->tc) { + switch_to_tcp(qs); + DEBUG_PDNSDA_MSG("Reply from %s was truncated. Trying again using TCP.\n", + PDNSDA2STR(PDNSD_A(qs))); + goto retryquery2; + } +#endif + } + else if (rv==RC_NAMEERR || rv==RC_FATALERR) { + goto done; + } + } + } + /* recheck, this might have changed after the last p_exec_query */ + if (qs->state==QS_DONE && i==dc) + dc++; + } + if(nevents>0) { + /* We have not managed to handle all the events reported by poll/select. + Better call it quits, or we risk getting caught in a wasteful cycle. + */ + if(++poll_errs<=MAXPOLLERRS) + log_error("%d unhandled poll/select event(s) in p_recursive_query() at %s, line %d.",nevents,__FILE__,__LINE__); + rv=RC_SERVFAIL; + goto done; + } + } while (dc 1) + ++n; + if(n>0) { + pdnsd_a addrs[n]; /* variable length array */ + k=0; + for (i=0;ineeds_testing > 1) + addrs[k++]= *PDNSD_A(qs); + } + sched_server_test(addrs,n,-1); + } + } + } + + if(entsave) { + /* + * If we didn't get rrs from any of the authoritative servers, or the answers were + * unsatisfactory for another reason, take the one we had. + * However, raise the CF_NOCACHE flag, so that it won't be used again (outside the + * cache latency period). + */ + DEBUG_PDNSDA_MSG("Using %s reply from %s.\n", + !(qualval&NOTFAILMASK)? "reportedly failed": + !(qualval&W_NOTFAILED)? "inconsistent": + !(qualval&W_NOTTRUNC)? "truncated": + !(qualval&W_AUTHOK)? "non-authoritative": "good", + PDNSDA2STR(PDNSD_A(qse))); + ent=entsave; + serv=servsave; + ns=nssave; + if(qualvalflags&DF_NEGATIVE)) { + int jlim= RRARR_LEN(ent); + for (j=0; jflags |= CF_NOCACHE; + } + } + else /* Very unlikely, but not impossible. */ + ent->flags |= DF_NOCACHE; + } + rv=RC_OK; + } + else if (rv!=RC_OK) { + if(rv==RC_FATALERR) { + DEBUG_MSG("Unrecoverable error encountered while processing query.\n"); + rv=RC_SERVFAIL; + } + DEBUG_MSG("%sReturning error code \"%s\"\n", + rv!=RC_NAMEERR? "No query succeeded. ": "", + get_ename(rv)); + goto clean_up_return; + } + + if(nocache) *nocache=qse->nocache; + + if (DA_NEL(serv)>0) { + /* Authority records present. Ask them, because the answer was non-authoritative. */ + qstatnode_t qsn={q,qslist}; + unsigned char save_ns=ent->c_ns,save_soa=ent->c_soa; + + if(qse->aa || qse->ra) { + /* The server claimed to be authoritative or have recursion available, + yet we did not completely trust the answer for some reason. + We will try to ask the servers in the authority records, + but in case we fail, we will save a copy of the answer. */ + entsave=ent; + } + else { + free_cent(ent DBG1); + pdnsd_free(ent); + entsave=NULL; + } + rv=p_dns_cached_resolve(serv, name, thint,&ent,hops-1,&qsn,qhlist,time(NULL),c_soa); + if(rv==RC_OK || rv==RC_CACHED || (rv==RC_STALE && !entsave)) { + if(save_ns!=cundef && (ent->c_ns==cundef || ent->c_nsc_ns=save_ns; + if(save_soa!=cundef && (ent->c_soa==cundef || ent->c_soac_soa=save_soa; + goto free_entsave; + } + else if(rv==RC_NAMEERR) { + if(c_soa && save_soa!=cundef && (*c_soa==cundef || *c_soaaa? "be authoritative": "have recursion available"); + ent=entsave; + rv=RC_OK; + } + } + + clean_up_return: + /* Always free the serv array before freeing the ns list, + because the serv array contains references to data within the ns list! */ + del_qserv(serv); + dlist_free(ns); + + if(rv==RC_OK || rv==RC_CACHED || rv==RC_STALE) *entp=ent; + return rv; +# undef save_query_result +} + +/* auth_ok returns 1 if we don't need an authoritative answer or + if we can find servers to ask for an authoritative answer. + In the latter case these servers will be added to the *serv list. + A return value of 0 means the answer is not satisfactory in the + previous sense. + A return value of -1 indicates an error. +*/ +static int auth_ok(query_stat_array q, const unsigned char *name, int thint, dns_cent_t *ent, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, + query_stat_t *qse, dlist ns, query_stat_array *serv) +{ + int retval=0; + + /* If the answer was obtained from a name server which returned a failure code, + the answer is never satisfactory. */ + if(qse->failed > 1) return 0; + + /* + Look into the query type hint. If it is a wildcard (QT_*), we need an authoritative answer. + Same if there is no record that answers the query. + This test will also succeed if we have a negative cached record. This is done purposely. + */ +#define aa_needed ((thint>=QT_MIN && thint<=QT_MAX) || \ + ((thint>=T_MIN && thint<=T_MAX) && \ + (!have_rr(ent,thint) && !have_rr_CNAME(ent)))) + + /* We will want to query authoritative servers if all of the following conditions apply: + + 1) The server from which we got the answer was not configured as "proxy only". + 2) The answer is not a negatively cached domain (i.e. the server did not reply with NXDOMAIN). + 3) The query type is a wild card (QT_*), or no record answers the query. + 4) The answer that we have is non-authoritative. + */ + if(!(qse->auth_serv && !(ent->flags&DF_NEGATIVE) && aa_needed)) + return 1; + + if(qse->aa) { + /* The reply we have claims to be authoritative. + However, I have seen cases where name servers raise the authority flag incorrectly (groan...), + so as a work-around, we will check whether the domains for which the servers in the ns + list are responsible, match the queried name better than the domain for which the + last server was responsible. */ + unsigned char *nsdomain; + + if(!qse->nsdomain) + return 1; + + nsdomain=dlist_first(ns); + if(!nsdomain) + return 1; + for(;;) { + unsigned int rem,crem; + domain_match(nsdomain,qse->nsdomain,&rem,&crem); + if(!(rem>0 && crem==0)) + return 1; + domain_match(nsdomain,name,&rem,NULL); + if(rem!=0) + return 1; + do { + nsdomain=dlist_next(nsdomain); + if(!nsdomain) + goto done_checkauth; + } while(*nsdomain==0xff); /* Skip repeats. */ + } + done_checkauth:; + + /* The name servers in the ns list are a better match for the queried name than + the server from which we got the last reply, so ignore the aa flag. + */ +#if DEBUG>0 + if(debug_p) { + unsigned char dbuf[DNSNAMEBUFSIZE],sdbuf[DNSNAMEBUFSIZE]; + nsdomain=dlist_first(ns); + DEBUG_PDNSDA_MSG("The name server %s which is responsible for the %s domain, raised the aa flag, but appears to delegate to the sub-domain %s\n", + PDNSDA2STR(PDNSD_A(qse)), + rhn2str(qse->nsdomain,dbuf,sizeof(dbuf)), + rhn2str(nsdomain,sdbuf,sizeof(sdbuf))); + } +#endif + } + + /* The answer was non-authoritative. Try to build a list of addresses of authoritative servers. */ + if (hops>0) { + unsigned char *nsdomp, *nsdomain=NULL; + rr_set_t *localrrset=NULL; + rr_bucket_t *localrr=NULL; + for (nsdomp=dlist_first(ns);;) { + unsigned char *nsname=NULL; /* Initialize to inhibit compiler warning. */ + int nserva, ia, n; + pdnsd_a2 serva[MAXNAMESERVIPS]; + + /* Get next name server. */ + if(localrr) { + /* Use next locally defined NS record. */ + nsname=(unsigned char *)(localrr->data); + localrr= localrr->next; + } + else { + if(localrrset) { + /* clean up rrset */ + del_rrset(localrrset DBG1); + localrrset=NULL; + } + if(!nsdomp) + break; + else if(*nsdomp!=0xff) { + /* New domain. */ + nsdomain=nsdomp; + if (global.paranoid) { + unsigned int rem; + /* paranoia mode: don't query name servers that are not responsible */ + domain_match(nsdomain,name,&rem,NULL); + if (rem!=0) { +#if DEBUG>0 + unsigned char nmbuf[DNSNAMEBUFSIZE],dbuf[DNSNAMEBUFSIZE],nsbuf[DNSNAMEBUFSIZE]; + DEBUG_MSG("The name server %s is responsible for the %s domain, which does not match %s\n", + rhn2str(nsname,nsbuf,sizeof(nsbuf)), + rhn2str(nsdomain,dbuf,sizeof(dbuf)), + rhn2str(name,nmbuf,sizeof(nmbuf))); +#endif + /* Skip records in ns list for the same domain. */ + do { + nsdomp=dlist_next(nsdomp); + } while (nsdomp && *nsdomp==0xff); + continue; + } + } + /* Check if we have locally defined NS records, because + they will override the ones provided by remote servers. + */ + localrrset=lookup_cache_local_rrset(nsdomain,T_NS); + if(localrrset) { + /* Skip records in ns list for the same domain. */ + do { + nsdomp=dlist_next(nsdomp); + } while (nsdomp && *nsdomp==0xff); + localrr=localrrset->rrs; + if(!localrr) continue; + nsname=(unsigned char *)(localrr->data); + localrr= localrr->next; + } + else { + nsname=skiprhn(nsdomp); + nsdomp=dlist_next(nsdomp); + } + } + else { + /* domain repeated. */ + nsname= nsdomp+1; + nsdomp=dlist_next(nsdomp); + } + } + /* look it up in the cache or resolve it if needed. + The records received should be in the cache now, so it's ok. + */ + nserva=0; + + { + const unsigned char *nm=name; + int tp=thint; + qhintnode_t *ql=qhlist; + + for(;;) { + if(rhnicmp(nm,nsname) && tp==T_A) { + DEBUG_RHN_MSG("Not looking up address for name server \"%s\": " + "risk of infinite recursion.\n",RHN2STR(nsname)); + goto skip_server; + } + if(!ql) break; + nm=ql->nm; + tp=ql->tp; + ql=ql->next; + } + { + qhintnode_t qhn={name,thint,qhlist}; + dns_cent_t *servent; + if (r_dns_cached_resolve(nsname,T_A, &servent, hops-1, &qhn,time(NULL),NULL)==RC_OK) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + rr_set_t *rrset=getrrset_A(servent); + rr_bucket_t *rrs; + if (rrset) + for(rrs=rrset->rrs; rrs && nservanext) + serva[nserva++].ipv4 = *((struct in_addr *)rrs->data); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + rr_set_t *rrset6=getrrset_AAAA(servent); + rr_bucket_t *rrs6= (rrset6? rrset6->rrs: NULL); + rr_set_t *rrset4=getrrset_A(servent); + rr_bucket_t *rrs4= (rrset4? rrset4->rrs: NULL); + while(nservadata); + rrs6=rrs6->next; + if (rrs4) { + /* Store IPv4 address as fallback. */ + serva[nserva].ipv4 = *((struct in_addr *)rrs4->data); + rrs4=rrs4->next; + } + else + serva[nserva].ipv4.s_addr=INADDR_ANY; + } + else if (rrs4) { + struct in_addr *ina = (struct in_addr *)rrs4->data; + struct in6_addr *in6a = &serva[nserva].ipv6; + IPV6_MAPIPV4(ina,in6a); + serva[nserva].ipv4.s_addr=INADDR_ANY; + rrs4=rrs4->next; + } + else + break; + ++nserva; + } + } +#endif + free_cent(servent DBG1); + pdnsd_free(servent); + } + } + } + +#if DEBUG>0 + if(nserva==0) { + DEBUG_RHN_MSG("Looking up address for name server \"%s\" failed.\n",RHN2STR(nsname)); + } +#endif + n=DA_NEL(*serv); + for(ia=0; iastate==QS_DONE && equiv_inaddr2(PDNSD_A(qs),pserva)) { + DEBUG_PDNSDA_MSG("Not trying name server %s, already queried.\n", PDNSDA2STR(PDNSD_A2_TO_A(pserva))); + goto skip_server_addr; + } + } + if(!ql) break; + qa=ql->qa; + ql=ql->next; + } + } + + /* lean query mode is inherited. CF_AUTH and CF_ADDITIONAL are not (as specified + * in CFF_NOINHERIT). */ + if (!add_qserv(serv, pserva, 53, qse->timeout, qse->flags&~CFF_NOINHERIT, 0, + qse->lean_query,qse->edns_query,2,0,!global.paranoid,nsdomain, + inherit_rejectlist(qse)?qse->rejectlist:NULL)) + { + return -1; + } + retval=1; + skip_server_addr:; + } + skip_server:; + } +#if DEBUG>0 + if(!retval) { + DEBUG_PDNSDA_MSG("No remaining authoritative name servers to try in authority section from %s.\n", PDNSDA2STR(PDNSD_A(qse))); + } +#endif + } + else { + DEBUG_MSG("Maximum hops count reached; not trying any more name servers.\n"); + } + + return retval; + +#undef aa_needed +} + +/* + * This checks the given name to resolve against the access list given for the server using the + * include=, exclude= and policy= parameters. + */ +static int use_server(servparm_t *s, const unsigned char *name) +{ + int i,n=DA_NEL(s->alist); + + for (i=0;ialist,i); + unsigned int nrem,lrem; + domain_match(name,sl->domain,&nrem,&lrem); + if(!lrem && (!sl->exact || !nrem)) + return sl->rule==C_INCLUDED; + } + + if (s->policy==C_SIMPLE_ONLY || s->policy==C_FQDN_ONLY) { + if(rhnsegcnt(name)<=1) + return s->policy==C_SIMPLE_ONLY; + else + return s->policy==C_FQDN_ONLY; + } + + return s->policy==C_INCLUDED; +} + +#if ALLOW_LOCAL_AAAA +#define serv_has_rejectlist(s) ((s)->reject_a4!=NULL || (s)->reject_a6!=NULL) +#else +#define serv_has_rejectlist(s) ((s)->reject_a4!=NULL) +#endif + +/* Take the lists of IP addresses from a server section sp and + convert them into a form that can be used by p_exec_query(). + If successful, add_rejectlist returns a new list which is added to the old list rl, + otherwise the return value is NULL. +*/ +static rejectlist_t *add_rejectlist(rejectlist_t *rl, servparm_t *sp) +{ + int i,na4=DA_NEL(sp->reject_a4); + addr4maskpair_t *a4p; +#if ALLOW_LOCAL_AAAA + int na6=DA_NEL(sp->reject_a6); + addr6maskpair_t *a6p; +#endif + rejectlist_t *rlist = malloc(sizeof(rejectlist_t) + na4*sizeof(addr4maskpair_t) +#if ALLOW_LOCAL_AAAA + + na6*sizeof(addr6maskpair_t) +#endif + ); + + if(rlist) { +#if ALLOW_LOCAL_AAAA + /* Store the larger IPv6 addresses first to avoid possible alignment problems. */ + rlist->na6 = na6; + a6p = (addr6maskpair_t *)rlist->rdata; + for(i=0;ireject_a6,i); +#endif + rlist->na4 = na4; +#if ALLOW_LOCAL_AAAA + a4p = (addr4maskpair_t *)a6p; +#else + a4p = (addr4maskpair_t *)rlist->rdata; +#endif + for(i=0;ireject_a4,i); + + rlist->policy = sp->rejectpolicy; + rlist->inherit = sp->rejectrecursively; + rlist->next = rl; + } + else { + DEBUG_MSG("Out of memory in add_rejectlist()\n"); + } + + return rlist; +} + +inline static void free_rejectlist(rejectlist_t *rl) +{ + while(rl) { + rejectlist_t *next = rl->next; + free(rl); + rl=next; + } +} + +/* Lookup addresses of nameservers provided by root servers for a given domain in the cache. + Returns NULL if unsuccessful (or the cache entries have timed out). +*/ +static addr2_array lookup_ns(const unsigned char *domain) +{ + addr2_array res=NULL; + + dns_cent_t *cent=lookup_cache(domain,NULL); + if(cent) { + rr_set_t *rrset=getrrset_NS(cent); + if(rrset && (rrset->flags&CF_ROOTSERV) && !timedout(rrset)) { + rr_bucket_t *rr; + for(rr=rrset->rrs; rr; rr=rr->next) { + dns_cent_t *servent=lookup_cache((unsigned char*)(rr->data),NULL); + int nserva=0; + pdnsd_a2 serva[MAXNAMESERVIPS]; + if(servent) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + rr_set_t *rrset=getrrset_A(servent); + rr_bucket_t *rrs; + if (rrset && !timedout(rrset)) + for(rrs=rrset->rrs; rrs && nservanext) + serva[nserva++].ipv4 = *((struct in_addr *)rrs->data); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + rr_set_t *rrset6=getrrset_AAAA(servent); + rr_set_t *rrset4=getrrset_A(servent); + rr_bucket_t *rrs6=NULL, *rrs4=NULL; + if (rrset6 && !(rrset6->flags&CF_NEGATIVE)) { + if(!timedout(rrset6)) { + rrs6= rrset6->rrs; + if (rrs6 && rrset4 && !(rrset4->flags&CF_NEGATIVE)) { + if(timedout(rrset4) || !(rrs4=rrset4->rrs)) + /* Treat this as a failure. */ + rrs6=NULL; + } + } + } + else if (rrset4 && !timedout(rrset4)) + rrs4= rrset4->rrs; + + while(nservadata); + rrs6=rrs6->next; + if (rrs4) { + /* Store IPv4 address as fallback. */ + serva[nserva].ipv4 = *((struct in_addr *)rrs4->data); + rrs4=rrs4->next; + } + else + serva[nserva].ipv4.s_addr=INADDR_ANY; + } + else if (rrs4) { + struct in_addr *ina = (struct in_addr *)rrs4->data; + struct in6_addr *in6a = &serva[nserva].ipv6; + IPV6_MAPIPV4(ina,in6a); + serva[nserva].ipv4.s_addr=INADDR_ANY; + rrs4=rrs4->next; + } + else + break; + ++nserva; + } + } +#endif + free_cent(servent DBG1); + pdnsd_free(servent); + } + if(nserva==0) { + /* Address lookup failed. */ + da_free(res); res=NULL; + break; + } + else { + int i, j, n=DA_NEL(res); + for(i=0; irrs; rr; rr=rr->next) { + dns_cent_t *servent; + int nserva=0; + pdnsd_a2 serva[MAXNAMESERVIPS]; + + rc=simple_dns_cached_resolve(atup_a,port,edns_query,timeout, + (const unsigned char *)(rr->data),T_A,&servent); + if(rc==RC_OK) { +#ifdef ENABLE_IPV4 + if (run_ipv4) { + rr_set_t *rrset=getrrset_A(servent); + rr_bucket_t *rrs; + if (rrset) + for(rrs=rrset->rrs; rrs && nservanext) + serva[nserva++].ipv4 = *((struct in_addr *)rrs->data); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + rr_set_t *rrset6=getrrset_AAAA(servent); + rr_bucket_t *rrs6= (rrset6? rrset6->rrs: NULL); + rr_set_t *rrset4=getrrset_A(servent); + rr_bucket_t *rrs4= (rrset4? rrset4->rrs: NULL); + while(nservadata); + rrs6=rrs6->next; + if (rrs4) { + /* Store IPv4 address as fallback. */ + serva[nserva].ipv4 = *((struct in_addr *)rrs4->data); + rrs4=rrs4->next; + } + else + serva[nserva].ipv4.s_addr=INADDR_ANY; + } + else if (rrs4) { + struct in_addr *ina = (struct in_addr *)rrs4->data; + struct in6_addr *in6a = &serva[nserva].ipv6; + IPV6_MAPIPV4(ina,in6a); + serva[nserva].ipv4.s_addr=INADDR_ANY; + rrs4=rrs4->next; + } + else + break; + ++nserva; + } + } +#endif + free_cent(servent DBG1); + pdnsd_free(servent); + } + else { + DEBUG_RHN_MSG("Simple query for %s type A failed (rc: %s)\n", + RHN2STR((const unsigned char *)(rr->data)),get_ename(rc)); + } + + if(nserva==0) { + /* Address lookup failed. */ + DEBUG_RHN_MSG("Failed to obtain address of root server %s in dns_rootserver_resolv()\n", + RHN2STR((const unsigned char *)(rr->data))); + ++nfail; + } + else { + int i, j, n=DA_NEL(res); + for(i=0; iDA_NEL(res)) { + DEBUG_MSG("Too many root-server resolve failures (%u succeeded, %u failed)," + " rejecting the result.\n", DA_NEL(res),nfail); + da_free(res); res=NULL; + } + } + free_cent_return: + free_cent(cent DBG1); + pdnsd_free(cent); + } + else { + DEBUG_MSG("Simple query for root domain type NS failed (rc: %s)\n",get_ename(rc)); + } + + return res; +} + + +static int p_dns_resolve(const unsigned char *name, int thint, dns_cent_t **cachedp, int hops, qhintnode_t *qhlist, + unsigned char *c_soa) +{ + int i,n,rc; + int one_up=0,seenrootserv=0; + query_stat_array serv=NULL; + rejectlist_t *rejectlist=NULL; + + /* try the servers in the order of their definition */ + lock_server_data(); + n=DA_NEL(servers); + for (i=0;irootserver<=1 && use_server(sp,name)) { + int m=DA_NEL(sp->atup_a); + if(m>0) { + rejectlist_t *rjl=NULL; + int j=0, jstart=0; + if(sp->rand_servers) j=jstart=random()%m; + do { + atup_t *at=&DA_INDEX(sp->atup_a,j); + if (at->is_up) { + if(sp->rootserver) { + if(!seenrootserv) { + int nseg,mseg=1,l=0; + const unsigned char *topdomain=NULL; + addr2_array adrs=NULL; + seenrootserv=1; + nseg=rhnsegcnt(name); + if(nseg>=2) { + static const unsigned char rhn_arpa[6]= {4,'a','r','p','a',0}; + unsigned int rem; + /* Check if the queried name ends in "arpa" */ + domain_match(rhn_arpa, name, &rem,NULL); + if(rem==0) mseg=3; + } + if(nseg<=mseg) { + if(nseg>0) mseg=nseg-1; else mseg=0; + } + for(;mseg>=1; --mseg) { + topdomain=skipsegs(name,nseg-mseg); + adrs=lookup_ns(topdomain); + l=DA_NEL(adrs); + if(l>0) break; + if(adrs) da_free(adrs); + } + if(l>0) { + /* The name servers for this top level domain have been found in the cache. + Instead of asking the root server, we will use this cached information. + */ + int k=0, kstart=0; + if(sp->rand_servers) k=kstart=random()%l; + if(serv_has_rejectlist(sp) && sp->rejectrecursively && !rjl) { + rjl=add_rejectlist(rejectlist,sp); + if(!rjl) {one_up=0; da_free(adrs); goto done;} + rejectlist=rjl; + } + do { + one_up=add_qserv(&serv, &DA_INDEX(adrs,k), 53, sp->timeout, + mk_flag_val(sp)&~CFF_NOINHERIT, sp->nocache, + sp->lean_query, sp->edns_query, 2, 0, + !global.paranoid, topdomain, rjl); + if(!one_up) { + da_free(adrs); + goto done; + } + if(++k==l) k=0; + } while(k!=kstart); + da_free(adrs); + DEBUG_PDNSDA_MSG("Not querying root-server %s, using cached information instead.\n", + PDNSDA2STR(PDNSD_A2_TO_A(&at->a))); + seenrootserv=2; + break; + } + } + else if(seenrootserv==2) + break; + } + if(serv_has_rejectlist(sp) && !rjl) { + rjl=add_rejectlist(rejectlist,sp); + if(!rjl) {one_up=0; goto done;} + rejectlist=rjl; + } + one_up=add_qserv(&serv, &at->a, sp->port, sp->timeout, + mk_flag_val(sp), sp->nocache, sp->lean_query, sp->edns_query, + sp->rootserver?3:(!sp->is_proxy), + needs_testing(sp), 1, NULL, rjl); + if(!one_up) + goto done; + } + if(++j==m) j=0; + } while(j!=jstart); + } + } + } + done: + unlock_server_data(); + if (one_up) { + dns_cent_t *cached; + int nocache; + rc=p_recursive_query(serv, name, thint, &cached, &nocache, hops, NULL, qhlist, c_soa); + if (rc==RC_OK) { + if (!nocache) { + dns_cent_t *tc; + add_cache(cached); + if ((tc=lookup_cache(name,NULL))) { + /* The cache may hold more information than the recent query yielded. + * try to get the merged record. If that fails, revert to the new one. */ + free_cent(cached DBG1); + pdnsd_free(cached); + cached=tc; + /* rc=RC_CACHED; */ + } else + DEBUG_MSG("p_dns_resolve: merging answer with cache failed, using local cent copy.\n"); + } else + DEBUG_MSG("p_dns_resolve: nocache.\n"); + + *cachedp=cached; + } + else if(rc==RC_CACHED || rc==RC_STALE) + *cachedp=cached; + } + else { + DEBUG_MSG("No server is marked up and allowed for this domain.\n"); + rc=RC_SERVFAIL; /* No server up */ + } + del_qserv(serv); + free_rejectlist(rejectlist); + return rc; +} + +static int set_flags_ttl(unsigned short *flags, time_t *ttl, dns_cent_t *cached, int tp) +{ + rr_set_t *rrset=getrrset(cached,tp); + if (rrset) { + time_t t; + *flags|=rrset->flags; + t=rrset->ts+CLAT_ADJ(rrset->ttl); + if (!*ttl || *ttl>t) + *ttl=t; + return 1; + } + return 0; +} + +static void set_all_flags_ttl(unsigned short *flags, time_t *ttl, dns_cent_t *cached) +{ + int i, ilim= RRARR_LEN(cached); + + for(i=0; iflags; + t=rrset->ts+CLAT_ADJ(rrset->ttl); + if (!*ttl || *ttl>t) + *ttl=t; + } + } +} + +/* + Lookup name in the cache, and if records of type thint are found, check whether a requery is needed. + Possible returns values are: + RC_OK: the name is locally defined. + RC_NAMEERR: the name is locally negatively cached. + RC_CACHED: name was found in the cache, requery not needed. + RC_STALE: name was found in the cache, but requery is needed. + RC_NOTCACHED: name was not found in the cache. +*/ +static int lookup_cache_status(const unsigned char *name, int thint, dns_cent_t **cachedp, unsigned short *flagsp, + time_t queryts, unsigned char *c_soa) +{ + dns_cent_t *cached; + int rc=RC_NOTCACHED; + int wild=0; + unsigned short flags=0; + + if ((cached=lookup_cache(name,&wild))) { + short int neg=0,timed=0,need_req=0; + time_t ttl=0; + + if (cached->flags&DF_LOCAL) { +#if DEBUG>0 + { + char dflagstr[DFLAGSTRLEN]; + DEBUG_RHN_MSG("Entry found in cache for '%s' with dflags=%s.\n", + RHN2STR(cached->qname),dflags2str(cached->flags,dflagstr)); + } +#endif + if((cached->flags&DF_NEGATIVE) || wild==w_locnerr) { + if(c_soa) { + if(cached->c_soa!=cundef) + *c_soa=cached->c_soa; + else if(have_rr_SOA(cached)) + *c_soa=rhnsegcnt(cached->qname); + else { + unsigned char *owner=getlocalowner(cached->qname,T_SOA); + if(owner) + *c_soa=rhnsegcnt(owner); + } + } + free_cent(cached DBG1); + pdnsd_free(cached); + rc= RC_NAMEERR; + goto return_rc; + } + else { + rc= RC_OK; + goto return_rc_cent; + } + } + DEBUG_RHN_MSG("Record found in cache for %s\n",RHN2STR(cached->qname)); + if (cached->flags&DF_NEGATIVE) { + if ((ttl=cached->neg.ts+CLAT_ADJ(cached->neg.ttl))>=queryts) + neg=1; + else + timed=1; + } else { + if (thint==QT_ALL) { + set_all_flags_ttl(&flags, &ttl, cached); + } + else if (!set_flags_ttl(&flags, &ttl, cached, T_CNAME) || (getrrset_CNAME(cached)->flags&CF_NEGATIVE)) { + flags=0; ttl=0; + if (thint>=T_MIN && thint<=T_MAX) { + if (set_flags_ttl(&flags, &ttl, cached, thint)) + neg=getrrset(cached,thint)->flags&CF_NEGATIVE && ttl>=queryts; + } + else if (thint==QT_MAILB) { + set_flags_ttl(&flags, &ttl, cached, T_MB); + set_flags_ttl(&flags, &ttl, cached, T_MG); + set_flags_ttl(&flags, &ttl, cached, T_MR); + } + else if (thint==QT_MAILA) { + set_flags_ttl(&flags, &ttl, cached, T_MD); + set_flags_ttl(&flags, &ttl, cached, T_MF); + } + } + if(!(flags&CF_LOCAL)) { + if (thint==QT_ALL) { + if(!(cached->flags&DF_AUTH)) + need_req=1; + } + else if (thint>=QT_MIN && thint<=QT_MAX) { + if(!(flags&CF_AUTH && !(flags&CF_ADDITIONAL))) + need_req=1; + } + if (ttl0 + { + char dflagstr[DFLAGSTRLEN],cflagstr[CFLAGSTRLEN]; + DEBUG_MSG("Requery decision: dflags=%s, cflags=%s, req=%i, neg=%i, timed=%i, %s=%li\n", + dflags2str(cached->flags,dflagstr),cflags2str(flags,cflagstr),need_req,neg,timed, + ttl?"ttl":"timestamp",(long)(ttl?(ttl-queryts):ttl)); + } +#endif + rc = (!neg && (need_req || timed))? RC_STALE: RC_CACHED; + return_rc_cent: + *cachedp=cached; + } + +return_rc: + if(flagsp) *flagsp=flags; + return rc; +} + + +/* + * Resolve records for name into dns_cent_t, type thint. + * q is the set of servers to query from. Set q to NULL if you want to ask the servers registered with pdnsd. + * qslist should refer to a list of server arrays already used higher up the calling chain (may be NULL). + * p_dns_cached_resolve() returns one of the following values: + * RC_OK means that the name was successfully resolved by querying other servers. + * RC_CACHED or RC_STALE means that the name was found in the cache. + * RC_NAMEERR or RC_SERVFAIL indicates a resolve error. + */ +static int p_dns_cached_resolve(query_stat_array q, const unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qstatnode_t *qslist, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa) +{ + dns_cent_t *cached=NULL; + int rc; + unsigned short flags=0; + + DEBUG_RHN_MSG("Starting cached resolve for: %s, query %s\n",RHN2STR(name),get_tname(thint)); + rc= lookup_cache_status(name, thint, &cached, &flags,queryts,c_soa); + if(rc==RC_OK) { + /* Locally defined record. */ + *cachedp=cached; + return RC_CACHED; + } + else if(rc==RC_NAMEERR) /* Locally negated name. */ + return RC_NAMEERR; + + /* update server records set onquery */ + if(global.onquery) test_onquery(); + if (global.lndown_kluge && !(flags&CF_LOCAL)) { + int i,n,linkdown=1; + lock_server_data(); + n=DA_NEL(servers); + for(i=0;irootserver<=1) { + int j,m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).is_up) { + linkdown=0; + goto done; + } + } + } + } + done: + unlock_server_data(); + if (linkdown) { + DEBUG_MSG("Link is down.\n"); + rc=RC_SERVFAIL; + goto cleanup_return; + } + } + if (rc!=RC_CACHED) { + dns_cent_t *ent; + DEBUG_MSG("Trying name servers.\n"); + if (q) + rc=p_recursive_query(q,name,thint, &ent,NULL,hops,qslist,qhlist,c_soa); + else + rc=p_dns_resolve(name,thint, &ent,hops,qhlist,c_soa); + + if(rc==RC_OK || rc==RC_CACHED || rc==RC_STALE) { + if (cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + cached=ent; + } + else if (rc==RC_SERVFAIL && cached && (flags&CF_NOPURGE)) { + /* We could not get a new record, but we have a timed-out cached one + with the nopurge flag set. This means that we shall use it even + if timed out when no new one is available*/ + DEBUG_MSG("Falling back to cached record.\n"); + rc=RC_STALE; + } + else + goto cleanup_return; + } else { + DEBUG_MSG("Using cached record.\n"); + } + *cachedp=cached; + return rc; + + cleanup_return: + if(cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + } + return rc; +} + + +/* r_dns_cached_resolve() is like p_dns_cached_resolve(), except that r_dns_cached_resolve() + will not return negatively cached entries, but returns RC_NAMEERR instead. + It also does not return RC_CACHED or RC_STALE, but RC_OK instead. +*/ +int r_dns_cached_resolve(unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa) +{ + dns_cent_t *cached; + int rc=p_dns_cached_resolve(NULL,name,thint,&cached,hops,NULL,qhlist,queryts,c_soa); + if(rc==RC_OK || rc==RC_CACHED || rc==RC_STALE) { + if(cached->flags&DF_NEGATIVE) { + if(c_soa) + *c_soa=cached->c_soa; + free_cent(cached DBG1); + pdnsd_free(cached); + return RC_NAMEERR; + } + else { + *cachedp=cached; + return RC_OK; + } + } + return rc; +} + + +static int simple_dns_cached_resolve(atup_array atup_a, int port, char edns_query, time_t timeout, + const unsigned char *name, int thint, dns_cent_t **cachedp) +{ + dns_cent_t *cached=NULL; + int rc; + + DEBUG_RHN_MSG("Starting simple cached resolve for: %s, query %s\n",RHN2STR(name),get_tname(thint)); + rc= lookup_cache_status(name, thint, &cached, NULL, time(NULL), NULL); + if(rc==RC_OK) { + /* Locally defined record. */ + *cachedp=cached; + return RC_OK; + } + else if(rc==RC_NAMEERR) /* Locally negated name. */ + return RC_NAMEERR; + + if (rc!=RC_CACHED) { + query_stat_array qserv; + int j,m; + if (cached) { + free_cent(cached DBG1); + pdnsd_free(cached); + cached=NULL; + } + DEBUG_MSG("Trying name servers.\n"); + qserv=NULL; + m=DA_NEL(atup_a); + for(j=0; jflags&DF_NEGATIVE) { + free_cent(cached DBG1); + pdnsd_free(cached); + return RC_NAMEERR; + } + + *cachedp=cached; + return RC_OK; +} + + +/* Check whether a server is responsive by sending it an (empty) query. + rep is the number of times this is tried in case of no reply. + */ +int query_uptest(pdnsd_a *addr, int port, const unsigned char *name, time_t timeout, int rep) +{ + query_stat_t qs; + int iter=0,rv; + +#ifdef ENABLE_IPV4 + if (run_ipv4) { + memset(&qs.a.sin4,0,sizeof(qs.a.sin4)); + qs.a.sin4.sin_family=AF_INET; + qs.a.sin4.sin_port=htons(port); + qs.a.sin4.sin_addr=addr->ipv4; + SET_SOCKA_LEN4(qs.a.sin4); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + memset(&qs.a.sin6,0,sizeof(qs.a.sin6)); + qs.a.sin6.sin6_family=AF_INET6; + qs.a.sin6.sin6_port=htons(port); + qs.a.sin6.sin6_flowinfo=IPV6_FLOWINFO; + qs.a.sin6.sin6_addr=addr->ipv6; + SET_SOCKA_LEN6(qs.a.sin6); + + qs.a4fallback.s_addr=INADDR_ANY; + } +#endif + qs.timeout=timeout; + qs.flags=0; + qs.nocache=0; + qs.auth_serv=0; + qs.lean_query=1; + qs.edns_query=0; + qs.needs_testing=0; + qs.trusted=1; + qs.aa=0; + qs.tc=0; + qs.ra=0; + qs.failed=0; + qs.nsdomain=NULL; + qs.rejectlist=NULL; + + try_again: + qs.state=QS_INITIAL; + qs.qm=global.query_method; + qs.s_errno=0; + rv=p_exec_query(NULL, name, T_A, &qs, NULL, NULL); + if(rv==-1) { + time_t ts, tpassed; + for(ts=time(NULL), tpassed=0;; tpassed=time(NULL)-ts) { + int event; +#ifdef NO_POLL + fd_set reads; + fd_set writes; + struct timeval tv; + FD_ZERO(&reads); + FD_ZERO(&writes); + PDNSD_ASSERT(qs.socktpassed?timeout-tpassed:0; + tv.tv_usec=0; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that doing + this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + p_cancel_query(&qs); + return 0; + } + event=select(qs.sock+1,&reads,&writes,NULL,&tv); +#else + struct pollfd pfd; + pfd.fd=qs.sock; + switch (qs.state) { + QS_READ_CASES: + pfd.events=POLLIN; + break; + QS_WRITE_CASES: + pfd.events=POLLOUT; + break; + default: + pfd.events=0; + } + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that doing + this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + p_cancel_query(&qs); + return 0; + } + event=poll(&pfd,1,timeout>tpassed?(timeout-tpassed)*1000:0); +#endif + if (event<0) { + if(errno==EINTR && is_interrupted_servstat_thread()) { + DEBUG_MSG("poll/select interrupted in server status thread.\n"); + } + else + log_warn("poll/select failed: %s",strerror(errno)); + p_cancel_query(&qs); + return 0; + } + if(event==0) { + /* timed out */ + p_cancel_query(&qs); + if(++iter. +*/ + + +#ifndef DNS_QUERY_H +#define DNS_QUERY_H + +#include "cache.h" + +/* Default UDP buffer size (when EDNS is not used). */ +#define UDP_BUFSIZE 512 + + +typedef struct qhintnode_s qhintnode_t; + +/* --- parallel query */ +int r_dns_cached_resolve(unsigned char *name, int thint, dns_cent_t **cachedp, + int hops, qhintnode_t *qhlist, time_t queryts, + unsigned char *c_soa); +#define dns_cached_resolve(name,thint,cachedp,hops,queryts,c_soa) \ + r_dns_cached_resolve(name,thint,cachedp,hops,NULL,queryts,c_soa) + +addr2_array dns_rootserver_resolv(atup_array atup_a, int port, char edns_query, time_t timeout); +int query_uptest(pdnsd_a *addr, int port, const unsigned char *name, time_t timeout, int rep); + +/* --- from dns_answer.c */ +int add_opt_pseudo_rr(dns_msg_t **ans, size_t *sz, size_t *allocsz, + unsigned short udpsize, unsigned short rcode, + unsigned short ednsver, unsigned short Zflags); +size_t remove_opt_pseudo_rr(dns_msg_t *ans, size_t sz); + +#endif diff --git a/service/src/main/jni/pdnsd/src/error.c b/service/src/main/jni/pdnsd/src/error.c new file mode 100644 index 0000000..4584866 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/error.c @@ -0,0 +1,142 @@ +/* error.c - Error handling + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2005, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "helpers.h" +#include "conff.h" + + +pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER; +volatile short int use_log_lock=0; + +/* + * Initialize a mutex for io-locking in order not to produce gibberish on + * multiple simultaneous errors. + */ +/* This is now defined as an inline function in error.h */ +#if 0 +void init_log_lock(void) +{ + use_log_lock=1; +} +#endif + +/* We crashed? Ooops... */ +void crash_msg(char *msg) +{ + log_error("%s", msg); + log_error("pdnsd probably crashed due to a bug. Please consider sending a bug"); + log_error("report to p.a.rombouts@home.nl or tmoestl@gmx.net"); +} + +/* Log a warning, error or info message. + * If we are a daemon, use the syslog. s is a format string like in printf, + * the optional following arguments are the arguments like in printf */ +void log_message(int prior, const char *s, ...) +{ + int gotlock=0; + va_list va; + FILE *f; + + if (use_log_lock) { + gotlock=softlock_mutex(&loglock); + /* If we failed to get the lock and the type of the + message is "info" or less important, then don't bother. */ + if(!gotlock && prior>=LOG_INFO) + return; + } + if (global.daemon) { + openlog("pdnsd",LOG_PID,LOG_DAEMON); + va_start(va,s); + vsyslog(prior,s,va); + va_end(va); + closelog(); + } + else { + f=stderr; +#if DEBUG > 0 + goto printtofile; + } + if(debug_p) { + f=dbg_file; + printtofile: +#endif + { + char ts[sizeof "* 12/31 23:59:59| "]; + time_t tt = time(NULL); + struct tm tm; + + if(!localtime_r(&tt, &tm) || strftime(ts, sizeof(ts), "* %m/%d %T| ", &tm) <=0) + ts[0]=0; + fprintf(f,"%spdnsd: %s: ", ts, + prior<=LOG_CRIT?"critical": + prior==LOG_ERR?"error": + prior==LOG_WARNING?"warning": + "info"); + } + va_start(va,s); + vfprintf(f,s,va); + va_end(va); + { + const char *p=strchr(s,0); + if(!p || p==s || *(p-1)!='\n') + fputc('\n',f); + } + } + if (gotlock) + pthread_mutex_unlock(&loglock); +} + + +#if DEBUG > 0 +/* XXX: The timestamp generation makes this a little heavy-weight */ +void debug_msg(int c, const char *fmt, ...) +{ + va_list va; + + if (!c) { + char ts[sizeof "12/31 23:59:59"]; + time_t tt = time(NULL); + struct tm tm; + unsigned *id; + + if(localtime_r(&tt, &tm) && strftime(ts, sizeof(ts), "%m/%d %T", &tm) > 0) { + if((id = (unsigned *)pthread_getspecific(thrid_key))) + fprintf(dbg_file,"%u %s| ", *id, ts); + else + fprintf(dbg_file,"- %s| ", ts); + } + } + va_start(va,fmt); + vfprintf(dbg_file,fmt,va); + va_end(va); + fflush(dbg_file); +} +#endif /* DEBUG */ diff --git a/service/src/main/jni/pdnsd/src/error.h b/service/src/main/jni/pdnsd/src/error.h new file mode 100644 index 0000000..1678744 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/error.h @@ -0,0 +1,115 @@ +/* error.h - Error handling + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef ERROR_H +#define ERROR_H + +#include +#include +#include +#include +#include +#include + +#include "thread.h" +#include "helpers.h" +#include "pdnsd_assert.h" + +/* --- from error.c */ +extern volatile short int use_log_lock; +/* --- */ + +void crash_msg(char *msg); + +inline static void init_log_lock(void) __attribute__((always_inline)); +inline static void init_log_lock(void) +{ + use_log_lock=1; +} + +void log_message(int prior,const char *s, ...) printfunc(2, 3); +#if !defined(CPP_C99_VARIADIC_MACROS) +/* GNU C Macro Varargs style. */ +#define log_error(args...) log_message(LOG_ERR,args) +#define log_warn(args...) log_message(LOG_WARNING,args) +#define log_info(level,args...) {if((level)<=global.verbosity) log_message(LOG_INFO,args);} +#else +/* ANSI C99 style. */ +#define log_error(...) log_message(LOG_ERR,__VA_ARGS__) +#define log_warn(...) log_message(LOG_WARNING,__VA_ARGS__) +#define log_info(level,...) {if((level)<=global.verbosity) log_message(LOG_INFO,__VA_ARGS__);} +#endif + +/* Following are some ugly macros for debug messages that + * should inhibit any code generation when DEBUG is not defined. + * Of course, those messages could be done in a function, but I + * want to save the overhead when DEBUG is not defined. + * debug_p needs to be defined (by including conff.h), or you + * will get strange errors. + * A macro call expands to a complete statement, so a semicolon after + * the macro call is redundant. + * The arguments are normal printfs, so you know how to use the args + */ +#if DEBUG>0 +void debug_msg(int c, const char *fmt, ...) printfunc(2, 3); +/* from main.c */ +extern FILE *dbg_file; +#endif + +#if !defined(CPP_C99_VARIADIC_MACROS) +/* GNU C Macro Varargs style. */ +# if DEBUG > 0 +# define DEBUG_MSG(args...) {if (debug_p) debug_msg(0,args);} +# define DEBUG_MSGC(args...) {if (debug_p) debug_msg(1,args);} +# define DEBUG_PDNSDA_MSG(args...) {char _debugsockabuf[ADDRSTR_MAXLEN]; DEBUG_MSG(args);} +# define PDNSDA2STR(a) pdnsd_a2str(a,_debugsockabuf,sizeof(_debugsockabuf)) +# define DEBUG_RHN_MSG(args...) {unsigned char _debugstrbuf[DNSNAMEBUFSIZE]; DEBUG_MSG(args);} +# define RHN2STR(a) rhn2str(a,_debugstrbuf,sizeof(_debugstrbuf)) +# else +# define DEBUG_MSG(args...) +# define DEBUG_MSGC(args...) +# define DEBUG_PDNSDA_MSG(args...) +# define DEBUG_RHN_MSG(args...) +# endif /* DEBUG > 0 */ +#else +/* ANSI C99 style. */ +# if DEBUG > 0 +/* + * XXX: The ANSI and GCC variadic macros should be merged as far as possible, but that + * might make things even more messy... + */ +# define DEBUG_MSG(...) {if (debug_p) debug_msg(0,__VA_ARGS__);} +# define DEBUG_MSGC(...) {if (debug_p) debug_msg(1,__VA_ARGS__);} +# define DEBUG_PDNSDA_MSG(...) {char _debugsockabuf[ADDRSTR_MAXLEN]; DEBUG_MSG(__VA_ARGS__);} +# define PDNSDA2STR(a) pdnsd_a2str(a,_debugsockabuf,ADDRSTR_MAXLEN) +# define DEBUG_RHN_MSG(...) {unsigned char _debugstrbuf[DNSNAMEBUFSIZE]; DEBUG_MSG(__VA_ARGS__);} +# define RHN2STR(a) rhn2str(a,_debugstrbuf,sizeof(_debugstrbuf)) +# else +# define DEBUG_MSG(...) +# define DEBUG_MSGC(...) +# define DEBUG_PDNSDA_MSG(...) +# define DEBUG_RHN_MSG(...) +# endif /* DEBUG > 0 */ +#endif + +#endif diff --git a/service/src/main/jni/pdnsd/src/freebsd_netinet_ip_icmp.h b/service/src/main/jni/pdnsd/src/freebsd_netinet_ip_icmp.h new file mode 100644 index 0000000..e4577ce --- /dev/null +++ b/service/src/main/jni/pdnsd/src/freebsd_netinet_ip_icmp.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD: src/sys/netinet/ip_icmp.h,v 1.20 2003/03/21 15:28:10 mdodd Exp $ + */ + +#ifndef _NETINET_IP_ICMP_H_ +#define _NETINET_IP_ICMP_H_ + +/* + * Interface Control Message Protocol Definitions. + * Per RFC 792, September 1981. + */ + +/* + * Internal of an ICMP Router Advertisement + */ +struct icmp_ra_addr { + u_int32_t ira_addr; + u_int32_t ira_preference; +}; + +/* + * Structure of an icmp header. + */ +struct icmp { + u_char icmp_type; /* type of message, see below */ + u_char icmp_code; /* type sub code */ + u_short icmp_cksum; /* ones complement cksum of struct */ + union { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + n_short icd_id; + n_short icd_seq; + } ih_idseq; + int ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu { + n_short ipm_void; + n_short ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv { + u_char irt_num_addrs; + u_char irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union { + struct id_ts { /* ICMP Timestamp */ + n_time its_otime; /* Originate */ + n_time its_rtime; /* Receive */ + n_time its_ttime; /* Transmit */ + } id_ts; + struct id_ip { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + char id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; + +/* + * Lower bounds on packet lengths for various types. + * For the error advice packets must first insure that the + * packet is large enough to contain the returned ip header. + * Only then can we do the check to see if 64 bits of packet + * data have been returned, since we need to check the returned + * ip header length. + */ +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */ +#define ICMP_MASKLEN 12 /* address mask */ +#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ +#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) + /* N.B.: must separately check that ip_hl >= 5 */ + +/* + * Definition of type and code field values. + */ +#define ICMP_ECHOREPLY 0 /* echo reply */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ +#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ +#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ +#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ +#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ +#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ +#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ +#define ICMP_UNREACH_FILTER_PROHIB 13 /* admin prohib */ +#define ICMP_UNREACH_HOST_PRECEDENCE 14 /* host prec vio. */ +#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 /* prec cutoff */ +#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ +#define ICMP_REDIRECT 5 /* shorter route, codes: */ +#define ICMP_REDIRECT_NET 0 /* for network */ +#define ICMP_REDIRECT_HOST 1 /* for host */ +#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ +#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ +#define ICMP_ECHO 8 /* echo service */ +#define ICMP_ROUTERADVERT 9 /* router advertisement */ +#define ICMP_ROUTERSOLICIT 10 /* router solicitation */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ +#define ICMP_PARAMPROB 12 /* ip header bad */ +#define ICMP_PARAMPROB_ERRATPTR 0 /* error at param ptr */ +#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ +#define ICMP_PARAMPROB_LENGTH 2 /* bad length */ +#define ICMP_TSTAMP 13 /* timestamp request */ +#define ICMP_TSTAMPREPLY 14 /* timestamp reply */ +#define ICMP_IREQ 15 /* information request */ +#define ICMP_IREQREPLY 16 /* information reply */ +#define ICMP_MASKREQ 17 /* address mask request */ +#define ICMP_MASKREPLY 18 /* address mask reply */ + +#define ICMP_MAXTYPE 18 + +#define ICMP_INFOTYPE(type) \ + ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ + (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ + (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ + (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ + (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) + +#ifdef _KERNEL +void icmp_error(struct mbuf *, int, int, n_long, struct ifnet *); +void icmp_input(struct mbuf *, int); +#endif + +#endif diff --git a/service/src/main/jni/pdnsd/src/hash.c b/service/src/main/jni/pdnsd/src/hash.c new file mode 100644 index 0000000..12e2074 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/hash.c @@ -0,0 +1,322 @@ +/* hash.c - Manage hashes for cached dns records + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include "hash.h" +#include "cache.h" +#include "error.h" +#include "helpers.h" +#include "consts.h" + + +/* This is not a perfect hash, but I hope it holds. It is designed for 1024 hash + * buckets, and hashes strings with case-insensitivity. + * It is position-aware in a limited way. + * It is exactly seen a two-way hash: because I do not want to exaggerate + * the hash buckets (i do have 1024), but I hash strings and string-comparisons + * are expensive, I save another 32 bit hash in each hash element that is checked + * before the string. The 32 bit hash is also used to order the entries in a hash chain. + * I hope not to have all too much collision concentration. + * + * The ip hash was removed. I don't think it concentrated the collisions too much. + * If it does, the hash algorithm needs to be changed, rather than using another + * hash. + * Some measurements seem to indicate that the hash algorithm is doing reasonable well. + */ + +dns_hash_ent_t *hash_buckets[HASH_NUM_BUCKETS]; + + +/* + * Hash a dns name (length-byte string format) to HASH_SZ bit. + * *rhash is set to a long int hash. + */ +static unsigned dns_hash(const unsigned char *str, unsigned long *rhash) +{ + unsigned s,i,lb,c; + unsigned long r; + s=0; r=0; + i=0; + while((lb=str[i])) { + s+=lb<<(i%(HASH_SZ-5)); + r+=((unsigned long)lb)<<(i%(8*sizeof(unsigned long)-7)); + ++i; + do { + c=toupper(str[i]); + s+=c<<(i%(HASH_SZ-5)); + r+=((unsigned long)c)<<(i%(8*sizeof(unsigned long)-7)); + ++i; + } while(--lb); + } + s=(s&HASH_BITMASK)+((s&(~HASH_BITMASK))>>HASH_SZ); + s=(s&HASH_BITMASK)+((s&(~HASH_BITMASK))>>HASH_SZ); + s &= HASH_BITMASK; +#ifdef DEBUG_HASH + { + unsigned char buf[DNSNAMEBUFSIZE]; + printf("Diagnostic: hashes for %s: %03x,%04lx\n",rhn2str(str,buf,sizeof(buf)),s,r); + } +#endif + if(rhash) *rhash=r; + return s; +} + +/* + * Initialize hash to hold a dns hash table + */ +/* This is now defined as an inline function in hash.h */ +#if 0 +void mk_dns_hash() +{ + int i; + for(i=0;irhash<=rh) { + if (he->rhash==rh && rhnicmp(key,he->data->qname)) { + retval = he->data; + break; + } + hep = &he->next; + } + if(loc) { + loc->pos = hep; + loc->rhash = rh; + } + return retval; +} + +/* + Add a cache entry to the hash table. + + loc must contain the location where the the new entry should be inserted + (this location can be obtained with dns_lookup). + + add_dns_hash returns 1 on success, or 0 if out of memory. +*/ +int add_dns_hash(dns_cent_t *data, dns_hash_loc_t *loc) +{ + dns_hash_ent_t *he = malloc(sizeof(dns_hash_ent_t)); + + if(!he) + return 0; + + he->next = *(loc->pos); + he->rhash = loc->rhash; + he->data = data; + *(loc->pos) = he; + + return 1; +} + +/* + Delete the hash entry indentified by the location returned by dns_lookup(). +*/ +dns_cent_t *del_dns_hash_ent(dns_hash_loc_t *loc) +{ + dns_hash_ent_t *he = *(loc->pos); + dns_cent_t *data; + + *(loc->pos) = he->next; + data = he->data; + free(he); + return data; +} + +/* + * Delete the first entry indexed by key from the hash. Returns the data field or NULL. + * Since two cents are not allowed to be for the same host name, there will be only one. + */ +dns_cent_t *del_dns_hash(const unsigned char *key) +{ + unsigned idx; + unsigned long rh; + dns_hash_ent_t **hep,*he; + dns_cent_t *data; + + idx = dns_hash(key,&rh); + hep = &hash_buckets[idx]; + while ((he= *hep) && he->rhash<=rh) { + if (he->rhash==rh && rhnicmp(key,he->data->qname)) { + *hep = he->next; + data = he->data; + free(he); + return data; + } + hep = &he->next; + } + return NULL; /* not found */ +} + + +/* + * Delete all entries in a hash bucket. + */ +void free_dns_hash_bucket(int i) +{ + dns_hash_ent_t *he,*hen; + + he=hash_buckets[i]; + hash_buckets[i]=NULL; + while (he) { + hen=he->next; + del_cent(he->data); + free(he); + he=hen; + } +} + +/* + * Delete all entries in a hash bucket whose names match those in + * an include/exclude list. + */ +void free_dns_hash_selected(int i, slist_array sla) +{ + dns_hash_ent_t **hep,*he,*hen; + int j,m=DA_NEL(sla); + + hep= &hash_buckets[i]; + he= *hep; + + while (he) { + unsigned char *name=he->data->qname; + for(j=0;jdomain,&nrem,&lrem); + if(!lrem && (!sl->exact || !nrem)) { + if(sl->rule==C_INCLUDED) + goto delete_entry; + else + break; + } + } + /* default policy is not to delete */ + hep= &he->next; + he= *hep; + continue; + + delete_entry: + *hep=hen=he->next;; + del_cent(he->data); + free(he); + he=hen; + } +} + +/* + * Delete the whole hash table, freeing all memory + */ +void free_dns_hash() +{ + int i; + dns_hash_ent_t *he,*hen; + for (i=0;inext; + del_cent(he->data); + free(he); + he=hen; + } + } +} + +/* + * The following functions are for iterating over the hash. + * fetch_first returns the data field of the first element (or NULL if there is none), and fills pos + * for subsequent calls of fetch_next. + * fetch_next returns the data field of the element after the element that was returned by the last + * call with the same position argument (or NULL if there is none) + * + * Note that these are designed so that you may actually delete the elements you retrieved from the hash. + */ +dns_cent_t *fetch_first(dns_hash_pos_t *pos) +{ + int i; + for (i=0;ibucket=i; + pos->ent=he->next; + return he->data; + } + } + return NULL; +} + +dns_cent_t *fetch_next(dns_hash_pos_t *pos) +{ + dns_hash_ent_t *he=pos->ent; + int i; + if (he) { + pos->ent=he->next; + return he->data; + } + + for (i=pos->bucket+1;ibucket=i; + pos->ent=he->next; + return he->data; + } + } + return NULL; +} + +#ifdef DEBUG_HASH +void dumphash() +{ + if(debug_p) { + int i, j; + dns_hash_ent_t *he; + + for (i=0; inext, j++) ; + DEBUG_MSG("bucket %d: %d entries\n", i, j); + } + } +} +#endif diff --git a/service/src/main/jni/pdnsd/src/hash.h b/service/src/main/jni/pdnsd/src/hash.h new file mode 100644 index 0000000..db25a34 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/hash.h @@ -0,0 +1,83 @@ +/* hash.h - Manage hashes for cached dns records + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2003, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _HASH_H_ +#define _HASH_H_ +#include +#include "cache.h" + +typedef struct dns_hash_ent_s { + struct dns_hash_ent_s *next; + unsigned long rhash; /* this is a better hash */ + dns_cent_t *data; +} dns_hash_ent_t; + +/* Redefine this if you want another hash size. Should work ;-). + * The number of hash buckets is computed as power of two; + * so, e.g. HASH_SZ set to 10 yields 1024 hash rows (2^10 or 1<<10). + * Only powers of two are possible conveniently. + * HASH_SZ may not be bigger than 32 (if you set it even close to that value, + * you are nuts.) */ +/* #define HASH_SZ 10 */ /* Now defined in config.h */ +#define HASH_NUM_BUCKETS (1<. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipvers.h" +#include "thread.h" +#include "error.h" +#include "helpers.h" +#include "cache.h" +#include "conff.h" + + +/* + * This is to exit pdnsd from any thread. + */ +void pdnsd_exit() +{ + pthread_kill(main_thrid,SIGTERM); + pthread_exit(NULL); +} + +/* + * Try to grab a mutex. If we can't, fail. This will loop until we get the + * mutex or fail. This is only used in debugging code or at exit, otherwise + * we might run into lock contention problems. + */ +int softlock_mutex(pthread_mutex_t *mutex) +{ + unsigned int tr=0; + while(pthread_mutex_trylock(mutex)) { + if (++tr>=SOFTLOCK_MAXTRIES) + return 0; + usleep_r(10000); + } + return 1; +} + +/* + * setuid() and setgid() for a specified user. + */ +int run_as(const char *user) +{ + if (user[0]) { +#ifdef HAVE_GETPWNAM_R + struct passwd pwdbuf, *pwd; + size_t buflen; + int err; + + for(buflen=128;; buflen*=2) { + char buf[buflen]; /* variable length array */ + + /* Note that we use getpwnam_r() instead of getpwnam(), + which returns its result in a statically allocated buffer and + cannot be considered thread safe. + Doesn't use NSS! */ + err=getpwnam_r(user, &pwdbuf, buf, buflen, &pwd); + if(err==0 && pwd) { + /* setgid first, because we may not be allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid)!=0) { + log_error("Could not change group id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + + /* initgroups uses NSS, so we can disable it, + i.e. we might need DNS for LDAP lookups, which times out */ + if (global.use_nss && (initgroups(user, pwd->pw_gid)!=0)) { + log_error("Could not initialize the group access list of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + if (setuid(pwd->pw_uid)!=0) { + log_error("Could not change user id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + break; + } + else if(err!=ERANGE) { + if(err) + log_error("run_as user '%s' could not be found: %s",user,strerror(err)); + else + log_error("run_as user '%s' could not be found.",user); + return 0; + } + else if(buflen>=16*1024) { + /* If getpwnam_r() seems defective, call it quits rather than + keep on allocating ever larger buffers until we crash. */ + log_error("getpwnam_r() requires more than %u bytes of buffer space.",(unsigned)buflen); + return 0; + } + /* Else try again with larger buffer. */ + } +#else + /* No getpwnam_r() :-( We'll use getpwnam() and hope for the best. */ + struct passwd *pwd; + + if (!(pwd=getpwnam(user))) { + log_error("run_as user %s could not be found.",user); + return 0; + } + /* setgid first, because we may not allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid)!=0) { + log_error("Could not change group id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + /* initgroups uses NSS, so we can disable it, + i.e. we might need DNS for LDAP lookups, which times out */ + if (global.use_nss && (initgroups(user, pwd->pw_gid)!=0)) { + log_error("Could not initialize the group access list of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } + if (setuid(pwd->pw_uid)!=0) { + log_error("Could not change user id to that of run_as user '%s': %s", + user,strerror(errno)); + return 0; + } +#endif + } + + return 1; +} + +/* + * returns whether c is allowed in IN domain names + */ +#if 0 +int isdchar (unsigned char c) +{ + if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='-' +#ifdef UNDERSCORE + || c=='_' +#endif + ) + return 1; + return 0; +} +#endif + +/* + * Convert a string given in dotted notation to the transport format (length byte prepended + * domain name parts, ended by a null length sequence) + * The memory areas referenced by str and rhn may not overlap. + * The buffer rhn points to is assumed to be at least DNSNAMEBUFSIZE bytes in size. + * + * Returns 1 if successful, otherwise 0. + */ +int str2rhn(const unsigned char *str, unsigned char *rhn) +{ + unsigned int i,j; + + if(*str=='.' && !*(str+1)) { + /* Special case: root domain */ + rhn[0]=0; + return 1; + } + + for(i=0;;) { + unsigned int jlim= i+63; + if(jlim>DNSNAMEBUFSIZE-2) jlim=DNSNAMEBUFSIZE-2; /* DNSNAMEBUFSIZE-2 because the termination 0 has to follow */ + for(j=i; str[j] && str[j]!='.'; ++j) { + if(j>=jlim) return 0; + rhn[j+1]=str[j]; + } + if(!str[j]) + break; + if(j<=i) + return 0; + rhn[i]=(unsigned char)(j-i); + i = j+1; + } + + rhn[i]=0; + if (j>i || i==0) + return 0; + return 1; +} + +/* + parsestr2rhn is essentially the same as str2rhn, except that it doesn't look beyond + the first len chars in the input string. It also tolerates strings + not ending in a dot and returns a message in case of an error. + */ +const char *parsestr2rhn(const unsigned char *str, unsigned int len, unsigned char *rhn) +{ + unsigned int i,j; + + if(len>0 && *str=='.' && (len==1 || !*(str+1))) { + /* Special case: root domain */ + rhn[0]=0; + return NULL; + } + + i=0; + do { + unsigned int jlim= i+63; + if(jlim>DNSNAMEBUFSIZE-2) jlim=DNSNAMEBUFSIZE-2; + for(j=i; j=jlim) + return "Domain name element too long"; + rhn[j+1]=str[j]; + } + + if(j<=i) { + if(j=2) + str[j++]='.'; + } + else { + do { + for (;lb;--lb) { + unsigned char c; + if(j+2>=size) + goto overflow; + c=rhn[i++]; + if(isgraph(c)) { + if(c=='.' || c=='\\' || c=='"') { + str[j++]='\\'; + if(j+2>=size) + goto overflow; + } + str[j++]=c; + } + else { + unsigned int rem=size-1-j; + int n=snprintf(charp &str[j],rem,"\\%03o",c); + if(n<0 || n>=rem) { + str[j++]='.'; + goto overflow; + } + j+=n; + } + } + str[j++]='.'; + lb=rhn[i++]; + } while(lb); + } + str[j]=0; + return str; + + overflow: + j=size; + str[--j]=0; + if(j>0) { + str[--j]='.'; + if(j>0) { + str[--j]='.'; + if(j>0) + str[--j]='.'; + } + } + return str; +} + +/* Return the length of a domain name in transport format. + The definition has in fact been moved to helpers.h as an inline function. + Note added by Paul Rombouts: + Compared to the definition used by Thomas Moestl (strlen(rhn)+1), the following definition of rhnlen + may yield a different result in certain error situations (when a domain name segment contains null byte). +*/ +#if 0 +unsigned int rhnlen(const unsigned char *rhn) +{ + unsigned int i=0,lb; + + while((lb=rhn[i++])) + i+=lb; + return i; +} +#endif + +/* + * Non-validating rhn copy (use with checked or generated data only). + * Returns number of characters copied. The buffer dst points to is assumed to be DNSNAMEBUFSIZE (or + * at any rate large enough) bytes in size. + * The answer assembly code uses this; it is guaranteed to not clobber anything + * after the name. + */ +unsigned int rhncpy(unsigned char *dst, const unsigned char *src) +{ + unsigned int len = rhnlen(src); + + PDNSD_ASSERT(len<=DNSNAMEBUFSIZE,"rhncpy: src too long!"); + memcpy(dst,src,len>DNSNAMEBUFSIZE?DNSNAMEBUFSIZE:len); + return len; +} + + +/* Check whether a name is a normal wire-encoded domain name, + i.e. is not compressed, doesn't use extended labels and is not + too long. +*/ +int isnormalencdomname(const unsigned char *rhn, unsigned maxlen) +{ + unsigned int i,lb; + + if(maxlen>DNSNAMEBUFSIZE) + maxlen=DNSNAMEBUFSIZE; + for(i=0;;) { + if(i>=maxlen) return 0; + lb=rhn[i++]; + if(lb==0) break; + if(lb>0x3f) return 0; + i += lb; + } + + return 1; +} + +int str2pdnsd_a(const char *addr, pdnsd_a *a) +{ +#ifdef ENABLE_IPV4 + if (run_ipv4) { + return inet_aton(addr,&a->ipv4); + } +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + /* Try to map an IPv4 address to IPv6 */ + struct in_addr a4; + if(inet_aton(addr,&a4)) { + a->ipv6=global.ipv4_6_prefix; + ((uint32_t *)(&a->ipv6))[3]=a4.s_addr; + return 1; + } + return inet_pton(AF_INET6,addr,&a->ipv6)>0; + } +#endif + /* return 0; */ +} + +/* definition moved to helpers.h */ +#if 0 +int is_inaddr_any(pdnsd_a *a) +{ + return SEL_IPVER( a->ipv4.s_addr==INADDR_ANY, + IN6_IS_ADDR_UNSPECIFIED(&a->ipv6) ); +} +#endif + +/* + * This is used for user output only, so it does not matter when an error occurs. + */ +const char *pdnsd_a2str(pdnsd_a *a, char *buf, int maxlen) +{ + const char *res= SEL_IPVER( inet_ntop(AF_INET,&a->ipv4,buf,maxlen), + inet_ntop(AF_INET6,&a->ipv6,buf,maxlen) ); + if (!res) { + log_error("inet_ntop: %s", strerror(errno)); + return "?.?.?.?"; + } + + return res; +} + + +/* Appropriately set our random device */ +#ifdef R_DEFAULT +# if (TARGET == TARGET_BSD) && !defined(__NetBSD__) +# define R_ARC4RANDOM 1 +# else +# define R_RANDOM 1 +# endif +#endif + +#ifdef RANDOM_DEVICE +FILE *rand_file; +#endif + +#ifdef R_RANDOM +void init_crandom() +{ + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv,&tz); + srandom(tv.tv_sec^tv.tv_usec); /* not as guessable as time() */ +} +#endif + +/* initialize the PRNG */ +int init_rng() +{ +#ifdef RANDOM_DEVICE + if (!(rand_file=fopen(RANDOM_DEVICE,"r"))) { + log_error("Could not open %s.",RANDOM_DEVICE); + return 0; + } +#endif +#ifdef R_RANDOM + init_crandom(); +#endif + return 1; +} + +/* The following function is now actually defined as a macro in helpers.h */ +#if 0 +void free_rng() +{ +#ifdef RANDOM_DEVICE + if (rand_file) + fclose(rand_file); +#endif +} +#endif + +/* generate a (more or less) random number 16 bits long. */ +unsigned short get_rand16() +{ +#ifdef RANDOM_DEVICE + unsigned short rv; + + if (rand_file) { + if (fread(&rv,sizeof(rv),1, rand_file)!=1) { + log_error("Error while reading from random device: %s", strerror(errno)); + pdnsd_exit(); + } + return rv&0xffff; + } else + return random()&0xffff; +#endif +#ifdef R_RANDOM + return random()&0xffff; +#endif +#ifdef R_ARC4RANDOM + return arc4random()&0xffff; +#endif +} + +/* fsprintf does formatted output to a file descriptor. + The functionality is similar to fprintf, but note that fd + is of type int instead of FILE*. + This function has been rewritten by Paul Rombouts */ +int fsprintf(int fd, const char *format, ...) +{ + int n; + va_list va; + + { + char buf[256]; + + va_start(va,format); + n=vsnprintf(buf,sizeof(buf),format,va); + va_end(va); + + if(n<(int)sizeof(buf)) { + if(n>0) n=write_all(fd,buf,n); + return n; + } + } + /* retry with a right sized buffer, needs glibc 2.1 or higher to work */ + { + unsigned bufsize=n+1; + char buf[bufsize]; + + va_start(va,format); + n=vsnprintf(buf,bufsize,format,va); + va_end(va); + + if(n>0) n=write_all(fd,buf,n); + } + return n; +} + +/* Convert data into a hexadecimal representation (for debugging purposes).. + The result is stored in the character array buf. + If buf is not large enough to hold the result, the + truncation is indicated by trailing dots. +*/ +void hexdump(const void *data, int dlen, char *buf, int buflen) +{ + const unsigned char *p=data; + int i,j=0; + for(i=0;i=rem) goto truncated; + j += n; + } + goto done; + + truncated: + if(j>=6) { + j -= 3; + if(j+4>=buflen) + j -= 3; + buf[j++]=' '; + buf[j++]='.'; + buf[j++]='.'; + buf[j++]='.'; + } + else { + int ndots=buflen-1; + if(ndots>3) ndots=3; + j=0; + while(j=size) + return -1; + c=in[i]; + if(!isprint(c)) { + int rem=size-j; + int n=snprintf(&str[j],rem,"\\%03o",c); + if(n<0 || n>=rem) { + return -1; + } + j+=n; + } + else { + if(c=='\\' || c=='"') { + str[j++]='\\'; + if(j+1>=size) + return -1; + } + str[j++]=c; + } + } + str[j]=0; + return j; +} + +/* + * This is not like strcmp, but will return 1 on match or 0 if the + * strings are different. + */ +/* definition moved to helpers.h as an inline function. */ +#if 0 +int stricomp(char *a, char *b) +{ + int i; + if (strlen(a) != strlen(b)) + return 0; + for (i=0;i= dstsz-1 && o!='\0') + return 0; + return 1; +} +#endif + +#ifndef HAVE_GETLINE +/* Note by Paul Rombouts: I know that getline is a GNU extension and is not really portable, + but the alternative standard functions have some real problems. + The following substitute does not have exactly the same semantics as the GNU getline, + but it should be good enough, as long as the stream doesn't contain any null chars. + This version is actually based on fgets_realloc() that I found in the WWWOFFLE source. +*/ + +#define BUFSIZE 256 +int getline(char **lineptr, size_t *n, FILE *stream) +{ + char *line=*lineptr; + size_t sz=*n,i; + + if(!line || sz=sz) { + /* retry with a right sized buffer, needs glibc 2.1 or higher to work */ + sz=n+1; + { + char *tmp=realloc(line,sz); + if(tmp) { + line=tmp; + n=vsnprintf(line,sz,format,vasave); + } + else + n= -1; + } + } + va_end(vasave); + + if(n>=0) + *lineptr=line; + else + free(line); + return n; +} +#endif + +#ifndef HAVE_ASPRINTF +int asprintf (char **lineptr, const char *format, ...) +{ + int n; + va_list va; + + va_start(va,format); + n=vasprintf(lineptr,format,va); + va_end(va); + + return n; +} +#endif + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, size_t size) +{ + const char *rc = NULL; + + if (src != NULL && dst != NULL && size > 0) { + switch (af) { + case AF_INET: + { + const unsigned char *p=src; + int n = snprintf(dst, size, "%u.%u.%u.%u", + p[0],p[1],p[2],p[3]); + if (n >= 0 && n < size) rc = dst; + } + break; + +#ifdef AF_INET6 + case AF_INET6: + { + const unsigned char *p=src; + unsigned int i,offs=0; + for (i=0;i<16;i+=2) { + int n=snprintf(dst+offs, size-offs,i==0?"%x":":%x", ((unsigned)p[i]<<8)|p[i+1]); + if(n<0) return NULL; + offs+=n; + if(offs>=size) return NULL; + } + rc = dst; + } + break; +#endif + } + } + + return rc; +} +#endif diff --git a/service/src/main/jni/pdnsd/src/helpers.h b/service/src/main/jni/pdnsd/src/helpers.h new file mode 100644 index 0000000..11dc6d3 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/helpers.h @@ -0,0 +1,309 @@ +/* helpers.h - Various helper functions + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef HELPERS_H +#define HELPERS_H + +#include +#include +#include +#include +#include +#include "cache.h" +#include "pdnsd_assert.h" + +#define SOFTLOCK_MAXTRIES 1000 + +int run_as(const char *user); +void pdnsd_exit(void); +int softlock_mutex(pthread_mutex_t *mutex); + +#if 0 +inline static int isdchar (unsigned char c) +{ + return ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='-' +#ifdef UNDERSCORE + || c=='_' +#endif + ); +} +#endif + +const unsigned char *rhn2str(const unsigned char *rhn, unsigned char *str, unsigned int size); +int str2rhn(const unsigned char *str, unsigned char *rhn); +const char *parsestr2rhn(const unsigned char *str, unsigned int len, unsigned char *rhn); + +/* Note added by Paul Rombouts: + Compared to the definition used by Thomas Moestl (strlen(rhn)+1), the following definition of rhnlen + may yield a different result in certain error situations (when a domain name segment contains a null byte). +*/ +inline static unsigned int rhnlen(const unsigned char *rhn) + __attribute__((always_inline)); +inline static unsigned int rhnlen(const unsigned char *rhn) +{ + unsigned int i=0,lb; + + while((lb=rhn[i++])) + i+=lb; + return i; +} + +/* Skip k segments in a name in length-byte string notation. */ +inline static const unsigned char *skipsegs(const unsigned char *nm, unsigned k) + __attribute__((always_inline)); +inline static const unsigned char *skipsegs(const unsigned char *nm, unsigned k) +{ + unsigned lb; + for(;k && (lb= *nm); --k) { + nm += lb+1; + } + return nm; +} + +/* Skip a name in length-byte string notation and return a pointer to the + position right after the terminating null byte. +*/ +inline static unsigned char *skiprhn(unsigned char *rhn) + __attribute__((always_inline)); +inline static unsigned char *skiprhn(unsigned char *rhn) +{ + unsigned lb; + + while((lb= *rhn++)) + rhn += lb; + return rhn; +} + +/* count the number of name segments of a name in length-byte string notation. */ +inline static unsigned int rhnsegcnt(const unsigned char *rhn) + __attribute__((always_inline)); +inline static unsigned int rhnsegcnt(const unsigned char *rhn) +{ + unsigned int res=0,lb; + + while((lb= *rhn)) { + ++res; + rhn += lb+1; + } + return res; +} + +unsigned int rhncpy(unsigned char *dst, const unsigned char *src); +int isnormalencdomname(const unsigned char *rhn, unsigned maxlen); + +inline static int is_inaddr_any(pdnsd_a *a) __attribute__((always_inline)); +inline static int is_inaddr_any(pdnsd_a *a) +{ + return SEL_IPVER( a->ipv4.s_addr==INADDR_ANY, + IN6_IS_ADDR_UNSPECIFIED(&a->ipv6) ); +} + + +inline static int same_inaddr(pdnsd_a *a, pdnsd_a *b) + __attribute__((always_inline)); +inline static int same_inaddr(pdnsd_a *a, pdnsd_a *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) ); +} + +/* Compare a pdnsd_a* with a pdnsd_a2*. */ +inline static int same_inaddr2(pdnsd_a *a, pdnsd_a2 *b) + __attribute__((always_inline)); +inline static int same_inaddr2(pdnsd_a *a, pdnsd_a2 *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) && b->ipv4.s_addr==INADDR_ANY ); +} + +inline static int equiv_inaddr2(pdnsd_a *a, pdnsd_a2 *b) + __attribute__((always_inline)); +inline static int equiv_inaddr2(pdnsd_a *a, pdnsd_a2 *b) +{ + return SEL_IPVER( a->ipv4.s_addr==b->ipv4.s_addr, + IN6_ARE_ADDR_EQUAL(&a->ipv6,&b->ipv6) || + (b->ipv4.s_addr!=INADDR_ANY && ADDR_EQUIV6_4(&a->ipv6,&b->ipv4)) ); +} + +int str2pdnsd_a(const char *addr, pdnsd_a *a); +const char *pdnsd_a2str(pdnsd_a *a, char *buf, int maxlen); + +int init_rng(void); +#ifdef RANDOM_DEVICE +extern FILE *rand_file; +/* Because this is usually empty, it is now defined as a macro to save overhead.*/ +#define free_rng() {if (rand_file) fclose(rand_file);} +#else +#define free_rng() +#endif + +unsigned short get_rand16(void); + +int fsprintf(int fd, const char *format, ...) printfunc(2, 3); +#if !defined(CPP_C99_VARIADIC_MACROS) +/* GNU C Macro Varargs style. */ +# define fsprintf_or_return(args...) {int _retval; if((_retval=fsprintf(args))<0) return _retval;} +#else +/* ANSI C99 style variadic macro. */ +# define fsprintf_or_return(...) {int _retval; if((_retval=fsprintf(__VA_ARGS__))<0) return _retval;} +#endif + +/* Added by Paul Rombouts */ +inline static ssize_t write_all(int fd,const void *data,size_t n) + __attribute__((always_inline)); +inline static ssize_t write_all(int fd,const void *data,size_t n) +{ + ssize_t written=0; + + while(written +int vasprintf (char **lineptr, const char *format, va_list va); +#endif + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, size_t size); +#endif + +#define strlitlen(strlit) (sizeof(strlit)-1) + +#endif /* HELPERS_H */ diff --git a/service/src/main/jni/pdnsd/src/icmp.c b/service/src/main/jni/pdnsd/src/icmp.c new file mode 100644 index 0000000..6e3e46f --- /dev/null +++ b/service/src/main/jni/pdnsd/src/icmp.c @@ -0,0 +1,544 @@ +/* icmp.c - Server response tests using ICMP echo requests + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2005, 2007, 2012 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +/* + * This should now work on both Linux and FreeBSD (and CYGWIN?). If anyone + * with experience in other Unix flavors wants to contribute platform-specific + * code, he is very welcome. + */ + +#include +#ifdef HAVE_SYS_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "ipvers.h" +#if (TARGET==TARGET_LINUX) +# include +# include +# include +#elif (TARGET==TARGET_BSD) +# include +# include +# include +#elif (TARGET==TARGET_CYGWIN) +# include +# include +# include +# include "freebsd_netinet_ip_icmp.h" +#else +# error Unsupported platform! +#endif +#ifdef ENABLE_IPV6 +# include +# include +#endif +#include +#include "icmp.h" +#include "error.h" +#include "helpers.h" +#include "servers.h" + + +#define ICMP_MAX_ERRS 10 +static volatile unsigned long icmp_errs=0; /* This is only here to minimize log output. + Since the consequences of a race is only + one log message more/less (out of + ICMP_MAX_ERRS), no lock is required. */ + +volatile int ping_isocket=-1; +#ifdef ENABLE_IPV6 +volatile int ping6_isocket=-1; +#endif + +/* different names, same thing... be careful, as these are macros... */ +#if (TARGET==TARGET_LINUX) +# define ip_saddr saddr +# define ip_daddr daddr +# define ip_hl ihl +# define ip_p protocol +#else +# define icmphdr icmp +# define iphdr ip +# define ip_saddr ip_src.s_addr +# define ip_daddr ip_dst.s_addr +#endif + +#if (TARGET==TARGET_LINUX) +# define icmp_type type +# define icmp_code code +# define icmp_cksum checksum +# define icmp_id un.echo.id +# define icmp_seq un.echo.sequence +#else +# define ICMP_DEST_UNREACH ICMP_UNREACH +# define ICMP_TIME_EXCEEDED ICMP_TIMXCEED +#endif + +#define ICMP_BASEHDR_LEN 8 +#define ICMP4_ECHO_LEN ICMP_BASEHDR_LEN + +#if (TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +/* + * These are the ping implementations for Linux/FreeBSD in their IPv4/ICMPv4 and IPv6/ICMPv6 versions. + * I know they share some code, but I'd rather keep them separated in some parts, as some + * things might go in different directions there. + */ + +/* Initialize the sockets for pinging */ +void init_ping_socket() +{ + if ((ping_isocket=socket(PF_INET, SOCK_RAW, IPPROTO_ICMP))==-1) { + log_warn("icmp ping: socket() failed: %s",strerror(errno)); + } +#ifdef ENABLE_IPV6 + if (!run_ipv4) { + /* Failure to initialize the IPv4 ping socket is not + necessarily a problem, as long as the IPv6 version works. */ + if ((ping6_isocket=socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6))==-1) { + log_warn("icmpv6 ping: socket() failed: %s",strerror(errno)); + } + } +#endif +} + +/* Takes a packet as send out and a received ICMP packet and looks whether the ICMP packet is + * an error reply on the sent-out one. packet is only the packet (without IP header). + * errmsg includes an IP header. + * to is the destination address of the original packet (the only thing that is actually + * compared of the IP header). The RFC says that we get at least 8 bytes of the offending packet. + * We do not compare more, as this is all we need.*/ +static int icmp4_errcmp(char *packet, int plen, struct in_addr *to, char *errmsg, int elen, int errtype) +{ + struct iphdr iph; + struct icmphdr icmph; + struct iphdr eiph; + char *data; + + /* XXX: lots of memcpy to avoid unaligned accesses on alpha */ + if (elen= ICMP_BASEHDR_LEN, "icmp4_errcmp: ICMP_BASEHDR_LEN botched"); + memcpy(&icmph,errmsg+iph.ip_hl*4,ICMP_BASEHDR_LEN); + memcpy(&eiph,errmsg+iph.ip_hl*4+ICMP_BASEHDR_LEN,sizeof(eiph)); + if (elens_addr, &eiph.ip_daddr, sizeof(to->s_addr))==0 && + memcmp(data, packet, plen<8?plen:8)==0; +} + +/* IPv4/ICMPv4 ping. Called from ping (see below) */ +static int ping4(struct in_addr addr, int timeout, int rep) +{ + int i; + int isock; +#if (TARGET==TARGET_LINUX) + struct icmp_filter f; +#endif + unsigned short id=(unsigned short)get_rand16(); /* randomize a ping id */ + + isock=ping_isocket; + +#if (TARGET==TARGET_LINUX) + /* Fancy ICMP filering -- only on Linux (as far is I know) */ + + /* In fact, there should be macros for treating icmp_filter, but I haven't found them in Linux 2.2.15. + * So, set it manually and unportable ;-) */ + /* This filter lets ECHO_REPLY (0), DEST_UNREACH(3) and TIME_EXCEEDED(11) pass. */ + /* !(0000 1000 0000 1001) = 0xff ff f7 f6 */ + f.data=0xfffff7f6; + if (setsockopt(isock,SOL_RAW,ICMP_FILTER,&f,sizeof(f))==-1) { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("icmp ping: setsockopt() failed: %s", strerror(errno)); + } + return -1; + } +#endif + + for (i=0;i> 16) + (sum & 0xffff); + sum += (sum >> 16); + icmpd.icmp_cksum=~sum; + + memset(&to,0,sizeof(to)); + to.sin_family=AF_INET; + to.sin_port=0; + to.sin_addr=addr; + SET_SOCKA_LEN4(to); + if (sendto(isock,&icmpd,ICMP4_ECHO_LEN,0,(struct sockaddr *)&to,sizeof(to))==-1) { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("icmp ping: sendto() failed: %s.",strerror(errno)); + } + return -1; + } + /* listen for reply. */ + tm=time(NULL); tpassed=0; + do { + int psres; +#ifdef NO_POLL + fd_set fds,fdse; + struct timeval tv; + FD_ZERO(&fds); + PDNSD_ASSERT(isocktpassed?timeout-tpassed:0; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=select(isock+1,&fds,NULL,&fdse,&tv); +#else + struct pollfd pfd; + pfd.fd=isock; + pfd.events=POLLIN; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=poll(&pfd,1,timeout>tpassed?(timeout-tpassed)*1000:0); +#endif + + if (psres<0) { + if(errno==EINTR && is_interrupted_servstat_thread()) { + DEBUG_MSG("poll/select interrupted in server status thread.\n"); + } + else if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("poll/select failed: %s",strerror(errno)); + } + return -1; + } + if (psres==0) /* timed out */ + break; + +#ifdef NO_POLL + if (FD_ISSET(isock,&fds) || FD_ISSET(isock,&fdse)) +#else + if (pfd.revents&(POLLIN|POLLERR)) +#endif + { + char buf[1024]; + socklen_t sl=sizeof(from); + int len; + + if ((len=recvfrom(isock,&buf,sizeof(buf),0,(struct sockaddr *)&from,&sl))!=-1) { + if (len>sizeof(struct iphdr)) { + struct iphdr iph; + + memcpy(&iph, buf, sizeof(iph)); + if (len-iph.ip_hl*4>=ICMP_BASEHDR_LEN) { + struct icmphdr icmpp; + + memcpy(&icmpp, ((uint32_t *)buf)+iph.ip_hl, sizeof(icmpp)); + if (iph.ip_saddr==addr.s_addr && icmpp.icmp_type==ICMP_ECHOREPLY && + ntohs(icmpp.icmp_id)==id && ntohs(icmpp.icmp_seq)<=i) { + return (i-ntohs(icmpp.icmp_seq))*timeout+(time(NULL)-tm); /* return the number of ticks */ + } else { + /* No regular echo reply. Maybe an error? */ + if (icmp4_errcmp((char *)&icmpd, ICMP4_ECHO_LEN, &to.sin_addr, buf, len, ICMP_DEST_UNREACH) || + icmp4_errcmp((char *)&icmpd, ICMP4_ECHO_LEN, &to.sin_addr, buf, len, ICMP_TIME_EXCEEDED)) { + return -1; + } + } + } + } + } else { + return -1; /* error */ + } + } + else { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_error("Unhandled poll/select event in ping4() at %s, line %d.",__FILE__,__LINE__); + } + return -1; + } + tpassed=time(NULL)-tm; + } while (tpassedtpassed?timeout-tpassed:0; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=select(isock+1,&fds,NULL,&fdse,&tv); +#else + struct pollfd pfd; + pfd.fd=isock; + pfd.events=POLLIN; + /* There is a possible race condition with the arrival of a signal here, + but it is so unlikely to be a problem in practice that the effort + to do this properly is not worth the trouble. + */ + if(is_interrupted_servstat_thread()) { + DEBUG_MSG("server status thread interrupted.\n"); + return -1; + } + psres=poll(&pfd,1,timeout>tpassed?(timeout-tpassed)*1000:0); +#endif + + if (psres<0) { + if(errno==EINTR && is_interrupted_servstat_thread()) { + DEBUG_MSG("poll/select interrupted in server status thread.\n"); + } + else if (++icmp_errs<=ICMP_MAX_ERRS) { + log_warn("poll/select failed: %s",strerror(errno)); + } + return -1; + } + if (psres==0) /* timed out */ + break; + +#ifdef NO_POLL + if (FD_ISSET(isock,&fds) || FD_ISSET(isock,&fdse)) +#else + if (pfd.revents&(POLLIN|POLLERR)) +#endif + { + char buf[1024]; + socklen_t sl=sizeof(from); + int len; + if ((len=recvfrom(isock,&buf,sizeof(buf),0,(struct sockaddr *)&from,&sl))!=-1) { + if (len>=sizeof(struct icmp6_hdr)) { + /* we get packets without IPv6 header, luckily */ + struct icmp6_hdr icmpp; + + memcpy(&icmpp, buf, sizeof(icmpp)); + if (IN6_ARE_ADDR_EQUAL(&from.sin6_addr,&a) && + ntohs(icmpp.icmp6_id)==id && ntohs(icmpp.icmp6_seq)<=i) { + return (i-ntohs(icmpp.icmp6_seq))*timeout+(time(NULL)-tm); /* return the number of ticks */ + } else { + /* No regular echo reply. Maybe an error? */ + if (icmp6_errcmp((char *)&icmpd, sizeof(icmpd), &from.sin6_addr, buf, len, ICMP6_DST_UNREACH) || + icmp6_errcmp((char *)&icmpd, sizeof(icmpd), &from.sin6_addr, buf, len, ICMP6_TIME_EXCEEDED)) { + return -1; + } + } + } + } else { + return -1; /* error */ + } + } + else { + if (++icmp_errs<=ICMP_MAX_ERRS) { + log_error("Unhandled poll/select event in ping6() at %s, line %d.",__FILE__,__LINE__); + } + return -1; + } + tpassed=time(NULL)-tm; + } while (tpassedipv4,timeout,rep); +#endif +#ifdef ENABLE_IPV6 + ELSE_IPV6 { + /* If it is a IPv4 mapped IPv6 address, we prefer ICMPv4. */ + if (ping_isocket!=-1 && IN6_IS_ADDR_V4MAPPED(&addr->ipv6)) { + struct in_addr v4; + v4.s_addr=((uint32_t *)&addr->ipv6)[3]; + return ping4(v4,timeout,rep); + } else + return ping6(addr->ipv6,timeout,rep); + } +#endif + return -1; +} + +#else +# error "Huh! No OS macro defined!" +#endif /*(TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN)*/ diff --git a/service/src/main/jni/pdnsd/src/icmp.h b/service/src/main/jni/pdnsd/src/icmp.h new file mode 100644 index 0000000..3fa6778 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/icmp.h @@ -0,0 +1,43 @@ +/* icmp.h - Server response tests using ICMP echo requests + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2007 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef ICMP_H +#define ICMP_H + + +#include +#include "ipvers.h" + +volatile extern int ping_isocket; +volatile extern int ping6_isocket; + +/* initialize a socket for pinging */ +void init_ping_socket(void); + +/* + * This is a classical ping routine. + * timeout in 10ths of seconds, rep is the repetition count. + */ + +int ping(pdnsd_a *addr, int timeout, int rep); + +#endif diff --git a/service/src/main/jni/pdnsd/src/ipvers.h b/service/src/main/jni/pdnsd/src/ipvers.h new file mode 100644 index 0000000..b1d7a2c --- /dev/null +++ b/service/src/main/jni/pdnsd/src/ipvers.h @@ -0,0 +1,297 @@ +/* ipvers.h - definitions for IPv4 and IPv6 + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2007, 2009 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef IPVERS_H +#define IPVERS_H + +#include +#include +#include +#include +#include +#include "rr_types.h" + +#if defined(ENABLE_IPV4) && !defined(ENABLE_IPV6) +# ifdef DEFAULT_IPV4 +# undef DEFAULT_IPV4 +# endif +# define DEFAULT_IPV4 1 +#endif + +#if !defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +# ifdef DEFAULT_IPV4 +# undef DEFAULT_IPV4 +# endif +# define DEFAULT_IPV4 0 +#endif + +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +# define ELSE_IPV6 else +#else +# define ELSE_IPV6 +#endif + +/* From main.c */ +#ifdef ENABLE_IPV4 +# ifdef ENABLE_IPV6 +extern short int run_ipv4; +extern short int cmdlineipv; +# define SEL_IPVER(a4,a6) (run_ipv4? a4: a6) +# else +# define run_ipv4 1 +# define SEL_IPVER(a4,a6) (a4) +# endif +#else +# define run_ipv4 0 +# define SEL_IPVER(a4,a6) (a6) +#endif +#ifdef ENABLE_IPV6 +#define DEFAULT_IPV4_6_PREFIX "::ffff:0.0.0.0" +/* extern short int cmdlineprefix; */ +#endif + +#if (TARGET==TARGET_LINUX) && !defined(HAVE_STRUCT_IN_PKTINFO) +struct in_pktinfo +{ + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +#if (TARGET==TARGET_LINUX) +/* some older glibc versions seem to lack this. */ +# ifndef IP_PKTINFO +# define IP_PKTINFO 8 +# endif +# ifndef CMSG_LEN +/* ---- from glibc 2.1.2 */ + +/* Ancillary data object manipulation macros. */ +# if !defined __STRICT_ANSI__ && defined __GNUC__ && __GNUC__ >= 2 +# define CMSG_DATA(cmsg) ((cmsg)->__cmsg_data) +# else +# define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1)) +# endif +# define CMSG_NXTHDR(mhdr, cmsg) __cmsg_nxthdr (mhdr, cmsg) +# define CMSG_FIRSTHDR(mhdr) \ + ((size_t) (mhdr)->msg_controllen >= sizeof (struct cmsghdr) \ + ? (struct cmsghdr *) (mhdr)->msg_control : (struct cmsghdr *) NULL) +# define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \ + & ~(sizeof (size_t) - 1)) +# define CMSG_SPACE(len) (CMSG_ALIGN (len) \ + + CMSG_ALIGN (sizeof (struct cmsghdr))) +# define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +extern struct cmsghdr *__cmsg_nxthdr __P ((struct msghdr *__mhdr, + struct cmsghdr *__cmsg)); +# ifdef __USE_EXTERN_INLINES +# ifndef _EXTERN_INLINE +# define _EXTERN_INLINE extern __inline +# endif +_EXTERN_INLINE struct cmsghdr * +__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg) __THROW +{ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) + /* The kernel header does this so there may be a reason. */ + return 0; + + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); + if ((unsigned char *) (__cmsg + 1) >= ((unsigned char *) __mhdr->msg_control + + __mhdr->msg_controllen) + || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) + >= ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) + /* No more entries. */ + return 0; + return __cmsg; +} +# endif /* Use `extern inline'. */ +/* ---- */ +# endif +#endif + +#if defined(ENABLE_IPV4) && !defined(SIN_LEN) && (TARGET==TARGET_BSD) +# define SIN_LEN +#endif + +#if defined(ENABLE_IPV6) && (TARGET==TARGET_LINUX) + +/* Some glibc versions (I know of 2.1.2) get this wrong, so we define our own. To be exact, this is fixed + * glibc code. */ +#ifdef IN6_ARE_ADDR_EQUAL +# undef IN6_ARE_ADDR_EQUAL +#endif +#define IN6_ARE_ADDR_EQUAL(a,b) \ + ((((uint32_t *) (a))[0] == ((uint32_t *) (b))[0]) && \ + (((uint32_t *) (a))[1] == ((uint32_t *) (b))[1]) && \ + (((uint32_t *) (a))[2] == ((uint32_t *) (b))[2]) && \ + (((uint32_t *) (a))[3] == ((uint32_t *) (b))[3])) + +#endif + +/* This is the IPv6 flowid that we pass on to the IPv6 protocol stack. This value was not currently defined + * at the time of writing. Should this change, define a appropriate flowinfo here. */ +#ifndef IPV6_FLOWINFO +#define IPV6_FLOWINFO 0 +#endif + +/* There does not seem to be a function/macro to generate IPv6-mapped IPv4-Adresses. So here comes mine. + * Pass an in_addr* and an in6_addr* */ +#define IPV6_MAPIPV4(a,b) {((uint32_t *)(b))[3]=(a)->s_addr; \ + ((uint32_t *)(b))[2]=htonl(0xffff); \ + ((uint32_t *)(b))[1]=((uint32_t *)(b))[0]=0; } + +/* A macro to extract the pointer to the address of a struct sockaddr (_in or _in6) */ + +#define SOCKA_A4(a) ((pdnsd_a *)&((struct sockaddr_in *)(a))->sin_addr) +#define SOCKA_A6(a) ((pdnsd_a *)&((struct sockaddr_in6 *)(a))->sin6_addr) + +#define SOCKA_A(a) SEL_IPVER(SOCKA_A4(a),SOCKA_A6(a)) +#define PDNSD_PF_INET SEL_IPVER(PF_INET,PF_INET6) +#define PDNSD_AF_INET SEL_IPVER(AF_INET,AF_INET6) + +/* This is to compare two addresses. This is a macro because it may change due to the more complex IPv6 adressing architecture + * (there are, for example, two equivalent addresses of the loopback device) + * Pass this two addresses as in_addr or in6_addr. pdnsd_a is ok (it is a union) */ + +#define ADDR_EQUIV4(a,b) (((struct in_addr *)(a))->s_addr==((struct in_addr *)(b))->s_addr) +#define ADDR_EQUIV6(a,b) IN6_ARE_ADDR_EQUAL(((struct in6_addr *)(a)),((struct in6_addr *)(b))) + +#define ADDR_EQUIV(a,b) SEL_IPVER(ADDR_EQUIV4(a,b), ADDR_EQUIV6(a,b)) + +/* Compare an IPv6 adress with an IPv4 one. b should have type struct in_addr*. + Note the similarity with the IPV6_MAPIPV4 macro. */ +#define ADDR_EQUIV6_4(a,b) (((uint32_t *)(a))[3]==(b)->s_addr && \ + ((uint32_t *)(a))[2]==htonl(0xffff) && \ + ((uint32_t *)(a))[1]==0 && \ + ((uint32_t *)(a))[0]==0) + +/* Compare two address a and b in combination with a netmask m. + Only the bits coresponding to those set in the netmask are matched, the rest are ignored. + Pass in_addr* or in6_addr* arguments, respectively. */ +#define ADDR4MASK_EQUIV(a,b,m) ((((a)->s_addr^(b)->s_addr)&(m)->s_addr)==0) +#define ADDR6MASK_EQUIV(a,b,m) (((((uint32_t *)(a))[0]^((uint32_t *)(b))[0])&((uint32_t *)(m))[0])==0 && \ + ((((uint32_t *)(a))[1]^((uint32_t *)(b))[1])&((uint32_t *)(m))[1])==0 && \ + ((((uint32_t *)(a))[2]^((uint32_t *)(b))[2])&((uint32_t *)(m))[2])==0 && \ + ((((uint32_t *)(a))[3]^((uint32_t *)(b))[3])&((uint32_t *)(m))[3])==0) + +/* See if we need 4.4BSD style sockaddr_* structures and define some macros that set the length field. + * The non-4.4BSD behaviour is the only one that is POSIX-conformant.*/ +#if defined(SIN6_LEN) || defined(SIN_LEN) +# define BSD44_SOCKA +# define SET_SOCKA_LEN4(socka) (socka.sin_len=sizeof(struct sockaddr_in)) +# define SET_SOCKA_LEN6(socka) (socka.sin6_len=sizeof(struct sockaddr_in6)) +#else +# define SET_SOCKA_LEN4(socka) +# define SET_SOCKA_LEN6(socka) +#endif + +#ifdef ENABLE_IPV6 +# define ADDRSTR_MAXLEN INET6_ADDRSTRLEN +#else +# ifdef INET_ADDRSTRLEN +# define ADDRSTR_MAXLEN INET_ADDRSTRLEN +# else +# define ADDRSTR_MAXLEN 16 +# endif +#endif + +#if (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +# define SOL_IPV6 IPPROTO_IPV6 +#endif + +#ifdef ENABLE_IPV6 +# ifndef IPV6_RECVPKTINFO +/* This appears to be needed e.g. on Darwin (Mac OS X). */ +# define IPV6_RECVPKTINFO IPV6_PKTINFO +# endif +#endif + +typedef union { +#ifdef ENABLE_IPV4 + struct in_addr ipv4; +#endif +#ifdef ENABLE_IPV6 + struct in6_addr ipv6; +#endif +} pdnsd_a; + +#ifdef ENABLE_IPV4 +#define PDNSD_A_INITIALIZER {{INADDR_ANY}} +#else +#define PDNSD_A_INITIALIZER {IN6ADDR_ANY_INIT} +#endif + + +/* The pdnsd_a2 type is very similar to pdnsd_a, but can hold + both an IPv4 and an IPv6 address at the same time, + i.e. a struct instead of a union. +*/ +typedef struct { +#ifdef ENABLE_IPV6 + struct in6_addr ipv6; +#endif + struct in_addr ipv4; +} pdnsd_a2; + +/* Macros/functions for assigning/converting a pdnsd_a* to a pdnsd_a2* type, + and vice versa. +*/ +#ifdef ENABLE_IPV6 +inline static void SET_PDNSD_A2(pdnsd_a2 *a2, pdnsd_a *a) __attribute__((always_inline)); +inline static void SET_PDNSD_A2(pdnsd_a2 *a2, pdnsd_a *a) +{ +#ifdef ENABLE_IPV4 + if(run_ipv4) + a2->ipv4=a->ipv4; + else +#endif + { + a2->ipv6=a->ipv6; + a2->ipv4.s_addr=INADDR_ANY; + } +} +#else +# define SET_PDNSD_A2(a2,a) ((a2)->ipv4=(a)->ipv4) +#endif + +#define PDNSD_A2_TO_A(a2) SEL_IPVER(((pdnsd_a *)&(a2)->ipv4),((pdnsd_a *)&(a2)->ipv6)) + +/* Do we have sufficient support in the C libraries to allow local AAAA records + to be defined? */ +#if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_PTON) +# define ALLOW_LOCAL_AAAA IS_CACHED_AAAA +#else +# define ALLOW_LOCAL_AAAA 0 +#endif + +/* Used to enter local records */ +typedef union { + struct in_addr ipv4; +#if ALLOW_LOCAL_AAAA + struct in6_addr ipv6; +#endif +} pdnsd_ca; + + +#endif diff --git a/service/src/main/jni/pdnsd/src/list.c b/service/src/main/jni/pdnsd/src/list.c new file mode 100644 index 0000000..0370186 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/list.c @@ -0,0 +1,171 @@ +/* list.c - Dynamic array and list handling + + Copyright (C) 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2007, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include "helpers.h" +#include "error.h" +#include "list.h" + + +/* Grow a dynamic array to hold one extra element. + This could be done using da_resize(), but this is such a common operation + it is has been given its own optimized implementation. + da_grow1() returns a pointer to the new (possibly reallocated) array if + successful, otherwise it frees the old array (after freeing all the array + elements if a clean-up routine is supplied) and returns NULL. +*/ +darray da_grow1(darray a, size_t headsz, size_t elemsz, void (*cleanuproutine) (void *)) +{ + size_t k = (a?a->nel:0); + if(!a || (k!=0 && (k&7)==0)) { + darray tmp=(darray)realloc(a, headsz+elemsz*(k+8)); + if (!tmp && a) { + if(cleanuproutine) { + size_t i; + for(i=0;inel=k+1; + return a; +} + +inline static size_t alloc_nel(size_t n) +{ + return n==0 ? 8 : (n+7)&(~7); +} + +/* da_resize() allows you to grow (or shrink) a dynamic array to an arbitrary length n, + but is otherwise similar to da_grow1(). +*/ +darray da_resize(darray a, size_t headsz, size_t elemsz, size_t n, void (*cleanuproutine) (void *)) +{ + size_t ael = (a?alloc_nel(a->nel):0); + size_t new_ael = alloc_nel(n); + if(new_ael != ael) { + /* adjust alloced space. */ + darray tmp=(darray)realloc(a, headsz+elemsz*new_ael); + if (!tmp && a) { + if(cleanuproutine) { + size_t i,k=a->nel; + for(i=0;inel=n; + return a; +} + +#ifdef ALLOC_DEBUG +void DBGda_free(darray a, size_t headsz, size_t elemsz, char *file, int line) +{ + if (a==NULL) + {DEBUG_MSG("- da_free, %s:%d, not initialized\n", file, line);} + else + {DEBUG_MSG("- da_free, %s:%d, %lu bytes\n", file, line, + (unsigned long)(headsz+elemsz*alloc_nel(a->nel)));} + free(a); +} +#endif + + +#define DLISTALIGN(len) (((len) + (sizeof(size_t)-1)) & ~(sizeof(size_t)-1)) +/* This mask corresponds to a chunk size of 1024. */ +#define DLISTCHUNKSIZEMASK ((size_t)0x3ff) + +/* Add space for a new item of size len to the list a. + dlist_grow() returns a pointer to the new (possibly reallocated) list structure if + successful, otherwise it frees the old list and returns NULL. +*/ +dlist dlist_grow(dlist a, size_t len) +{ + size_t sz=0, allocsz=0, szincr, newsz; + if(a) { + sz=a->last+a->lastsz; + allocsz = (sz+DLISTCHUNKSIZEMASK)&(~DLISTCHUNKSIZEMASK); + *((size_t *)&a->data[a->last])=a->lastsz; + } + szincr=DLISTALIGN(len+sizeof(size_t)); + newsz=sz+szincr; + if(newsz>allocsz) { + dlist tmp; + allocsz = (newsz+DLISTCHUNKSIZEMASK)&(~DLISTCHUNKSIZEMASK); + tmp=realloc(a, sizeof(struct _dynamic_list_head)+allocsz); + if (!tmp) + free(a); + a=tmp; + } + if(a) { + a->last=sz; + a->lastsz=szincr; + *((size_t *)&a->data[sz])=0; + } + return a; +} + + +/* Add a new node, capable of holding data of size len, at the end of a linked list. + llist_grow() returns 1 if successful, otherwise it frees the entire linked list + and returns 0. + */ +int llist_grow(llist *a, size_t len) +{ + struct llistnode_s *new= (struct llistnode_s *)malloc(sizeof(struct llistnode_s)+len); + + if(!new) { + llist_free(a); + return 0; + } + + new->next=NULL; + + if(!a->first) + a->first=new; + else + a->last->next=new; + + a->last=new; + + return 1; +} + +void llist_free(llist *a) +{ + struct llistnode_s *p= a->first; + + while(p) { + struct llistnode_s *next= p->next; + free(p); + p=next; + } + + a->first=NULL; + a->last= NULL; +} diff --git a/service/src/main/jni/pdnsd/src/list.h b/service/src/main/jni/pdnsd/src/list.h new file mode 100644 index 0000000..c63534e --- /dev/null +++ b/service/src/main/jni/pdnsd/src/list.h @@ -0,0 +1,170 @@ +/* list.h - Dynamic array and list handling + + Copyright (C) 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef LIST_H +#define LIST_H + +#include +#include +#include "pdnsd_assert.h" + + +typedef struct {size_t nel;} *darray; + +/* used in type declarations */ +#define DYNAMIC_ARRAY(typ) struct {size_t nel; typ elem[0];} +#define DA_TYP_OFFSET(atyp) ((size_t)((atyp)0)->elem) +#define DA_OFFSET(a) DA_TYP_OFFSET(typeof (a)) + +#define DA_CREATE(atyp,n) ((atyp)da_resize(NULL,DA_TYP_OFFSET(atyp),sizeof(((atyp)0)->elem[0]),n,NULL)) +#define DA_INDEX(a,i) ((a)->elem[i]) +/* Used often, so make special-case macro here */ +#define DA_LAST(a) ((a)->elem[(a)->nel-1]) + +#define DA_GROW1(a) ((typeof (a))da_grow1((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]),NULL)) +#define DA_GROW1_F(a,cleanup) ((typeof (a))da_grow1((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]),cleanup)) +#define DA_RESIZE(a,n) ((typeof (a))da_resize((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]),n,NULL)) +#define DA_NEL(a) da_nel((darray)(a)) + +/* + * Some or all of these should be inline. + * They aren't macros for type safety. + */ + +darray da_grow1(darray a, size_t headsz, size_t elemsz, void (*cleanuproutine) (void *)); +darray da_resize(darray a, size_t headsz, size_t elemsz, size_t n, void (*cleanuproutine) (void *)); + +inline static unsigned int da_nel(darray a) + __attribute__((always_inline)); +inline static unsigned int da_nel(darray a) +{ + if (a==NULL) + return 0; + return a->nel; +} + +/* alloc/free debug code.*/ +#ifdef ALLOC_DEBUG +void DBGda_free(darray a, size_t headsz, size_t elemsz, char *file, int line); +#define da_free(a) DBGda_free((darray)(a),DA_OFFSET(a),sizeof((a)->elem[0]), __FILE__, __LINE__) +#else +#define da_free free +#endif + + +/* This dynamic "list" structure is useful if the items are not all the same size. + The elements can only be read back in sequential order, not indexed as with the dynamic arrays. +*/ +struct _dynamic_list_head { + size_t last,lastsz; + char data[0]; +}; + +typedef struct _dynamic_list_head *dlist; + +inline static void *dlist_first(dlist a) + __attribute__((always_inline)); +inline static void *dlist_first(dlist a) +{ + return a?&a->data[sizeof(size_t)]:NULL; +} + +/* dlist_next() returns a reference to the next item in the list, or NULL is there is no next item. + ref should be properly aligned. + If the dlist was grown with dlist_grow(), this should be OK. +*/ +inline static void *dlist_next(void *ref) + __attribute__((always_inline)); +inline static void *dlist_next(void *ref) +{ + size_t incr= *(((size_t *)ref)-1); + return incr?((char *)ref)+incr:NULL; +} + +/* dlist_last() returns a reference to the last item. */ +inline static void *dlist_last(dlist a) + __attribute__((always_inline)); +inline static void *dlist_last(dlist a) +{ + return a?&a->data[a->last+sizeof(size_t)]:NULL; +} + +dlist dlist_grow(dlist a, size_t len); + +#define dlist_free free + + +/* linked list data type. */ +struct llistnode_s { + struct llistnode_s *next; + char *data[0]; +}; + +typedef struct { + struct llistnode_s *first, *last; +} + llist; + +inline static void llist_init(llist *a) + __attribute__((always_inline)); +inline static void llist_init(llist *a) +{ + a->first=NULL; + a->last= NULL; +} + +inline static int llist_isempty(llist *a) + __attribute__((always_inline)); +inline static int llist_isempty(llist *a) +{ + return a->first==NULL; +} + +inline static void *llist_first(llist *a) + __attribute__((always_inline)); +inline static void *llist_first(llist *a) +{ + struct llistnode_s *p= a->first; + return p?p->data:NULL; +} + +inline static void *llist_next(void *ref) + __attribute__((always_inline)); +inline static void *llist_next(void *ref) +{ + struct llistnode_s *next= *(((struct llistnode_s **)ref)-1); + return next?next->data:NULL; +} + +inline static void *llist_last(llist *a) + __attribute__((always_inline)); +inline static void *llist_last(llist *a) +{ + struct llistnode_s *p= a->last; + return p?p->data:NULL; +} + +int llist_grow(llist *a, size_t len); +void llist_free(llist *a); + +#endif /* def LIST_H */ diff --git a/service/src/main/jni/pdnsd/src/main.c b/service/src/main/jni/pdnsd/src/main.c new file mode 100644 index 0000000..ad9863d --- /dev/null +++ b/service/src/main/jni/pdnsd/src/main.c @@ -0,0 +1,717 @@ +/* main.c - Command line parsing, intialisation and server start + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +/* in order to use O_NOFOLLOW on Linux: */ +/* #define _GNU_SOURCE */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "consts.h" +#include "cache.h" +#include "status.h" +#include "servers.h" +#include "dns_answer.h" +#include "dns_query.h" +#include "error.h" +#include "helpers.h" +#include "icmp.h" +#include "hash.h" + + +#if DEBUG>0 +short int debug_p=0; +#endif +short int stat_pipe=0; + +/* int sigr=0; */ +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +short int cmdlineipv=0; +#endif +cmdlineflags_t cmdline={0}; +pthread_t main_thrid,servstat_thrid,statsock_thrid,tcps_thrid,udps_thrid; +uid_t init_uid; +#if DEBUG>0 +FILE *dbg_file=NULL; +#endif +volatile int tcp_socket=-1; +volatile int udp_socket=-1; +sigset_t sigs_msk; +char *conf_file=NULL; + + +/* version and licensing information */ +static const char info_message[] = + + "pdnsd - dns proxy daemon, version " VERSION "\n\n" + "Copyright (C) 2000, 2001 Thomas Moestl\n" + "Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 Paul A. Rombouts\n\n" + "pdnsd is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 3 of the License, or\n" + "(at your option) any later version.\n\n" + "pdnsd is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with pdsnd; see the file COPYING. If not, see\n" + ".\n"; + + +/* the help page */ +static const char help_message[] = + + "\n\nUsage: pdnsd [-h] [-V] [-s] [-d] [-g] [-t] [-p file] [-vn] [-mxx] [-c file]" +#ifdef ENABLE_IPV4 + " [-4]" +#endif +#ifdef ENABLE_IPV6 + " [-6] [-i prefix]" +#endif +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + " [-a]" +#endif + "\n\n" + "Options:\n" + "-h\t\t--or--\n" + "--help\t\tprint this help page and exit.\n" + "-V\t\t--or--\n" + "--version\tprint version and license information and exit.\n" + "--pdnsd-user\tprint the user pdnsd will run as and exit.\n" + "-s\t\t--or--\n" + "--status\tEnable status control socket in the cache directory.\n" + "-d\t\t--or--\n" + "--daemon\tStart pdnsd in daemon mode (as background process.)\n" + "-g\t\t--or--\n" + "--debug\t\tPrint some debug messages on the console or to the\n" + "\t\tfile pdnsd.debug in your cache directory (in daemon mode).\n" + "-t\t\t--or--\n" + "--tcp\t\tEnables the TCP server thread. pdnsd will then serve\n" + "\t\tTCP and UDP queries.\n" + "-p\t\tWrites the pid the server runs as to a specified filename.\n" + "\t\tWorks only in daemon mode.\n" + "-vn\t\tsets the verbosity of pdnsd. n is a numeric argument from 0\n" + "\t\t(normal operation) to 9 (many messages for debugging).\n" + "\t\tUse like -v2\n" + "-mxx\t\tsets the query method pdnsd uses. Possible values for xx are:\n" + "\t\tuo (UDP only), to (TCP only), tu (TCP or, if the server\n" + "\t\tdoes not support this, UDP) and ut (UDP and, if the reply was\n" + "\t\ttruncated, TCP). Use like -muo. Preset: " +#if M_PRESET==UDP_ONLY + "-muo" +#elif M_PRESET==TCP_ONLY + "-mto" +#elif M_PRESET==TCP_UDP + "-mtu" +#else + "-mut" +#endif + "\n" + "-c\t\t--or--\n" + "--config-file\tspecifies the file the configuration is read from.\n" +#ifdef ENABLE_IPV4 + "-4\t\tswitches to IPv4 mode.\n" + "\t\t" +# if DEFAULT_IPV4 + "On" +# else + "Off" +# endif + " by default.\n" +#endif +#ifdef ENABLE_IPV6 + "-6\t\tswitches to IPv6 mode.\n" + "\t\t" +# if DEFAULT_IPV4 + "Off" +# else + "On" +# endif + " by default.\n" + "-i\t\t--or--\n" + "--ipv4_6_prefix\tspecifies the prefix pdnsd uses to map IPv4 to IPv6\n" + "\t\taddresses. Must be a valid IPv6 address.\n" + "\t\tDefault is " DEFAULT_IPV4_6_PREFIX "\n" +#endif +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) + "-a\t\tWith this option, pdnsd will try to detect automatically if\n" + "\t\tthe system supports IPv6, and revert to IPv4 otherwise.\n" +#endif + "\n\n\"no\" can be prepended to the --status, --daemon, --debug and --tcp\n" + "options (e.g. --notcp) to reverse their effect.\n"; + + +/* These are some init steps we have to call before we get daemon on linux, but need + * to call after daemonizing on other OSes. + * Theay are also the last steps before we drop privileges. */ +int final_init() +{ +#ifndef NO_TCP_SERVER + if (!global.notcp) + tcp_socket=init_tcp_socket(); +#endif + udp_socket=init_udp_socket(); + if (tcp_socket==-1 && udp_socket==-1) { + log_error("tcp and udp initialization failed. Exiting."); + return 0; + } + if (global.strict_suid) { + if (!run_as(global.run_as)) { + return 0; + } + } + return 1; +} + +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +/* Check if IPv6 is available. + * With thanks to Juliusz Chroboczek. + */ +static int check_ipv6() +{ + int fd; + fd = socket(PF_INET6, SOCK_STREAM, 0); + if(fd < 0) { + if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL) + return 0; + return -1; + } + close(fd); + return 1; +} +#endif + + +/* + * Argument parsing, init, server startup + */ +int main(int argc,char *argv[]) +{ + int i,sig,pfd=-1; /* Initialized to inhibit compiler warning */ + + main_thrid=pthread_self(); + servstat_thrid=main_thrid; + statsock_thrid=main_thrid; + tcps_thrid=main_thrid; + udps_thrid=main_thrid; + init_uid=getuid(); +#ifdef ENABLE_IPV6 + { + int err; + if((err=inet_pton(AF_INET6,DEFAULT_IPV4_6_PREFIX,&global.ipv4_6_prefix))<=0) { + fprintf(stderr,"Error: inet_pton() wont accept default prefix %s in %s, line %d\n", + DEFAULT_IPV4_6_PREFIX,__FILE__,__LINE__); + if(err) + perror("inet_pton"); + exit(1); + } + } +#endif + + /* Parse the command line. + Remember which options were specified here, because the command-line options + shall override the ones given in the config file */ + for (i=1;ipw_name); + else + printf("%i\n",uid); + } + exit(0); + } + + if(!global.cache_dir) { + fprintf(stderr,"Error: cache_dir is unset\n"); + exit(1); + } + if(!global.scheme_file) global.scheme_file = "/var/lib/pcmcia/scheme"; + stat_pipe=global.stat_pipe; + + if (!(global.run_as[0] && global.strict_suid)) { + for (i=0; iuptest==C_EXEC && sp->uptest_usr[0]=='\0') { + uid_t uid=getuid(); + struct passwd *pws=getpwuid(uid); + + /* No explicit uptest user given. If we run_as and strict_suid, we assume that + * this is safe. If not - warn. */ + fprintf(stderr,"Warning: uptest command \"%s\" will implicitly be executed as user ", sp->uptest_cmd); + if (pws) + fprintf(stderr,"%s\n",pws->pw_name); + else + fprintf(stderr,"%i\n",uid); + + } + } + } + + if (global.daemon && global.pidfile) { + if (unlink(global.pidfile)!=0 && errno!=ENOENT) { + log_error("Error: could not unlink pid file %s: %s",global.pidfile, strerror(errno)); + exit(1); + } + if ((pfd=open(global.pidfile,O_WRONLY|O_CREAT|O_EXCL +#ifdef O_NOFOLLOW + |O_NOFOLLOW +#else + /* + * No O_NOFOLLOW. Nevertheless, this not a hole, since the + * directory for pidfiles should not be world writeable. + * OS's that do not support O_NOFOLLOW are currently not + * supported, this is just-in-case code. + */ +#endif + , 0600))==-1) + { + log_error("Error: could not open pid file %s: %s",global.pidfile, strerror(errno)); + exit(1); + } + } + for (i=0;i0 + if (global.debug) { + char dbgpath[strlen(global.cache_dir)+sizeof("/pdnsd.debug")]; + stpcpy(stpcpy(dbgpath,global.cache_dir),"/pdnsd.debug"); + if (!(dbg_file=fopen(dbgpath,"w"))) + log_warn("Warning: could not open debug file %s: %s",dbgpath, strerror(errno)); + } +#endif + } else { +#if DEBUG>0 + dbg_file=stdout; +#endif + } + +#if DEBUG>0 + debug_p= (global.debug && dbg_file); +#endif + log_info(0,"pdnsd-%s starting.\n",VERSION); + DEBUG_MSG("Debug messages activated\n"); + +#if (TARGET!=TARGET_LINUX) + if (!final_init()) + _exit(1); +#endif + DEBUG_MSG(SEL_IPVER("Using IPv4.\n", "Using IPv6.\n")); + + /* initialize attribute for creating detached threads */ + pthread_attr_init(&attr_detached); + pthread_attr_setdetachstate(&attr_detached,PTHREAD_CREATE_DETACHED); + + read_disk_cache(); + + /* This must be done before any other thread is started to avoid races. */ + if (stat_pipe) + init_stat_sock(); + + + /* Before this point, logging and cache accesses are not locked because we are single-threaded. */ + init_log_lock(); + init_cache_lock(); + + sigemptyset(&sigs_msk); + sigaddset(&sigs_msk,SIGHUP); + sigaddset(&sigs_msk,SIGINT); +#ifndef THREADLIB_NPTL + sigaddset(&sigs_msk,SIGILL); +#endif + sigaddset(&sigs_msk,SIGABRT); + sigaddset(&sigs_msk,SIGFPE); +#ifndef THREADLIB_NPTL + sigaddset(&sigs_msk,SIGSEGV); +#endif + sigaddset(&sigs_msk,SIGTERM); + /* if (!daemon_p) { + sigaddset(&sigs_msk,SIGQUIT); + } */ +#if (TARGET==TARGET_LINUX) + pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL); +#endif + +#if DEBUG>0 + { + int err; + /* Generate a key for storing our thread id's */ + if ((err=pthread_key_create(&thrid_key, NULL)) != 0) { + log_error("pthread_key_create failed: %s",strerror(err)); + _exit(1); + } + } +#endif + + { +#if DEBUG>0 + int thrdsucc=1; +# define thrdfail (thrdsucc=0) +#else +# define thrdfail +#endif + + if(start_servstat_thread()) thrdfail; + +#if (TARGET==TARGET_LINUX) + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + _exit(1); + } + } +#endif + + if (stat_pipe) + if(start_stat_sock()) thrdfail; + + start_dns_servers(); + +#if DEBUG>0 + if(thrdsucc) { + DEBUG_MSG("All threads started successfully.\n"); + } +#endif +#undef thrdfail + } + +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) + pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL); + waiting=1; +#endif + { + int err; + while ((err=sigwait(&sigs_msk,&sig))) { + if (err!=EINTR) { + log_error("sigwait failed: %s",strerror(err)); + sig=0; + break; + } + } + } + if(sig) DEBUG_MSG("Signal %i caught.\n",sig); + write_disk_cache(); + destroy_cache(); + if(sig) log_warn("Caught signal %i. Exiting.",sig); + if (sig==SIGSEGV || sig==SIGILL || sig==SIGBUS) + crash_msg("This is a fatal signal probably triggered by a bug."); + if (ping_isocket!=-1) + close(ping_isocket); +#ifdef ENABLE_IPV6 + if (ping6_isocket!=-1) + close(ping6_isocket); +#endif + /* Close and delete the status socket */ + if(stat_pipe) close(stat_sock); + if (sock_path && unlink(sock_path)) + log_warn("Failed to unlink %s: %s",sock_path, strerror(errno)); + + free_rng(); +#if DEBUG>0 + if (debug_p && global.daemon) + if(fclose(dbg_file)<0) { + log_warn("Could not close debug file: %s", strerror(errno)); + } +#endif + _exit(0); +} diff --git a/service/src/main/jni/pdnsd/src/make_rr_types_h.pl b/service/src/main/jni/pdnsd/src/make_rr_types_h.pl new file mode 100644 index 0000000..c8c0f9f --- /dev/null +++ b/service/src/main/jni/pdnsd/src/make_rr_types_h.pl @@ -0,0 +1,309 @@ +#!/usr/bin/perl -w + +# This Perl script is used to generate rr_types.h, using rr_types.in as input. +# +# Copyright (C) 2010, 2011 Paul A. Rombouts +# +# This file is part of the pdnsd package. +# + +use strict; +use integer; + +print << "END-OF-TEXT"; +/* This file was generated by running '$0 @ARGV'. + Modifications to this file may be lost the next time it is automatically + regenerated. +*/ + +END-OF-TEXT + +my %valdic; +my %namedic; +my %classdic; +my %muset; +my $nrr=0; +my $nmu=0; +my $minval; +my $maxval; +#my $maxmuval; + +while(<>) { + if(/\S/ && !/^\s*\#/) { + if(/^\s*(?:([*+-])\s*)?([\w-]+)\s+(\d+)\s+(?:\((\w+)\))?/) { + my $mu = $1; my $name=$2; my $val=$3+0; my $class=$4; + $name =~ s/-/_/g; + if(defined($valdic{$name})) {warn "The name \"$name\" does not have a unique value.\n"} + if(defined($namedic{$val})) {warn "The value \"$val\" does not have a unique name.\n"} + $valdic{$name}=$val; $namedic{$val}=$name; $classdic{$val}=$class if defined($class); + if(defined($mu)) { + if($mu eq '-') {next} + $muset{$val}= 1; + ++$nmu; + #if(!defined($maxmuval) || $val>$maxmuval) {$maxmuval=$val} + } + else {$muset{$val}= 0} + ++$nrr; + if(!defined($minval) || $val<$minval) {$minval=$val} + if(!defined($maxval) || $val>$maxval) {$maxval=$val} + } + else {die "Can't find name-value pair in following line:\n$_\n"} + } +} + +defined($minval) or die "No values defined.\n"; +if($nrr>255) {warn "Warning: total number of cache-able RR types is greater than 255.\n"} + +print << 'END-OF-TEXT'; +/* rr_types.h - A header file with names & descriptions of + all rr types known to pdnsd + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef _RR_TYPES_H_ +#define _RR_TYPES_H_ + +#include + +END-OF-TEXT + +print "#define T_MIN $minval\n"; +foreach my $name (sort {$valdic{$a} <=> $valdic{$b} } (keys %valdic)) { + printf("#define %-12s %2d\n", "T_$name", $valdic{$name}); +} +print "#define T_MAX $maxval\n"; +print("\n/* T_MAX - T_MIN + 1 */\n","#define T_NUM ",$maxval+1-$minval,"\n"); +#print("\n/* Largest most frequently used type value. */\n","#define T_MAXMU $maxmuval\n"); +print("\n/* Number of most frequently used rr types. */\n","#define NRRMU $nmu\n"); +print("\n/* Number of remaining rr types. */\n","#define NRREXT ",$nrr-$nmu,"\n"); +print("\n/* NRRMU + NRREXT */\n","#define NRRTOT $nrr\n"); + +print << 'END-OF-TEXT'; + +/* Lookup table for converting rr type values to internally used indices. */ +extern const unsigned short int rrlkuptab[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrlkuptab[T_NUM] = { +END-OF-TEXT +my @rrtpval=(); +for(my $val=$minval, my $i=0, my $j=$nmu, my $k=$nrr; $val<=$maxval; ++$val) { + my $idx; + if(defined($muset{$val})) { + if($muset{$val}) { + $idx = $i++; + } + else { + $idx = $j++; + } + $rrtpval[$idx]=$val; + } + else { + $idx = $k++; + } + + printf('%4d', $idx); + if(defined($namedic{$val})) { + print " /* $namedic{$val} */"; + } + print ',' if $val<$maxval; + print "\n"; +} +#print << 'END-OF-TEXT'; +#}; +##endif +# +#/* Table for converting internally used indices to rr type values. +# This is more or less the inverse of the rrlkuptab[] mapping. */ +#extern const unsigned short int rrtpval[NRRTOT]; +##if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +#const unsigned short int rrtpval[NRRTOT] = { +#END-OF-TEXT +#for(my $i=0; $i<$nrr; ++$i) { +# if($i ==0) { +# print " /* Most frequently used types. */\n"; +# } +# else { +# print ",\n"; +# } +# print " /* Remaining (less frequently used) types. */\n" +# if $i == $nmu; +# my $val= $rrtpval[$i]; +# print(" ",defined($namedic{$val})? "T_$namedic{$val}": $val); +#} +print << 'END-OF-TEXT'; +}; +#endif + +/* List of most frequently used RR types in ascending order. */ +extern const unsigned short int rrmuiterlist[NRRMU]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrmuiterlist[NRRMU] = { +END-OF-TEXT +for(my $val=$minval, my $i=0; $val<=$maxval; ++$val) { + if(defined($muset{$val}) && $muset{$val}) { + print ",\n" if $i++; + print(" ",defined($namedic{$val})? "T_$namedic{$val}": $val); + } +} +print << 'END-OF-TEXT'; + +}; +#endif + +/* List of the cache-able RR types in ascending order. */ +extern const unsigned short int rrcachiterlist[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS +const unsigned short int rrcachiterlist[NRRTOT] = { +END-OF-TEXT +for(my $val=$minval, my $i=0; $val<=$maxval; ++$val) { + if(defined($muset{$val})) { + print ",\n" if $i++; + print(" ",defined($namedic{$val})? "T_$namedic{$val}": $val); + } +} +print << 'END-OF-TEXT'; + +}; +#endif + +/* Optimized getrrset macros for fixed rr types. */ +END-OF-TEXT +for(my $val=$minval, my $i=0, my $j=0; $val<=$maxval; ++$val) { + if(defined($muset{$val})) { + my $name = $namedic{$val}; + my $mdef; + if($muset{$val}) { + $mdef= "GET_RRSMU(cent,$i)"; + ++$i; + } + else { + $mdef= "GET_RRSEXT(cent,$j)"; + ++$j; + } + printf("#define %-25s %s\n", "getrrset_$name(cent)", $mdef) + if defined($name); + } +} +print << 'END-OF-TEXT'; + +/* have_rr macros for fixed rr types. */ +END-OF-TEXT +for(my $val=$minval, my $i=0, my $j=0; $val<=$maxval; ++$val) { + my $name = $namedic{$val}; + my $mdef = '0'; + if(defined($muset{$val})) { + if($muset{$val}) { + $mdef= "HAVE_RRMU(cent,$i)"; + ++$i; + } + else { + $mdef= "HAVE_RREXT(cent,$j)"; + ++$j; + } + } + printf("#define %-25s %s\n", "have_rr_$name(cent)", $mdef) + if defined($name); +} +print << 'END-OF-TEXT'; + +/* These macros specify which RR types are cached by pdnsd. */ +END-OF-TEXT +for(my $val=$minval; $val<=$maxval; ++$val) { + if(defined($muset{$val})) { + my $name = $namedic{$val}; + printf("#define IS_CACHED_%-10s 1\n", defined($name)? $name: "TYPE$val") + } +} +print << 'END-OF-TEXT'; + +/* Array indices for most frequently used rr types. */ +END-OF-TEXT +for(my $val=$minval, my $i=0; $val<=$maxval; ++$val) { + if(defined($muset{$val}) && $muset{$val}) { + printf("#define %-18s %2d\n", "RRMUINDEX_$namedic{$val}", $i) + if defined($namedic{$val}); + ++$i; + } +} +print << 'END-OF-TEXT'; + +/* Table of rr names. */ +extern const char *const rrnames[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS +const char *const rrnames[T_NUM] = { +END-OF-TEXT +for(my $val=$minval; $val<=$maxval; ++$val) { + my $name = $namedic{$val}; + print(' "', defined($name)? $name: "TYPE$val", '"'); + print ',' if $val<$maxval; + print "\n"; +} +print << 'END-OF-TEXT'; +}; +#endif + +/* Structure for rr information */ +struct rr_infos { + unsigned short class; /* class (values see below) */ + unsigned short excludes; /* relations to other classes. + Mutual exclusion is marked by or'ing the + respective RRCL value in this field. + Exclusions should be symmetric. */ +}; + +/* Class values */ +#define RRCL_ALIAS 1 /* for CNAMES, conflicts with RRCL_RECORD */ +#define RRCL_RECORD 2 /* normal direct record */ +#define RRCL_IDEM 4 /* types that conflict with no others (MX, CNAME, ...) */ +#define RRCL_PTR 8 /* PTR */ + +/* Standard excludes for the classes */ +#define RRX_ALIAS (RRCL_RECORD|RRCL_PTR) +#define RRX_RECORD (RRCL_ALIAS|RRCL_PTR) +#define RRX_IDEM 0 +#define RRX_PTR (RRCL_ALIAS|RRCL_RECORD) + +/* There could be a separate table detailing the relationship of types, but this + * is slightly more flexible, as it allows a finer granularity of exclusion. Also, + * Membership in multiple classes could be added. + * Index by internally used RR-set indices, not RR type values! + */ +extern const struct rr_infos rr_info[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const struct rr_infos rr_info[NRRTOT] = { +END-OF-TEXT +for(my $i=0; $i<$nrr; ++$i) { + print ",\n" if $i; + my $val=$rrtpval[$i]; + my $class = (defined($classdic{$val})? $classdic{$val}: 'IDEM'); + printf(' %-16s %-15s %s',"{RRCL_$class,", "RRX_$class}", defined($namedic{$val})?"/* $namedic{$val} */":""); +} +print << 'END-OF-TEXT'; + +}; +#endif + +int rr_tp_byname(char *name); +const char *loc2str(const void *binary, char *ascii, size_t asclen); + +#endif +END-OF-TEXT + +exit diff --git a/service/src/main/jni/pdnsd/src/netdev.c b/service/src/main/jni/pdnsd/src/netdev.c new file mode 100644 index 0000000..bd5f8c4 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/netdev.c @@ -0,0 +1,363 @@ +/* netdev.c - Test network devices for existence and status + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +/* + * Portions are under the following copyright and taken from FreeBSD + * (clause 3 deleted as it no longer applies): + * + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD: src/sys/net/if.h,v 1.58.2.1 2000/05/05 13:37:04 jlemon Exp $ + */ + +#include +#include "ipvers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "helpers.h" +#include "netdev.h" +#include "error.h" + + +#if (TARGET==TARGET_BSD) +/* Taken from FreeBSD net/if.h rev. 1.58.2.1 */ +#define SIZEOF_ADDR_IFREQ(ifr) \ + ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \ + (sizeof(struct ifreq) - sizeof(struct sockaddr) + \ + (ifr).ifr_addr.sa_len) : sizeof(struct ifreq)) +#elif (TARGET==TARGET_CYGWIN) +#define SIZEOF_ADDR_IFREQ(ifr) (sizeof(struct sockaddr)) +#endif + +#define MAX_SOCKETOPEN_ERRS 10 +static volatile unsigned long socketopen_errs=0; + +/* + * Portions of the following code are Linux/FreeBSD specific. + * Please write interface-detection routines for other flavours of Unix if you can and want. + */ + +#if (TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +# if (TARGET==TARGET_LINUX) + +static volatile unsigned long isdn_errs=0; + +# ifdef ISDN_SUPPORT + +/* + * Test the status of an ippp interface. Taken from the isdn4k-utils (thanks!) and adapted + * by me (I love free software!) + * This will not work with older kernels. + * If your kernel is too old or too new, just try to get the status as uptest=exec command + * This will work, although slower. + */ + +# include + +int statusif(char *name) +{ + isdn_net_ioctl_phone phone; + int isdninfo,rc=0; + + if ((isdninfo = open("/dev/isdninfo", O_RDONLY))<0) { + if (++isdn_errs<=2) { + log_warn("Could not open /dev/isdninfo for uptest: %s",strerror(errno)); + } + return 0; + } + + strncp(phone.name, name, sizeof(phone.name)); + if (ioctl(isdninfo, IIOCNETGPN, &phone)==0) + rc=1; + close(isdninfo); + return rc; +} +# endif + +/* + * Test whether the network interface specified in ifname and its + * associated device specified in devname have locks owned by the + * same process. + */ +int dev_up(char *ifname, char *devname) +{ + FILE *fd; + int pidi, pidd, rv; + + { + char path[sizeof("/var/run/.pid")+strlen(ifname)]; + stpcpy(stpcpy(stpcpy(path,"/var/run/"),ifname),".pid"); + if ((fd=fopen(path, "r")) == NULL ) + return 0; + + if (fscanf(fd, "%d", &pidi) != 1 ) { + fclose(fd) ; + return 0; + } + fclose(fd); + } + + { + char path[sizeof("/var/lock/LCK..")+strlen(devname)]; + stpcpy(stpcpy(path,"/var/lock/LCK.."),devname); + if ((fd=fopen(path, "r")) == NULL) + return 0; + + if (fscanf(fd, "%d", &pidd) != 1) { + fclose(fd); + return 0; + } + fclose(fd); + } + + if (pidi != pidd) + return 0; + /* Test whether pppd is still alive */ + rv=kill(pidi,0); + return (rv==0 || (rv==-1 && errno!=ESRCH)); +} + + +# endif /*(TARGET==TARGET_LINUX)*/ + +/* + * Test whether the network device specified in devname is up and + * running (returns -1) or non-existent, down or not-running (returns 0) + * + * Note on IPv6-Comptability: rfc2133 requires all IPv6 implementation + * to be backwards-compatible to IPv4 in means of permitting socket(PF_INET,...) + * and similar. So, I don't put code here for both IPv4 and IPv6, since + * I use that socket only for ioctls. If somebody notices incompatabilities, + * please notify me. + */ +int if_up(char *devname) +{ + int sock; + struct ifreq ifr; +# if (TARGET==TARGET_LINUX) + unsigned int devnamelen=strlen(devname); + if (devnamelen>4 && devnamelen<=6 && strncmp(devname,"ippp",4)==0) { + /* This function didn't manage the interface uptest correctly. Thanks to + * Joachim Dorner for pointing out. + * The new code (statusif()) was shamelessly stolen from isdnctrl.c of the + * isdn4k-utils. */ +# ifdef ISDN_SUPPORT + return statusif(devname); +# else + if (isdn_errs++==0) { + log_warn("An ippp? device was specified for uptest, but pdnsd was compiled without ISDN support."); + log_warn("The uptest result will be wrong."); + } +# endif + /* If it doesn't match our rules for isdn devices, treat as normal if */ + } +# endif + if ((sock=socket(PF_INET,SOCK_DGRAM, IPPROTO_UDP))==-1) { + if(++socketopen_errs<=MAX_SOCKETOPEN_ERRS) { + log_warn("Could not open socket in if_up(): %s",strerror(errno)); + } + return 0; + } + strncp(ifr.ifr_name,devname,IFNAMSIZ); + if (ioctl(sock,SIOCGIFFLAGS,&ifr)==-1) { + close(sock); + return 0; + } + close(sock); + return (ifr.ifr_flags&IFF_UP) && (ifr.ifr_flags&IFF_RUNNING); +} + +# if (TARGET==TARGET_LINUX) +# ifdef ENABLE_IPV6 +#define MAX_IF_INET6_OPEN_ERRS 10 +static volatile unsigned long if_inet6_open_errs=0; +# endif + +int is_local_addr(pdnsd_a *a) +{ + int res=0; + +# ifdef ENABLE_IPV4 + if (run_ipv4) { + int i,sock; + struct ifreq ifr; + if ((sock=socket(PF_INET,SOCK_DGRAM, IPPROTO_UDP))==-1) { + if(++socketopen_errs<=MAX_SOCKETOPEN_ERRS) { + log_warn("Could not open socket in is_local_addr(): %s",strerror(errno)); + } + return 0; + } + for (i=1;i<255;i++) { + ifr.ifr_ifindex=i; + if (ioctl(sock,SIOCGIFNAME,&ifr)==-1) { + /* There may be gaps in the interface enumeration, so just continue */ + continue; + } + if (ioctl(sock,SIOCGIFADDR, &ifr)==-1) { + continue; + } + if (((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr.s_addr==a->ipv4.s_addr) { + res=1; + break; + } + } + close(sock); + } + +# endif +# ifdef ENABLE_IPV6 + ELSE_IPV6 { + char buf[40]; + FILE *f; + struct in6_addr b; + /* the interface configuration and information retrieval is obiously currently done via + * rt-netlink sockets. I think it is relatively likely to change in an incompatible way the + * Linux kernel (there seem to be some major changes for 2.4). + * Right now, I just analyze the /proc/net/if_inet6 entry. This may not be the fastest, but + * should work and is easy to adapt should the format change. */ + if (!(f=fopen("/proc/net/if_inet6","r"))) { + if(++if_inet6_open_errs<=MAX_IF_INET6_OPEN_ERRS) { + log_warn("Could not open /proc/net/if_inet6 in is_local_addr(): %s",strerror(errno)); + } + return 0; + } + /* The address is at the start of the line. We just read 32 characters and insert a ':' 7 + * times. Such, we can use inet_pton conveniently. More portable, that. */ + for(;;) { + int i,ch; char *p=buf; + for (i=0;i<32;i++) { + if(i && i%4==0) *p++ = ':'; + if ((ch=fgetc(f))==EOF) + goto fclose_return; /* we are at the end of the file and haven't found anything.*/ + if(ch=='\n') goto nextline; + *p++ = ch; + } + *p=0; + if (inet_pton(AF_INET6,buf,&b) >0) { + if (IN6_ARE_ADDR_EQUAL(&b,&a->ipv6)) { + res=1; + goto fclose_return; + } + } + do { + if ((ch=fgetc(f))==EOF) goto fclose_return; + } while(ch!='\n'); + nextline:; + } + fclose_return: + fclose(f); + } +# endif + return res; +} + +# else /*(TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN)*/ + + +#define MAX_SIOCGIFCONF_ERRS 4 +static volatile unsigned long siocgifconf_errs=0; + +int is_local_addr(pdnsd_a *a) +{ + int retval=0, sock, cnt; + struct ifconf ifc; + char buf[2048]; + + + if ((sock=socket(PF_INET,SOCK_DGRAM, IPPROTO_UDP))==-1) { + if(++socketopen_errs<=MAX_SOCKETOPEN_ERRS) { + log_warn("Could not open socket in is_local_addr(): %s",strerror(errno)); + } + return 0; + } + + ifc.ifc_len=sizeof(buf); + ifc.ifc_buf=buf; + if (ioctl(sock,SIOCGIFCONF,&ifc)==-1) { + if(++siocgifconf_errs<=MAX_SIOCGIFCONF_ERRS) { + log_warn("ioctl() call with request SIOCGIFCONF failed in is_local_addr(): %s",strerror(errno)); + } + goto close_sock_return; + } + + cnt=0; + while(cnt+sizeof(struct ifreq)<=ifc.ifc_len) { + struct ifreq *ir= (struct ifreq *)(buf+cnt); + cnt += SIZEOF_ADDR_IFREQ(*ir); + if (cnt>ifc.ifc_len) + break; + if (SEL_IPVER(ir->ifr_addr.sa_family==AF_INET && + ((struct sockaddr_in *)&ir->ifr_addr)->sin_addr.s_addr==a->ipv4.s_addr, + ir->ifr_addr.sa_family==AF_INET6 && + IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)&ir->ifr_addr)->sin6_addr, + &a->ipv6))) + { + retval=1; + break; + } + } + + close_sock_return: + close(sock); + + return retval; +} + +# endif + +#else +# error "Huh. No OS macro defined." +#endif diff --git a/service/src/main/jni/pdnsd/src/netdev.h b/service/src/main/jni/pdnsd/src/netdev.h new file mode 100644 index 0000000..529b8a8 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/netdev.h @@ -0,0 +1,32 @@ +/* netdev.h - Test network devices for existence and status + Copyright (C) 2000 Thomas Moestl + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _NETDEV_H_ +#define _NETDEV_H_ + +#include +#include "ipvers.h" + +int if_up(char *devname); +int dev_up(char *ifname, char *devname); +int is_local_addr(pdnsd_a *a); + +#endif diff --git a/service/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.am b/service/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.am new file mode 100644 index 0000000..bcb7327 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.am @@ -0,0 +1,18 @@ + +sbin_PROGRAMS = pdnsd-ctl + +pdnsd_ctl_SOURCES = pdnsd-ctl.c +pdnsd_ctl_LDADD = rr_types.o +pdnsd_ctl_DEPENDENCIES = rr_types.o + +# These are Symlinks we want to have in the package +#EXTRA_DIST = rr_types.h + +pdnsd-ctl.o rr_types.o: ../rr_types.h + +../rr_types.h: ../make_rr_types_h.pl ../rr_types.in + perl ../make_rr_types_h.pl ../rr_types.in > ../rr_types.h + +rr_types.o: %.o: ../%.c + $(COMPILE) -DCLIENT_ONLY -c $< + diff --git a/service/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.in b/service/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.in new file mode 100644 index 0000000..80f3987 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/pdnsd-ctl/Makefile.in @@ -0,0 +1,470 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +sbin_PROGRAMS = pdnsd-ctl$(EXEEXT) +subdir = src/pdnsd-ctl +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +am_pdnsd_ctl_OBJECTS = pdnsd-ctl.$(OBJEXT) +pdnsd_ctl_OBJECTS = $(am_pdnsd_ctl_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(pdnsd_ctl_SOURCES) +DIST_SOURCES = $(pdnsd_ctl_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +pdnsd_ctl_SOURCES = pdnsd-ctl.c +pdnsd_ctl_LDADD = rr_types.o +pdnsd_ctl_DEPENDENCIES = rr_types.o +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/pdnsd-ctl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/pdnsd-ctl/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) +pdnsd-ctl$(EXEEXT): $(pdnsd_ctl_OBJECTS) $(pdnsd_ctl_DEPENDENCIES) + @rm -f pdnsd-ctl$(EXEEXT) + $(LINK) $(pdnsd_ctl_OBJECTS) $(pdnsd_ctl_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdnsd-ctl.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-sbinPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-sbinPROGRAMS ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-sbinPROGRAMS + + +# These are Symlinks we want to have in the package +#EXTRA_DIST = rr_types.h + +pdnsd-ctl.o rr_types.o: ../rr_types.h + +../rr_types.h: ../make_rr_types_h.pl ../rr_types.in + perl ../make_rr_types_h.pl ../rr_types.in > ../rr_types.h + +rr_types.o: %.o: ../%.c + $(COMPILE) -DCLIENT_ONLY -c $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c b/service/src/main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c new file mode 100644 index 0000000..0840c20 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/pdnsd-ctl/pdnsd-ctl.c @@ -0,0 +1,802 @@ +/* pdnsd-ctl.c - Control pdnsd through a pipe + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include /* for offsetof */ +#include "../helpers.h" +#include "../status.h" +#include "../conff.h" +#include "../list.h" +#include "../dns.h" +#include "../rr_types.h" +#include "../cache.h" + +#if !defined(HAVE_ALLOCA) && !defined(alloca) +#define alloca malloc +#endif + + +#if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_PTON) +# define ALLOW_AAAA IS_CACHED_AAAA +#else +# define ALLOW_AAAA 0 +#endif + +static short int verbose=1; + +typedef struct { + char *name; + int val; +} cmd_s; + +#define CMD_LIST_RRTYPES (CTL_MAX+1) +#define CMD_HELP (CTL_MAX+2) +#define CMD_VERSION (CTL_MAX+3) + +static const cmd_s top_cmds[]={ + {"help",CMD_HELP},{"version",CMD_VERSION},{"list-rrtypes",CMD_LIST_RRTYPES}, + {"status",CTL_STATS},{"server",CTL_SERVER},{"record",CTL_RECORD}, + {"source",CTL_SOURCE},{"add",CTL_ADD},{"neg",CTL_NEG}, + {"config",CTL_CONFIG},{"include",CTL_INCLUDE},{"eval",CTL_EVAL}, + {"empty-cache",CTL_EMPTY}, {"dump",CTL_DUMP}, + {NULL,0} +}; +static const cmd_s server_cmds[]= {{"up",CTL_S_UP},{"down",CTL_S_DOWN},{"retest",CTL_S_RETEST},{NULL,0}}; +static const cmd_s record_cmds[]= {{"delete",CTL_R_DELETE},{"invalidate",CTL_R_INVAL},{NULL,0}}; +static const cmd_s onoff_cmds[]= {{"off",0},{"on",1},{NULL,0}}; +static const cmd_s rectype_cmds[]= {{"a",T_A}, +#if ALLOW_AAAA + {"aaaa",T_AAAA}, +#endif + {"ptr",T_PTR},{"cname",T_CNAME},{"mx",T_MX},{"ns",T_NS},{NULL,0}}; + +static const char version_message[] = + "pdnsd-ctl, version pdnsd-" VERSION "\n\n"; + +static const char license_statement[] = + "Copyright (C) 2000, 2001 Thomas Moestl\n" + "Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 Paul A. Rombouts\n\n" + "This program is part of the pdnsd package.\n\n" + "pdnsd is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 3 of the License, or\n" + "(at your option) any later version.\n\n" + "pdnsd is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with pdsnd; see the file COPYING. If not, see\n" + ".\n"; + +static const char *const help_messages[] = +{ + "Usage: pdnsd-ctl [-c cachedir] [-q] [arguments]\n\n" + + "Command-line options:\n" + + "-c\tcachedir\n\tSet the cache directory to cachedir (must match pdnsd setting).\n" + "-q\n\tBe quiet unless output is specified by command or something goes wrong.\n\n" + + "Commands and needed arguments are:\n" + + "help\t[no arguments]\n\tPrint this help.\n" + "version\t[no arguments]\n\tPrint version and license info.\n", + + "status\t[no arguments]\n\tPrint pdnsd's status.\n", + + "server\t(index|label)\t(up|down|retest)\t[dns1[,dns2[,...]]]\n" + "\tSet the status of the server with the given index to up or down, or\n" + "\tforce a retest. The index is assigned in the order of definition in\n" + "\tpdnsd.conf starting with 0. Use the status command to see the indexes.\n" + "\tYou can specify the label of a server (that matches the label option)\n" + "\tinstead of an index to make this easier.\n" + + "\tYou can specify all instead of an index to perform the action for all\n" + "\tservers registered with pdnsd.\n" + + "\tAn optional third argument can be given consisting of a list of IP\n" + "\taddresses separated by commas or spaces. This list will replace the\n" + "\taddresses of name servers used by pdnsd for the given server section.\n" + "\tThis feature is useful for run-time configuration of pdnsd with dynamic\n" + "\tDNS data in scripts called by ppp or DHCP clients. The last argument\n" + "\tmay also be an empty string, which causes existing IP addresses to be\n" + "\tremoved and the corresponding server section to become inactive.\n", + + "record\tname\t(delete|invalidate)\n" + "\tDelete or invalidate the record of the given domain if it is in the\n" + "\tcache.\n", + + "source\tfn\towner\t[ttl]\t[(on|off)]\t[noauth]\n" + "\tLoad a hosts-style file. Works like using the pdnsd source\n" + "\tconfiguration section.\n" + "\tOwner and ttl are used as in the source section. ttl has a default\n" + "\tof 900 (it does not need to be specified). The next to last argument\n" + "\tcorresponds to the serve_aliases option, and is off by default.\n" + "\tnoauth is used to make the domains non-authoritative (please\n" + "\tconsult the pdnsd manual for what that means).\n" + "\tfn is the name of the file, which must be readable by pdnsd.\n", + + "add\ta\taddr\tname\t[ttl]\t[noauth]\n" +#if ALLOW_AAAA + "add\taaaa\taddr\tname\t[ttl]\t[noauth]\n" +#endif + "add\tptr\thost\tname\t[ttl]\t[noauth]\n" + "add\tcname\thost\tname\t[ttl]\t[noauth]\n" + "add\tmx\thost\tname\tpref\t[ttl]\t[noauth]\n" + "add\tns\thost\tname\t[ttl]\t[noauth]\n" + "\tAdd a record of the given type to the pdnsd cache, replacing existing\n" + "\trecords for the same name and type. The 2nd argument corresponds\n" + "\tto the value of the option in the rr section that is named like\n" + "\tthe first argument. The addr argument may be a list of IP addresses,\n" + "\tseparated by commas or white space. The ttl is optional, the default is\n" + "\t900 seconds. noauth is used to make the domains non-authoritative.\n" + "\tIf you want no other record than the newly added in the cache, do\n" + "\tpdnsdctl record delete\n" + "\tbefore adding records.\n", + + "neg\tname\t[type]\t[ttl]\n" + "\tAdd a negatively cached record to pdnsd's cache, replacing existing\n" + "\trecords for the same name and type. If no type is given, the whole\n" + "\tdomain is cached negatively. For negatively cached records, errors are\n" + "\timmediately returned on a query, without querying other servers first.\n" + "\tThe ttl is optional, the default is 900 seconds.\n", + + "config\t[filename]\n" + "\tReload pdnsd's configuration file.\n" + "\tThe config file must be owned by the uid that pdnsd had when it was\n" + "\tstarted, and be readable by pdnsd's run_as uid. If no file name is\n" + "\tspecified, the config file used at start up is reloaded.\n", + + "include\tfilename\n" + "\tParse the given file as an include file, which may contain the same\n" + "\ttype of sections as a config file, expect for global and server\n" + "\tsections, which are not allowed. This command can be used to add data\n" + "\tto the cache without reconfiguring pdnsd.\n", + + "eval\tstring\n" + "\tParse string as if it were part of pdnsd's configuration file.\n" + "\tThe string should hold one or more complete configuration sections,\n" + "\tbut no global and server sections, which are not allowed.\n" + "\tIf multiple strings are given, they will be joined using newline chars\n" + "\tand parsed together.\n", + + "empty-cache\t[[+|-]name ...]\n" + "\tDelete all entries in the cache matching include/exclude rules.\n" + "\tIf no arguments are provided, the cache is completely emptied,\n" + "\tfreeing all existing entries. This also removes \"local\" records,\n" + "\tas defined by the config file. To restore local records, run\n" + "\t\"pdnsd-ctl config\" or \"pdnsd-ctl include filename\" immediately\n" + "\tafterwards.\n" + "\tIf one or more arguments are provided, these are interpreted as \n" + "\tinclude/exclude names. If an argument starts with a '+' the name is to\n" + "\tbe included. If an argument starts with a '-' it is to be excluded.\n" + "\tIf an argument does not begin with '+' or '-', a '+' is assumed.\n" + "\tIf the domain name of a cache entry ends in one of the names in the\n" + "\tlist, the first match will determine what happens. If the matching name\n" + "\tis to be included, the cache entry is deleted, otherwise it remains.\n" + "\tIf there are no matches, the default action is not to delete.\n", + + "dump\t[name]\n" + "\tPrint information stored in the cache about name.\n" + "\tIf name begins with a dot and is not the root domain, information\n" + "\tabout the names in the cache ending in name (including name without\n" + "\tthe leading dot) will be printed. If name is missing, information about\n" + "\tall the names in the cache will be printed.\n", + + "list-rrtypes\t[no arguments]\n" + "\tList available rr types for the neg command. Note that those are only\n" + "\tused for the neg command, not for add!\n" +}; + +#define NUM_HELP_MESSAGES (sizeof(help_messages)/sizeof(char*)) + + +/* Open connection to control socket and send command code. + If successful, open_sock returns a file descriptor for the new socket, + otherwise the program is aborted. +*/ +static int open_sock(const char *cache_dir, uint16_t cmd) +{ + struct sockaddr_un *sa; + unsigned int sa_len; + int sock; + uint16_t nc; + + if ((sock=socket(PF_UNIX,SOCK_STREAM,0))==-1) { + perror("Error: could not open socket"); + exit(2); + } + + sa_len = (offsetof(struct sockaddr_un, sun_path) + strlitlen("/pdnsd.status") + strlen(cache_dir)); + sa=(struct sockaddr_un *)alloca(sa_len+1); + sa->sun_family=AF_UNIX; + stpcpy(stpcpy(sa->sun_path,cache_dir),"/pdnsd.status"); + + if (connect(sock,(struct sockaddr *)sa,sa_len)==-1) { + fprintf(stderr,"Error: could not open socket %s: %s\n",sa->sun_path,strerror(errno)); + close(sock); + exit(2); + } + if(verbose) printf("Opening socket %s\n",sa->sun_path); + + /* Send command code */ + + nc=htons(cmd|CTL_CMDVERNR); /* Add magic number, convert to network byte order. */ + + if (write(sock,&nc,sizeof(nc))!=sizeof(nc)) { + perror("Error: could not write command code"); + close(sock); + exit(2); + } + + return sock; +} + +static void send_long(int fd,uint32_t cmd) +{ + uint32_t nc=htonl(cmd); + + if (write(fd,&nc,sizeof(nc))!=sizeof(nc)) { + perror("Error: could not write long"); + close(fd); + exit(2); + } +} + +static void send_short(int fd,uint16_t cmd) +{ + uint16_t nc=htons(cmd); + + if (write(fd,&nc,sizeof(nc))!=sizeof(nc)) { + perror("Error: could not write short"); + close(fd); + exit(2); + } +} + +#define MAXSENDSTRLEN 0xfffe + +static void send_string(int fd, const char *s) +{ + if(s) { + size_t len=strlen(s); + if(len>MAXSENDSTRLEN) { + fprintf(stderr,"Error: send_string: string length (%lu) exceeds maximum (%u).\n", + (unsigned long)len, MAXSENDSTRLEN); + close(fd); + exit(2); + } + send_short(fd,len); + if (write_all(fd,s,len)!=len) { + perror("Error: could not write string"); + close(fd); + exit(2); + } + } + else + send_short(fd, ~0); +} + +static uint16_t read_short(int fd) +{ + ssize_t err; + uint16_t nc; + + if ((err=read(fd,&nc,sizeof(nc)))!=sizeof(nc)) { + fprintf(stderr,"Error: could not read short: %s\n",err<0?strerror(errno):"unexpected EOF"); + close(fd); + exit(2); + } + return ntohs(nc); +} + +/* copy data from file descriptor fd to file stream out until EOF + or error is encountered. +*/ +static ssize_t copymsgtofile(int fd, FILE* out) +{ + ssize_t n,ntot=0; + char buf[1024]; + + while ((n=read(fd,buf,sizeof(buf)))>0) + ntot+=fwrite(buf,1,n,out); + + if(n<0) + return n; + + return ntot; +} + +static int match_cmd(const char *cmd, const cmd_s cmds[]) +{ + int i; + for(i=0; cmds[i].name; ++i) { + if (strcasecmp(cmd,cmds[i].name)==0) + return cmds[i].val; + } + return -1; +} + +int main(int argc, char *argv[]) +{ + char *cache_dir= NULL; + int rv=0; + { + int i; + char *arg; + for(i=1; i4) + goto wrong_args; + acnt=2; + server_cmd=match_cmd(argv[2],server_cmds); + if(server_cmd==-1) goto bad_arg; + pf=open_sock(cache_dir, cmd); + send_string(pf,argv[1]); + send_short(pf,server_cmd); + send_string(pf,argc<4?NULL:argv[3]); + } + goto read_retval; + + case CTL_RECORD: { + int record_cmd; + if (argc!=3) + goto wrong_args; + acnt=2; + record_cmd=match_cmd(argv[2],record_cmds); + if(record_cmd==-1) goto bad_arg; + pf=open_sock(cache_dir, cmd); + send_short(pf,record_cmd); + send_string(pf,argv[1]); + } + goto read_retval; + + case CTL_SOURCE: { + long ttl; + int servaliases,flags; + if (argc<3 || argc>6) + goto wrong_args; + ttl=900; + flags=DF_LOCAL; + acnt=3; + if (argc==6 || (argc>=4 && isdigit(argv[3][0]))) { + char *endptr; + ttl=strtol(argv[3],&endptr,0); + if (*endptr) + goto bad_arg; + acnt++; + } + servaliases=0; + if (acntacnt+2) + goto wrong_args; + + ttl=900; + flags=DF_LOCAL; + pref=0; + if(tp==T_MX) { + char *endptr; + pref=strtol(argv[4],&endptr,0); + if (*endptr) { + acnt=4; + goto bad_arg; + } + } + + if (acnt4) + goto wrong_args; + tp=255; + ttl=900; + acnt=2; + if (argc==3) { + if (isdigit(argv[2][0])) { + char *endptr; + ttl=strtol(argv[2],&endptr,0); + if (*endptr) + goto bad_arg; + } else if ((tp=rr_tp_byname(argv[2]))==-1) { + goto bad_type; + } + } else if (argc==4) { + char *endptr; + if ((tp=rr_tp_byname(argv[2]))==-1) + goto bad_type; + ttl=strtol(argv[3],&endptr,0); + if (*endptr) { + acnt=3; + goto bad_arg; + } + } + pf=open_sock(cache_dir, cmd); + send_string(pf,argv[1]); + send_short(pf,tp); + send_long(pf,ttl); + } + goto read_retval; + + case CTL_CONFIG: + if (argc>2) + goto wrong_args; + pf=open_sock(cache_dir, cmd); + send_string(pf,argc<2?NULL:argv[1]); + goto read_retval; + + case CTL_INCLUDE: + if (argc!=2) + goto wrong_args; + pf=open_sock(cache_dir, cmd); + send_string(pf,argv[1]); + goto read_retval; + + case CTL_EVAL: { + int i; size_t bufsz; + + if (argc<2) + goto wrong_args; + bufsz=0; + for(i=1; iMAXSENDSTRLEN) { + fprintf(stderr,"Cannot send 'eval' command: " + "string length (%lu) exceeds maximum (%u).\n", + (unsigned long)bufsz, MAXSENDSTRLEN); + exit(2); + } + pf=open_sock(cache_dir, cmd); + send_short(pf,bufsz); + { + /* Variable-size array for storing the joined strings. */ + char buf[bufsz]; + char *p=buf; + for(i=1; iMAXSENDSTRLEN) { + fprintf(stderr,"Cannot send 'empty' command: " + "string length (%lu) exceeds maximum (%u).\n", + (unsigned long)totsz, MAXSENDSTRLEN); + exit(2); + } + pf=open_sock(cache_dir, cmd); + if(argc>1) { + send_short(pf,totsz); + for(i=1; i2) + goto wrong_args; + pf=open_sock(cache_dir, cmd); + send_string(pf,argc<2?NULL:argv[1]); + copy_pf: + if((rv=read_short(pf))) + goto retval_failed; + if(copymsgtofile(pf,stdout)<0) { + perror("Error while reading from socket"); + close(pf); + exit(2); + } + goto close_pf; + + read_retval: + if((rv=read_short(pf))) { + retval_failed: + fprintf(stderr,"Failed: "); + if(copymsgtofile(pf,stderr)<0) + fprintf(stderr,"(could not read error message from socket: %s)",strerror(errno)); + + fputc('\n',stderr); + } + close_pf: + if(close(pf)==-1) + perror("Couldn't close socket"); + else if (rv==0 && verbose) + printf("Succeeded\n"); + break; + wrong_args: + fprintf(stderr,"Wrong number of arguments for '%s' command.\n",argv[0]); + goto print_cmd_usage; + bad_arg: + fprintf(stderr,"Bad argument for '%s' command: %s\n",argv[0],argv[acnt]); + print_cmd_usage: + fprintf(stderr,"Usage:\n\n%s\n" + "Try 'pdnsd-ctl help' for a description of all available commands and options.\n", + help_messages[cmd]); + exit(2); + bad_type: + fprintf(stderr,"Bad (type) argument for '%s' command: %s\n" + "Run 'pdnsd-ctl list-rrtypes' for a list of available rr types.\n", + argv[0],argv[acnt]); + exit(2); + } + } + + return rv; +} + diff --git a/service/src/main/jni/pdnsd/src/pdnsd_assert.h b/service/src/main/jni/pdnsd/src/pdnsd_assert.h new file mode 100644 index 0000000..0acdfc2 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/pdnsd_assert.h @@ -0,0 +1,51 @@ +/* This include file was added by Paul A. Rombouts. + I had terrible difficulties with cyclic dependencies of the include files + written by Thomas Moestl. The only way I knew how to break the cycle was to + put some declarations in a seperate file. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef PDNSD_ASSERT_H +#define PDNSD_ASSERT_H + +/* Originally in helpers.h */ + +/* format string checking for printf-like functions */ +#ifdef __GNUC__ +#define printfunc(fmt, firstva) __attribute__((__format__(__printf__, fmt, firstva))) +#else +#define printfunc(fmt, firstva) +#endif + +void pdnsd_exit(void); + + +/* + * Assert macro, used in some places. For now, it should be always defined, not + * only in the DEBUG case, to be on the safe side security-wise. + */ +#define PDNSD_ASSERT(cond, msg) \ + { if (!(cond)) { \ + log_error("%s:%d: %s", __FILE__, __LINE__, msg); \ + pdnsd_exit(); \ + } } + +#endif diff --git a/service/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.am b/service/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.am new file mode 100644 index 0000000..2a7b420 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.am @@ -0,0 +1,7 @@ + +install-exec-local: + if [ "$(distribution)" = "ArchLinux" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/pdnsd";\ + fi + diff --git a/service/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.in b/service/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.in new file mode 100644 index 0000000..6af6f9e --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/ArchLinux/Makefile.in @@ -0,0 +1,332 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/ArchLinux +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/ArchLinux/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/ArchLinux/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + if [ "$(distribution)" = "ArchLinux" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/pdnsd";\ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in b/service/src/main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in new file mode 100644 index 0000000..c392750 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/ArchLinux/pdnsd.in @@ -0,0 +1,45 @@ +#!/bin/bash + +. /etc/rc.conf +. /etc/rc.d/functions + +PID=`pidof -o %PPID @prefix@/sbin/pdnsd` + +start() { + stat_busy "Starting PDNSD" + [ -z "$PID" ] && @prefix@/sbin/pdnsd -d -c /etc/pdnsd.conf + if [ $? -gt 0 ]; then + stat_fail + else + add_daemon pdnsd + stat_done + fi +} + +stop() { + stat_busy "Stopping PDNSD" + [ ! -z "$PID" ] && kill $PID &> /dev/null + if [ $? -gt 0 ]; then + stat_fail + else + rm_daemon pdnsd + stat_done + fi +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + $0 stop + sleep 2 + $0 start + ;; + *) + echo "usage: $0 {start|stop|restart}" +esac +exit 0 diff --git a/service/src/main/jni/pdnsd/src/rc/Debian/Makefile.am b/service/src/main/jni/pdnsd/src/rc/Debian/Makefile.am new file mode 100644 index 0000000..61d3eb3 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Debian/Makefile.am @@ -0,0 +1,8 @@ + +install-exec-local: + if [ "$(distribution)" = "Debian" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/etc/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/init.d"; \ + update-rc.d pdnsd defaults 19 ;\ + fi diff --git a/service/src/main/jni/pdnsd/src/rc/Debian/Makefile.in b/service/src/main/jni/pdnsd/src/rc/Debian/Makefile.in new file mode 100644 index 0000000..992a059 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Debian/Makefile.in @@ -0,0 +1,334 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/Debian +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/Debian/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/Debian/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + if [ "$(distribution)" = "Debian" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/etc/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/init.d"; \ + update-rc.d pdnsd defaults 19 ;\ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in b/service/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in new file mode 100644 index 0000000..068606e --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Debian/pdnsd.in @@ -0,0 +1,52 @@ +#!/bin/sh + +# +# This script was written and contributed by Markus Mohr, and +# slightly modified by me for version 1.0.6 (which obviously +# broke it some way). I then applied a set of corrections +# by Markus Mohr. +# +# Carsten Block has patched this with some magic so that +# the actual script is generated by configure, and that +# the pdnsd user is determined from pdnsd.conf +# I changed this a little to use the --pdnsd-user option +# of pdnsd to determine the run_as user. +# + +PATH=/sbin:/bin:/usr/sbin:/usr/bin + +test -x @prefix@/sbin/pdnsd || exit 0 + +case "$1" in + start) + # Check if cache dir exists and recreate if neccessary + test -d @cachedir@ || mkdir @cachedir@ + RUNAS=`@prefix@/sbin/pdnsd --pdnsd-user` || echo -n " failed" + [ -z "$RUNAS" ] && RUNAS=nobody + chown $RUNAS @cachedir@ + echo -n "Starting domain name service: pdnsd" + start-stop-daemon --start --quiet --pidfile /var/run/pdnsd.pid --name pdnsd \ + --exec @prefix@/sbin/pdnsd -- --daemon -p /var/run/pdnsd.pid \ + || echo -n " failed" + echo "." + ;; + + stop) + echo -n "Stopping domain name service: pdnsd" + start-stop-daemon --stop --quiet --pidfile /var/run/pdnsd.pid --name pdnsd --exec @prefix@/sbin/pdnsd \ + || echo -n " failed" + echo "." + ;; + + restart) + $0 stop + $0 start + ;; + + *) + echo "Usage: /etc/init.d/pdnsd {start|stop|restart}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/service/src/main/jni/pdnsd/src/rc/Makefile.am b/service/src/main/jni/pdnsd/src/rc/Makefile.am new file mode 100644 index 0000000..abf2e6d --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Makefile.am @@ -0,0 +1,5 @@ + +SUBDIRS = RedHat SuSE Debian Slackware ArchLinux + +EXTRA_DIST = README + diff --git a/service/src/main/jni/pdnsd/src/rc/Makefile.in b/service/src/main/jni/pdnsd/src/rc/Makefile.in new file mode 100644 index 0000000..38ee297 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Makefile.in @@ -0,0 +1,526 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = RedHat SuSE Debian Slackware ArchLinux +EXTRA_DIST = README +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ + install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic ctags \ + ctags-recursive distclean distclean-generic distclean-tags \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \ + tags-recursive uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/rc/README b/service/src/main/jni/pdnsd/src/rc/README new file mode 100644 index 0000000..4e00764 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/README @@ -0,0 +1,104 @@ +rc/ +=== + +These are start scripts for different Linux distros and other things that +do not directly belong to pdnsd. +If you do start scripts for the distro you use, please GPL them and send +them in, so that they can be included in this package for other users. +Note that there is NO WARRANTY OF ANY KIND on anything in this directory; +read the COPYING that comes with pdnsd for details. +So far there are files in the following directories: + +SuSE +---- +pdnsd - Start script for SuSE Linux. Tested for 6.? but should run on some + versions below. You can do 'make install' as root in the SuSE + directory to install it, or you can install manually: + --manual installation------------------------------------------------- + For manual installation, copy it into /sbin/init.d/, go to + /sbin/init.d/rc2.d/ and create there the following two symlinks: + S11pdnsd -> ../pdnsd (do "ln -s ../pdnsd S11pdnsd" in that dir) + K34pdnsd -> ../pdnsd (do "ln -s ../pdnsd K34pdnsd" in that dir) + The numbers dictate the order different services are started and + might need to be modified. Then edit your /etc/rc.config file and + add the line "START_PDNSD=yes" to start pdnsd at boot time. + ---------------------------------------------------------------------- + If you used the 'make install' command, "START_PDNSD=yes" has been + appended to your /etc/rc.config file, causing pdnsd to be started + at boot time. If you don't want that, change the "yes" into "no". + This start script was created from /sbin/init.d/skeleton by me, so the + most is copyrighted by SuSE. They put it under the GPL, however, so + the licence stated in COPYING also applies to this script. + This is no official SuSE script, and SuSE naturally does NO support + for it. + +Redhat +------ +The contents of the Redhat directory and the following documentation were +contributed by Torben Janssen. Thanks a lot! + +pdnsd - Start script for Redhat Linux. Tested for 6.1 but should run on 5.0+. + You can do 'make install' as root in the Redhat directory to + install it, or you can install manually: + + --manual installation------------------------------------------------- + For manual installation, copy pdnsd into /etc/rc.d/init.d/ + + Then go to /etc/rc.d/rc3.d and create there the following symlink: + S78pdnsd -> ../init.d/pdnsd + (do "ln -f -s ../init.d/pdnsd S78pdnsd" in that dir) + + Then go to /etc/rc.d/rc0.d and create there the following symlink: + K78pdnsd -> ../init.d/pdnsd + (do "ln -f -s ../init.d/pdnsd K78pdnsd" in that dir) + + Then go to /etc/rc.d/rc6.d and create there the following symlink: + K78pdnsd -> ../init.d/pdnsd + (do "ln -f -s ../init.d/pdnsd K78pdnsd" in that dir) + + WHY + --- + the rc[0-6].d dirs includes the scripts which starts/stops the + services on entering runlevel [0-6] + the interesting runlevels on Redhat are: + 0 - halt + 3 - multi user system + 6 - reboot + The links have an 'S' or 'K' and a number at the beginnig. All links + with 'S' starts the script on entering the runlevel and 'K' stops + them. + So, there's an 'S' link in rc3 and 'K' links in rc0 and rc6. + I choose 78 as number, because there was no script with this number on + my system. You can choose every number you want, AFAIK . + + This is no offical Redhat script, and Redhat naturally does NO support + for it. + +Debian +------ +The contents of the Debian directory were contributed by Markus Mohr. +His installation instructions are (translated): +Install the pdnsd script to /etc/init and run +update-rc.d pdnsd defaults 19 +as root. + +Slackware +--------- +A Slackware start-up script rc.pdnsd was contributed by Nikola Kotur . +His comments were: + +Slackware uses traditional BSD style init script layout instead of SystemV +style startup scripts. So I adjusted the start-up script for pdnsd, and +now it can be used with Slackware 9.1 distribution, and probably with all the +others. + +Additional info: +1) put these lines in the /etc/rc.d/rc.M: + if [ -x /etc/rc.d/rc.pdnsd ]; then + /etc/rc.d/rc.pdnsd start + fi + +2) put these lines in the /etc/rc.d/rc.6 and /etc/rc.d/rc.K: + if [ -x /etc/rc.d/rc.pdnsd ]; then + /etc/rc.d/rc.pdnsd stop + fi diff --git a/service/src/main/jni/pdnsd/src/rc/RedHat/Makefile.am b/service/src/main/jni/pdnsd/src/rc/RedHat/Makefile.am new file mode 100644 index 0000000..cb8de88 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/RedHat/Makefile.am @@ -0,0 +1,12 @@ + +# no need to create links. 'chkconfig' will take care of this. +# In the spec case, chkconfig is called during rpm install +install-exec-local: + if [ "$(distribution)" = "RedHat" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/init.d/pdnsd"; \ + if [ "$(specbuild)" = "no" ] ; then \ + /sbin/chkconfig --add pdnsd; \ + fi \ + fi + diff --git a/service/src/main/jni/pdnsd/src/rc/RedHat/Makefile.in b/service/src/main/jni/pdnsd/src/rc/RedHat/Makefile.in new file mode 100644 index 0000000..a88a037 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/RedHat/Makefile.in @@ -0,0 +1,337 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/RedHat +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/RedHat/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/RedHat/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +# no need to create links. 'chkconfig' will take care of this. +# In the spec case, chkconfig is called during rpm install +install-exec-local: + if [ "$(distribution)" = "RedHat" ] ; then \ + $(mkinstalldirs) "$(DESTDIR)/etc/rc.d/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/etc/rc.d/init.d/pdnsd"; \ + if [ "$(specbuild)" = "no" ] ; then \ + /sbin/chkconfig --add pdnsd; \ + fi \ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in b/service/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in new file mode 100644 index 0000000..b6d9081 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/RedHat/pdnsd.in @@ -0,0 +1,88 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/pdnsd +# +# Script for starting the Proxy DNS Daemon +# Modified by Paul Rombouts, 2003 +# +# chkconfig: 2345 11 89 +# description: Proxy DNS Daemon +# processname: pdnsd +# config: /etc/pdnsd.conf + +PATH=/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin + +# Source function library. +. /etc/rc.d/init.d/functions + +# Source networking configuration. +. /etc/sysconfig/network + +# Check that networking is up. +if [[ $NETWORKING == [Nn][Oo] ]]; then exit 0; fi + +# Source sysconfig settings, if any. +if [ -f /etc/sysconfig/pdnsd ]; then . /etc/sysconfig/pdnsd; fi + +start() { + echo -n 'Starting pdnsd: ' + daemon @prefix@/sbin/pdnsd -d -s -p /var/run/pdnsd.pid "$EXTRAOPTIONS" + local RETVAL=$? + echo + if [ $RETVAL -eq 0 ]; then touch /var/lock/subsys/pdnsd; fi + return $RETVAL +} + +stop() { + echo -n 'Shutting down pdnsd: ' + killproc pdnsd + local RETVAL=$? + case @threadlib@ in + [Ll]inux[Tt]hreads*|lt*) + # Wait until all threads have terminated. + local -i count=20 + while [[ count -gt 0 ]] && pidof pdnsd > /dev/null + do + usleep 200000 + let --count + done + ;; + esac + echo + if [ $RETVAL -eq 0 ]; then rm -f /var/lock/subsys/pdnsd; fi + return $RETVAL +} + +restart() { + stop + start +} + +# +# See how we were called. +# +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status pdnsd + ;; + reload) + @prefix@/sbin/pdnsd-ctl config + ;; + restart) + restart + ;; + condrestart) + if [ -f /var/lock/subsys/pdnsd ]; then restart; fi + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}" + exit 1 +esac + +exit diff --git a/service/src/main/jni/pdnsd/src/rc/Slackware/Makefile.am b/service/src/main/jni/pdnsd/src/rc/Slackware/Makefile.am new file mode 100644 index 0000000..e44b50f --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Slackware/Makefile.am @@ -0,0 +1,3 @@ +# TODO: write an install rule for the Slackware start-up script. + +install-exec-local: diff --git a/service/src/main/jni/pdnsd/src/rc/Slackware/Makefile.in b/service/src/main/jni/pdnsd/src/rc/Slackware/Makefile.in new file mode 100644 index 0000000..14ebd33 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Slackware/Makefile.in @@ -0,0 +1,330 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# TODO: write an install rule for the Slackware start-up script. +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/Slackware +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/rc.pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = rc.pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/Slackware/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/Slackware/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +rc.pdnsd: $(top_builddir)/config.status $(srcdir)/rc.pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/rc/Slackware/rc.pdnsd.in b/service/src/main/jni/pdnsd/src/rc/Slackware/rc.pdnsd.in new file mode 100644 index 0000000..6e96971 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/Slackware/rc.pdnsd.in @@ -0,0 +1,74 @@ +#!/bin/bash +# +# /etc/rc.d/rc.pdnsd +# +# Starts the Proxy DNS Daemon +# +# description: Proxy DNS Daemon +# processname: pdnsd +# config: /etc/pdnsd.conf +# distribution: Slackware +# author: Nikola Kotur +# +# Additional info: +# 1) put these lines in the /etc/rc.d/rc.M: +# if [ -x /etc/rc.d/rc.pdnsd ]; then +# /etc/rc.d/rc.pdnsd start +# fi +# +# 2) put these lines in the /etc/rc.d/rc.6 and /etc/rc.d/rc.K: +# if [ -x /etc/rc.d/rc.pdnsd ]; then +# /etc/rc.d/rc.pdnsd stop +# fi + + +test -x @prefix@/sbin/pdnsd || exit 0 +[ -f @sysconfdir@/pdnsd.conf ] || exit 1 + +RETVAL=0 + +start() { + echo -n "Starting pdnsd... " + RETVAL=$? + @prefix@/sbin/pdnsd -d + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/pdnsd + echo ' OK' +} + +stop() { + echo -n "Shutting down pdnsd... " + killall pdnsd + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/pdnsd + echo ' OK' +} + +restart() { + stop + start +} + +condrestart() { + [ -e /var/lock/subsys/pdnsd ] && restart + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + reload|restart) + restart + ;; + condrestart) + condrestart + ;; + *) + echo $"Usage: $0 {start|stop|restart|condrestart|reload}" + RETVAL=1 +esac + +exit $RETVAL diff --git a/service/src/main/jni/pdnsd/src/rc/SuSE/Makefile.am b/service/src/main/jni/pdnsd/src/rc/SuSE/Makefile.am new file mode 100644 index 0000000..dc5f485 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/SuSE/Makefile.am @@ -0,0 +1,22 @@ + +install-exec-local: + if [ "$(distribution)" = "SuSE" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/sbin/init.d/pdnsd"; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc2.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc2.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc3.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc3.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + grep "START_PDNSD" "$(DESTDIR)/etc/rc.config" > /dev/null ; \ + if [ $$? -eq 1 ] ; then \ + echo -e "\n\n#\n# Set to yes to start pdnsd at boot time\n#\nSTART_PDNSD=yes" >> /etc/rc.config ; \ + fi \ + fi + + + diff --git a/service/src/main/jni/pdnsd/src/rc/SuSE/Makefile.in b/service/src/main/jni/pdnsd/src/rc/SuSE/Makefile.in new file mode 100644 index 0000000..df1660f --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/SuSE/Makefile.in @@ -0,0 +1,345 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/rc/SuSE +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/pdnsd.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = pdnsd +CONFIG_CLEAN_VPATH_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/rc/SuSE/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/rc/SuSE/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +pdnsd: $(top_builddir)/config.status $(srcdir)/pdnsd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am + + +install-exec-local: + if [ "$(distribution)" = "SuSE" ] ; then \ + CURDIR=`pwd`; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d"; \ + $(INSTALL_SCRIPT) $(srcdir)/pdnsd "$(DESTDIR)/sbin/init.d/pdnsd"; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc2.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc2.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + $(mkinstalldirs) "$(DESTDIR)/sbin/init.d/rc3.d"; \ + cd "$(DESTDIR)/sbin/init.d/rc3.d"; \ + ln -fs ../pdnsd K34pdnsd; ln -s ../pdnsd S11pdnsd; \ + cd $$CURDIR ; \ + grep "START_PDNSD" "$(DESTDIR)/etc/rc.config" > /dev/null ; \ + if [ $$? -eq 1 ] ; then \ + echo -e "\n\n#\n# Set to yes to start pdnsd at boot time\n#\nSTART_PDNSD=yes" >> /etc/rc.config ; \ + fi \ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in b/service/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in new file mode 100644 index 0000000..9711eca --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rc/SuSE/pdnsd.in @@ -0,0 +1,68 @@ +#! /bin/sh +# Copyright (c) 1995-1998 SuSE GmbH Nuernberg, Germany. +# +# Modified 2000 from SuSE Linux 6.3 /sbin/init.d/skeleton by Thomas Moestl +# +# /sbin/init.d/pdnsd +# +# and symbolic its link +# +# /sbin/rc?/pdnsd +# + +. /etc/rc.config + +# Determine the base and follow a runlevel link name. +base=${0##*/} +link=${base#*[SK][0-9][0-9]} + +# Force execution if not called by a runlevel directory. +test $link = $base && START_PDNSD=yes +test "$START_PDNSD" = yes || exit 0 + +# The echo return value for success (defined in /etc/rc.config). +return=$rc_done +case "$1" in + start) + echo -n "Starting pdnsd" + ## Start daemon with startproc(8). If this fails + ## the echo return value is set appropriate. + + startproc @prefix@/sbin/pdnsd -d || return=$rc_failed + + echo -e "$return" + ;; + stop) + echo -n "Shutting down pdnsd" + ## Stop daemon with killproc(8) and if this fails + ## set echo the echo return value. + + killproc -TERM @prefix@/sbin/pdnsd || return=$rc_failed + + echo -e "$return" + ;; + restart) + ## If first returns OK call the second, if first or + ## second command fails, set echo return value. + $0 stop && $0 start || return=$rc_failed + ;; + reload) + $0 stop && $0 start || return=$rc_failed + ;; + status) + echo -n "Checking for pdnsd: " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + checkproc @prefix@/sbin/pdnsd && echo OK || echo No process + ;; + *) + echo "Usage: $0 {start|stop|status|restart|reload}" + exit 1 + ;; +esac + +# Inform the caller not only verbosely and set an exit status. +test "$return" = "$rc_done" || exit 1 +exit 0 + diff --git a/service/src/main/jni/pdnsd/src/rr_types.c b/service/src/main/jni/pdnsd/src/rr_types.c new file mode 100644 index 0000000..275a90a --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rr_types.c @@ -0,0 +1,172 @@ +/* rr_types.c - Tables with information for handling + all rr types known to pdnsd, plus + some helper functions useful for turning + binary RR data into text or vice versa. + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2003, 2004, 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#define DEFINE_RR_TYPE_ARRAYS 1 +#include "helpers.h" +#include "dns.h" +#include "rr_types.h" + + +/* + * OK, this is inefficient. But it is used _really_ seldom (only in some cases while parsing the + * config file or by pdnsd-ctl), so it is much more effective to sort by id. + */ +int rr_tp_byname(char *name) +{ + int i; + + for (i=0;i> 4); + exponent = (prec & 0x0f); + + if(mantissa>=10 || exponent>=10) + return NULL; + if (exponent>= 2) + sprintf(retbuf, "%u", mantissa * poweroften[exponent-2]); + else + sprintf(retbuf, "0.%.2u", mantissa * poweroften[exponent]); + return (retbuf); +} + +/* takes an on-the-wire LOC RR and formats it in a human readable format. + This is an adapted version of the loc_ntoa function that + can be found in the BIND 9 source. + */ +const char *loc2str(const void *binary, char *ascii, size_t asclen) +{ + const unsigned char *cp = binary; + + int latdeg, latmin, latsec, latsecfrac; + int longdeg, longmin, longsec, longsecfrac; + char northsouth, eastwest; + const char *altsign; + int altmeters, altfrac; + + const uint32_t referencealt = 100000 * 100; + + int32_t latval, longval, altval; + uint32_t templ; + uint8_t sizeval, hpval, vpval, versionval; + + char sizestr[NPRECSIZE],hpstr[NPRECSIZE],vpstr[NPRECSIZE]; + + versionval = *cp++; + + if (versionval) { + /* unknown LOC RR version */ + return NULL; + } + + sizeval = *cp++; + + hpval = *cp++; + vpval = *cp++; + + GETINT32(templ, cp); + latval = (templ - ((unsigned)1<<31)); + + GETINT32(templ, cp); + longval = (templ - ((unsigned)1<<31)); + + GETINT32(templ, cp); + if (templ < referencealt) { /* below WGS 84 spheroid */ + altval = referencealt - templ; + altsign = "-"; + } else { + altval = templ - referencealt; + altsign = ""; + } + + if (latval < 0) { + northsouth = 'S'; + latval = -latval; + } else + northsouth = 'N'; + + latsecfrac = latval % 1000; + latval /= 1000; + latsec = latval % 60; + latval /= 60; + latmin = latval % 60; + latval /= 60; + latdeg = latval; + + if (longval < 0) { + eastwest = 'W'; + longval = -longval; + } else + eastwest = 'E'; + + longsecfrac = longval % 1000; + longval /= 1000; + longsec = longval % 60; + longval /= 60; + longmin = longval % 60; + longval /= 60; + longdeg = longval; + + altfrac = altval % 100; + altmeters = (altval / 100); + + if(!precsize_ntoa(sizeval,sizestr) || !precsize_ntoa(hpval,hpstr) || !precsize_ntoa(vpval,vpstr)) + return NULL; + { + int n=snprintf(ascii,asclen, + "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %s%d.%.2dm %sm %sm %sm", + latdeg, latmin, latsec, latsecfrac, northsouth, + longdeg, longmin, longsec, longsecfrac, eastwest, + altsign, altmeters, altfrac, + sizestr, hpstr, vpstr); + if(n<0 || n>=asclen) + return NULL; + } + + return (ascii); +} + +#endif diff --git a/service/src/main/jni/pdnsd/src/rr_types.h b/service/src/main/jni/pdnsd/src/rr_types.h new file mode 100644 index 0000000..6025fae --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rr_types.h @@ -0,0 +1,536 @@ +/* This file was generated by running 'make_rr_types_h.pl rr_types.in'. + Modifications to this file may be lost the next time it is automatically + regenerated. +*/ + +/* rr_types.h - A header file with names & descriptions of + all rr types known to pdnsd + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2007, 2010, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#ifndef _RR_TYPES_H_ +#define _RR_TYPES_H_ + +#include + +#define T_MIN 1 +#define T_A 1 +#define T_NS 2 +#define T_MD 3 +#define T_MF 4 +#define T_CNAME 5 +#define T_SOA 6 +#define T_MB 7 +#define T_MG 8 +#define T_MR 9 +#define T_NULL 10 +#define T_WKS 11 +#define T_PTR 12 +#define T_HINFO 13 +#define T_MINFO 14 +#define T_MX 15 +#define T_TXT 16 +#define T_RP 17 +#define T_AFSDB 18 +#define T_X25 19 +#define T_ISDN 20 +#define T_RT 21 +#define T_NSAP 22 +#define T_NSAP_PTR 23 +#define T_SIG 24 +#define T_KEY 25 +#define T_PX 26 +#define T_GPOS 27 +#define T_AAAA 28 +#define T_LOC 29 +#define T_NXT 30 +#define T_EID 31 +#define T_NIMLOC 32 +#define T_SRV 33 +#define T_ATMA 34 +#define T_NAPTR 35 +#define T_KX 36 +#define T_CERT 37 +#define T_A6 38 +#define T_DNAME 39 +#define T_SINK 40 +#define T_OPT 41 +#define T_APL 42 +#define T_DS 43 +#define T_SSHFP 44 +#define T_IPSECKEY 45 +#define T_RRSIG 46 +#define T_NSEC 47 +#define T_DNSKEY 48 +#define T_DHCID 49 +#define T_NSEC3 50 +#define T_NSEC3PARAM 51 +#define T_HIP 55 +#define T_NINFO 56 +#define T_RKEY 57 +#define T_TALINK 58 +#define T_SPF 99 +#define T_UINFO 100 +#define T_UID 101 +#define T_GID 102 +#define T_UNSPEC 103 +#define T_MAX 51 + +/* T_MAX - T_MIN + 1 */ +#define T_NUM 51 + +/* Number of most frequently used rr types. */ +#define NRRMU 8 + +/* Number of remaining rr types. */ +#define NRREXT 39 + +/* NRRMU + NRREXT */ +#define NRRTOT 47 + +/* Lookup table for converting rr type values to internally used indices. */ +extern const unsigned short int rrlkuptab[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrlkuptab[T_NUM] = { + 0 /* A */, + 1 /* NS */, + 8 /* MD */, + 9 /* MF */, + 2 /* CNAME */, + 3 /* SOA */, + 10 /* MB */, + 11 /* MG */, + 12 /* MR */, + 13 /* NULL */, + 14 /* WKS */, + 4 /* PTR */, + 15 /* HINFO */, + 16 /* MINFO */, + 5 /* MX */, + 6 /* TXT */, + 17 /* RP */, + 18 /* AFSDB */, + 19 /* X25 */, + 20 /* ISDN */, + 21 /* RT */, + 22 /* NSAP */, + 23 /* NSAP_PTR */, + 24 /* SIG */, + 25 /* KEY */, + 26 /* PX */, + 27 /* GPOS */, + 7 /* AAAA */, + 28 /* LOC */, + 29 /* NXT */, + 30 /* EID */, + 31 /* NIMLOC */, + 32 /* SRV */, + 33 /* ATMA */, + 34 /* NAPTR */, + 35 /* KX */, + 36 /* CERT */, + 47 /* A6 */, + 48 /* DNAME */, + 49 /* SINK */, + 50 /* OPT */, + 37 /* APL */, + 38 /* DS */, + 39 /* SSHFP */, + 40 /* IPSECKEY */, + 41 /* RRSIG */, + 42 /* NSEC */, + 43 /* DNSKEY */, + 44 /* DHCID */, + 45 /* NSEC3 */, + 46 /* NSEC3PARAM */ +}; +#endif + +/* List of most frequently used RR types in ascending order. */ +extern const unsigned short int rrmuiterlist[NRRMU]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const unsigned short int rrmuiterlist[NRRMU] = { + T_A, + T_NS, + T_CNAME, + T_SOA, + T_PTR, + T_MX, + T_TXT, + T_AAAA +}; +#endif + +/* List of the cache-able RR types in ascending order. */ +extern const unsigned short int rrcachiterlist[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS +const unsigned short int rrcachiterlist[NRRTOT] = { + T_A, + T_NS, + T_MD, + T_MF, + T_CNAME, + T_SOA, + T_MB, + T_MG, + T_MR, + T_NULL, + T_WKS, + T_PTR, + T_HINFO, + T_MINFO, + T_MX, + T_TXT, + T_RP, + T_AFSDB, + T_X25, + T_ISDN, + T_RT, + T_NSAP, + T_NSAP_PTR, + T_SIG, + T_KEY, + T_PX, + T_GPOS, + T_AAAA, + T_LOC, + T_NXT, + T_EID, + T_NIMLOC, + T_SRV, + T_ATMA, + T_NAPTR, + T_KX, + T_CERT, + T_APL, + T_DS, + T_SSHFP, + T_IPSECKEY, + T_RRSIG, + T_NSEC, + T_DNSKEY, + T_DHCID, + T_NSEC3, + T_NSEC3PARAM +}; +#endif + +/* Optimized getrrset macros for fixed rr types. */ +#define getrrset_A(cent) GET_RRSMU(cent,0) +#define getrrset_NS(cent) GET_RRSMU(cent,1) +#define getrrset_MD(cent) GET_RRSEXT(cent,0) +#define getrrset_MF(cent) GET_RRSEXT(cent,1) +#define getrrset_CNAME(cent) GET_RRSMU(cent,2) +#define getrrset_SOA(cent) GET_RRSMU(cent,3) +#define getrrset_MB(cent) GET_RRSEXT(cent,2) +#define getrrset_MG(cent) GET_RRSEXT(cent,3) +#define getrrset_MR(cent) GET_RRSEXT(cent,4) +#define getrrset_NULL(cent) GET_RRSEXT(cent,5) +#define getrrset_WKS(cent) GET_RRSEXT(cent,6) +#define getrrset_PTR(cent) GET_RRSMU(cent,4) +#define getrrset_HINFO(cent) GET_RRSEXT(cent,7) +#define getrrset_MINFO(cent) GET_RRSEXT(cent,8) +#define getrrset_MX(cent) GET_RRSMU(cent,5) +#define getrrset_TXT(cent) GET_RRSMU(cent,6) +#define getrrset_RP(cent) GET_RRSEXT(cent,9) +#define getrrset_AFSDB(cent) GET_RRSEXT(cent,10) +#define getrrset_X25(cent) GET_RRSEXT(cent,11) +#define getrrset_ISDN(cent) GET_RRSEXT(cent,12) +#define getrrset_RT(cent) GET_RRSEXT(cent,13) +#define getrrset_NSAP(cent) GET_RRSEXT(cent,14) +#define getrrset_NSAP_PTR(cent) GET_RRSEXT(cent,15) +#define getrrset_SIG(cent) GET_RRSEXT(cent,16) +#define getrrset_KEY(cent) GET_RRSEXT(cent,17) +#define getrrset_PX(cent) GET_RRSEXT(cent,18) +#define getrrset_GPOS(cent) GET_RRSEXT(cent,19) +#define getrrset_AAAA(cent) GET_RRSMU(cent,7) +#define getrrset_LOC(cent) GET_RRSEXT(cent,20) +#define getrrset_NXT(cent) GET_RRSEXT(cent,21) +#define getrrset_EID(cent) GET_RRSEXT(cent,22) +#define getrrset_NIMLOC(cent) GET_RRSEXT(cent,23) +#define getrrset_SRV(cent) GET_RRSEXT(cent,24) +#define getrrset_ATMA(cent) GET_RRSEXT(cent,25) +#define getrrset_NAPTR(cent) GET_RRSEXT(cent,26) +#define getrrset_KX(cent) GET_RRSEXT(cent,27) +#define getrrset_CERT(cent) GET_RRSEXT(cent,28) +#define getrrset_APL(cent) GET_RRSEXT(cent,29) +#define getrrset_DS(cent) GET_RRSEXT(cent,30) +#define getrrset_SSHFP(cent) GET_RRSEXT(cent,31) +#define getrrset_IPSECKEY(cent) GET_RRSEXT(cent,32) +#define getrrset_RRSIG(cent) GET_RRSEXT(cent,33) +#define getrrset_NSEC(cent) GET_RRSEXT(cent,34) +#define getrrset_DNSKEY(cent) GET_RRSEXT(cent,35) +#define getrrset_DHCID(cent) GET_RRSEXT(cent,36) +#define getrrset_NSEC3(cent) GET_RRSEXT(cent,37) +#define getrrset_NSEC3PARAM(cent) GET_RRSEXT(cent,38) + +/* have_rr macros for fixed rr types. */ +#define have_rr_A(cent) HAVE_RRMU(cent,0) +#define have_rr_NS(cent) HAVE_RRMU(cent,1) +#define have_rr_MD(cent) HAVE_RREXT(cent,0) +#define have_rr_MF(cent) HAVE_RREXT(cent,1) +#define have_rr_CNAME(cent) HAVE_RRMU(cent,2) +#define have_rr_SOA(cent) HAVE_RRMU(cent,3) +#define have_rr_MB(cent) HAVE_RREXT(cent,2) +#define have_rr_MG(cent) HAVE_RREXT(cent,3) +#define have_rr_MR(cent) HAVE_RREXT(cent,4) +#define have_rr_NULL(cent) HAVE_RREXT(cent,5) +#define have_rr_WKS(cent) HAVE_RREXT(cent,6) +#define have_rr_PTR(cent) HAVE_RRMU(cent,4) +#define have_rr_HINFO(cent) HAVE_RREXT(cent,7) +#define have_rr_MINFO(cent) HAVE_RREXT(cent,8) +#define have_rr_MX(cent) HAVE_RRMU(cent,5) +#define have_rr_TXT(cent) HAVE_RRMU(cent,6) +#define have_rr_RP(cent) HAVE_RREXT(cent,9) +#define have_rr_AFSDB(cent) HAVE_RREXT(cent,10) +#define have_rr_X25(cent) HAVE_RREXT(cent,11) +#define have_rr_ISDN(cent) HAVE_RREXT(cent,12) +#define have_rr_RT(cent) HAVE_RREXT(cent,13) +#define have_rr_NSAP(cent) HAVE_RREXT(cent,14) +#define have_rr_NSAP_PTR(cent) HAVE_RREXT(cent,15) +#define have_rr_SIG(cent) HAVE_RREXT(cent,16) +#define have_rr_KEY(cent) HAVE_RREXT(cent,17) +#define have_rr_PX(cent) HAVE_RREXT(cent,18) +#define have_rr_GPOS(cent) HAVE_RREXT(cent,19) +#define have_rr_AAAA(cent) HAVE_RRMU(cent,7) +#define have_rr_LOC(cent) HAVE_RREXT(cent,20) +#define have_rr_NXT(cent) HAVE_RREXT(cent,21) +#define have_rr_EID(cent) HAVE_RREXT(cent,22) +#define have_rr_NIMLOC(cent) HAVE_RREXT(cent,23) +#define have_rr_SRV(cent) HAVE_RREXT(cent,24) +#define have_rr_ATMA(cent) HAVE_RREXT(cent,25) +#define have_rr_NAPTR(cent) HAVE_RREXT(cent,26) +#define have_rr_KX(cent) HAVE_RREXT(cent,27) +#define have_rr_CERT(cent) HAVE_RREXT(cent,28) +#define have_rr_A6(cent) 0 +#define have_rr_DNAME(cent) 0 +#define have_rr_SINK(cent) 0 +#define have_rr_OPT(cent) 0 +#define have_rr_APL(cent) HAVE_RREXT(cent,29) +#define have_rr_DS(cent) HAVE_RREXT(cent,30) +#define have_rr_SSHFP(cent) HAVE_RREXT(cent,31) +#define have_rr_IPSECKEY(cent) HAVE_RREXT(cent,32) +#define have_rr_RRSIG(cent) HAVE_RREXT(cent,33) +#define have_rr_NSEC(cent) HAVE_RREXT(cent,34) +#define have_rr_DNSKEY(cent) HAVE_RREXT(cent,35) +#define have_rr_DHCID(cent) HAVE_RREXT(cent,36) +#define have_rr_NSEC3(cent) HAVE_RREXT(cent,37) +#define have_rr_NSEC3PARAM(cent) HAVE_RREXT(cent,38) + +/* These macros specify which RR types are cached by pdnsd. */ +#define IS_CACHED_A 1 +#define IS_CACHED_NS 1 +#define IS_CACHED_MD 1 +#define IS_CACHED_MF 1 +#define IS_CACHED_CNAME 1 +#define IS_CACHED_SOA 1 +#define IS_CACHED_MB 1 +#define IS_CACHED_MG 1 +#define IS_CACHED_MR 1 +#define IS_CACHED_NULL 1 +#define IS_CACHED_WKS 1 +#define IS_CACHED_PTR 1 +#define IS_CACHED_HINFO 1 +#define IS_CACHED_MINFO 1 +#define IS_CACHED_MX 1 +#define IS_CACHED_TXT 1 +#define IS_CACHED_RP 1 +#define IS_CACHED_AFSDB 1 +#define IS_CACHED_X25 1 +#define IS_CACHED_ISDN 1 +#define IS_CACHED_RT 1 +#define IS_CACHED_NSAP 1 +#define IS_CACHED_NSAP_PTR 1 +#define IS_CACHED_SIG 1 +#define IS_CACHED_KEY 1 +#define IS_CACHED_PX 1 +#define IS_CACHED_GPOS 1 +#define IS_CACHED_AAAA 1 +#define IS_CACHED_LOC 1 +#define IS_CACHED_NXT 1 +#define IS_CACHED_EID 1 +#define IS_CACHED_NIMLOC 1 +#define IS_CACHED_SRV 1 +#define IS_CACHED_ATMA 1 +#define IS_CACHED_NAPTR 1 +#define IS_CACHED_KX 1 +#define IS_CACHED_CERT 1 +#define IS_CACHED_APL 1 +#define IS_CACHED_DS 1 +#define IS_CACHED_SSHFP 1 +#define IS_CACHED_IPSECKEY 1 +#define IS_CACHED_RRSIG 1 +#define IS_CACHED_NSEC 1 +#define IS_CACHED_DNSKEY 1 +#define IS_CACHED_DHCID 1 +#define IS_CACHED_NSEC3 1 +#define IS_CACHED_NSEC3PARAM 1 + +/* Array indices for most frequently used rr types. */ +#define RRMUINDEX_A 0 +#define RRMUINDEX_NS 1 +#define RRMUINDEX_CNAME 2 +#define RRMUINDEX_SOA 3 +#define RRMUINDEX_PTR 4 +#define RRMUINDEX_MX 5 +#define RRMUINDEX_TXT 6 +#define RRMUINDEX_AAAA 7 + +/* Table of rr names. */ +extern const char *const rrnames[T_NUM]; +#if DEFINE_RR_TYPE_ARRAYS +const char *const rrnames[T_NUM] = { + "A", + "NS", + "MD", + "MF", + "CNAME", + "SOA", + "MB", + "MG", + "MR", + "NULL", + "WKS", + "PTR", + "HINFO", + "MINFO", + "MX", + "TXT", + "RP", + "AFSDB", + "X25", + "ISDN", + "RT", + "NSAP", + "NSAP_PTR", + "SIG", + "KEY", + "PX", + "GPOS", + "AAAA", + "LOC", + "NXT", + "EID", + "NIMLOC", + "SRV", + "ATMA", + "NAPTR", + "KX", + "CERT", + "A6", + "DNAME", + "SINK", + "OPT", + "APL", + "DS", + "SSHFP", + "IPSECKEY", + "RRSIG", + "NSEC", + "DNSKEY", + "DHCID", + "NSEC3", + "NSEC3PARAM" +}; +#endif + +/* Structure for rr information */ +struct rr_infos { + unsigned short class; /* class (values see below) */ + unsigned short excludes; /* relations to other classes. + Mutual exclusion is marked by or'ing the + respective RRCL value in this field. + Exclusions should be symmetric. */ +}; + +/* Class values */ +#define RRCL_ALIAS 1 /* for CNAMES, conflicts with RRCL_RECORD */ +#define RRCL_RECORD 2 /* normal direct record */ +#define RRCL_IDEM 4 /* types that conflict with no others (MX, CNAME, ...) */ +#define RRCL_PTR 8 /* PTR */ + +/* Standard excludes for the classes */ +#define RRX_ALIAS (RRCL_RECORD|RRCL_PTR) +#define RRX_RECORD (RRCL_ALIAS|RRCL_PTR) +#define RRX_IDEM 0 +#define RRX_PTR (RRCL_ALIAS|RRCL_RECORD) + +/* There could be a separate table detailing the relationship of types, but this + * is slightly more flexible, as it allows a finer granularity of exclusion. Also, + * Membership in multiple classes could be added. + * Index by internally used RR-set indices, not RR type values! + */ +extern const struct rr_infos rr_info[NRRTOT]; +#if DEFINE_RR_TYPE_ARRAYS && !defined(CLIENT_ONLY) +const struct rr_infos rr_info[NRRTOT] = { + {RRCL_RECORD, RRX_RECORD} /* A */, + {RRCL_IDEM, RRX_IDEM} /* NS */, + {RRCL_ALIAS, RRX_ALIAS} /* CNAME */, + {RRCL_IDEM, RRX_IDEM} /* SOA */, + {RRCL_PTR, RRX_PTR} /* PTR */, + {RRCL_IDEM, RRX_IDEM} /* MX */, + {RRCL_IDEM, RRX_IDEM} /* TXT */, + {RRCL_RECORD, RRX_RECORD} /* AAAA */, + {RRCL_IDEM, RRX_IDEM} /* MD */, + {RRCL_IDEM, RRX_IDEM} /* MF */, + {RRCL_IDEM, RRX_IDEM} /* MB */, + {RRCL_IDEM, RRX_IDEM} /* MG */, + {RRCL_IDEM, RRX_IDEM} /* MR */, + {RRCL_IDEM, RRX_IDEM} /* NULL */, + {RRCL_RECORD, RRX_RECORD} /* WKS */, + {RRCL_RECORD, RRX_RECORD} /* HINFO */, + {RRCL_IDEM, RRX_IDEM} /* MINFO */, + {RRCL_RECORD, RRX_RECORD} /* RP */, + {RRCL_RECORD, RRX_RECORD} /* AFSDB */, + {RRCL_RECORD, RRX_RECORD} /* X25 */, + {RRCL_RECORD, RRX_RECORD} /* ISDN */, + {RRCL_RECORD, RRX_RECORD} /* RT */, + {RRCL_RECORD, RRX_RECORD} /* NSAP */, + {RRCL_PTR, RRX_PTR} /* NSAP_PTR */, + {RRCL_IDEM, RRX_IDEM} /* SIG */, + {RRCL_IDEM, RRX_IDEM} /* KEY */, + {RRCL_IDEM, RRX_IDEM} /* PX */, + {RRCL_RECORD, RRX_RECORD} /* GPOS */, + {RRCL_RECORD, RRX_RECORD} /* LOC */, + {RRCL_IDEM, RRX_IDEM} /* NXT */, + {RRCL_RECORD, RRX_RECORD} /* EID */, + {RRCL_RECORD, RRX_RECORD} /* NIMLOC */, + {RRCL_RECORD, RRX_RECORD} /* SRV */, + {RRCL_RECORD, RRX_RECORD} /* ATMA */, + {RRCL_RECORD, RRX_RECORD} /* NAPTR */, + {RRCL_RECORD, RRX_RECORD} /* KX */, + {RRCL_RECORD, RRX_RECORD} /* CERT */, + {RRCL_IDEM, RRX_IDEM} /* APL */, + {RRCL_IDEM, RRX_IDEM} /* DS */, + {RRCL_IDEM, RRX_IDEM} /* SSHFP */, + {RRCL_IDEM, RRX_IDEM} /* IPSECKEY */, + {RRCL_IDEM, RRX_IDEM} /* RRSIG */, + {RRCL_IDEM, RRX_IDEM} /* NSEC */, + {RRCL_IDEM, RRX_IDEM} /* DNSKEY */, + {RRCL_IDEM, RRX_IDEM} /* DHCID */, + {RRCL_IDEM, RRX_IDEM} /* NSEC3 */, + {RRCL_IDEM, RRX_IDEM} /* NSEC3PARAM */ +}; +#endif + +int rr_tp_byname(char *name); +const char *loc2str(const void *binary, char *ascii, size_t asclen); + +#endif diff --git a/service/src/main/jni/pdnsd/src/rr_types.in b/service/src/main/jni/pdnsd/src/rr_types.in new file mode 100644 index 0000000..5ebd2f3 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/rr_types.in @@ -0,0 +1,99 @@ +# This file is part of the pdnsd package. + +# This file contains information about the RR types implemented in pdnsd +# and is used for generating rr_types.h. +# It was derived from the following source: http://www.bind9.net/dns-parameters +# +# After making modifications to this file the file rr_types.h should be regenerated! +# +# Info about the format of this file: +# Blank lines and lines starting with '#' are ignored, all other lines +# are assumed to define an RR type. Lines starting with '+' define most +# frequently used types. An RR type preceded by a '-' will not be cached +# by pdnsd. The next two fields are interpreted as the name and the value +# of the RR type, resp. A subsequent word in parenthesis will be interpreted a +# class name (used for conflict resolution). Remaining fields are ignored. +# +# Adding or removing an initial '+' can be done safely without requiring +# changes to the source code (other than regenerating rr_types.h). +# +# If you are sure that you will never use certain RR types you can disable +# caching for them and make pdnsd slightly more efficient by placing a +# '-' sign in front of the lines that define those types. +# For a list of obsolete RR types see e.g. +# http://en.wikipedia.org/wiki/List_of_DNS_record_types . +# Note that some RR types are essential for pdnsd; these are currently: +# A, NS, CNAME, SOA, PTR, MX and (if you want IPv6 support) AAAA. +# Disabling caching for these types will cause pdnsd to fail to compile +# or cause a fatal run-time error. +# +# Removing a '-' sign to enable caching can be risky if the support in +# the pdnsd code is missing or inadequate, so only do this if you really know +# what you are doing. SPF records are supported, however, so it should be safe +# to enable caching for them. + +# RR TYPE Value (class) and meaning Reference +# ----------- --------------------------------------------- --------- ++ A 1 (RECORD) a host address [RFC 1035] ++ NS 2 an authoritative name server [RFC 1035] + MD 3 a mail destination (Obsolete - use MX) [RFC 1035] + MF 4 a mail forwarder (Obsolete - use MX) [RFC 1035] ++ CNAME 5 (ALIAS) the canonical name for an alias [RFC 1035] ++ SOA 6 marks the start of a zone of authority [RFC 1035] + MB 7 a mailbox domain name (EXPERIMENTAL) [RFC 1035] + MG 8 a mail group member (EXPERIMENTAL) [RFC 1035] + MR 9 a mail rename domain name (EXPERIMENTAL) [RFC 1035] + NULL 10 a null RR (EXPERIMENTAL) [RFC 1035] + WKS 11 (RECORD) a well known service description [RFC 1035] ++ PTR 12 (PTR) a domain name pointer [RFC 1035] + HINFO 13 (RECORD) host information [RFC 1035] + MINFO 14 mailbox or mail list information [RFC 1035] ++ MX 15 mail exchange [RFC 1035] ++ TXT 16 text strings [RFC 1035] + RP 17 (RECORD) for Responsible Person [RFC 1183] + AFSDB 18 (RECORD) for AFS Data Base location [RFC 1183][RFC 5864] + X25 19 (RECORD) for X.25 PSDN address [RFC 1183] + ISDN 20 (RECORD) for ISDN address [RFC 1183] + RT 21 (RECORD) for Route Through [RFC 1183] + NSAP 22 (RECORD) for NSAP address, NSAP style A record [RFC 1706] + NSAP-PTR 23 (PTR) for domain name pointer, NSAP style [RFC 1348] + SIG 24 for security signature [RFC 4034][RFC 3755][RFC 2535] + KEY 25 for security key [RFC 4034][RFC 3755][RFC 2535] + PX 26 X.400 mail mapping information [RFC 2163] + GPOS 27 (RECORD) Geographical Position [RFC 1712] ++ AAAA 28 (RECORD) IP6 Address [RFC 3596] + LOC 29 (RECORD) Location Information [RFC 1876] + NXT 30 Next Domain - OBSOLETE [RFC 3755][RFC 2535] + EID 31 (RECORD) Endpoint Identifier [Patton] + NIMLOC 32 (RECORD) Nimrod Locator [Patton] + SRV 33 (RECORD) Server Selection [RFC 2782] + ATMA 34 (RECORD) ATM Address [ATMDOC] + NAPTR 35 (RECORD) Naming Authority Pointer [RFC 2915][RFC 2168][RFC 3403] + KX 36 (RECORD) Key Exchanger [RFC 2230] + CERT 37 (RECORD) CERT [RFC 4398] +- A6 38 A6 (Experimental) [RFC 3226][RFC 2874] +- DNAME 39 (ALIAS) DNAME [RFC 2672] +- SINK 40 SINK [Eastlake] +- OPT 41 OPT [RFC 2671] + APL 42 APL [RFC 3123] + DS 43 Delegation Signer [RFC 4034][RFC 3658] + SSHFP 44 SSH Key Fingerprint [RFC 4255] + IPSECKEY 45 IPSECKEY [RFC 4025] + RRSIG 46 RRSIG [RFC 4034][RFC 3755] + NSEC 47 NSEC [RFC 4034][RFC 3755] + DNSKEY 48 DNSKEY [RFC 4034][RFC 3755] + DHCID 49 DHCID [RFC 4701] + NSEC3 50 NSEC3 [RFC 5155] + NSEC3PARAM 51 NSEC3PARAM [RFC 5155] +# Unassigned 52-54 +- HIP 55 Host Identity Protocol [RFC 5205] +- NINFO 56 NINFO [Reid] +- RKEY 57 RKEY [Reid] +- TALINK 58 Trust Anchor LINK [Wijngaards] +# Unassigned 59-98 +- SPF 99 Sender Policy Framework [RFC 4408] +- UINFO 100 [IANA-Reserved] +- UID 101 [IANA-Reserved] +- GID 102 [IANA-Reserved] +- UNSPEC 103 [IANA-Reserved] +# Unassigned 104-248 diff --git a/service/src/main/jni/pdnsd/src/servers.c b/service/src/main/jni/pdnsd/src/servers.c new file mode 100644 index 0000000..8549865 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/servers.c @@ -0,0 +1,856 @@ +/* servers.c - manage a set of dns servers + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2005, 2007, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread.h" +#include "error.h" +#include "servers.h" +#include "conff.h" +#include "consts.h" +#include "icmp.h" +#include "netdev.h" +#include "helpers.h" +#include "dns_query.h" + + +/* + * We may be a little over-strict with locks here. Never mind... + * Also, there may be some code-redundancy regarding uptests. It saves some locks, though. + */ + +static pthread_mutex_t servers_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t server_data_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t server_test_cond = PTHREAD_COND_INITIALIZER; +static int server_data_users = 0, server_status_ping = 0; +/* Used to notify the server status thread that it should discontinue uptests. */ +volatile int signal_interrupt=0; +#define statusintsig SIGHUP + +static short retest_flag=0; + +static char schm[32]; + +static void sigint_handler(int signum); + +/* + * Execute an individual uptest. Call with locks applied + */ +static int uptest (servparm_t *serv, int j) +{ + int ret=0, count_running_ping=0; + pdnsd_a *s_addr= PDNSD_A2_TO_A(&DA_INDEX(serv->atup_a,j).a); + + DEBUG_PDNSDA_MSG("performing uptest (type=%s) for %s\n",const_name(serv->uptest),PDNSDA2STR(s_addr)); + + /* Unlock the mutex because some of the tests may take a while. */ + ++server_data_users; + if((serv->uptest==C_PING || serv->uptest==C_QUERY) && pthread_equal(pthread_self(),servstat_thrid)) { + /* Inform other threads that a ping is in progress. */ + count_running_ping=1; + ++server_status_ping; + } + pthread_mutex_unlock(&servers_lock); + + switch (serv->uptest) { + case C_NONE: + /* Don't change */ + ret=DA_INDEX(serv->atup_a,j).is_up; + break; + case C_PING: + ret=ping(is_inaddr_any(&serv->ping_a) ? s_addr : &serv->ping_a, serv->ping_timeout,PINGREPEAT)!=-1; + break; + case C_IF: + case C_DEV: + case C_DIALD: + ret=if_up(serv->interface); +#if (TARGET==TARGET_LINUX) + if (ret!=0) { + if(serv->uptest==C_DEV) + ret=dev_up(serv->interface,serv->device); + else if (serv->uptest==C_DIALD) + ret=dev_up("diald",serv->device); + } +#endif + break; + case C_EXEC: { + pid_t pid; + + if ((pid=fork())==-1) { + DEBUG_MSG("Could not fork to perform exec uptest: %s\n",strerror(errno)); + break; + } else if (pid==0) { /* child */ + /* + * If we ran as setuid or setgid, do not inherit this to the + * command. This is just a last guard. Running pdnsd as setuid() + * or setgid() is a no-no. + */ + if (setgid(getgid()) == -1 || setuid(getuid()) == -1) { + log_error("Could not reset uid or gid: %s",strerror(errno)); + _exit(1); + } + /* Try to setuid() to a different user as specified. Good when you + don't want the test command to run as root */ + if (!run_as(serv->uptest_usr)) { + _exit(1); + } + { + struct rlimit rl; int i; + /* + * Mark all open fd's FD_CLOEXEC for paranoia reasons. + */ + if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { + log_error("getrlimit() failed: %s",strerror(errno)); + _exit(1); + } + for (i = 0; i < rl.rlim_max; i++) { + if (fcntl(i, F_SETFD, FD_CLOEXEC) == -1 && errno != EBADF) { + log_error("fcntl(F_SETFD) failed: %s",strerror(errno)); + _exit(1); + } + } + } + execl("/bin/sh", "uptest_sh","-c",serv->uptest_cmd,(char *)NULL); + _exit(1); /* failed execl */ + } else { /* parent */ + int status; + pid_t wpid = waitpid(pid,&status,0); + if (wpid==pid) { + if(WIFEXITED(status)) { + int exitstatus=WEXITSTATUS(status); + DEBUG_MSG("uptest command \"%s\" exited with status %d\n", + serv->uptest_cmd, exitstatus); + ret=(exitstatus==0); + } +#if DEBUG>0 + else if(WIFSIGNALED(status)) { + DEBUG_MSG("uptest command \"%s\" was terminated by signal %d\n", + serv->uptest_cmd, WTERMSIG(status)); + } + else { + DEBUG_MSG("status of uptest command \"%s\" is of unkown type (0x%x)\n", + serv->uptest_cmd, status); + } +#endif + } +#if DEBUG>0 + else if (wpid==-1) { + DEBUG_MSG("Error while waiting for uptest command \"%s\" to terminate: " + "waitpid for pid %d failed: %s\n", + serv->uptest_cmd, pid, strerror(errno)); + } + else { + DEBUG_MSG("Error while waiting for uptest command \"%s\" to terminate: " + "waitpid returned %d, expected pid %d\n", + serv->uptest_cmd, wpid, pid); + } +#endif + } + } + break; + case C_QUERY: + ret=query_uptest(s_addr, serv->port, serv->query_test_name, + serv->timeout>=global.timeout?serv->timeout:global.timeout, + PINGREPEAT); + } /* end of switch */ + + pthread_mutex_lock(&servers_lock); + if(count_running_ping) + --server_status_ping; + PDNSD_ASSERT(server_data_users>0, "server_data_users non-positive before attempt to decrement it"); + if (--server_data_users==0) pthread_cond_broadcast(&server_data_cond); + + DEBUG_PDNSDA_MSG("result of uptest for %s: %s\n", + PDNSDA2STR(s_addr), + ret?"OK":"failed"); + return ret; +} + +static int scheme_ok(servparm_t *serv) +{ + if (serv->scheme[0]) { + if (!schm[0]) { + ssize_t nschm; + int sc = open(global.scheme_file, O_RDONLY); + char *s; + if (sc<0) + return 0; + nschm = read(sc, schm, sizeof(schm)-1); + close(sc); + if (nschm < 0) + return 0; + schm[nschm] = '\0'; + s = strchr(schm, '\n'); + if (s) + *s='\0'; + } + if (fnmatch(serv->scheme, schm, 0)) + return 0; + } + return 1; +} + +/* Internal server test. Call with locks applied. + May test a single server ip or several collectively. + */ +static void retest(int i, int j) +{ + time_t s_ts; + servparm_t *srv=&DA_INDEX(servers,i); + int nsrvs=DA_NEL(srv->atup_a); + + if(!nsrvs) return; + if(j>=0) { + if(jatup_a,j); + at->is_up=0; + at->i_ts=s_ts; + } + } + else if(srv->uptest==C_NONE) { + s_ts=time(NULL); + + for(;jatup_a,j).i_ts=s_ts; + } + } + else if(srv->uptest==C_QUERY || (srv->uptest==C_PING && is_inaddr_any(&srv->ping_a))) { /* test each ip address separately */ + for(;jatup_a,j); + s_ts=time(NULL); + at->is_up=uptest(srv,j); + if(signal_interrupt) + break; + at->i_ts=s_ts; + } + } + else { /* test ip addresses collectively */ + int res; + + s_ts=time(NULL); + res=uptest(srv,j); + for(;jatup_a,j); + at->is_up=res; + if(signal_interrupt && srv->uptest==C_PING) + continue; + at->i_ts=s_ts; + } + } +} + + +/* This is called by the server status thread to discover the addresses of root servers. + Call with server_lock applied. +*/ +static addr2_array resolv_rootserver_addrs(atup_array a, int port, char edns_query, time_t timeout) +{ + addr2_array retval=NULL; + + /* Unlock the mutex because this may take a while. */ + ++server_data_users; + pthread_mutex_unlock(&servers_lock); + + retval= dns_rootserver_resolv(a,port,edns_query,timeout); + + pthread_mutex_lock(&servers_lock); + PDNSD_ASSERT(server_data_users>0, "server_data_users non-positive before attempt to decrement it"); + if (--server_data_users==0) pthread_cond_broadcast(&server_data_cond); + + return retval; +} + +/* + * Refresh the server status by pinging or testing the interface in the given interval. + * Note that you may get inaccuracies in the dimension of the ping timeout or the runtime + * of your uptest command if you have uptest=ping or uptest=exec for at least one server. + * This happens when all the uptests for the first n servers take more time than the inteval + * of n+1 (or 0 when n+1>servnum). I do not think that these delays are critical, so I did + * not to anything about that (because that may also be costly). + */ +void *servstat_thread(void *p) +{ + struct sigaction action; + int keep_testing; + + /* (void)p; */ /* To inhibit "unused variable" warning */ + + THREAD_SIGINIT; + + pthread_mutex_lock(&servers_lock); + /* servstat_thrid=pthread_self(); */ + + signal_interrupt=0; + action.sa_handler = sigint_handler; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + if(sigaction(statusintsig, &action, NULL) == 0) { + sigset_t smask; + sigemptyset(&smask); + sigaddset(&smask, statusintsig); + pthread_sigmask(SIG_UNBLOCK,&smask,NULL); + } + else { + log_warn("Cannot install signal handler for server status thread: %s\n",strerror(errno)); + } + + for(;;) { + do { + int i,n; + keep_testing=0; + retest_flag=0; + schm[0] = '\0'; + n=DA_NEL(servers); + for (i=0;irootserver==2) { + /* First get addresses of root servers. */ + addr2_array adrs; + int l, one_up=0; + + if(!scheme_ok(sp)) { + time_t now=time(NULL); + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).i_ts=now; + } else if(sp->uptest==C_PING || sp->uptest==C_QUERY) { + /* Skip ping or query tests until after discovery. */ + if(sp->interval>0) + one_up= DA_NEL(sp->atup_a); + else { + time_t now=time(NULL); + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j); + if(at->is_up || at->i_ts==0) + one_up=1; + at->i_ts=now; + } + } + } + else { + retest(i,-1); + + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).is_up) { + one_up=1; + break; + } + } + } + + if(!one_up) { + if (needs_intermittent_testing(sp)) keep_testing=1; + continue; + } + + DEBUG_MSG("Attempting to discover root servers for server section #%d.\n",i); + adrs=resolv_rootserver_addrs(sp->atup_a,sp->port,sp->edns_query,sp->timeout); + l= DA_NEL(adrs); + if(l>0) { + struct timeval now; + struct timespec timeout; + atup_array ata; + DEBUG_MSG("Filling server section #%d with %d root server addresses.\n",i,l); + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + 60; /* time out after 60 seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + while (server_data_users>0) { + if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) { + DEBUG_MSG("Timed out while waiting for exclusive access to server data" + " to set root server addresses of server section #%d\n",i); + da_free(adrs); + keep_testing=1; + continue; + } + } + ata = DA_CREATE(atup_array, l); + if(!ata) { + log_warn("Out of memory in servstat_thread() while discovering root servers."); + da_free(adrs); + keep_testing=1; + continue; + } + for(j=0; ja = DA_INDEX(adrs,j); + at->is_up=sp->preset; + at->i_ts= sp->interval<0 ? time(NULL): 0; + } + da_free(sp->atup_a); + sp->atup_a=ata; + da_free(adrs); + /* Successfully set IP addresses for this server section. */ + sp->rootserver=1; + } + else { + DEBUG_MSG("Failed to discover root servers in servstat_thread() (server section #%d).\n",i); + if(adrs) da_free(adrs); + if(DA_NEL(sp->atup_a)) keep_testing=1; + continue; + } + } + + if (needs_testing(sp)) keep_testing=1; + m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).i_ts) + goto individual_tests; + /* Test collectively */ + if(!signal_interrupt) retest(i,-1); + continue; + + individual_tests: + for(j=0; !signal_interrupt && jatup_a,j).i_ts, now; + + if (ts==0 /* Always test servers with timestamp 0 */ || + (needs_intermittent_testing(sp) && + ((now=time(NULL))-ts>sp->interval || + ts>now /* kluge for clock skew */))) + { + retest(i,j); + } + } + } + } while(!signal_interrupt && retest_flag); + + signal_interrupt=0; + + /* Break the loop and exit the thread if it is no longer needed. */ + if(!keep_testing) break; + + { + struct timeval now; + struct timespec timeout; + time_t minwait; + int i,n,retval; + + gettimeofday(&now,NULL); + minwait=3600; /* Check at least once every hour. */ + n=DA_NEL(servers); + for (i=0;iatup_a); + for(j=0;jatup_a,j).i_ts; + if(ts==0) { + /* Test servers with timestamp 0 without delay */ + if(minwait > 0) minwait=0; + } + else if(needs_intermittent_testing(sp)) { + time_t wait= ts + sp->interval - now.tv_sec; + if(wait < minwait) minwait=wait; + } + } + } + timeout.tv_sec = now.tv_sec; + if(minwait>0) + timeout.tv_sec += minwait; + timeout.tv_nsec = now.tv_usec * 1000 + 500000000; /* wait at least half a second. */ + if(timeout.tv_nsec>=1000000000) { + timeout.tv_nsec -= 1000000000; + ++timeout.tv_sec; + } + /* While we wait for a server_test_cond condition or a timeout + the servers_lock mutex is unlocked, so other threads can access + server data + */ + retval=pthread_cond_timedwait(&server_test_cond, &servers_lock, &timeout); + DEBUG_MSG("Server status thread woke up (%s signal).\n", + retval==0?"test condition":retval==ETIMEDOUT?"timer":retval==EINTR?"interrupt":"error"); + } + } + + /* server status thread no longer needed. */ + servstat_thrid=main_thrid; + pthread_mutex_unlock(&servers_lock); + DEBUG_MSG("Server status thread exiting.\n"); + return NULL; +} + +/* + * Start the server status thread. + */ +int start_servstat_thread() +{ + pthread_t stt; + + int rv=pthread_create(&stt,&attr_detached,servstat_thread,NULL); + if (rv) + log_warn("Failed to start server status thread: %s",strerror(rv)); + else { + servstat_thrid=stt; + log_info(2,"Server status thread started."); + } + return rv; +} + +/* + * This can be used to mark a server (or a list of nadr servers) up (up=1) or down (up=0), + * or to schedule an immediate retest (up=-1). + * We can't always use indices to identify a server, because we allow run-time + * configuration of server addresses, so the servers are identified by their IP addresses. + */ +void sched_server_test(pdnsd_a *sa, int nadr, int up) +{ + int k,signal_test; + + pthread_mutex_lock(&servers_lock); + + signal_test=0; + /* This obviously isn't very efficient, but nadr should be small + and anything else would introduce considerable overhead */ + for(k=0;katup_a); + for(j=0;jatup_a,j); + if(equiv_inaddr2(sak,&at->a)) { + if(up>=0) { + at->is_up=up; + at->i_ts=time(NULL); + DEBUG_PDNSDA_MSG("Marked server %s %s.\n",PDNSDA2STR(sak),up?"up":"down"); + } + else if(at->i_ts) { + /* A test may take a while, and we don't want to hold + up the calling thread. + Instead we set the timestamp to zero and signal + a condition which should wake up the server test thread. + */ + at->i_ts=0; + signal_test=1; + } + } + } + } + } + if(signal_test) pthread_cond_signal(&server_test_cond); + + pthread_mutex_unlock(&servers_lock); +} + +/* Mark a set of servers up or down or schedule uptests. + * If i>=0 only the server section with index i is scanned, + * if i<0 all sections are scanned. + * Only sections matching label are actually set. A NULL label matches + * any section. + * up=1 or up=0 means mark server up or down, up=-1 means retest. + * + * A non-zero return value indicates an error. + */ +int mark_servers(int i, char *label, int up) +{ + int retval=0,n,signal_test; + + pthread_mutex_lock(&servers_lock); + + signal_test=0; + n=DA_NEL(servers); + if(i>=0) { + /* just one section */ + if(ilabel && !strcmp(sp->label,label))) { + int j,m=DA_NEL(sp->atup_a); + + /* If a section with undiscovered root servers is marked up, signal a test. */ + if(m && sp->rootserver>1 && up>0) signal_test=1; + + for(j=0;jatup_a,j); + if(up>=0) { + at->is_up=up; + at->i_ts=time(NULL); + } + else if(at->i_ts) { + /* A test may take a while, and we don't want to hold + up the calling thread. + Instead we set the timestamp to zero and signal + a condition which should wake up the server test thread. + */ + at->i_ts=0; + signal_test=1; + } + } + } + } + if(signal_test) { + if(pthread_equal(servstat_thrid,main_thrid)) + retval=start_servstat_thread(); + else { + retest_flag=1; + retval=pthread_cond_signal(&server_test_cond); + } + } + + pthread_mutex_unlock(&servers_lock); + return retval; +} + +/* + * Test called by the dns query handlers to handle interval=onquery cases. + */ +void test_onquery() +{ + int i,n,signal_test; + + pthread_mutex_lock(&servers_lock); + schm[0] = '\0'; + signal_test=0; + n=DA_NEL(servers); + for (i=0;iinterval==-1) { + if(sp->rootserver<=1) + retest(i,-1); + else { + /* We leave root-server discovery to the server status thread */ + int j,m=DA_NEL(sp->atup_a); + for(j=0;jatup_a,j).i_ts=0; + signal_test=1; + } + } + } + + if(signal_test) { + int rv; + if(pthread_equal(servstat_thrid,main_thrid)) + start_servstat_thread(); + else { + retest_flag=1; + if((rv=pthread_cond_signal(&server_test_cond))) { + DEBUG_MSG("test_onquery(): couldn't signal server status thread: %s\n",strerror(rv)); + } + } + } + + pthread_mutex_unlock(&servers_lock); +} + +/* non-exclusive lock, for read only access to server data. */ +void lock_server_data() +{ + pthread_mutex_lock(&servers_lock); + ++server_data_users; + pthread_mutex_unlock(&servers_lock); +} + +void unlock_server_data() +{ + pthread_mutex_lock(&servers_lock); + PDNSD_ASSERT(server_data_users>0, "server_data_users non-positive before attempt to decrement it"); + if (--server_data_users==0) pthread_cond_broadcast(&server_data_cond); + pthread_mutex_unlock(&servers_lock); +} + +/* Try to obtain an exclusive lock, needed for modifying server data. + Return 1 on success, 0 on failure (time out after tm seconds). +*/ +int exclusive_lock_server_data(int tm) +{ + struct timeval now; + struct timespec timeout; + + pthread_mutex_lock(&servers_lock); + if(server_status_ping>0 && !pthread_equal(servstat_thrid,main_thrid)) { + int err; + /* Try to interrupt server status thread to prevent delays. */ + DEBUG_MSG("Sending server status thread an interrupt signal.\n"); + if((err=pthread_kill(servstat_thrid,statusintsig))) { + DEBUG_MSG("pthread_kill failed: %s\n",strerror(err)); + } + } + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + tm; /* time out after tm seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + while (server_data_users>0) { + if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) { + pthread_mutex_unlock(&servers_lock); + return 0; + } + } + return 1; +} +/* Call this to free the lock obtained with exclusive_lock_server_data(). + If retest is nonzero, the server-status thread is reactivated to check + which servers are up. This is useful in case the configuration has changed. +*/ +void exclusive_unlock_server_data(int retest) +{ + if(retest) { + if(pthread_equal(servstat_thrid,main_thrid)) + start_servstat_thread(); + else + pthread_cond_signal(&server_test_cond); + } + pthread_mutex_unlock(&servers_lock); +} + +/* + Change addresses of servers during runtime. + i is the number of the server section to change. + ar should point to an array of IP addresses (may be NULL). + up=1 or up=0 means mark server up or down afterwards, + up=-1 means retest. + + A non-zero return value indicates an error. +*/ +int change_servers(int i, addr_array ar, int up) +{ + int retval=0,j,change,signal_test; + int n; + servparm_t *sp; + + pthread_mutex_lock(&servers_lock); + + signal_test=0; + change=0; + n=DA_NEL(ar); + sp=&DA_INDEX(servers,i); + if(n != DA_NEL(sp->atup_a) || sp->rootserver>1) + change=1; + else { + int j; + for(j=0;jatup_a,j).a)) { + change=1; + break; + } + } + if(change) { + /* we need exclusive access to the server data to make the changes */ + struct timeval now; + struct timespec timeout; + atup_array ata; + + if(server_status_ping>0 && !pthread_equal(servstat_thrid,main_thrid)) { + int err; + /* Try to interrupt server status thread to prevent delays. */ + DEBUG_MSG("Sending server status thread an interrupt signal.\n"); + if((err=pthread_kill(servstat_thrid,statusintsig))) { + DEBUG_MSG("pthread_kill failed: %s\n",strerror(err)); + } + } + + DEBUG_MSG("Changing IPs of server section #%d\n",i); + gettimeofday(&now,NULL); + timeout.tv_sec = now.tv_sec + 60; /* time out after 60 seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + while (server_data_users>0) { + if(pthread_cond_timedwait(&server_data_cond, &servers_lock, &timeout) == ETIMEDOUT) { + retval=ETIMEDOUT; + goto unlock_mutex; + } + } + + ata= DA_CREATE(atup_array, n); + if(!ata) { + log_warn("Out of memory in change_servers()."); + retval=ENOMEM; + goto unlock_mutex; + } + da_free(sp->atup_a); + sp->atup_a=ata; + /* Stop trying to discover rootservers + if we set the addresses using this routine. */ + if(sp->rootserver>1) sp->rootserver=1; + } + + for(j=0; jatup_a,j); + if(change) { + SET_PDNSD_A2(&at->a, &DA_INDEX(ar,j)); + at->is_up=sp->preset; + } + if(up>=0) { + at->is_up=up; + at->i_ts=time(NULL); + } + else if(change || at->i_ts) { + /* A test may take a while, and we don't want to hold + up the calling thread. + Instead we set the timestamp to zero and signal + a condition which should wake up the server test thread. + */ + at->i_ts=0; + signal_test=1; + } + } + + if(signal_test) { + if(pthread_equal(servstat_thrid,main_thrid)) + retval=start_servstat_thread(); + else { + retest_flag=1; + retval=pthread_cond_signal(&server_test_cond); + } + } + + unlock_mutex: + pthread_mutex_unlock(&servers_lock); + return retval; +} + + +/* + The signal handler for the signal to tell the server status thread to discontinue testing. +*/ +static void sigint_handler(int signum) +{ + signal_interrupt=1; +} diff --git a/service/src/main/jni/pdnsd/src/servers.h b/service/src/main/jni/pdnsd/src/servers.h new file mode 100644 index 0000000..fd263c0 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/servers.h @@ -0,0 +1,68 @@ +/* servers.h - manage a set of dns servers + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _SERVERS_H_ +#define _SERVERS_H_ + +#include +#include "consts.h" + +/* Number of ping timeouts before we take a server offline. */ +#define PINGREPEAT 2 + +extern pthread_t servstat_thrid; +extern volatile int signal_interrupt; + + +int start_servstat_thread(void); +void sched_server_test(pdnsd_a *sa, int nadr, int up); +int mark_servers(int i, char* label, int up); +void test_onquery(void); +void lock_server_data(); +void unlock_server_data(); +int exclusive_lock_server_data(int tm); +void exclusive_unlock_server_data(int retest); +int change_servers(int i, addr_array ar, int up); + +inline static int needs_testing(servparm_t *sp) + __attribute__((always_inline)); +inline static int needs_testing(servparm_t *sp) +{ + return ((sp->interval>0 || sp->interval==-2) && (sp->uptest!=C_NONE || sp->scheme[0])); +} + +inline static int needs_intermittent_testing(servparm_t *sp) + __attribute__((always_inline)); +inline static int needs_intermittent_testing(servparm_t *sp) +{ + return (sp->interval>0 && (sp->uptest!=C_NONE || sp->scheme[0])); +} + +inline static int is_interrupted_servstat_thread() + __attribute__((always_inline)); +inline static int is_interrupted_servstat_thread() +{ + return (signal_interrupt && pthread_equal(pthread_self(),servstat_thrid)); +} + +#endif diff --git a/service/src/main/jni/pdnsd/src/sort_namevalues.pl b/service/src/main/jni/pdnsd/src/sort_namevalues.pl new file mode 100644 index 0000000..2014f49 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/sort_namevalues.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +use strict; + +my %dic; +my $maxkeylen=0; + +while(<>) { + if(/"(\w+)".*?(\w+)/) { + my $key=$1; my $val=$2; + if($dic{$key}) {die "The key \"$key\" does not have a unique value.\n"} + $dic{$key}=$val; + if(length($key)>$maxkeylen) {$maxkeylen=length($key)} + } + else {die "Can't find key-value pair in following line:\n$_\n"} +} + +my $linenr=0; +foreach my $key (sort(keys %dic)) { + if($linenr++) {print ",\n"} + printf("\t{%-*s%s}",$maxkeylen+4,"\"$key\",",$dic{$key}); +} +print "\n"; + +exit diff --git a/service/src/main/jni/pdnsd/src/status.c b/service/src/main/jni/pdnsd/src/status.c new file mode 100644 index 0000000..4240069 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/status.c @@ -0,0 +1,824 @@ +/* status.c - Allow control of a running server using a socket + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2011 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#ifdef HAVE_ALLOCA_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for offsetof */ +#include "ipvers.h" +#include "status.h" +#include "thread.h" +#include "cache.h" +#include "error.h" +#include "servers.h" +#include "dns_answer.h" +#include "helpers.h" +#include "conf-parser.h" + +#if !defined(HAVE_ALLOCA) && !defined(alloca) +#define alloca malloc +#endif + + +char *sock_path=NULL; +int stat_sock; + + +/* Print an error to the socket */ +static int print_serr(int rs, const char *msg) +{ + uint16_t cmd; + + DEBUG_MSG("Sending error message to control socket: '%s'\n",msg); + cmd=htons(1); + if(write(rs,&cmd,sizeof(cmd))!=sizeof(cmd) || + write_all(rs,msg,strlen(msg))<0) + { + DEBUG_MSG("Error writing to control socket: %s\n",strerror(errno)); + return 0; + } + return 1; +} + +/* Print a success code to the socket */ +static int print_succ(int rs) +{ + uint16_t cmd; + + cmd=htons(0); + if(write(rs,&cmd,sizeof(cmd))!=sizeof(cmd)) { + DEBUG_MSG("Error writing to control socket: %s\n" + "Failed to send success code.\n",strerror(errno)); + return 0; + } + return 1; +} + +/* Read a cmd short */ +static int read_short(int fh, uint16_t *res) +{ + uint16_t cmd; + + if (read(fh,&cmd,sizeof(cmd))!=sizeof(cmd)) { + /* print_serr(fh,"Bad arg."); */ + return 0; + } + *res= ntohs(cmd); + return 1; +} + +/* Read a cmd long */ +static int read_long(int fh, uint32_t *res) +{ + uint32_t cmd; + + if (read(fh,&cmd,sizeof(cmd))!=sizeof(cmd)) { + /* print_serr(fh,"Bad arg."); */ + return 0; + } + *res= ntohl(cmd); + return 1; +} + +/* Read a string preceded by a char count. + A buffer of the right size is allocated to hold the result. + A return value of 1 means success, + -1 means the result is undefined (*res is set to NULL), + 0 means read or allocation error. +*/ +static int read_allocstring(int fh, char **res, unsigned *len) +{ + uint16_t count; + char *buf; + unsigned int nread; + + if(!read_short(fh,&count)) return 0; + if(count==(uint16_t)(~0)) {*res=NULL; return -1;} + if(!(buf=malloc(count+1))) return 0; + nread=0; + while(nread=buflen) return 0; + nread=0; + while(nread=buflen) return 0; + buf[count]='.'; buf[count+1]=0; + } +#endif + return 1; +} + +static void *status_thread (void *p) +{ + THREAD_SIGINIT; + /* (void)p; */ /* To inhibit "unused variable" warning */ + + if (!global.strict_suid) { + if (!run_as(global.run_as)) { + pdnsd_exit(); + } + } + + if (listen(stat_sock,5)==-1) { + log_warn("Error: could not listen on socket: %s.\nStatus readback will be impossible",strerror(errno)); + goto exit_thread; + } + for(;;) { + struct sockaddr_un ra; + socklen_t res=sizeof(ra); + int rs; + if ((rs=accept(stat_sock,(struct sockaddr *)&ra,&res))!=-1) { + uint16_t cmd; + DEBUG_MSG("Status socket query pending.\n"); + if (read_short(rs,&cmd)) { + /* Check magic number in command */ + if((cmd & 0xff00) == CTL_CMDVERNR) { + const char *errmsg; + cmd &= 0xff; + switch(cmd) { + case CTL_STATS: { + struct utsname nm; + DEBUG_MSG("Received STATUS query.\n"); + if(!print_succ(rs)) + break; + uname(&nm); + if(fsprintf(rs,"pdnsd-%s running on %s.\n",VERSION,nm.nodename)<0 || + report_cache_stat(rs)<0 || + report_thread_stat(rs)<0 || + report_conf_stat(rs)<0) + { + DEBUG_MSG("Error writing to control socket: %s\n" + "Failed to send status report.\n",strerror(errno)); + } + } + break; + case CTL_SERVER: { + char *label,*dnsaddr; + int indx; + uint16_t cmd2; + DEBUG_MSG("Received SERVER command.\n"); + if (read_allocstring(rs,&label,NULL)<=0) { + print_serr(rs,"Error reading server label."); + break; + } + if (!read_short(rs,&cmd2)) { + print_serr(rs,"Missing up|down|retest."); + goto free_label_break; + } + if(!read_allocstring(rs, &dnsaddr,NULL)) { + print_serr(rs,"Error reading DNS addresses."); + goto free_label_break; + } + /* Note by Paul Rombouts: + We are about to access server configuration data. + Now that the configuration can be changed during run time, + we should be using locks before accessing server config data, even if it + is read-only access. + However, as long as this is the only thread that calls reload_config_file() + it should be OK to read the server config without locks, but it is + something to keep in mind. + */ + { + char *endptr; + indx=strtol(label,&endptr,0); + if(!*endptr) { + if (indx<0 || indx>=DA_NEL(servers)) { + print_serr(rs,"Server index out of range."); + goto free_dnsaddr_label_break; + } + } + else { + if (!strcmp(label, "all")) + indx=-2; /* all servers */ + else + indx=-1; /* compare names */ + } + } + if(cmd2==CTL_S_UP || cmd2==CTL_S_DOWN || cmd2==CTL_S_RETEST) { + if(!dnsaddr) { + if (indx==-1) { + int i; + for (i=0;idomain=malloc(sz))) { + print_serr(rs,"Out of memory."); + goto free_sla_names_break; + } + memcpy(sl->domain,rhn,sz); + sl->exact=0; + sl->rule=tp; + p = q+1; + } + } + if(empty_cache(sla)) + print_succ(rs); + else + print_serr(rs,"Could not lock the cache."); + free_sla_names_break: + free_slist_array(sla); + free_names_break: + free(names); + } + break; + case CTL_DUMP: { + int rv,exact=0; + unsigned char *nm=NULL; + char buf[DNSNAMEBUFSIZE]; + unsigned char rhn[DNSNAMEBUFSIZE]; + DEBUG_MSG("Received DUMP command.\n"); + if (!(rv=read_domain(rs,buf,sizeof(buf)))) { + print_serr(rs,"Bad domain name."); + break; + } + if(rv>0) { + int sz; + exact=1; nm= ucharp buf; sz=sizeof(buf); + if(buf[0]=='.' && buf[1]) { + exact=0; ++nm; --sz; + } + if ((errmsg=parsestr2rhn(nm,sz,rhn))!=NULL) + goto bad_domain_name; + nm=rhn; + } + if(!print_succ(rs)) + break; + if((rv=dump_cache(rs,nm,exact))<0 || + (!rv && fsprintf(rs,"Could not find %s%s in the cache.\n", + exact?"":nm?"any entries matching ":"any entries", + nm?buf:"")<0)) + { + DEBUG_MSG("Error writing to control socket: %s\n",strerror(errno)); + } + } + break; + incomplete_command: + print_serr(rs,"Malformed or incomplete command."); + break; + bad_arg: + print_serr(rs,"Bad arg."); + break; + bad_domain_name: + print_serr(rs,errmsg); + break; + bad_ttl: + print_serr(rs, "Bad TTL."); + break; + bad_flags: + print_serr(rs, "Bad cache flags."); + break; + out_of_memory: + print_serr(rs,"Out of memory."); + break; + default: + print_serr(rs,"Unknown command."); + } + } + else { + DEBUG_MSG("Incorrect magic number in status-socket command code: %02x\n",cmd>>8); + print_serr(rs,"Command code contains incompatible version number."); + } + } + else { + DEBUG_MSG("short status-socket query\n"); + print_serr(rs,"Command code missing or too short."); + } + close(rs); + usleep_r(100000); /* sleep some time. I do not want the query frequency to be too high. */ + } + else if (errno!=EINTR) { + log_warn("Failed to accept connection on status socket: %s. " + "Status readback will be impossible",strerror(errno)); + break; + } + } + + exit_thread: + stat_pipe=0; + close(stat_sock); + statsock_thrid=main_thrid; + + return NULL; +} + +/* + * Initialize the status socket + */ +void init_stat_sock() +{ + struct sockaddr_un *sa; + /* Should I include the terminating null byte in the calculation of the length parameter + for the socket address? The glibc info page "Details of Local Namespace" tells me I should not, + yet it is immediately followed by an example that contradicts that. + The SUN_LEN macro seems to be defined as + (offsetof(struct sockaddr_un, sun_path) + strlen(sa->sun_path)), + so I conclude it is not necessary to count the null byte, but it probably makes no + difference if you do. + */ + unsigned int sa_len = (offsetof(struct sockaddr_un, sun_path) + strlitlen("/pdnsd.status") + strlen(global.cache_dir)); + + sa=(struct sockaddr_un *)alloca(sa_len+1); + stpcpy(stpcpy(sa->sun_path,global.cache_dir),"/pdnsd.status"); + + if (unlink(sa->sun_path)!=0 && errno!=ENOENT) { /* Delete the socket */ + log_warn("Failed to unlink %s: %s.\nStatus readback will be disabled",sa->sun_path, strerror(errno)); + stat_pipe=0; + return; + } + if ((stat_sock=socket(PF_UNIX,SOCK_STREAM,0))==-1) { + log_warn("Failed to open socket: %s. Status readback will be impossible",strerror(errno)); + stat_pipe=0; + return; + } + sa->sun_family=AF_UNIX; +#ifdef BSD44_SOCKA + sa->sun_len=SUN_LEN(sa); +#endif + /* Early initialization, so that umask can be used race-free. */ + { + mode_t old_mask = umask((S_IRWXU|S_IRWXG|S_IRWXO)&(~global.ctl_perms)); + if (bind(stat_sock,(struct sockaddr *)sa,sa_len)==-1) { + log_warn("Error: could not bind socket: %s.\nStatus readback will be impossible",strerror(errno)); + close(stat_sock); + stat_pipe=0; + } + umask(old_mask); + } + + if(stat_pipe) sock_path= strdup(sa->sun_path); +} + +/* + * Start the status socket thread (see above) + */ +int start_stat_sock() +{ + pthread_t st; + + int rv=pthread_create(&st,&attr_detached,status_thread,NULL); + if (rv) + log_warn("Failed to start status thread. The status socket will be unuseable"); + else { + statsock_thrid=st; + log_info(2,"Status thread started."); + } + return rv; +} diff --git a/service/src/main/jni/pdnsd/src/status.h b/service/src/main/jni/pdnsd/src/status.h new file mode 100644 index 0000000..1d249f7 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/status.h @@ -0,0 +1,59 @@ +/* status.h - Make server status information accessible through a named pipe + + Copyright (C) 2000, 2001 Thomas Moestl + Copyright (C) 2002, 2004, 2008, 2009 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include +#include "conff.h" + +extern char *sock_path; +extern int stat_sock; + +/* The commands for pdnsd-ctl */ +#define CTL_CMDVERNR 0x6800 /* pdnsd-ctl command version (magic number used to check compatibility) */ + +#define CTL_MIN 1 +#define CTL_STATS 1 /* Give out stats (like the "traditional" status pipe) */ +#define CTL_SERVER 2 /* Enable or disable a server */ +#define CTL_RECORD 3 /* Delete or invalidate records */ +#define CTL_SOURCE 4 /* Read a hosts-style file */ +#define CTL_ADD 5 /* Add a record of the given type */ +#define CTL_NEG 6 /* Add a negative cached record */ +#define CTL_CONFIG 7 /* Re-read config file */ +#define CTL_INCLUDE 8 /* Read file as config file, disregarding global and server sections */ +#define CTL_EVAL 9 /* Parse string as if part of config file */ +#define CTL_EMPTY 10 /* Empty the cache */ +#define CTL_DUMP 11 /* Dump cache contents */ +#define CTL_MAX 11 + +#define CTL_S_UP 1 +#define CTL_S_DOWN 2 +#define CTL_S_RETEST 3 +#define CTL_R_DELETE 1 +#define CTL_R_INVAL 2 + +void init_stat_sock(void); +int start_stat_sock(void); + +#endif diff --git a/service/src/main/jni/pdnsd/src/test/Makefile.am b/service/src/main/jni/pdnsd/src/test/Makefile.am new file mode 100644 index 0000000..81da088 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/test/Makefile.am @@ -0,0 +1,35 @@ + +.PHONY: all clean distclean + +noinst_PROGRAMS = if_up is_local_addr tping random + +## Dirty trick: I demand that these objects be built; then, with the knowledge +## that the object files will end up here, I redefine the link chain. + +TESTADDSRC= +#TESTADDSRC= netdev.c error.c thread.c helpers.c icmp.c +TESTDEPS = netdev.o error.o thread.o helpers.o icmp.o + +TESTOBJS = netdev.o error.o thread.o helpers.o icmp.o + +if_up_SOURCES = if_up.c $(TESTADDSRC) +if_up_LDADD = $(TESTOBJS) @thread_CFLAGS@ +if_up_DEPENDENCIES = $(TESTDEPS) + +is_local_addr_SOURCES = is_local_addr.c $(TESTADDSRC) +is_local_addr_LDADD = $(TESTOBJS) @thread_CFLAGS@ +is_local_addr_DEPENDENCIES = $(TESTDEPS) + +tping_SOURCES = tping.c $(TESTADDSRC) +tping_LDADD = $(TESTOBJS) @thread_CFLAGS@ +tping_DEPENDENCIES = $(TESTDEPS) + +random_SOURCES = random.c $(TESTADDSRC) +random_LDADD = $(TESTOBJS) @thread_CFLAGS@ +random_DEPENDENCIES = $(TESTDEPS) + +# These are Symlinks we want to have in the package +#EXTRA_DIST = conff.h error.h helpers.h icmp.h ipvers.h netdev.h thread.h cacheing + +$(TESTOBJS): %.o: ../%.c + $(COMPILE) @thread_CFLAGS@ -c $< diff --git a/service/src/main/jni/pdnsd/src/test/Makefile.in b/service/src/main/jni/pdnsd/src/test/Makefile.in new file mode 100644 index 0000000..2ad8641 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/test/Makefile.in @@ -0,0 +1,464 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +noinst_PROGRAMS = if_up$(EXEEXT) is_local_addr$(EXEEXT) tping$(EXEEXT) \ + random$(EXEEXT) +subdir = src/test +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +am__objects_1 = +am_if_up_OBJECTS = if_up.$(OBJEXT) $(am__objects_1) +if_up_OBJECTS = $(am_if_up_OBJECTS) +am_is_local_addr_OBJECTS = is_local_addr.$(OBJEXT) $(am__objects_1) +is_local_addr_OBJECTS = $(am_is_local_addr_OBJECTS) +am_random_OBJECTS = random.$(OBJEXT) $(am__objects_1) +random_OBJECTS = $(am_random_OBJECTS) +am_tping_OBJECTS = tping.$(OBJEXT) $(am__objects_1) +tping_OBJECTS = $(am_tping_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(if_up_SOURCES) $(is_local_addr_SOURCES) $(random_SOURCES) \ + $(tping_SOURCES) +DIST_SOURCES = $(if_up_SOURCES) $(is_local_addr_SOURCES) \ + $(random_SOURCES) $(tping_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +cachedir = @cachedir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +def_id = @def_id@ +distribution = @distribution@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fullversion = @fullversion@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +packagerelease = @packagerelease@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +specbuild = @specbuild@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +thread_CFLAGS = @thread_CFLAGS@ +threadlib = @threadlib@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +TESTADDSRC = +#TESTADDSRC= netdev.c error.c thread.c helpers.c icmp.c +TESTDEPS = netdev.o error.o thread.o helpers.o icmp.o +TESTOBJS = netdev.o error.o thread.o helpers.o icmp.o +if_up_SOURCES = if_up.c $(TESTADDSRC) +if_up_LDADD = $(TESTOBJS) @thread_CFLAGS@ +if_up_DEPENDENCIES = $(TESTDEPS) +is_local_addr_SOURCES = is_local_addr.c $(TESTADDSRC) +is_local_addr_LDADD = $(TESTOBJS) @thread_CFLAGS@ +is_local_addr_DEPENDENCIES = $(TESTDEPS) +tping_SOURCES = tping.c $(TESTADDSRC) +tping_LDADD = $(TESTOBJS) @thread_CFLAGS@ +tping_DEPENDENCIES = $(TESTDEPS) +random_SOURCES = random.c $(TESTADDSRC) +random_LDADD = $(TESTOBJS) @thread_CFLAGS@ +random_DEPENDENCIES = $(TESTDEPS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/test/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/test/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) +if_up$(EXEEXT): $(if_up_OBJECTS) $(if_up_DEPENDENCIES) + @rm -f if_up$(EXEEXT) + $(LINK) $(if_up_OBJECTS) $(if_up_LDADD) $(LIBS) +is_local_addr$(EXEEXT): $(is_local_addr_OBJECTS) $(is_local_addr_DEPENDENCIES) + @rm -f is_local_addr$(EXEEXT) + $(LINK) $(is_local_addr_OBJECTS) $(is_local_addr_LDADD) $(LIBS) +random$(EXEEXT): $(random_OBJECTS) $(random_DEPENDENCIES) + @rm -f random$(EXEEXT) + $(LINK) $(random_OBJECTS) $(random_LDADD) $(LIBS) +tping$(EXEEXT): $(tping_OBJECTS) $(tping_DEPENDENCIES) + @rm -f tping$(EXEEXT) + $(LINK) $(tping_OBJECTS) $(tping_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/if_up.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/is_local_addr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tping.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-noinstPROGRAMS ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am + + +.PHONY: all clean distclean + +# These are Symlinks we want to have in the package +#EXTRA_DIST = conff.h error.h helpers.h icmp.h ipvers.h netdev.h thread.h cacheing + +$(TESTOBJS): %.o: ../%.c + $(COMPILE) @thread_CFLAGS@ -c $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/service/src/main/jni/pdnsd/src/test/if_up.c b/service/src/main/jni/pdnsd/src/test/if_up.c new file mode 100644 index 0000000..af6ec97 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/test/if_up.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" +#include "../netdev.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(int argc, char *argv[]) +{ + if (argc!=2) { + printf("Usage: %s \n",argv[0]); + exit(1); + } + printf("if_up: %s - %s\n",argv[1],if_up(argv[1])?"up":"down"); + return 0; +} diff --git a/service/src/main/jni/pdnsd/src/test/is_local_addr.c b/service/src/main/jni/pdnsd/src/test/is_local_addr.c new file mode 100644 index 0000000..fda517d --- /dev/null +++ b/service/src/main/jni/pdnsd/src/test/is_local_addr.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" +#include "../netdev.h" +#include "../ipvers.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(int argc, char *argv[]) +{ + pdnsd_a a; + + if (argc!=2) { + printf("Usage: %s
    \n",argv[0]); + exit(1); + } +#ifdef ENABLE_IPV4 + if (inet_aton(argv[1],&a.ipv4)) { +# ifdef ENABLE_IPV6 + run_ipv4=1; +# endif + printf("is %s a local addr: %s\n",argv[1],is_local_addr(&a)?"yes":"no"); + return 0; + } +#endif +#ifdef ENABLE_IPV6 + if (inet_pton(AF_INET6,argv[1],&a.ipv6)) { +# ifdef ENABLE_IPV4 + run_ipv4=0; +# endif + printf("is %s a local addr: %s\n",argv[1],is_local_addr(&a)?"yes":"no"); + return 0; + } +#endif + printf("Adress invalid.\n"); + return 0; +} diff --git a/service/src/main/jni/pdnsd/src/test/random.c b/service/src/main/jni/pdnsd/src/test/random.c new file mode 100644 index 0000000..a447e80 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/test/random.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(void) +{ + init_rng(); + printf("%i\n",(int)get_rand16()); + free_rng(); + return 0; +} diff --git a/service/src/main/jni/pdnsd/src/test/tping.c b/service/src/main/jni/pdnsd/src/test/tping.c new file mode 100644 index 0000000..734a25f --- /dev/null +++ b/service/src/main/jni/pdnsd/src/test/tping.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include "../helpers.h" +#include "../conff.h" +#include "../icmp.h" +#include "../ipvers.h" + +short int daemon_p=0; +#if DEBUG>0 +short int debug_p=0; +#endif +short int verbosity=VERBOSITY; +#if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) +short int run_ipv4=DEFAULT_IPV4; +#endif +#ifdef ENABLE_IPV6 +struct in6_addr ipv4_6_prefix; +#endif +pthread_t main_thrid,servstat_thrid; +volatile int signal_interrupt; +#if DEBUG>0 +FILE *dbg_file; +#endif +globparm_t global; + + +int main(int argc, char *argv[]) +{ + pdnsd_a a; + + if (argc!=2) { + printf("Usage: %s
    \n",argv[0]); + exit(1); + } +#ifdef ENABLE_IPV4 + if (inet_aton(argv[1],&a.ipv4)) { +# ifdef ENABLE_IPV6 + run_ipv4=1; +# endif + init_ping_socket(); + printf("ping (v4) echo from %s: %i\n",argv[1],ping(&a,100,2)); + return 0; + } +#endif +#ifdef ENABLE_IPV6 + if (inet_pton(AF_INET6,argv[1],&a.ipv6)) { +# ifdef ENABLE_IPV4 + run_ipv4=0; +# endif + init_ping_socket(); + printf("ping (v6) echo from %s: %i\n",argv[1],ping(&a,100,2)); + return 0; + } +#endif + printf("Adress invalid.\n"); + return 0; +} diff --git a/service/src/main/jni/pdnsd/src/thread.c b/service/src/main/jni/pdnsd/src/thread.c new file mode 100644 index 0000000..84c34f6 --- /dev/null +++ b/service/src/main/jni/pdnsd/src/thread.c @@ -0,0 +1,85 @@ +/* thread.c - Threading helpers + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2002, 2003 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread.h" +#include "error.h" +#include "helpers.h" +#include "conff.h" + + +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) +volatile short int waiting=0; /* Has the main thread already done sigwait() ? */ +#endif +pthread_attr_t attr_detached; +#if DEBUG>0 +pthread_key_t thrid_key; +#endif + +/* This is a handler for signals to the threads. We just hand the sigs on to the main thread. + * Note that this may result in blocked locks. We have no means to open the locks here, because in LinuxThreads + * the mutex functions are not async-signal safe. So, locks may still be active. We account for this by using + * softlocks (see below) in any functions called after sigwait from main(). */ +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) +void thread_sig(int sig) +{ + if (sig==SIGTSTP || sig==SIGTTOU || sig==SIGTTIN) { + /* nonfatal signal. Ignore, because proper handling is very difficult. */ + return; + } + if (waiting) { + log_warn("Caught signal %i.",sig); + if (sig==SIGSEGV || sig==SIGILL || sig==SIGBUS) + crash_msg("A fatal signal occured."); + pthread_kill(main_thrid,SIGTERM); + pthread_exit(NULL); + } else { + crash_msg("An error occured at startup."); + _exit(1); + } +} +#endif + +/* This is now defined as an inline function in thread.h */ +#if 0 +void usleep_r(unsigned long usec) +{ +#if ((TARGET==TARGET_LINUX) || (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN)) && defined(HAVE_USLEEP) + usleep(usec); +#else + struct timeval tv; + + tv.tv_sec=usec/1000000; + tv.tv_usec=usec%1000000; + select(0, NULL, NULL, NULL, tv); +#endif +} +#endif + diff --git a/service/src/main/jni/pdnsd/src/thread.h b/service/src/main/jni/pdnsd/src/thread.h new file mode 100644 index 0000000..70ec47b --- /dev/null +++ b/service/src/main/jni/pdnsd/src/thread.h @@ -0,0 +1,143 @@ +/* thread.h - Threading helpers + + Copyright (C) 2000 Thomas Moestl + Copyright (C) 2002, 2003, 2005 Paul A. Rombouts + + This file is part of the pdnsd package. + + pdnsd 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. + + pdnsd 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 pdnsd; see the file COPYING. If not, see + . +*/ + + +#ifndef _THREAD_H_ +#define _THREAD_H_ + +#include +#include +#include + +/* --- from main.c */ +extern sigset_t sigs_msk; +/* --- */ + +#if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) +extern volatile short int waiting; +void thread_sig(int sig); +#endif + +/* These are macros for setting up the signal handling of a new thread. They + * are needed because the LinuxThreads implementation obviously has some + * problems in signal handling, which makes the recommended solution (doing + * sigwait() in one thread and blocking the signals in all threads) impossible. + * So, for Linux, we have to install the fatal_sig handler. + * It seems to me that signal handlers in fact aren't shared between threads + * under Linux. Also, sigwait() does not seem to work as indicated in the docs */ + +/* Note added by Paul Rombouts: In the new Native POSIX Thread Library for Linux (NPTL) + signal handling has changed from per-thread signal handling to POSIX process signal handling, + which makes the recommended solution mentioned by Thomas Moestl possible. + In this case I can simply define THREAD_SIGINIT to be empty. + The signals are blocked in main() before any threads are created, + and we simply never unblock them except by calling sigwait() in main(). */ + +#if (TARGET==TARGET_LINUX) +# ifdef THREADLIB_NPTL +# define THREAD_SIGINIT +# else +# ifdef THREADLIB_LINUXTHREADS2 +# define THREAD_SIGINIT { \ + struct sigaction action; \ + pthread_sigmask(SIG_UNBLOCK,&sigs_msk,NULL); \ + action.sa_handler = thread_sig; \ + action.sa_mask = sigs_msk; \ + action.sa_flags = 0; \ + sigaction(SIGINT,&action,NULL); \ + sigaction(SIGILL,&action,NULL); \ + sigaction(SIGABRT,&action,NULL); \ + sigaction(SIGFPE,&action,NULL); \ + sigaction(SIGSEGV,&action,NULL); \ + sigaction(SIGTSTP,&action,NULL); \ + sigaction(SIGTTOU,&action,NULL); \ + sigaction(SIGTTIN,&action,NULL); \ + sigaction(SIGTERM,&action,NULL); \ + action.sa_handler = SIG_IGN; \ + sigemptyset(&action.sa_mask); \ + action.sa_flags = 0; \ + sigaction(SIGPIPE,&action,NULL); \ + } +# else +# define THREAD_SIGINIT { \ + struct sigaction action; \ + pthread_sigmask(SIG_UNBLOCK,&sigs_msk,NULL); \ + action.sa_handler = thread_sig; \ + action.sa_mask = sigs_msk; \ + action.sa_flags = 0; \ + sigaction(SIGILL,&action,NULL); \ + sigaction(SIGABRT,&action,NULL); \ + sigaction(SIGFPE,&action,NULL); \ + sigaction(SIGSEGV,&action,NULL); \ + sigaction(SIGTSTP,&action,NULL); \ + sigaction(SIGTTOU,&action,NULL); \ + sigaction(SIGTTIN,&action,NULL); \ + action.sa_handler = SIG_IGN; \ + sigemptyset(&action.sa_mask); \ + action.sa_flags = 0; \ + sigaction(SIGPIPE,&action,NULL); \ + } +# endif +# endif +#elif (TARGET==TARGET_BSD) || (TARGET==TARGET_CYGWIN) +#define THREAD_SIGINIT pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL) +#else +# error Unsupported platform! +#endif + + +/* This is a thread-safe usleep(). + Implementation of the BSD usleep function using nanosleep. +*/ +inline static int usleep_r(unsigned long useconds) + __attribute__((always_inline)); +inline static int usleep_r(unsigned long useconds) +{ + struct timespec ts = { (useconds / 1000000), + (useconds % 1000000) * 1000ul }; + + return nanosleep(&ts, NULL); +} + +/* This is a thread-safe sleep(). + The semantics are somewhat different from the POSIX sleep function, + but it suits our purposes. +*/ +inline static int sleep_r (unsigned int seconds) + __attribute__((always_inline)); +inline static int sleep_r (unsigned int seconds) +{ + struct timespec ts = { seconds, 0 }; + + return nanosleep(&ts, NULL); +} + + +/* Used for creating detached threads */ +extern pthread_attr_t attr_detached; + +#if DEBUG>0 +/* Key for storing private thread ID's */ +extern pthread_key_t thrid_key; +#endif + +#endif diff --git a/service/src/main/jni/pdnsd/version b/service/src/main/jni/pdnsd/version new file mode 100644 index 0000000..c400a37 --- /dev/null +++ b/service/src/main/jni/pdnsd/version @@ -0,0 +1 @@ +1.2.9b-par diff --git a/service/src/main/jni/system.cpp b/service/src/main/jni/system.cpp new file mode 100644 index 0000000..eb8d747 --- /dev/null +++ b/service/src/main/jni/system.cpp @@ -0,0 +1,137 @@ +#define LOG_TAG "Socksdroid" + +#include "jni.h" +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0) +#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0) +#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0) + +extern "C" +JNIEXPORT void JNICALL +Java_net_typeblog_socks_System_jniclose(JNIEnv *env, jclass clazz, jint fd) { + close(fd); +} + +extern "C" +JNIEXPORT jint JNICALL +Java_net_typeblog_socks_System_sendfd(JNIEnv *env, jclass clazz, jint tun_fd, jstring sock) { + int fd; + struct sockaddr_un addr; + const char *sockpath; + + if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + LOGE("socket() failed: %s (socket fd = %d)\n", strerror(errno), fd); + return (jint)-1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + sockpath = env->GetStringUTFChars(sock, 0); + strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)-1); + env->ReleaseStringUTFChars(sock, sockpath); + + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + LOGE("connect() failed: %s (fd = %d)\n", strerror(errno), fd); + close(fd); + return (jint)-1; + } + + if (ancil_send_fd(fd, tun_fd)) { + LOGE("ancil_send_fd: %s", strerror(errno)); + close(fd); + return (jint)-1; + } + + close(fd); + return 0; +} + +static const char *classPathName = "net/typeblog/socks/System"; + +static JNINativeMethod method_table[] = { + { "jniclose", "(I)V", + (void*) Java_net_typeblog_socks_System_jniclose }, + { "sendfd", "(ILjava/lang/String;)I", + (void*) Java_net_typeblog_socks_System_sendfd } +}; + + + +/* + * Register several native methods for one class. + */ +static int registerNativeMethods(JNIEnv* env, const char* className, + JNINativeMethod* gMethods, int numMethods) +{ + jclass clazz; + + clazz = env->FindClass(className); + if (clazz == NULL) { + LOGE("Native registration unable to find class '%s'", className); + return JNI_FALSE; + } + if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { + LOGE("RegisterNatives failed for '%s'", className); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +/* + * Register native methods for all classes we know about. + * + * returns JNI_TRUE on success. + */ +static int registerNatives(JNIEnv* env) +{ + if (!registerNativeMethods(env, classPathName, method_table, + sizeof(method_table) / sizeof(method_table[0]))) { + return JNI_FALSE; + } + + return JNI_TRUE; +} + +/* + * This is called by the VM when the shared library is first loaded. + */ + +typedef union { + JNIEnv* env; + void* venv; +} UnionJNIEnvToVoid; + +jint JNI_OnLoad(JavaVM* vm, void* reserved) { + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + jint result = -1; + JNIEnv* env = NULL; + + LOGI("JNI_OnLoad"); + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { + LOGE("ERROR: GetEnv failed"); + goto bail; + } + env = uenv.env; + + if (registerNatives(env) != JNI_TRUE) { + LOGE("ERROR: registerNatives failed"); + goto bail; + } + + result = JNI_VERSION_1_4; + +bail: + return result; +} diff --git a/service/src/main/res/drawable-anydpi-v21/ic_cloud_black_24dp.xml b/service/src/main/res/drawable-anydpi-v21/ic_cloud_black_24dp.xml new file mode 100644 index 0000000..e0940ca --- /dev/null +++ b/service/src/main/res/drawable-anydpi-v21/ic_cloud_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/service/src/main/res/drawable-anydpi-v21/ic_power_settings_new_black_24dp.xml b/service/src/main/res/drawable-anydpi-v21/ic_power_settings_new_black_24dp.xml new file mode 100644 index 0000000..26272ab --- /dev/null +++ b/service/src/main/res/drawable-anydpi-v21/ic_power_settings_new_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/service/src/main/res/raw/pdnsd_local b/service/src/main/res/raw/pdnsd_local new file mode 100644 index 0000000..8854c1f --- /dev/null +++ b/service/src/main/res/raw/pdnsd_local @@ -0,0 +1,21 @@ +global { + perm_cache=1024; + cache_dir="%2$s"; + server_port = %4$d; + server_ip = %3$s; + query_method=tcp_only; + min_ttl=15m; + max_ttl=1w; + timeout=10; + daemon=off; +} + +%1$s + +rr { + name=localhost; + reverse=on; + a=127.0.0.1; + owner=localhost; + soa=localhost,root.localhost,42,86400,900,86400,86400; +} diff --git a/service/src/main/res/values/strings.xml b/service/src/main/res/values/strings.xml new file mode 100644 index 0000000..5ee4cde --- /dev/null +++ b/service/src/main/res/values/strings.xml @@ -0,0 +1,40 @@ + + + DragonSSH XHTTP Example + Cancel + OK + Stop + XHTTP SSH connection + Quiet status notification for the active XHTTP SSH tunnel + Starting XHTTP SSH + Stopping XHTTP SSH + Starting + Connecting + Authenticating + Authenticated + Getting configuration + Assigning IP address + Adding routes + Connected + Disconnected + Reconnecting + Stopping + Not running + Resolving host + Connecting TCP + Waiting for network + Waiting %1$s seconds before reconnecting + Waiting for authentication + Unknown state + Server banner: + Connection lost + SSH connection established + Stopping SSH + SSH compression enabled + SSH authentication failed + Reconnecting… + Waiting for network… + Failed to start the Android VPN tunnel + Invalid XHTTP endpoint + VPN permission is required + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..b4190b7 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "DragonSSH-XHTTP-Public-Example" +include ':app', ':service'