pax_global_header00006660000000000000000000000064127235450330014516gustar00rootroot0000000000000052 comment=dff16271476c50df32b54ea73f596c6a35b83495 captagent-6.1.0.20/000077500000000000000000000000001272354503300137105ustar00rootroot00000000000000captagent-6.1.0.20/.gitattributes000066400000000000000000000000551272354503300166030ustar00rootroot00000000000000captagent/autom4te.cache/* linguist-vendored captagent-6.1.0.20/.gitignore000066400000000000000000000011041272354503300156740ustar00rootroot00000000000000INSTALL Makefile Makefile.in aclocal.m4 config.guess src/config.h src/config.h.in src/config.h.in~ src/capplan.c src/capplan.h config.log config.status config.sub configure depcomp install-sh libtool ltmain.sh missing stamp-h1 *.o *.lo *.a *.la *.Po *.Plo .*.swp .deps .libs autom4te.cache libtool.m4 lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 capplan.tab.h lex.yy.c ylwrap captagent.spec conf/captagent.xml pkg/debian/*.substvars pkg/debian/*.debhelper pkg/debian/*.debhelper.log pkg/debian/files pkg/debian/captagent/ pkg/debian/captagent.init src/captagent *.dh-orig captagent-6.1.0.20/.travis.yml000066400000000000000000000004761272354503300160300ustar00rootroot00000000000000language: c compiler: - gcc install: - sudo apt-get update || true - sudo apt-get install build-essential - sudo apt-get install libpcap-dev libexpat-dev libjson0-dev bison flex libtool autoconf automake autogen script: - ./build.sh - ./configure - make - ./src/captagent -h - ./src/captagent -v captagent-6.1.0.20/AUTHORS000066400000000000000000000000231272354503300147530ustar00rootroot00000000000000Alexandr Dubovikov captagent-6.1.0.20/COPYING000066400000000000000000001045131272354503300147470ustar00rootroot00000000000000 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 . captagent-6.1.0.20/ChangeLog000066400000000000000000000000431272354503300154570ustar00rootroot00000000000000 10.07.2015 captagent 6.0.0 beta 1 captagent-6.1.0.20/LICENSE000066400000000000000000001044601272354503300147220ustar00rootroot00000000000000GNU 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. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . 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: {project} Copyright (C) {year} {fullname} 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 .captagent-6.1.0.20/Makefile.am000066400000000000000000000002361272354503300157450ustar00rootroot00000000000000include common.am SUBDIRS = \ src \ include EXTRA_DIST = \ conf \ @PACKAGE_NAME@.spec ACLOCAL_AMFLAGS = -I m4 distclean-local: rm -rf autom4te.cache captagent-6.1.0.20/NEWS000066400000000000000000000000411272354503300144020ustar00rootroot00000000000000NEWS file for captagent project. captagent-6.1.0.20/README000066400000000000000000000000521272354503300145650ustar00rootroot00000000000000Sample readme file for captagent project. captagent-6.1.0.20/README.md000066400000000000000000000036441272354503300151760ustar00rootroot00000000000000![](http://i.imgur.com/3kEIR.png) CaptAgent 6 Project ========= #####The Next-Generation capture agent for Sipcapture's [Homer](https://github.com/sipcapture/homer) Project ------------- Download the latest code from our repository and compile it on your system. Capagent requires: *libexpat, libpcap, libtool, automake* to compile. ``` cd /usr/src git clone https://github.com/sipcapture/captagent.git captagent cd captagent ./build.sh ./configure make && make install ``` Captagent should be now ready to be configured. ## Configuration Captagent 6 provides a modular configuration structure supporting includes. To get familiar with the new configuration please use the projects [wiki](https://github.com/sipcapture/captagent/wiki) ------------- ### Support If you found a bug or issue with the code, please raise an Issue on the project tracker. If you have specific questions or require professional support please contact us at support@sipcapture.org ![HomerFlow](http://i.imgur.com/U7UBI.png) ### Developers Contributions to our project are always welcome! If you intend to participate and help us improve CAPTANGENT, we kindly ask you to sign a [CLA (Contributor License Agreement)](http://cla.qxip.net) and coordinate at best with the existing team via the [homer-dev](http://groups.google.com/group/homer-dev) mailing list. ---------- ##### If you use CAPTAGENT in production, please consider supporting us with a [donation](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=donation%40sipcapture%2eorg&lc=US&item_name=SIPCAPTURE&no_note=0¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHostedGuest) [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=donation%40sipcapture%2eorg&lc=US&item_name=SIPCAPTURE&no_note=0¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHostedGuest) captagent-6.1.0.20/build.sh000077500000000000000000000010601272354503300153430ustar00rootroot00000000000000#!/bin/sh echo "You need to have m4, automake, autoconf, libtool..."; #aclocal list_of_config_files="./src/modules"; # list_of_config_files_pro="./src/modules_pro"; #echo adding modules #for file in $list_of_config_files; do # echo "AC_CONFIG_FILES([${list_of_config_files}/${file}])" #done > modules_makefiles.m4 autoreconf --force --install automake --add-missing autoconf #./configure --enable-pcre --enable-redis #FreeBSD has libexpat in /usr/local/lib (ports installation) #./configure CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" captagent-6.1.0.20/captagent.spec.in000066400000000000000000000350371272354503300171470ustar00rootroot00000000000000Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Release: %{BUILD_NUMBER} Summary: SIP capture server Group: Applications/Communications License: GPLv3 URL: http://www.sipcapture.org/ Source0: %name-%version.tar.gz Buildrequires: json-c-devel expat-devel libpcap-devel flex-devel Requires: json-c expat libpcap %description HOMER5 a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling ########### module packages ########################### %package database-hash Requires: %name = %version Summary: database hash module for sipcapture HOMER5 %description database-hash hash database module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package database-redis Requires: %name = %version Summary: database redis module for sipcapture HOMER5 %description database-redis redis database module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package protocol-sip Requires: %name = %version Summary: protocol sip module for sipcapture HOMER5 %description protocol-sip sip protocol module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package protocol-rtcp Requires: %name = %version Summary: protocol rtcp module for sipcapture HOMER5 %description protocol-rtcp rtcp protocol module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package socket-pcap Requires: %name = %version Summary: socket pcap module for sipcapture HOMER5 %description socket-pcap pcap socket module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package socket-raw Requires: %name = %version Summary: socket raw module for sipcapture HOMER5 %description socket-raw raw socket module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package socket-rtcpxr Requires: %name = %version Summary: socket rtcpxr module for sipcapture HOMER5 %description socket-rtcpxr rtcpxr socket module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package transport-hep Requires: %name = %version Summary: transport hep module for sipcapture HOMER5 %description transport-hep hep transport module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package transport-json Requires: %name = %version Summary: transport json module for sipcapture HOMER5 %description transport-json json transport module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package interface-http Requires: %name = %version Summary: interface http module for sipcapture HOMER5 %description interface-http http interface module for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling ########### module development packages ########################### %package database-hash-devel Requires: %name = %version Requires: database-hash Summary: database hash module development package for sipcapture HOMER5 %description database-hash-devel hash database module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package database-redis-devel Requires: %name = %version Requires: database-redis Summary: database redis module development package for sipcapture HOMER5 %description database-redis-devel redis database module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package protocol-sip-devel Requires: %name = %version Requires: protocol-sip Summary: protocol sip module development package for sipcapture HOMER5 %description protocol-sip-devel sip protocol module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package protocol-rtcp-devel Requires: %name = %version Requires: protocol-rtcp Summary: protocol rtcp module development package for sipcapture HOMER5 %description protocol-rtcp-devel rtcp protocol module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package socket-pcap-devel Requires: %name = %version Requires: socket-pcap Summary: socket pcap module development package for sipcapture HOMER5 %description socket-pcap-devel pcap socket module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package socket-raw-devel Requires: %name = %version Requires: socket-raw Summary: socket raw module development package for sipcapture HOMER5 %description socket-raw-devel raw socket module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package socket-rtcpxr-devel Requires: %name = %version Requires: socket-rtcpxr Summary: socket rtcpxr module development package for sipcapture HOMER5 %description socket-rtcpxr-devel rtcpxr socket module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package transport-hep-devel Requires: %name = %version Requires: transport-hep Summary: transport hep module development package for sipcapture HOMER5 %description transport-hep-devel hep transport module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package transport-json-devel Requires: %name = %version Requires: transport-json Summary: transport json module development package for sipcapture HOMER5 %description transport-json-devel json transport module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %package interface-http-devel Requires: %name = %version Requires: interface-http Summary: interface http module development package for sipcapture HOMER5 %description interface-http-devel http interface module development package for a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling %prep %setup -b0 -q %build autoreconf -if %configure -C \ --prefix=%{_prefix} %{__make} %install %{__make} DESTDIR=%{buildroot} install %files %defattr(644,root,root,755) %config(noreplace) %{_sysconfdir}/%name/%name.xml %attr(755,root,root) %{_bindir}/%name %files database-hash %config(noreplace) %{_sysconfdir}/%name/database_hash.xml %{_libdir}/%name/modules/database_hash.so %files database-redis %config(noreplace) %{_sysconfdir}/%name/database_redis.xml %{_libdir}/%name/modules/database_redis.so %files protocol-sip %config(noreplace) %{_sysconfdir}/%name/protocol_sip.xml %config(noreplace) %{_sysconfdir}/%name/captureplans/sip_capture_plan.cfg %{_libdir}/%name/modules/protocol_sip.so %files protocol-rtcp %config(noreplace) %{_sysconfdir}/%name/protocol_rtcp.xml %config(noreplace) %{_sysconfdir}/%name/captureplans/rtcp_capture_plan.cfg %{_libdir}/%name/modules/protocol_rtcp.so %files socket-pcap %config(noreplace) %{_sysconfdir}/%name/socket_pcap.xml %{_libdir}/%name/modules/socket_pcap.so %files socket-raw %config(noreplace) %{_sysconfdir}/%name/socket_raw.xml %{_libdir}/%name/modules/socket_raw.so %files socket-rtcpxr %config(noreplace) %{_sysconfdir}/%name/socket_rtcpxr.xml %config(noreplace) %{_sysconfdir}/%name/captureplans/rtcpxr_capture_plan.cfg %{_libdir}/%name/modules/socket_rtcpxr.so %files transport-hep %config(noreplace) %{_sysconfdir}/%name/transport_hep.xml %{_libdir}/%name/modules/transport_hep.so %files transport-json %config(noreplace) %{_sysconfdir}/%name/transport_json.xml %{_libdir}/%name/modules/transport_json.so %files interface-http %{_libdir}/%name/modules/interface_http.so #################################################### %files database-hash-devel %{_libdir}/%name/modules/database_hash.la %{_libdir}/%name/modules/database_hash.a %files protocol-sip-devel %{_libdir}/%name/modules/protocol_sip.la %{_libdir}/%name/modules/protocol_sip.a %files protocol-rtcp-devel %{_libdir}/%name/modules/protocol_rtcp.la %{_libdir}/%name/modules/protocol_rtcp.a %files socket-pcap-devel %{_libdir}/%name/modules/socket_pcap.la %{_libdir}/%name/modules/socket_pcap.a %files socket-raw-devel %{_libdir}/%name/modules/socket_raw.la %{_libdir}/%name/modules/socket_raw.a %files socket-rtcpxr-devel %{_libdir}/%name/modules/socket_rtcpxr.la %{_libdir}/%name/modules/socket_rtcpxr.a %files transport-hep-devel %{_libdir}/%name/modules/transport_hep.la %{_libdir}/%name/modules/transport_hep.a %files transport-json-devel %{_libdir}/%name/modules/transport_json.la %{_libdir}/%name/modules/transport_json.a %files interface-http-devel %{_libdir}/%name/modules/interface_http.la %{_libdir}/%name/modules/interface_http.a #################################################### captagent-6.1.0.20/captagent/000077500000000000000000000000001272354503300156565ustar00rootroot00000000000000captagent-6.1.0.20/captagent/conf/000077500000000000000000000000001272354503300166035ustar00rootroot00000000000000captagent-6.1.0.20/captagent/conf/captagent.xml000066400000000000000000000065201272354503300212760ustar00rootroot00000000000000 captagent-6.1.0.20/captagent/mod/000077500000000000000000000000001272354503300164355ustar00rootroot00000000000000captagent-6.1.0.20/captagent/mod/core_hep/000077500000000000000000000000001272354503300202215ustar00rootroot00000000000000captagent-6.1.0.20/captagent/mod/core_hep/core_hep.c000066400000000000000000000663411272354503300221630ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif /* __FAVOR_BSD */ #include #include #include #include #ifdef USE_IPV6 #include #endif /* USE_IPV6 */ #include #include "src/api.h" #include "src/log.h" #include "core_hep.h" pthread_t call_thread; pthread_mutex_t lock; #ifdef USE_ZLIB z_stream strm; #endif /* USE_ZLIB */ int send_hep_basic (rc_info_t *rcinfo, unsigned char *data, unsigned int len) { unsigned char *zipData = NULL; int sendzip = 0; #ifdef USE_ZLIB int status = 0; unsigned long dlen; if(pl_compress && hep_version == 3) { //dlen = len/1000+len*len+13; dlen = compressBound(len); zipData = malloc(dlen); /* give a little bit memmory */ /* do compress */ status = compress( zipData, &dlen, data, len ); if( status != Z_OK ){ LERR( "data couldn't be compressed\n"); sendzip = 0; if(zipData) free(zipData); /* release */ } else { sendzip = 1; len = dlen; } } #endif /* USE_ZLIB */ switch(hep_version) { case 3: return send_hepv3(rcinfo, sendzip ? zipData : data , len, sendzip); break; case 2: case 1: return send_hepv2(rcinfo, data, len); break; default: LERR( "Unsupported HEP version [%d]\n", hep_version); break; } if(zipData) free(zipData); return 0; } int send_hepv3 (rc_info_t *rcinfo, unsigned char *data, unsigned int len, unsigned int sendzip) { struct hep_generic *hg=NULL; void* buffer; unsigned int buflen=0, iplen=0,tlen=0; hep_chunk_ip4_t src_ip4, dst_ip4; #ifdef USE_IPV6 hep_chunk_ip6_t src_ip6, dst_ip6; #endif hep_chunk_t payload_chunk; hep_chunk_t authkey_chunk; hep_chunk_t correlation_chunk; static int errors = 0; hg = malloc(sizeof(struct hep_generic)); memset(hg, 0, sizeof(struct hep_generic)); /* header set */ memcpy(hg->header.id, "\x48\x45\x50\x33", 4); /* IP proto */ hg->ip_family.chunk.vendor_id = htons(0x0000); hg->ip_family.chunk.type_id = htons(0x0001); hg->ip_family.data = rcinfo->ip_family; hg->ip_family.chunk.length = htons(sizeof(hg->ip_family)); /* Proto ID */ hg->ip_proto.chunk.vendor_id = htons(0x0000); hg->ip_proto.chunk.type_id = htons(0x0002); hg->ip_proto.data = rcinfo->ip_proto; hg->ip_proto.chunk.length = htons(sizeof(hg->ip_proto)); /* IPv4 */ if(rcinfo->ip_family == AF_INET) { /* SRC IP */ src_ip4.chunk.vendor_id = htons(0x0000); src_ip4.chunk.type_id = htons(0x0003); inet_pton(AF_INET, rcinfo->src_ip, &src_ip4.data); src_ip4.chunk.length = htons(sizeof(src_ip4)); /* DST IP */ dst_ip4.chunk.vendor_id = htons(0x0000); dst_ip4.chunk.type_id = htons(0x0004); inet_pton(AF_INET, rcinfo->dst_ip, &dst_ip4.data); dst_ip4.chunk.length = htons(sizeof(dst_ip4)); iplen = sizeof(dst_ip4) + sizeof(src_ip4); } #ifdef USE_IPV6 /* IPv6 */ else if(rcinfo->ip_family == AF_INET6) { /* SRC IPv6 */ src_ip6.chunk.vendor_id = htons(0x0000); src_ip6.chunk.type_id = htons(0x0005); inet_pton(AF_INET6, rcinfo->src_ip, &src_ip6.data); src_ip6.chunk.length = htonl(sizeof(src_ip6)); /* DST IPv6 */ dst_ip6.chunk.vendor_id = htons(0x0000); dst_ip6.chunk.type_id = htons(0x0006); inet_pton(AF_INET6, rcinfo->dst_ip, &dst_ip6.data); dst_ip6.chunk.length = htonl(sizeof(dst_ip6)); iplen = sizeof(dst_ip6) + sizeof(src_ip6); } #endif /* SRC PORT */ hg->src_port.chunk.vendor_id = htons(0x0000); hg->src_port.chunk.type_id = htons(0x0007); hg->src_port.data = htons(rcinfo->src_port); hg->src_port.chunk.length = htons(sizeof(hg->src_port)); /* DST PORT */ hg->dst_port.chunk.vendor_id = htons(0x0000); hg->dst_port.chunk.type_id = htons(0x0008); hg->dst_port.data = htons(rcinfo->dst_port); hg->dst_port.chunk.length = htons(sizeof(hg->dst_port)); /* TIMESTAMP SEC */ hg->time_sec.chunk.vendor_id = htons(0x0000); hg->time_sec.chunk.type_id = htons(0x0009); hg->time_sec.data = htonl(rcinfo->time_sec); hg->time_sec.chunk.length = htons(sizeof(hg->time_sec)); /* TIMESTAMP USEC */ hg->time_usec.chunk.vendor_id = htons(0x0000); hg->time_usec.chunk.type_id = htons(0x000a); hg->time_usec.data = htonl(rcinfo->time_usec); hg->time_usec.chunk.length = htons(sizeof(hg->time_usec)); /* Protocol TYPE */ hg->proto_t.chunk.vendor_id = htons(0x0000); hg->proto_t.chunk.type_id = htons(0x000b); hg->proto_t.data = rcinfo->proto_type; hg->proto_t.chunk.length = htons(sizeof(hg->proto_t)); /* Capture ID */ hg->capt_id.chunk.vendor_id = htons(0x0000); hg->capt_id.chunk.type_id = htons(0x000c); hg->capt_id.data = htons(capt_id); hg->capt_id.chunk.length = htons(sizeof(hg->capt_id)); /* Payload */ payload_chunk.vendor_id = htons(0x0000); payload_chunk.type_id = sendzip ? htons(0x0010) : htons(0x000f); payload_chunk.length = htons(sizeof(payload_chunk) + len); tlen = sizeof(struct hep_generic) + len + iplen + sizeof(hep_chunk_t); /* auth key */ if(capt_password != NULL) { tlen += sizeof(hep_chunk_t); /* Auth key */ authkey_chunk.vendor_id = htons(0x0000); authkey_chunk.type_id = htons(0x000e); authkey_chunk.length = htons(sizeof(authkey_chunk) + strlen(capt_password)); tlen += strlen(capt_password); } /* correlation key */ if(rcinfo->correlation_id.s && rcinfo->correlation_id.len > 0) { tlen += sizeof(hep_chunk_t); /* Correlation key */ correlation_chunk.vendor_id = htons(0x0000); correlation_chunk.type_id = htons(0x0011); correlation_chunk.length = htons(sizeof(correlation_chunk) + rcinfo->correlation_id.len); tlen += rcinfo->correlation_id.len; } /* total */ hg->header.length = htons(tlen); //LERR( "LEN: [%d] vs [%d] = IPLEN:[%d] LEN:[%d] CH:[%d]\n", hg->header.length, ntohs(hg->header.length), iplen, len, sizeof(struct hep_chunk)); buffer = (void*)malloc(tlen); if (buffer==0){ LERR("ERROR: out of memory\n"); free(hg); return 1; } memcpy((void*) buffer, hg, sizeof(struct hep_generic)); buflen = sizeof(struct hep_generic); /* IPv4 */ if(rcinfo->ip_family == AF_INET) { /* SRC IP */ memcpy((void*) buffer+buflen, &src_ip4, sizeof(struct hep_chunk_ip4)); buflen += sizeof(struct hep_chunk_ip4); memcpy((void*) buffer+buflen, &dst_ip4, sizeof(struct hep_chunk_ip4)); buflen += sizeof(struct hep_chunk_ip4); } #ifdef USE_IPV6 /* IPv6 */ else if(rcinfo->ip_family == AF_INET6) { /* SRC IPv6 */ memcpy((void*) buffer+buflen, &src_ip4, sizeof(struct hep_chunk_ip6)); buflen += sizeof(struct hep_chunk_ip6); memcpy((void*) buffer+buflen, &dst_ip6, sizeof(struct hep_chunk_ip6)); buflen += sizeof(struct hep_chunk_ip6); } #endif /* AUTH KEY CHUNK */ if(capt_password != NULL) { memcpy((void*) buffer+buflen, &authkey_chunk, sizeof(struct hep_chunk)); buflen += sizeof(struct hep_chunk); /* Now copying payload self */ memcpy((void*) buffer+buflen, capt_password, strlen(capt_password)); buflen+=strlen(capt_password); } /* Correlation KEY CHUNK */ if(rcinfo->correlation_id.s && rcinfo->correlation_id.len > 0) { memcpy((void*) buffer+buflen, &correlation_chunk, sizeof(struct hep_chunk)); buflen += sizeof(struct hep_chunk); /* Now copying payload self */ memcpy((void*) buffer+buflen, rcinfo->correlation_id.s, rcinfo->correlation_id.len); buflen+= rcinfo->correlation_id.len; } /* PAYLOAD CHUNK */ memcpy((void*) buffer+buflen, &payload_chunk, sizeof(struct hep_chunk)); buflen += sizeof(struct hep_chunk); if(!data) { LERR( "the captured data is empty....\n"); goto error; } /* Now copying payload self */ memcpy((void*) buffer+buflen, data, len); buflen+=len; /* make sleep after 100 errors */ if(errors > 50) { LERR( "HEP server is down... retrying after sleep...\n"); if(!usessl) { sleep(2); if(init_hepsocket_blocking()) { initfails++; } errors=0; } #ifdef USE_SSL else { sleep(2); if(initSSL()) { initfails++; } errors=0; } #endif /* USE SSL */ } /* send this packet out of our socket */ if(send_data(buffer, buflen)) { errors++; } /* FREE */ if(buffer) free(buffer); if(hg) free(hg); return 1; error: if(buffer) free(buffer); if(hg) free(hg); return 0; } int send_hepv2 (rc_info_t *rcinfo, unsigned char *data, unsigned int len) { void* buffer; struct hep_hdr hdr; struct hep_timehdr hep_time; struct hep_iphdr hep_ipheader; unsigned int totlen=0, buflen=0; static int errors=0; #ifdef USE_IPV6 struct hep_ip6hdr hep_ip6header; #endif /* USE IPV6 */ /* Version && proto */ hdr.hp_v = hep_version; hdr.hp_f = rcinfo->ip_family; hdr.hp_p = rcinfo->ip_proto; hdr.hp_sport = htons(rcinfo->src_port); /* src port */ hdr.hp_dport = htons(rcinfo->dst_port); /* dst port */ /* IP version */ switch (hdr.hp_f) { case AF_INET: totlen = sizeof(struct hep_iphdr); break; #ifdef USE_IPV6 case AF_INET6: totlen = sizeof(struct hep_ip6hdr); break; #endif /* USE IPV6 */ } hdr.hp_l = totlen + sizeof(struct hep_hdr); /* COMPLETE LEN */ totlen += sizeof(struct hep_hdr); totlen += len; if(hep_version == 2) { totlen += sizeof(struct hep_timehdr); hep_time.tv_sec = rcinfo->time_sec; hep_time.tv_usec = rcinfo->time_usec; hep_time.captid = capt_id; } /*buffer for ethernet frame*/ buffer = (void*)malloc(totlen); if (buffer==0){ LERR("ERROR: out of memory\n"); goto error; } /* copy hep_hdr */ memcpy((void*) buffer, &hdr, sizeof(struct hep_hdr)); buflen = sizeof(struct hep_hdr); switch (hdr.hp_f) { case AF_INET: /* Source && Destination ipaddresses*/ inet_pton(AF_INET, rcinfo->src_ip, &hep_ipheader.hp_src); inet_pton(AF_INET, rcinfo->dst_ip, &hep_ipheader.hp_dst); /* copy hep ipheader */ memcpy((void*)buffer + buflen, &hep_ipheader, sizeof(struct hep_iphdr)); buflen += sizeof(struct hep_iphdr); break; #ifdef USE_IPV6 case AF_INET6: inet_pton(AF_INET6, rcinfo->src_ip, &hep_ip6header.hp6_src); inet_pton(AF_INET6, rcinfo->dst_ip, &hep_ip6header.hp6_dst); /* copy hep6 ipheader */ memcpy((void*)buffer + buflen, &hep_ip6header, sizeof(struct hep_ip6hdr)); buflen += sizeof(struct hep_ip6hdr); break; #endif /* USE_IPV6 */ } /* Version 2 has timestamp, captnode ID */ if(hep_version == 2) { /* TIMING */ memcpy((void*)buffer + buflen, &hep_time, sizeof(struct hep_timehdr)); buflen += sizeof(struct hep_timehdr); } if(!data) { LERR( "the captured data is empty....\n"); goto error; } memcpy((void *)(buffer + buflen) , (void*)(data), len); buflen +=len; /* make sleep after 100 errors*/ if(errors > 50) { LERR( "HEP server is down... retrying after sleep...\n"); if(!usessl) { sleep(2); if(init_hepsocket_blocking()) { initfails++; } errors=0; } #ifdef USE_SSL else { sleep(2); if(initSSL()) { initfails++; } errors=0; } #endif /* USE SSL */ } /* send this packet out of our socket */ if(send_data(buffer, buflen)) { errors++; } /* FREE */ if(buffer) free(buffer); return 1; error: if(buffer) free(buffer); return 0; } int send_data (void *buf, unsigned int len) { /* send this packet out of our socket */ int r = 0; void * p = buf; int sentbytes = 0; if(!usessl) { /* size_t sendlen = send(sock, p, len, 0); if(sendlen == -1) { LDEBUG("send error\n"); return -1; } sendPacketsCount++; */ /* size_t sendlen = len < 1024 ? len : 1024; size_t remlen = len; const void *curpos = buf; LDEBUG("SENDING!!!!!!!!!!!\n"); while (remlen > 0) { ssize_t len = send(sock, curpos, sendlen, MSG_NOSIGNAL); if (len == -1) return -1; curpos += len; remlen -= len; sendlen = (remlen < 1024) ? remlen : 1024; } */ while (sentbytes < len){ if( (r = send(sock, p, len - sentbytes, MSG_NOSIGNAL )) == -1) { LERR("send error\n"); return -1; } if (r != len - sentbytes) LDEBUG("send:multiple calls: %d\n", r); sentbytes += r; p += r; } sendPacketsCount++; } #ifdef USE_SSL else { if(SSL_write(ssl, buf, len) < 0) { LERR("capture: couldn't re-init ssl socket\r\n"); return -1; } sendPacketsCount++; } #endif /* RESET ERRORS COUNTER */ return 0; } int unload_module(void) { LNOTICE("unloaded module core_hep\n"); LNOTICE("count sends:[%d]\n", sendPacketsCount); /* Close socket */ if(sock) close(sock); #ifdef USE_SSL if(ssl) SSL_free(ssl); if(ctx) SSL_CTX_free(ctx); #endif /* use SSL */ pthread_mutex_destroy(&lock); return 0; } void select_loop (void) { int n = 0; int initfails = 0; fd_set readfd; time_t prevtime = time(NULL); FD_ZERO(&readfd); FD_SET(sock, &readfd); while (1){ if (select(sock+1, &readfd, 0, 0, NULL) < 0){ LERR("select failed\n"); handler(1); } if (FD_ISSET(sock, &readfd)){ ioctl(sock, FIONREAD, &n); if (n == 0){ /* server disconnected*/ if(!usessl) { if(init_hepsocket()) initfails++; } #ifdef USE_SSL else { if(initSSL()) initfails++; } #endif /* USE_SSL */ if (initfails > 10) { time_t curtime = time (NULL); if (curtime - prevtime < 2){ pthread_mutex_lock(&lock); LERR( "HEP server is down... retrying after sleep...\n"); sleep(2); pthread_mutex_unlock(&lock); } initfails=0; prevtime = curtime; } } } } } int load_module(xml_node *config) { xml_node *modules; char *key, *value; //int s; /* READ CONFIG */ modules = config; while(1) { if(modules == NULL) break; modules = xml_get("param", modules, 1 ); if(modules->attr[0] != NULL && modules->attr[2] != NULL) { /* bad parser */ if(strncmp(modules->attr[2], "value", 5) || strncmp(modules->attr[0], "name", 4)) { LERR( "bad keys in the config\n"); goto next; } key = modules->attr[1]; value = modules->attr[3]; if(key == NULL || value == NULL) { LERR( "bad values in the config\n"); goto next; } if(!strncmp(key, "capture-host", 10)) capt_host = value; else if(!strncmp(key, "capture-port", 13)) capt_port = value; else if(!strncmp(key, "capture-proto", 14)) capt_proto = value; else if(!strncmp(key, "capture-password", 17)) capt_password = value; else if(!strncmp(key, "capture-id", 11)) capt_id = atoi(value); else if(!strncmp(key, "payload-compression", 19) && !strncmp(value, "true", 5)) pl_compress = 1; else if(!strncmp(key, "version", 7)) hep_version = atoi(value); } next: modules = modules->next; } #ifndef USE_ZLIB if(pl_compress) LDEBUG("The captagent has not compiled with zlib. Please reconfigure with --enable-compression\n"); #endif /* USE_ZLIB */ LNOTICE("Loaded core_hep\n"); hints->ai_flags = AI_NUMERICSERV; hints->ai_family = AF_UNSPEC; if(!strncmp(capt_proto, "udp", 3)) { hints->ai_socktype = SOCK_DGRAM; hints->ai_protocol = IPPROTO_UDP; } else if(!strncmp(capt_proto, "tcp", 3) || !strncmp(capt_proto, "ssl", 3)) { hints->ai_socktype = SOCK_STREAM; hints->ai_protocol = IPPROTO_TCP; /*TLS || SSL*/ if(!strncmp(capt_proto, "ssl", 3)) { #ifdef USE_SSL usessl = 1; /* init SSL library */ SSL_library_init(); #else LERR("The captagent has not compiled with ssl support. Please reconfigure with --enable-ssl\n"); #endif /* end USE_SSL */ } } else { LERR("Unsupported protocol\n"); return -1; } /* if ((s = getaddrinfo(capt_host, capt_port, hints, &ai)) != 0) { LERR( "capture: getaddrinfo: %s\n", gai_strerror(s)); return 2; } */ if(!usessl) { if(init_hepsocket_blocking()) { LERR("capture: couldn't init socket\r\n"); return 2; } } #ifdef USE_SSL else { if(initSSL()) { LERR("capture: couldn't init SSL socket\r\n"); handler(1); return 2; } // start select thread /* pthread_create(&call_thread, NULL, (void *)select_loop, NULL); if (pthread_mutex_init(&lock, NULL) != 0) { LERR("mutex init failed\n"); return 3; } */ } #endif /* use SSL */ sigPipe(); return 0; } int init_hepsocket (void) { struct timeval tv; socklen_t lon; long arg; fd_set myset; int valopt, res, ret = 0, s; if(sock) close(sock); if ((s = getaddrinfo(capt_host, capt_port, hints, &ai)) != 0) { LERR( "capture: getaddrinfo: %s\n", gai_strerror(s)); return 2; } if((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) { LERR("Sender socket creation failed: %s\n", strerror(errno)); return 1; } // Set non-blocking if((arg = fcntl(sock, F_GETFL, NULL)) < 0) { LERR( "Error fcntl(..., F_GETFL) (%s)\n", strerror(errno)); close(sock); return 1; } arg |= O_NONBLOCK; if( fcntl(sock, F_SETFL, arg) < 0) { LERR( "Error fcntl(..., F_SETFL) (%s)\n", strerror(errno)); close(sock); return 1; } if((res = connect(sock, ai->ai_addr, (socklen_t)(ai->ai_addrlen))) < 0) { if (errno == EINPROGRESS) { do { tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&myset); FD_SET(sock, &myset); res = select(sock + 1 , NULL, &myset, NULL, &tv); if (res < 0 && errno != EINTR) { LERR( "Error connecting %d - %s\n", errno, strerror(errno)); close(sock); ret = 1; break; } else if (res > 0) { // Socket selected for write lon = sizeof(int); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) { close(sock); LERR( "Error in getsockopt() %d - %s\n", errno, strerror(errno)); ret = 2; } // Check the value returned... if (valopt) { close(sock); LERR( "Error in delayed connection() %d - %s\n", valopt, strerror(valopt)); ret = 3; } break; } else { close(sock); LERR( "Timeout in select() - Cancelling!\n"); ret = 4; break; } } while (1); } } return ret; } int init_hepsocket_blocking (void) { int s; struct timeval tv; fd_set myset; if(sock) close(sock); if ((s = getaddrinfo(capt_host, capt_port, hints, &ai)) != 0) { LERR( "capture: getaddrinfo: %s\n", gai_strerror(s)); return 2; } if((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) { LERR("Sender socket creation failed: %s\n", strerror(errno)); return 1; } if (connect(sock, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == -1) { select(sock + 1 , NULL, &myset, NULL, &tv); if (errno != EINPROGRESS) { LERR("Sender socket creation failed: %s\n", strerror(errno)); return 1; } } return 0; } #ifdef USE_SSL SSL_CTX* initCTX(void) { SSL_METHOD *method; SSL_CTX *ctx; OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */ SSL_load_error_strings(); /* Bring in and register error messages */ /* we use SSLv3 OBSOLETE */ // method = SSLv3_client_method(); /* Create new client-method instance */ /* tls v1 */ method = TLSv1_method(); ctx = SSL_CTX_new(method); /* Create new context */ if ( ctx == NULL ) { ERR_print_errors_fp(stderr); abort(); } return ctx; } void showCerts(SSL* ssl) { X509 *cert; char *line; cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */ if ( cert != NULL ) { LERR("Server certificates:\n"); line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); LERR("Subject: %s\n", line); free(line); /* free the malloc'ed string */ line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); LERR("Issuer: %s\n", line); free(line); /* free the malloc'ed string */ X509_free(cert); /* free the malloc'ed certificate copy */ } else LERR("No certificates.\n"); } int initSSL(void) { long ctx_options; /* if(ssl) SSL_free(ssl); if(ctx) SSL_CTX_free(ctx); */ if(init_hepsocket_blocking()) { LERR("capture: couldn't init hep socket\r\n"); return 1; } ctx = initCTX(); /* workaround bug openssl */ ctx_options = SSL_OP_ALL; ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_SSLv3; SSL_CTX_set_options(ctx, ctx_options); /*extra*/ SSL_CTX_ctrl(ctx, BIO_C_SET_NBIO, 1, NULL); /* create new SSL connection state */ ssl = SSL_new(ctx); SSL_set_connect_state(ssl); /* attach socket */ SSL_set_fd(ssl, sock); /* attach the socket descriptor */ /* perform the connection */ if ( SSL_connect(ssl) == -1 ) { ERR_print_errors_fp(stderr); return 1; } showCerts(ssl); return 0; } #endif /* use SSL */ char *description(void) { LNOTICE("Loaded description\n"); char *description = "test description"; return description; } int statistic(char *buf) { snprintf(buf, 1024, "Statistic of CORE_HEP module:\r\nSend packets: [%i]\r\n", sendPacketsCount); return 1; } int handlerPipe(void) { LERR("SIGPIPE... trying to reconnect...\n"); return 1; } int sigPipe(void) { struct sigaction new_action; /* sigation structure */ new_action.sa_handler = handlerPipe; sigemptyset (&new_action.sa_mask); new_action.sa_flags = 0; if( sigaction (SIGPIPE, &new_action, NULL) == -1) { perror("Failed to set new Handle"); return -1; } } captagent-6.1.0.20/captagent/mod/proto_rtcp/000077500000000000000000000000001272354503300206305ustar00rootroot00000000000000captagent-6.1.0.20/captagent/mod/proto_rtcp/proto_rtcp.c000066400000000000000000000337251272354503300232010ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2014 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif /* __FAVOR_BSD */ #include #include #include #include #ifdef USE_IPV6 #include #endif /* USE_IPV6 */ #include #include "../../src/api.h" #include "../../src/log.h" #include "proto_rtcp.h" #include "../proto_uni/capthash.h" #include "rtcp_parser.h" static uint8_t rtcp_link_offset = 14; uint8_t hdr_offset = 0; pcap_t *sniffer_rtp; pthread_t rtp_thread; unsigned char* ethaddr = NULL; unsigned char* mplsaddr = NULL; /* Callback function that is passed to pcap_loop() */ void rtcpback_proto(u_char *useless, struct pcap_pkthdr *pkthdr, u_char *packet) { /* Pat Callahan's patch for MPLS */ memcpy(ðaddr, (packet + 12), 2); memcpy(&mplsaddr, (packet + 16), 2); if (ntohs((uint16_t)*(ðaddr)) == 0x8100) { if (ntohs((uint16_t)*(&mplsaddr)) == 0x8847) { hdr_offset = 8; } else { hdr_offset = 4; } } struct ip *ip4_pkt = (struct ip *) (packet + rtcp_link_offset + hdr_offset); #if USE_IPv6 struct ip6_hdr *ip6_pkt = (struct ip6_hdr*)(packet + rtcp_link_offset + ((ntohs((uint16_t)*(packet + 12)) == 0x8100)? 4: 0) ); #endif uint32_t ip_ver; uint8_t ip_proto = 0; uint32_t ip_hl = 0; uint32_t ip_off = 0; uint8_t fragmented = 0; uint16_t frag_offset = 0; uint32_t frag_id = 0; char ip_src[INET6_ADDRSTRLEN + 1], ip_dst[INET6_ADDRSTRLEN + 1]; unsigned char *data; uint32_t len = pkthdr->caplen; ip_ver = ip4_pkt->ip_v; switch (ip_ver) { case 4: { #if defined(AIX) #undef ip_hl ip_hl = ip4_pkt->ip_ff.ip_fhl * 4; #else ip_hl = ip4_pkt->ip_hl * 4; #endif ip_proto = ip4_pkt->ip_p; ip_off = ntohs(ip4_pkt->ip_off); fragmented = ip_off & (IP_MF | IP_OFFMASK); frag_offset = (fragmented) ? (ip_off & IP_OFFMASK) * 8 : 0; frag_id = ntohs(ip4_pkt->ip_id); inet_ntop(AF_INET, (const void *)&ip4_pkt->ip_src, ip_src, sizeof(ip_src)); inet_ntop(AF_INET, (const void *)&ip4_pkt->ip_dst, ip_dst, sizeof(ip_dst)); } break; #if USE_IPv6 case 6: { ip_hl = sizeof(struct ip6_hdr); ip_proto = ip6_pkt->ip6_nxt; if (ip_proto == IPPROTO_FRAGMENT) { struct ip6_frag *ip6_fraghdr; ip6_fraghdr = (struct ip6_frag *)((unsigned char *)(ip6_pkt) + ip_hl); ip_hl += sizeof(struct ip6_frag); ip_proto = ip6_fraghdr->ip6f_nxt; fragmented = 1; frag_offset = ntohs(ip6_fraghdr->ip6f_offlg & IP6F_OFF_MASK); frag_id = ntohl(ip6_fraghdr->ip6f_ident); } inet_ntop(AF_INET6, (const void *)&ip6_pkt->ip6_src, ip_src, sizeof(ip_src)); inet_ntop(AF_INET6, (const void *)&ip6_pkt->ip6_dst, ip_dst, sizeof(ip_dst)); } break; #endif } switch (ip_proto) { case IPPROTO_UDP: { struct udphdr *udp_pkt = (struct udphdr *)((unsigned char *)(ip4_pkt) + ip_hl); uint16_t udphdr_offset = (frag_offset) ? 0 : sizeof(*udp_pkt); data = (unsigned char *)(udp_pkt) + udphdr_offset; len -= rtcp_link_offset + ip_hl + udphdr_offset + hdr_offset; #if USE_IPv6 if (ip_ver == 6) len -= ntohs(ip6_pkt->ip6_plen); #endif if ((int32_t)len < 0) len = 0; dump_rtp_packet(pkthdr, packet, ip_proto, data, len, ip_src, ip_dst, ntohs(udp_pkt->uh_sport), ntohs(udp_pkt->uh_dport), 0, udphdr_offset, fragmented, frag_offset, frag_id, ip_ver); } break; default: break; } } int dump_rtp_packet(struct pcap_pkthdr *pkthdr, u_char *packet, uint8_t proto, unsigned char *data, uint32_t len, const char *ip_src, const char *ip_dst, uint16_t sport, uint16_t dport, uint8_t flags, uint16_t hdr_offset, uint8_t frag, uint16_t frag_offset, uint32_t frag_id, uint32_t ip_ver) { struct timeval tv; time_t curtime; char timebuffer[30]; rc_info_t *rcinfo = NULL; unsigned char *senddata; int json_len; gettimeofday(&tv,NULL); sendPacketsCount++; curtime = tv.tv_sec; strftime(timebuffer,30,"%m-%d-%Y %T.",localtime(&curtime)); if(len < 5) { LERR("rtcp the message is too small: %d\n", len); return -1; } LDEBUG("GOT RTCP %s:%d -> %s:%d. LEN: %d\n", ip_src, sport, ip_dst, dport, len); if(find_and_update(sip_callid, ip_src, sport, ip_dst, dport) == 0) { return 0; } if(rtcp_as_json) { json_rtcp_buffer[0] = '\0'; if((json_len = capt_parse_rtcp((char *)data, len, json_rtcp_buffer, sizeof(json_rtcp_buffer))) > 0) { senddata = json_rtcp_buffer; len = strlen(json_rtcp_buffer); } else { LDEBUG("GOODBYE or APP MESSAGE. Ignore!\n"); return 0; } LDEBUG("JSON RTCP %s\n", json_rtcp_buffer); } else senddata = data; rcinfo = malloc(sizeof(rc_info_t)); memset(rcinfo, 0, sizeof(rc_info_t)); LDEBUG("CALLID RTCP %s\n", sip_callid); rcinfo->src_port = sport; rcinfo->dst_port = dport; rcinfo->src_ip = ip_src; rcinfo->dst_ip = ip_dst; rcinfo->ip_family = ip_ver = 4 ? AF_INET : AF_INET6 ; rcinfo->ip_proto = proto; rcinfo->time_sec = pkthdr->ts.tv_sec; rcinfo->time_usec = pkthdr->ts.tv_usec; rcinfo->proto_type = rtcp_proto_type; /* correlation stuff */ rcinfo->correlation_id.len = strlen(sip_callid); rcinfo->correlation_id.s = &sip_callid; if(debug_proto_rtcp_enable) LDEBUG("SENDING PACKET: Len: [%d]\n", len); /* Duplcate */ if(!send_message(rcinfo, senddata, (unsigned int) len)) { LERR("Not duplicated\n"); } if(rcinfo) free(rcinfo); return 1; } void* rtp_collect( void* device ) { struct bpf_program filter; char errbuf[PCAP_ERRBUF_SIZE]; char *filter_expr; uint16_t snaplen = 65535, timeout = 100, len = 300, ret = 0; if(device) { if((sniffer_rtp = pcap_open_live((char *)device, snaplen, rtcp_promisc, timeout, errbuf)) == NULL) { LERR("Failed to open packet sniffer on %s: pcap_open_live(): %s\n", (char *)device, errbuf); return NULL; } } else { if((sniffer_rtp = pcap_open_offline(usefile, errbuf)) == NULL) { LERR("Failed to open packet sniffer rtp on %s: pcap_open_offline(): %s\n", usefile, errbuf); return NULL; } } len += (rtcp_portrange != NULL) ? strlen(rtcp_portrange) : 10; len += (rtcp_userfilter != NULL) ? strlen(rtcp_userfilter) : 0; filter_expr = malloc(sizeof(char) * len); ret += snprintf(filter_expr, len, RTCP_FILTER); /* FILTER */ if(rtcp_portrange != NULL) ret += snprintf(filter_expr+ret, (len - ret), "%s portrange %s ", ret ? " and": "", rtcp_portrange); /* CUSTOM FILTER */ if(rtcp_userfilter != NULL) ret += snprintf(filter_expr+ret, (len - ret), " %s", rtcp_userfilter); /* compile filter expression (global constant, see above) */ if (pcap_compile(sniffer_rtp, &filter, filter_expr, 1, 0) == -1) { LERR("Failed to compile filter \"%s\": %s\n", filter_expr, pcap_geterr(sniffer_rtp)); if(filter_expr) free(filter_expr); return NULL; } /* install filter on sniffer session */ if (pcap_setfilter(sniffer_rtp, &filter)) { LERR("Failed to install filter: %s\n", pcap_geterr(sniffer_rtp)); if(filter_expr) free(filter_expr); return NULL; } if(filter_expr) free(filter_expr); /* detect rtcp_link_offset. Thanks ngrep for this. */ switch(pcap_datalink(sniffer_rtp)) { case DLT_EN10MB: rtcp_link_offset = ETHHDR_SIZE; break; case DLT_IEEE802: rtcp_link_offset = TOKENRING_SIZE; break; case DLT_FDDI: rtcp_link_offset = FDDIHDR_SIZE; break; case DLT_SLIP: rtcp_link_offset = SLIPHDR_SIZE; break; case DLT_PPP: rtcp_link_offset = PPPHDR_SIZE; break; case DLT_LOOP: case DLT_NULL: rtcp_link_offset = LOOPHDR_SIZE; break; case DLT_RAW: rtcp_link_offset = RAWHDR_SIZE; break; case DLT_LINUX_SLL: rtcp_link_offset = ISDNHDR_SIZE; break; case DLT_IEEE802_11: rtcp_link_offset = IEEE80211HDR_SIZE; break; default: LERR( "fatal: unsupported interface type %u\n", pcap_datalink(sniffer_rtp)); exit(-1); } while (pcap_loop(sniffer_rtp, 0, (pcap_handler)rtcpback_proto, 0)); /* terminate from here */ handler(1); return NULL; } int unload_module(void) { LNOTICE("unloaded module proto_rtcp\n"); /* Close socket */ pcap_close(sniffer_rtp); return 0; } int load_module(xml_node *config) { char *dev = NULL, *usedev = NULL; char errbuf[PCAP_ERRBUF_SIZE]; xml_node *modules; char *key, *value = NULL; LNOTICE("Loaded proto_rtcp\n"); /* READ CONFIG */ modules = config; while(1) { if(modules == NULL) break; modules = xml_get("param", modules, 1 ); if(modules->attr[0] != NULL && modules->attr[2] != NULL) { /* bad parser */ if(strncmp(modules->attr[2], "value", 5) || strncmp(modules->attr[0], "name", 4)) { LERR( "bad keys in the config\n"); goto next; } key = modules->attr[1]; value = modules->attr[3]; if(key == NULL || value == NULL) { LERR( "bad values in the config\n"); goto next; } if(!strncmp(key, "dev", 3)) usedev = value; else if(!strncmp(key, "portrange", 9)) rtcp_portrange = value; else if(!strncmp(key, "promisc", 7) && !strncmp(value, "false", 5)) rtcp_promisc = 0; else if(!strncmp(key, "filter", 6)) rtcp_userfilter = value; else if(!strncmp(key, "rtcp-json", 9) && !strncmp(value, "false", 5) ) rtcp_as_json = 0; else if(!strncmp(key, "send-sdes", 9) && !strncmp(value, "false", 5) ) send_sdes = 0; else if(!strncmp(key, "vlan", 4) && !strncmp(value, "true", 4)) rtcp_vlan = 1; else if(!strncmp(key, "debug", 5) && !strncmp(value, "true", 4)) debug_proto_rtcp_enable = 1; } next: modules = modules->next; } /* DEV || FILE */ if(!usefile) { dev = usedev ? usedev : pcap_lookupdev(errbuf); if (!dev) { perror(errbuf); exit(-1); } } // start thread pthread_create(&rtp_thread, NULL, rtp_collect, (void *)dev); return 0; } char *description(void) { LNOTICE("Loaded description\n"); char *description = "test description"; return description; } int statistic(char *buf) { snprintf(buf, 1024, "Statistic of PROTO_RTCP module:\r\nSend packets: [%i]\r\n", sendPacketsCount); return 1; } captagent-6.1.0.20/captagent/mod/proto_uni/000077500000000000000000000000001272354503300204535ustar00rootroot00000000000000captagent-6.1.0.20/captagent/mod/proto_uni/proto_uni.c000066400000000000000000000662471272354503300226540ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif /* __FAVOR_BSD */ #include #include #include #include #ifdef USE_IPV6 #include #endif /* USE_IPV6 */ #include /* reasambling */ #include "ipreasm.h" #include "tcpreasm.h" #include "src/api.h" #include "src/log.h" #include "proto_uni.h" #include "sipparse.h" #include "captarray.h" #include "capthash.h" static uint8_t link_offset = 14; pcap_t *sniffer_proto; pthread_t call_thread; unsigned char* ethaddr = NULL; unsigned char* mplsaddr = NULL; /* Callback function that is passed to pcap_loop() */ void callback_proto(u_char *useless, struct pcap_pkthdr *pkthdr, u_char *packet) { uint8_t hdr_offset = 0; /* Pat Callahan's patch for MPLS */ memcpy(ðaddr, (packet + 12), 2); memcpy(&mplsaddr, (packet + 16), 2); if (ntohs((uint16_t)*(ðaddr)) == 0x8100) { if (ntohs((uint16_t)*(&mplsaddr)) == 0x8847) { hdr_offset = 8; } else { hdr_offset = 4; } } struct ip *ip4_pkt = (struct ip *) (packet + link_offset + hdr_offset); #if USE_IPv6 struct ip6_hdr *ip6_pkt = (struct ip6_hdr*)(packet + link_offset + ((ntohs((uint16_t)*(packet + 12)) == 0x8100)? 4: 0) ); #endif uint32_t ip_ver; uint8_t ip_proto = 0; uint32_t ip_hl = 0; uint32_t ip_off = 0; uint8_t fragmented = 0; uint16_t frag_offset = 0; uint32_t frag_id = 0; char ip_src[INET6_ADDRSTRLEN + 1], ip_dst[INET6_ADDRSTRLEN + 1]; unsigned char *data, *datatcp; u_char *pack = NULL; uint32_t len = pkthdr->caplen; uint8_t psh = 0; int ret = 0; if(debug_proto_uni_enable) LDEBUG("GOT Message: LEN:[%d]\n", len); if (reasm != NULL && reasm_enable) { unsigned new_len; u_char *new_p = malloc(len - link_offset - ((ntohs((uint16_t)*(packet + 12)) == 0x8100)? 4:0)); memcpy(new_p, ip4_pkt, len - link_offset - ((ntohs((uint16_t)*(packet + 12)) == 0x8100)? 4:0)); pack = reasm_ip_next(reasm, new_p, len - link_offset - ((ntohs((uint16_t)*(packet + 12)) == 0x8100)? 4:0), (reasm_time_t) 1000000UL * pkthdr->ts.tv_sec + pkthdr->ts.tv_usec, &new_len); if (pack == NULL) return; len = new_len + link_offset + ((ntohs((uint16_t)*(pack + 12)) == 0x8100)? 4:0); pkthdr->len = new_len; pkthdr->caplen = new_len; ip4_pkt = (struct ip *) pack; #if USE_IPv6 ip6_pkt = (struct ip6_hdr*)pack; #endif } ip_ver = ip4_pkt->ip_v; switch (ip_ver) { case 4: { #if defined(AIX) #undef ip_hl ip_hl = ip4_pkt->ip_ff.ip_fhl * 4; #else ip_hl = ip4_pkt->ip_hl * 4; #endif ip_proto = ip4_pkt->ip_p; ip_off = ntohs(ip4_pkt->ip_off); fragmented = ip_off & (IP_MF | IP_OFFMASK); frag_offset = (fragmented) ? (ip_off & IP_OFFMASK) * 8 : 0; frag_id = ntohs(ip4_pkt->ip_id); if(debug_proto_uni_enable) LDEBUG("Message IPV4: LEN:[%d]\n", len); inet_ntop(AF_INET, (const void *)&ip4_pkt->ip_src, ip_src, sizeof(ip_src)); inet_ntop(AF_INET, (const void *)&ip4_pkt->ip_dst, ip_dst, sizeof(ip_dst)); } break; #if USE_IPv6 case 6: { ip_hl = sizeof(struct ip6_hdr); ip_proto = ip6_pkt->ip6_nxt; if (ip_proto == IPPROTO_FRAGMENT) { struct ip6_frag *ip6_fraghdr; ip6_fraghdr = (struct ip6_frag *)((unsigned char *)(ip6_pkt) + ip_hl); ip_hl += sizeof(struct ip6_frag); ip_proto = ip6_fraghdr->ip6f_nxt; fragmented = 1; frag_offset = ntohs(ip6_fraghdr->ip6f_offlg & IP6F_OFF_MASK); frag_id = ntohl(ip6_fraghdr->ip6f_ident); } if(debug_proto_uni_enable) LDEBUG("Message IPV6: LEN:[%d]\n", len); inet_ntop(AF_INET6, (const void *)&ip6_pkt->ip6_src, ip_src, sizeof(ip_src)); inet_ntop(AF_INET6, (const void *)&ip6_pkt->ip6_dst, ip_dst, sizeof(ip_dst)); } break; #endif } switch (ip_proto) { case IPPROTO_TCP: { struct tcphdr *tcp_pkt = (struct tcphdr *)((unsigned char *)(ip4_pkt) + ip_hl); //uint16_t tcphdr_offset = (frag_offset) ? 0 : (tcp_pkt->th_off * 4); uint16_t tcphdr_offset = frag_offset ? 0 : (uint16_t) (tcp_pkt->th_off * 4); data = (unsigned char *)(tcp_pkt) + tcphdr_offset; len -= link_offset + ip_hl + tcphdr_offset + hdr_offset; #if USE_IPv6 if (ip_ver == 6) len -= ntohs(ip6_pkt->ip6_plen); #endif if ((int32_t)len < 0) len = 0; if(debug_proto_uni_enable) LDEBUG("TCP Message: LEN:[%d], [%.*s]\n", len, len, data); if(tcpreasm != NULL && tcpdefrag_enable && (len > 0) && (tcp_pkt->th_flags & TH_ACK)) { unsigned new_len; u_char *new_p_2 = malloc(len+10); memcpy(new_p_2, data, len); if((tcp_pkt->th_flags & TH_PUSH)) psh = 1; if(debug_proto_uni_enable) LDEBUG("DEFRAG TCP process: EN:[%d], LEN:[%d], ACK:[%d], PSH[%d]\n", tcpdefrag_enable, len, (tcp_pkt->th_flags & TH_ACK), psh); datatcp = tcpreasm_ip_next_tcp(tcpreasm, new_p_2, len , (tcpreasm_time_t) 1000000UL * pkthdr->ts.tv_sec + pkthdr->ts.tv_usec, &new_len, &ip4_pkt->ip_src, &ip4_pkt->ip_dst, ntohs(tcp_pkt->th_sport), ntohs(tcp_pkt->th_dport), psh); if (datatcp == NULL) return; len = new_len; if(debug_proto_uni_enable) LDEBUG("COMPLETE TCP DEFRAG: LEN[%d], PACKET:[%s]\n", len, datatcp); dump_proto_packet(pkthdr, packet, ip_proto, datatcp, len, ip_src, ip_dst, ntohs(tcp_pkt->th_sport), ntohs(tcp_pkt->th_dport), tcp_pkt->th_flags, tcphdr_offset, fragmented, frag_offset, frag_id, ip_ver); /* clear datatcp */ free(datatcp); } else { if(debug_proto_uni_enable) LDEBUG("NORMAL TCP PACKET: LEN[%d], ACK: [%d], PACKET: [%s]\n", len, (tcp_pkt->th_flags & TH_ACK), data); ret = dump_proto_packet(pkthdr, packet, ip_proto, data, len, ip_src, ip_dst, ntohs(tcp_pkt->th_sport), ntohs(tcp_pkt->th_dport), tcp_pkt->th_flags, tcphdr_offset, fragmented, frag_offset, frag_id, ip_ver); } } break; case IPPROTO_UDP: { struct udphdr *udp_pkt = (struct udphdr *)((unsigned char *)(ip4_pkt) + ip_hl); uint16_t udphdr_offset = (frag_offset) ? 0 : sizeof(*udp_pkt); data = (unsigned char *)(udp_pkt) + udphdr_offset; len -= link_offset + ip_hl + udphdr_offset + hdr_offset; if(debug_proto_uni_enable) LDEBUG("UDP Message: LEN:[%d] [.*s]\n", len, len, data); #if USE_IPv6 if (ip_ver == 6) len -= ntohs(ip6_pkt->ip6_plen); #endif if ((int32_t)len < 0) len = 0; ret = dump_proto_packet(pkthdr, packet, ip_proto, data, len, ip_src, ip_dst, ntohs(udp_pkt->uh_sport), ntohs(udp_pkt->uh_dport), 0, udphdr_offset, fragmented, frag_offset, frag_id, ip_ver); } break; default: break; } if(pack != NULL) free(pack); } int dump_proto_packet(struct pcap_pkthdr *pkthdr, u_char *packet, uint8_t proto, unsigned char *data, uint32_t len, const char *ip_src, const char *ip_dst, uint16_t sport, uint16_t dport, uint8_t flags, uint16_t hdr_offset, uint8_t frag, uint16_t frag_offset, uint32_t frag_id, uint32_t ip_ver) { struct timeval tv; time_t curtime; char timebuffer[30]; //rc_info_t *rcinfo = NULL; rc_info_t rcinfo; preparsed_sip_t psip; miprtcp_t *mp = NULL; int i = 0; char ipptmp[256]; uint32_t bytes_parsed = 0; uint32_t newlen; int skip_len = 0; int loop = 1; int count_loop = 0; gettimeofday(&tv,NULL); sendPacketsCount++; curtime = tv.tv_sec; strftime(timebuffer,30,"%m-%d-%Y %T.",localtime(&curtime)); if(len <= 172) { //LDEBUG("SIP the message is too small: %d\n", len); return -1; } /* SIP must have alpha */ if((proto_type == PROTO_SIP && !isalpha(data[0])) || !strncmp((char *)data, "HEP3", 4)) { return -1; } /* gingle XMPP */ else if(proto_type == PROTO_XMPP && memcmp(" 5) { LERR("TOO MANY LOOP LEN [%d] vs NEWLEN: [%"PRIu32"] vs SKIP: [%d] vs PARSED: [%"PRIu32"]\n", len, newlen, skip_len, bytes_parsed); LERR("PACKET [%s]\n", data); loop = 0; break; } /* we can have more SIP message in one buffer */ if(proto == IPPROTO_TCP && len > 1300) { if(light_parse_message((char*) data+skip_len, (len-skip_len), &bytes_parsed, &psip) == 1) { newlen = psip.len; } else newlen = len-skip_len; } //if (proto_type == PROTO_SIP && sip_method){ if (proto_type == PROTO_SIP && sip_parse == 1){ //if ((sip_method_not == 1) ? (!sip_is_method((const char*)data, len,sip_method+1)): (sip_is_method ((const char*) data, len,sip_method))){ //LDEBUG("method not matched\n"); //return -1; //} memset(&psip, 0, sizeof(struct preparsed_sip)); psip.mrp_size = 0; psip.has_sdp = 0; bytes_parsed = 0; //LDEBUG("MESSAGE: [%.*s]\n", len, data); if(parse_message((char*) data+skip_len, newlen, &bytes_parsed, &psip) == 1) { if(rtcp_tracking == 1 && psip.has_sdp == 1) { if(psip.mrp_size > 10) { LERR("Bad MRP size [%d]\n", psip.mrp_size); psip.mrp_size = 0; } for(i=0; i < psip.mrp_size; i++) { mp = &psip.mrp[i]; if(mp->media_ip.len > 0 && mp->media_ip.s) { if(mp->rtcp_port == 0 ) mp->rtcp_port = mp->media_port+1; if(mp->rtcp_ip.len == 0) { mp->rtcp_ip.len = mp->media_ip.len; mp->rtcp_ip.s = mp->media_ip.s; } if(mp->rtcp_ip.len > 0 && mp->rtcp_ip.s) { /* our correlation index */ snprintf(ipptmp,sizeof(ipptmp), "%.*s:%d", mp->rtcp_ip.len, mp->rtcp_ip.s, mp->rtcp_port); /* put data to hash */ if(!find_ipport(ipptmp)) { add_ipport(ipptmp, &psip.callid); add_timer(ipptmp); } } } } } } else { LDEBUG("Not Parsed\n"); } } //LDEBUG("SIP: [%.*s]\n", len, data); if(newlen <= 172) { //LDEBUG("SIP the message is too small: %d\n", len); loop = 0; return -1; } /* SIP must have alpha */ if((proto_type == PROTO_SIP && !isalpha((data+skip_len)[0])) || !strncmp((char *)(data+skip_len), "HEP3", 4)) { loop = 0; return -1; } rcinfo.src_port = sport; rcinfo.dst_port = dport; rcinfo.src_ip = ip_src; rcinfo.dst_ip = ip_dst; rcinfo.ip_family = ip_ver = 4 ? AF_INET : AF_INET6 ; rcinfo.ip_proto = proto; rcinfo.time_sec = pkthdr->ts.tv_sec; rcinfo.time_usec = pkthdr->ts.tv_usec; rcinfo.proto_type = proto_type; rcinfo.correlation_id.len = 0; rcinfo.correlation_id.s = NULL; if(debug_proto_uni_enable) LDEBUG("SENDING PACKET: Len: [%d]\n", newlen); if(validate_len == 1 && check_len_message(data+skip_len, (unsigned int) newlen) == 0) { LDEBUG("BAD LEN: [%d], SKIP: [%d]\n", newlen, skip_len); loop = 0; return -1; } if(validate_sip == 1 && check_sip_message(data+skip_len, (unsigned int) newlen) == 0) { LDEBUG("BAD SIP MESSAGE: Len: [%d], SKIP: [%d]\n", newlen, skip_len); loop = 0; return -1; } /* Duplcate */ if(send_enable) { if(!send_message(&rcinfo, data+skip_len, (unsigned int) newlen)) { LDEBUG("Not duplicated\n"); } } skip_len += newlen; if(skip_len >= len || newlen >= len || newlen == 0 || bytes_parsed == 0 ) { loop = 0; break; } } return 1; } void* proto_collect( void* device ) { struct bpf_program filter; char errbuf[PCAP_ERRBUF_SIZE]; char *filter_expr; uint16_t snaplen = 65535, timeout = 100, len = 200, ret = 0; if(device) { if((sniffer_proto = pcap_open_live((char *)device, snaplen, promisc, timeout, errbuf)) == NULL) { LERR("Failed to open packet sniffer on %s: pcap_open_live(): %s\n", (char *)device, errbuf); return NULL; } } else { if((sniffer_proto = pcap_open_offline(usefile, errbuf)) == NULL) { LERR("Failed to open packet sniffer on %s: pcap_open_offline(): %s\n", usefile, errbuf); return NULL; } } len += (portrange != NULL) ? strlen(portrange) : 10; len += (ip_proto != NULL) ? strlen(ip_proto) : 0; len += (userfilter != NULL) ? strlen(userfilter) : 0; len += (reasm_enable && buildin_reasm_filter) ? strlen(BPF_DEFRAGMENTION_FILTER) : 0; filter_expr = malloc(sizeof(char) * len); /* REASM */ if(reasm_enable && buildin_reasm_filter) ret += snprintf(filter_expr, len, BPF_DEFRAGMENTION_FILTER); /* FILTER VLAN */ if(vlan) { ret += snprintf(filter_expr+ret, (len - ret), ret ? " or (vlan " : "(vlan "); if(portrange != NULL) ret += snprintf(filter_expr+ret, (len - ret), "and portrange %s ) ", portrange); else if(port > 0) ret += snprintf(filter_expr+ret, (len - ret), "and port %d ) ", port); } else { /* FILTER */ if(portrange != NULL) ret += snprintf(filter_expr+ret, (len - ret), "%s portrange %s ", ret ? " or": "", portrange); else if(port > 0) ret += snprintf(filter_expr+ret, (len - ret), "%s port %d ", ret ? " or": "", port); } /* PROTO */ if(ip_proto != NULL) ret += snprintf(filter_expr+ret, (len - ret), "%s %s ", ret ? " and": "", ip_proto); /* CUSTOM FILTER */ if(userfilter != NULL) ret += snprintf(filter_expr+ret, (len - ret), " %s", userfilter); /* create filter string */ //((ip[6:2] & 0x3fff != 0)) LDEBUG("FILTER: [%s]\n", filter_expr); /* compile filter expression (global constant, see above) */ if (pcap_compile(sniffer_proto, &filter, filter_expr, 1, 0) == -1) { LERR("Failed to compile filter \"%s\": %s\n", filter_expr, pcap_geterr(sniffer_proto)); if(filter_expr) free(filter_expr); return NULL; } /* install filter on sniffer session */ if (pcap_setfilter(sniffer_proto, &filter)) { LERR("Failed to install filter: %s\n", pcap_geterr(sniffer_proto)); if(filter_expr) free(filter_expr); return NULL; } if(filter_expr) free(filter_expr); /* detect link_offset. Thanks ngrep for this. */ switch(pcap_datalink(sniffer_proto)) { case DLT_EN10MB: link_offset = ETHHDR_SIZE; break; case DLT_IEEE802: link_offset = TOKENRING_SIZE; break; case DLT_FDDI: link_offset = FDDIHDR_SIZE; break; case DLT_SLIP: link_offset = SLIPHDR_SIZE; break; case DLT_PPP: link_offset = PPPHDR_SIZE; break; case DLT_LOOP: case DLT_NULL: link_offset = LOOPHDR_SIZE; break; case DLT_RAW: link_offset = RAWHDR_SIZE; break; case DLT_LINUX_SLL: link_offset = ISDNHDR_SIZE; break; case DLT_IEEE802_11: link_offset = IEEE80211HDR_SIZE; break; default: LERR( "fatal: unsupported interface type %u\n", pcap_datalink(sniffer_proto)); exit(-1); } /* REASM */ if(reasm_enable) { reasm = reasm_ip_new (); reasm_ip_set_timeout (reasm, 30000000); } if(tcpdefrag_enable) { tcpreasm = tcpreasm_ip_new (); tcpreasm_ip_set_timeout (tcpreasm, 30000000); } while (pcap_loop(sniffer_proto, 0, (pcap_handler)callback_proto, 0)); /* terminate from here */ handler(1); return NULL; } int unload_module(void) { LNOTICE("unloaded module proto_uni\n"); if (reasm != NULL) reasm_ip_free(reasm); if (tcpreasm != NULL) tcpreasm_ip_free(tcpreasm); timer_loop_stop = 0; /* Close socket */ pcap_close(sniffer_proto); return 0; } int load_module(xml_node *config) { char *dev = NULL, *usedev = NULL; char errbuf[PCAP_ERRBUF_SIZE]; xml_node *modules; char *key, *value = NULL, *local_pt = NULL; LNOTICE("Loaded proto_uni\n"); /* READ CONFIG */ modules = config; while(1) { if(modules == NULL) break; modules = xml_get("param", modules, 1 ); if(modules->attr[0] != NULL && modules->attr[2] != NULL) { /* bad parser */ if(strncmp(modules->attr[2], "value", 5) || strncmp(modules->attr[0], "name", 4)) { LERR( "bad keys in the config\n"); goto next; } key = modules->attr[1]; value = modules->attr[3]; if(key == NULL || value == NULL) { LERR( "bad values in the config\n"); goto next; } if(!strncmp(key, "dev", 3)) usedev = value; else if(!strncmp(key, "ip-proto", 8)) ip_proto = value; else if(!strncmp(key, "proto-type", 10)) local_pt = value; else if(!strncmp(key, "portrange", 9)) portrange = value; else if(!strncmp(key, "promisc", 7) && !strncmp(value, "false", 5)) promisc = 0; else if(!strncmp(key, "validate-len", 12) && !strncmp(value, "true", 4)) validate_len = 1; else if(!strncmp(key, "validate-sip", 12) && !strncmp(value, "true", 4)) validate_sip = 1; else if(!strncmp(key, "expire-timer", 12)) { expire_timer_array = atoi(value); if(expire_timer_array <= 10) expire_timer_array = EXPIRE_TIMER_ARRAY; } else if(!strncmp(key, "expire-rtcp", 11)) { expire_hash_value = atoi(value); if(expire_hash_value <= 10) expire_hash_value = EXPIRE_RTCP_HASH; } else if(!strncmp(key, "filter", 6)) userfilter = value; else if(!strncmp(key, "port", 4)) port = atoi(value); else if(!strncmp(key, "sip-parse", 9) && !strncmp(value, "true", 4)) sip_parse = 1; else if(!strncmp(key, "rtcp-tracking", 13) && !strncmp(value, "true", 4)) rtcp_tracking = 1; else if(!strncmp(key, "vlan", 4) && !strncmp(value, "true", 4)) vlan = 1; else if(!strncmp(key, "reasm", 5) && !strncmp(value, "true", 4)) reasm_enable = 1; else if(!strncmp(key, "debug", 5) && !strncmp(value, "true", 4)) debug_proto_uni_enable = 1; else if(!strncmp(key, "buildin-reasm-filter", 20) && !strncmp(value, "true", 4)) buildin_reasm_filter = 1; else if(!strncmp(key, "tcpdefrag", 9) && !strncmp(value, "true", 4)) tcpdefrag_enable = 1; else if(!strncmp(key, "send-message", 9) && !strncmp(value, "false", 5)) send_enable = 0; else if (!strncmp(key, "sip_method", 10)) sip_method = value; } next: modules = modules->next; } /* DEV || FILE */ if(!usefile) { dev = usedev ? usedev : pcap_lookupdev(errbuf); if (!dev) { perror(errbuf); exit(-1); } } /* if(port == 0 && portrange == NULL) { LERR( "bad port or portranges in the config\n"); return -1; } */ /* CHECK PROTO */ if(!strncmp(local_pt, "sip", 3)) proto_type = PROTO_SIP; else if(!strncmp(local_pt, "xmpp", 4)) proto_type = PROTO_XMPP; else { LERR( "Unsupported protocol. Switched to SIP\n"); proto_type = PROTO_SIP; } /* check sip method */ if (proto_type == PROTO_SIP && sip_method ) { if (sip_method[0] == '!'){ sip_method_not = 1; } } /* start timer */ if(sip_parse && rtcp_tracking) timer_init (); // start thread pthread_create(&call_thread, NULL, proto_collect, (void *)dev); return 0; } char *description(void) { LNOTICE("Loaded description\n"); char *description = "test description"; return description; } int statistic(char *buf) { snprintf(buf, 1024, "Statistic of PROTO_UNI module:\r\nSend packets: [%i]\r\n", sendPacketsCount); return 1; } captagent-6.1.0.20/common.am000066400000000000000000000006651272354503300155260ustar00rootroot00000000000000DISTCLEANFILES = \ INSTALL \ Makefile \ Makefile.in \ configure \ config \ aclocal.m4 \ config.guess \ config.h.in \ config.h.in~ \ config.log \ config.status \ config.sub \ depcomp \ install-sh \ ltmain.sh \ missing \ capplan.tab.h \ capplan.tab.c \ capplan.c \ m4/libtool.m4 \ m4/lt~obsolete.m4 \ m4/ltoptions.m4 \ m4/ltsugar.m4 \ m4/ltversion.m4 \ ylwrap SUFFIXES = .c .h .y .l AM_YFLAGS = -d AM_LFLAGS = -i captagent-6.1.0.20/compile000077500000000000000000000162451272354503300152760ustar00rootroot00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2013 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, see . # 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 # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= 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'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= 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 $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= 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 's|^.*[\\/]||; s|^[a-zA-Z]:||; 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 test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || 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-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: captagent-6.1.0.20/conf/000077500000000000000000000000001272354503300146355ustar00rootroot00000000000000captagent-6.1.0.20/conf/captagent.xml.in000066400000000000000000000032031272354503300177300ustar00rootroot00000000000000 captagent-6.1.0.20/conf/captureplans/000077500000000000000000000000001272354503300173365ustar00rootroot00000000000000captagent-6.1.0.20/conf/captureplans/rtcp_capture_plan.cfg000066400000000000000000000011161272354503300235230ustar00rootroot00000000000000capture[pcap] { # here we can check source/destination IP/port, message size if(msg_check("size", "30")) { if(is_rtcp()) { #Only for redis! if(is_rtcp_exist()) { #Convert to JSON if needed. if(parse_rtcp_to_json()) { #Can be defined many profiles in transport_hep.xml if(!send_hep("hepsocket")) { clog("ERROR", "Error sending HEP!!!!"); } } else { clog("ERROR", "couldn't parse RTCP to json"); } } else { clog("ERROR", "Couldnot find this call"); } } else { clog("ERROR", "This is not RTCP"); } } drop; } captagent-6.1.0.20/conf/captureplans/rtcpxr_capture_plan.cfg000066400000000000000000000010151272354503300240730ustar00rootroot00000000000000capture[rtcpxr] { # here we can check source/destination IP/port, message size if(msg_check("size", "100")) { #Do parsing if(parse_full_sip()) { #check if our methos is PUBLISH if(sip_is_method() && sip_check("method","PUBLISH")) { send_reply("200", "OK"); #Can be defined many profiles in transport_hep.xml if(!send_hep_proto("hepsocket", "99")) { clog("ERROR", "Error sending HEP!!!!"); } } else { send_reply("503", "Server internal error"); } } } drop; } captagent-6.1.0.20/conf/captureplans/sip_capture_plan.cfg000066400000000000000000000013131272354503300233450ustar00rootroot00000000000000capture[pcap] { # here we can check source/destination IP/port, message size if(msg_check("size", "100")) { #Do parsing if(parse_sip()) { #Can be defined many profiles in transport_hep.xml if(!send_hep("hepsocket")) { clog("ERROR", "Error sending HEP!!!!"); } # if(sip_has_sdp()) # { # #Activate it for RTCP checks # if(!check_rtcp_ipport()) # { # clog("ERROR", "ALREADY EXIST"); # } # } #Duplicate all INVITEs to JSON transport # if(sip_is_method() && sip_check("method","INVITE")) { # #Can be defined many profiles in transport_json.xml # if(!send_json("jsonsocket")) { # clog("ERROR", "Error sending JSON!!!"); # } # } } } drop; } captagent-6.1.0.20/conf/database_hash.xml000066400000000000000000000005331272354503300201270ustar00rootroot00000000000000 captagent-6.1.0.20/conf/database_redis.xml000066400000000000000000000010541272354503300203110ustar00rootroot00000000000000 captagent-6.1.0.20/conf/protocol_rtcp.xml000066400000000000000000000005271272354503300202540ustar00rootroot00000000000000 captagent-6.1.0.20/conf/protocol_sip.xml000066400000000000000000000005771272354503300201040ustar00rootroot00000000000000 captagent-6.1.0.20/conf/socket_pcap.xml000066400000000000000000000022141272354503300176510ustar00rootroot00000000000000 portrange 5060-5091 portrange 5060-50000 captagent-6.1.0.20/conf/socket_raw.xml000066400000000000000000000007761272354503300175320ustar00rootroot00000000000000 udp and port 5060 captagent-6.1.0.20/conf/socket_rtcpxr.xml000066400000000000000000000010721272354503300202510ustar00rootroot00000000000000 captagent-6.1.0.20/conf/transport_hep.xml000066400000000000000000000011531272354503300202470ustar00rootroot00000000000000 captagent-6.1.0.20/conf/transport_json.xml000066400000000000000000000010761272354503300204500ustar00rootroot00000000000000 captagent-6.1.0.20/configure.ac000066400000000000000000000220751272354503300162040ustar00rootroot00000000000000AC_PREREQ(2.61) AC_INIT(captagent,6.1.0.20,support@sipcapture.org,,[http://www.sipcapture.org]) AC_COPYRIGHT("SIP Capture Solution") AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE(foreign tar-ustar) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_SRCDIR([src/captagent.c]) AC_MSG_CHECKING([whether to use compression]) AC_PREFIX_DEFAULT(/usr/local/captagent) if test "$prefix" = "NONE"; then prefix=$ac_default_prefix fi AS_AC_EXPAND(agent_config_dir, "$sysconfdir/captagent/") AS_AC_EXPAND(agent_plan_dir, "$sysconfdir/captagent/") AS_AC_EXPAND(module_dir, "$libdir/captagent/modules") AC_DEFINE_UNQUOTED(AGENT_PREFIX, ["$prefix"], [our system prefix]) AC_DEFINE_UNQUOTED(AGENT_CONFIG_DIR, ["$agent_config_dir"], [captagent config dir]) AC_DEFINE_UNQUOTED(AGENT_PLAN_DIR, ["$agent_plan_dir"], [capture plans dir]) AC_DEFINE_UNQUOTED(MODULE_DIR, ["$module_dir"], [directory that modules will be installed to]) enableCompression=no AC_ARG_ENABLE(compression, [ --enable-compression Enable compression support)], [ZLIB="$enableval"] enableCompression=yes, [ZLIB="no"] ) AC_MSG_RESULT([$ZLIB]) AC_SUBST([ZLIB]) AC_MSG_CHECKING([whether to use ssl]) enableSSL=no AC_ARG_ENABLE(ssl, [ --enable-ssl Enable SSL support], [SSL="$enableval"] enableSSL=yes, [SSL="no"] ) AC_MSG_RESULT([$SSL]) AC_SUBST([SSL]) useMysql=no AC_MSG_CHECKING([whether to use mysql]) AC_ARG_ENABLE(mysql, [ --enable-mysql Enable mysql support], [MYSQL="$enableval"] useMysql=yes, [MYSQL="no"] ) AC_MSG_RESULT([$MYSQL]) AC_SUBST([MYSQL]) usePCRE=no AC_MSG_CHECKING([whether to use pcre]) AC_ARG_ENABLE(pcre, [ --enable-pcre Enable pcre support], [PCRE="$enableval"] usePCRE=yes, [PCRE="no"] ) AC_MSG_RESULT([$PCRE]) AC_SUBST([PCRE]) useRedis=no AC_MSG_CHECKING([whether to use redis]) AC_ARG_ENABLE(redis, [ --enable-redis Enable redis support], [REDIS="$enableval"] useRedis=yes, [REDIS="no"] ) AC_MSG_RESULT([$REDIS]) AC_SUBST([REDIS]) useLIBUV=no AC_MSG_CHECKING([whether to use libuv]) AC_ARG_ENABLE(libuv, [ --enable-libuv Enable libuv support], [LIBUV="$enableval"] useLIBUV=yes, [LIBUV="no"] ) AC_MSG_RESULT([$LIBUV]) AC_SUBST([LIBUV]) enableExtraModules=no AC_ARG_ENABLE(extramodules, [ --enable-extramodules Enable extra modules], [EXTRAMODULES="$enableval"] enableExtraModules=yes, [EXTRAMODULES="no"] ) AC_MSG_RESULT([$EXTRAMODULES]) AC_SUBST([EXTRAMODULES]) CONFIG_CFLAGS="${CFLAGS}" CONFIG_LDFLAGS="${LDFLAGS}" MODULES='$$(grep -v "\#" $(captagent_builddir)/modules.list | sed -e "s|^.*/||" | sort | uniq )' AM_MAKEFLAGS='"OUR_MODULES=$(MODULESS)" `test -n "$(VERBOSE)" || echo -s`' AC_SUBST(OUR_MODS) #AC_ENABLE_SHARED(yes) #AC_ENABLE_STATIC(no) #AC_CANONICAL_SYSTEM #AM_INIT_AUTOMAKE() LT_INIT AC_CANONICAL_HOST case "${host}" in *-*-darwin*) AC_DEFINE([OS_DARWIN], [1], [Define to 1 if Operating System is Darwin]) AC_SUBST(OS_DARWIN, 1) ;; *-*-freebsd*) AC_DEFINE([OS_FREEBSD], [1], [Define to 1 if Operating System is FreeBSD]) AC_SUBST(OS_FREEBSD, 1) ;; *-*-linux*) AC_DEFINE([OS_LINUX], [1], [Define to 1 if Operating System is Linux]) AC_SUBST(OS_LINUX, 1) ;; *-*-netbsd*) AC_DEFINE([OS_NETBSD], [1], [Define to 1 if Operating System is NETBSD]) AC_SUBST(OS_NETBSD, 1) ;; *-*-solaris2*) AC_DEFINE([OS_SOLARIS], [1], [Define to 1 if Operating System is SOLARIS]) AC_SUBST(OS_SOLARIS, 1) ;; *) AC_MSG_RESULT([Unsupported operating system: ${host}]) ;; esac # Checks for programs AC_PROG_CC # AC_PROG_CC([gcc cc]) AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_LIBTOOL_DLOPEN AC_PROG_LIBTOOL AC_PROG_LEX if test "$LEX" != "flex"; then AC_MSG_ERROR([flex not found. Please install flex]) fi if test -z "`echo %%|$LEX -t|grep yypop_buffer_state`"; then AC_MSG_ERROR([flex missing yypop_buffer_state - upgrade to version 2.5.33 or later]) fi AC_PROG_YACC if test "$YACC" != "bison -y"; then AC_MSG_ERROR([bison not found. Please install bison]) fi # Checks for libraries. AC_CHECK_LIB(pthread, pthread_create, , [AC_MSG_ERROR([captagent requires but cannot find pthread])]) if test "$OS_LINUX" = 1 ; then AC_CHECK_LIB(dl, dlopen, , [AC_MSG_ERROR([captagent requires but cannot find libdl])]) fi AC_CHECK_LIB(expat, XML_ParserCreate, , [AC_MSG_ERROR([captagent requires but cannot find libexpat])]) AC_CHECK_LIB(pcap, pcap_open_live, ,[AC_CHECK_LIB(wpcap, pcap_open_live, ,[AC_MSG_ERROR([captagent requires but cannot find libpcap])])]) AC_CHECK_LIB(json, json_object_get,[ JSON_LIBS="-ljson" ],[ AC_CHECK_LIB(json-c, json_object_get,[ JSON_LIBS="-ljson-c" ],[ echo "ERROR: You need libjson to build CaptAgent API module."; echo " Verify that you have libjson.a or libjson.so installed"; echo " If it is in a different directory, try using"; echo " the LDFLAGS to set its proper path."; AC_MSG_ERROR([Fatal: libjson not found.])])]) AC_CHECK_LIB(fl, yywrap, [ FLEX_LIBS="-lfl" ] , [AC_MSG_ERROR([captagent requires but cannot find libfl])]) AC_SUBST(PTHREAD_LIBS) AC_SUBST(DL_LIBS) AC_SUBST(EXPAT_LIBS) AC_SUBST(PCAP_LIBS) AC_SUBST(JSON_LIBS) AC_SUBST(PCRE_LIBS) AC_SUBST(HIREDIS_LIBS) AC_SUBST(FLEX_LIBS) dnl dnl check for pcre library dnl # Checks for libpcre if test "$PCRE" = "yes"; then AC_CHECKING([for pcre Library and Header files]) AC_CHECK_HEADER([pcre.h], ,AC_MSG_ERROR([Could not find pcre headers !])) AC_CHECK_LIB([pcre], [pcre_compile], [PCRE_LIBS="-lpcre"], [AC_MSG_ERROR([libpcre required])]) AC_DEFINE(USE_PCRE, 1, [Use PCRE library]) AC_SUBST(PCRE_LIBS) fi dnl dnl check for compression library dnl if test "$ZLIB" = "yes"; then AC_CHECKING([for zip Library and Header files]) AC_CHECK_HEADER(zlib.h,,[AC_MSG_ERROR([zlib.h headers not found.])]) AC_CHECK_LIB(z, inflate, [ LIBS="${LIBS} -lz" ], [AC_MSG_ERROR([captagent requires but cannot find lz])]) AC_DEFINE(USE_ZLIB, 1, [Use ZIP library]) fi dnl dnl check for redis library dnl dnl dnl check for MYSQL library dnl if test "$MYSQL" = "yes"; then AC_CHECKING([for MYSQL Library and Header files]) AC_CHECK_HEADER([mysql/mysql.h], ,AC_MSG_ERROR([Could not find mysql headers !])) AC_CHECK_LIB(mysqlclient, mysql_init, [ MYSQL_LIBS="-lmysqlclient" ], [AC_MSG_ERROR([captagent requires but cannot find mysqlclient])]) AC_DEFINE(USE_MYSQL, 1, [Use MYSQL library]) AC_SUBST(MYSQL_LIBS) fi dnl dnl check for redis library dnl if test "$REDIS" = "yes"; then AC_CHECKING([for redis Library and Header files]) AC_CHECK_HEADER(hiredis/hiredis.h,,[AC_MSG_ERROR([hiredis/hiredis.h headers not found.])]) AC_CHECK_LIB(hiredis, redisCommand, [ HIREDIS_LIBS="-lhiredis" ], [AC_MSG_ERROR([captagent requires but cannot find lhiredis])]) AC_DEFINE(USE_REDIS, 1, [Use REDIS library]) AC_SUBST(HIREDIS_LIBS) fi dnl dnl check for extra modules dnl if test "$EXTRAMODULES" = "yes"; then AC_CHECKING([for extra modules files]) AC_DEFINE(HAVE_EXTRA_MODULES, 1, [We have extra modules]) fi dnl dnl check for libuv library dnl if test "$LIBUV" = "yes"; then AC_CHECKING([for LiBUV and Header files]) AC_CHECK_HEADER(uv.h,,[AC_MSG_ERROR([uv.h headers not found.])]) AC_CHECK_LIB(uv, uv_now, [ UV_LIBS="-luv" ], [AC_MSG_ERROR([captagent requires but cannot find libuv])]) AC_DEFINE(USE_LIBUV, 1, [Use lib UV]) AC_SUBST(UV_LIBS) fi dnl dnl check for OpenSSL-SSL library dnl if test "$SSL" = "yes"; then AC_CHECKING([for OpenSSL SSL Library and Header files]) AC_CHECK_HEADER(openssl/ssl.h,, [AC_MSG_ERROR([OpenSSL SSL headers not found.])]) AC_CHECK_LIB(ssl, SSL_accept, [ LIBS="${LIBS} -lssl" ], [AC_MSG_ERROR([captagent requires but cannot find ssl])]) AC_DEFINE(USE_SSL, 1, [Use OpenSSL SSL library]) fi # Checks for header files. AC_CHECK_HEADER(pcap.h,,[AC_MSG_ERROR([captagent cannot find pcap.h])]) AC_CHECK_HEADERS([json-c/json.h json/json.h json.h]) # check JSON AM_CONDITIONAL([HAVE_JSON_JSON_H],[test "$ac_cv_header_json_json_h" = 'yes']) AM_CONDITIONAL([HAVE_JSON_C_JSON_H],[test "$ac_cv_header_json_c_json_h" = 'yes']) AM_CONDITIONAL([HAVE_JSON_H],[test "$ac_cv_header_json_h" = 'yes']) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_INT32_T AC_TYPE_INT8_T AC_TYPE_PID_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_FORK #AC_FUNC_MALLOC AC_CHECK_FUNCS([gettimeofday memset select socket strdup strerror strndup]) AC_CONFIG_FILES([ Makefile captagent.spec include/Makefile src/Makefile conf/captagent.xml pkg/debian/captagent.init ]) m4_include([m4/modules_makefiles.m4]) AC_OUTPUT echo echo $PACKAGE $VERSION echo echo Build directory............. : $captagent_builddir echo Installation prefix......... : $prefix echo HEP Compression............. : $enableCompression echo HEP SSL/TLS................. : $enableSSL echo Flex........................ : ${LEX:-NONE} echo Bison....................... : ${YACC:-NONE} echo echo Build with REDIS............ : $useRedis echo Build with MySQL............ : $useMysql echo Build with PCRE............. : $usePCRE echo Build with LibUV............ : $useLIBUV echo captagent-6.1.0.20/include/000077500000000000000000000000001272354503300153335ustar00rootroot00000000000000captagent-6.1.0.20/include/Makefile.am000066400000000000000000000004411272354503300173660ustar00rootroot00000000000000include $(top_srcdir)/common.am noinst_HEADERS = \ captagent/api.h \ captagent/capture.h \ captagent/export_function.h \ captagent/globals.h \ captagent/log.h \ captagent/modules_api.h \ captagent/modules.h \ captagent/proto_sip.h \ captagent/structure.h \ captagent/xmlread.h captagent-6.1.0.20/include/captagent/000077500000000000000000000000001272354503300173015ustar00rootroot00000000000000captagent-6.1.0.20/include/captagent/action.h000066400000000000000000000011471272354503300207320ustar00rootroot00000000000000#ifndef action_h #define action_h struct action{ int type; /* forward, drop, log, send ...*/ int index; int p1_type; int p2_type; int p3_type; union { int number; char* string; void* data; }p1, p2, p3; struct action* next; }; struct run_act_ctx{ int rec_lev; int run_flags; int last_retcode; /* return from last route */ }; int do_action(struct run_act_ctx* c, struct action* a, msg_t *msg); int run_actions(struct run_act_ctx* c, struct action* a, msg_t* msg); #endif captagent-6.1.0.20/include/captagent/api.h000066400000000000000000000100471272354503300202250ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef API_H_ #define API_H_ #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #ifndef AGENT_CONFIG_DIR #define AGENT_CONFIG_DIR "/usr/local/etc/captagent/" #endif //DEF_CONF #ifndef AGENT_PLAN_DIR #define AGENT_PLAN_DIR "/usr/local/etc/captagent/captureplans" #endif #ifdef OS_LINUX #include #endif /* OS_LINUX */ typedef struct xml_node { char *key; char *value; char **attr; struct xml_node *child; struct xml_node *parent; struct xml_node *next; } xml_node; typedef struct _str { char* s; int len; } str; struct rc_info { uint8_t ip_family; /* IP family IPv6 IPv4 */ uint8_t ip_proto; /* IP protocol ID : tcp/udp */ uint8_t proto_type; /* SIP: 0x001, SDP: 0x03*/ char *src_mac; char *dst_mac; char *src_ip; char *dst_ip; uint16_t src_port; uint16_t dst_port; uint32_t time_sec; uint32_t time_usec; uint32_t liid; uint32_t cval1; uint32_t cval2; uint16_t sessionid; uint8_t direction; char *uuid; str correlation_id; int *socket; } ; typedef struct rc_info rc_info_t; typedef enum msg_body_type { MSG_BODY_UNKNOWN = 0, MSG_BODY_SDP } msg_body_type_t; typedef struct stats_object { unsigned int total_req; unsigned int curr_req; unsigned int total_x2; unsigned int failed_x2; unsigned long total_x3; unsigned long failed_x3; unsigned int curr_calls; unsigned int total_calls; } stats_object_t; extern struct stats_object stats_obj; struct hep_module *hepmod; extern int send_message (rc_info_t *rcinfo, unsigned char *data, unsigned int len); extern int get_basestat(char *module, char *stats, size_t len); struct module *module_list; typedef unsigned int bool; #ifndef TRUE #define TRUE 1 #endif /* TRUE */ #ifndef FALSE #define FALSE 0 #endif /* FALSE */ typedef enum { DB_INT, /* Integer number */ DB_DOUBLE, /* Decimal number */ DB_STRING, /* String */ DB_STR, /* str structure */ DB_DATETIME, /* Date and time */ DB_BLOB /* Binary large object */ } db_type_t; typedef struct db_value { str key; db_type_t type; /* Type of the value */ int nul; /* NULL flag */ union { int int_val; /* Integer value */ double double_val; /* Double value */ time_t time_val; /* Unix time_t value */ const char* string_val; /* Zero terminated string */ str str_val; /* str structure */ str blob_val; /* Structure describing blob */ } val; } db_value_t; #define SAFE_FREE(pt) \ assert(pt!=NULL); \ free(pt); \ pt = NULL; #define SAFE_PTR(pt) assert(pt!=NULL); pt #endif /* API_H_ */ captagent-6.1.0.20/include/captagent/capture.h000066400000000000000000000030231272354503300211130ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef CAPTURE_H_ #define CAPTURE_H_ struct capture_list{ struct action* clist[20]; int idx; int entries; char names[20][100]; }; #define FILTER_LEN 4080 /* our payload range between 0 - 191 */ #define RTP_FILTER "(ip and ip[6] & 0x2 = 0 and ip[6:2] & 0x1fff = 0 and udp and udp[8] & 0xc0 = 0x80 )" /* our payload range between 200 and 204 */ #define RTCP_FILTER "(ip and ip[6] & 0x2 = 0 and ip[6:2] & 0x1fff = 0 and udp and udp[8] & 0xc0 = 0x80 and udp[9] >= 0xc8 && udp[9] <= 0xcc)" #endif /* CAPTURE_H_ */ captagent-6.1.0.20/include/captagent/export_function.h000066400000000000000000000025101272354503300226760ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef EXPORT_FUNC_H_ #define EXPORT_FUNC_H_ /* new */ cmd_function find_export(char* name, int param_no, int flags); cmd_export_t* find_mod_export_record(char* mod, char* name, int param_no, int flags, unsigned* mod_if_ver); cmd_export_t* find_export_record(char* name, int param_no, int flags, unsigned* mod_if_ver); #endif /* EXPORT_FUNC_H_ */ captagent-6.1.0.20/include/captagent/globals.h000066400000000000000000000032371272354503300211020ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef GLOBALS_H_ #define GLOBALS_H_ #define CT_NO 10 /* capture tables number */ #define DEFAULT_CT 0 /* default capture table */ #ifndef NULL #define NULL ((void *)0) #endif extern int cfg_errors; extern int debug; extern int nofork; extern int foreground; extern int debug_level; extern char *usefile; extern char *global_license; extern char *global_chroot; extern char *global_config_path; extern char *global_capture_plan_path; extern char *global_uuid; extern char *backup_dir; extern char *global_node_name; extern int timestart; extern int serial; extern const char *captagent_config; extern struct capture_list main_ct; extern struct action* clist[20]; #endif /* GLOBALS_H_ */ captagent-6.1.0.20/include/captagent/log.h000066400000000000000000000040351272354503300202350ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef LOG_H_ #define LOG_H_ #include void init_log(char *_prgname, int _use_syslog); void set_log_level(int level); void destroy_log(void); void data_log(int priority, const char * fmt, ...); #define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))); #define LEMERG(fmt, args...) data_log(LOG_EMERG, "[DEBUG] %s:%d " fmt, __FILE__, __LINE__, ## args) #define LALERT(fmt, args...) data_log(LOG_ALERT, "[ALERT] %s:%d " fmt, __FILE__, __LINE__, ## args) #define LCRIT(fmt, args...) data_log(LOG_CRIT, "[CRIT] %s:%d " fmt, __FILE__, __LINE__, ## args) #define LERR(fmt, args...) data_log(LOG_ERR, "[ERR] %s:%d " fmt, __FILE__, __LINE__, ## args) #define LWARNING(fmt, args...) data_log(LOG_WARNING, "[WARNING] %s:%d " fmt, __FILE__, __LINE__, ## args) #define LNOTICE(fmt, args...) data_log(LOG_NOTICE, "[NOTICE] " fmt, ## args) #define LINFO(fmt, args...) data_log(LOG_INFO, "[INFO] %s:%d " fmt, __FILE__, __LINE__, ## args) #define LDEBUG(fmt, args...) data_log(LOG_DEBUG, "[DEBUG] %s:%d " fmt, __FILE__, __LINE__, ## args) #endif /* LOG_H_ */ captagent-6.1.0.20/include/captagent/md5.h000066400000000000000000000067501272354503300201470ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef MD5_H_ #define MD5_H_ /* * This an amalgamation of md5.c and md5.h into a single file * with all static declaration to reduce linker conflicts * in Civetweb. * * The MD5_STATIC declaration was added to facilitate static * inclusion. * No Face Press, LLC */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #endif /* MD5_H_ */ captagent-6.1.0.20/include/captagent/modules.h000066400000000000000000000062051272354503300211250ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef MODULES_H_ #define MODULES_H_ char *module_path; #define VAR_PARAM_NO -128 struct rc_info; typedef struct hep_module { int (*send_hep_basic)(struct rc_info *rcinfo, unsigned char *data, unsigned int len); int (*send_hep_advance)(void); } hep_module_t; typedef int (*init_function)(xml_node *config); typedef int (*destroy_function)(void); typedef int (*description_function)(char *descr); typedef int (*statistic_function)(char *stats, size_t len); typedef void (*onbreak_function)(msg_t* msg); typedef uint64_t (*serial_function)(void); typedef struct module { init_function load_f; destroy_function unload_f; description_function description_f; statistic_function stats_f; serial_function serial_f; onbreak_function onbreak_f; cmd_export_t* cmds; void *lib; char *path; char name[256]; struct module *next; } module_t; typedef struct module_exports { char* name; cmd_export_t* cmds; init_function load_f; destroy_function unload_f; description_function description_f; statistic_function stats_f; serial_function serial_f; onbreak_function onbreak_f; char** param_names; /* parameter names registered by this modules */ char** cmd_names; /* cmd names registered by this modules */ int cmd_no; /* number of registered commands */ int par_no; /* number of registered parameters */ int* param_no; /* number of parameters used*/ cmd_function* cmd_pointers; /* pointers to the corresponding functions */ modparam_t* param_types; /* Type of parameters */ void** param_pointers; /* Pointers to the corresponding memory locations */ } module_exports_t; int register_module(char *resource_name, xml_node *config, bool global); int register_modules(xml_node *tree); int unregister_modules(void); int usecount(void); /* How many channels provided by this module are in use? */ //char *description(void); /* Description of this module */ //int *statistic(char *stats, size_t len); /* Statistic of this module */ #endif /* MODULES_H_ */ captagent-6.1.0.20/include/captagent/modules_api.h000066400000000000000000000165361272354503300217660ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef MODULES_API_H_ #define MODULES_API_H_ typedef struct module_exports* (*module_register)(void); typedef int (*cmd_function)(msg_t*, char* param1, char* param2); typedef int (*fixup_function)(void** param, int param_no); typedef struct cmd_export_ { char* name; /**< null terminated command name */ cmd_function function; /**< pointer to the corresponding function */ int param_no; /**< number of parameters used by the function */ int flags; /**< Function flags */ int fixup_flags; void* module_exports; /**< pointer to module structure */ } cmd_export_t; typedef enum { STR_PARAM, /* String parameter type */ INT_PARAM, /* Integer parameter type */ } modparam_t; /* Allowed types of parameters */ /* modules API */ typedef int (*ul_set_keepalive_timeout_t)(int _to); typedef int (*parse_message_t)(msg_t *msg); typedef int (*parse_only_message_t)(msg_t *msg, void* packet); typedef int (*send_message_t)(msg_t *msg); typedef int (*send_stats_t)(stats_msg_t *stats_msg); typedef int (*reload_t)(char *erbuf, int len); typedef int (*apply_filter_t)(filter_msg_t *filter); typedef int (*update_db_t)(const db_msg_t *msg, const db_value_t* _v, const int _n); typedef int (*delete_db_t)(const db_msg_t *msg, const db_value_t* _v, const int _n); typedef int (*insert_db_t)(const db_msg_t *msg, const db_value_t* _v, const int _n); typedef int (*select_db_t)(const db_msg_t* msg, db_value_t* _v, const int _n); typedef int (*query_db_t)(char *query, const db_msg_t *msg, db_value_t* _v, const int _n); typedef int (*count_db_t)(char *query, const db_msg_t *msg); /* socket module API export structure */ typedef struct socket_module_api { int use_domain; /*! use_domain module parameter */ char *module_name; int db_mode; /*! db_mode module parameter */ unsigned int nat_flag; /*! nat_flag module parameter */ reload_t reload_f; apply_filter_t apply_filter_f; ul_set_keepalive_timeout_t set_keepalive_timeout; } socket_module_api_t; typedef struct protocol_module_api { int use_domain; /*! use_domain module parameter */ char *module_name; int db_mode; /*! db_mode module parameter */ unsigned int nat_flag; /*! nat_flag module parameter */ parse_message_t parse_f; parse_only_message_t parse_only_f; reload_t reload_f; ul_set_keepalive_timeout_t set_keepalive_timeout; } protocol_module_api_t; typedef struct transport_module_api { int use_domain; /*! use_domain module parameter */ char *module_name; int db_mode; /*! db_mode module parameter */ unsigned int nat_flag; /*! nat_flag module parameter */ send_message_t send_f; reload_t reload_f; ul_set_keepalive_timeout_t set_keepalive_timeout; } transport_module_api_t; typedef struct statistic_module_api { int use_domain; /*! use_domain module parameter */ char *module_name; int db_mode; /*! db_mode module parameter */ unsigned int nat_flag; /*! nat_flag module parameter */ send_stats_t send_stats_f; reload_t reload_f; } statistic_module_api_t; typedef struct database_module_api { int db_mode; /*! db_mode module parameter */ char *module_name; update_db_t update; delete_db_t delete; insert_db_t insert; select_db_t select; count_db_t count; query_db_t raw_query; reload_t reload_f; } database_module_api_t; typedef int (*bind_socket_module_api_t)(socket_module_api_t* api); typedef int (*bind_command_api_t)(msg_t *_m, char *param1, char *param2); typedef int (*bind_protocol_module_api_t)(protocol_module_api_t* api); typedef int (*bind_transport_module_api_t)(transport_module_api_t* api); typedef int (*bind_statistic_module_api_t)(statistic_module_api_t* api); typedef int (*bind_database_module_api_t)(database_module_api_t* api); #define MAX_FILTER_LEN 8000 #define MAX_API 10 /* profile socket */ typedef struct profile_socket { char *name; char *description; char *device; char *host; char *port; uint32_t serial; uint8_t reasm; uint8_t promisc; int socket; char *capture_plan; char *filter; int action; int protocol; char *capture_filter; uint32_t ring_buffer; uint32_t snap_len; uint32_t link_type; uint32_t timeout; uint8_t full_packet; struct profile_socket *next; void *reasm_t; } profile_socket_t; /* profile protocol */ typedef struct profile_protocol { char *name; char *description; uint32_t serial; uint16_t dialog_timeout; uint8_t dialog_type; uint8_t rtcp_tracking; uint8_t type; int action; char *ignore; struct profile_protocol *next; } profile_protocol_t; /* profile transport */ typedef struct profile_transport { char *name; char *description; int socket; unsigned int usessl; #ifdef USE_SSL SSL *ssl; SSL_CTX *ctx; #endif /* USE_SSL */ unsigned int initfails; int serial; int version; char *capt_host; char *capt_port; char *capt_proto; unsigned int capt_id; char *capt_password; int compression; char *statistic_pipe; char *statistic_profile; int action; struct profile_transport *next; unsigned int flag; } profile_transport_t; /* database profile */ typedef struct profile_database { char *name; char *description; int serial; int type; char *host; char *port; char *db_name; char *user; char *password; char *statistic_pipe; char *statistic_profile; struct profile_database *next; } profile_database_t; /* interface profile */ typedef struct profile_interface { char *name; char *description; int serial; int type; int server_type; char *server_host; char *server_port; char *remote_host; char *remote_port; int remote_timeout; int remote_ssl; int server_auth; char *server_realm; char *server_auth_file; char *server_worker; char *server_directory; char *server_index; char *database_pipe; char *statistic_pipe; char *database_profile; char *statistic_profile; struct profile_interface *next; } profile_interface_t; #endif /* MODULES_API_H_ */ captagent-6.1.0.20/include/captagent/proto_sip.h000066400000000000000000000120601272354503300214670ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef PROTO_SIP_H_ #define PROTO_SIP_H_ #define SIP_REQUEST 1 #define SIP_REPLY 2 #define INVITE_METHOD "INVITE" #define ACK_METHOD "ACK" #define CANCEL_METHOD "CANCEL" #define BYE_METHOD "BYE" #define INFO_METHOD "INFO" #define REGISTER_METHOD "REGISTER" #define SUBSCRIBE_METHOD "SUBSCRIBE" #define NOTIFY_METHOD "NOTIFY" #define MESSAGE_METHOD "MESSAGE" #define OPTIONS_METHOD "OPTIONS" #define PRACK_METHOD "PRACK" #define UPDATE_METHOD "UPDATE" #define REFER_METHOD "REFER" #define PUBLISH_METHOD "PUBLISH" #define NOTIFY_METHOD "NOTIFY" #define OPTIONS_METHOD "OPTIONS" #define ACK_METHOD "ACK" #define UNKNOWN_METHOD "UNKNOWN" #define RESPONSE_METHOD "RESPONSE" #define SERVICE_METHOD "SERVICE" #define SIP_VERSION "SIP/2.0" #define SIP_VERSION_LEN 7 #define INVITE_LEN 6 #define CANCEL_LEN 6 #define ACK_LEN 3 #define BYE_LEN 3 #define INFO_LEN 4 #define REGISTER_LEN 8 #define SUBSCRIBE_LEN 9 #define NOTIFY_LEN 6 #define MESSAGE_LEN 7 #define OPTIONS_LEN 7 #define PRACK_LEN 5 #define UPDATE_LEN 6 #define REFER_LEN 5 #define PUBLISH_LEN 7 #define UAC_LEN 10 #define RESPONSE_LEN 8 #define SERVICE_LEN 7 #define TO_LEN 2 #define PAI_LEN 19 #define FROM_LEN 4 #define EXPIRE_LEN 6 #define CALLID_LEN 7 #define CSEQ_LEN 4 #define VIA_LEN 3 #define PROXY_AUTH_LEN 19 #define WWW_AUTH_LEN 16 #define CONTACT_LEN 7 #define CONTENTLENGTH_LEN 14 #define CONTENTTYPE_LEN 12 #define USERAGENT_LEN 10 #define AUTHORIZATION_LEN 13 #define PPREFERREDIDENTITY_LEN 20 #define PASSERTEDIDENTITY_LEN 19 #define P_NGCP_CALLER_INFO_LEN 18 #define P_NGCP_CALLEE_INFO_LEN 18 #define XOIP_LEN 5 #define PRTPSTAT_LEN 10 #define XRTPSTAT_LEN 10 #define XRTPSTATISTICS_LEN 16 #define XSIEMENSRTPSTAT_LEN 19 #define XNGRTPSTAT_LEN 15 #define RTPRXTXSTAT_LEN 10 /* define for rtp stats type */ #define XRTPSTAT_TYPE 1 #define XRTPSTATISTICS_TYPE 2 #define PRTPSTAT_TYPE 3 #define RTPRXSTAT_TYPE 4 #define RTPTXSTAT_TYPE 5 #define XSIEMENSRTPSTATS_TYPE 6 #define XNGRTPSTATS_TYPE 7 #define MAX_MEDIA_HOSTS 20 #define RTCPXR_VQSESSIONREPORT_LEN 15 #define RTCPXR_CALLID_LEN 6 #define RTCPXR_SESSIONDESC_LEN 11 #define RTCPXR_JITTERBUFFER_LEN 12 #define RTCPXR_PACKETLOSS_LEN 10 #define RTCPXR_BURSTGAPLOSS_LEN 12 #define RTCPXR_DELAY_LEN 5 #define RTCPXR_QUALITYEST_LEN 10 #define CALL_CANCEL_TERMINATION 1 #define CALL_BYE_TERMINATION 2 #define CALL_MOVED_TERMINATION 3 #define CALL_BUSY_TERMINATION 4 #define CALL_AUTH_TERMINATION 5 #define CALL_4XX_TERMINATION 5 #define CALL_5XX_TERMINATION 6 #define CALL_6XX_TERMINATION 7 #define REGISTRATION_200_TERMINATION 1 #define REGISTRATION_AUTH_TERMINATION 2 #define REGISTRATION_4XX_TERMINATION 3 #define REGISTRATION_5XX_TERMINATION 4 #define REGISTRATION_6XX_TERMINATION 5 typedef enum { UNKNOWN = 0, CANCEL = 1, ACK = 2, INVITE = 3, BYE = 4, INFO = 5, REGISTER = 6, SUBSCRIBE = 7, NOTIFY = 8, MESSAGE = 9, OPTIONS = 10, PRACK = 11, UPDATE = 12, REFER = 13, PUBLISH = 14, RESPONSE = 15, SERVICE = 16 } method_t; typedef struct _miprtcp { str media_ip; int media_port; str rtcp_ip; int rtcp_port; int prio_codec; } miprtcp_t; struct _codecmap; typedef struct _codecmap { char name[120]; int id; int rate; struct _codecmap* next; } codecmap_t; typedef struct sip_msg { unsigned int responseCode; bool isRequest; method_t methodType; str methodString; int method_len; str callId; str reason; bool hasSdp; codecmap_t cdm[MAX_MEDIA_HOSTS]; miprtcp_t mrp[MAX_MEDIA_HOSTS]; int cdm_count; unsigned int mrp_size; unsigned int contentLength; unsigned int len; unsigned int cSeqNumber; bool hasVqRtcpXR; str rtcpxr_callid; str cSeqMethodString; method_t cSeqMethod; str cSeq; str via; str contactURI; /* extra */ str ruriUser; str ruriDomain; str fromUser; str fromDomain; str toUser; str toDomain; str paiUser; str paiDomain; str requestURI; str pidURI; bool hasPid; str fromURI; bool hasFrom; str toURI; bool hasTo; str ruriURI; bool hasRuri; str toTag; bool hasToTag; str fromTag; bool hasFromTag; } sip_msg_t; #endif /* PROTO_SIP_H_ */ captagent-6.1.0.20/include/captagent/structure.h000066400000000000000000000034341272354503300215160ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef STRUCTURE_H_ #define STRUCTURE_H_ #include "proto_sip.h" typedef struct msg { void *data; char *profile_name; uint32_t len; uint16_t hdr_len; uint8_t tcpflag; rc_info_t rcinfo; uint8_t parse_it; void *parsed_data; sip_msg_t sip; void *cap_packet; void *cap_header; void *var; char *corrdata; uint8_t mfree; int flag[10]; } msg_t; typedef struct stats_msg { char *mod_name; uint32_t value; } stats_msg_t; typedef struct filter_msg { char *data; uint32_t value; } filter_msg_t; typedef struct db_msg { str key_name; str profile_name; uint16_t expire; uint32_t len; uint8_t batch; } db_msg_t; #endif /* STRUCTURE_H_ */ captagent-6.1.0.20/include/captagent/xmlread.h000066400000000000000000000025011272354503300211040ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef XMLREAD_H_ #define XMLREAD_H_ xml_node *xml_parse( const char *filename ); int xml_parse_with_report(const char *filename, char *erbuf, int erlen); xml_node *xml_get( const char *key, xml_node *ref, int recurs ); xml_node *xml_node_str(char *str, int len); void xml_free(xml_node *node); #define BUFSIZE 8192 #endif /* XMLREAD_H_ */ captagent-6.1.0.20/init/000077500000000000000000000000001272354503300146535ustar00rootroot00000000000000captagent-6.1.0.20/init/deb/000077500000000000000000000000001272354503300154055ustar00rootroot00000000000000captagent-6.1.0.20/init/deb/debian/000077500000000000000000000000001272354503300166275ustar00rootroot00000000000000captagent-6.1.0.20/init/deb/debian/captagent.default000066400000000000000000000011551272354503300221450ustar00rootroot00000000000000# # Captagent startup options # # Set to yes to enable captagent, once configured properly. RUN_CAPTAGENT=yes # Config file CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml # Enable the server to leave a core file when it crashes. # Set this to 'yes' to enable Captagent to leave a core file when it crashes # or 'no' to disable this feature. This option is case sensitive and only # accepts 'yes' and 'no' and only in lowercase letters. # On some systems it is necessary to specify a directory for the core files # to get a dump. Look into the captagent init file for an example configuration. #DUMP_CORE=yes captagent-6.1.0.20/init/deb/debian/captagent.init000066400000000000000000000041331272354503300214630ustar00rootroot00000000000000#! /bin/sh # ### BEGIN INIT INFO # Provides: captagent # Required-Start: $syslog $network $local_fs $remote_fs $time # Should-Start: $named slapd mysql postgresql snmpd radiusd # Should-Stop: $named slapd mysql postgresql snmpd radiusd # Required-Stop: $syslog $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the Captagent # Description: Start the Captagent ### END INIT INFO . /lib/lsb/init-functions PATH=/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/local/captagent/bin/captagent NAME=`basename "$0"` DESC="Captagent" HOMEDIR=/var/run/$NAME PIDFILE=/var/run/$NAME.pid DEFAULTS=/etc/default/$NAME CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml RUN_CAPTAGENT=no USER=captagent GROUP=captagent DUMP_CORE=no test -f $DAEMON || exit 0 # Load startup options if available if [ -f $DEFAULTS ]; then . $DEFAULTS || true fi if [ "$RUN_CAPTAGENT" != "yes" ]; then log_failure_msg "Captagent not yet configured. Edit /etc/default/$NAME first." exit 0 fi set -e if test "$DUMP_CORE" = "yes" ; then # set proper ulimit ulimit -c unlimited # directory for the core dump files # COREDIR=/home/corefiles # [ -d $COREDIR ] || mkdir $COREDIR # chmod 777 $COREDIR # echo "$COREDIR/core.%e.sig%s.%p" > /proc/sys/kernel/core_pattern fi # /var/run can be a tmpfs if [ ! -d $HOMEDIR ]; then mkdir -p $HOMEDIR chown ${USER}:${GROUP} $HOMEDIR fi OPTIONS="-d -f $CFGFILE" case "$1" in start|debug) log_daemon_msg "Starting $DESC: $NAME" start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $DAEMON -- $OPTIONS || log_failure_msg " already running" log_end_msg 0 ;; stop) log_daemon_msg "Stopping $DESC: $NAME" start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \ --exec $DAEMON log_end_msg 0 ;; restart|force-reload) $0 stop sleep 1 $0 start ;; status) log_daemon_msg "Status of $DESC: " status_of_proc -p"$PIDFILE" $NAME $NAME ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload|status|debug}" >&2 exit 1 ;; esac exit 0 captagent-6.1.0.20/init/deb/debian/captagent.service000066400000000000000000000007331272354503300221620ustar00rootroot00000000000000[Unit] Description=Captagent - monitoring system After=network.target [Service] Type=forking Environment='CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml' EnvironmentFile=-/etc/default/captagent EnvironmentFile=-/etc/default/captagent.d/* # PIDFile requires a full absolute path PIDFile=/var/run/captagent.pid # ExecStart requires a full absolute path ExecStart=/usr/local/captagent/bin/captagent -f $CFGFILE -d Restart=always [Install] WantedBy=multi-user.target captagent-6.1.0.20/init/deb/jessie/000077500000000000000000000000001272354503300166675ustar00rootroot00000000000000captagent-6.1.0.20/init/deb/jessie/captagent.default000066400000000000000000000011551272354503300222050ustar00rootroot00000000000000# # Captagent startup options # # Set to yes to enable captagent, once configured properly. RUN_CAPTAGENT=yes # Config file CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml # Enable the server to leave a core file when it crashes. # Set this to 'yes' to enable Captagent to leave a core file when it crashes # or 'no' to disable this feature. This option is case sensitive and only # accepts 'yes' and 'no' and only in lowercase letters. # On some systems it is necessary to specify a directory for the core files # to get a dump. Look into the captagent init file for an example configuration. #DUMP_CORE=yes captagent-6.1.0.20/init/deb/jessie/captagent.init000066400000000000000000000041331272354503300215230ustar00rootroot00000000000000#! /bin/sh # ### BEGIN INIT INFO # Provides: captagent # Required-Start: $syslog $network $local_fs $remote_fs $time # Should-Start: $named slapd mysql postgresql snmpd radiusd # Should-Stop: $named slapd mysql postgresql snmpd radiusd # Required-Stop: $syslog $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the Captagent # Description: Start the Captagent ### END INIT INFO . /lib/lsb/init-functions PATH=/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/local/captagent/bin/captagent NAME=`basename "$0"` DESC="Captagent" HOMEDIR=/var/run/$NAME PIDFILE=/var/run/$NAME.pid DEFAULTS=/etc/default/$NAME CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml RUN_CAPTAGENT=no USER=captagent GROUP=captagent DUMP_CORE=no test -f $DAEMON || exit 0 # Load startup options if available if [ -f $DEFAULTS ]; then . $DEFAULTS || true fi if [ "$RUN_CAPTAGENT" != "yes" ]; then log_failure_msg "Captagent not yet configured. Edit /etc/default/$NAME first." exit 0 fi set -e if test "$DUMP_CORE" = "yes" ; then # set proper ulimit ulimit -c unlimited # directory for the core dump files # COREDIR=/home/corefiles # [ -d $COREDIR ] || mkdir $COREDIR # chmod 777 $COREDIR # echo "$COREDIR/core.%e.sig%s.%p" > /proc/sys/kernel/core_pattern fi # /var/run can be a tmpfs if [ ! -d $HOMEDIR ]; then mkdir -p $HOMEDIR chown ${USER}:${GROUP} $HOMEDIR fi OPTIONS="-d -f $CFGFILE" case "$1" in start|debug) log_daemon_msg "Starting $DESC: $NAME" start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $DAEMON -- $OPTIONS || log_failure_msg " already running" log_end_msg 0 ;; stop) log_daemon_msg "Stopping $DESC: $NAME" start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \ --exec $DAEMON log_end_msg 0 ;; restart|force-reload) $0 stop sleep 1 $0 start ;; status) log_daemon_msg "Status of $DESC: " status_of_proc -p"$PIDFILE" $NAME $NAME ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload|status|debug}" >&2 exit 1 ;; esac exit 0 captagent-6.1.0.20/init/deb/jessie/captagent.service000066400000000000000000000007331272354503300222220ustar00rootroot00000000000000[Unit] Description=Captagent - monitoring system After=network.target [Service] Type=forking Environment='CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml' EnvironmentFile=-/etc/default/captagent EnvironmentFile=-/etc/default/captagent.d/* # PIDFile requires a full absolute path PIDFile=/var/run/captagent.pid # ExecStart requires a full absolute path ExecStart=/usr/local/captagent/bin/captagent -f $CFGFILE -d Restart=always [Install] WantedBy=multi-user.target captagent-6.1.0.20/init/deb/squeeze/000077500000000000000000000000001272354503300170665ustar00rootroot00000000000000captagent-6.1.0.20/init/deb/squeeze/captagent.default000066400000000000000000000011551272354503300224040ustar00rootroot00000000000000# # Captagent startup options # # Set to yes to enable captagent, once configured properly. RUN_CAPTAGENT=yes # Config file CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml # Enable the server to leave a core file when it crashes. # Set this to 'yes' to enable Captagent to leave a core file when it crashes # or 'no' to disable this feature. This option is case sensitive and only # accepts 'yes' and 'no' and only in lowercase letters. # On some systems it is necessary to specify a directory for the core files # to get a dump. Look into the captagent init file for an example configuration. #DUMP_CORE=yes captagent-6.1.0.20/init/deb/squeeze/captagent.init000066400000000000000000000041331272354503300217220ustar00rootroot00000000000000#! /bin/sh # ### BEGIN INIT INFO # Provides: captagent # Required-Start: $syslog $network $local_fs $remote_fs $time # Should-Start: $named slapd mysql postgresql snmpd radiusd # Should-Stop: $named slapd mysql postgresql snmpd radiusd # Required-Stop: $syslog $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the Captagent # Description: Start the Captagent ### END INIT INFO . /lib/lsb/init-functions PATH=/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/local/captagent/bin/captagent NAME=`basename "$0"` DESC="Captagent" HOMEDIR=/var/run/$NAME PIDFILE=/var/run/$NAME.pid DEFAULTS=/etc/default/$NAME CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml RUN_CAPTAGENT=no USER=captagent GROUP=captagent DUMP_CORE=no test -f $DAEMON || exit 0 # Load startup options if available if [ -f $DEFAULTS ]; then . $DEFAULTS || true fi if [ "$RUN_CAPTAGENT" != "yes" ]; then log_failure_msg "Captagent not yet configured. Edit /etc/default/$NAME first." exit 0 fi set -e if test "$DUMP_CORE" = "yes" ; then # set proper ulimit ulimit -c unlimited # directory for the core dump files # COREDIR=/home/corefiles # [ -d $COREDIR ] || mkdir $COREDIR # chmod 777 $COREDIR # echo "$COREDIR/core.%e.sig%s.%p" > /proc/sys/kernel/core_pattern fi # /var/run can be a tmpfs if [ ! -d $HOMEDIR ]; then mkdir -p $HOMEDIR chown ${USER}:${GROUP} $HOMEDIR fi OPTIONS="-d -f $CFGFILE" case "$1" in start|debug) log_daemon_msg "Starting $DESC: $NAME" start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $DAEMON -- $OPTIONS || log_failure_msg " already running" log_end_msg 0 ;; stop) log_daemon_msg "Stopping $DESC: $NAME" start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \ --exec $DAEMON log_end_msg 0 ;; restart|force-reload) $0 stop sleep 1 $0 start ;; status) log_daemon_msg "Status of $DESC: " status_of_proc -p"$PIDFILE" $NAME $NAME ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload|status|debug}" >&2 exit 1 ;; esac exit 0 captagent-6.1.0.20/init/deb/wheezy/000077500000000000000000000000001272354503300167205ustar00rootroot00000000000000captagent-6.1.0.20/init/deb/wheezy/captagent.default000066400000000000000000000011551272354503300222360ustar00rootroot00000000000000# # Captagent startup options # # Set to yes to enable captagent, once configured properly. RUN_CAPTAGENT=yes # Config file CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml # Enable the server to leave a core file when it crashes. # Set this to 'yes' to enable Captagent to leave a core file when it crashes # or 'no' to disable this feature. This option is case sensitive and only # accepts 'yes' and 'no' and only in lowercase letters. # On some systems it is necessary to specify a directory for the core files # to get a dump. Look into the captagent init file for an example configuration. #DUMP_CORE=yes captagent-6.1.0.20/init/deb/wheezy/captagent.init000066400000000000000000000041331272354503300215540ustar00rootroot00000000000000#! /bin/sh # ### BEGIN INIT INFO # Provides: captagent # Required-Start: $syslog $network $local_fs $remote_fs $time # Should-Start: $named slapd mysql postgresql snmpd radiusd # Should-Stop: $named slapd mysql postgresql snmpd radiusd # Required-Stop: $syslog $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the Captagent # Description: Start the Captagent ### END INIT INFO . /lib/lsb/init-functions PATH=/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/local/captagent/bin/captagent NAME=`basename "$0"` DESC="Captagent" HOMEDIR=/var/run/$NAME PIDFILE=/var/run/$NAME.pid DEFAULTS=/etc/default/$NAME CFGFILE=/usr/local/captagent/etc/captagent/captagent.xml RUN_CAPTAGENT=no USER=captagent GROUP=captagent DUMP_CORE=no test -f $DAEMON || exit 0 # Load startup options if available if [ -f $DEFAULTS ]; then . $DEFAULTS || true fi if [ "$RUN_CAPTAGENT" != "yes" ]; then log_failure_msg "Captagent not yet configured. Edit /etc/default/$NAME first." exit 0 fi set -e if test "$DUMP_CORE" = "yes" ; then # set proper ulimit ulimit -c unlimited # directory for the core dump files # COREDIR=/home/corefiles # [ -d $COREDIR ] || mkdir $COREDIR # chmod 777 $COREDIR # echo "$COREDIR/core.%e.sig%s.%p" > /proc/sys/kernel/core_pattern fi # /var/run can be a tmpfs if [ ! -d $HOMEDIR ]; then mkdir -p $HOMEDIR chown ${USER}:${GROUP} $HOMEDIR fi OPTIONS="-d -f $CFGFILE" case "$1" in start|debug) log_daemon_msg "Starting $DESC: $NAME" start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $DAEMON -- $OPTIONS || log_failure_msg " already running" log_end_msg 0 ;; stop) log_daemon_msg "Stopping $DESC: $NAME" start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \ --exec $DAEMON log_end_msg 0 ;; restart|force-reload) $0 stop sleep 1 $0 start ;; status) log_daemon_msg "Status of $DESC: " status_of_proc -p"$PIDFILE" $NAME $NAME ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload|status|debug}" >&2 exit 1 ;; esac exit 0 captagent-6.1.0.20/init/el/000077500000000000000000000000001272354503300152535ustar00rootroot00000000000000captagent-6.1.0.20/init/el/6/000077500000000000000000000000001272354503300154205ustar00rootroot00000000000000captagent-6.1.0.20/init/el/6/captagent.init000066400000000000000000000037021272354503300202550ustar00rootroot00000000000000#!/bin/bash # # Startup script for captagent # # chkconfig: 345 85 15 # description: captagent - the Open Source Homer Capture Agent # # processname: captagent # pidfile: /var/run/captagent.pid # config: /usr/local/etc/captagent/captagent.xml # ### BEGIN INIT INFO # Provides: captagent # Required-Start: $local_fs $network # Short-Description: captagent - the Open Source Homer Capture Agent # Description: Homer captagent is an Open Source Capture Programm released # under GPLv3, able to handle thousands of call setups per second. ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions prog=captagent APP_FILE=/usr/local/captagent/bin/$prog PID_FILE=/var/run/$prog.pid LOCK_FILE=/var/lock/subsys/$prog RETVAL=0 [ -z "$CFG_FILE" ] && CFG_FILE=/usr/local/captagent/etc/captagent/captagent.xml OPTIONS="-f $CFG_FILE -d" [ -f /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog start() { if [ -e $PID_FILE ]; then echo "[FAILED] Captagent is already running with PID: `cat $PID_FILE`" else echo -n $"Starting $prog: " # there is something at end of this output which is needed to # report proper [ OK ] status in CentOS scripts $APP_FILE $OPTIONS && success || failure RETVAL=$? echo [ $RETVAL = 0 ] && touch $LOCK_FILE fi } stop() { echo -n $"Stopping $prog: " killproc $APP_FILE RETVAL=$? echo [ $RETVAL = 0 ] && rm -f $LOCK_FILE $PID_FILE } status() { if [ -e $PID_FILE ]; then echo "[OK] Captagent is running with PID: `cat $PID_FILE`" else echo "[FAILED] $PID_FILE does not exist" RETVAL=1 fi return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status ;; restart) stop start ;; condrestart) if [ -f $PID_FILE ] ; then stop start fi ;; *) echo $"Usage: $prog {start|stop|restart|condrestart|status|help}" exit 1 esac exit $RETVAL captagent-6.1.0.20/init/el/7/000077500000000000000000000000001272354503300154215ustar00rootroot00000000000000captagent-6.1.0.20/init/el/7/captagent.service000066400000000000000000000004701272354503300207520ustar00rootroot00000000000000[Unit] Description=SIP capture agent server daemon After=syslog.target network.target auditd.service [Service] EnvironmentFile=/etc/sysconfig/captagent ExecStart=/usr/local/captagent/bin/captagent -f $CFG_FILE -d KillMode=process Restart=Always RestartSec=42s Type=forking [Install] WantedBy=multi-user.target captagent-6.1.0.20/init/el/captagent.sysconfig000066400000000000000000000001321272354503300211430ustar00rootroot00000000000000# # captagent startup options # CFG_FILE=/usr/local/captagent/etc/captagent/captagent.xml captagent-6.1.0.20/init/freebsd/000077500000000000000000000000001272354503300162655ustar00rootroot00000000000000captagent-6.1.0.20/init/freebsd/captagent000066400000000000000000000012731272354503300201610ustar00rootroot00000000000000#!/bin/sh # PROVIDE: captagent # REQUIRE: DAEMON # KEYWORD: shutdown # # Add the following lines to /etc/rc.conf to enable captagent: # # captagent_enable="YES" # . /etc/rc.subr name="captagent" rcvar=`set_rcvar` load_rc_config $name : ${captagent_enable="NO"} : ${captagent_pidfile="/var/run/captagent.pid"} start_cmd=${name}_start stop_cmd=${name}_stop pidfile=${captagent_pidfile} captagent_start() { /usr/local/bin/captagent -d } captagent_stop() { kill `cat ${captagent_pidfile}` } #command="/usr/local/bin/captagent -P /var/run/captagent.pid" pidfile=${captagent_pidfile:-"/var/run/captagent.pid"} #captagent_enable=${captagent_enable:-"NO"} run_rc_command "$1" captagent-6.1.0.20/m4/000077500000000000000000000000001272354503300142305ustar00rootroot00000000000000captagent-6.1.0.20/m4/as-ac-expand.m4000066400000000000000000000025531272354503300167400ustar00rootroot00000000000000dnl as-ac-expand.m4 0.2.0 -*- autoconf -*- dnl autostars m4 macro for expanding directories using configure's prefix dnl (C) 2003, 2004, 2005 Thomas Vander Stichele dnl Copying and distribution of this file, with or without modification, dnl are permitted in any medium without royalty provided the copyright dnl notice and this notice are preserved. dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) dnl example: dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local AC_DEFUN([AS_AC_EXPAND], [ EXP_VAR=[$1] FROM_VAR=[$2] dnl first expand prefix and exec_prefix if necessary prefix_save=$prefix exec_prefix_save=$exec_prefix dnl if no prefix given, then use /usr/local, the default prefix if test "x$prefix" = "xNONE"; then prefix="$ac_default_prefix" fi dnl if no exec_prefix given, then use prefix if test "x$exec_prefix" = "xNONE"; then exec_prefix=$prefix fi full_var="$FROM_VAR" dnl loop until it doesn't change anymore while true; do new_full_var="`eval echo $full_var`" if test "x$new_full_var" = "x$full_var"; then break; fi full_var=$new_full_var done dnl clean up full_var=$new_full_var AC_SUBST([$1], "$full_var") dnl restore prefix and exec_prefix prefix=$prefix_save exec_prefix=$exec_prefix_save ]) captagent-6.1.0.20/m4/modules_makefiles.m4000066400000000000000000000010121272354503300201540ustar00rootroot00000000000000AC_CONFIG_FILES([ src/modules/database/hash/Makefile src/modules/protocol/sip/Makefile src/modules/protocol/sip/captureplan/Makefile src/modules/protocol/rtcp/Makefile src/modules/protocol/rtcp/captureplan/Makefile src/modules/socket/pcap/Makefile src/modules/socket/raw/Makefile src/modules/socket/rtcpxr/Makefile src/modules/socket/rtcpxr/captureplan/Makefile src/modules/transport/hep/Makefile src/modules/transport/json/Makefile src/modules/interface/http/Makefile src/modules/database/redis/Makefile ]) captagent-6.1.0.20/modules.am000066400000000000000000000004221272354503300156750ustar00rootroot00000000000000moddir = $(libdir)/@PACKAGE_NAME@/modules confdir = $(sysconfdir)/@PACKAGE_NAME@ AM_CPPFLAGS = ${AM_INCLUDES} -I$(top_srcdir)/include AM_CFLAGS = ${AM_CFLAGS} -I$(top_srcdir)/include CLEANFILES = *.la *.lo *.o *.so *.slo distclean-local: rm -rf .deps .libs Makefile.in captagent-6.1.0.20/pkg/000077500000000000000000000000001272354503300144715ustar00rootroot00000000000000captagent-6.1.0.20/pkg/debian/000077500000000000000000000000001272354503300157135ustar00rootroot00000000000000captagent-6.1.0.20/pkg/debian/captagent.cron.d.ex000066400000000000000000000002121272354503300213740ustar00rootroot00000000000000# # Regular cron jobs for the captagent package # #0 4 * * * root [ -x /usr/bin/captagent_maintenance ] && /usr/bin/captagent_maintenance captagent-6.1.0.20/pkg/debian/captagent.default.ex000066400000000000000000000003611272354503300216420ustar00rootroot00000000000000# Defaults for captagent initscript # sourced by /etc/init.d/captagent # installed at /etc/default/captagent by the maintainer scripts # # This is a POSIX shell fragment # # Additional options that are passed to the Daemon. DAEMON_OPTS="" captagent-6.1.0.20/pkg/debian/captagent.init.in000066400000000000000000000037061272354503300211610ustar00rootroot00000000000000#! /bin/sh # ### BEGIN INIT INFO # Provides: captagent # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Required-Start: $local_fs $network # Required-Stop: # Short-Description: captagent - the Open Source Homer Capture Agent # Description: Homer captagent is an Open Source Capture Programm released # under GPLv3, able to handle thousands of call setups per second. ### END INIT INFO cap=/usr/bin/captagent prog=captagent pidfile=/var/run/$prog.pid lockfile=/var/lock/$prog RETVAL=0 start() { if [ -e $pidfile ]; then echo "[FAILED] Captagent is already running with PID: `cat $pidfile`" else echo -n "Starting $prog: " # there is something at end of this output which is needed to # report proper [ OK ] status in CentOS scripts start-stop-daemon --start --quiet --pidfile $pidfile --exec $cap -- $OPTIONS || echo "Failed" RETVAL=$? echo [ $RETVAL = 0 ] && touch $lockfile fi } stop() { echo -n "Stopping $prog: " #killproc $cap if [ -e $pidfile ]; then start-stop-daemon --oknodo --stop --quiet --pidfile $pidfile --signal 9 || echo "Failed" RETVAL=$? echo [ $RETVAL = 0 ] && rm -f $lockfile $pidfile else RETVAL=1 echo [ $RETVAL = 1 ] && rm -f $lockfile $pidfile echo "[FAILED] Captagent is not running" fi } status() { RETVAL=0 if [ -e $pidfile ]; then echo "[OK] Captagent is running with PID: `cat $pidfile`" else echo "[FAILED] Captagent is not running" RETVAL=1 fi return $RETVAL } [ -z "$CONFIG" ] && CONFIG=@sysconfdir@/captagent/captagent.xml OPTIONS="-d -f $CONFIG" # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status ;; restart) stop start ;; condrestart) if [ -f $pidfile ] ; then stop start fi ;; *) echo $"Usage: $prog {start|stop|restart|condrestart|status|help}" exit 1 esac exit $RETVAL captagent-6.1.0.20/pkg/debian/changelog000066400000000000000000000005211272354503300175630ustar00rootroot00000000000000captagent (6.0.1) unstable; urgency=low * Fixed some DEB packaging issues -- Konstantin S. Vishnivetsky Fri, 18 Sep 2015 14:27:00 +0600 captagent (6.0.1) unstable; urgency=low * Initial DEB packaging -- Konstantin S. Vishnivetsky Wed, 16 Sep 2015 21:52:10 +0600 captagent-6.1.0.20/pkg/debian/compat000066400000000000000000000000021272354503300171110ustar00rootroot000000000000009 captagent-6.1.0.20/pkg/debian/control000066400000000000000000000020451272354503300173170ustar00rootroot00000000000000Source: captagent Section: comm Priority: optional Maintainer: Konstantin S. Vishnivetsky Build-Depends: debhelper (>= 8.0.0), debconf, autotools-dev, dh-autoreconf, bison, flex, libexpat-dev, libjson0-dev, libmysqlclient-dev, libpcap0.8-dev Standards-Version: 6.0.1 Homepage: http://sipcapture.org Vcs-Git: git://github.com/sipcapture/captagent/tree/captagent6 Package: captagent Architecture: linux-any Depends: ${shlibs:Depends}, ${misc:Depends}, libpcap0.8, libexpat1 Description: SIP capture server HOMER5 a robust, carrier-grade, scalable SIP Capture system and Monitoring Application with HEP/HEP2, IP Proto4 (IPIP) encapsulation & port mirroring/monitoring support right out of the box, ready to process & store insane amounts of signaling with instant search, end-to-end analysis and drill-down capabilities for ITSPs, VoIP Providers and Trunk Suppliers using SIP signaling # #Package: captagent-doc #Architecture: all #Description: documentation for captagent # captagent-6.1.0.20/pkg/debian/copyright000066400000000000000000001045131272354503300176520ustar00rootroot00000000000000 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 . captagent-6.1.0.20/pkg/debian/docs000066400000000000000000000000561272354503300165670ustar00rootroot00000000000000AUTHORS COPYING LICENSE NEWS README README.md captagent-6.1.0.20/pkg/debian/manpage.1.ex000066400000000000000000000032071272354503300200220ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" (C) Copyright 2015 Konstantin S. Vishnivetsky , .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH CAPTAGENT SECTION "September 16, 2015" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME captagent \- program to do something .SH SYNOPSIS .B captagent .RI [ options ] " files" ... .br .B bar .RI [ options ] " files" ... .SH DESCRIPTION This manual page documents briefly the .B captagent and .B bar commands. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. \fBcaptagent\fP is a program that... .SH OPTIONS These programs follow the usual GNU command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. For a complete description, see the Info files. .TP .B \-h, \-\-help Show summary of options. .TP .B \-v, \-\-version Show version of program. .SH SEE ALSO .BR bar (1), .BR baz (1). .br The programs are documented fully by .IR "The Rise and Fall of a Fooish Bar" , available via the Info system. captagent-6.1.0.20/pkg/debian/manpage.sgml.ex000066400000000000000000000111151272354503300206210ustar00rootroot00000000000000 manpage.1'. You may view the manual page with: `docbook-to-man manpage.sgml | nroff -man | less'. A typical entry in a Makefile or Makefile.am is: manpage.1: manpage.sgml docbook-to-man $< > $@ The docbook-to-man binary is found in the docbook-to-man package. Please remember that if you create the nroff version in one of the debian/rules file targets (such as build), you will need to include docbook-to-man in your Build-Depends control field. --> FIRSTNAME"> SURNAME"> September 16, 2015"> SECTION"> kvishnivetsky@sipcapture.org"> CAPTAGENT"> Debian"> GNU"> GPL"> ]>
&dhemail;
&dhfirstname; &dhsurname; 2003 &dhusername; &dhdate;
&dhucpackage; &dhsection; &dhpackage; program to do something &dhpackage; DESCRIPTION This manual page documents briefly the &dhpackage; and bar commands. This manual page was written for the &debian; distribution because the original program does not have a manual page. Instead, it has documentation in the &gnu; Info format; see below. &dhpackage; is a program that... OPTIONS These programs follow the usual &gnu; command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. For a complete description, see the Info files. Show summary of options. Show version of program. SEE ALSO bar (1), baz (1). The programs are documented fully by The Rise and Fall of a Fooish Bar available via the Info system. AUTHOR This manual page was written by &dhusername; &dhemail; for the &debian; system (and may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the &gnu; General Public License, Version 2 any later version published by the Free Software Foundation. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL.
captagent-6.1.0.20/pkg/debian/manpage.xml.ex000066400000000000000000000254401272354503300204650ustar00rootroot00000000000000 .
will be generated. You may view the manual page with: nroff -man .
| less'. A typical entry in a Makefile or Makefile.am is: DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/docbook-xsl/manpages/docbook.xsl XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" manpage.1: manpage.xml $(XP) $(DB2MAN) $< The xsltproc binary is found in the xsltproc package. The XSL files are in docbook-xsl. A description of the parameters you can use can be found in the docbook-xsl-doc-* packages. Please remember that if you create the nroff version in one of the debian/rules file targets (such as build), you will need to include xsltproc and docbook-xsl in your Build-Depends control field. Alternatively use the xmlto command/package. That will also automatically pull in xsltproc and docbook-xsl. Notes for using docbook2x: docbook2x-man does not automatically create the AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as ... . To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be found in the docbook-xsl-doc-html package. Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` General documentation about man-pages and man-page-formatting: man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ --> ]> &dhtitle; &dhpackage; &dhfirstname; &dhsurname; Wrote this manpage for the Debian system.
&dhemail;
2007 &dhusername; This manual page was written for the Debian system (and may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or (at your option) any later version published by the Free Software Foundation. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL.
&dhucpackage; &dhsection; &dhpackage; program to do something &dhpackage; this this that &dhpackage; DESCRIPTION This manual page documents briefly the &dhpackage; and bar commands. This manual page was written for the Debian distribution because the original program does not have a manual page. Instead, it has documentation in the GNU info 1 format; see below. &dhpackage; is a program that... OPTIONS The program follows the usual GNU command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. For a complete description, see the info 1 files. Does this and that. Show summary of options. Show version of program. FILES /etc/foo.conf The system-wide configuration file to control the behaviour of &dhpackage;. See foo.conf 5 for further details. ${HOME}/.foo.conf The per-user configuration file to control the behaviour of &dhpackage;. See foo.conf 5 for further details. ENVIRONMENT FOO_CONF If used, the defined file is used as configuration file (see also ). DIAGNOSTICS The following diagnostics may be issued on stderr: Bad configuration file. Exiting. The configuration file seems to contain a broken configuration line. Use the option, to get more info. &dhpackage; provides some return codes, that can be used in scripts: Code Diagnostic 0 Program exited successfully. 1 The configuration file seems to be broken. BUGS The program is currently limited to only work with the foobar library. The upstreams BTS can be found at . SEE ALSO bar 1 , baz 1 , foo.conf 5 The programs are documented fully by The Rise and Fall of a Fooish Bar available via the info 1 system.
captagent-6.1.0.20/pkg/debian/menu.ex000066400000000000000000000001711272354503300172140ustar00rootroot00000000000000?package(captagent):needs="X11|text|vc|wm" section="Applications/comm"\ title="captagent" command="/usr/bin/captagent" captagent-6.1.0.20/pkg/debian/postinst.ex000066400000000000000000000016771272354503300201470ustar00rootroot00000000000000#!/bin/sh # postinst script for captagent # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `configure' # * `abort-upgrade' # * `abort-remove' `in-favour' # # * `abort-remove' # * `abort-deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in configure) ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 captagent-6.1.0.20/pkg/debian/postrm.ex000066400000000000000000000016441272354503300176020ustar00rootroot00000000000000#!/bin/sh # postrm script for captagent # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `remove' # * `purge' # * `upgrade' # * `failed-upgrade' # * `abort-install' # * `abort-install' # * `abort-upgrade' # * `disappear' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) ;; *) echo "postrm called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 captagent-6.1.0.20/pkg/debian/preinst.ex000066400000000000000000000012641272354503300177400ustar00rootroot00000000000000#!/bin/sh # preinst script for captagent # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `install' # * `install' # * `upgrade' # * `abort-upgrade' # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in install|upgrade) ;; abort-upgrade) ;; *) echo "preinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 captagent-6.1.0.20/pkg/debian/prerm.ex000066400000000000000000000015571272354503300174060ustar00rootroot00000000000000#!/bin/sh # prerm script for captagent # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `remove' # * `upgrade' # * `failed-upgrade' # * `remove' `in-favour' # * `deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in remove|upgrade|deconfigure) ;; failed-upgrade) ;; *) echo "prerm called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 captagent-6.1.0.20/pkg/debian/rules000077500000000000000000000005711272354503300167760ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. export DH_VERBOSE=1 # This has to be exported to make some magic below work. export DH_OPTIONS build: autoreconf -if ./configure --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH) make binary-arch: dh_install %: dh $@ --with autotools-dev captagent-6.1.0.20/pkg/debian/source/000077500000000000000000000000001272354503300172135ustar00rootroot00000000000000captagent-6.1.0.20/pkg/debian/source/format000066400000000000000000000000151272354503300204220ustar00rootroot000000000000003.0 (native) captagent-6.1.0.20/pkg/debian/source/options000066400000000000000000000000251272354503300206260ustar00rootroot00000000000000compression = "gzip" captagent-6.1.0.20/pkg/debian/watch.ex000066400000000000000000000014311272354503300173560ustar00rootroot00000000000000# Example watch control file for uscan # Rename this file to "watch" and then you can run the "uscan" command # to check for upstream updates and more. # See uscan(1) for format # Compulsory line, this is a version 3 file version=3 # Uncomment to examine a Webpage # #http://www.example.com/downloads.php captagent-(.*)\.tar\.gz # Uncomment to examine a Webserver directory #http://www.example.com/pub/captagent-(.*)\.tar\.gz # Uncommment to examine a FTP server #ftp://ftp.example.com/pub/captagent-(.*)\.tar\.gz debian uupdate # Uncomment to find new files on sourceforge, for devscripts >= 2.9 # http://sf.net/captagent/captagent-(.*)\.tar\.gz # Uncomment to find new files on GooglePages # http://example.googlepages.com/foo.html captagent-(.*)\.tar\.gz captagent-6.1.0.20/pkg/el/000077500000000000000000000000001272354503300150715ustar00rootroot00000000000000captagent-6.1.0.20/pkg/el/6/000077500000000000000000000000001272354503300152365ustar00rootroot00000000000000captagent-6.1.0.20/pkg/el/6/captagent.init000066400000000000000000000036651272354503300201030ustar00rootroot00000000000000#!/bin/bash # # Startup script for captagent # # chkconfig: 345 85 15 # description: captagent - the Open Source Homer Capture Agent # # processname: captagent # pidfile: /var/run/captagent.pid # config: /usr/local/etc/captagent/captagent.xml # ### BEGIN INIT INFO # Provides: captagent # Required-Start: $local_fs $network # Short-Description: captagent - the Open Source Homer Capture Agent # Description: Homer captagent is an Open Source Capture Programm released # under GPLv3, able to handle thousands of call setups per second. ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions prog=captagent APP_FILE=/usr/local/bin/$prog PID_FILE=/var/run/$prog.pid LOCK_FILE=/var/lock/subsys/$prog RETVAL=0 [ -z "$CFG_FILE" ] && CFG_FILE=/usr/local/captagent/etc/captagent/captagent.xml OPTIONS="-f $CFG_FILE" [ -f /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog start() { if [ -e $PID_FILE ]; then echo "[FAILED] Captagent is already running with PID: `cat $PID_FILE`" else echo -n $"Starting $prog: " # there is something at end of this output which is needed to # report proper [ OK ] status in CentOS scripts $APP_FILE $OPTIONS && success || failure RETVAL=$? echo [ $RETVAL = 0 ] && touch $LOCK_FILE fi } stop() { echo -n $"Stopping $prog: " killproc $APP_FILE RETVAL=$? echo [ $RETVAL = 0 ] && rm -f $LOCK_FILE $PID_FILE } status() { if [ -e $PID_FILE ]; then echo "[OK] Captagent is running with PID: `cat $PID_FILE`" else echo "[FAILED] $PID_FILE does not exist" RETVAL=1 fi return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status ;; restart) stop start ;; condrestart) if [ -f $PID_FILE ] ; then stop start fi ;; *) echo $"Usage: $prog {start|stop|restart|condrestart|status|help}" exit 1 esac exit $RETVAL captagent-6.1.0.20/pkg/el/7/000077500000000000000000000000001272354503300152375ustar00rootroot00000000000000captagent-6.1.0.20/pkg/el/7/captagent.service000066400000000000000000000004531272354503300205710ustar00rootroot00000000000000[Unit] Description=SIP capture agent server daemon After=syslog.target network.target auditd.service [Service] EnvironmentFile=/etc/sysconfig/captagent ExecStart=/opt/sbin/sipcapture -f $CFG_FILE KillMode=process Restart=on-failure RestartSec=42s Type=forking [Install] WantedBy=multi-user.target captagent-6.1.0.20/pkg/el/captagent.sysconfig000066400000000000000000000001321272354503300207610ustar00rootroot00000000000000# # captagent startup options # CFG_FILE=/usr/local/captagent/etc/captagent/captagent.xml captagent-6.1.0.20/pkg/fpm/000077500000000000000000000000001272354503300152535ustar00rootroot00000000000000captagent-6.1.0.20/pkg/fpm/deb/000077500000000000000000000000001272354503300160055ustar00rootroot00000000000000captagent-6.1.0.20/pkg/fpm/deb/README.txt000066400000000000000000000002121272354503300174760ustar00rootroot00000000000000# Syntax docker run --rm -v $(pwd)/:/tmp/build -v $(pwd)/:/scripts --entrypoint=/scripts/builder.sh alanfranz/fwd-debian-jessie:latest captagent-6.1.0.20/pkg/fpm/deb/build.sh000077500000000000000000000014251272354503300174450ustar00rootroot00000000000000#!/bin/bash # CaptAgent 6 - Debian Builder export VERSION=$(date +%Y%m%d%H%M) export TMP_DIR=/tmp cd $TMP_DIR apt-get -y update && apt-get -y install git libexpat-dev libpcap-dev libjson0-dev libtool automake flex bison git clone https://github.com/sipcapture/captagent captagent cd captagent/ git checkout 6.1 ./build.sh ./configure make mkdir -p $TMP_DIR/captagent make DESTDIR=$TMP_DIR/captagent_install install export CODEVERSION=$(./src/captagent -v | cut -c10-) fpm -s dir -t deb -C $TMP_DIR/captagent_install --name captagent --version $CODEVERSION --iteration 1 --deb-no-default-config-files --depends libpcap,json-c,expat --description "captagent" . ls -alF *.deb cp -v *.deb ${TMP_DIR}/ # Clean up temp files cd $TMP_DIR; rm -rf ./captagent ./captagent_install echo "done!" captagent-6.1.0.20/pkg/fpm/rpm/000077500000000000000000000000001272354503300160515ustar00rootroot00000000000000captagent-6.1.0.20/pkg/fpm/rpm/README.txt000066400000000000000000000002041272354503300175430ustar00rootroot00000000000000# Syntax docker run --rm -v $(pwd)/:/tmp/build -v $(pwd)/:/scripts --entrypoint=/scripts/builder.sh alanfranz/fwd-centos-7:latest captagent-6.1.0.20/pkg/fpm/rpm/build.sh000077500000000000000000000013141272354503300175060ustar00rootroot00000000000000#!/bin/bash # CaptAgent 6 - CentOS Builder export VERSION=$(date +%Y%m%d%H%M) export TMP_DIR=/tmp cd $TMP_DIR yum -y install json-c-devel expat-devel libpcap-devel flex-devel automake libtool bison git clone https://github.com/sipcapture/captagent captagent cd captagent/ git checkout 6.1 ./build.sh ./configure make mkdir -p $TMP_DIR/captagent make DESTDIR=$TMP_DIR/captagent_install install export CODEVERSION=$(./src/captagent -v | cut -c10-) fpm -s dir -t rpm -C $TMP_DIR/captagent_install --name captagent --version $CODEVERSION --iteration 1 --depends json-c,expat,libpcap --description "captagent" . ls -alF *.rpm cp -v *.rpm ${TMP_DIR} cd $TMP_DIR; rm -rf ./captagent ./captagent-installer echo "done!" captagent-6.1.0.20/pkg/freebsd/000077500000000000000000000000001272354503300161035ustar00rootroot00000000000000captagent-6.1.0.20/pkg/freebsd/captagent000066400000000000000000000012731272354503300177770ustar00rootroot00000000000000#!/bin/sh # PROVIDE: captagent # REQUIRE: DAEMON # KEYWORD: shutdown # # Add the following lines to /etc/rc.conf to enable captagent: # # captagent_enable="YES" # . /etc/rc.subr name="captagent" rcvar=`set_rcvar` load_rc_config $name : ${captagent_enable="NO"} : ${captagent_pidfile="/var/run/captagent.pid"} start_cmd=${name}_start stop_cmd=${name}_stop pidfile=${captagent_pidfile} captagent_start() { /usr/local/bin/captagent -d } captagent_stop() { kill `cat ${captagent_pidfile}` } #command="/usr/local/bin/captagent -P /var/run/captagent.pid" pidfile=${captagent_pidfile:-"/var/run/captagent.pid"} #captagent_enable=${captagent_enable:-"NO"} run_rc_command "$1" captagent-6.1.0.20/src/000077500000000000000000000000001272354503300144775ustar00rootroot00000000000000captagent-6.1.0.20/src/Makefile.am000066400000000000000000000014531272354503300165360ustar00rootroot00000000000000include $(top_srcdir)/common.am SUBDIRS = \ . \ modules/socket/pcap \ modules/socket/raw \ modules/socket/rtcpxr \ modules/protocol/sip \ modules/protocol/rtcp \ modules/transport/hep \ modules/transport/json \ modules/database/hash \ modules/database/redis \ modules/interface/http bin_PROGRAMS = captagent AM_CFLAGS = -g -fPIC -rdynamic -I$(top_srcdir)/include AM_CPPFLAGS = -DSYSCONFDIR='"$(sysconfdir)"' -I$(top_srcdir)/include BUILT_SOURCES = capplan.tab.h noinst_HEADERS = md5.h captagent.h conf_function.h captagent_SOURCES = captagent.c conf_function.c log.c md5.c modules.c xmlread.c capplan.l capplan.tab.y captagent_LDADD = ${PTHREAD_LIBS} ${EXPAT_LIBS} ${DL_LIBS} ${FLEX_LIBS} captagentconfdir = $(sysconfdir)/$(bin_PROGRAMS) captagentconf_DATA = $(top_srcdir)/conf/$(bin_PROGRAMS).xml captagent-6.1.0.20/src/capplan.l000066400000000000000000000145631272354503300163030ustar00rootroot00000000000000/* * */ %{ #include "capplan.tab.h" #include #include #include #include #include #include #include "conf_function.h" #include #include #include /* states */ #define INITIAL_S 0 #define COMMENT_S 1 #define COMMENT_LN_S 2 #define STRING_S 3 static int comment_nest=0; static int state=0; static char* tstr=0; int line=1; int column=1; int startcolumn=1; char *capturename = 0; static char* addstr(char*, char**); static void count(); %} /* start conditions */ %x STRING1 STRING2 COMMENT COMMENT_LN /* action keywords */ FORWARD forward DROP "drop"|"break" SEND send LOG log ERROR error CAPTURE capture REPLY_CAPTURE reply_capture LEN_GT len_gt APPEND_BRANCH "append_branch" IF "if" ELSE "else" /* condition keywords */ METHOD method URI uri SRCIP src_ip DSTIP dst_ip MYSELF myself /* operators */ EQUAL = EQUAL_T == MATCH =~ NOT !|"not" AND "and"|"&&"|"&" OR "or"|"||"|"|" /* config vars. */ DEBUG debug FORK fork LOGSTDERROR log_stderror LISTEN listen ALIAS alias DNS dns REV_DNS rev_dns PORT port STAT statistics MAXBUFFER maxbuffer CHILDREN children CHECK_VIA check_via SYN_BRANCH syn_branch MEMLOG memlog SIP_WARNING sip_warning FIFO fifo FIFO_MODE fifo_mode SERVER_SIGNATURE server_signature REPLY_TO_VIA reply_to_via USER "user"|"uid" GROUP "group"|"gid" LOADMODULE loadmodule MODPARAM modparam /* values */ YES "yes"|"true"|"on"|"enable" NO "no"|"false"|"off"|"disable" LETTER [a-zA-Z] DIGIT [0-9] ALPHANUM {LETTER}|{DIGIT}|[_] NUMBER {DIGIT}+ ID {LETTER}{ALPHANUM}* HEX [0-9a-fA-F] HEX4 {HEX}{1,4} IPV6ADDR ({HEX4}":"){7}{HEX4}|({HEX4}":"){1,7}(":"{HEX4}){1,7}|":"(":"{HEX4}){1,7}|({HEX4}":"){1,7}":"|"::" QUOTES \" TICK \' SLASH "/" SEMICOLON ; RPAREN \) LPAREN \( LBRACE \{ RBRACE \} LBRACK \[ RBRACK \] COMMA "," DOT \. CR \n COM_LINE # COM_START "/\*" COM_END "\*/" EAT_ABLE [\ \t\b\r] %% {EAT_ABLE} { count(); } {FORWARD} {count(); yylval.strval=yytext; return FORWARD; } {DROP} { count(); yylval.strval=yytext; return DROP; } {SEND} { count(); yylval.strval=yytext; return SEND; } {CAPTURE} { count(); yylval.strval=yytext; return CAPTURE; } {IF} { count(); yylval.strval=yytext; return IF; } {ELSE} { count(); yylval.strval=yytext; return ELSE; } {METHOD} { count(); yylval.strval=yytext; return METHOD; } {DEBUG} { count(); yylval.strval=yytext; return DEBUG; } {EQUAL} { count(); return EQUAL; } {EQUAL_T} { count(); return EQUAL_T; } {MATCH} { count(); return MATCH; } {NOT} { count(); return NOT; } {AND} { count(); return AND; } {OR} { count(); return OR; } {IPV6ADDR} { count(); yylval.strval=yytext; return IPV6ADDR; } {NUMBER} { count(); yylval.intval=atoi(yytext);return NUMBER; } {YES} { count(); yylval.intval=1; return NUMBER; } {NO} { count(); yylval.intval=0; return NUMBER; } {COMMA} { count(); return COMMA; } {SEMICOLON} { count(); return SEMICOLON; } {RPAREN} { count(); return RPAREN; } {LPAREN} { count(); return LPAREN; } {LBRACE} { count(); return LBRACE; } {RBRACE} { count(); return RBRACE; } {LBRACK} { count(); return LBRACK; } {RBRACK} { count(); return RBRACK; } {SLASH} { count(); return SLASH; } {DOT} { count(); return DOT; } \\{CR} {count(); } /* eat the escaped CR */ {CR} { count();/* return CR;*/ } {QUOTES} { count(); state=STRING_S; BEGIN(STRING1); } {TICK} { count(); state=STRING_S; BEGIN(STRING2); } {QUOTES} { count(); state=INITIAL_S; BEGIN(INITIAL); yytext[yyleng-1]=0; yyleng--; addstr(yytext, &tstr); yylval.strval=tstr; tstr=0; return STRING; } {TICK} { count(); state=INITIAL_S; BEGIN(INITIAL); yytext[yyleng-1]=0; yyleng--; addstr(yytext, &tstr); yylval.strval=tstr; tstr=0; return STRING; } .|{EAT_ABLE}|{CR} { yymore(); } \\n { count(); yytext[yyleng-2]='\n';yytext[yyleng-1]=0; yyleng--; addstr(yytext, &tstr); } \\r { count(); yytext[yyleng-2]='\r';yytext[yyleng-1]=0; yyleng--; addstr(yytext, &tstr); } \\a { count(); yytext[yyleng-2]='\a';yytext[yyleng-1]=0; yyleng--; addstr(yytext, &tstr); } \\t { count(); yytext[yyleng-2]='\t';yytext[yyleng-1]=0; yyleng--; addstr(yytext, &tstr); } \\\\ { count(); yytext[yyleng-2]='\\';yytext[yyleng-1]=0; yyleng--; addstr(yytext, &tstr); } .|{EAT_ABLE}|{CR} { yymore(); } {COM_START} { count(); comment_nest++; state=COMMENT_S; BEGIN(COMMENT); } {COM_END} { count(); comment_nest--; if (comment_nest==0){ state=INITIAL_S; BEGIN(INITIAL); } } .|{EAT_ABLE}|{CR} { count(); }; {COM_LINE}.*{CR} { count(); } {ID} { count(); addstr(yytext, &tstr); yylval.strval=tstr; tstr=0; return ID; } <> { switch(state){ case STRING_S: printf( "ERROR: cfg. parser: unexpected EOF in" " unclosed string\n"); if (tstr) {free(tstr); tstr=0;} break; case COMMENT_S: printf( "ERROR: cfg. parser: unexpected EOF:" " %d comments open\n", comment_nest); break; case COMMENT_LN_S: printf( "ERROR: unexpected EOF:" "comment line open\n"); break; } return 0; } %% static char* addstr(char * src, char ** dest) { char *tmp; unsigned len1, len2; if (*dest==0){ *dest=strdup(src); }else{ len1=strlen(*dest); len2=strlen(src); tmp=malloc(len1+len2+1); if (tmp==0) goto error; memcpy(tmp, *dest, len1); memcpy(tmp+len1, src, len2); tmp[len1+len2]=0; free(*dest); *dest=tmp; } return *dest; error: printf("ERROR:lex:addstr: memory allocation error\n"); return 0; } static void count() { int i; startcolumn=column; for (i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include "conf_function.h" #include #include "config.h" extern int yylex(); void yyerror(char* s); char* tmp; void* f_tmp; static int i_tmp; extern char *capturename; %} %union { int intval; unsigned uval; char* strval; struct expr* expr; struct action* action; struct net* ipnet; } /* terminals */ /* keywords */ %token FORWARD %token SEND %token DROP %token IF %token ELSE %token METHOD %token CAPTURE /* config vars. */ %token DEBUG /* operators */ %nonassoc EQUAL %nonassoc EQUAL_T %nonassoc MATCH %left OR %left AND %left NOT /* values */ %token NUMBER %token ID %token STRING %token IPV6ADDR /* other */ %token COMMA %token SEMICOLON %token RPAREN %token LPAREN %token LBRACE %token RBRACE %token LBRACK %token RBRACK %token SLASH %token DOT %token CR %type exp exp_elem %type action actions cmd if_cmd stm %type capture_name; %% cfg: statements ; statements: statements statement {} | statement {} | statements error { yyerror(""); YYABORT;} ; statement: assign_stm | capture_stm | CR /* null statement*/ ; assign_stm: DEBUG EQUAL NUMBER { debug=$3; } | error EQUAL { yyerror("unknown config variable"); } ; capture_name: ID { capturename = $1; $$=$1; } | STRING { capturename = $1; $$=$1; } ; capture_stm: CAPTURE LBRACE actions RBRACE { push($3, &main_ct.clist[DEFAULT_CT]); } | CAPTURE LBRACK capture_name RBRACK LBRACE actions RBRACE { i_tmp=capture_get(&main_ct, $3); if (i_tmp==-1){ yyerror("internal error"); YYABORT; } if (main_ct.clist[i_tmp]){ yyerror("duplicate capture"); YYABORT; } push($6, &main_ct.clist[i_tmp]); } | CAPTURE error { yyerror("invalid capture statement"); } ; exp: exp AND exp { $$=mk_exp(AND_OP, $1, $3); } | exp OR exp { $$=mk_exp(OR_OP, $1, $3); } | NOT exp { $$=mk_exp(NOT_OP, $2, 0); } | LPAREN exp RPAREN { $$=$2; } | exp_elem { $$=$1; } ; exp_elem: METHOD EQUAL_T STRING {$$= mk_elem( EQUAL_OP, STRING_ST, METHOD_O, $3);} | METHOD EQUAL_T ID {$$ = mk_elem( EQUAL_OP, STRING_ST, METHOD_O, $3); } | METHOD EQUAL_T error { $$=0; yyerror("string expected"); } | METHOD MATCH STRING {$$ = mk_elem( MATCH_OP, STRING_ST, METHOD_O, $3); } | METHOD MATCH ID {$$ = mk_elem( MATCH_OP, STRING_ST, METHOD_O, $3); } | METHOD MATCH error { $$=0; yyerror("string expected"); } | METHOD error { $$=0; yyerror("invalid operator == or =~ expected");} | stm { $$=mk_elem( NO_OP, ACTIONS_ST, ACTION_O, $1 ); } | NUMBER {$$=mk_elem( NO_OP, NUMBER_ST, NUMBER_O, (void*)$1 ); } ; stm: cmd { $$=$1; } | LBRACE actions RBRACE { $$=$2; } ; actions: actions action {$$=append_action($1, $2); } | action {$$=$1;} | actions error { $$=0; yyerror("bad command"); } ; action: cmd SEMICOLON {$$=$1;} | if_cmd {$$=$1;} | SEMICOLON /* null action */ {$$=0;} | cmd error { $$=0; yyerror("bad command: missing ';'?"); } ; if_cmd: IF exp stm { $$=mk_action3( IF_T, EXPR_ST, ACTIONS_ST, NOSUBTYPE, $2, $3, 0); } | IF exp stm ELSE stm { $$=mk_action3( IF_T, EXPR_ST, ACTIONS_ST, ACTIONS_ST, $2, $3, $5); } ; cmd: SEND LPAREN STRING RPAREN { $$=mk_action( SEND_T, STRING_ST, NUMBER_ST, $3, 0); } | SEND LPAREN STRING COMMA NUMBER RPAREN {$$=mk_action( SEND_T, STRING_ST, NUMBER_ST, $3, (void*)$5); } | SEND error { $$=0; yyerror("missing '(' or ')' ?"); } | SEND LPAREN error RPAREN { $$=0; yyerror("bad send argument"); } | DROP LPAREN RPAREN {$$=mk_action(DROP_T,0, 0, 0, 0); } | DROP {$$=mk_action(DROP_T,0, 0, 0, 0); } | ID LPAREN RPAREN { f_tmp=(void*)find_export($1, 0, 0); if (f_tmp==0){ yyerror("unknown command, missing" " loadmodule?\n"); $$=0; }else{ $$=mk_action( MODULE_T, CMDF_ST, 0, f_tmp, 0 ); } } | ID LPAREN STRING RPAREN { f_tmp=(void*)find_export($1, 1, 0); if (f_tmp==0){ yyerror("unknown command, missing" " loadmodule?\n"); $$=0; }else{ $$=mk_action( MODULE_T, CMDF_ST, STRING_ST, f_tmp, $3 ); } } | ID LPAREN STRING COMMA STRING RPAREN { f_tmp=(void*)find_export($1, 2, 0); if (f_tmp==0){ yyerror("unknown command, missing" " loadmodule?\n"); $$=0; }else{ $$=mk_action3( MODULE_T, CMDF_ST, STRING_ST, STRING_ST, f_tmp, $3, $5 ); } } | ID LPAREN error RPAREN { $$=0; yyerror("bad arguments"); } | if_cmd { $$=$1; } ; %% extern int line; extern int column; extern int startcolumn; extern int cfg_errors; void yyerror(char* s) { printf( "parse error (%d,%d-%d): %s\n", line, startcolumn, column, s); cfg_errors++; } /* int main(int argc, char ** argv) { if (yyparse()!=0) fprintf(stderr, "parsing error\n"); } */ captagent-6.1.0.20/src/captagent.c000066400000000000000000000247471272354503300166270ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "md5.h" #include #include "captagent.h" #include "config.h" char *dupArgs[2]; char *server; xml_node *tree; int cfg_errors=0; int debug = 0; int nofork = 1; int foreground = 0; int debug_level = 1; char *usefile = NULL; char *global_license = NULL; char *global_chroot = NULL; char *global_config_path = NULL; char *global_node_name = NULL; char *global_capture_plan_path = NULL; char *global_uuid = NULL; char *backup_dir; int timestart; int serial; const char *captagent_config; struct capture_list main_ct; struct action* clist[20]; struct stats_object stats_obj; void handler(int value) { int terminating = 1; LDEBUG("The agent has been terminated"); unlink(pid_file); if (!unregister_modules()) { LDEBUG("modules unload"); } /* free variables */ if(module_path) free(module_path); if(pid_file) free(pid_file); if(global_license) free(global_license); if(global_uuid) free(global_uuid); if(global_chroot) free(global_chroot); if(global_config_path) free(global_config_path); if(global_node_name) free(global_node_name); if(global_capture_plan_path) free(global_capture_plan_path); destroy_log(); exit(0); } int send_message(rc_info_t *rcinfo, unsigned char *data, unsigned int len) { int res; if (hepmod->send_hep_basic) { if (!(res = hepmod->send_hep_basic(rcinfo, data, len))) { LERR("not send, returning %d", res); return -1; } } return 1; } int get_basestat(char *module, char *buf, size_t len) { char *res; int pos = 0, ret = 0; char stats[200]; struct module *m = NULL; m = module_list; while (m) { if (!strncmp(module, "all", 3)) { if (m->stats_f(stats, sizeof(stats))) { pos += snprintf(buf + pos, len - pos, "%s\r\n", stats); ret = 1; } } else { if (!strncmp(m->name, module, strlen(module))) { if (m->stats_f(stats, sizeof(stats))) { ret = snprintf(buf, len, "%s\r\n", stats); ret = 1; break; } } } m = m->next; } return ret; } int daemonize(int nofork) { FILE *pid_stream; pid_t pid; int p; struct sigaction new_action; if (!nofork) { if ((pid = fork()) < 0) { LERR("Cannot fork:%s", strerror(errno)); goto error; } else if (pid != 0) { exit(0); } } if (pid_file != 0) { if ((pid_stream = fopen(pid_file, "r")) != NULL) { if (fscanf(pid_stream, "%d", &p) < 0) { LERR("could not parse pid file %s", pid_file); } fclose(pid_stream); if (p == -1) { LERR( "pid file %s exists, but doesn't contain a valid" " pid number", pid_file); goto error; } if (kill((pid_t) p, 0) == 0 || errno == EPERM) { LERR("running process found in the pid file %s", pid_file); goto error; } else { LERR("pid file contains old pid, replacing pid"); } } pid = getpid(); if ((pid_stream = fopen(pid_file, "w")) == NULL) { LERR("unable to create pid file %s: %s", pid_file, strerror(errno)); goto error; } else { fprintf(pid_stream, "%i\n", (int) pid); fclose(pid_stream); } } /* sigation structure */ new_action.sa_handler = handler; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; if (sigaction(SIGINT, &new_action, NULL) == -1) { LERR("Failed to set new Handle"); return -1; } if (sigaction(SIGTERM, &new_action, NULL) == -1) { LERR("Failed to set new Handle"); return -1; } return 0; error: return -1; } void usage(int8_t e) { printf( "usage: captagent <-vh> <-f config>\n" " -h is help/usage\n" " -v is version information\n" " -f is the config file\n" " -D is use specified pcap file instead of a device from the config\n" " -c is checkout\n" " -d is daemon mode\n" " -n is foreground mode\n" " -K is hardware key of your system\n" ""); exit(e); } void print_hw() { char k[33]; if((ghk(k))) printf("HW: [%s]\n",k); else printf("error during key generation"); } int main(int argc, char *argv[]) { xml_node *next, *modules, *config, *sockets; const char **attr, **attr_mod; int i = 0, y = 0, c, checkout = 0; bool global = FALSE; int errout = 1; char *k; /* how much entries */ main_ct.entries = 0; main_ct.idx = -1; timestart = time(0); captagent_config = DEFAULT_CAPT_CONFIG; while ((c = getopt(argc, argv, "dcvhnEKf:D:")) != EOF) { switch (c) { case 'v': printf("version: %s\n", VERSION); exit(0); break; case 'f': captagent_config = optarg; break; case 'd': nofork = 0; break; case '?': case 'h': usage(0); break; case 'c': checkout = 1; break; case 'D': usefile = optarg; break; case 'E': errout = 0; break; case 'K': print_hw(); exit(0); break; case 'n': foreground = 1; break; default: abort(); } } set_log_level(5); init_log("captagent", 0); /* PATH */ module_path = MODULE_DIR; hepmod = malloc(sizeof(hep_module_t)); load_xml_config(); /*CORE CONFIG */ if (!(config = get_core_config("core", tree))) { LERR("Config for core has been not found"); } else { if (!core_config(config)) { LERR("Config for core found"); } } if (foreground) nofork = 1; if (daemonize(nofork) != 0) { LERR("Daemonize failed: %s", strerror(errno)); exit(-1); } /* do register modules */ register_modules(tree); free_xml_config(); LDEBUG("The Captagent is ready"); select(0, NULL, NULL, NULL, NULL); return EXIT_SUCCESS; } int load_xml_config() { if ((tree = xml_parse(captagent_config)) == NULL) { LERR("Unable to open configuration file: %s", captagent_config); exit(1); } return 1; } void free_xml_config() { /* now we are free */ if(tree) xml_free(tree); } xml_node *get_module_config_by_name(char *mod_name) { xml_node *config = NULL; load_xml_config(); if (!(config = get_module_config(mod_name, tree))) { LERR("CAP: Config for [%s] has been not found", mod_name); } return config; } xml_node *get_module_config(const char *mod_name, xml_node *mytree) { xml_node *next, *modules = NULL, *config; int i = 0; if (mytree == NULL) return modules; next = mytree; while (next) { next = xml_get("module", next, 1); if (next == NULL) break; for (i = 0; next->attr[i]; i++) { if (!strncmp(next->attr[i], "name", 4)) { if (!strncmp(next->attr[i + 1], mod_name, strlen(mod_name))) { modules = next; break; } } } next = next->next; } return modules; } xml_node *get_core_config(const char *mod_name, xml_node *mytree) { xml_node *next, *modules = NULL, *config; int ret = 0, i = 0; char cfg[128]; if (mytree == NULL) return modules; ret = snprintf(cfg, sizeof(cfg), "%s.conf", mod_name); next = mytree; while (next) { next = xml_get("configuration", next, 1); if (next == NULL) break; for (i = 0; next->attr[i]; i++) { if (!strncmp(next->attr[i], "name", 4)) { if (!strncmp(next->attr[i + 1], cfg, ret)) { modules = next; break; } } } next = next->next; } return modules; } int core_config(xml_node *config) { char *dev, *usedev = NULL; xml_node *modules; char *key, *value; int _use_syslog = 0; int mlen = 0; LNOTICE("Loaded core config"); if (config == NULL) { LERR("xml config is null"); } /* READ CONFIG */ modules = config; while (modules) { //if (modules == NULL) break; modules = xml_get("param", modules, 1); if (modules->attr[0] != NULL && modules->attr[2] != NULL) { /* bad parser */ if (strncmp(modules->attr[2], "value", 5) || strncmp(modules->attr[0], "name", 4)) { LERR("bad keys in the config"); goto next; } key = modules->attr[1]; value = modules->attr[3]; if (key == NULL || value == NULL) { LERR("bad values in the config"); goto next; } if (!strncmp(key, "debug", 5)) debug_level = atoi(value); else if (!strncmp(key, "serial", 6)) serial = atoi(value); else if (!strncmp(key, "daemon", 6) && !strncmp(value, "true", 5) && nofork == 1) nofork = 0; else if (!strncmp(key, "module_path", 11)) module_path = strdup(value); else if (!strncmp(key, "syslog", 6) && !strncmp(value, "true", 5)) _use_syslog = 1; else if (!strncmp(key, "pid_file", 8)) pid_file = strdup(value); else if (!strncmp(key, "license", 7)) global_license = strdup(value); else if (!strncmp(key, "uuid", 4)) global_uuid = strdup(value); else if (!strncmp(key, "chroot", 6)) global_chroot = strdup(value); else if (!strncmp(key, "config_path", 11)) global_config_path = strdup(value); else if (!strncmp(key, "node", 4)) global_node_name = strdup(value); else if (!strncmp(key, "capture_plans_path", 18)) global_capture_plan_path = strdup(value); else if (!strncmp(key, "backup", 6)) backup_dir = strdup(value); } next: modules = modules->next; } if(!global_node_name) { global_node_name = malloc(8); snprintf(global_node_name, 8, "default"); } if(!global_config_path) { global_config_path = strdup(AGENT_CONFIG_DIR); } if(!global_capture_plan_path) { global_capture_plan_path = strdup(AGENT_PLAN_DIR); } set_log_level(debug_level); if (_use_syslog == 0) { destroy_log(); init_log("captagent", 0); } return 1; } captagent-6.1.0.20/src/captagent.h000066400000000000000000000037521272354503300166250ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef CAPTAGENT_H_ #define CAPTAGENT_H_ #include "md5.h" #include "config.h" #include #define CAPTAGENT_VERSION "6.1.0" #define DEFAULT_CAPT_CONFIG AGENT_CONFIG_DIR "captagent.xml" #define DEFAULT_PIDFILE "/var/run/captagent.pid" #define MAX_STATS 3000 /* sender socket */ int sock; char* pid_file = DEFAULT_PIDFILE; xml_node *get_core_config( const char *mod_name, xml_node *mytree); xml_node *get_module_config( const char *mod_name, xml_node *mytree); int load_xml_config(); void free_xml_config(); xml_node *get_module_config_by_name(char *mod_name); int core_config (xml_node *config); void print_hw(); static inline int ghk(char *_0){unsigned aO=0;FILE *f;char _1[50];md5_byte_t h[33];md5_state_t c;f=fopen("/sys/class/dmi/id/product_uuid","r");if(f==NULL) return 0;fgets(_1, 37, f);fclose(f);aO=strlen(_1); _1[aO]='\0';md5_init(&c);md5_append(&c,(const md5_byte_t*)_1,aO-1);md5_finish(&c,h);for(aO=0;aO<16;aO++)sprintf(_0+(aO*2),"%02X",(unsigned int)h[aO]);return 1;} #endif /* CAPTAGENT_H_ */ captagent-6.1.0.20/src/conf_function.c000066400000000000000000000367051272354503300175100ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "md5.h" #include #include #include #include "conf_function.h" #define E_UNSPEC -1 #define MAX_REC_LEV 100 /* maximum number of recursive calls */ #define ROUTE_MAX_REC_LEV 10 /* maximum number of recursive calls for capture()*/ /* ret= 0! if action -> end of list(e.g DROP), > 0 to continue processing next actions and <0 on error */ int do_action(struct run_act_ctx* h, struct action* a, msg_t* msg) { int ret = 0; int v; union sockaddr_union* to; struct socket_info* send_sock; struct proxy_l* p; char* tmp; char *new_uri, *end, *crt; int len; int user; unsigned short port; /* reset the value of error to E_UNSPEC so avoid unknowledgable functions to return with errror (status<0) and not setting it leaving there previous error; cache the previous value though for functions which want to process it */ ret = 1; switch (a->type){ case DROP_T: ret=0; break; case IF_T: /* if null expr => ignore if? */ if ((a->p1_type==EXPR_ST)&&a->p1.data){ v=eval_expr(h, (struct expr*)a->p1.data, msg); if (v<0){ if (v==EXPR_DROP){ /* hack to quit on DROP*/ ret=0; break; }else{ LERR("WARNING: do_action: error in expression\n"); } } ret=1; /*default is continue */ if (v>0) { if ((a->p2_type==ACTIONS_ST)&&a->p2.data){ ret=run_actions(h,(struct action*)a->p2.data, msg); } }else if ((a->p3_type==ACTIONS_ST)&&a->p3.data){ ret=run_actions(h,(struct action*)a->p3.data, msg); } } break; case MODULE_T: if ( ((a->p1_type==CMDF_ST)&&a->p1.data)){ ret=((cmd_function)(a->p1.data))(msg, (char*)a->p2.data, (char*)a->p3.data); }else{ LERR("BUG: do_action: bad module call\n"); } break; default: LERR("BUG: do_action: unknown type %d\n", a->type); ret = 0; } return ret; } static int eval_elem(struct run_act_ctx* h, struct expr* e, msg_t* msg) { int ret; ret=E_BUG; if (e->type!=ELEM_T){ LERR(" BUG: eval_elem: invalid type\n"); goto error; } switch(e->l.operand){ case NUMBER_O: ret=!(!e->r.intval); /* !! to transform it in {0,1} */ break; case ACTION_O: ret=run_actions(h,(struct action*)e->r.param, msg); if (ret<=0) ret=(ret==0)?EXPR_DROP:0; else ret=1; break; default: LERR("BUG: eval_elem: invalid operand %d\n",e->l.operand); } return ret; error: return -1; } /* ret= 0/1 (true/false) , -1 on error or EXPR_DROP (-127) */ int eval_expr(struct run_act_ctx* h, struct expr* e, msg_t* msg) { static int rec_lev=0; int ret; rec_lev++; if (rec_lev>MAX_REC_LEV){ LERR("ERROR: eval_expr: too many expressions (%d)\n", rec_lev); ret=-1; goto skip; } if (e->type==ELEM_T){ ret=eval_elem(h, e, msg); }else if (e->type==EXP_T){ switch(e->op){ case AND_OP: ret=eval_expr(h, e->l.expr, msg); /* if error or false stop evaluating the rest */ if (ret!=1) break; ret=eval_expr(h, e->r.expr, msg); /*ret1 is 1*/ break; case OR_OP: ret=eval_expr(h, e->l.expr, msg); /* if true or error stop evaluating the rest */ if (ret!=0) break; ret=eval_expr(h, e->r.expr, msg); /* ret1 is 0 */ break; case NOT_OP: ret=eval_expr(h, e->l.expr, msg); if (ret<0) break; ret= ! ret; break; default: LERR("BUG: eval_expr: unknown op %d\n", e->op); ret=-1; } }else{ LERR("BUG: eval_expr: unknown type %d\n", e->type); ret=-1; } skip: rec_lev--; return ret; } /* returns: 0, or 1 on success, <0 on error */ /* (0 if drop or break encountered, 1 if not ) */ int run_actions(struct run_act_ctx* h, struct action* a, msg_t* msg) { struct action* t; int ret=E_UNSPEC; //static int rec_lev=0; struct sr_module *mod; //printf("RUN: [%d]", h->rec_lev); h->rec_lev++; if (h->rec_lev>ROUTE_MAX_REC_LEV){ printf("WARNING: too many recursive routing table lookups (%d)" " giving up!\n", h->rec_lev); printf("WARNING: Action: type: (%d), Ret: (%d), p2_type: (%s), p3_type: (%s)\n", a->type, ret, (char *)a->p2.data, (char *)a->p3.data); ret=E_UNSPEC; goto error; } if (a==0){ printf("WARNING: run_actions: null action list (rec_level=%d)\n", h->rec_lev); ret=0; } for (t=a; t!=0; t=t->next){ ret=do_action(h, t, msg); if(ret==0) break; /* ignore errors */ //else if (ret<0){ ret=-1; goto error; } } h->rec_lev--; /* process module onbreak handlers if present */ if (h->rec_lev==0 && ret==0) for (mod=modules;mod;mod=mod->next) if (mod->exports && mod->exports->onbreak_f) { mod->exports->onbreak_f( msg ); printf("DEBUG: %s onbreak handler called\n", mod->exports->name); } return ret; error: h->rec_lev--; return ret; } int capture_get(struct capture_list* rt, char* name) { int len; int i; //printf("!!!!!!!!!!!!!!!---------------> ROUTE GET: %s, E: [%d], I: [%d]\n", name, rt->entries, rt->idx); rt->entries+=1; rt->idx+=1; //printf("!!!!!!!!!!!!!!!---------------> ROUTE GET NN: [%d], I:[%d]\n", rt->entries, rt->idx); /* check if exists an non empty*/ return rt->idx; error: return -1; } /* adds an action list to head; a must be null terminated (last a->next=0))*/ void push(struct action* a, struct action** head) { struct action *t; if (*head==0){ *head=a; return; } for (t=*head; t->next;t=t->next); t->next=a; } /* searches the module list and returns a pointer to the "name" function or * 0 if not found */ cmd_function find_export2(char* name, int param_no) { struct sr_module* t; cmd_function s; int r; for(t=modules;t;t=t->next){ for(r=0;rexports->cmd_no;r++){ if((strcmp(name, t->exports->cmd_names[r])==0)&& (t->exports->param_no[r]==param_no) ){ printf("find_export: found <%s> in module %s [%s]\n", name, t->exports->name, t->path); return t->exports->cmd_pointers[r]; } } } LERR("find_export: <%s> not found \n", name); return s; } void* find_param_export(char* mod, char* name, modparam_t type) { struct sr_module* t; int r; for(t = modules; t; t = t->next) { if (strcmp(mod, t->exports->name) == 0) { for(r = 0; r < t->exports->par_no; r++) { if ((strcmp(name, t->exports->param_names[r]) == 0) && (t->exports->param_types[r] == type)) { printf("find_param_export: found <%s> in module %s [%s]\n", name, t->exports->name, t->path); return t->exports->param_pointers[r]; } } } } LERR("find_param_export: parameter <%s> or module <%s> not found\n", name, mod); return 0; } struct action* append_action(struct action* a, struct action* b) { struct action *t; if (b==0) return a; if (a==0) return b; for(t=a;t->next;t=t->next); t->next=b; return a; } struct expr* mk_exp(int op, struct expr* left, struct expr* right) { struct expr * e; e=(struct expr*)malloc(sizeof (struct expr)); if (e==0) goto error; e->type=EXP_T; e->op=op; e->l.expr=left; e->r.expr=right; return e; error: printf( "ERROR: mk_exp: memory allocation failure\n"); return 0; } struct expr* mk_elem(int op, int subtype, int operand, void* param) { struct expr * e; e=(struct expr*)malloc(sizeof (struct expr)); if (e==0) goto error; e->type=ELEM_T; e->op=op; e->subtype=subtype; e->l.operand=operand; e->r.param=param; return e; error: LERR( "ERROR: mk_elem: memory allocation failure\n"); return 0; } struct action* mk_action(int type, int p1_type, int p2_type, void* p1, void* p2) { struct action* a; a=(struct action*)malloc(sizeof(struct action)); if (a==0) goto error; memset(a,0,sizeof(struct action)); a->type=type; a->p1_type=p1_type; a->p2_type=p2_type; a->p1.string=(char*) p1; a->p2.string=(char*) p2; a->next=0; return a; error: LERR( "ERROR: mk_action: memory allocation failure\n"); return 0; } struct action* mk_action3(int type, int p1_type, int p2_type, int p3_type, void* p1, void* p2, void* p3) { struct action* a; //printf("ZZ TYPE: [%d], PLTYPE1: [%d], PLTYPE2: [%d], PLTYPE3: [%d]\n", type, p1_type, p2_type, p3_type); a=mk_action(type, p1_type, p2_type, p1, p2); if (a){ a->p3_type=p3_type; a->p3.data=p3; } return a; } /* new one */ cmd_export_t* find_export_record(char* name, int param_no, int flags, unsigned* mod_if_ver) { return find_mod_export_record(0, name, param_no, flags, mod_if_ver); } cmd_export_t* find_mod_export_record(char* mod, char* name, int param_no, int flags, unsigned* mod_if_ver) { struct module* t; cmd_export_t* cmd; for(t=module_list;t;t=t->next){ if (mod!=0 && (strcmp(t->name, mod) !=0)) continue; if (t->cmds) for(cmd=&t->cmds[0]; cmd->name; cmd++) { //LERR("NAME: [%s] vs [%s]\n", name, cmd->name); //LERR("PARAM: [%d] vs [%d]\n", param_no, cmd->param_no); if((strcmp(name, cmd->name) == 0) && ((cmd->param_no == param_no) || (cmd->param_no==VAR_PARAM_NO)) && ((cmd->flags & flags) == flags) ){ LDEBUG("find_export_record: found <%s> in module %s [%s]", name, t->name, t->path); return cmd; } } } LERR("find_export_record: <%s> not found \n", name); return 0; } cmd_function find_mod_export(char* mod, char* name, int param_no, int flags) { cmd_export_t* cmd; unsigned mver; cmd=find_mod_export_record(mod, name, param_no, flags, &mver); if (cmd) return cmd->function; LERR("find_mod_export: <%s> in module <%s> not found\n", name, mod); return 0; } cmd_function find_export(char* name, int param_no, int flags) { cmd_export_t* cmd; unsigned mver; cmd = find_export_record(name, param_no, flags, &mver); return cmd?cmd->function:0; } captagent-6.1.0.20/src/conf_function.h000066400000000000000000000066711272354503300175140ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef CONF_FUNCTION_H_ #define CONF_FUNCTION_H_ #define EXPR_DROP -127 /* used only by the expression and if evaluator */ #define E_BUG -5 enum { EXP_T=1, ELEM_T }; enum { AND_OP=1, OR_OP, NOT_OP }; enum { EQUAL_OP=10, MATCH_OP, NO_OP }; enum { METHOD_O=1, DEFAULT_O, ACTION_O, NUMBER_O}; enum { FORWARD_T=1, SEND_T, DROP_T, IF_T, MODULE_T}; enum { NOSUBTYPE=0, STRING_ST, NET_ST, ACTIONS_ST, CMDF_ST, EXPR_ST, NUMBER_ST }; struct expr{ int type; /* exp, exp_elem */ int op; /* and, or, not | ==, =~ */ int subtype; union { struct expr* expr; int operand; }l; union { struct expr* expr; void* param; int intval; }r; }; static int eval_elem(struct run_act_ctx* h, struct expr* e, msg_t* msg); int eval_expr(struct run_act_ctx* h, struct expr* e, msg_t* msg); int capture_get(struct capture_list* rt, char* name); void push(struct action* a, struct action** head); struct expr* mk_exp(int op, struct expr* left, struct expr* right); struct expr* mk_elem(int op, int subtype, int operand, void* param); struct action* mk_action(int type, int p1_type, int p2_type, void* p1, void* p2); struct action* mk_action3(int type, int p1_type, int p2_type, int p3_type, void* p1, void* p2, void* p3); struct action* append_action(struct action* a, struct action* b); void print_action(struct action* a); void print_expr(struct expr* exp); typedef int (*response_function)(struct sip_msg*); typedef int (*child_init_function)(int rank); struct sr_module{ char* path; void* handle; struct module_exports* exports; struct sr_module* next; }; struct sr_module* modules; /* global module list */ int register_builtin_modules(); int load_module(char* path); cmd_function find_export2(char* name, int param_no); struct sr_module* find_module(void *f, int* r); void destroy_modules(); int init_child(int rank); int init_modules(void); /* * Find a parameter with given type and return it's * address in memory * If there is no such parameter, NULL is returned */ void* find_param_export(char* mod, char* name, modparam_t type); /* new */ cmd_function find_export(char* name, int param_no, int flags); cmd_export_t* find_mod_export_record(char* mod, char* name, int param_no, int flags, unsigned* mod_if_ver); cmd_export_t* find_export_record(char* name, int param_no, int flags, unsigned* mod_if_ver); #endif /* CONF_FUNCTION_H_ */ captagent-6.1.0.20/src/log.c000066400000000000000000000037131272354503300154300ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef LOG_C_ #define LOG_C_ #include #include #include static int use_syslog = 0; static int log_level = LOG_WARNING; void init_log(char *_prgname, int _use_syslog) { use_syslog = _use_syslog; if (use_syslog) { openlog(_prgname, LOG_PID, LOG_DAEMON); } } void set_log_level(int level) { log_level = level; } void destroy_log(void) { if (use_syslog) closelog(); } void log_stdout(const char * format, va_list ap) { vfprintf(stdout, format, ap); fprintf(stdout, "\r\n"); fflush(stdout); } void data_log(int priority, const char *fmt, ...) { va_list args; if (priority<=log_level) { //vsnprintf("SYSLOG:%s:%d:%s: ", file, line, func); va_start(args, fmt); if (use_syslog) vsyslog(priority, fmt, args); else log_stdout(fmt, args); va_end(args); } } #endif /* LOG_C_ */ captagent-6.1.0.20/src/md5.c000066400000000000000000000307741272354503300153430ustar00rootroot00000000000000/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) #include "md5.h" static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 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 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } captagent-6.1.0.20/src/md5.h000066400000000000000000000067501272354503300153450ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef MD5_H_ #define MD5_H_ /* * This an amalgamation of md5.c and md5.h into a single file * with all static declaration to reduce linker conflicts * in Civetweb. * * The MD5_STATIC declaration was added to facilitate static * inclusion. * No Face Press, LLC */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #endif /* MD5_H_ */ captagent-6.1.0.20/src/modules.c000066400000000000000000000126031272354503300163150ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef MODULES_C_ #define MODULES_C_ #include #include #include #include #include #include #include #include #include #include #include int register_module(char *resource_name, xml_node *config, bool global) { const char *error; module_exports_t *exp; int flag = RTLD_NOW; int i = 0, n = 0; cmd_export_t* ret; LDEBUG("Loading module: [%s]", resource_name); static char fn[256]; int errors = 0, res, hep_error = 0; struct module *m = malloc(sizeof(struct module)); if (!m) { LERR("Out of memory modules"); return -1; } /* if (global == TRUE) { flag = RTLD_NOW | RTLD_GLOBAL; LDEBUG("[%s] registerd as 'global'", resource_name); } */ strncpy(m->name, resource_name, sizeof(m->name)); if (resource_name[0] == '/') strncpy(fn, resource_name, sizeof(fn)); else snprintf(fn, sizeof(fn), "%s/%s.so", module_path, resource_name); /*if (!(m->lib = dlopen(fn, RTLD_NOW | RTLD_GLOBAL))) { */ if (!(m->lib = dlopen(fn, flag))) { LERR("dlopen error [%s]", dlerror()); free(m); return -1; } dlerror(); exp = (struct module_exports*)dlsym(m->lib, "exports"); if ( (error =(char*)dlerror())!=0 ){ LERR( "ERROR: load_module: %s\n", error); errors++; } m->load_f = exp->load_f; m->unload_f = exp->unload_f; m->description_f = exp->description_f; m->stats_f = exp->stats_f; m->serial_f = exp->serial_f; m->path = module_path; /* if (!(m->load_f = dlsym(m->lib, "load_module"))) { LERR("No load_module in module %s", fn); errors++; } else if (!(m->unload_f = dlsym(m->lib, "unload_module"))) { LERR("No unload_module in module %s", fn); errors++; } else if (!(m->description_f = dlsym(m->lib, "description"))) { LERR("No description in module %s", fn); errors++; } else if (!(m->stats_f = dlsym(m->lib, "statistic"))) { LERR("No statistic in module %s", fn); errors++; } */ if (errors) { LERR("%d error(s) loading module %s, aborted", errors, fn); dlclose(m->lib); free(m); return -1; } for (; exp->cmds[n].name; n++); ret = malloc(sizeof(*ret)*(n+1)); memset(ret, 0, sizeof(*ret)*(n+1)); for (i=0; i < n; i++) { ret[i].name = exp->cmds[i].name; ret[i].function = exp->cmds[i].function; ret[i].param_no = exp->cmds[i].param_no; ret[i].flags = exp->cmds[i].flags; ret[i].module_exports = m; } m->cmds = ret; if ((res = m->load_f(config))) { LERR("load_module [%s] failed, returning %d", m->name, res); free(m); return -1; } m->next = module_list; module_list = m; return 1; } int unregister_modules(void) { struct module *m, *ml = NULL; int res = -1; m = module_list; cmd_export_t* ret; while (m) { unregister_module(m); module_list = m->next; ml = m; m = m->next; ret = ml->cmds; free(ret); free(ml); } return res; } int unregister_module(struct module *m) { int res = -1; res = m->unload_f(); if (res) { LERR("module unload failed for [%s]", m->name); dlclose(m->lib); } return res; } int register_modules(xml_node *tree) { /* SOCKETS */ xml_node *next, *modules, *config, *sockets; bool global = FALSE; int i = 0; next = tree; while (next) { next = xml_get("configuration", next, 1); if (next == NULL) break; for (i = 0; next->attr[i]; i++) { if (!strncmp(next->attr[i], "name", 4)) { if (!strncmp(next->attr[i + 1], "modules.conf", 13)) { modules = next; while (modules) { /* modules by default dont' share own functions */ global = FALSE; modules = xml_get("load", modules, 1); if (modules == NULL) break; if (modules->attr[0] != NULL && modules->attr[1] != NULL) { /* get config */ /*if (!(config = get_module_config(modules->attr[1], tree))) { LERR("Config for [%s] has been not found", modules->attr[1]); } if (modules->attr[2] != NULL && !strncmp(modules->attr[2], "register", 8)) { if (modules->attr[3] != NULL && !strncmp(modules->attr[3], "global", 6)) global = TRUE; } */ if (!register_module(modules->attr[1], config, global)) { LERR("Module [%s] couldnot be registered", modules->attr[1]); } } modules = modules->next; } } } } next = next->next; } return 0; } #endif /* MODULES_C_ */ captagent-6.1.0.20/src/modules/000077500000000000000000000000001272354503300161475ustar00rootroot00000000000000captagent-6.1.0.20/src/modules/database/000077500000000000000000000000001272354503300177135ustar00rootroot00000000000000captagent-6.1.0.20/src/modules/database/hash/000077500000000000000000000000001272354503300206365ustar00rootroot00000000000000captagent-6.1.0.20/src/modules/database/hash/Makefile.am000066400000000000000000000007671272354503300227040ustar00rootroot00000000000000include $(top_srcdir)/modules.am SUBDIRS = . noinst_HEADERS = captarray.h database_hash.h hash_structure.h list.h localapi.h utarray.h uthash.h utlist.h utstring.h # database_hash_la_SOURCES = database_hash.c captarray.c localapi.c database_hash_la_CFLAGS = -Wall ${MODULE_CFLAGS} ${EXPAT_LIBS} database_hash_la_LDFLAGS = -module -avoid-version database_hash_laconfdir = $(confdir) database_hash_laconf_DATA = $(top_srcdir)/conf/database_hash.xml include_HEADERS = mod_LTLIBRARIES = database_hash.la captagent-6.1.0.20/src/modules/database/hash/captarray.c000066400000000000000000000053761272354503300230030ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-14 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include "captarray.h" #include #include int expire_timer_array = EXPIRE_TIMER_ARRAY; pthread_t thread_timer; struct list_head g_queue_head; void timer_init () { /* start waiting thread */ if( pthread_create(&thread_timer , NULL , timer_loop, NULL) < 0) { fprintf(stderr, "could not create timer thread"); } } int add_timer(char *pid) { timer_queue_t *timer_node = (timer_queue_t *)malloc(sizeof(timer_queue_t)); if (IS_EQUAL(timer_node, NULL)) { perror("add cus-group:"); return -1; } memset(timer_node, 0, sizeof(timer_queue_t)); timer_node->expire = (unsigned)time(NULL) + expire_timer_array; snprintf(timer_node->id, sizeof(timer_node->id), "%s", pid); list_add_tail(&timer_node->node, &g_queue_head); return 0; } int delete_timer(timer_queue_t *timer) { list_del(&timer->node); free(timer); return 1; } int gather_data_run() { timer_queue_t *pos, *lpos; unsigned int mycount = 0; while (timer_loop_stop) { list_for_each_entry_safe(pos, lpos, &g_queue_head, node) { while (pos->expire > time(NULL)) { sleep(1); } if (check_ipport(pos->id) == 0) { add_timer(pos->id); } delete_timer(pos); mycount = list_size(); } if (mycount == 0) sleep(1); } return 1; } int list_size() { unsigned int count = 0; timer_queue_t *pos, *lpos; list_for_each_entry_safe(pos, lpos, &g_queue_head, node) count++; return count; } void* timer_loop() { INIT_LIST_HEAD(&g_queue_head); gather_data_run(); return (void*) 1; } captagent-6.1.0.20/src/modules/database/hash/captarray.h000066400000000000000000000011611272354503300227740ustar00rootroot00000000000000 #ifndef _CAPTARRAY_H #define _CAPTARRAY_H #include #include #include #include #include "list.h" #define IS_EQUAL(x, y) ((x) == (y)) #define IS_BIGGER (x, y) ((x) > (y)) #define EXPIRE_TIMER_ARRAY 80 extern int timer_loop_stop; extern int check_ipport(char *name); typedef struct timer_queue { struct list_head node; char id[256]; uint32_t expire; }timer_queue_t; void timer_init(); int add_timer(char *pid); int delete_timer(timer_queue_t *timer); int process_alarm_sig(int sig); int gather_data_run(); void* timer_loop(); int list_size(); #endif captagent-6.1.0.20/src/modules/database/hash/database_hash.c000066400000000000000000000323161272354503300235560ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "database_hash.h" #include #include "localapi.h" #include "captarray.h" pthread_rwlock_t ipport_lock; unsigned int profile_size = 0; xml_node *module_xml_config = NULL; char *module_name="database_hash"; uint64_t module_serial = 0; char *module_description = NULL; static int load_module(xml_node *config); static int unload_module(void); static int description(char *descr); static int statistic(char *buf, size_t len); static int reload_config (char *erbuf, int erlen); static uint64_t serial_module(void); static cmd_export_t cmds[] = { {"database_hash_bind_api", (cmd_function)bind_api, 1, 0, 0, 0}, {"check_rtcp_ipport", (cmd_function) w_check_rtcp_ipport, 0, 0, 0, 0 }, {"is_rtcp_exist", (cmd_function) w_is_rtcp_exists, 0, 0, 0, 0 }, {"bind_database_has", (cmd_function)bind_database_hash, 0, 0, 0, 0}, /* ================================ */ {0, 0, 0, 0, 0, 0} }; struct module_exports exports = { "database_hash", cmds, /* Exported functions */ load_module, /* module initialization function */ unload_module, description, statistic, serial_module }; int bind_api(database_module_api_t* api) { api->reload_f = reload_config; api->module_name = module_name; return 0; } int w_check_rtcp_ipport(msg_t *msg) { int i = 0; miprtcp_t *mp = NULL; char ipptmp[256]; char callid[256]; snprintf(callid, sizeof(callid), "%.*s", msg->sip.callId.len, msg->sip.callId.s); for (i = 0; i < msg->sip.mrp_size; i++) { mp = &msg->sip.mrp[i]; if (mp->rtcp_ip.len > 0 && mp->rtcp_ip.s) { snprintf(ipptmp, sizeof(ipptmp), "%.*s:%d", mp->rtcp_ip.len, mp->rtcp_ip.s, mp->rtcp_port); LDEBUG("RTCP CALLID: %.*s", msg->sip.callId.len, msg->sip.callId.s); LDEBUG("RTCP IP PORT: %s", ipptmp); /* one pair = one timer */ if(!find_and_update(ipptmp, callid)) { add_timer(ipptmp); add_ipport(ipptmp, callid); } } } return 1; } int w_is_rtcp_exists(msg_t *msg) { struct ipport_items *ipport = NULL; LDEBUG("IP PORT: %s:%i", msg->rcinfo.src_ip, msg->rcinfo.src_port); ipport = find_ipport(msg->rcinfo.src_ip, msg->rcinfo.src_port); if(!ipport) { ipport = find_ipport( msg->rcinfo.dst_ip, msg->rcinfo.dst_port); if(!ipport) return -1; msg->rcinfo.direction = 0; ipport->modify_ts = (unsigned)time(NULL); } LDEBUG("SESSION ID: %s", ipport->sessionid); ipport->modify_ts = (unsigned)time(NULL); msg->rcinfo.correlation_id.s = ipport->sessionid; msg->rcinfo.correlation_id.len = strlen(ipport->sessionid); msg->var = (void *) ipport; return 1; } /* ADD IPPPORT */ void add_ipport(char *key, char *callid) { struct ipport_items *ipport; ipport = (struct ipport_items*)malloc(sizeof(struct ipport_items)); snprintf(ipport->name, sizeof(ipport->name), "%s", key); snprintf(ipport->sessionid, sizeof(ipport->sessionid), "%s", callid); ipport->modify_ts = (unsigned)time(NULL); if (pthread_rwlock_wrlock(&ipport_lock) != 0) { fprintf(stderr,"can't acquire write lock"); exit(-1); } HASH_ADD_STR(ipports, name, ipport); pthread_rwlock_unlock(&ipport_lock); } /* ADD IPPPORT */ void update_ipport(char *key, char *callid) { struct ipport_items *ipport; ipport = (struct ipport_items*)malloc(sizeof(struct ipport_items)); snprintf(ipport->name, sizeof(ipport->name), "%s", key); snprintf(ipport->sessionid, sizeof(ipport->sessionid), "%s", callid); ipport->modify_ts = (unsigned)time(NULL); if (pthread_rwlock_wrlock(&ipport_lock) != 0) { fprintf(stderr,"can't acquire write lock"); exit(-1); } HASH_ADD_STR(ipports, name, ipport); pthread_rwlock_unlock(&ipport_lock); } int find_and_update(char *key, char *callid) { struct ipport_items *ipport = NULL; int ret = 0; if (pthread_rwlock_rdlock(&ipport_lock) != 0) { fprintf(stderr,"can't acquire write lock"); exit(-1); } HASH_FIND_STR( ipports, key, ipport); if(ipport) { snprintf(ipport->sessionid, sizeof(ipport->sessionid), "%s", callid); ipport->modify_ts = (unsigned)time(NULL); ret = 1; } pthread_rwlock_unlock(&ipport_lock); return ret; } struct ipport_items *find_ipport_key(char *key) { struct ipport_items *ipport = NULL; int ret = 0; if (pthread_rwlock_rdlock(&ipport_lock) != 0) { LERR("can't acquire write lock"); exit(-1); } HASH_FIND_STR( ipports, key, ipport); pthread_rwlock_unlock(&ipport_lock); return ipport; } struct ipport_items *find_ipport(char *ip, int port) { char name[400]; snprintf(name, 400, "%s:%i", ip, port); LDEBUG("IP PORT: [%s]", name); return find_ipport_key(name); } int clear_ipport(struct ipport_items *ipport ) { if(ipport) { if (pthread_rwlock_wrlock(&ipport_lock) != 0) { LERR("can't acquire write lock"); exit(-1); } LDEBUG("DELETING.................."); LDEBUG("NAME: [%s]", ipport->name); HASH_DEL( ipports, ipport); /* free */ free(ipport); pthread_rwlock_unlock(&ipport_lock); return 1; } return 0; } int delete_ipport(char *ip, int port) { struct ipport_items *ipport; LDEBUG("delete ipport !"); ipport = find_ipport(ip, port); return clear_ipport(ipport); } void clear_ipports() { struct ipport_items *s, *tmp; if (pthread_rwlock_wrlock(&ipport_lock) != 0) { LERR("can't acquire write lock"); exit(-1); } /* free the hash table contents */ HASH_ITER(hh, ipports, s, tmp) { HASH_DEL(ipports, s); free(s); } pthread_rwlock_unlock(&ipport_lock); } int check_ipport(char *name) { int ret = 1; ipport_items_t *ipport = NULL; if(!name) { LERR("bad name pointer in check_ipports!\n"); return 3; } if (pthread_rwlock_rdlock(&ipport_lock) != 0) { fprintf(stderr, "can't acquire write lock"); exit(-1); } HASH_FIND_STR( ipports, name, ipport); if(ipport) { if(((unsigned) time(NULL) - ipport->modify_ts) >= rtcp_timeout) { HASH_DEL( ipports, ipport); free(ipport); ret = 2; } else { ret = 0; } } pthread_rwlock_unlock(&ipport_lock); return ret; } void print_ipports() { struct ipport_items *s, *tmp; if (pthread_rwlock_rdlock(&ipport_lock) != 0) { LERR("can't acquire write lock"); exit(-1); } HASH_ITER(hh, ipports, s, tmp) { LDEBUG("NAME IPPORTS: %s", s->name); } pthread_rwlock_unlock(&ipport_lock); } int reload_config (char *erbuf, int erlen) { char module_config_name[500]; xml_node *config; LNOTICE("reloading config for [%s]", module_name); snprintf(module_config_name, 500, "%s/%s.xml", global_config_path, module_name); if(xml_parse_with_report(module_config_name, erbuf, erlen)) { unload_module(); load_module(config); return 1; } return 0; } int load_module_xml_config() { char module_config_name[500]; xml_node *next; int i = 0; snprintf(module_config_name, 500, "%s/%s.xml", global_config_path, module_name); if ((module_xml_config = xml_parse(module_config_name)) == NULL) { LERR("Unable to open configuration file: %s", module_config_name); return -1; } /* check if this module is our */ next = xml_get("module", module_xml_config, 1); if (next == NULL) { LERR("wrong config for module: %s", module_name); return -2; } for (i = 0; next->attr[i]; i++) { if (!strncmp(next->attr[i], "name", 4)) { if (strncmp(next->attr[i + 1], module_name, strlen(module_name))) { return -3; } } else if (!strncmp(next->attr[i], "serial", 6)) { module_serial = atol(next->attr[i + 1]); } else if (!strncmp(next->attr[i], "description", 11)) { module_description = next->attr[i + 1]; } } return 1; } void free_module_xml_config() { /* now we are free */ if(module_xml_config) { xml_free(module_xml_config); } } /* modules external API */ static int load_module(xml_node *config) { xml_node *params, *profile=NULL, *settings; char *key, *value = NULL; LNOTICE("Loaded %s", module_name); load_module_xml_config(); /* READ CONFIG */ profile = module_xml_config; /* reset profile */ profile_size = 0; while (profile) { profile = xml_get("profile", profile, 1); if (profile == NULL) break; if (!profile->attr[4] || strncmp(profile->attr[4], "enable", 6)) { goto nextprofile; } /* if not equals "true" */ if (!profile->attr[5] || strncmp(profile->attr[5], "true", 4)) { goto nextprofile; } if(profile_size == 2) { break; } memset(&profile_database[profile_size], 0, sizeof(profile_database_t)); /* set values */ profile_database[profile_size].name = strdup(profile->attr[1]); profile_database[profile_size].description = strdup(profile->attr[3]); profile_database[profile_size].serial = atoi(profile->attr[7]); /* SETTINGS */ settings = xml_get("settings", profile, 1); if (settings != NULL) { params = settings; while (params) { params = xml_get("param", params, 1); if (params == NULL) break; if (params->attr[0] != NULL) { /* bad parser */ if (strncmp(params->attr[0], "name", 4)) { LERR("bad keys in the config"); goto nextparam; } key = params->attr[1]; if (params->attr[2] && params->attr[3] && !strncmp(params->attr[2], "value", 5)) { value = params->attr[3]; } else { value = params->child->value; } if (key == NULL || value == NULL) { LERR("bad values in the config"); goto nextparam; } /* cache */ if (!strncmp(key, "timer-timeout", 13) && atoi(value) > 200) timer_timeout = atoi(value); else if (!strncmp(key, "rtcp-timeout", 12) && atoi(value) > 80) rtcp_timeout = atoi(value); } nextparam: params = params->next; } } profile_size++; nextprofile: profile = profile->next; } /* free */ free_module_xml_config(); timer_init(); return 0; } static int free_profile(unsigned int idx) { if (profile_database[idx].name) free(profile_database[idx].name); if (profile_database[idx].description) free(profile_database[idx].description); return 1; } static int unload_module(void) { unsigned int i = 0; LNOTICE("unloaded module %s", module_name); timer_loop_stop = 0; for (i = 0; i < profile_size; i++) { free_profile(i); } return 0; } static uint64_t serial_module(void) { return module_serial; } static int description(char *descr) { LNOTICE("Loaded description"); descr = module_description; return 1; } static int statistic(char *buf, size_t len) { int ret = 0; ret += snprintf(buf+ret, sizeof(buf) - len, "TEST STATISTICS"); return 1; } captagent-6.1.0.20/src/modules/database/hash/database_hash.h000066400000000000000000000041341272354503300235600ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef DATABASE_HASH_H_ #define DATABASE_HASH_H_ #include #include "uthash.h" #include "hash_structure.h" int timer_timeout = 10; int timer_loop_stop = 0; static int global_session_id = 0; int cin_min = 100; int cin_max = 800; #define MAX_DATABASE 10 profile_database_t profile_database[MAX_DATABASE]; #define EXPIRE_RTCP_HASH 80 #define EXPIRE_TIMER_ARRAY 80 int expire_hash_value = EXPIRE_RTCP_HASH; int rtcp_timeout = EXPIRE_RTCP_HASH; typedef struct mediaport { char ipportid[400]; } mediaport_t; struct ipport_items *ipports = NULL; bool hash_mode = FALSE; int bind_api(database_module_api_t* api); int w_is_rtcp_exists(msg_t *msg); int w_check_rtcp_ipport(msg_t *msg); extern char* global_config_path; /* IPPORTS */ struct ipport_items *find_ipport(char *ip, int port); struct ipport_items *find_ipport_key(char *key); void add_ipport(char *key, char *callid); int delete_ipport(char *ip, int port); int clear_ipport(struct ipport_items *ipport); int find_and_update(char *key, char *callid); void clear_ipports(); void print_ipports(); int check_ipport(char *name); #endif /* DATABASE_LI_H_ */ captagent-6.1.0.20/src/modules/database/hash/hash_structure.h000066400000000000000000000023511272354503300240530ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef HASH_STRUCTURE_H_ #define HASH_STRUCTURE_H_ typedef struct ipport_items { char name[400]; char ip[250]; int port; char sessionid[250]; long create_ts; long modify_ts; UT_hash_handle hh; } ipport_items_t; #endif /* HASH_STRUCTURE_H_ */ captagent-6.1.0.20/src/modules/database/hash/list.h000066400000000000000000000225441272354503300217710ustar00rootroot00000000000000#ifndef _LINUX_LIST_H #define _LINUX_LIST_H #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #define container_of(ptr, type, member) ( { \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) ); } ) static inline void prefetch(const void *x) {;} static inline void prefetchw(const void *x) {;} #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /* * Insert a new entry between two known consecutive entries. * * This is only for intenal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } static inline void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } static inline void list_move(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add(list, head); } static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add_tail(list, head); } static inline int list_empty(const struct list_head *head) { return head->next == head; } static inline int list_empty_careful(const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->prev); } static inline void __list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } /** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head); INIT_LIST_HEAD(list); } } #define list_entry(ptr, type, member) container_of(ptr, type, member) #define list_for_each(pos, head) \ for (pos = (head)->next; prefetch(pos->next), pos != (head); \ pos = pos->next) #define __list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ pos = pos->prev) #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member); \ prefetch(pos->member.prev), &pos->member != (head); \ pos = list_entry(pos->member.prev, typeof(*pos), member)) #define list_prepare_entry(pos, head, member) \ ((pos) ? : list_entry(head, typeof(*pos), member)) #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member); \ prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) //HASH LIST struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; #define HLIST_HEAD_INIT { .first = NULL } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) #define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) static inline int hlist_unhashed(const struct hlist_node *h) { return !h->pprev; } static inline int hlist_empty(const struct hlist_head *h) { return !h->first; } static inline void __hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; if (next) next->pprev = pprev; } static inline void hlist_del(struct hlist_node *n) { __hlist_del(n); n->next = LIST_POISON1; n->pprev = LIST_POISON2; } static inline void hlist_del_init(struct hlist_node *n) { if (n->pprev) { __hlist_del(n); INIT_HLIST_NODE(n); } } static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; } /* next must be != NULL */ static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; *(n->pprev) = n; } static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next) { next->next = n->next; n->next = next; next->pprev = &n->next; if(next->next) next->next->pprev = &next->next; } #define hlist_entry(ptr, type, member) container_of(ptr,type,member) #define hlist_for_each(pos, head) \ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ pos = pos->next) #define hlist_for_each_safe(pos, n, head) \ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ pos = n) #define hlist_for_each_entry(tpos, pos, head, member) \ for (pos = (head)->first; \ pos && ({ prefetch(pos->next); 1;}) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) #define hlist_for_each_entry_continue(tpos, pos, member) \ for (pos = (pos)->next; \ pos && ({ prefetch(pos->next); 1;}) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) #define hlist_for_each_entry_from(tpos, pos, member) \ for (; pos && ({ prefetch(pos->next); 1;}) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) #define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ for (pos = (head)->first; \ pos && ({ n = pos->next; 1; }) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = n) #endif captagent-6.1.0.20/src/modules/database/hash/localapi.c000066400000000000000000000030001272354503300225570ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include "localapi.h" char* hashapi_lookup(char *ip, int port) { LERR("LOOKUP IP: [%s], PORT: [%d]", ip, port); return find_ipport(ip, port); } int bind_database_hash(database_hash_api_t* api) { if (!api) { LERR("Invalid parameter value\n"); return -1; } api->lookup = hashapi_lookup; return 0; } captagent-6.1.0.20/src/modules/database/hash/localapi.h000066400000000000000000000037221272354503300225770ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef DATABASE_HASH_API_H_ #define DATABASE_HASH_API_H_ #include #include typedef char* (*hashapi_lookup_f)(char *ip, int port); char* hashapi_lookup(char *ip, int port); typedef struct database_hash_api { hashapi_lookup_f lookup; } database_hash_api_t; typedef int (*bind_database_hash_f)(database_hash_api_t* api); int bind_database_hash(database_hash_api_t* api); /** * @brief Load the database_hash API */ static inline int database_hash_load_api(database_hash_api_t *api) { bind_database_hash_f binddatabase_hash; binddatabase_hash = (bind_database_hash_f)find_export("bind_database_hash", 0, 0); if(binddatabase_hash == 0) { LERR("cannot find bind_database_hash\n"); return -1; } if (binddatabase_hash(api) < 0) { LERR("cannot bind database_hash api\n"); return -1; } return 0; } #endif /* DATABASE_HASH_API_H_ */ captagent-6.1.0.20/src/modules/database/hash/utarray.h000066400000000000000000000311231272354503300224760ustar00rootroot00000000000000/* Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ 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. 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 COPYRIGHT OWNER 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. */ /* a dynamic array implementation using macros */ #ifndef UTARRAY_H #define UTARRAY_H #define UTARRAY_VERSION 1.9.9 #ifdef __GNUC__ #define _UNUSED_ __attribute__ ((__unused__)) #else #define _UNUSED_ #endif #include /* size_t */ #include /* memset, etc */ #include /* exit */ #ifndef oom #define oom() exit(-1) #endif typedef void (ctor_f)(void *dst, const void *src); typedef void (dtor_f)(void *elt); typedef void (init_f)(void *elt); typedef struct { size_t sz; init_f *init; ctor_f *copy; dtor_f *dtor; } UT_icd; typedef struct { unsigned i,n;/* i: index of next available slot, n: num slots */ UT_icd icd; /* initializer, copy and destructor functions */ char *d; /* n slots of size icd->sz*/ } UT_array; #define utarray_init(a,_icd) do { \ memset(a,0,sizeof(UT_array)); \ (a)->icd=*_icd; \ } while(0) #define utarray_done(a) do { \ if ((a)->n) { \ if ((a)->icd.dtor) { \ size_t _ut_i; \ for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ } \ } \ free((a)->d); \ } \ (a)->n=0; \ } while(0) #define utarray_new(a,_icd) do { \ a=(UT_array*)malloc(sizeof(UT_array)); \ if (a == NULL) oom(); \ utarray_init(a,_icd); \ } while(0) #define utarray_free(a) do { \ utarray_done(a); \ free(a); \ } while(0) #define utarray_reserve(a,by) do { \ if (((a)->i+(by)) > ((a)->n)) { \ char *utarray_tmp; \ while(((a)->i+(by)) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ if (utarray_tmp == NULL) oom(); \ (a)->d=utarray_tmp; \ } \ } while(0) #define utarray_push_back(a,p) do { \ utarray_reserve(a,1); \ if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ } while(0) #define utarray_pop_back(a) do { \ if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ else { (a)->i--; } \ } while(0) #define utarray_extend_back(a) do { \ utarray_reserve(a,1); \ if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ (a)->i++; \ } while(0) #define utarray_len(a) ((a)->i) #define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) #define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) ))) #define utarray_insert(a,p,j) do { \ if (j > (a)->i) utarray_resize(a,j); \ utarray_reserve(a,1); \ if ((j) < (a)->i) { \ memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ ((a)->i - (j))*((a)->icd.sz)); \ } \ if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ (a)->i++; \ } while(0) #define utarray_inserta(a,w,j) do { \ if (utarray_len(w) == 0) break; \ if (j > (a)->i) utarray_resize(a,j); \ utarray_reserve(a,utarray_len(w)); \ if ((j) < (a)->i) { \ memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ _utarray_eltptr(a,j), \ ((a)->i - (j))*((a)->icd.sz)); \ } \ if ((a)->icd.copy) { \ size_t _ut_i; \ for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \ } \ } else { \ memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ utarray_len(w)*((a)->icd.sz)); \ } \ (a)->i += utarray_len(w); \ } while(0) #define utarray_resize(dst,num) do { \ size_t _ut_i; \ if (dst->i > (size_t)(num)) { \ if ((dst)->icd.dtor) { \ for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \ (dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \ } \ } \ } else if (dst->i < (size_t)(num)) { \ utarray_reserve(dst,num-dst->i); \ if ((dst)->icd.init) { \ for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \ (dst)->icd.init(utarray_eltptr(dst,_ut_i)); \ } \ } else { \ memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \ } \ } \ dst->i = num; \ } while(0) #define utarray_concat(dst,src) do { \ utarray_inserta((dst),(src),utarray_len(dst)); \ } while(0) #define utarray_erase(a,pos,len) do { \ if ((a)->icd.dtor) { \ size_t _ut_i; \ for(_ut_i=0; _ut_i < len; _ut_i++) { \ (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \ } \ } \ if ((a)->i > (pos+len)) { \ memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \ (((a)->i)-(pos+len))*((a)->icd.sz)); \ } \ (a)->i -= (len); \ } while(0) #define utarray_renew(a,u) do { \ if (a) utarray_clear(a); \ else utarray_new((a),(u)); \ } while(0) #define utarray_clear(a) do { \ if ((a)->i > 0) { \ if ((a)->icd.dtor) { \ size_t _ut_i; \ for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ } \ } \ (a)->i = 0; \ } \ } while(0) #define utarray_sort(a,cmp) do { \ qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ } while(0) #define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) #define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) #define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) #define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) #define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) #define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(size_t)(a)->icd.sz) : -1) /* last we pre-define a few icd for common utarrays of ints and strings */ static void utarray_str_cpy(void *dst, const void *src) { char **_src = (char**)src, **_dst = (char**)dst; *_dst = (*_src == NULL) ? NULL : strdup(*_src); } static void utarray_str_dtor(void *elt) { char **eltc = (char**)elt; if (*eltc != NULL) free(*eltc); } static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL}; #endif /* UTARRAY_H */ captagent-6.1.0.20/src/modules/database/hash/uthash.h000066400000000000000000001714201272354503300223100ustar00rootroot00000000000000/* Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ 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. 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 COPYRIGHT OWNER 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 UTHASH_H #define UTHASH_H #include /* memcmp,strlen */ #include /* ptrdiff_t */ #include /* exit() */ /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ when compiling c++ source) this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ #if defined(_MSC_VER) /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define DECLTYPE(x) (decltype(x)) #else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE #define DECLTYPE(x) #endif #elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) #define NO_DECLTYPE #define DECLTYPE(x) #else /* GNU, Sun and other compilers */ #define DECLTYPE(x) (__typeof(x)) #endif #ifdef NO_DECLTYPE #define DECLTYPE_ASSIGN(dst,src) \ do { \ char **_da_dst = (char**)(&(dst)); \ *_da_dst = (char*)(src); \ } while(0) #else #define DECLTYPE_ASSIGN(dst,src) \ do { \ (dst) = DECLTYPE(dst)(src); \ } while(0) #endif /* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ #if defined(_WIN32) #if defined(_MSC_VER) && _MSC_VER >= 1600 #include #elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) #include #else typedef unsigned int uint32_t; typedef unsigned char uint8_t; #endif #elif defined(__GNUC__) && !defined(__VXWORKS__) #include #else typedef unsigned int uint32_t; typedef unsigned char uint8_t; #endif #define UTHASH_VERSION 1.9.9 #ifndef uthash_fatal #define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ #endif #ifndef uthash_malloc #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ #endif #ifndef uthash_free #define uthash_free(ptr,sz) free(ptr) /* free fcn */ #endif #ifndef uthash_noexpand_fyi #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ #endif #ifndef uthash_expand_fyi #define uthash_expand_fyi(tbl) /* can be defined to log expands */ #endif /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ #define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ /* calculate the element whose hash handle address is hhe */ #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) #define HASH_FIND(hh,head,keyptr,keylen,out) \ do { \ out=NULL; \ if (head != NULL) { \ unsigned _hf_bkt,_hf_hashv; \ HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv) != 0) { \ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ keyptr,keylen,out); \ } \ } \ } while (0) #ifdef HASH_BLOOM #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) #define HASH_BLOOM_MAKE(tbl) \ do { \ (tbl)->bloom_nbits = HASH_BLOOM; \ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ } while (0) #define HASH_BLOOM_FREE(tbl) \ do { \ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ } while (0) #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) #define HASH_BLOOM_ADD(tbl,hashv) \ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) #define HASH_BLOOM_TEST(tbl,hashv) \ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) #else #define HASH_BLOOM_MAKE(tbl) #define HASH_BLOOM_FREE(tbl) #define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) (1) #define HASH_BLOOM_BYTELEN 0U #endif #define HASH_MAKE_TABLE(hh,head) \ do { \ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ sizeof(UT_hash_table)); \ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ (head)->hh.tbl->tail = &((head)->hh); \ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ memset((head)->hh.tbl->buckets, 0, \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ HASH_BLOOM_MAKE((head)->hh.tbl); \ (head)->hh.tbl->signature = HASH_SIGNATURE; \ } while(0) #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ do { \ replaced=NULL; \ HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ if (replaced!=NULL) { \ HASH_DELETE(hh,head,replaced); \ } \ HASH_ADD(hh,head,fieldname,keylen_in,add); \ } while(0) #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ unsigned _ha_bkt; \ (add)->hh.next = NULL; \ (add)->hh.key = (char*)(keyptr); \ (add)->hh.keylen = (unsigned)(keylen_in); \ if (!(head)) { \ head = (add); \ (head)->hh.prev = NULL; \ HASH_MAKE_TABLE(hh,head); \ } else { \ (head)->hh.tbl->tail->next = (add); \ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ (head)->hh.tbl->tail = &((add)->hh); \ } \ (head)->hh.tbl->num_items++; \ (add)->hh.tbl = (head)->hh.tbl; \ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ (add)->hh.hashv, _ha_bkt); \ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ HASH_FSCK(hh,head); \ } while(0) #define HASH_TO_BKT( hashv, num_bkts, bkt ) \ do { \ bkt = ((hashv) & ((num_bkts) - 1U)); \ } while(0) /* delete "delptr" from the hash table. * "the usual" patch-up process for the app-order doubly-linked-list. * The use of _hd_hh_del below deserves special explanation. * These used to be expressed using (delptr) but that led to a bug * if someone used the same symbol for the head and deletee, like * HASH_DELETE(hh,users,users); * We want that to work, but by changing the head (users) below * we were forfeiting our ability to further refer to the deletee (users) * in the patch-up process. Solution: use scratch space to * copy the deletee pointer, then the latter references are via that * scratch pointer rather than through the repointed (users) symbol. */ #define HASH_DELETE(hh,head,delptr) \ do { \ struct UT_hash_handle *_hd_hh_del; \ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ head = NULL; \ } else { \ unsigned _hd_bkt; \ _hd_hh_del = &((delptr)->hh); \ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ (head)->hh.tbl->tail = \ (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ (head)->hh.tbl->hho); \ } \ if ((delptr)->hh.prev != NULL) { \ ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ } else { \ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ } \ if (_hd_hh_del->next != NULL) { \ ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ (head)->hh.tbl->hho))->prev = \ _hd_hh_del->prev; \ } \ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ (head)->hh.tbl->num_items--; \ } \ HASH_FSCK(hh,head); \ } while (0) /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ #define HASH_FIND_STR(head,findstr,out) \ HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out) #define HASH_ADD_STR(head,strfield,add) \ HASH_ADD(hh,head,strfield[0],(unsigned int)strlen(add->strfield),add) #define HASH_REPLACE_STR(head,strfield,add,replaced) \ HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced) #define HASH_FIND_INT(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(int),out) #define HASH_ADD_INT(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(int),add) #define HASH_REPLACE_INT(head,intfield,add,replaced) \ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) #define HASH_FIND_PTR(head,findptr,out) \ HASH_FIND(hh,head,findptr,sizeof(void *),out) #define HASH_ADD_PTR(head,ptrfield,add) \ HASH_ADD(hh,head,ptrfield,sizeof(void *),add) #define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) #define HASH_DEL(head,delptr) \ HASH_DELETE(hh,head,delptr) /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. */ #ifdef HASH_DEBUG #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) #define HASH_FSCK(hh,head) \ do { \ struct UT_hash_handle *_thh; \ if (head) { \ unsigned _bkt_i; \ unsigned _count; \ char *_prev; \ _count = 0; \ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ unsigned _bkt_count = 0; \ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ _prev = NULL; \ while (_thh) { \ if (_prev != (char*)(_thh->hh_prev)) { \ HASH_OOPS("invalid hh_prev %p, actual %p\n", \ _thh->hh_prev, _prev ); \ } \ _bkt_count++; \ _prev = (char*)(_thh); \ _thh = _thh->hh_next; \ } \ _count += _bkt_count; \ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ HASH_OOPS("invalid bucket count %u, actual %u\n", \ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ } \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("invalid hh item count %u, actual %u\n", \ (head)->hh.tbl->num_items, _count ); \ } \ /* traverse hh in app order; check next/prev integrity, count */ \ _count = 0; \ _prev = NULL; \ _thh = &(head)->hh; \ while (_thh) { \ _count++; \ if (_prev !=(char*)(_thh->prev)) { \ HASH_OOPS("invalid prev %p, actual %p\n", \ _thh->prev, _prev ); \ } \ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ (head)->hh.tbl->hho) : NULL ); \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("invalid app item count %u, actual %u\n", \ (head)->hh.tbl->num_items, _count ); \ } \ } \ } while (0) #else #define HASH_FSCK(hh,head) #endif /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to * the descriptor to which this macro is defined for tuning the hash function. * The app can #include to get the prototype for write(2). */ #ifdef HASH_EMIT_KEYS #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ do { \ unsigned _klen = fieldlen; \ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ } while (0) #else #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) #endif /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ #ifdef HASH_FUNCTION #define HASH_FCN HASH_FUNCTION #else #define HASH_FCN HASH_JEN #endif /* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ #define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _hb_keylen=(unsigned)keylen; \ const unsigned char *_hb_key=(const unsigned char*)(key); \ (hashv) = 0; \ while (_hb_keylen-- != 0U) { \ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ } \ bkt = (hashv) & (num_bkts-1U); \ } while (0) /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ #define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _sx_i; \ const unsigned char *_hs_key=(const unsigned char*)(key); \ hashv = 0; \ for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ } \ bkt = hashv & (num_bkts-1U); \ } while (0) /* FNV-1a variation */ #define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _fn_i; \ const unsigned char *_hf_key=(const unsigned char*)(key); \ hashv = 2166136261U; \ for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ hashv = hashv ^ _hf_key[_fn_i]; \ hashv = hashv * 16777619U; \ } \ bkt = hashv & (num_bkts-1U); \ } while(0) #define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _ho_i; \ const unsigned char *_ho_key=(const unsigned char*)(key); \ hashv = 0; \ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ hashv += _ho_key[_ho_i]; \ hashv += (hashv << 10); \ hashv ^= (hashv >> 6); \ } \ hashv += (hashv << 3); \ hashv ^= (hashv >> 11); \ hashv += (hashv << 15); \ bkt = hashv & (num_bkts-1U); \ } while(0) #define HASH_JEN_MIX(a,b,c) \ do { \ a -= b; a -= c; a ^= ( c >> 13 ); \ b -= c; b -= a; b ^= ( a << 8 ); \ c -= a; c -= b; c ^= ( b >> 13 ); \ a -= b; a -= c; a ^= ( c >> 12 ); \ b -= c; b -= a; b ^= ( a << 16 ); \ c -= a; c -= b; c ^= ( b >> 5 ); \ a -= b; a -= c; a ^= ( c >> 3 ); \ b -= c; b -= a; b ^= ( a << 10 ); \ c -= a; c -= b; c ^= ( b >> 15 ); \ } while (0) #define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ unsigned const char *_hj_key=(unsigned const char*)(key); \ hashv = 0xfeedbeefu; \ _hj_i = _hj_j = 0x9e3779b9u; \ _hj_k = (unsigned)(keylen); \ while (_hj_k >= 12U) { \ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + ( (unsigned)_hj_key[2] << 16 ) \ + ( (unsigned)_hj_key[3] << 24 ) ); \ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + ( (unsigned)_hj_key[6] << 16 ) \ + ( (unsigned)_hj_key[7] << 24 ) ); \ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + ( (unsigned)_hj_key[10] << 16 ) \ + ( (unsigned)_hj_key[11] << 24 ) ); \ \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ \ _hj_key += 12; \ _hj_k -= 12U; \ } \ hashv += (unsigned)(keylen); \ switch ( _hj_k ) { \ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ case 1: _hj_i += _hj_key[0]; \ } \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ bkt = hashv & (num_bkts-1U); \ } while(0) /* The Paul Hsieh hash function */ #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif #define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned const char *_sfh_key=(unsigned const char*)(key); \ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ \ unsigned _sfh_rem = _sfh_len & 3U; \ _sfh_len >>= 2; \ hashv = 0xcafebabeu; \ \ /* Main loop */ \ for (;_sfh_len > 0U; _sfh_len--) { \ hashv += get16bits (_sfh_key); \ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ hashv = (hashv << 16) ^ _sfh_tmp; \ _sfh_key += 2U*sizeof (uint16_t); \ hashv += hashv >> 11; \ } \ \ /* Handle end cases */ \ switch (_sfh_rem) { \ case 3: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 16; \ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ hashv += hashv >> 11; \ break; \ case 2: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 11; \ hashv += hashv >> 17; \ break; \ case 1: hashv += *_sfh_key; \ hashv ^= hashv << 10; \ hashv += hashv >> 1; \ } \ \ /* Force "avalanching" of final 127 bits */ \ hashv ^= hashv << 3; \ hashv += hashv >> 5; \ hashv ^= hashv << 4; \ hashv += hashv >> 17; \ hashv ^= hashv << 25; \ hashv += hashv >> 6; \ bkt = hashv & (num_bkts-1U); \ } while(0) #ifdef HASH_USING_NO_STRICT_ALIASING /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. * MurmurHash uses the faster approach only on CPU's where we know it's safe. * * Note the preprocessor built-in defines can be emitted using: * * gcc -m64 -dM -E - < /dev/null (on gcc) * cc -## a.c (where a.c is a simple test file) (Sun Studio) */ #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) #define MUR_GETBLOCK(p,i) p[i] #else /* non intel */ #define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) #define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) #define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) #define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) #define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) #if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) #define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) #define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) #define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) #else /* assume little endian non-intel */ #define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) #define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) #define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) #endif #define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ MUR_ONE_THREE(p)))) #endif #define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) #define MUR_FMIX(_h) \ do { \ _h ^= _h >> 16; \ _h *= 0x85ebca6bu; \ _h ^= _h >> 13; \ _h *= 0xc2b2ae35u; \ _h ^= _h >> 16; \ } while(0) #define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ do { \ const uint8_t *_mur_data = (const uint8_t*)(key); \ const int _mur_nblocks = (int)(keylen) / 4; \ uint32_t _mur_h1 = 0xf88D5353u; \ uint32_t _mur_c1 = 0xcc9e2d51u; \ uint32_t _mur_c2 = 0x1b873593u; \ uint32_t _mur_k1 = 0; \ const uint8_t *_mur_tail; \ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ int _mur_i; \ for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ _mur_k1 *= _mur_c2; \ \ _mur_h1 ^= _mur_k1; \ _mur_h1 = MUR_ROTL32(_mur_h1,13); \ _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ } \ _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ _mur_k1=0; \ switch((keylen) & 3U) { \ case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ _mur_k1 *= _mur_c2; \ _mur_h1 ^= _mur_k1; \ } \ _mur_h1 ^= (uint32_t)(keylen); \ MUR_FMIX(_mur_h1); \ hashv = _mur_h1; \ bkt = hashv & (num_bkts-1U); \ } while(0) #endif /* HASH_USING_NO_STRICT_ALIASING */ /* key comparison function; return 0 if keys equal */ #define HASH_KEYCMP(a,b,len) memcmp(a,b,(unsigned long)(len)) /* iterate over items in a known bucket to find desired item */ #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ do { \ if (head.hh_head != NULL) { DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); } \ else { out=NULL; } \ while (out != NULL) { \ if ((out)->hh.keylen == (keylen_in)) { \ if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) { break; } \ } \ if ((out)->hh.hh_next != NULL) { DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); } \ else { out = NULL; } \ } \ } while(0) /* add an item to a bucket */ #define HASH_ADD_TO_BKT(head,addhh) \ do { \ head.count++; \ (addhh)->hh_next = head.hh_head; \ (addhh)->hh_prev = NULL; \ if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ (head).hh_head=addhh; \ if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ && ((addhh)->tbl->noexpand != 1U)) { \ HASH_EXPAND_BUCKETS((addhh)->tbl); \ } \ } while(0) /* remove an item from a given bucket */ #define HASH_DEL_IN_BKT(hh,head,hh_del) \ (head).count--; \ if ((head).hh_head == hh_del) { \ (head).hh_head = hh_del->hh_next; \ } \ if (hh_del->hh_prev) { \ hh_del->hh_prev->hh_next = hh_del->hh_next; \ } \ if (hh_del->hh_next) { \ hh_del->hh_next->hh_prev = hh_del->hh_prev; \ } /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the * items will distribute more or less evenly into the new buckets * (the extent to which this is true is a measure of the quality of * the hash function as it applies to the key domain). * * With the items distributed into more buckets, the chain length * (item count) in each bucket is reduced. Thus by expanding buckets * the hash keeps a bound on the chain length. This bounded chain * length is the essence of how a hash provides constant time lookup. * * The calculation of tbl->ideal_chain_maxlen below deserves some * explanation. First, keep in mind that we're calculating the ideal * maximum chain length based on the *new* (doubled) bucket count. * In fractions this is just n/b (n=number of items,b=new num buckets). * Since the ideal chain length is an integer, we want to calculate * ceil(n/b). We don't depend on floating point arithmetic in this * hash, so to calculate ceil(n/b) with integers we could write * * ceil(n/b) = (n/b) + ((n%b)?1:0) * * and in fact a previous version of this hash did just that. * But now we have improved things a bit by recognizing that b is * always a power of two. We keep its base 2 log handy (call it lb), * so now we can write this with a bit shift and logical AND: * * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) * */ #define HASH_EXPAND_BUCKETS(tbl) \ do { \ unsigned _he_bkt; \ unsigned _he_bkt_i; \ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ memset(_he_new_buckets, 0, \ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ tbl->ideal_chain_maxlen = \ (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ tbl->nonideal_items = 0; \ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ { \ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ while (_he_thh != NULL) { \ _he_hh_nxt = _he_thh->hh_next; \ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ tbl->nonideal_items++; \ _he_newbkt->expand_mult = _he_newbkt->count / \ tbl->ideal_chain_maxlen; \ } \ _he_thh->hh_prev = NULL; \ _he_thh->hh_next = _he_newbkt->hh_head; \ if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ _he_thh; } \ _he_newbkt->hh_head = _he_thh; \ _he_thh = _he_hh_nxt; \ } \ } \ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ tbl->num_buckets *= 2U; \ tbl->log2_num_buckets++; \ tbl->buckets = _he_new_buckets; \ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ (tbl->ineff_expands+1U) : 0U; \ if (tbl->ineff_expands > 1U) { \ tbl->noexpand=1; \ uthash_noexpand_fyi(tbl); \ } \ uthash_expand_fyi(tbl); \ } while(0) /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ /* Note that HASH_SORT assumes the hash handle name to be hh. * HASH_SRT was added to allow the hash handle name to be passed in. */ #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) #define HASH_SRT(hh,head,cmpfcn) \ do { \ unsigned _hs_i; \ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ if (head != NULL) { \ _hs_insize = 1; \ _hs_looping = 1; \ _hs_list = &((head)->hh); \ while (_hs_looping != 0U) { \ _hs_p = _hs_list; \ _hs_list = NULL; \ _hs_tail = NULL; \ _hs_nmerges = 0; \ while (_hs_p != NULL) { \ _hs_nmerges++; \ _hs_q = _hs_p; \ _hs_psize = 0; \ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ _hs_psize++; \ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ if (! (_hs_q) ) { break; } \ } \ _hs_qsize = _hs_insize; \ while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ if (_hs_psize == 0U) { \ _hs_e = _hs_q; \ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_qsize--; \ } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ _hs_e = _hs_p; \ if (_hs_p != NULL){ \ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ ((void*)((char*)(_hs_p->next) + \ (head)->hh.tbl->hho)) : NULL); \ } \ _hs_psize--; \ } else if (( \ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ ) <= 0) { \ _hs_e = _hs_p; \ if (_hs_p != NULL){ \ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ ((void*)((char*)(_hs_p->next) + \ (head)->hh.tbl->hho)) : NULL); \ } \ _hs_psize--; \ } else { \ _hs_e = _hs_q; \ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_qsize--; \ } \ if ( _hs_tail != NULL ) { \ _hs_tail->next = ((_hs_e != NULL) ? \ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ } else { \ _hs_list = _hs_e; \ } \ if (_hs_e != NULL) { \ _hs_e->prev = ((_hs_tail != NULL) ? \ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ } \ _hs_tail = _hs_e; \ } \ _hs_p = _hs_q; \ } \ if (_hs_tail != NULL){ \ _hs_tail->next = NULL; \ } \ if ( _hs_nmerges <= 1U ) { \ _hs_looping=0; \ (head)->hh.tbl->tail = _hs_tail; \ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ } \ _hs_insize *= 2U; \ } \ HASH_FSCK(hh,head); \ } \ } while (0) /* This function selects items from one hash into another hash. * The end result is that the selected items have dual presence * in both hashes. There is no copy of the items made; rather * they are added into the new hash through a secondary hash * hash handle that must be present in the structure. */ #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ unsigned _src_bkt, _dst_bkt; \ void *_last_elt=NULL, *_elt; \ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ if (src != NULL) { \ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ _src_hh != NULL; \ _src_hh = _src_hh->hh_next) { \ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ if (cond(_elt)) { \ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ _dst_hh->key = _src_hh->key; \ _dst_hh->keylen = _src_hh->keylen; \ _dst_hh->hashv = _src_hh->hashv; \ _dst_hh->prev = _last_elt; \ _dst_hh->next = NULL; \ if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ if (dst == NULL) { \ DECLTYPE_ASSIGN(dst,_elt); \ HASH_MAKE_TABLE(hh_dst,dst); \ } else { \ _dst_hh->tbl = (dst)->hh_dst.tbl; \ } \ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ (dst)->hh_dst.tbl->num_items++; \ _last_elt = _elt; \ _last_elt_hh = _dst_hh; \ } \ } \ } \ } \ HASH_FSCK(hh_dst,dst); \ } while (0) #define HASH_CLEAR(hh,head) \ do { \ if (head != NULL) { \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ (head)=NULL; \ } \ } while(0) #define HASH_OVERHEAD(hh,head) \ ((head != NULL) ? ( \ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ sizeof(UT_hash_table) + \ (HASH_BLOOM_BYTELEN))) : 0U) #ifdef NO_DECLTYPE #define HASH_ITER(hh,head,el,tmp) \ for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) #else #define HASH_ITER(hh,head,el,tmp) \ for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) #endif /* obtain a count of items in the hash */ #define HASH_COUNT(head) HASH_CNT(hh,head) #define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) typedef struct UT_hash_bucket { struct UT_hash_handle *hh_head; unsigned count; /* expand_mult is normally set to 0. In this situation, the max chain length * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If * the bucket's chain exceeds this length, bucket expansion is triggered). * However, setting expand_mult to a non-zero value delays bucket expansion * (that would be triggered by additions to this particular bucket) * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. * (The multiplier is simply expand_mult+1). The whole idea of this * multiplier is to reduce bucket expansions, since they are expensive, in * situations where we know that a particular bucket tends to be overused. * It is better to let its chain length grow to a longer yet-still-bounded * value, than to do an O(n) bucket expansion too often. */ unsigned expand_mult; } UT_hash_bucket; /* random signature used only to find hash tables in external analysis */ #define HASH_SIGNATURE 0xa0111fe1u #define HASH_BLOOM_SIGNATURE 0xb12220f2u typedef struct UT_hash_table { UT_hash_bucket *buckets; unsigned num_buckets, log2_num_buckets; unsigned num_items; struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ /* in an ideal situation (all buckets used equally), no bucket would have * more than ceil(#items/#buckets) items. that's the ideal chain length. */ unsigned ideal_chain_maxlen; /* nonideal_items is the number of items in the hash whose chain position * exceeds the ideal chain maxlen. these items pay the penalty for an uneven * hash distribution; reaching them in a chain traversal takes >ideal steps */ unsigned nonideal_items; /* ineffective expands occur when a bucket doubling was performed, but * afterward, more than half the items in the hash had nonideal chain * positions. If this happens on two consecutive expansions we inhibit any * further expansion, as it's not helping; this happens when the hash * function isn't a good fit for the key domain. When expansion is inhibited * the hash will still work, albeit no longer in constant time. */ unsigned ineff_expands, noexpand; uint32_t signature; /* used only to find hash tables in external analysis */ #ifdef HASH_BLOOM uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ uint8_t *bloom_bv; uint8_t bloom_nbits; #endif } UT_hash_table; typedef struct UT_hash_handle { struct UT_hash_table *tbl; void *prev; /* prev element in app order */ void *next; /* next element in app order */ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ struct UT_hash_handle *hh_next; /* next hh in bucket order */ void *key; /* ptr to enclosing struct's key */ unsigned keylen; /* enclosing struct's key len */ unsigned hashv; /* result of hash-fcn(key) */ } UT_hash_handle; #endif /* UTHASH_H */ captagent-6.1.0.20/src/modules/database/hash/utlist.h000066400000000000000000001734441272354503300223500ustar00rootroot00000000000000/* Copyright (c) 2007-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ 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. 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 COPYRIGHT OWNER 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 UTLIST_H #define UTLIST_H #define UTLIST_VERSION 1.9.9 #include /* * This file contains macros to manipulate singly and doubly-linked lists. * * 1. LL_ macros: singly-linked lists. * 2. DL_ macros: doubly-linked lists. * 3. CDL_ macros: circular doubly-linked lists. * * To use singly-linked lists, your structure must have a "next" pointer. * To use doubly-linked lists, your structure must "prev" and "next" pointers. * Either way, the pointer to the head of the list must be initialized to NULL. * * ----------------.EXAMPLE ------------------------- * struct item { * int id; * struct item *prev, *next; * } * * struct item *list = NULL: * * int main() { * struct item *item; * ... allocate and populate item ... * DL_APPEND(list, item); * } * -------------------------------------------------- * * For doubly-linked lists, the append and delete macros are O(1) * For singly-linked lists, append and delete are O(n) but prepend is O(1) * The sort macro is O(n log(n)) for all types of single/double/circular lists. */ /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ when compiling c++ code), this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ #ifdef _MSC_VER /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define LDECLTYPE(x) decltype(x) #else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE #define LDECLTYPE(x) char* #endif #elif defined(__ICCARM__) #define NO_DECLTYPE #define LDECLTYPE(x) char* #else /* GNU, Sun and other compilers */ #define LDECLTYPE(x) __typeof(x) #endif /* for VS2008 we use some workarounds to get around the lack of decltype, * namely, we always reassign our tmp variable to the list head if we need * to dereference its prev/next pointers, and save/restore the real head.*/ #ifdef NO_DECLTYPE #define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } #define _NEXT(elt,list,next) ((char*)((list)->next)) #define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } /* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ #define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } #define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } #define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } #else #define _SV(elt,list) #define _NEXT(elt,list,next) ((elt)->next) #define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) /* #define _PREV(elt,list,prev) ((elt)->prev) */ #define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) #define _RS(list) #define _CASTASGN(a,b) (a)=(b) #endif /****************************************************************************** * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * * Unwieldy variable names used here to avoid shadowing passed-in variables. * *****************************************************************************/ #define LL_SORT(list, cmp) \ LL_SORT2(list, cmp, next) #define LL_SORT2(list, cmp, next) \ do { \ LDECLTYPE(list) _ls_p; \ LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ _CASTASGN(_ls_p,list); \ list = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ } else if (_ls_qsize == 0 || !_ls_q) { \ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ } else { \ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ } \ if (_ls_tail) { \ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ } else { \ _CASTASGN(list,_ls_e); \ } \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ if (_ls_tail) { \ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ } \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) #define DL_SORT(list, cmp) \ DL_SORT2(list, cmp, prev, next) #define DL_SORT2(list, cmp, prev, next) \ do { \ LDECLTYPE(list) _ls_p; \ LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ _CASTASGN(_ls_p,list); \ list = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ } else if ((_ls_qsize == 0) || (!_ls_q)) { \ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ } else { \ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ } \ if (_ls_tail) { \ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ } else { \ _CASTASGN(list,_ls_e); \ } \ _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ _CASTASGN(list->prev, _ls_tail); \ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) #define CDL_SORT(list, cmp) \ CDL_SORT2(list, cmp, prev, next) #define CDL_SORT2(list, cmp, prev, next) \ do { \ LDECLTYPE(list) _ls_p; \ LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ LDECLTYPE(list) _ls_oldhead; \ LDECLTYPE(list) _tmp; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ _CASTASGN(_ls_p,list); \ _CASTASGN(_ls_oldhead,list); \ list = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ _SV(_ls_q,list); \ if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ _ls_q = NULL; \ } else { \ _ls_q = _NEXT(_ls_q,list,next); \ } \ _RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } else if (_ls_qsize == 0 || !_ls_q) { \ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else { \ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } \ if (_ls_tail) { \ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ } else { \ _CASTASGN(list,_ls_e); \ } \ _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ _CASTASGN(list->prev,_ls_tail); \ _CASTASGN(_tmp,list); \ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) /****************************************************************************** * singly linked list macros (non-circular) * *****************************************************************************/ #define LL_PREPEND(head,add) \ LL_PREPEND2(head,add,next) #define LL_PREPEND2(head,add,next) \ do { \ (add)->next = head; \ head = add; \ } while (0) #define LL_CONCAT(head1,head2) \ LL_CONCAT2(head1,head2,next) #define LL_CONCAT2(head1,head2,next) \ do { \ LDECLTYPE(head1) _tmp; \ if (head1) { \ _tmp = head1; \ while (_tmp->next) { _tmp = _tmp->next; } \ _tmp->next=(head2); \ } else { \ (head1)=(head2); \ } \ } while (0) #define LL_APPEND(head,add) \ LL_APPEND2(head,add,next) #define LL_APPEND2(head,add,next) \ do { \ LDECLTYPE(head) _tmp; \ (add)->next=NULL; \ if (head) { \ _tmp = head; \ while (_tmp->next) { _tmp = _tmp->next; } \ _tmp->next=(add); \ } else { \ (head)=(add); \ } \ } while (0) #define LL_DELETE(head,del) \ LL_DELETE2(head,del,next) #define LL_DELETE2(head,del,next) \ do { \ LDECLTYPE(head) _tmp; \ if ((head) == (del)) { \ (head)=(head)->next; \ } else { \ _tmp = head; \ while (_tmp->next && (_tmp->next != (del))) { \ _tmp = _tmp->next; \ } \ if (_tmp->next) { \ _tmp->next = ((del)->next); \ } \ } \ } while (0) /* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ #define LL_APPEND_VS2008(head,add) \ LL_APPEND2_VS2008(head,add,next) #define LL_APPEND2_VS2008(head,add,next) \ do { \ if (head) { \ (add)->next = head; /* use add->next as a temp variable */ \ while ((add)->next->next) { (add)->next = (add)->next->next; } \ (add)->next->next=(add); \ } else { \ (head)=(add); \ } \ (add)->next=NULL; \ } while (0) #define LL_DELETE_VS2008(head,del) \ LL_DELETE2_VS2008(head,del,next) #define LL_DELETE2_VS2008(head,del,next) \ do { \ if ((head) == (del)) { \ (head)=(head)->next; \ } else { \ char *_tmp = (char*)(head); \ while ((head)->next && ((head)->next != (del))) { \ head = (head)->next; \ } \ if ((head)->next) { \ (head)->next = ((del)->next); \ } \ { \ char **_head_alias = (char**)&(head); \ *_head_alias = _tmp; \ } \ } \ } while (0) #ifdef NO_DECLTYPE #undef LL_APPEND #define LL_APPEND LL_APPEND_VS2008 #undef LL_DELETE #define LL_DELETE LL_DELETE_VS2008 #undef LL_DELETE2 #define LL_DELETE2 LL_DELETE2_VS2008 #undef LL_APPEND2 #define LL_APPEND2 LL_APPEND2_VS2008 #undef LL_CONCAT /* no LL_CONCAT_VS2008 */ #undef DL_CONCAT /* no DL_CONCAT_VS2008 */ #endif /* end VS2008 replacements */ #define LL_COUNT(head,el,counter) \ LL_COUNT2(head,el,counter,next) \ #define LL_COUNT2(head,el,counter,next) \ { \ counter = 0; \ LL_FOREACH2(head,el,next){ ++counter; } \ } #define LL_FOREACH(head,el) \ LL_FOREACH2(head,el,next) #define LL_FOREACH2(head,el,next) \ for(el=head;el;el=(el)->next) #define LL_FOREACH_SAFE(head,el,tmp) \ LL_FOREACH_SAFE2(head,el,tmp,next) #define LL_FOREACH_SAFE2(head,el,tmp,next) \ for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) #define LL_SEARCH_SCALAR(head,out,field,val) \ LL_SEARCH_SCALAR2(head,out,field,val,next) #define LL_SEARCH_SCALAR2(head,out,field,val,next) \ do { \ LL_FOREACH2(head,out,next) { \ if ((out)->field == (val)) break; \ } \ } while(0) #define LL_SEARCH(head,out,elt,cmp) \ LL_SEARCH2(head,out,elt,cmp,next) #define LL_SEARCH2(head,out,elt,cmp,next) \ do { \ LL_FOREACH2(head,out,next) { \ if ((cmp(out,elt))==0) break; \ } \ } while(0) #define LL_REPLACE_ELEM2(head, el, add, next) \ do { \ LDECLTYPE(head) _tmp; \ assert(head != NULL); \ assert(el != NULL); \ assert(add != NULL); \ (add)->next = (el)->next; \ if ((head) == (el)) { \ (head) = (add); \ } else { \ _tmp = head; \ while (_tmp->next && (_tmp->next != (el))) { \ _tmp = _tmp->next; \ } \ if (_tmp->next) { \ _tmp->next = (add); \ } \ } \ } while (0) #define LL_REPLACE_ELEM(head, el, add) \ LL_REPLACE_ELEM2(head, el, add, next) #define LL_PREPEND_ELEM2(head, el, add, next) \ do { \ if((el)) { \ LDECLTYPE(head) _tmp; \ assert(head != NULL); \ assert(add != NULL); \ (add)->next = (el); \ if ((head) == (el)) { \ (head) = (add); \ } else { \ _tmp = head; \ while (_tmp->next && (_tmp->next != (el))) { \ _tmp = _tmp->next; \ } \ if (_tmp->next) { \ _tmp->next = (add); \ } \ } \ } else { \ LL_APPEND2(head, add, next); \ } \ } while (0) \ #define LL_PREPEND_ELEM(head, el, add) \ LL_PREPEND_ELEM2(head, el, add, next) #define LL_APPEND_ELEM2(head, el, add, next) \ do { \ if((el)) { \ assert(head != NULL); \ assert(add != NULL); \ (add)->next = (el)->next; \ (el)->next = (add); \ } else { \ LL_PREPEND2(head, add, next); \ } \ } while (0) \ #define LL_APPEND_ELEM(head, el, add) \ LL_APPEND_ELEM2(head, el, add, next) /****************************************************************************** * doubly linked list macros (non-circular) * *****************************************************************************/ #define DL_PREPEND(head,add) \ DL_PREPEND2(head,add,prev,next) #define DL_PREPEND2(head,add,prev,next) \ do { \ (add)->next = head; \ if (head) { \ (add)->prev = (head)->prev; \ (head)->prev = (add); \ } else { \ (add)->prev = (add); \ } \ (head) = (add); \ } while (0) #define DL_APPEND(head,add) \ DL_APPEND2(head,add,prev,next) #define DL_APPEND2(head,add,prev,next) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (head)->prev->next = (add); \ (head)->prev = (add); \ (add)->next = NULL; \ } else { \ (head)=(add); \ (head)->prev = (head); \ (head)->next = NULL; \ } \ } while (0) #define DL_CONCAT(head1,head2) \ DL_CONCAT2(head1,head2,prev,next) #define DL_CONCAT2(head1,head2,prev,next) \ do { \ LDECLTYPE(head1) _tmp; \ if (head2) { \ if (head1) { \ _tmp = (head2)->prev; \ (head2)->prev = (head1)->prev; \ (head1)->prev->next = (head2); \ (head1)->prev = _tmp; \ } else { \ (head1)=(head2); \ } \ } \ } while (0) #define DL_DELETE(head,del) \ DL_DELETE2(head,del,prev,next) #define DL_DELETE2(head,del,prev,next) \ do { \ assert((del)->prev != NULL); \ if ((del)->prev == (del)) { \ (head)=NULL; \ } else if ((del)==(head)) { \ (del)->next->prev = (del)->prev; \ (head) = (del)->next; \ } else { \ (del)->prev->next = (del)->next; \ if ((del)->next) { \ (del)->next->prev = (del)->prev; \ } else { \ (head)->prev = (del)->prev; \ } \ } \ } while (0) #define DL_COUNT(head,el,counter) \ DL_COUNT2(head,el,counter,next) \ #define DL_COUNT2(head,el,counter,next) \ { \ counter = 0; \ DL_FOREACH2(head,el,next){ ++counter; } \ } #define DL_FOREACH(head,el) \ DL_FOREACH2(head,el,next) #define DL_FOREACH2(head,el,next) \ for(el=head;el;el=(el)->next) /* this version is safe for deleting the elements during iteration */ #define DL_FOREACH_SAFE(head,el,tmp) \ DL_FOREACH_SAFE2(head,el,tmp,next) #define DL_FOREACH_SAFE2(head,el,tmp,next) \ for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) /* these are identical to their singly-linked list counterparts */ #define DL_SEARCH_SCALAR LL_SEARCH_SCALAR #define DL_SEARCH LL_SEARCH #define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 #define DL_SEARCH2 LL_SEARCH2 #define DL_REPLACE_ELEM2(head, el, add, prev, next) \ do { \ assert(head != NULL); \ assert(el != NULL); \ assert(add != NULL); \ if ((head) == (el)) { \ (head) = (add); \ (add)->next = (el)->next; \ if ((el)->next == NULL) { \ (add)->prev = (add); \ } else { \ (add)->prev = (el)->prev; \ (add)->next->prev = (add); \ } \ } else { \ (add)->next = (el)->next; \ (add)->prev = (el)->prev; \ (add)->prev->next = (add); \ if ((el)->next == NULL) { \ (head)->prev = (add); \ } else { \ (add)->next->prev = (add); \ } \ } \ } while (0) #define DL_REPLACE_ELEM(head, el, add) \ DL_REPLACE_ELEM2(head, el, add, prev, next) #define DL_PREPEND_ELEM2(head, el, add, prev, next) \ do { \ if((el)) { \ assert(head != NULL); \ assert(add != NULL); \ (add)->next = (el); \ (add)->prev = (el)->prev; \ (el)->prev = (add); \ if ((head) == (el)) { \ (head) = (add); \ } else { \ (add)->prev->next = (add); \ } \ } else { \ DL_APPEND2(head, add, prev, next); \ } \ } while (0) \ #define DL_PREPEND_ELEM(head, el, add) \ DL_PREPEND_ELEM2(head, el, add, prev, next) #define DL_APPEND_ELEM2(head, el, add, prev, next) \ do { \ if((el)) { \ assert(head != NULL); \ assert(add != NULL); \ (add)->next = (el)->next; \ (add)->prev = (el); \ (el)->next = (add); \ if ((add)->next) { \ (add)->next->prev = (add); \ } else { \ (head)->prev = (add); \ } \ } else { \ DL_PREPEND2(head, add, prev, next); \ } \ } while (0) \ #define DL_APPEND_ELEM(head, el, add) \ DL_APPEND_ELEM2(head, el, add, prev, next) /****************************************************************************** * circular doubly linked list macros * *****************************************************************************/ #define CDL_APPEND(head,add) \ CDL_APPEND2(head,add,prev,next) #define CDL_APPEND2(head,add,prev,next) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (add)->next = (head); \ (head)->prev = (add); \ (add)->prev->next = (add); \ } else { \ (add)->prev = (add); \ (add)->next = (add); \ (head) = (add); \ } \ } while (0) #define CDL_PREPEND(head,add) \ CDL_PREPEND2(head,add,prev,next) #define CDL_PREPEND2(head,add,prev,next) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (add)->next = (head); \ (head)->prev = (add); \ (add)->prev->next = (add); \ } else { \ (add)->prev = (add); \ (add)->next = (add); \ } \ (head) = (add); \ } while (0) #define CDL_DELETE(head,del) \ CDL_DELETE2(head,del,prev,next) #define CDL_DELETE2(head,del,prev,next) \ do { \ if ( ((head)==(del)) && ((head)->next == (head))) { \ (head) = NULL; \ } else { \ (del)->next->prev = (del)->prev; \ (del)->prev->next = (del)->next; \ if ((del) == (head)) (head)=(del)->next; \ } \ } while (0) #define CDL_COUNT(head,el,counter) \ CDL_COUNT2(head,el,counter,next) \ #define CDL_COUNT2(head, el, counter,next) \ { \ counter = 0; \ CDL_FOREACH2(head,el,next){ ++counter; } \ } #define CDL_FOREACH(head,el) \ CDL_FOREACH2(head,el,next) #define CDL_FOREACH2(head,el,next) \ for(el=head;el;el=(((el)->next==head) ? 0L : (el)->next)) #define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) #define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ (el) && ((tmp2)=(el)->next, 1); \ ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) #define CDL_SEARCH_SCALAR(head,out,field,val) \ CDL_SEARCH_SCALAR2(head,out,field,val,next) #define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ do { \ CDL_FOREACH2(head,out,next) { \ if ((out)->field == (val)) break; \ } \ } while(0) #define CDL_SEARCH(head,out,elt,cmp) \ CDL_SEARCH2(head,out,elt,cmp,next) #define CDL_SEARCH2(head,out,elt,cmp,next) \ do { \ CDL_FOREACH2(head,out,next) { \ if ((cmp(out,elt))==0) break; \ } \ } while(0) #define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ do { \ assert(head != NULL); \ assert(el != NULL); \ assert(add != NULL); \ if ((el)->next == (el)) { \ (add)->next = (add); \ (add)->prev = (add); \ (head) = (add); \ } else { \ (add)->next = (el)->next; \ (add)->prev = (el)->prev; \ (add)->next->prev = (add); \ (add)->prev->next = (add); \ if ((head) == (el)) { \ (head) = (add); \ } \ } \ } while (0) #define CDL_REPLACE_ELEM(head, el, add) \ CDL_REPLACE_ELEM2(head, el, add, prev, next) #define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ do { \ if((el)) \ { \ assert(head != NULL); \ assert(add != NULL); \ (add)->next = (el); \ (add)->prev = (el)->prev; \ (el)->prev = (add); \ (add)->prev->next = (add); \ if ((head) == (el)) \ (head) = (add); \ } else { \ CDL_APPEND2(head, add, prev, next); \ } \ } while (0) #define CDL_PREPEND_ELEM(head, el, add) \ CDL_PREPEND_ELEM2(head, el, add, prev, next) #define CDL_APPEND_ELEM2(head, el, add, prev, next) \ do { \ if((el)) \ { \ assert(head != NULL); \ assert(add != NULL); \ (add)->next = (el)->next; \ (add)->prev = (el); \ (el)->next = (add); \ (add)->next->prev = (add); \ } else { \ CDL_PREPEND2(head, add, prev, next); \ } \ } while (0) #define CDL_APPEND_ELEM(head, el, add) \ CDL_APPEND_ELEM2(head, el, add, prev, next) #endif /* UTLIST_H */ captagent-6.1.0.20/src/modules/database/hash/utstring.h000066400000000000000000000266561272354503300227050ustar00rootroot00000000000000/* Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ 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. 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 COPYRIGHT OWNER 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. */ /* a dynamic string implementation using macros */ #ifndef UTSTRING_H #define UTSTRING_H #define UTSTRING_VERSION 1.9.9 #ifdef __GNUC__ #define _UNUSED_ __attribute__ ((__unused__)) #else #define _UNUSED_ #endif #include #include #include #include #ifndef oom #define oom() exit(-1) #endif typedef struct { char *d; size_t n; /* allocd size */ size_t i; /* index of first unused byte */ } UT_string; #define utstring_reserve(s,amt) \ do { \ if (((s)->n - (s)->i) < (size_t)(amt)) { \ char *utstring_tmp = (char*)realloc( \ (s)->d, (s)->n + (amt)); \ if (utstring_tmp == NULL) oom(); \ (s)->d = utstring_tmp; \ (s)->n += (amt); \ } \ } while(0) #define utstring_init(s) \ do { \ (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ utstring_reserve(s,100); \ (s)->d[0] = '\0'; \ } while(0) #define utstring_done(s) \ do { \ if ((s)->d != NULL) free((s)->d); \ (s)->n = 0; \ } while(0) #define utstring_free(s) \ do { \ utstring_done(s); \ free(s); \ } while(0) #define utstring_new(s) \ do { \ s = (UT_string*)calloc(sizeof(UT_string),1); \ if (!s) oom(); \ utstring_init(s); \ } while(0) #define utstring_renew(s) \ do { \ if (s) { \ utstring_clear(s); \ } else { \ utstring_new(s); \ } \ } while(0) #define utstring_clear(s) \ do { \ (s)->i = 0; \ (s)->d[0] = '\0'; \ } while(0) #define utstring_bincpy(s,b,l) \ do { \ utstring_reserve((s),(l)+1); \ if (l) memcpy(&(s)->d[(s)->i], b, l); \ (s)->i += (l); \ (s)->d[(s)->i]='\0'; \ } while(0) #define utstring_concat(dst,src) \ do { \ utstring_reserve((dst),((src)->i)+1); \ if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ (dst)->i += (src)->i; \ (dst)->d[(dst)->i]='\0'; \ } while(0) #define utstring_len(s) ((unsigned)((s)->i)) #define utstring_body(s) ((s)->d) _UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { int n; va_list cp; for (;;) { #ifdef _WIN32 cp = ap; #else va_copy(cp, ap); #endif n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); va_end(cp); if ((n > -1) && ((size_t) n < (s->n-s->i))) { s->i += n; return; } /* Else try again with more space. */ if (n > -1) utstring_reserve(s,n+1); /* exact */ else utstring_reserve(s,(s->n)*2); /* 2x */ } } #ifdef __GNUC__ /* support printf format checking (2=the format string, 3=start of varargs) */ static void utstring_printf(UT_string *s, const char *fmt, ...) __attribute__ (( format( printf, 2, 3) )); #endif _UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) { va_list ap; va_start(ap,fmt); utstring_printf_va(s,fmt,ap); va_end(ap); } /******************************************************************************* * begin substring search functions * ******************************************************************************/ /* Build KMP table from left to right. */ _UNUSED_ static void _utstring_BuildTable( const char *P_Needle, size_t P_NeedleLen, long *P_KMP_Table) { long i, j; i = 0; j = i - 1; P_KMP_Table[i] = j; while (i < (long) P_NeedleLen) { while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) { j = P_KMP_Table[j]; } i++; j++; if (i < (long) P_NeedleLen) { if (P_Needle[i] == P_Needle[j]) { P_KMP_Table[i] = P_KMP_Table[j]; } else { P_KMP_Table[i] = j; } } else { P_KMP_Table[i] = j; } } return; } /* Build KMP table from right to left. */ _UNUSED_ static void _utstring_BuildTableR( const char *P_Needle, size_t P_NeedleLen, long *P_KMP_Table) { long i, j; i = P_NeedleLen - 1; j = i + 1; P_KMP_Table[i + 1] = j; while (i >= 0) { while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) { j = P_KMP_Table[j + 1]; } i--; j--; if (i >= 0) { if (P_Needle[i] == P_Needle[j]) { P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; } else { P_KMP_Table[i + 1] = j; } } else { P_KMP_Table[i + 1] = j; } } return; } /* Search data from left to right. ( Multiple search mode. ) */ _UNUSED_ static long _utstring_find( const char *P_Haystack, size_t P_HaystackLen, const char *P_Needle, size_t P_NeedleLen, long *P_KMP_Table) { long i, j; long V_FindPosition = -1; /* Search from left to right. */ i = j = 0; while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) { while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) { i = P_KMP_Table[i]; } i++; j++; if (i >= (int)P_NeedleLen) { /* Found. */ V_FindPosition = j - i; break; } } return V_FindPosition; } /* Search data from right to left. ( Multiple search mode. ) */ _UNUSED_ static long _utstring_findR( const char *P_Haystack, size_t P_HaystackLen, const char *P_Needle, size_t P_NeedleLen, long *P_KMP_Table) { long i, j; long V_FindPosition = -1; /* Search from right to left. */ j = (P_HaystackLen - 1); i = (P_NeedleLen - 1); while ( (j >= 0) && (j >= i) ) { while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) { i = P_KMP_Table[i + 1]; } i--; j--; if (i < 0) { /* Found. */ V_FindPosition = j + 1; break; } } return V_FindPosition; } /* Search data from left to right. ( One time search mode. ) */ _UNUSED_ static long utstring_find( UT_string *s, long P_StartPosition, /* Start from 0. -1 means last position. */ const char *P_Needle, size_t P_NeedleLen) { long V_StartPosition; long V_HaystackLen; long *V_KMP_Table; long V_FindPosition = -1; if (P_StartPosition < 0) { V_StartPosition = s->i + P_StartPosition; } else { V_StartPosition = P_StartPosition; } V_HaystackLen = s->i - V_StartPosition; if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) { V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); if (V_KMP_Table != NULL) { _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); V_FindPosition = _utstring_find(s->d + V_StartPosition, V_HaystackLen, P_Needle, P_NeedleLen, V_KMP_Table); if (V_FindPosition >= 0) { V_FindPosition += V_StartPosition; } free(V_KMP_Table); } } return V_FindPosition; } /* Search data from right to left. ( One time search mode. ) */ _UNUSED_ static long utstring_findR( UT_string *s, long P_StartPosition, /* Start from 0. -1 means last position. */ const char *P_Needle, size_t P_NeedleLen) { long V_StartPosition; long V_HaystackLen; long *V_KMP_Table; long V_FindPosition = -1; if (P_StartPosition < 0) { V_StartPosition = s->i + P_StartPosition; } else { V_StartPosition = P_StartPosition; } V_HaystackLen = V_StartPosition + 1; if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) { V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); if (V_KMP_Table != NULL) { _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); V_FindPosition = _utstring_findR(s->d, V_HaystackLen, P_Needle, P_NeedleLen, V_KMP_Table); free(V_KMP_Table); } } return V_FindPosition; } /******************************************************************************* * end substring search functions * ******************************************************************************/ #endif /* UTSTRING_H */ captagent-6.1.0.20/src/modules/database/redis/000077500000000000000000000000001272354503300210215ustar00rootroot00000000000000captagent-6.1.0.20/src/modules/database/redis/Makefile.am000066400000000000000000000006771272354503300230670ustar00rootroot00000000000000include $(top_srcdir)/modules.am SUBDIRS = . noinst_HEADERS = database_redis.h # database_redis_la_SOURCES = database_redis.c database_redis_la_CFLAGS = -Wall ${MODULE_CFLAGS} ${EXPAT_LIBS} ${JSON_LIBS} ${MYSQL_LIBS} ${HIREDIS_LIBS} database_redis_la_LDFLAGS = -module -avoid-version database_redis_laconfdir = $(confdir) database_redis_laconf_DATA = $(top_srcdir)/conf/database_redis.xml include_HEADERS = mod_LTLIBRARIES = database_redis.la captagent-6.1.0.20/src/modules/database/redis/database_redis.c000066400000000000000000000514021272354503300241210ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012-2015 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_REDIS #include "hiredis/hiredis.h" #endif #include #include "database_redis.h" xml_node *module_xml_config = NULL; char *module_name="database_redis"; uint64_t module_serial = 0; char *module_description = NULL; static database_redis_stats_t stats; #ifdef USE_REDIS redisContext *redisCon[MAX_DATABASE]; #endif uint8_t link_offset = 14; static int load_module(xml_node *config); static int unload_module(void); static int description(char *descr); static int statistic(char *buf, size_t len); static int free_profile(unsigned int idx); static uint64_t serial_module(void); bind_transport_module_api_t transport_bind_api; unsigned int profile_size = 0; //osip_message_t *sip; static cmd_export_t cmds[] = { {"database_redis_bind_api", (cmd_function)bind_redis_api, 1, 0, 0, 0}, {"check_redis_rtcp_ipport", (cmd_function) w_check_redis_rtcp_ipport, 0, 0, 0, 0 }, {"is_redis_rtcp_exist", (cmd_function) w_is_redis_rtcp_exists, 0, 0, 0, 0 }, {0, 0, 0, 0, 0, 0} }; struct module_exports exports = { "database_redis", cmds, /* Exported functions */ load_module, /* module initialization function */ unload_module, description, statistic, serial_module }; int bind_redis_api(database_module_api_t* api) { api->insert = insert_redis; api->delete = delete_redis; api->update = insert_redis; api->select = select_redis; api->count = count_redis; api->raw_query = raw_query_redis; api->reload_f = reload_config; api->module_name = module_name; return 0; } int reload_config (char *erbuf, int erlen) { char module_config_name[500]; xml_node *config; LNOTICE("reloading config for [%s]", module_name); snprintf(module_config_name, 500, "%s/%s.xml", global_config_path, module_name); if(xml_parse_with_report(module_config_name, erbuf, erlen)) { unload_module(); load_module(config); return 1; } return 0; } int w_check_redis_rtcp_ipport(msg_t *msg) { int i = 0; miprtcp_t *mp = NULL; for (i = 0; i < msg->sip.mrp_size; i++) { mp = &msg->sip.mrp[i]; if (mp->rtcp_ip.len > 0 && mp->rtcp_ip.s) { insert_and_update(mp->rtcp_ip.len, mp->rtcp_ip.s, mp->rtcp_port, msg->sip.callId.len, msg->sip.callId.s); } } return 1; } int insert_and_update(int iplen, char *ip, int port, int callidlen, char *callid) { int ret = 0; #ifdef USE_REDIS char query[1000]; snprintf(query, sizeof(query), "SETEX %.*s:%d %d %.*s", iplen, ip, port, rtcp_timeout, callidlen, callid); LDEBUG("QUERY: %s", query); ret = redis_command_free(0, query); #endif return ret; } int w_is_redis_rtcp_exists(msg_t *msg) { LDEBUG("IP PORT: %s:%i", msg->rcinfo.src_ip, msg->rcinfo.src_port); if(!get_and_expire(msg->rcinfo.src_ip, msg->rcinfo.src_port, &msg->corrdata)) { if(!get_and_expire( msg->rcinfo.dst_ip, msg->rcinfo.dst_port, &msg->corrdata)) { return -1; } msg->rcinfo.direction = 0; } LDEBUG("SESSION ID: %s", msg->corrdata); msg->rcinfo.correlation_id.s = msg->corrdata; msg->rcinfo.correlation_id.len = strlen(msg->corrdata); return 1; } profile_database_t* get_profile_by_name(char *name) { unsigned int i = 0; if(profile_size == 1) return &profile_database[0]; for (i = 0; i < profile_size; i++) { if(!strncmp(profile_database[i].name, name, strlen(profile_database[i].name))) { return &profile_database[1]; } } return NULL; } unsigned int get_profile_index_by_name(char *name) { unsigned int i = 0; if(profile_size == 1) return 0; for (i = 0; i < profile_size; i++) { if(!strncmp(profile_database[i].name, name, strlen(profile_database[i].name))) { return i; } } return 0; } /* REDIS CACHE */ int make_cache_reconnect(unsigned int idx) { #ifdef USE_REDIS redisReply *reply; struct timeval timeout = { 1, 500000 }; stats.reconnect_total++; if (redisCon[idx]) redisFree(redisCon[idx]); redisCon[idx] = redisConnectWithTimeout(profile_database[idx].host, atoi(profile_database[idx].port), timeout); if (redisCon[idx] == NULL || redisCon[idx]->err) { if (redisCon[idx]) { LERR("Redis connection error: %s", redisCon[idx]->errstr); redisFree(redisCon[idx]); redisCon[idx] = NULL; return 0; } else { LERR("Redis connection error: can't allocate redis context"); redisCon[idx] = NULL; return 0; } } if(profile_database[idx].password != NULL && strlen(profile_database[idx].password) > 0 ) { reply= redisCommand(redisCon[idx], "AUTH %s", profile_database[idx].password); if (reply && reply->type == REDIS_REPLY_ERROR) { /* Authentication failed */ LERR("Redis AUTH error"); } freeReplyObject(reply); } reply= redisCommand(redisCon[idx], "PING", profile_database[idx].password); if (reply && reply->type == REDIS_REPLY_ERROR) { LERR("Redis ping error"); } freeReplyObject(reply); if (atoi(profile_database[idx].db_name)) { reply = redisCommand(redisCon[idx], "SELECT %d", atoi(profile_database[idx].db_name)); freeReplyObject(reply); } #endif return 1; } void close_cache_connection(unsigned int idx) { #ifdef USE_REDIS if(redisCon[idx]) redisFree(redisCon[idx]); redisCon[idx] = NULL; #endif return; } #ifdef USE_REDIS redisReply *redis_command(unsigned int idx, char *query) { redisReply *reply = NULL; if (redisCon[idx] == NULL || !(reply = redisCommand(redisCon[idx], query))) { if(make_cache_reconnect(idx)) { reply = redisCommand(redisCon[idx], query); } } return reply; } int redis_command_free(unsigned int idx, char *query) { redisReply *reply = NULL; if (redisCon[idx] == NULL || !(reply = redisCommand(redisCon[idx], query))) { if(make_cache_reconnect(idx)) reply = redisCommand(redisCon[idx], query); } if(reply) freeReplyObject(reply); return 1; } #endif int get_and_expire(char *ip, int port, char **callid) { unsigned int ret = 0; #ifdef USE_REDIS unsigned int idx = 0; redisReply *reply; char ipptmp[256]; char query[MAX_QUERY_SIZE]; snprintf(ipptmp, sizeof(ipptmp), "%s:%d", ip, port); snprintf(query, MAX_QUERY_SIZE, "GET %s", ipptmp); if ((reply = redis_command(idx, query))) { if (reply != NULL) { if(reply->type == REDIS_REPLY_STRING) { /* max size of callid should be 256 */ *callid = malloc(256); snprintf(*callid, 256, "%s", reply->str); ret = 1; snprintf(query, MAX_QUERY_SIZE, "EXPIRE %s %d", ipptmp, rtcp_timeout); redis_command_free(idx, query); } freeReplyObject(reply); } } #endif return ret; } /* redis cache push */ int insert_redis(const db_msg_t *msg, const db_value_t* _v, const int _n) { int i = 0; #ifdef USE_REDIS /* send to parse module */ char query[MAX_QUERY_SIZE]; unsigned int idx = 0, ret = 0; /* stats */ stats.recieved_packets_total++; idx = get_profile_index_by_name(msg->profile_name.s); redisReply *reply; if (msg->batch == 0) { ret = snprintf(query, MAX_QUERY_SIZE, "SET"); } else { ret = snprintf(query, MAX_QUERY_SIZE, "HMSET %.*s", msg->key_name.len, msg->key_name.s); } for (i = 0; i < _n; i++) { if (_v[i].type == DB_STRING) { ret += snprintf(query + ret, MAX_QUERY_SIZE - ret, " %.*s \"%.*s\"", _v[i].key.len, _v[i].key.s, _v[i].val.str_val.len, _v[i].val.str_val.s); /* set expire */ if (msg->batch == 0 && msg->expire > 0) ret += snprintf(query + ret, MAX_QUERY_SIZE - ret, " %d", msg->expire); } } if ((reply = redis_command(idx, query))) { if (reply->type == REDIS_REPLY_ERROR) { i = 0; LDEBUG("couldnot add call to cache"); } else { LDEBUG("Call SET [1]: [%s]", reply->str); i = 1; } freeReplyObject(reply); if (msg->batch == 1 && msg->expire > 0) { /* set auto expire */ ret = snprintf(query, MAX_QUERY_SIZE, "EXPIRE %.*s %d", msg->key_name.len, msg->key_name.s, msg->expire); if ((reply = redis_command(idx, query))) { if (reply->type == REDIS_REPLY_ERROR) { i = 0; LDEBUG("couldnot add call to cache"); } else { LDEBUG("coudlnot set expire for call: [%.*s]", msg->key_name.len, msg->key_name.s); i = 1; } freeReplyObject(reply); } } } #endif return i; } /* redis cache push */ int delete_redis(const db_msg_t *msg, const db_value_t* _v, const int _n) { int i = 0; #ifdef USE_REDIS unsigned int idx = 0, ret = 0; redisReply *reply; /* send to parse module */ char query[MAX_QUERY_SIZE]; /* stats */ stats.recieved_packets_total++; idx = get_profile_index_by_name(msg->profile_name.s); if (_n == 0) { ret = snprintf(query, MAX_QUERY_SIZE, "DEL %.*s ", msg->key_name.len, msg->key_name.s); } else { ret = snprintf(query, MAX_QUERY_SIZE, "HDEL %.*s ", msg->key_name.len, msg->key_name.s); if (_v[i].type == DB_STRING) { ret += snprintf(query + ret, MAX_QUERY_SIZE - ret, "%.*s", _v[i].key.len, _v[i].key.s); } } if ((reply = redis_command(idx, query))) { if (reply->type == REDIS_REPLY_ERROR) { i = 0; } else if (reply->type == REDIS_REPLY_ARRAY && reply->elements == 1) { i = 1; } else if (reply->type == REDIS_REPLY_INTEGER) { i = reply->integer; } freeReplyObject(reply); } else { LDEBUG("Bad delete a call from cache: %.*s", msg->key_name.len, msg->key_name.s); i = 0; } #endif return i; } /* redis cache push */ int select_redis(const db_msg_t *msg, db_value_t* _v, const int _n) { unsigned int ret = 0; #ifdef USE_REDIS unsigned int idx = 0; redisReply *reply; /* send to parse module */ char query[MAX_QUERY_SIZE]; int i = 0; /* stats */ stats.recieved_packets_total++; idx = get_profile_index_by_name(msg->profile_name.s); if (_n == 0) { ret = snprintf(query, MAX_QUERY_SIZE, "GET %.*s", msg->key_name.len, msg->key_name.s); i = 1; } else { ret = snprintf(query, MAX_QUERY_SIZE, "HMGET %.*s", msg->key_name.len, msg->key_name.s); for (i = 0; i < _n; i++) { if (_v[i].type == DB_STRING) { ret += snprintf(query + ret, MAX_QUERY_SIZE - ret, " %.*s", _v[i].key.len, _v[i].key.s); } } } ret = 0; if ((reply = redis_command(idx, query))) { if (reply != NULL && reply->type == REDIS_REPLY_ARRAY && reply->elements == _n) { for (i = 0; i < _n; i++) { if (reply->element[i] != NULL && reply->element[i]->type != REDIS_REPLY_NIL) { if(reply->element[i]->type == REDIS_REPLY_STRING) { if(isCharsDigit(reply->element[i]->str)) { _v[i].type = DB_INT; _v[i].val.int_val = atoi(reply->element[i]->str); } else { _v[i].type = DB_STR; _v[i].val.str_val.len = strlen(reply->element[i]->str); _v[i].val.str_val.s = (char*) malloc(_v[i].val.str_val.len + 1); memcpy(_v[i].val.str_val.s, reply->element[i]->str, _v[i].val.str_val.len); } } else if(reply->element[i]->type == REDIS_REPLY_INTEGER) { _v[i].type = DB_INT; _v[i].val.int_val = reply->element[i]->integer; } ret = i; } } freeReplyObject(reply); } else if (reply->type == REDIS_REPLY_INTEGER) { _v[0].val.int_val = reply->integer; _v[i].type = DB_INT; ret = 1; } } #endif return ret; } /* redis cache push */ int raw_query_redis(char* query, const db_msg_t *msg, db_value_t* _v, const int _n) { int i = 0; #ifdef USE_REDIS unsigned int idx = 0; redisReply *reply; /* stats */ stats.recieved_packets_total++; idx = get_profile_index_by_name(msg->profile_name.s); if ((reply = redis_command(idx, query))) { /* if _n == 0 we don't want to get this value. */ if (_n > 0 && reply->type == REDIS_REPLY_ARRAY && reply->elements == _n) { if(reply->type == REDIS_REPLY_ARRAY) for (i = 0; i < _n; i++) { _v[i].type = DB_STR; _v[i].val.str_val.len = strlen(reply->element[i]->str); _v[i].val.str_val.s = (char*) malloc(_v[i].val.str_val.len + 1); memcpy(_v[i].val.str_val.s, reply->element[i]->str, _v[i].val.str_val.len); } i = _n; } else if (reply->type == REDIS_REPLY_INTEGER) { _v[i].val.int_val = reply->integer; i = 1; } if(reply) freeReplyObject(reply); } #endif return i; } /* count redis */ int count_redis(char* query, const db_msg_t *msg) { int i = 0; #ifdef USE_REDIS unsigned int idx = 0; redisReply *reply; /* send to parse module */ stats.recieved_packets_total++; idx = get_profile_index_by_name(msg->profile_name.s); if ((reply = redis_command(idx, query))) { if (reply->type == REDIS_REPLY_INTEGER) i =reply->integer; if(reply) freeReplyObject(reply); } #endif return i; } bool isCharsDigit(char *numArray) { int i; const int len = strlen(numArray); bool ret = TRUE; for (i = 0; i < len; i++) { /* #include for 'isdigit()'. */ if (!isdigit(numArray[i])) { ret = FALSE; break; } } return ret; } int load_module_xml_config() { char module_config_name[500]; xml_node *next; int i = 0; snprintf(module_config_name, 500, "%s/%s.xml", global_config_path, module_name); if ((module_xml_config = xml_parse(module_config_name)) == NULL) { LERR("Unable to open configuration file: %s", module_config_name); return -1; } /* check if this module is our */ next = xml_get("module", module_xml_config, 1); if (next == NULL) { LERR("wrong config for module: %s", module_name); return -2; } for (i = 0; next->attr[i]; i++) { if (!strncmp(next->attr[i], "name", 4)) { if (strncmp(next->attr[i + 1], module_name, strlen(module_name))) { return -3; } } else if (!strncmp(next->attr[i], "serial", 6)) { module_serial = atol(next->attr[i + 1]); } else if (!strncmp(next->attr[i], "description", 11)) { module_description = next->attr[i + 1]; } } return 1; } void free_module_xml_config() { /* now we are free */ if(module_xml_config) xml_free(module_xml_config); } /* modules external API */ static int load_module(xml_node *config) { xml_node *params, *profile, *settings, *condition, *action; char *key, *value = NULL; unsigned int i = 0; LNOTICE("Loaded database_redis"); #ifndef USE_REDIS LERR("redis support was not activated. Please recompile with --enable-redis "); #endif load_module_xml_config(); /* READ CONFIG */ profile = module_xml_config; /* reset profile */ profile_size = 0; while (profile) { profile = xml_get("profile", profile, 1); if (profile == NULL) break; if(!profile->attr[4] || strncmp(profile->attr[4], "enable", 6)) { goto nextprofile; } /* if not equals "true" */ if(!profile->attr[5] || strncmp(profile->attr[5], "true", 4)) { goto nextprofile; } /* set values */ profile_database[profile_size].name = strdup(profile->attr[1]); profile_database[profile_size].description = strdup(profile->attr[3]); profile_database[profile_size].serial = atoi(profile->attr[7]); /* SETTINGS */ settings = xml_get("settings", profile, 1); if (settings != NULL) { params = settings; while (params) { params = xml_get("param", params, 1); if (params == NULL) break; if (params->attr[0] != NULL) { /* bad parser */ if (strncmp(params->attr[0], "name", 4)) { LERR("bad keys in the config"); goto nextparam; } key = params->attr[1]; if(params->attr[2] && params->attr[3] && !strncmp(params->attr[2], "value", 5)) { value = params->attr[3]; } else { value = params->child->value; } if (key == NULL || value == NULL) { LERR("bad values in the config"); goto nextparam; } if(!strncmp(key, "host", 4)) profile_database[profile_size].host = strdup(value); else if(!strncmp(key, "port", 4)) profile_database[profile_size].port = strdup(value); else if(!strncmp(key, "password", 8)) profile_database[profile_size].password = strdup(value); else if(!strncmp(key, "user", 4)) profile_database[profile_size].user = strdup(value); else if(!strncmp(key, "db-num", 6)) profile_database[profile_size].db_name = strdup(value); else if (!strncmp(key, "rtcp-timeout", 12) && atoi(value) > 80) rtcp_timeout = atoi(value); } nextparam: params = params->next; } } /* STATS */ condition = xml_get("statistic", profile, 1); while (condition) { condition = xml_get("condition", condition, 1); if (condition == NULL) break; if (condition->attr[0] != NULL && condition->attr[2] != NULL) { /* bad parser */ if (strncmp(condition->attr[0], "field", 5) || strncmp(condition->attr[2], "expression", 10)) { LERR("bad keys in the config"); goto nextstatistic; } key = condition->attr[1]; value = condition->attr[3]; if (key == NULL || value == NULL) { LERR("bad values in the config"); goto nextstatistic; } action = condition->child; if (action && !strncmp(action->key, "action", 6)) { for (i = 0; action->attr[i]; i++) { if (!strncmp(action->attr[i], "application", 4)) { profile_database[profile_size].statistic_pipe = strdup(action->attr[i + 1]); } else if (!strncmp(action->attr[i], "profile", 7)) { profile_database[profile_size].statistic_profile = strdup(action->attr[i + 1]); } } } } nextstatistic: condition = condition->next; } profile_size++; nextprofile: profile = profile->next; } /* free it */ free_module_xml_config(); for (i = 0; i < profile_size; i++) { //snprintf(module_name, 256, "%s_bind_api", profile_database[i].transport_pipe); //transport_bind_api = (bind_transport_module_api_t) find_export(module_name, 1, 0); //transport_bind_api(&profile_database[i].transport_api); make_cache_reconnect(i); } return 0; } static int unload_module(void) { LNOTICE("unloaded module database_redis"); unsigned int i = 0; /* Close socket */ for (i = 0; i < profile_size; i++) { close_cache_connection(i); free_profile(i); } return 0; } static uint64_t serial_module(void) { return module_serial; } static int free_profile(unsigned int idx) { /*free profile chars **/ if (profile_database[idx].name) free(profile_database[idx].name); if (profile_database[idx].description) free(profile_database[idx].description); if (profile_database[idx].host) free(profile_database[idx].host); if (profile_database[idx].port) free(profile_database[idx].port); if (profile_database[idx].user) free(profile_database[idx].user); if (profile_database[idx].db_name) free(profile_database[idx].db_name); if (profile_database[idx].statistic_pipe) free(profile_database[idx].statistic_pipe); if (profile_database[idx].statistic_profile) free(profile_database[idx].statistic_profile); return 1; } static int description(char *descr) { LNOTICE("Loaded description"); descr = module_description; return 1; } static int statistic(char *buf, size_t len) { int ret = 0; ret += snprintf(buf+ret, len-ret, "received: [%" PRId64 "]\r\n", stats.recieved_packets_total); ret += snprintf(buf+ret, len-ret, "wrote: [%" PRId64 "]\r\n", stats.write_packets_total); ret += snprintf(buf+ret, len-ret, "reconnect: [%" PRId64 "]\r\n", stats.reconnect_total); return 1; } captagent-6.1.0.20/src/modules/database/redis/database_redis.h000066400000000000000000000056611272354503300241340ustar00rootroot00000000000000/* * $Id$ * * captagent - Homer capture agent. Modular * Duplicate SIP messages in Homer Encapulate Protocol [HEP] [ipv6 version] * * Author: Alexandr Dubovikov * (C) Homer Project 2012 (http://www.sipcapture.org) * * Homer capture agent 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 * * Homer capture agent 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 * */ #ifndef _database_redis_H_ #define _database_redis_H_ #define FILTER_LEN 4080 /* SYNC this list: http://hep.sipcapture.org */ #define PROTO_SIP 0x01 #define PROTO_XMPP 0x02 #define PROTO_SDP 0x03 #define PROTO_RTP 0x04 #define PROTO_RTCP 0x05 #define PROTO_MGCP 0x06 #define PROTO_MEGACO 0x07 #define PROTO_M2UA 0x08 #define PROTO_M3UA 0x09 #define PROTO_IAX 0x0a #define PROTO_H322 0x0b #define PROTO_H321 0x0c typedef struct database_redis_stats { uint64_t recieved_packets_total; uint64_t reconnect_total; uint64_t write_packets_total; } database_redis_stats_t; #define EXPIRE_RTCP_REDIS 80 int rtcp_timeout = EXPIRE_RTCP_REDIS; #define MAX_DATABASE 10 #define MAX_QUERY_SIZE 3000 profile_database_t profile_database[MAX_DATABASE]; extern char *global_config_path; profile_database_t* get_profile_by_name(char *name); unsigned int get_profile_index_by_name(char *name); int bind_redis_api(database_module_api_t* api); int insert_redis(const db_msg_t *msg, const db_value_t* _v, const int _n); int delete_redis(const db_msg_t *msg, const db_value_t* _v, const int _n); int update_redis(const db_msg_t *msg, const db_value_t* _v, const int _n); int select_redis(const db_msg_t *msg, db_value_t* _v, const int _n); int raw_query_redis(char* query, const db_msg_t *msg, db_value_t* _v, const int _n); int count_redis(char* query, const db_msg_t *msg); bool isCharsDigit(char *numArray); void free_module_xml_config(); int reload_config (char *erbuf, int erlen); int w_check_redis_rtcp_ipport(msg_t *msg); int w_is_redis_rtcp_exists(msg_t *msg); int insert_and_update(int iplen, char *ip, int port, int callidlen, char *callid); int get_and_expire(char *ip, int port, char **callid); #ifdef USE_REDIS redisReply *redis_command(unsigned int idx, char *query); int redis_command_free(unsigned int idx, char *query); #endif /* if USE REDIS */ int make_cache_reconnect(unsigned int idx); void close_cache_connection(unsigned int idx); #endif /* _database_redis_H_ */ captagent-6.1.0.20/src/modules/interface/000077500000000000000000000000001272354503300201075ustar00rootroot00000000000000captagent-6.1.0.20/src/modules/interface/http/000077500000000000000000000000001272354503300210665ustar00rootroot00000000000000captagent-6.1.0.20/src/modules/interface/http/Makefile.am000066400000000000000000000005621272354503300231250ustar00rootroot00000000000000include $(top_srcdir)/modules.am SUBDIRS = . noinst_HEADERS = civetweb.h interface_http.h extra_api.h # interface_http_la_SOURCES = interface_http.c civetweb.c extra_api.c interface_http_la_CFLAGS = -Wall ${MODULE_CFLAGS} ${EXPAT_LIBS} ${JSON_LIBS} ${PCRE_LIBS} interface_http_la_LDFLAGS = -module -avoid-version include_HEADERS = mod_LTLIBRARIES = interface_http.la captagent-6.1.0.20/src/modules/interface/http/civetweb.c000066400000000000000000007061311272354503300230520ustar00rootroot00000000000000/* Copyright (c) 2013-2014 the Civetweb developers * Copyright (c) 2004-2013 Sergey Lyubka * * 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. */ #if defined(_WIN32) #if !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ #endif #else #ifdef __linux__ #define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ #endif #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ #endif #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ #endif #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS /* wants this for C++ */ #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ #endif #endif #if defined (_MSC_VER) /* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */ #pragma warning (disable : 4306 ) /* conditional expression is constant: introduced by FD_SET(..) */ #pragma warning (disable : 4127) /* non-constant aggregate initializer: issued due to missing C99 support */ #pragma warning (disable : 4204) #endif /* Disable WIN32_LEAN_AND_MEAN. This makes windows.h always include winsock2.h */ #if defined(WIN32_LEAN_AND_MEAN) #undef WIN32_LEAN_AND_MEAN #endif #if defined USE_IPV6 && defined(_WIN32) #include #endif #if defined(__SYMBIAN32__) #define NO_SSL /* SSL is not supported */ #define NO_CGI /* CGI is not supported */ #define PATH_MAX FILENAME_MAX #endif /* __SYMBIAN32__ */ #ifndef IGNORE_UNUSED_RESULT #define IGNORE_UNUSED_RESULT(a) (void)((a) && 1) #endif #ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */ #include #include #include #include #include #endif /* !_WIN32_WCE */ #include #include #include #include #include #include #include #include #include #ifndef MAX_WORKER_THREADS #define MAX_WORKER_THREADS 1024 #endif #if defined(_WIN32) && !defined(__SYMBIAN32__) /* Windows specific */ #if defined(_MSC_VER) && _MSC_VER <= 1400 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0400 /* To make it link in VS2005 */ #endif #include #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #ifndef _IN_PORT_T #ifndef in_port_t #define in_port_t u_short #endif #endif #ifndef _WIN32_WCE #include #include #include #else /* _WIN32_WCE */ #define NO_CGI /* WinCE has no pipes */ typedef long off_t; #define errno GetLastError() #define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10) #endif /* _WIN32_WCE */ #define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \ ((uint64_t)((uint32_t)(hi))) << 32)) #define RATE_DIFF 10000000 /* 100 nsecs */ #define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de) #define SYS2UNIX_TIME(lo, hi) \ (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF) /* Visual Studio 6 does not know __func__ or __FUNCTION__ The rest of MS compilers use __FUNCTION__, not C99 __func__ Also use _strtoui64 on modern M$ compilers */ #if defined(_MSC_VER) && _MSC_VER < 1300 #define STRX(x) #x #define STR(x) STRX(x) #define __func__ __FILE__ ":" STR(__LINE__) #define strtoull(x, y, z) (unsigned __int64) _atoi64(x) #define strtoll(x, y, z) _atoi64(x) #else #define __func__ __FUNCTION__ #define strtoull(x, y, z) _strtoui64(x, y, z) #define strtoll(x, y, z) _strtoi64(x, y, z) #endif /* _MSC_VER */ #define ERRNO GetLastError() #define NO_SOCKLEN_T #define SSL_LIB "ssleay32.dll" #define CRYPTO_LIB "libeay32.dll" #define O_NONBLOCK 0 #if !defined(EWOULDBLOCK) #define EWOULDBLOCK WSAEWOULDBLOCK #endif /* !EWOULDBLOCK */ #define _POSIX_ #define INT64_FMT "I64d" #define WINCDECL __cdecl #define SHUT_WR 1 #define snprintf _snprintf #define vsnprintf _vsnprintf #define mg_sleep(x) Sleep(x) #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY) #ifndef popen #define popen(x, y) _popen(x, y) #endif #ifndef pclose #define pclose(x) _pclose(x) #endif #define close(x) _close(x) #define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y)) #define RTLD_LAZY 0 #define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z)) #define fdopen(x, y) _fdopen((x), (y)) #define write(x, y, z) _write((x), (y), (unsigned) z) #define read(x, y, z) _read((x), (y), (unsigned) z) #define flockfile(x) EnterCriticalSection(&global_log_file_lock) #define funlockfile(x) LeaveCriticalSection(&global_log_file_lock) #define sleep(x) Sleep((x) * 1000) #define rmdir(x) _rmdir(x) #if defined(USE_LUA) && defined(USE_WEBSOCKET) #define USE_TIMERS #endif #if !defined(va_copy) #define va_copy(x, y) x = y #endif /* !va_copy MINGW #defines va_copy */ #if !defined(fileno) #define fileno(x) _fileno(x) #endif /* !fileno MINGW #defines fileno */ typedef HANDLE pthread_mutex_t; typedef DWORD pthread_key_t; typedef HANDLE pthread_t; typedef struct { CRITICAL_SECTION threadIdSec; int waitingthreadcount; /* The number of threads queued. */ pthread_t *waitingthreadhdls; /* The thread handles. */ } pthread_cond_t; #ifndef __clockid_t_defined typedef DWORD clockid_t; #endif #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC (1) #endif #ifndef CLOCK_REALTIME #define CLOCK_REALTIME (2) #endif #ifndef _TIMESPEC_DEFINED struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; #endif #define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ static int pthread_mutex_lock(pthread_mutex_t *); static int pthread_mutex_unlock(pthread_mutex_t *); static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len); struct file; static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p); #if defined(HAVE_STDINT) #include #else typedef unsigned int uint32_t; typedef unsigned short uint16_t; typedef unsigned __int64 uint64_t; typedef __int64 int64_t; #define INT64_MAX 9223372036854775807 #endif /* HAVE_STDINT */ /* POSIX dirent interface */ struct dirent { char d_name[PATH_MAX]; }; typedef struct DIR { HANDLE handle; WIN32_FIND_DATAW info; struct dirent result; } DIR; #if !defined(USE_IPV6) && defined(_WIN32) #ifndef HAVE_POLL struct pollfd { SOCKET fd; short events; short revents; }; #define POLLIN 1 #endif #endif /* Mark required libraries */ #ifdef _MSC_VER #pragma comment(lib, "Ws2_32.lib") #endif #else /* UNIX specific */ #include #include #include #include #include #include #include #include #include #include #if defined(ANDROID) typedef unsigned short int in_port_t; #endif #include #include #include #if !defined(NO_SSL_DL) && !defined(NO_SSL) #include #endif #include #if defined(__MACH__) #define SSL_LIB "libssl.dylib" #define CRYPTO_LIB "libcrypto.dylib" #else #if !defined(SSL_LIB) #define SSL_LIB "libssl.so" #endif #if !defined(CRYPTO_LIB) #define CRYPTO_LIB "libcrypto.so" #endif #endif #ifndef O_BINARY #define O_BINARY 0 #endif /* O_BINARY */ #define closesocket(a) close(a) #define mg_mkdir(x, y) mkdir(x, y) #define mg_remove(x) remove(x) #define mg_sleep(x) usleep((x) * 1000) #define ERRNO errno #define INVALID_SOCKET (-1) #define INT64_FMT PRId64 typedef int SOCKET; #define WINCDECL #endif /* End of Windows and UNIX specific includes */ #ifdef _WIN32 static CRITICAL_SECTION global_log_file_lock; static DWORD pthread_self(void) { return GetCurrentThreadId(); } int pthread_key_create(pthread_key_t *key, void (*_must_be_zero)(void*) /* destructor function not supported for windows */) { assert(_must_be_zero == NULL); if ((key!=0) && (_must_be_zero == NULL)) { *key = TlsAlloc(); return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1; } return -2; } int pthread_key_delete(pthread_key_t key) { return TlsFree(key) ? 0 : 1; } int pthread_setspecific(pthread_key_t key, void * value) { return TlsSetValue(key, value) ? 0 : 1; } void *pthread_getspecific(pthread_key_t key) { return TlsGetValue(key); } #endif /* _WIN32 */ #include "civetweb.h" #define PASSWORDS_FILE_NAME ".htpasswd" #define CGI_ENVIRONMENT_SIZE 4096 #define MAX_CGI_ENVIR_VARS 64 #define MG_BUF_LEN 8192 #ifndef MAX_REQUEST_SIZE #define MAX_REQUEST_SIZE 16384 #endif #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) #if !defined(DEBUG_TRACE) #if defined(DEBUG) static void DEBUG_TRACE_FUNC(const char *func, unsigned line, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(3, 4); static void DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...) { va_list args; flockfile(stdout); printf("*** %lu.%p.%s.%u: ", (unsigned long) time(NULL), (void *) pthread_self(), func, line); va_start(args, fmt); vprintf(fmt, args); va_end(args); putchar('\n'); fflush(stdout); funlockfile(stdout); } #define DEBUG_TRACE(fmt, ...) DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) #else #define DEBUG_TRACE(fmt, ...) #endif /* DEBUG */ #endif /* DEBUG_TRACE */ #if defined(MEMORY_DEBUGGING) static unsigned long blockCount = 0; static unsigned long totalMemUsed = 0; static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { void * data = malloc(size + sizeof(size_t)); void * memory = 0; char mallocStr[256]; if (data) { *(size_t*)data = size; totalMemUsed += size; blockCount++; memory = (void *)(((char*)data)+sizeof(size_t)); } sprintf(mallocStr, "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", memory, (unsigned long)size, totalMemUsed, blockCount, file, line); #if defined(_WIN32) OutputDebugStringA(mallocStr); #else DEBUG_TRACE("%s", mallocStr); #endif return memory; } static void * mg_calloc_ex(size_t count, size_t size, const char * file, unsigned line) { void * data = mg_malloc_ex(size*count, file, line); if (data) memset(data, 0, size); return data; } static void mg_free_ex(void * memory, const char * file, unsigned line) { char mallocStr[256]; void * data = (void *)(((char*)memory)-sizeof(size_t)); size_t size; if (memory) { size = *(size_t*)data; totalMemUsed -= size; blockCount--; sprintf(mallocStr, "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", memory, (unsigned long)size, totalMemUsed, blockCount, file, line); #if defined(_WIN32) OutputDebugStringA(mallocStr); #else DEBUG_TRACE("%s", mallocStr); #endif free(data); } } static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, unsigned line) { char mallocStr[256]; void * data; void * _realloc; size_t oldsize; if (newsize) { if (memory) { data = (void *)(((char*)memory)-sizeof(size_t)); oldsize = *(size_t*)data; _realloc = realloc(data, newsize+sizeof(size_t)); if (_realloc) { data = _realloc; totalMemUsed -= oldsize; sprintf(mallocStr, "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", memory, (unsigned long)oldsize, totalMemUsed, blockCount, file, line); #if defined(_WIN32) OutputDebugStringA(mallocStr); #else DEBUG_TRACE("%s", mallocStr); #endif totalMemUsed += newsize; sprintf(mallocStr, "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", memory, (unsigned long)newsize, totalMemUsed, blockCount, file, line); #if defined(_WIN32) OutputDebugStringA(mallocStr); #else DEBUG_TRACE("%s", mallocStr); #endif *(size_t*)data = newsize; data = (void *)(((char*)data)+sizeof(size_t)); } else { #if defined(_WIN32) OutputDebugStringA("MEM: realloc failed\n"); #else DEBUG_TRACE("MEM: realloc failed\n"); #endif return _realloc; } } else { data = mg_malloc_ex(newsize, file, line); } } else { data = 0; mg_free_ex(memory, file, line); } return data; } #define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) #define mg_calloc(a,b) mg_calloc_ex(a, b, __FILE__, __LINE__) #define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__) #define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) #else static __inline void * mg_malloc(size_t a) {return malloc(a);} static __inline void * mg_calloc(size_t a, size_t b) {return calloc(a, b);} static __inline void * mg_realloc(void * a, size_t b) {return realloc(a, b);} static __inline void mg_free(void * a) {free(a);} #endif /* This following lines are just meant as a reminder to use the mg-functions for memory management */ #ifdef malloc #undef malloc #endif #ifdef calloc #undef calloc #endif #ifdef realloc #undef realloc #endif #ifdef free #undef free #endif #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free #define MD5_STATIC static #include /* Darwin prior to 7.0 and Win32 do not have socklen_t */ #ifdef NO_SOCKLEN_T typedef int socklen_t; #endif /* NO_SOCKLEN_T */ #define _DARWIN_UNLIMITED_SELECT #define IP_ADDR_STR_LEN 50 /* IPv6 hex string is 46 chars */ #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif #if !defined(SOMAXCONN) #define SOMAXCONN 100 #endif #if !defined(PATH_MAX) #define PATH_MAX 4096 #endif /* Size of the accepted socket queue */ #if !defined(MGSQLEN) #define MGSQLEN 20 #endif static const char *http_500_error = "Internal Server Error"; #if defined(NO_SSL_DL) #include #include #else /* SSL loaded dynamically from DLL. I put the prototypes here to be independent from OpenSSL source installation. */ typedef struct ssl_st SSL; typedef struct ssl_method_st SSL_METHOD; typedef struct ssl_ctx_st SSL_CTX; struct ssl_func { const char *name; /* SSL function name */ void (*ptr)(void); /* Function pointer */ }; #define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr) #define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr) #define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr) #define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr) #define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr) #define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr) #define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr) #define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr) #define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr) #define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr) #define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr) #define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \ const char *, int)) ssl_sw[11].ptr) #define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \ const char *, int)) ssl_sw[12].ptr) #define SSL_CTX_set_default_passwd_cb \ (* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr) #define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr) #define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr) #define SSL_CTX_use_certificate_chain_file \ (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr) #define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr) #define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr) #define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr) #define SSL_shutdown (* (int (*)(SSL *)) ssl_sw[20].ptr) #define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr) #define CRYPTO_set_locking_callback \ (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr) #define CRYPTO_set_id_callback \ (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr) #define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr) #define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr) /* set_ssl_option() function updates this array. It loads SSL library dynamically and changes NULLs to the actual addresses of respective functions. The macros above (like SSL_connect()) are really just calling these functions indirectly via the pointer. */ static struct ssl_func ssl_sw[] = { {"SSL_free", NULL}, {"SSL_accept", NULL}, {"SSL_connect", NULL}, {"SSL_read", NULL}, {"SSL_write", NULL}, {"SSL_get_error", NULL}, {"SSL_set_fd", NULL}, {"SSL_new", NULL}, {"SSL_CTX_new", NULL}, {"SSLv23_server_method", NULL}, {"SSL_library_init", NULL}, {"SSL_CTX_use_PrivateKey_file", NULL}, {"SSL_CTX_use_certificate_file",NULL}, {"SSL_CTX_set_default_passwd_cb",NULL}, {"SSL_CTX_free", NULL}, {"SSL_load_error_strings", NULL}, {"SSL_CTX_use_certificate_chain_file", NULL}, {"SSLv23_client_method", NULL}, {"SSL_pending", NULL}, {"SSL_CTX_set_verify", NULL}, {"SSL_shutdown", NULL}, {NULL, NULL} }; /* Similar array as ssl_sw. These functions could be located in different lib. */ #if !defined(NO_SSL) static struct ssl_func crypto_sw[] = { {"CRYPTO_num_locks", NULL}, {"CRYPTO_set_locking_callback", NULL}, {"CRYPTO_set_id_callback", NULL}, {"ERR_get_error", NULL}, {"ERR_error_string", NULL}, {NULL, NULL} }; #endif /* NO_SSL */ #endif /* NO_SSL_DL */ static const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* Unified socket address. For IPv6 support, add IPv6 address structure in the union u. */ union usa { struct sockaddr sa; struct sockaddr_in sin; #if defined(USE_IPV6) struct sockaddr_in6 sin6; #endif }; /* Describes a string (chunk of memory). */ struct vec { const char *ptr; size_t len; }; struct file { int is_directory; time_t modification_time; int64_t size; FILE *fp; const char *membuf; /* Non-NULL if file data is in memory */ /* set to 1 if the content is gzipped in which case we need a content-encoding: gzip header */ int gzipped; }; #define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL, 0} /* Describes listening socket, or socket which was accept()-ed by the master thread and queued for future handling by the worker thread. */ struct socket { SOCKET sock; /* Listening socket */ union usa lsa; /* Local socket address */ union usa rsa; /* Remote socket address */ unsigned is_ssl:1; /* Is port SSL-ed */ unsigned ssl_redir:1; /* Is port supposed to redirect everything to SSL port */ }; /* NOTE(lsm): this enum shoulds be in sync with the config_options below. */ enum { CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER, PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, THROTTLE, ACCESS_LOG_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE, GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST, EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE, NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT, #if defined(USE_LUA) LUA_PRELOAD_FILE, LUA_SCRIPT_EXTENSIONS, LUA_SERVER_PAGE_EXTENSIONS, #endif #if defined(USE_WEBSOCKET) WEBSOCKET_ROOT, #endif #if defined(USE_LUA) && defined(USE_WEBSOCKET) LUA_WEBSOCKET_EXTENSIONS, #endif ACCESS_CONTROL_ALLOW_ORIGIN, ERROR_PAGES, NUM_OPTIONS }; /* TODO: replace 12345 by proper config types */ static struct mg_option config_options[] = { {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, {"cgi_environment", CONFIG_TYPE_STRING, NULL}, {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL}, {"cgi_interpreter", CONFIG_TYPE_FILE, NULL}, {"protect_uri", 12345, NULL}, {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"}, {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"}, {"throttle", 12345, NULL}, {"access_log_file", CONFIG_TYPE_FILE, NULL}, {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"}, {"error_log_file", CONFIG_TYPE_FILE, NULL}, {"global_auth_file", CONFIG_TYPE_FILE, NULL}, {"index_files", 12345, #ifdef USE_LUA "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php"}, #else "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"}, #endif {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"}, {"access_control_list", 12345, NULL}, {"extra_mime_types", 12345, NULL}, {"listening_ports", CONFIG_TYPE_NUMBER, "8080"}, {"document_root", CONFIG_TYPE_DIRECTORY, NULL}, {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, {"num_threads", CONFIG_TYPE_NUMBER, "50"}, {"run_as_user", CONFIG_TYPE_STRING, NULL}, {"url_rewrite_patterns", 12345, NULL}, {"hide_files_patterns", 12345, NULL}, {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, #if defined(USE_LUA) {"lua_preload_file", CONFIG_TYPE_FILE, NULL}, {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, #endif #if defined(USE_WEBSOCKET) {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL}, #endif #if defined(USE_LUA) && defined(USE_WEBSOCKET) {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, #endif {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"}, {"error_pages", CONFIG_TYPE_DIRECTORY, NULL}, {NULL, CONFIG_TYPE_UNKNOWN, NULL} }; struct mg_request_handler_info { char *uri; size_t uri_len; mg_request_handler handler; void *cbdata; struct mg_request_handler_info *next; }; struct mg_context { volatile int stop_flag; /* Should we stop event loop */ void *ssllib_dll_handle; /* Store the ssl library handle. */ void *cryptolib_dll_handle; /* Store the crypto library handle. */ SSL_CTX *ssl_ctx; /* SSL context */ char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */ struct mg_callbacks callbacks; /* User-defined callback function */ void *user_data; /* User-defined data */ struct socket *listening_sockets; in_port_t *listening_ports; int num_listening_sockets; volatile int num_threads; /* Number of threads */ pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */ pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */ struct socket queue[MGSQLEN]; /* Accepted sockets */ volatile int sq_head; /* Head of the socket queue */ volatile int sq_tail; /* Tail of the socket queue */ pthread_cond_t sq_full; /* Signaled when socket is produced */ pthread_cond_t sq_empty; /* Signaled when socket is consumed */ pthread_t masterthreadid; /* The master thread ID */ int workerthreadcount; /* The amount of worker threads. */ pthread_t *workerthreadids; /* The worker thread IDs */ unsigned long start_time; /* Server start time, used for authentication */ pthread_mutex_t nonce_mutex; /* Protects nonce_count */ unsigned long nonce_count; /* Used nonces, used for authentication */ char *systemName; /* What operating system is running */ /* linked list of uri handlers */ struct mg_request_handler_info *request_handlers; #if defined(USE_LUA) && defined(USE_WEBSOCKET) /* linked list of shared lua websockets */ struct mg_shared_lua_websocket_list *shared_lua_websockets; #endif #ifdef USE_TIMERS struct timers * timers; #endif }; struct mg_connection { struct mg_request_info request_info; struct mg_context *ctx; SSL *ssl; /* SSL descriptor */ SSL_CTX *client_ssl_ctx; /* SSL context for client connections */ struct socket client; /* Connected client */ time_t birth_time; /* Time when request was received */ int64_t num_bytes_sent; /* Total bytes sent to client */ int64_t content_len; /* Content-Length header value */ int64_t consumed_content; /* How many bytes of content have been read */ char *buf; /* Buffer for received data */ char *path_info; /* PATH_INFO part of the URL */ int must_close; /* 1 if connection must be closed */ int in_error_handler; /* 1 if in handler for user defined error pages */ int buf_size; /* Buffer size */ int request_len; /* Size of the request + headers in a buffer */ int data_len; /* Total size of data in a buffer */ int status_code; /* HTTP reply status code, e.g. 200 */ int throttle; /* Throttling, bytes/sec. <= 0 means no throttle */ time_t last_throttle_time; /* Last time throttled data was sent */ int64_t last_throttle_bytes; /* Bytes sent this second */ pthread_mutex_t mutex; /* Used by mg_lock_connection/mg_unlock_connection to ensure atomic transmissions for websockets */ #if defined(USE_LUA) && defined(USE_WEBSOCKET) void * lua_websocket_state; /* Lua_State for a websocket connection */ #endif }; static pthread_key_t sTlsKey; /* Thread local storage index */ static int sTlsInit = 0; struct mg_workerTLS { int is_master; #if defined(_WIN32) && !defined(__SYMBIAN32__) HANDLE pthread_cond_helper_mutex; #endif }; /* Directory entry */ struct de { struct mg_connection *conn; char *file_name; struct file file; }; #if defined(USE_WEBSOCKET) static int is_websocket_request(const struct mg_connection *conn); #endif #if defined(MG_LEGACY_INTERFACE) const char **mg_get_valid_option_names(void) { static const char * data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; int i; for (i=0; config_options[i].name != NULL; i++) { data[i * 2] = config_options[i].name; data[i * 2 + 1] = config_options[i].default_value; } return data; } #endif const struct mg_option *mg_get_valid_options(void) { return config_options; } static int is_file_in_memory(struct mg_connection *conn, const char *path, struct file *filep) { size_t size = 0; if ((filep->membuf = conn->ctx->callbacks.open_file == NULL ? NULL : conn->ctx->callbacks.open_file(conn, path, &size)) != NULL) { /* NOTE: override filep->size only on success. Otherwise, it might break constructs like if (!mg_stat() || !mg_fopen()) ... */ filep->size = size; } return filep->membuf != NULL; } static int is_file_opened(const struct file *filep) { return filep->membuf != NULL || filep->fp != NULL; } static int mg_fopen(struct mg_connection *conn, const char *path, const char *mode, struct file *filep) { if (!is_file_in_memory(conn, path, filep)) { #ifdef _WIN32 wchar_t wbuf[PATH_MAX], wmode[20]; to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); filep->fp = _wfopen(wbuf, wmode); #else filep->fp = fopen(path, mode); #endif } return is_file_opened(filep); } static void mg_fclose(struct file *filep) { if (filep != NULL && filep->fp != NULL) { fclose(filep->fp); } } static void mg_strlcpy(register char *dst, register const char *src, size_t n) { for (; *src != '\0' && n > 1; n--) { *dst++ = *src++; } *dst = '\0'; } static int lowercase(const char *s) { return tolower(* (const unsigned char *) s); } int mg_strncasecmp(const char *s1, const char *s2, size_t len) { int diff = 0; if (len > 0) do { diff = lowercase(s1++) - lowercase(s2++); } while (diff == 0 && s1[-1] != '\0' && --len > 0); return diff; } static int mg_strcasecmp(const char *s1, const char *s2) { int diff; do { diff = lowercase(s1++) - lowercase(s2++); } while (diff == 0 && s1[-1] != '\0'); return diff; } static char * mg_strndup(const char *ptr, size_t len) { char *p; if ((p = (char *) mg_malloc(len + 1)) != NULL) { mg_strlcpy(p, ptr, len + 1); } return p; } static char * mg_strdup(const char *str) { return mg_strndup(str, strlen(str)); } static const char *mg_strcasestr(const char *big_str, const char *small_str) { int i, big_len = (int)strlen(big_str), small_len = (int)strlen(small_str); for (i = 0; i <= big_len - small_len; i++) { if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { return big_str + i; } } return NULL; } /* Like snprintf(), but never returns negative value, or a value that is larger than a supplied buffer. Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability in his audit report. */ static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen, const char *fmt, va_list ap) { int n; if (buflen == 0) return 0; n = vsnprintf(buf, buflen, fmt, ap); if (n < 0) { mg_cry(conn, "vsnprintf error"); n = 0; } else if (n >= (int) buflen) { mg_cry(conn, "truncating vsnprintf buffer: [%.*s]", n > 200 ? 200 : n, buf); n = (int) buflen - 1; } buf[n] = '\0'; return n; } static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(4, 5); static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = mg_vsnprintf(conn, buf, buflen, fmt, ap); va_end(ap); return n; } static int get_option_index(const char *name) { int i; for (i = 0; config_options[i].name != NULL; i++) { if (strcmp(config_options[i].name, name) == 0) { return i; } } return -1; } const char *mg_get_option(const struct mg_context *ctx, const char *name) { int i; if ((i = get_option_index(name)) == -1) { return NULL; } else if (ctx->config[i] == NULL) { return ""; } else { return ctx->config[i]; } } size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* ssl) { size_t i; for (i = 0; i < size && i < (size_t)ctx->num_listening_sockets; i++) { ssl[i] = ctx->listening_sockets[i].is_ssl; ports[i] = ctx->listening_ports[i]; } return i; } static void sockaddr_to_string(char *buf, size_t len, const union usa *usa) { buf[0] = '\0'; #if defined(USE_IPV6) inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ? (void *) &usa->sin.sin_addr : (void *) &usa->sin6.sin6_addr, buf, len); #elif defined(_WIN32) /* Only Windows Vista (and newer) have inet_ntop() */ mg_strlcpy(buf, inet_ntoa(usa->sin.sin_addr), len); #else inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len); #endif } /* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be included in all responses other than 100, 101, 5xx. */ static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { struct tm *tm; tm = gmtime(t); if (tm != NULL) { strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); } else { mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); buf[buf_len - 1] = '\0'; } } /* Print error message to the opened error log stream. */ void mg_cry(struct mg_connection *conn, const char *fmt, ...) { char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; va_list ap; FILE *fp; time_t timestamp; va_start(ap, fmt); IGNORE_UNUSED_RESULT(vsnprintf(buf, sizeof(buf), fmt, ap)); va_end(ap); /* Do not lock when getting the callback value, here and below. I suppose this is fine, since function cannot disappear in the same way string option can. */ if (conn->ctx->callbacks.log_message == NULL || conn->ctx->callbacks.log_message(conn, buf) == 0) { fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); if (fp != NULL) { flockfile(fp); timestamp = time(NULL); sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp, src_addr); if (conn->request_info.request_method != NULL) { fprintf(fp, "%s %s: ", conn->request_info.request_method, conn->request_info.uri); } fprintf(fp, "%s", buf); fputc('\n', fp); funlockfile(fp); fclose(fp); } } } /* Return fake connection structure. Used for logging, if connection is not applicable at the moment of logging. */ static struct mg_connection *fc(struct mg_context *ctx) { static struct mg_connection fake_connection; fake_connection.ctx = ctx; return &fake_connection; } const char *mg_version(void) { return CIVETWEB_VERSION; } struct mg_request_info *mg_get_request_info(struct mg_connection *conn) { return &conn->request_info; } /* Skip the characters until one of the delimiters characters found. 0-terminate resulting word. Skip the delimiter and following whitespaces. Advance pointer to buffer to the next word. Return found 0-terminated word. Delimiters can be quoted with quotechar. */ static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) { char *p, *begin_word, *end_word, *end_whitespace; begin_word = *buf; end_word = begin_word + strcspn(begin_word, delimiters); /* Check for quotechar */ if (end_word > begin_word) { p = end_word - 1; while (*p == quotechar) { /* If there is anything beyond end_word, copy it */ if (*end_word == '\0') { *p = '\0'; break; } else { size_t end_off = strcspn(end_word + 1, delimiters); memmove (p, end_word, end_off + 1); p += end_off; /* p must correspond to end_word - 1 */ end_word += end_off + 1; } } for (p++; p < end_word; p++) { *p = '\0'; } } if (*end_word == '\0') { *buf = end_word; } else { end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); for (p = end_word; p < end_whitespace; p++) { *p = '\0'; } *buf = end_whitespace; } return begin_word; } /* Simplified version of skip_quoted without quote char and whitespace == delimiters */ static char *skip(char **buf, const char *delimiters) { return skip_quoted(buf, delimiters, delimiters, 0); } /* Return HTTP header value, or NULL if not found. */ static const char *get_header(const struct mg_request_info *ri, const char *name) { int i; for (i = 0; i < ri->num_headers; i++) if (!mg_strcasecmp(name, ri->http_headers[i].name)) return ri->http_headers[i].value; return NULL; } const char *mg_get_header(const struct mg_connection *conn, const char *name) { return get_header(&conn->request_info, name); } /* A helper function for traversing a comma separated list of values. It returns a list pointer shifted to the next value, or NULL if the end of the list found. Value is stored in val vector. If value has form "x=y", then eq_val vector is initialized to point to the "y" part, and val vector length is adjusted to point only to "x". */ static const char *next_option(const char *list, struct vec *val, struct vec *eq_val) { if (list == NULL || *list == '\0') { /* End of the list */ list = NULL; } else { val->ptr = list; if ((list = strchr(val->ptr, ',')) != NULL) { /* Comma found. Store length and shift the list ptr */ val->len = list - val->ptr; list++; } else { /* This value is the last one */ list = val->ptr + strlen(val->ptr); val->len = list - val->ptr; } if (eq_val != NULL) { /* Value has form "x=y", adjust pointers and lengths so that val points to "x", and eq_val points to "y". */ eq_val->len = 0; eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); if (eq_val->ptr != NULL) { eq_val->ptr++; /* Skip over '=' character */ eq_val->len = val->ptr + val->len - eq_val->ptr; val->len = (eq_val->ptr - val->ptr) - 1; } } } return list; } /* Perform case-insensitive match of string against pattern */ static int match_prefix(const char *pattern, int pattern_len, const char *str) { const char *or_str; int i, j, len, res; if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) { res = match_prefix(pattern, (int)(or_str - pattern), str); return res > 0 ? res : match_prefix(or_str + 1, (int)((pattern + pattern_len) - (or_str + 1)), str); } i = j = 0; for (; i < pattern_len; i++, j++) { if (pattern[i] == '?' && str[j] != '\0') { continue; } else if (pattern[i] == '$') { return str[j] == '\0' ? j : -1; } else if (pattern[i] == '*') { i++; if (pattern[i] == '*') { i++; len = (int) strlen(str + j); } else { len = (int) strcspn(str + j, "/"); } if (i == pattern_len) { return j + len; } do { res = match_prefix(pattern + i, pattern_len - i, str + j + len); } while (res == -1 && len-- > 0); return res == -1 ? -1 : j + res + len; } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { return -1; } } return j; } /* HTTP 1.1 assumes keep alive if "Connection:" header is not set This function must tolerate situations when connection info is not set up, for example if request parsing failed. */ static int should_keep_alive(const struct mg_connection *conn) { const char *http_version = conn->request_info.http_version; const char *header = mg_get_header(conn, "Connection"); if (conn->must_close || conn->status_code == 401 || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || (header == NULL && http_version && 0!=strcmp(http_version, "1.1"))) { return 0; } return 1; } static const char *suggest_connection_header(const struct mg_connection *conn) { return should_keep_alive(conn) ? "keep-alive" : "close"; } static void handle_file_based_request(struct mg_connection *conn, const char *path, struct file *filep); static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep); static void send_http_error(struct mg_connection *, int, const char *, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(4, 5); static void send_http_error(struct mg_connection *conn, int status, const char *reason, const char *fmt, ...) { char buf[MG_BUF_LEN]; va_list ap; int len = 0, i, page_handler_found, scope; char date[64]; time_t curtime = time(NULL); const char *error_handler = NULL; struct file error_page_file = STRUCT_FILE_INITIALIZER; const char *error_page_file_ext, *tstr; conn->status_code = status; if (conn->in_error_handler || conn->ctx->callbacks.http_error == NULL || conn->ctx->callbacks.http_error(conn, status)) { if (!conn->in_error_handler) { /* Send user defined error pages, if defined */ error_handler = conn->ctx->config[ERROR_PAGES]; error_page_file_ext = conn->ctx->config[INDEX_FILES]; page_handler_found = 0; if (error_handler != NULL) { for (scope=1; (scope<=3) && !page_handler_found; scope++) { switch (scope) { case 1: len = mg_snprintf(conn, buf, sizeof(buf)-32, "%serror%03u.", error_handler, status); break; case 2: len = mg_snprintf(conn, buf, sizeof(buf)-32, "%serror%01uxx.", error_handler, status/100); break; default: len = mg_snprintf(conn, buf, sizeof(buf)-32, "%serror.", error_handler); break; } tstr = strchr(error_page_file_ext, '.'); while (tstr) { for (i=1; i<32 && tstr[i]!=0 && tstr[i]!=','; i++) buf[len+i-1]=tstr[i]; buf[len+i-1]=0; if (mg_stat(conn, buf, &error_page_file)) { page_handler_found = 1; break; } tstr = strchr(tstr+i, '.'); } } } if (page_handler_found) { conn->in_error_handler = 1; handle_file_based_request(conn, buf, &error_page_file); conn->in_error_handler = 0; return; } } buf[0] = '\0'; gmt_time_string(date, sizeof(date), &curtime); /* Errors 1xx, 204 and 304 MUST NOT send a body */ if (status > 199 && status != 204 && status != 304) { len = mg_snprintf(conn, buf, sizeof(buf)-1, "Error %d: %s", status, reason); buf[len] = '\n'; len++; buf[len] = 0; va_start(ap, fmt); len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap); va_end(ap); } DEBUG_TRACE("[%s]", buf); mg_printf(conn, "HTTP/1.1 %d %s\r\n" "Content-Length: %d\r\n" "Date: %s\r\n" "Connection: %s\r\n\r\n", status, reason, len, date, suggest_connection_header(conn)); conn->num_bytes_sent += mg_printf(conn, "%s", buf); } } #if defined(_WIN32) && !defined(__SYMBIAN32__) static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) { (void) unused; *mutex = CreateMutex(NULL, FALSE, NULL); return *mutex == NULL ? -1 : 0; } static int pthread_mutex_destroy(pthread_mutex_t *mutex) { return CloseHandle(*mutex) == 0 ? -1 : 0; } static int pthread_mutex_lock(pthread_mutex_t *mutex) { return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1; } static int pthread_mutex_trylock(pthread_mutex_t *mutex) { switch (WaitForSingleObject(*mutex, 0)) { case WAIT_OBJECT_0: return 0; case WAIT_TIMEOUT: return -2; /* EBUSY */ } return -1; } static int pthread_mutex_unlock(pthread_mutex_t *mutex) { return ReleaseMutex(*mutex) == 0 ? -1 : 0; } #ifndef WIN_PTHREADS_TIME_H static int clock_gettime(clockid_t clk_id, struct timespec *tp) { FILETIME ft; ULARGE_INTEGER li; BOOL ok = FALSE; double d; static double perfcnt_per_sec = 0.0; if (tp) { if (clk_id == CLOCK_REALTIME) { GetSystemTimeAsFileTime(&ft); li.LowPart = ft.dwLowDateTime; li.HighPart = ft.dwHighDateTime; li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */ tp->tv_sec = (time_t)(li.QuadPart / 10000000); tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; ok = TRUE; } else if (clk_id == CLOCK_MONOTONIC) { if (perfcnt_per_sec == 0.0) { QueryPerformanceFrequency((LARGE_INTEGER *) &li); perfcnt_per_sec = 1.0 / li.QuadPart; } if (perfcnt_per_sec != 0.0) { QueryPerformanceCounter((LARGE_INTEGER *) &li); d = li.QuadPart * perfcnt_per_sec; tp->tv_sec = (time_t)d; d -= tp->tv_sec; tp->tv_nsec = (long)(d*1.0E9); ok = TRUE; } } } return ok ? 0 : -1; } #endif static int pthread_cond_init(pthread_cond_t *cv, const void *unused) { (void) unused; InitializeCriticalSection(&cv->threadIdSec); cv->waitingthreadcount = 0; cv->waitingthreadhdls = mg_calloc(MAX_WORKER_THREADS, sizeof(pthread_t)); return (cv->waitingthreadhdls!=NULL) ? 0 : -1; } static int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mutex, const struct timespec * abstime) { struct mg_workerTLS * tls = (struct mg_workerTLS *)TlsGetValue(sTlsKey); int ok; struct timespec tsnow; int64_t nsnow, nswaitabs, nswaitrel; DWORD mswaitrel; EnterCriticalSection(&cv->threadIdSec); assert(cv->waitingthreadcount < MAX_WORKER_THREADS); cv->waitingthreadhdls[cv->waitingthreadcount] = tls->pthread_cond_helper_mutex; cv->waitingthreadcount++; LeaveCriticalSection(&cv->threadIdSec); if (abstime) { clock_gettime(CLOCK_REALTIME, &tsnow); nsnow = (((uint64_t)tsnow.tv_sec)<<32) + tsnow.tv_nsec; nswaitabs = (((uint64_t)abstime->tv_sec)<<32) + abstime->tv_nsec; nswaitrel = nswaitabs - nsnow; if (nswaitrel<0) nswaitrel=0; mswaitrel = (DWORD)(nswaitrel / 1000000); } else { mswaitrel = INFINITE; } pthread_mutex_unlock(mutex); ok = (WAIT_OBJECT_0 == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel)); pthread_mutex_lock(mutex); return ok ? 0 : -1; } static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) { return pthread_cond_timedwait(cv, mutex, NULL); } static int pthread_cond_signal(pthread_cond_t *cv) { int i; HANDLE wkup = NULL; BOOL ok = FALSE; EnterCriticalSection(&cv->threadIdSec); if (cv->waitingthreadcount) { wkup = cv->waitingthreadhdls[0]; ok = SetEvent(wkup); for (i=1; iwaitingthreadcount; i++) { cv->waitingthreadhdls[i-1] = cv->waitingthreadhdls[i]; } cv->waitingthreadcount--; assert(ok); } LeaveCriticalSection(&cv->threadIdSec); return ok ? 0 : 1; } static int pthread_cond_broadcast(pthread_cond_t *cv) { EnterCriticalSection(&cv->threadIdSec); while (cv->waitingthreadcount) { pthread_cond_signal(cv); } LeaveCriticalSection(&cv->threadIdSec); return 0; } static int pthread_cond_destroy(pthread_cond_t *cv) { EnterCriticalSection(&cv->threadIdSec); assert(cv->waitingthreadcount==0); mg_free(cv->waitingthreadhdls); cv->waitingthreadhdls = 0; LeaveCriticalSection(&cv->threadIdSec); DeleteCriticalSection(&cv->threadIdSec); return 0; } /* For Windows, change all slashes to backslashes in path names. */ static void change_slashes_to_backslashes(char *path) { int i; for (i = 0; path[i] != '\0'; i++) { if (path[i] == '/') path[i] = '\\'; /* i > 0 check is to preserve UNC paths, like \\server\file.txt */ if (path[i] == '\\' && i > 0) while (path[i + 1] == '\\' || path[i + 1] == '/') (void) memmove(path + i + 1, path + i + 2, strlen(path + i + 1)); } } /* Encode 'path' which is assumed UTF-8 string, into UNICODE string. wbuf and wbuf_len is a target buffer and its length. */ static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) { char buf[PATH_MAX], buf2[PATH_MAX]; mg_strlcpy(buf, path, sizeof(buf)); change_slashes_to_backslashes(buf); /* Convert to Unicode and back. If doubly-converted string does not match the original, something is fishy, reject. */ memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), NULL, NULL); if (strcmp(buf, buf2) != 0) { wbuf[0] = L'\0'; } } #if defined(_WIN32_WCE) static time_t time(time_t *ptime) { time_t t; SYSTEMTIME st; FILETIME ft; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime); if (ptime != NULL) { *ptime = t; } return t; } static struct tm *localtime(const time_t *ptime, struct tm *ptm) { int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF; FILETIME ft, lft; SYSTEMTIME st; TIME_ZONE_INFORMATION tzinfo; if (ptm == NULL) { return NULL; } * (int64_t *) &ft = t; FileTimeToLocalFileTime(&ft, &lft); FileTimeToSystemTime(&lft, &st); ptm->tm_year = st.wYear - 1900; ptm->tm_mon = st.wMonth - 1; ptm->tm_wday = st.wDayOfWeek; ptm->tm_mday = st.wDay; ptm->tm_hour = st.wHour; ptm->tm_min = st.wMinute; ptm->tm_sec = st.wSecond; ptm->tm_yday = 0; /* hope nobody uses this */ ptm->tm_isdst = GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0; return ptm; } static struct tm *gmtime(const time_t *ptime, struct tm *ptm) { /* FIXME(lsm): fix this. */ return localtime(ptime, ptm); } static size_t strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm) { (void) snprintf(dst, dst_size, "implement strftime() for WinCE"); return 0; } #endif /* Windows happily opens files with some garbage at the end of file name. For example, fopen("a.cgi ", "r") on Windows successfully opens "a.cgi", despite one would expect an error back. This function returns non-0 if path ends with some garbage. */ static int path_cannot_disclose_cgi(const char *path) { static const char *allowed_last_characters = "_-"; int last = path[strlen(path) - 1]; return isalnum(last) || strchr(allowed_last_characters, last) != NULL; } static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep) { wchar_t wbuf[PATH_MAX]; WIN32_FILE_ATTRIBUTE_DATA info; if (!is_file_in_memory(conn, path, filep)) { to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); filep->modification_time = SYS2UNIX_TIME( info.ftLastWriteTime.dwLowDateTime, info.ftLastWriteTime.dwHighDateTime); filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; /* If file name is fishy, reset the file structure and return error. Note it is important to reset, not just return the error, cause functions like is_file_opened() check the struct. */ if (!filep->is_directory && !path_cannot_disclose_cgi(path)) { memset(filep, 0, sizeof(*filep)); } } } return filep->membuf != NULL || filep->modification_time != 0; } static int mg_remove(const char *path) { wchar_t wbuf[PATH_MAX]; to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); return DeleteFileW(wbuf) ? 0 : -1; } static int mg_mkdir(const char *path, int mode) { char buf[PATH_MAX]; wchar_t wbuf[PATH_MAX]; (void) mode; mg_strlcpy(buf, path, sizeof(buf)); change_slashes_to_backslashes(buf); (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf)); return CreateDirectoryW(wbuf, NULL) ? 0 : -1; } /* Implementation of POSIX opendir/closedir/readdir for Windows. */ static DIR * opendir(const char *name) { DIR *dir = NULL; wchar_t wpath[PATH_MAX]; DWORD attrs; if (name == NULL) { SetLastError(ERROR_BAD_ARGUMENTS); } else if ((dir = (DIR *) mg_malloc(sizeof(*dir))) == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } else { to_unicode(name, wpath, ARRAY_SIZE(wpath)); attrs = GetFileAttributesW(wpath); if (attrs != 0xFFFFFFFF && ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { (void) wcscat(wpath, L"\\*"); dir->handle = FindFirstFileW(wpath, &dir->info); dir->result.d_name[0] = '\0'; } else { mg_free(dir); dir = NULL; } } return dir; } static int closedir(DIR *dir) { int result = 0; if (dir != NULL) { if (dir->handle != INVALID_HANDLE_VALUE) result = FindClose(dir->handle) ? 0 : -1; mg_free(dir); } else { result = -1; SetLastError(ERROR_BAD_ARGUMENTS); } return result; } static struct dirent *readdir(DIR *dir) { struct dirent *result = 0; if (dir) { if (dir->handle != INVALID_HANDLE_VALUE) { result = &dir->result; (void) WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName, -1, result->d_name, sizeof(result->d_name), NULL, NULL); if (!FindNextFileW(dir->handle, &dir->info)) { (void) FindClose(dir->handle); dir->handle = INVALID_HANDLE_VALUE; } } else { SetLastError(ERROR_FILE_NOT_FOUND); } } else { SetLastError(ERROR_BAD_ARGUMENTS); } return result; } #ifndef HAVE_POLL static int poll(struct pollfd *pfd, int n, int milliseconds) { struct timeval tv; fd_set set; int i, result; SOCKET maxfd = 0; tv.tv_sec = milliseconds / 1000; tv.tv_usec = (milliseconds % 1000) * 1000; FD_ZERO(&set); for (i = 0; i < n; i++) { FD_SET((SOCKET) pfd[i].fd, &set); pfd[i].revents = 0; if (pfd[i].fd > maxfd) { maxfd = pfd[i].fd; } } if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) { for (i = 0; i < n; i++) { if (FD_ISSET(pfd[i].fd, &set)) { pfd[i].revents = POLLIN; } } } return result; } #endif /* HAVE_POLL */ static void set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be null */) { (void) conn; /* Unused. */ (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); } int mg_start_thread(mg_thread_func_t f, void *p) { #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 */ return ((_beginthread((void (__cdecl *)(void *)) f, USE_STACK_SIZE, p) == ((uintptr_t)(-1L))) ? -1 : 0); #else return ((_beginthread((void (__cdecl *)(void *)) f, 0, p) == ((uintptr_t)(-1L))) ? -1 : 0); #endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ } /* Start a thread storing the thread context. */ static int mg_start_thread_with_id(unsigned (__stdcall *f)(void *), void *p, pthread_t *threadidptr) { uintptr_t uip; HANDLE threadhandle; int result = -1; uip = _beginthreadex(NULL, 0, (unsigned (__stdcall *)(void *)) f, p, 0, NULL); threadhandle = (HANDLE) uip; if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) { *threadidptr = threadhandle; result = 0; } return result; } /* Wait for a thread to finish. */ static int mg_join_thread(pthread_t threadid) { int result; DWORD dwevent; result = -1; dwevent = WaitForSingleObject(threadid, INFINITE); if (dwevent == WAIT_FAILED) { int err; err = GetLastError(); DEBUG_TRACE("WaitForSingleObject() failed, error %d", err); } else { if (dwevent == WAIT_OBJECT_0) { CloseHandle(threadid); result = 0; } } return result; } static HANDLE dlopen(const char *dll_name, int flags) { wchar_t wbuf[PATH_MAX]; (void) flags; to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf)); return LoadLibraryW(wbuf); } static int dlclose(void *handle) { int result; if (FreeLibrary(handle) != 0) { result = 0; } else { result = -1; } return result; } #if !defined(NO_CGI) #define SIGKILL 0 static int kill(pid_t pid, int sig_num) { (void) TerminateProcess(pid, sig_num); (void) CloseHandle(pid); return 0; } static void trim_trailing_whitespaces(char *s) { char *e = s + strlen(s) - 1; while (e > s && isspace(* (unsigned char *) e)) { *e-- = '\0'; } } static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir) { HANDLE me; char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX], cmdline[PATH_MAX], buf[PATH_MAX]; struct file file = STRUCT_FILE_INITIALIZER; STARTUPINFOA si; PROCESS_INFORMATION pi = { 0 }; (void) envp; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); /* TODO(lsm): redirect CGI errors to the error log file */ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; me = GetCurrentProcess(); DuplicateHandle(me, (HANDLE) _get_osfhandle(fdin), me, &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS); DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); /* If CGI file is a script, try to read the interpreter line */ interp = conn->ctx->config[CGI_INTERPRETER]; if (interp == NULL) { buf[0] = buf[1] = '\0'; /* Read the first line of the script into the buffer */ snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog); if (mg_fopen(conn, cmdline, "r", &file)) { p = (char *) file.membuf; mg_fgets(buf, sizeof(buf), &file, &p); mg_fclose(&file); buf[sizeof(buf) - 1] = '\0'; } if (buf[0] == '#' && buf[1] == '!') { trim_trailing_whitespaces(buf + 2); } else { buf[2] = '\0'; } interp = buf + 2; } if (interp[0] != '\0') { GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL); interp = full_interp; } GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL); mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s\"%s\\%s\"", interp, interp[0] == '\0' ? "" : " ", full_dir, prog); DEBUG_TRACE("Running [%s]", cmdline); if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) { mg_cry(conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, ERRNO); pi.hProcess = (pid_t) -1; } (void) CloseHandle(si.hStdOutput); (void) CloseHandle(si.hStdInput); if (pi.hThread != NULL) (void) CloseHandle(pi.hThread); return (pid_t) pi.hProcess; } #endif /* !NO_CGI */ static int set_non_blocking_mode(SOCKET sock) { unsigned long on = 1; return ioctlsocket(sock, FIONBIO, &on); } #else static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep) { struct stat st; if (!is_file_in_memory(conn, path, filep) && !stat(path, &st)) { filep->size = st.st_size; filep->modification_time = st.st_mtime; filep->is_directory = S_ISDIR(st.st_mode); } else { filep->modification_time = (time_t) 0; } return filep->membuf != NULL || filep->modification_time != (time_t) 0; } static void set_close_on_exec(int fd, struct mg_connection *conn /* may be null */) { if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { if (conn) { mg_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, strerror(ERRNO)); } } } int mg_start_thread(mg_thread_func_t func, void *param) { pthread_t thread_id; pthread_attr_t attr; int result; (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 */ (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE); #endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ result = pthread_create(&thread_id, &attr, func, param); pthread_attr_destroy(&attr); return result; } /* Start a thread storing the thread context. */ static int mg_start_thread_with_id(mg_thread_func_t func, void *param, pthread_t *threadidptr) { pthread_t thread_id; pthread_attr_t attr; int result; (void) pthread_attr_init(&attr); #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 */ (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE); #endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */ result = pthread_create(&thread_id, &attr, func, param); pthread_attr_destroy(&attr); if (threadidptr != NULL) { *threadidptr = thread_id; } return result; } /* Wait for a thread to finish. */ static int mg_join_thread(pthread_t threadid) { int result; result = pthread_join(threadid, NULL); return result; } #ifndef NO_CGI static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir) { pid_t pid; const char *interp; (void) envblk; if ((pid = fork()) == -1) { /* Parent */ send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO)); } else if (pid == 0) { /* Child */ if (chdir(dir) != 0) { mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); } else if (dup2(fdin, 0) == -1) { mg_cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO)); } else if (dup2(fdout, 1) == -1) { mg_cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO)); } else { /* Not redirecting stderr to stdout, to avoid output being littered with the error messages. */ (void) close(fdin); (void) close(fdout); /* After exec, all signal handlers are restored to their default values, with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's implementation, SIGCHLD's handler will leave unchanged after exec if it was set to be ignored. Restore it to default action. */ signal(SIGCHLD, SIG_DFL); interp = conn->ctx->config[CGI_INTERPRETER]; if (interp == NULL) { (void) execle(prog, prog, NULL, envp); mg_cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO)); } else { (void) execle(interp, interp, prog, NULL, envp); mg_cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog, strerror(ERRNO)); } } exit(EXIT_FAILURE); } return pid; } #endif /* !NO_CGI */ static int set_non_blocking_mode(SOCKET sock) { int flags; flags = fcntl(sock, F_GETFL, 0); (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK); return 0; } #endif /* _WIN32 */ /* Write data to the IO channel - opened file descriptor, socket or SSL descriptor. Return number of bytes written. */ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len) { int64_t sent; int n, k; (void) ssl; /* Get rid of warning */ sent = 0; while (sent < len) { /* How many bytes we send in this iteration */ k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent); #ifndef NO_SSL if (ssl != NULL) { n = SSL_write(ssl, buf + sent, k); } else #endif if (fp != NULL) { n = (int) fwrite(buf + sent, 1, (size_t) k, fp); if (ferror(fp)) n = -1; } else { n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL); } if (n <= 0) break; sent += n; } return sent; } /* Read from IO channel - opened file descriptor, socket, or SSL descriptor. Return negative value on error, or number of bytes read on success. */ static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) { int nread; if (fp != NULL) { /* Use read() instead of fread(), because if we're reading from the CGI pipe, fread() may block until IO buffer is filled up. We cannot afford to block and must pass all read bytes immediately to the client. */ nread = read(fileno(fp), buf, (size_t) len); #ifndef NO_SSL } else if (conn->ssl != NULL) { nread = SSL_read(conn->ssl, buf, len); #endif } else { nread = recv(conn->client.sock, buf, (size_t) len, 0); } return conn->ctx->stop_flag ? -1 : nread; } static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) { int n, nread = 0; while (len > 0 && conn->ctx->stop_flag == 0) { n = pull(fp, conn, buf + nread, len); if (n < 0) { nread = n; /* Propagate the error */ break; } else if (n == 0) { break; /* No more data to read */ } else { conn->consumed_content += n; nread += n; len -= n; } } return nread; } int mg_read(struct mg_connection *conn, void *buf, size_t len) { int64_t n, buffered_len, nread; const char *body; /* If Content-Length is not set for a PUT or POST request, read until socket is closed */ if (conn->consumed_content == 0 && conn->content_len == -1) { conn->content_len = INT64_MAX; conn->must_close = 1; } nread = 0; if (conn->consumed_content < conn->content_len) { /* Adjust number of bytes to read. */ int64_t to_read = conn->content_len - conn->consumed_content; if (to_read < (int64_t) len) { len = (size_t) to_read; } /* Return buffered data */ body = conn->buf + conn->request_len + conn->consumed_content; buffered_len = (int64_t)(&conn->buf[conn->data_len] - body); if (buffered_len > 0) { if (len < (size_t) buffered_len) { buffered_len = (int64_t) len; } memcpy(buf, body, (size_t) buffered_len); len -= buffered_len; conn->consumed_content += buffered_len; nread += buffered_len; buf = (char *) buf + buffered_len; } /* We have returned all buffered data. Read new data from the remote socket. */ n = pull_all(NULL, conn, (char *) buf, (int64_t) len); nread = n >= 0 ? nread + n : n; } return nread; } int mg_write(struct mg_connection *conn, const void *buf, size_t len) { time_t now; int64_t n, total, allowed; if (conn->throttle > 0) { if ((now = time(NULL)) != conn->last_throttle_time) { conn->last_throttle_time = now; conn->last_throttle_bytes = 0; } allowed = conn->throttle - conn->last_throttle_bytes; if (allowed > (int64_t) len) { allowed = len; } if ((total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf, (int64_t) allowed)) == allowed) { buf = (char *) buf + total; conn->last_throttle_bytes += total; while (total < (int64_t) len && conn->ctx->stop_flag == 0) { allowed = conn->throttle > (int64_t) len - total ? (int64_t) len - total : conn->throttle; if ((n = push(NULL, conn->client.sock, conn->ssl, (const char *) buf, (int64_t) allowed)) != allowed) { break; } sleep(1); conn->last_throttle_bytes = allowed; conn->last_throttle_time = time(NULL); buf = (char *) buf + n; total += n; } } } else { total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf, (int64_t) len); } return (int) total; } /* Alternative alloc_vprintf() for non-compliant C runtimes */ static int alloc_vprintf2(char **buf, const char *fmt, va_list ap) { va_list ap_copy; int size = MG_BUF_LEN; int len = -1; *buf = NULL; while (len == -1) { if (*buf) mg_free(*buf); *buf = (char *)mg_malloc(size *= 4); if (!*buf) break; va_copy(ap_copy, ap); len = vsnprintf(*buf, size, fmt, ap_copy); va_end(ap_copy); } return len; } /* Print message to buffer. If buffer is large enough to hold the message, return buffer. If buffer is to small, allocate large enough buffer on heap, and return allocated buffer. */ static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { va_list ap_copy; int len; /* Windows is not standard-compliant, and vsnprintf() returns -1 if buffer is too small. Also, older versions of msvcrt.dll do not have _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. Therefore, we make two passes: on first pass, get required message length. On second pass, actually print the message. */ va_copy(ap_copy, ap); len = vsnprintf(NULL, 0, fmt, ap_copy); va_end(ap_copy); if (len < 0) { /* C runtime is not standard compliant, vsnprintf() returned -1. Switch to alternative code path that uses incremental allocations. */ va_copy(ap_copy, ap); len = alloc_vprintf2(buf, fmt, ap); va_end(ap_copy); } else if (len > (int) size && (size = len + 1) > 0 && (*buf = (char *) mg_malloc(size)) == NULL) { len = -1; /* Allocation failed, mark failure */ } else { va_copy(ap_copy, ap); IGNORE_UNUSED_RESULT(vsnprintf(*buf, size, fmt, ap_copy)); va_end(ap_copy); } return len; } int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap); int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { char mem[MG_BUF_LEN], *buf = mem; int len; if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { len = mg_write(conn, buf, (size_t) len); } if (buf != mem && buf != NULL) { mg_free(buf); } return len; } int mg_printf(struct mg_connection *conn, const char *fmt, ...) { va_list ap; int result; va_start(ap, fmt); result = mg_vprintf(conn, fmt, ap); va_end(ap); return result; } int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) { int i, j, a, b; #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { if (i < src_len - 2 && src[i] == '%' && isxdigit(* (const unsigned char *) (src + i + 1)) && isxdigit(* (const unsigned char *) (src + i + 2))) { a = tolower(* (const unsigned char *) (src + i + 1)); b = tolower(* (const unsigned char *) (src + i + 2)); dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); i += 2; } else if (is_form_url_encoded && src[i] == '+') { dst[j] = ' '; } else { dst[j] = src[i]; } } dst[j] = '\0'; /* Null-terminate the destination */ return i >= src_len ? j : -1; } int mg_get_var(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len) { return mg_get_var2(data,data_len,name,dst,dst_len,0); } int mg_get_var2(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len, size_t occurrence) { const char *p, *e, *s; size_t name_len; int len; if (dst == NULL || dst_len == 0) { len = -2; } else if (data == NULL || name == NULL || data_len == 0) { len = -1; dst[0] = '\0'; } else { name_len = strlen(name); e = data + data_len; len = -1; dst[0] = '\0'; /* data is "var1=val1&var2=val2...". Find variable first */ for (p = data; p + name_len < e; p++) { if ((p == data || p[-1] == '&') && p[name_len] == '=' && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) { /* Point p to variable value */ p += name_len + 1; /* Point s to the end of the value */ s = (const char *) memchr(p, '&', (size_t)(e - p)); if (s == NULL) { s = e; } assert(s >= p); /* Decode variable into destination buffer */ len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1); /* Redirect error code from -1 to -2 (destination buffer too small). */ if (len == -1) { len = -2; } break; } } } return len; } int mg_get_cookie(const char *cookie_header, const char *var_name, char *dst, size_t dst_size) { const char *s, *p, *end; int name_len, len = -1; if (dst == NULL || dst_size == 0) { len = -2; } else if (var_name == NULL || (s = cookie_header) == NULL) { len = -1; dst[0] = '\0'; } else { name_len = (int) strlen(var_name); end = s + strlen(s); dst[0] = '\0'; for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { if (s[name_len] == '=') { s += name_len + 1; if ((p = strchr(s, ' ')) == NULL) p = end; if (p[-1] == ';') p--; if (*s == '"' && p[-1] == '"' && p > s + 1) { s++; p--; } if ((size_t) (p - s) < dst_size) { len = (int)(p - s); mg_strlcpy(dst, s, (size_t) len + 1); } else { len = -3; } break; } } } return len; } #if defined(USE_WEBSOCKET) || defined(USE_LUA) static void base64_encode(const unsigned char *src, int src_len, char *dst) { static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int i, j, a, b, c; for (i = j = 0; i < src_len; i += 3) { a = src[i]; b = i + 1 >= src_len ? 0 : src[i + 1]; c = i + 2 >= src_len ? 0 : src[i + 2]; dst[j++] = b64[a >> 2]; dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; if (i + 1 < src_len) { dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; } if (i + 2 < src_len) { dst[j++] = b64[c & 63]; } } while (j % 4 != 0) { dst[j++] = '='; } dst[j++] = '\0'; } static unsigned char b64reverse(char letter) { if (letter>='A' && letter<='Z') return letter-'A'; if (letter>='a' && letter<='z') return letter-'a'+26; if (letter>='0' && letter<='9') return letter-'0'+52; if (letter=='+') return 62; if (letter=='/') return 63; if (letter=='=') return 255; /* normal end */ return 254; /* error */ } static int base64_decode(const unsigned char *src, int src_len, char *dst, size_t *dst_len) { int i; unsigned char a, b, c, d; *dst_len = 0; for (i = 0; i < src_len; i += 4) { a = b64reverse(src[i]); if (a>=254) return i; b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]); if (b>=254) return i+1; c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]); if (c==254) return i+2; d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]); if (c==254) return i+3; dst[(*dst_len)++] = (a << 2) + (b >> 4); if (c!=255) { dst[(*dst_len)++] = (b << 4) + (c >> 2); if (d!=255) { dst[(*dst_len)++] = (c << 6) + d; } } } return -1; } #endif static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, size_t buf_len, struct file *filep, int * is_script_ressource) { struct vec a, b; const char *rewrite, *uri = conn->request_info.uri, *root = conn->ctx->config[DOCUMENT_ROOT]; char *p; int match_len; char gz_path[PATH_MAX]; char const* accept_encoding; *is_script_ressource = 0; #if defined(USE_WEBSOCKET) if (is_websocket_request(conn) && conn->ctx->config[WEBSOCKET_ROOT]) { root = conn->ctx->config[WEBSOCKET_ROOT]; } #endif /* Using buf_len - 1 because memmove() for PATH_INFO may shift part of the path one byte on the right. If document_root is NULL, leave the file empty. */ mg_snprintf(conn, buf, buf_len - 1, "%s%s", root == NULL ? "" : root, root == NULL ? "" : uri); rewrite = conn->ctx->config[REWRITE]; while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { if ((match_len = match_prefix(a.ptr, (int) a.len, uri)) > 0) { mg_snprintf(conn, buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr, uri + match_len); break; } } if (mg_stat(conn, buf, filep)) return; /* if we can't find the actual file, look for the file with the same name but a .gz extension. If we find it, use that and set the gzipped flag in the file struct to indicate that the response need to have the content- encoding: gzip header we can only do this if the browser declares support */ if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { if (strstr(accept_encoding,"gzip") != NULL) { snprintf(gz_path, sizeof(gz_path), "%s.gz", buf); if (mg_stat(conn, gz_path, filep)) { filep->gzipped = 1; return; } } } /* Support PATH_INFO for CGI scripts. */ for (p = buf + strlen(buf); p > buf + 1; p--) { if (*p == '/') { *p = '\0'; if ((match_prefix(conn->ctx->config[CGI_EXTENSIONS], (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 #ifdef USE_LUA || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), buf) > 0 #endif ) && mg_stat(conn, buf, filep)) { /* Shift PATH_INFO block one character right, e.g. "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" conn->path_info is pointing to the local variable "path" declared in handle_request(), so PATH_INFO is not valid after handle_request returns. */ conn->path_info = p + 1; memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for trailing \0 */ p[1] = '/'; *is_script_ressource = 1; break; } else { *p = '/'; } } } } /* Check whether full request is buffered. Return: -1 if request is malformed 0 if request is not yet fully buffered >0 actual request length, including last \r\n\r\n */ static int get_request_len(const char *buf, int buflen) { const char *s, *e; int len = 0; for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) /* Control characters are not allowed but >=128 is. */ if (!isprint(* (const unsigned char *) s) && *s != '\r' && *s != '\n' && * (const unsigned char *) s < 128) { len = -1; break; /* [i_a] abort scan as soon as one malformed character is found; */ /* don't let subsequent \r\n\r\n win us over anyhow */ } else if (s[0] == '\n' && s[1] == '\n') { len = (int) (s - buf) + 2; } else if (s[0] == '\n' && &s[1] < e && s[1] == '\r' && s[2] == '\n') { len = (int) (s - buf) + 3; } return len; } /* Convert month to the month number. Return -1 on error, or month number */ static int get_month_index(const char *s) { size_t i; for (i = 0; i < ARRAY_SIZE(month_names); i++) if (!strcmp(s, month_names[i])) return (int) i; return -1; } static int num_leap_years(int year) { return year / 4 - year / 100 + year / 400; } /* Parse UTC date-time string, and return the corresponding time_t value. */ static time_t parse_date_string(const char *datetime) { static const unsigned short days_before_month[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; char month_str[32]={0}; int second, minute, hour, day, month, year, leap_days, days; time_t result = (time_t) 0; if ((sscanf(datetime, "%d/%3s/%d %d:%d:%d", &day, month_str, &year, &hour, &minute, &second) == 6) || (sscanf(datetime, "%d %3s %d %d:%d:%d", &day, month_str, &year, &hour, &minute, &second) == 6) || (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", &day, month_str, &year, &hour, &minute, &second) == 6) || (sscanf(datetime, "%d-%3s-%d %d:%d:%d", &day, month_str, &year, &hour, &minute, &second) == 6)) { month = get_month_index(month_str); if ((month >= 0) && (year > 1970)) { leap_days = num_leap_years(year) - num_leap_years(1970); year -= 1970; days = year * 365 + days_before_month[month] + (day - 1) + leap_days; result = (time_t) days * 24 * 3600 + (time_t) hour * 3600 + minute * 60 + second; } } return result; } /* Protect against directory disclosure attack by removing '..', excessive '/' and '\' characters */ static void remove_double_dots_and_double_slashes(char *s) { char *p = s; while (*s != '\0') { *p++ = *s++; if (s[-1] == '/' || s[-1] == '\\') { /* Skip all following slashes, backslashes and double-dots */ while (s[0] != '\0') { if (s[0] == '/' || s[0] == '\\') { s++; } else if (s[0] == '.' && s[1] == '.') { s += 2; } else { break; } } } } *p = '\0'; } static const struct { const char *extension; size_t ext_len; const char *mime_type; } builtin_mime_types[] = { /* IANA registered MIME types (http://www.iana.org/assignments/media-types) application types */ {".doc", 4, "application/msword"}, {".eps", 4, "application/postscript"}, {".exe", 4, "application/octet-stream"}, {".js", 3, "application/javascript"}, {".json", 5, "application/json"}, {".pdf", 4, "application/pdf"}, {".ps", 3, "application/postscript"}, {".rtf", 4, "application/rtf"}, {".xhtml", 6, "application/xhtml+xml"}, {".xsl", 4, "application/xml"}, {".xslt", 5, "application/xml"}, /* audio */ {".mp3", 4, "audio/mpeg"}, {".oga", 4, "audio/ogg"}, {".ogg", 4, "audio/ogg"}, /* image */ {".gif", 4, "image/gif"}, {".ief", 4, "image/ief"}, {".jpeg", 5, "image/jpeg"}, {".jpg", 4, "image/jpeg"}, {".jpm", 4, "image/jpm"}, {".jpx", 4, "image/jpx"}, {".png", 4, "image/png"}, {".svg", 4, "image/svg+xml"}, {".tif", 4, "image/tiff"}, {".tiff", 5, "image/tiff"}, /* model */ {".wrl", 4, "model/vrml"}, /* text */ {".css", 4, "text/css"}, {".csv", 4, "text/csv"}, {".htm", 4, "text/html"}, {".html", 5, "text/html"}, {".sgm", 4, "text/sgml"}, {".shtm", 5, "text/html"}, {".shtml", 6, "text/html"}, {".txt", 4, "text/plain"}, {".xml", 4, "text/xml"}, /* video */ {".mov", 4, "video/quicktime"}, {".mp4", 4, "video/mp4"}, {".mpeg", 5, "video/mpeg"}, {".mpg", 4, "video/mpeg"}, {".ogv", 4, "video/ogg"}, {".qt", 3, "video/quicktime"}, /* not registered types (http://reference.sitepoint.com/html/mime-types-full, http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */ {".arj", 4, "application/x-arj-compressed"}, {".gz", 3, "application/x-gunzip"}, {".rar", 4, "application/x-arj-compressed"}, {".swf", 4, "application/x-shockwave-flash"}, {".tar", 4, "application/x-tar"}, {".tgz", 4, "application/x-tar-gz"}, {".torrent", 8, "application/x-bittorrent"}, {".ppt", 4, "application/x-mspowerpoint"}, {".xls", 4, "application/x-msexcel"}, {".zip", 4, "application/x-zip-compressed"}, {".aac", 4, "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */ {".aif", 4, "audio/x-aif"}, {".m3u", 4, "audio/x-mpegurl"}, {".mid", 4, "audio/x-midi"}, {".ra", 3, "audio/x-pn-realaudio"}, {".ram", 4, "audio/x-pn-realaudio"}, {".wav", 4, "audio/x-wav"}, {".bmp", 4, "image/bmp"}, {".ico", 4, "image/x-icon"}, {".pct", 4, "image/x-pct"}, {".pict", 5, "image/pict"}, {".rgb", 4, "image/x-rgb"}, {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */ {".asf", 4, "video/x-ms-asf"}, {".avi", 4, "video/x-msvideo"}, {".m4v", 4, "video/x-m4v"}, {NULL, 0, NULL} }; const char *mg_get_builtin_mime_type(const char *path) { const char *ext; size_t i, path_len; path_len = strlen(path); for (i = 0; builtin_mime_types[i].extension != NULL; i++) { ext = path + (path_len - builtin_mime_types[i].ext_len); if (path_len > builtin_mime_types[i].ext_len && mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) { return builtin_mime_types[i].mime_type; } } return "text/plain"; } /* Look at the "path" extension and figure what mime type it has. Store mime type in the vector. */ static void get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec) { struct vec ext_vec, mime_vec; const char *list, *ext; size_t path_len; path_len = strlen(path); /* Scan user-defined mime types first, in case user wants to override default mime types. */ list = ctx->config[EXTRA_MIME_TYPES]; while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { /* ext now points to the path suffix */ ext = path + path_len - ext_vec.len; if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { *vec = mime_vec; return; } } vec->ptr = mg_get_builtin_mime_type(path); vec->len = strlen(vec->ptr); } /* Stringify binary data. Output buffer must be twice as big as input, because each byte takes 2 bytes in string representation */ static void bin2str(char *to, const unsigned char *p, size_t len) { static const char *hex = "0123456789abcdef"; for (; len--; p++) { *to++ = hex[p[0] >> 4]; *to++ = hex[p[0] & 0x0f]; } *to = '\0'; } /* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */ char *mg_md5(char buf[33], ...) { md5_byte_t hash[16]; const char *p; va_list ap; md5_state_t ctx; md5_init(&ctx); va_start(ap, buf); while ((p = va_arg(ap, const char *)) != NULL) { md5_append(&ctx, (const md5_byte_t *) p, (int) strlen(p)); } va_end(ap); md5_finish(&ctx, hash); bin2str(buf, hash, sizeof(hash)); return buf; } /* Check the user's password, return 1 if OK */ static int check_password(const char *method, const char *ha1, const char *uri, const char *nonce, const char *nc, const char *cnonce, const char *qop, const char *response) { char ha2[32 + 1], expected_response[32 + 1]; /* Some of the parameters may be NULL */ if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL || qop == NULL || response == NULL) { return 0; } /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */ /* TODO(lsm): check for authentication timeout */ if (/* strcmp(dig->uri, c->ouri) != 0 || */ strlen(response) != 32 /* || now - strtoul(dig->nonce, NULL, 10) > 3600 */ ) { return 0; } mg_md5(ha2, method, ":", uri, NULL); mg_md5(expected_response, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, ":", ha2, NULL); return mg_strcasecmp(response, expected_response) == 0; } /* Use the global passwords file, if specified by auth_gpass option, or search for .htpasswd in the requested directory. */ static void open_auth_file(struct mg_connection *conn, const char *path, struct file *filep) { char name[PATH_MAX]; const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE]; struct file file = STRUCT_FILE_INITIALIZER; if (gpass != NULL) { /* Use global passwords file */ if (!mg_fopen(conn, gpass, "r", filep)) { #ifdef DEBUG mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO)); #endif } /* Important: using local struct file to test path for is_directory flag. If filep is used, mg_stat() makes it appear as if auth file was opened. */ } else if (mg_stat(conn, path, &file) && file.is_directory) { mg_snprintf(conn, name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME); if (!mg_fopen(conn, name, "r", filep)) { #ifdef DEBUG mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); #endif } } else { /* Try to find .htpasswd in requested directory. */ for (p = path, e = p + strlen(p) - 1; e > p; e--) { if (e[0] == '/') { break; } } mg_snprintf(conn, name, sizeof(name), "%.*s%c%s", (int) (e - p), p, '/', PASSWORDS_FILE_NAME); if (!mg_fopen(conn, name, "r", filep)) { #ifdef DEBUG mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); #endif } } } /* Parsed Authorization header */ struct ah { char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; }; /* Return 1 on success. Always initializes the ah structure. */ static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah) { char *name, *value, *s; const char *auth_header; unsigned long nonce; (void) memset(ah, 0, sizeof(*ah)); if ((auth_header = mg_get_header(conn, "Authorization")) == NULL || mg_strncasecmp(auth_header, "Digest ", 7) != 0) { return 0; } /* Make modifiable copy of the auth header */ (void) mg_strlcpy(buf, auth_header + 7, buf_size); s = buf; /* Parse authorization header */ for (;;) { /* Gobble initial spaces */ while (isspace(* (unsigned char *) s)) { s++; } name = skip_quoted(&s, "=", " ", 0); /* Value is either quote-delimited, or ends at first comma or space. */ if (s[0] == '\"') { s++; value = skip_quoted(&s, "\"", " ", '\\'); if (s[0] == ',') { s++; } } else { value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses spaces */ } if (*name == '\0') { break; } if (!strcmp(name, "username")) { ah->user = value; } else if (!strcmp(name, "cnonce")) { ah->cnonce = value; } else if (!strcmp(name, "response")) { ah->response = value; } else if (!strcmp(name, "uri")) { ah->uri = value; } else if (!strcmp(name, "qop")) { ah->qop = value; } else if (!strcmp(name, "nc")) { ah->nc = value; } else if (!strcmp(name, "nonce")) { ah->nonce = value; } } #ifndef NO_NONCE_CHECK /* Convert the nonce from the client to a number and check it. */ /* Server side nonce check is valuable in all situations but one: if the server restarts frequently, but the client should not see that, so the server should accept nonces from previous starts. */ if (ah->nonce == NULL) { return 0; } nonce = strtoul(ah->nonce, &s, 10); if ((s == NULL) || (*s != 0)) { return 0; } nonce ^= (unsigned long)(conn->ctx); if (noncectx->start_time) { /* nonce is from a previous start of the server and no longer valid (replay attack?) */ return 0; } if (nonce>=conn->ctx->start_time+conn->ctx->nonce_count) { return 0; } #endif /* CGI needs it as REMOTE_USER */ if (ah->user != NULL) { conn->request_info.remote_user = mg_strdup(ah->user); } else { return 0; } return 1; } static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p) { char *eof; size_t len; char *memend; if (filep->membuf != NULL && *p != NULL) { memend = (char *) &filep->membuf[filep->size]; eof = (char *) memchr(*p, '\n', memend - *p); /* Search for \n from p till the end of stream */ if (eof != NULL) { eof += 1; /* Include \n */ } else { eof = memend; /* Copy remaining data */ } len = (size_t) (eof - *p) > size - 1 ? size - 1 : (size_t) (eof - *p); memcpy(buf, *p, len); buf[len] = '\0'; *p += len; return len ? eof : NULL; } else if (filep->fp != NULL) { return fgets(buf, (int)size, filep->fp); } else { return NULL; } } struct read_auth_file_struct { struct mg_connection *conn; struct ah ah; char *domain; char buf[256+256+40]; char *f_user; char *f_domain; char *f_ha1; }; static int read_auth_file(struct file *filep, struct read_auth_file_struct * workdata) { char *p; int is_authorized = 0; struct file fp; int l; /* Loop over passwords file */ p = (char *) filep->membuf; while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) { l = strlen(workdata->buf); while (l>0) { if (isspace(workdata->buf[l-1]) || iscntrl(workdata->buf[l-1])) { l--; workdata->buf[l] = 0; } else break; } if (l<1) continue; workdata->f_user = workdata->buf; if (workdata->f_user[0]==':') { /* user names may not contain a ':' and may not be empty, so lines starting with ':' may be used for a special purpose */ if (workdata->f_user[1]=='#') { /* :# is a comment */ continue; } else if (!strncmp(workdata->f_user+1,"include=",8)) { if (mg_fopen(workdata->conn, workdata->f_user+9, "r", &fp)) { is_authorized = read_auth_file(&fp, workdata); mg_fclose(&fp); } else { mg_cry(workdata->conn, "%s: cannot open authorization file: %s", __func__, workdata->buf); } continue; } /* everything is invalid for the moment (might change in the future) */ mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", __func__, workdata->buf); continue; } workdata->f_domain = strchr(workdata->f_user, ':'); if (workdata->f_domain == NULL) { mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", __func__, workdata->buf); continue; } *(workdata->f_domain) = 0; (workdata->f_domain)++; workdata->f_ha1 = strchr(workdata->f_domain, ':'); if (workdata->f_ha1 == NULL) { mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", __func__, workdata->buf); continue; } *(workdata->f_ha1) = 0; (workdata->f_ha1)++; if (!strcmp(workdata->ah.user, workdata->f_user) && !strcmp(workdata->domain, workdata->f_domain)) { return check_password(workdata->conn->request_info.request_method, workdata->f_ha1, workdata->ah.uri, workdata->ah.nonce, workdata->ah.nc, workdata->ah.cnonce, workdata->ah.qop, workdata->ah.response); } } return is_authorized; } /* Authorize against the opened passwords file. Return 1 if authorized. */ static int authorize(struct mg_connection *conn, struct file *filep) { struct read_auth_file_struct workdata; char buf[MG_BUF_LEN]; memset(&workdata,0,sizeof(workdata)); workdata.conn = conn; if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) { return 0; } workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; return read_auth_file(filep, &workdata); } /* Return 1 if request is authorised, 0 otherwise. */ static int check_authorization(struct mg_connection *conn, const char *path) { char fname[PATH_MAX]; struct vec uri_vec, filename_vec; const char *list; struct file file = STRUCT_FILE_INITIALIZER; int authorized = 1; list = conn->ctx->config[PROTECT_URI]; while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) { mg_snprintf(conn, fname, sizeof(fname), "%.*s", (int) filename_vec.len, filename_vec.ptr); if (!mg_fopen(conn, fname, "r", &file)) { mg_cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno)); } break; } } if (!is_file_opened(&file)) { open_auth_file(conn, path, &file); } if (is_file_opened(&file)) { authorized = authorize(conn, &file); mg_fclose(&file); } return authorized; } static void send_authorization_request(struct mg_connection *conn) { char date[64]; time_t curtime = time(NULL); unsigned long nonce = (unsigned long)(conn->ctx->start_time); (void)pthread_mutex_lock(&conn->ctx->nonce_mutex); nonce += conn->ctx->nonce_count; ++conn->ctx->nonce_count; (void)pthread_mutex_unlock(&conn->ctx->nonce_mutex); nonce ^= (unsigned long)(conn->ctx); conn->status_code = 401; conn->must_close = 1; gmt_time_string(date, sizeof(date), &curtime); mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n" "Date: %s\r\n" "Connection: %s\r\n" "Content-Length: 0\r\n" "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", nonce=\"%lu\"\r\n\r\n", date, suggest_connection_header(conn), conn->ctx->config[AUTHENTICATION_DOMAIN], nonce); } static int is_authorized_for_put(struct mg_connection *conn) { struct file file = STRUCT_FILE_INITIALIZER; const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; int ret = 0; if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) { ret = authorize(conn, &file); mg_fclose(&file); } return ret; } int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass) { int found, i; char line[512], u[512] = "", d[512] ="", ha1[33], tmp[PATH_MAX+8]; FILE *fp, *fp2; found = 0; fp = fp2 = NULL; /* Regard empty password as no password - remove user record. */ if (pass != NULL && pass[0] == '\0') { pass = NULL; } /* Other arguments must not be empty */ if (fname == NULL || domain == NULL || user == NULL) return 0; /* Using the given file format, user name and domain must not contain ':' */ if (strchr(user, ':') != NULL) return 0; if (strchr(domain, ':') != NULL) return 0; /* Do not allow control characters like newline in user name and domain. Do not allow excessively long names either. */ for (i=0; i<255 && user[i]!=0; i++) { if (iscntrl(user[i])) return 0; } if (user[i]) return 0; for (i=0; i<255 && domain[i]!=0; i++) { if (iscntrl(domain[i])) return 0; } if (domain[i]) return 0; /* Create a temporary file name */ (void) snprintf(tmp, sizeof(tmp) - 1, "%s.tmp", fname); tmp[sizeof(tmp) - 1] = 0; /* Create the file if does not exist */ if ((fp = fopen(fname, "a+")) != NULL) { (void) fclose(fp); } /* Open the given file and temporary file */ if ((fp = fopen(fname, "r")) == NULL) { return 0; } else if ((fp2 = fopen(tmp, "w+")) == NULL) { fclose(fp); return 0; } /* Copy the stuff to temporary file */ while (fgets(line, sizeof(line), fp) != NULL) { if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) { continue; } u[255]=0; d[255]=0; if (!strcmp(u, user) && !strcmp(d, domain)) { found++; if (pass != NULL) { mg_md5(ha1, user, ":", domain, ":", pass, NULL); fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); } } else { fprintf(fp2, "%s", line); } } /* If new user, just add it */ if (!found && pass != NULL) { mg_md5(ha1, user, ":", domain, ":", pass, NULL); fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); } /* Close files */ fclose(fp); fclose(fp2); /* Put the temp file in place of real file */ IGNORE_UNUSED_RESULT(remove(fname)); IGNORE_UNUSED_RESULT(rename(tmp, fname)); return 1; } static SOCKET conn2(struct mg_context *ctx /* may be null */, const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len) { struct sockaddr_in sain; struct hostent *he; SOCKET sock = INVALID_SOCKET; if (host == NULL) { snprintf(ebuf, ebuf_len, "%s", "NULL host"); } else if (use_ssl && SSLv23_client_method == NULL) { snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized"); /* TODO(lsm): use something threadsafe instead of gethostbyname() */ } else if ((he = gethostbyname(host)) == NULL) { snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO)); } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); } else { set_close_on_exec(sock, fc(ctx)); memset(&sain, '\0', sizeof(sain)); sain.sin_family = AF_INET; sain.sin_port = htons((uint16_t) port); sain.sin_addr = * (struct in_addr *) he->h_addr_list[0]; if (connect(sock, (struct sockaddr *) &sain, sizeof(sain)) != 0) { snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", host, port, strerror(ERRNO)); closesocket(sock); sock = INVALID_SOCKET; } } return sock; } int mg_url_encode(const char *src, char *dst, size_t dst_len) { static const char *dont_escape = "._-$,;~()"; static const char *hex = "0123456789abcdef"; char *pos = dst; const char *end = dst + dst_len - 1; for (; *src != '\0' && pos < end; src++, pos++) { if (isalnum(*(const unsigned char *) src) || strchr(dont_escape, * (const unsigned char *) src) != NULL) { *pos = *src; } else if (pos + 2 < end) { pos[0] = '%'; pos[1] = hex[(* (const unsigned char *) src) >> 4]; pos[2] = hex[(* (const unsigned char *) src) & 0xf]; pos += 2; } else { return -1; } } *pos = '\0'; return (*src == '\0') ? (int)(pos - dst) : -1; } static void print_dir_entry(struct de *de) { char size[64], mod[64], href[PATH_MAX]; struct tm *tm; if (de->file.is_directory) { mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]"); } else { /* We use (signed) cast below because MSVC 6 compiler cannot convert unsigned __int64 to double. Sigh. */ if (de->file.size < 1024) { mg_snprintf(de->conn, size, sizeof(size), "%d", (int) de->file.size); } else if (de->file.size < 0x100000) { mg_snprintf(de->conn, size, sizeof(size), "%.1fk", (double) de->file.size / 1024.0); } else if (de->file.size < 0x40000000) { mg_snprintf(de->conn, size, sizeof(size), "%.1fM", (double) de->file.size / 1048576); } else { mg_snprintf(de->conn, size, sizeof(size), "%.1fG", (double) de->file.size / 1073741824); } } tm = localtime(&de->file.modification_time); if (tm != NULL) { strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm); } else { mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod)); mod[sizeof(mod) - 1] = '\0'; } mg_url_encode(de->file_name, href, sizeof(href)); de->conn->num_bytes_sent += mg_printf(de->conn, "%s%s" " %s  %s\n", de->conn->request_info.uri, href, de->file.is_directory ? "/" : "", de->file_name, de->file.is_directory ? "/" : "", mod, size); } /* This function is called from send_directory() and used for sorting directory entries by size, or name, or modification time. On windows, __cdecl specification is needed in case if project is built with __stdcall convention. qsort always requires __cdels callback. */ static int WINCDECL compare_dir_entries(const void *p1, const void *p2) { const struct de *a = (const struct de *) p1, *b = (const struct de *) p2; const char *query_string = a->conn->request_info.query_string; int cmp_result = 0; if (query_string == NULL) { query_string = "na"; } if (a->file.is_directory && !b->file.is_directory) { return -1; /* Always put directories on top */ } else if (!a->file.is_directory && b->file.is_directory) { return 1; /* Always put directories on top */ } else if (*query_string == 'n') { cmp_result = strcmp(a->file_name, b->file_name); } else if (*query_string == 's') { cmp_result = a->file.size == b->file.size ? 0 : a->file.size > b->file.size ? 1 : -1; } else if (*query_string == 'd') { cmp_result = a->file.modification_time == b->file.modification_time ? 0 : a->file.modification_time > b->file.modification_time ? 1 : -1; } return query_string[1] == 'd' ? -cmp_result : cmp_result; } static int must_hide_file(struct mg_connection *conn, const char *path) { const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; const char *pattern = conn->ctx->config[HIDE_FILES]; return match_prefix(pw_pattern, (int)strlen(pw_pattern), path) > 0 || (pattern != NULL && match_prefix(pattern, (int)strlen(pattern), path) > 0); } static int scan_directory(struct mg_connection *conn, const char *dir, void *data, void (*cb)(struct de *, void *)) { char path[PATH_MAX]; struct dirent *dp; DIR *dirp; struct de de; if ((dirp = opendir(dir)) == NULL) { return 0; } else { de.conn = conn; while ((dp = readdir(dirp)) != NULL) { /* Do not show current dir and hidden files */ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") || must_hide_file(conn, dp->d_name)) { continue; } mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); /* If we don't memset stat structure to zero, mtime will have garbage and strftime() will segfault later on in print_dir_entry(). memset is required only if mg_stat() fails. For more details, see http://code.google.com/p/mongoose/issues/detail?id=79 */ memset(&de.file, 0, sizeof(de.file)); if (!mg_stat(conn, path, &de.file)) { mg_cry(conn, "%s: mg_stat(%s) failed: %s", __func__, path, strerror(ERRNO)); } de.file_name = dp->d_name; cb(&de, data); } (void) closedir(dirp); } return 1; } static int remove_directory(struct mg_connection *conn, const char *dir) { char path[PATH_MAX]; struct dirent *dp; DIR *dirp; struct de de; if ((dirp = opendir(dir)) == NULL) { return 0; } else { de.conn = conn; while ((dp = readdir(dirp)) != NULL) { /* Do not show current dir (but show hidden files as they will also be removed) */ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { continue; } mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); /* If we don't memset stat structure to zero, mtime will have garbage and strftime() will segfault later on in print_dir_entry(). memset is required only if mg_stat() fails. For more details, see http://code.google.com/p/mongoose/issues/detail?id=79 */ memset(&de.file, 0, sizeof(de.file)); if (!mg_stat(conn, path, &de.file)) { mg_cry(conn, "%s: mg_stat(%s) failed: %s", __func__, path, strerror(ERRNO)); } if(de.file.modification_time) { if(de.file.is_directory) { remove_directory(conn, path); } else { mg_remove(path); } } } (void) closedir(dirp); IGNORE_UNUSED_RESULT(rmdir(dir)); } return 1; } struct dir_scan_data { struct de *entries; int num_entries; int arr_size; }; /* Behaves like realloc(), but frees original pointer on failure */ static void *realloc2(void *ptr, size_t size) { void *new_ptr = mg_realloc(ptr, size); if (new_ptr == NULL) { mg_free(ptr); } return new_ptr; } static void dir_scan_callback(struct de *de, void *data) { struct dir_scan_data *dsd = (struct dir_scan_data *) data; if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) { dsd->arr_size *= 2; dsd->entries = (struct de *) realloc2(dsd->entries, dsd->arr_size * sizeof(dsd->entries[0])); } if (dsd->entries == NULL) { /* TODO(lsm): propagate an error to the caller */ dsd->num_entries = 0; } else { dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name); dsd->entries[dsd->num_entries].file = de->file; dsd->entries[dsd->num_entries].conn = de->conn; dsd->num_entries++; } } static void handle_directory_request(struct mg_connection *conn, const char *dir) { int i, sort_direction; struct dir_scan_data data = { NULL, 0, 128 }; char date[64]; time_t curtime = time(NULL); if (!scan_directory(conn, dir, &data, dir_scan_callback)) { send_http_error(conn, 500, "Cannot open directory", "Error: opendir(%s): %s", dir, strerror(ERRNO)); return; } gmt_time_string(date, sizeof(date), &curtime); sort_direction = conn->request_info.query_string != NULL && conn->request_info.query_string[1] == 'd' ? 'a' : 'd'; conn->must_close = 1; mg_printf(conn, "HTTP/1.1 200 OK\r\n" "Date: %s\r\n" "Connection: close\r\n" "Content-Type: text/html; charset=utf-8\r\n\r\n", date); conn->num_bytes_sent += mg_printf(conn, "Index of %s" "" "

Index of %s

"
                                      ""
                                      ""
                                      ""
                                      "",
                                      conn->request_info.uri, conn->request_info.uri,
                                      sort_direction, sort_direction, sort_direction);

    /* Print first entry - link to a parent directory */
    conn->num_bytes_sent += mg_printf(conn,
                                      ""
                                      "\n",
                                      conn->request_info.uri, "..", "Parent directory", "-", "-");

    /* Sort and print directory entries */
    if (data.entries != NULL) {
        qsort(data.entries, (size_t) data.num_entries,
              sizeof(data.entries[0]), compare_dir_entries);
        for (i = 0; i < data.num_entries; i++) {
            print_dir_entry(&data.entries[i]);
            mg_free(data.entries[i].file_name);
        }
        mg_free(data.entries);
    }

    conn->num_bytes_sent += mg_printf(conn, "%s", "
NameModifiedSize

%s %s  %s
"); conn->status_code = 200; } /* Send len bytes from the opened file to the client. */ static void send_file_data(struct mg_connection *conn, struct file *filep, int64_t offset, int64_t len) { char buf[MG_BUF_LEN]; int to_read, num_read, num_written; /* Sanity check the offset */ offset = offset < 0 ? 0 : offset > filep->size ? filep->size : offset; if (len > 0 && filep->membuf != NULL && filep->size > 0) { if (len > filep->size - offset) { len = filep->size - offset; } mg_write(conn, filep->membuf + offset, (size_t) len); } else if (len > 0 && filep->fp != NULL) { if (offset > 0 && fseeko(filep->fp, offset, SEEK_SET) != 0) { mg_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO)); } while (len > 0) { /* Calculate how much to read from the file in the buffer */ to_read = sizeof(buf); if ((int64_t) to_read > len) { to_read = (int) len; } /* Read from file, exit the loop on error */ if ((num_read = (int) fread(buf, 1, (size_t) to_read, filep->fp)) <= 0) { break; } /* Send read bytes to the client, exit the loop on error */ if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) { break; } /* Both read and were successful, adjust counters */ conn->num_bytes_sent += num_written; len -= num_written; } } } static int parse_range_header(const char *header, int64_t *a, int64_t *b) { return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); } static void construct_etag(char *buf, size_t buf_len, const struct file *filep) { snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) filep->modification_time, filep->size); } static void fclose_on_exec(struct file *filep, struct mg_connection *conn) { if (filep != NULL && filep->fp != NULL) { #ifdef _WIN32 (void) conn; /* Unused. */ #else if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) { mg_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, strerror(ERRNO)); } #endif } } static void handle_static_file_request(struct mg_connection *conn, const char *path, struct file *filep) { char date[64], lm[64], etag[64], range[64]; const char *msg = "OK", *hdr; time_t curtime = time(NULL); int64_t cl, r1, r2; struct vec mime_vec; int n; char gz_path[PATH_MAX]; const char *encoding = ""; const char *cors1, *cors2, *cors3; get_mime_type(conn->ctx, path, &mime_vec); cl = filep->size; conn->status_code = 200; range[0] = '\0'; /* if this file is in fact a pre-gzipped file, rewrite its filename it's important to rewrite the filename after resolving the mime type from it, to preserve the actual file's type */ if (filep->gzipped) { snprintf(gz_path, sizeof(gz_path), "%s.gz", path); path = gz_path; encoding = "Content-Encoding: gzip\r\n"; } if (!mg_fopen(conn, path, "rb", filep)) { send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path, strerror(ERRNO)); return; } fclose_on_exec(filep, conn); /* If Range: header specified, act accordingly */ r1 = r2 = 0; hdr = mg_get_header(conn, "Range"); if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && r1 >= 0 && r2 >= 0) { /* actually, range requests don't play well with a pre-gzipped file (since the range is specified in the uncompressed space) */ if (filep->gzipped) { send_http_error(conn, 501, "Not Implemented", "range requests in gzipped files are not supported"); mg_fclose(filep); return; } conn->status_code = 206; cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; mg_snprintf(conn, range, sizeof(range), "Content-Range: bytes " "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", r1, r1 + cl - 1, filep->size); msg = "Partial Content"; } hdr = mg_get_header(conn, "Origin"); if (hdr) { /* Cross-origin resource sharing (CORS), see http://www.html5rocks.com/en/tutorials/cors/, http://www.html5rocks.com/static/images/cors_server_flowchart.png - preflight is not supported for files. */ cors1 = "Access-Control-Allow-Origin: "; cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; cors3 = "\r\n"; } else { cors1 = cors2 = cors3 = ""; } /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */ gmt_time_string(date, sizeof(date), &curtime); gmt_time_string(lm, sizeof(lm), &filep->modification_time); construct_etag(etag, sizeof(etag), filep); (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n" "%s%s%s" "Date: %s\r\n" "Last-Modified: %s\r\n" "Etag: %s\r\n" "Content-Type: %.*s\r\n" "Content-Length: %" INT64_FMT "\r\n" "Connection: %s\r\n" "Accept-Ranges: bytes\r\n" "%s%s\r\n", conn->status_code, msg, cors1, cors2, cors3, date, lm, etag, (int) mime_vec.len, mime_vec.ptr, cl, suggest_connection_header(conn), range, encoding); if (strcmp(conn->request_info.request_method, "HEAD") != 0) { send_file_data(conn, filep, r1, cl); } mg_fclose(filep); } void mg_send_file(struct mg_connection *conn, const char *path) { struct file file = STRUCT_FILE_INITIALIZER; if (mg_stat(conn, path, &file)) { handle_static_file_request(conn, path, &file); } else { send_http_error(conn, 404, "Not Found", "%s", "File not found"); } } /* Parse HTTP headers from the given buffer, advance buffer to the point where parsing stopped. */ static void parse_http_headers(char **buf, struct mg_request_info *ri) { int i; for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) { ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0); ri->http_headers[i].value = skip(buf, "\r\n"); if (ri->http_headers[i].name[0] == '\0') break; ri->num_headers = i + 1; } } static int is_valid_http_method(const char *method) { return !strcmp(method, "GET") || !strcmp(method, "POST") || !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") || !strcmp(method, "PUT") || !strcmp(method, "DELETE") || !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND") || !strcmp(method, "MKCOL") ; } /* Parse HTTP request, fill in mg_request_info structure. This function modifies the buffer by NUL-terminating HTTP request components, header names and header values. */ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) { int is_request, request_length = get_request_len(buf, len); if (request_length > 0) { /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port */ ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL; ri->num_headers = 0; buf[request_length - 1] = '\0'; /* RFC says that all initial whitespaces should be ingored */ while (*buf != '\0' && isspace(* (unsigned char *) buf)) { buf++; } ri->request_method = skip(&buf, " "); ri->uri = skip(&buf, " "); ri->http_version = skip(&buf, "\r\n"); /* HTTP message could be either HTTP request or HTTP response, e.g. "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." */ is_request = is_valid_http_method(ri->request_method); if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { request_length = -1; } else { if (is_request) { ri->http_version += 5; } parse_http_headers(&buf, ri); } } return request_length; } /* Keep reading the input (either opened file descriptor fd, or socket sock, or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the buffer (which marks the end of HTTP request). Buffer buf may already have some data. The length of the data is stored in nread. Upon every read operation, increase nread by the number of bytes read. */ static int read_request(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread) { int request_len, n = 0; request_len = get_request_len(buf, *nread); while (conn->ctx->stop_flag == 0 && *nread < bufsiz && request_len == 0 && (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) { *nread += n; assert(*nread <= bufsiz); request_len = get_request_len(buf, *nread); } return request_len <= 0 && n <= 0 ? -1 : request_len; } /* For given directory path, substitute it to valid index file. Return 1 if index file has been found, 0 if not found. If the file is found, it's stats is returned in stp. */ static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct file *filep) { const char *list = conn->ctx->config[INDEX_FILES]; struct file file = STRUCT_FILE_INITIALIZER; struct vec filename_vec; size_t n = strlen(path); int found = 0; /* The 'path' given to us points to the directory. Remove all trailing directory separator characters from the end of the path, and then append single directory separator character. */ while (n > 0 && path[n - 1] == '/') { n--; } path[n] = '/'; /* Traverse index files list. For each entry, append it to the given path and see if the file exists. If it exists, break the loop */ while ((list = next_option(list, &filename_vec, NULL)) != NULL) { /* Ignore too long entries that may overflow path buffer */ if (filename_vec.len > path_len - (n + 2)) continue; /* Prepare full path to the index file */ mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); /* Does it exist? */ if (mg_stat(conn, path, &file)) { /* Yes it does, break the loop */ *filep = file; found = 1; break; } } /* If no index file exists, restore directory path */ if (!found) { path[n] = '\0'; } return found; } /* Return True if we should reply 304 Not Modified. */ static int is_not_modified(const struct mg_connection *conn, const struct file *filep) { char etag[64]; const char *ims = mg_get_header(conn, "If-Modified-Since"); const char *inm = mg_get_header(conn, "If-None-Match"); construct_etag(etag, sizeof(etag), filep); return (inm != NULL && !mg_strcasecmp(etag, inm)) || (ims != NULL && filep->modification_time <= parse_date_string(ims)); } static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl) { const char *expect, *body; char buf[MG_BUF_LEN]; int to_read, nread, buffered_len, success = 0; expect = mg_get_header(conn, "Expect"); assert(fp != NULL); if (conn->content_len == -1) { send_http_error(conn, 411, "Length Required", "%s", ""); } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) { send_http_error(conn, 417, "Expectation Failed", "%s", ""); } else { if (expect != NULL) { (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); } body = conn->buf + conn->request_len + conn->consumed_content; buffered_len = (int)(&conn->buf[conn->data_len] - body); assert(buffered_len >= 0); assert(conn->consumed_content == 0); if (buffered_len > 0) { if ((int64_t) buffered_len > conn->content_len) { buffered_len = (int) conn->content_len; } push(fp, sock, ssl, body, (int64_t) buffered_len); conn->consumed_content += buffered_len; } nread = 0; while (conn->consumed_content < conn->content_len) { to_read = sizeof(buf); if ((int64_t) to_read > conn->content_len - conn->consumed_content) { to_read = (int) (conn->content_len - conn->consumed_content); } nread = pull(NULL, conn, buf, to_read); if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) { break; } conn->consumed_content += nread; } if (conn->consumed_content == conn->content_len) { success = nread >= 0; } /* Each error code path in this function must send an error */ if (!success) { send_http_error(conn, 577, http_500_error, "%s", ""); } } return success; } #if !defined(NO_CGI) /* This structure helps to create an environment for the spawned CGI program. Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, last element must be NULL. However, on Windows there is a requirement that all these VARIABLE=VALUE\0 strings must reside in a contiguous buffer. The end of the buffer is marked by two '\0' characters. We satisfy both worlds: we create an envp array (which is vars), all entries are actually pointers inside buf. */ struct cgi_env_block { struct mg_connection *conn; char buf[CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ int len; /* Space taken */ char *vars[MAX_CGI_ENVIR_VARS]; /* char **envp */ int nvars; /* Number of variables */ }; static char *addenv(struct cgi_env_block *block, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); /* Append VARIABLE=VALUE\0 string to the buffer, and add a respective pointer into the vars array. */ static char *addenv(struct cgi_env_block *block, const char *fmt, ...) { int n, space; char *added; va_list ap; /* Calculate how much space is left in the buffer */ space = sizeof(block->buf) - block->len - 2; assert(space >= 0); /* Make a pointer to the free space int the buffer */ added = block->buf + block->len; /* Copy VARIABLE=VALUE\0 string into the free space */ va_start(ap, fmt); n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap); va_end(ap); /* Make sure we do not overflow buffer and the envp array */ if (n > 0 && n + 1 < space && block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { /* Append a pointer to the added string into the envp array */ block->vars[block->nvars++] = added; /* Bump up used length counter. Include \0 terminator */ block->len += n + 1; } else { mg_cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt); } return added; } static void prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_env_block *blk) { const char *s, *slash; struct vec var_vec; char *p, src_addr[IP_ADDR_STR_LEN]; int i; blk->len = blk->nvars = 0; blk->conn = conn; sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]); addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); addenv(blk, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version()); /* Prepare the environment block */ addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */ /* TODO(lsm): fix this for IPv6 case */ addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port)); addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method); addenv(blk, "REMOTE_ADDR=%s", src_addr); addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port); addenv(blk, "REQUEST_URI=%s", conn->request_info.uri); /* SCRIPT_NAME */ assert(conn->request_info.uri[0] == '/'); slash = strrchr(conn->request_info.uri, '/'); if ((s = strrchr(prog, '/')) == NULL) s = prog; addenv(blk, "SCRIPT_NAME=%.*s%s", (int) (slash - conn->request_info.uri), conn->request_info.uri, s); addenv(blk, "SCRIPT_FILENAME=%s", prog); addenv(blk, "PATH_TRANSLATED=%s", prog); addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on"); if ((s = mg_get_header(conn, "Content-Type")) != NULL) addenv(blk, "CONTENT_TYPE=%s", s); if (conn->request_info.query_string != NULL) addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string); if ((s = mg_get_header(conn, "Content-Length")) != NULL) addenv(blk, "CONTENT_LENGTH=%s", s); if ((s = getenv("PATH")) != NULL) addenv(blk, "PATH=%s", s); if (conn->path_info != NULL) { addenv(blk, "PATH_INFO=%s", conn->path_info); } if (conn->status_code > 0) { /* CGI error handler should show the status code */ addenv(blk, "STATUS=%d", conn->status_code); } #if defined(_WIN32) if ((s = getenv("COMSPEC")) != NULL) { addenv(blk, "COMSPEC=%s", s); } if ((s = getenv("SYSTEMROOT")) != NULL) { addenv(blk, "SYSTEMROOT=%s", s); } if ((s = getenv("SystemDrive")) != NULL) { addenv(blk, "SystemDrive=%s", s); } if ((s = getenv("ProgramFiles")) != NULL) { addenv(blk, "ProgramFiles=%s", s); } if ((s = getenv("ProgramFiles(x86)")) != NULL) { addenv(blk, "ProgramFiles(x86)=%s", s); } #else if ((s = getenv("LD_LIBRARY_PATH")) != NULL) addenv(blk, "LD_LIBRARY_PATH=%s", s); #endif /* _WIN32 */ if ((s = getenv("PERLLIB")) != NULL) addenv(blk, "PERLLIB=%s", s); if (conn->request_info.remote_user != NULL) { addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user); addenv(blk, "%s", "AUTH_TYPE=Digest"); } /* Add all headers as HTTP_* variables */ for (i = 0; i < conn->request_info.num_headers; i++) { p = addenv(blk, "HTTP_%s=%s", conn->request_info.http_headers[i].name, conn->request_info.http_headers[i].value); /* Convert variable name into uppercase, and change - to _ */ for (; *p != '=' && *p != '\0'; p++) { if (*p == '-') *p = '_'; *p = (char) toupper(* (unsigned char *) p); } } /* Add user-specified variables */ s = conn->ctx->config[CGI_ENVIRONMENT]; while ((s = next_option(s, &var_vec, NULL)) != NULL) { addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr); } blk->vars[blk->nvars++] = NULL; blk->buf[blk->len++] = '\0'; assert(blk->nvars < (int) ARRAY_SIZE(blk->vars)); assert(blk->len > 0); assert(blk->len < (int) sizeof(blk->buf)); } static void handle_cgi_request(struct mg_connection *conn, const char *prog) { char *buf; size_t buflen; int headers_len, data_len, i, fdin[2] = { 0, 0 }, fdout[2] = { 0, 0 }; const char *status, *status_text, *connection_state; char *pbuf, dir[PATH_MAX], *p; struct mg_request_info ri; struct cgi_env_block blk; FILE *in = NULL, *out = NULL; struct file fout = STRUCT_FILE_INITIALIZER; pid_t pid = (pid_t) -1; buf = NULL; buflen = 16384; prepare_cgi_environment(conn, prog, &blk); /* CGI must be executed in its own directory. 'dir' must point to the directory containing executable program, 'p' must point to the executable program name relative to 'dir'. */ (void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog); if ((p = strrchr(dir, '/')) != NULL) { *p++ = '\0'; } else { dir[0] = '.', dir[1] = '\0'; p = (char *) prog; } if (pipe(fdin) != 0 || pipe(fdout) != 0) { send_http_error(conn, 500, http_500_error, "Cannot create CGI pipe: %s", strerror(ERRNO)); goto done; } pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir); if (pid == (pid_t) -1) { send_http_error(conn, 500, http_500_error, "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO)); goto done; } /* Make sure child closes all pipe descriptors. It must dup them to 0,1 */ set_close_on_exec(fdin[0], conn); set_close_on_exec(fdin[1], conn); set_close_on_exec(fdout[0], conn); set_close_on_exec(fdout[1], conn); /* Parent closes only one side of the pipes. If we don't mark them as closed, close() attempt before return from this function throws an exception on Windows. Windows does not like when closed descriptor is closed again. */ (void) close(fdin[0]); (void) close(fdout[1]); fdin[0] = fdout[1] = -1; if ((in = fdopen(fdin[1], "wb")) == NULL || (out = fdopen(fdout[0], "rb")) == NULL) { send_http_error(conn, 500, http_500_error, "fopen: %s", strerror(ERRNO)); goto done; } setbuf(in, NULL); setbuf(out, NULL); fout.fp = out; /* Send POST data to the CGI process if needed */ if (!strcmp(conn->request_info.request_method, "POST") && !forward_body_data(conn, in, INVALID_SOCKET, NULL)) { goto done; } /* Close so child gets an EOF. */ fclose(in); in = NULL; fdin[1] = -1; /* Now read CGI reply into a buffer. We need to set correct status code, thus we need to see all HTTP headers first. Do not send anything back to client, until we buffer in all HTTP headers. */ data_len = 0; buf = mg_malloc(buflen); if (buf == NULL) { send_http_error(conn, 500, http_500_error, "Not enough memory for buffer (%u bytes)", (unsigned int) buflen); goto done; } headers_len = read_request(out, conn, buf, (int) buflen, &data_len); if (headers_len <= 0) { send_http_error(conn, 500, http_500_error, "CGI program sent malformed or too big (>%u bytes) " "HTTP headers: [%.*s]", (unsigned) buflen, data_len, buf); goto done; } pbuf = buf; buf[headers_len - 1] = '\0'; parse_http_headers(&pbuf, &ri); /* Make up and send the status line */ status_text = "OK"; if ((status = get_header(&ri, "Status")) != NULL) { conn->status_code = atoi(status); status_text = status; while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') { status_text++; } } else if (get_header(&ri, "Location") != NULL) { conn->status_code = 302; } else { conn->status_code = 200; } connection_state = get_header(&ri, "Connection"); if (connection_state == NULL || mg_strcasecmp(connection_state, "keep-alive")) { conn->must_close = 1; } (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text); /* Send headers */ for (i = 0; i < ri.num_headers; i++) { mg_printf(conn, "%s: %s\r\n", ri.http_headers[i].name, ri.http_headers[i].value); } mg_write(conn, "\r\n", 2); /* Send chunk of data that may have been read after the headers */ conn->num_bytes_sent += mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len)); /* Read the rest of CGI output and send to the client */ send_file_data(conn, &fout, 0, INT64_MAX); done: if (pid != (pid_t) -1) { kill(pid, SIGKILL); #if !defined(_WIN32) { int st; while (waitpid(pid, &st, 0) != -1); /* clean zombies */ } #endif } if (fdin[0] != -1) { close(fdin[0]); } if (fdout[1] != -1) { close(fdout[1]); } if (in != NULL) { fclose(in); } else if (fdin[1] != -1) { close(fdin[1]); } if (out != NULL) { fclose(out); } else if (fdout[0] != -1) { close(fdout[0]); } if (buf != NULL) { mg_free(buf); } } #endif /* !NO_CGI */ /* For a given PUT path, create all intermediate subdirectories for given path. Return 0 if the path itself is a directory, or -1 on error, 1 if OK. */ static int put_dir(struct mg_connection *conn, const char *path) { char buf[PATH_MAX]; const char *s, *p; struct file file = STRUCT_FILE_INITIALIZER; int len, res = 1; for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { len = (int)(p - path); if (len >= (int) sizeof(buf)) { res = -1; break; } memcpy(buf, path, len); buf[len] = '\0'; /* Try to create intermediate directory */ DEBUG_TRACE("mkdir(%s)", buf); if (!mg_stat(conn, buf, &file) && mg_mkdir(buf, 0755) != 0) { res = -1; break; } /* Is path itself a directory? */ if (p[1] == '\0') { res = 0; } } return res; } static void mkcol(struct mg_connection *conn, const char *path) { int rc, body_len; struct de de; char date[64]; time_t curtime = time(NULL); memset(&de.file, 0, sizeof(de.file)); if (!mg_stat(conn, path, &de.file)) { mg_cry(conn, "%s: mg_stat(%s) failed: %s", __func__, path, strerror(ERRNO)); } if(de.file.modification_time) { send_http_error(conn, 405, "Method Not Allowed", "mkcol(%s): %s", path, strerror(ERRNO)); return; } body_len = conn->data_len - conn->request_len; if(body_len > 0) { send_http_error(conn, 415, "Unsupported media type", "mkcol(%s): %s", path, strerror(ERRNO)); return; } rc = mg_mkdir(path, 0755); if (rc == 0) { conn->status_code = 201; gmt_time_string(date, sizeof(date), &curtime); mg_printf(conn, "HTTP/1.1 %d Created\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n", conn->status_code, date, suggest_connection_header(conn)); } else if (rc == -1) { if(errno == EEXIST) send_http_error(conn, 405, "Method Not Allowed", "mkcol(%s): %s", path, strerror(ERRNO)); else if(errno == EACCES) send_http_error(conn, 403, "Forbidden", "mkcol(%s): %s", path, strerror(ERRNO)); else if(errno == ENOENT) send_http_error(conn, 409, "Conflict", "mkcol(%s): %s", path, strerror(ERRNO)); else send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path, strerror(ERRNO)); } } static void put_file(struct mg_connection *conn, const char *path) { struct file file = STRUCT_FILE_INITIALIZER; const char *range; int64_t r1, r2; int rc; char date[64]; time_t curtime = time(NULL); conn->status_code = mg_stat(conn, path, &file) ? 200 : 201; if ((rc = put_dir(conn, path)) == 0) { gmt_time_string(date, sizeof(date), &curtime); mg_printf(conn, "HTTP/1.1 %d OK\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n", conn->status_code, date, suggest_connection_header(conn)); } else if (rc == -1) { send_http_error(conn, 500, http_500_error, "put_dir(%s): %s", path, strerror(ERRNO)); } else if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) { mg_fclose(&file); send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path, strerror(ERRNO)); } else { fclose_on_exec(&file, conn); range = mg_get_header(conn, "Content-Range"); r1 = r2 = 0; if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { conn->status_code = 206; fseeko(file.fp, r1, SEEK_SET); } if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { conn->status_code = 500; } gmt_time_string(date, sizeof(date), &curtime); mg_printf(conn, "HTTP/1.1 %d OK\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n", conn->status_code, date, suggest_connection_header(conn)); mg_fclose(&file); } } static void send_ssi_file(struct mg_connection *, const char *, struct file *, int); static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level) { char file_name[MG_BUF_LEN], path[512], *p; struct file file = STRUCT_FILE_INITIALIZER; size_t len; /* sscanf() is safe here, since send_ssi_file() also uses buffer of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. */ if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) { /* File name is relative to the webserver root */ file_name[511]=0; (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s", conn->ctx->config[DOCUMENT_ROOT], '/', file_name); } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) { /* File name is relative to the webserver working directory or it is absolute system path */ file_name[511]=0; (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name); } else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1 || sscanf(tag, " \"%511[^\"]\"", file_name) == 1) { /* File name is relative to the currect document */ file_name[511]=0; (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi); if ((p = strrchr(path, '/')) != NULL) { p[1] = '\0'; } len = strlen(path); (void) mg_snprintf(conn, path + len, sizeof(path) - len, "%s", file_name); } else { mg_cry(conn, "Bad SSI #include: [%s]", tag); return; } if (!mg_fopen(conn, path, "rb", &file)) { mg_cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", tag, path, strerror(ERRNO)); } else { fclose_on_exec(&file, conn); if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], (int)strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) { send_ssi_file(conn, path, &file, include_level + 1); } else { send_file_data(conn, &file, 0, INT64_MAX); } mg_fclose(&file); } } #if !defined(NO_POPEN) static void do_ssi_exec(struct mg_connection *conn, char *tag) { char cmd[1024] = ""; struct file file = STRUCT_FILE_INITIALIZER; if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) { mg_cry(conn, "Bad SSI #exec: [%s]", tag); } else { cmd[1023]=0; if ((file.fp = popen(cmd, "r")) == NULL) { mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); } else { send_file_data(conn, &file, 0, INT64_MAX); pclose(file.fp); } } } #endif /* !NO_POPEN */ static int mg_fgetc(struct file *filep, int offset) { if (filep->membuf != NULL && offset >=0 && offset < filep->size) { return ((unsigned char *) filep->membuf)[offset]; } else if (filep->fp != NULL) { return fgetc(filep->fp); } else { return EOF; } } static void send_ssi_file(struct mg_connection *conn, const char *path, struct file *filep, int include_level) { char buf[MG_BUF_LEN]; int ch, offset, len, in_ssi_tag; if (include_level > 10) { mg_cry(conn, "SSI #include level is too deep (%s)", path); return; } in_ssi_tag = len = offset = 0; while ((ch = mg_fgetc(filep, offset)) != EOF) { if (in_ssi_tag && ch == '>') { in_ssi_tag = 0; buf[len++] = (char) ch; buf[len] = '\0'; assert(len <= (int) sizeof(buf)); if (len < 6 || memcmp(buf, "