diff --git a/libdap-chain-net b/libdap-chain-net deleted file mode 160000 index baa8092d1c013060d6c79e35083ebd61d2eaaf32..0000000000000000000000000000000000000000 --- a/libdap-chain-net +++ /dev/null @@ -1 +0,0 @@ -Subproject commit baa8092d1c013060d6c79e35083ebd61d2eaaf32 diff --git a/libdap-chain-net/.gitignore b/libdap-chain-net/.gitignore new file mode 100755 index 0000000000000000000000000000000000000000..715b2fcabce319becb23b4f67ba7b9bb4beaf7d5 --- /dev/null +++ b/libdap-chain-net/.gitignore @@ -0,0 +1,55 @@ +# Prerequisites +*.d +*.autosave +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf +/build/ +/.project +/.cproject diff --git a/libdap-chain-net/CMakeLists.txt b/libdap-chain-net/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7579c48ad2b9e4a9c62cee6e880348bd585f1b03 --- /dev/null +++ b/libdap-chain-net/CMakeLists.txt @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 3.0) +project (dap_chain_net) + + +set(DAP_CHAIN_NET_SRCS + dap_chain_net.c + dap_chain_net_remote.c + dap_chain_net_bugreport.c + dap_chain_node.c + dap_chain_node_cli.c + dap_chain_node_cli_cmd.c + dap_chain_node_cli_cmd_tx.c + dap_chain_node_client.c + dap_chain_node_remote.c + dap_chain_node_ping.c + ) + +set(DAP_CHAIN_NET_HEADERS + dap_chain_net.h + dap_chain_net_remote.h + dap_chain_net_bugreport.h + dap_chain_node.h + dap_chain_node_cli.h + dap_chain_node_cli_cmd.h + dap_chain_node_cli_cmd_tx.h + dap_chain_node_client.h + dap_chain_node_remote.h + dap_chain_node_ping.h + ) + +#if (ANDROID) +# set(DAP_CHAIN_NET_HEADERS ${DAP_CHAIN_NET_HEADERS} +# android/ifaddrs-android.h +# ) +#endif() + +set(IPUTILS_INCLUDE_DIRS + iputils/traceroute/ + ) + +if(NOT (WIN32)) + file(GLOB IPUTILS_SRCS iputils/*.c iputils/traceroute/*.c) + file(GLOB IPUTILS_HEADERS iputils/*.h ${IPUTILS_INCLUDE_DIRS}*.h) +endif() + +if(WIN32) + add_definitions ("-DUNDEBUG") + add_definitions ("-DNDEBUG") + add_definitions ("-DWIN32") + add_definitions ("-D_WINDOWS") + add_definitions ("-D__WINDOWS__") + add_definitions ("-D_CRT_SECURE_NO_WARNINGS") + add_definitions ("-D_POSIX") + add_definitions ("-D_POSIX_") + add_definitions ("-D_POSIX_THREAD_SAFE_FUNCTIONS") + add_compile_definitions(_GNU_SOURCE) + include_directories(../3rdparty/wepoll/) + include_directories(../3rdparty/uthash/src/) +endif() + +add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_NET_SRCS} ${DAP_CHAIN_NET_HEADERS} ${IPUTILS_SRCS} ${IPUTILS_HEADERS}) + +if(WIN32) + target_link_libraries(dap_chain_net dap_core dap_crypto dap_client dap_stream_ch_chain dap_stream_ch_chain_net dap_chain dap_chain_crypto dap_chain_wallet dap_chain_net_srv dap_chain_mempool dap_chain_global_db ) +endif() + +if(UNIX) + target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_client dap_stream_ch_chain dap_stream_ch_chain_net dap_chain + dap_chain_crypto dap_chain_wallet dap_chain_net_srv dap_chain_net_srv_vpn dap_chain_mempool dap_chain_global_db + resolv + ) +endif() + +target_include_directories(${PROJECT_NAME} INTERFACE . ) + +if (!WIN32) + target_include_directories(${PROJECT_NAME} PUBLIC ${IPUTILS_INCLUDE_DIRS}) +endif() + + +set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE) + +set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR} CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) diff --git a/libdap-chain-net/LICENSE b/libdap-chain-net/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..94a9ed024d3859793618152ea559a168bbcbb5e2 --- /dev/null +++ b/libdap-chain-net/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/libdap-chain-net/README.md b/libdap-chain-net/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b3b20222fe48b3b606cedeb96ccd3bc5ca31a405 --- /dev/null +++ b/libdap-chain-net/README.md @@ -0,0 +1,2 @@ +# libdap-chain-net +Dap ChainNetwork functions diff --git a/libdap-chain-net/dap_chain_net.c b/libdap-chain-net/dap_chain_net.c new file mode 100644 index 0000000000000000000000000000000000000000..d97caf8915102a2c6e835410ef1704982326cd49 --- /dev/null +++ b/libdap-chain-net/dap_chain_net.c @@ -0,0 +1,2051 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2017-2018 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <fnmatch.h> + +#ifdef DAP_OS_UNIX +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#endif + +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#endif + +#include <pthread.h> + +#include "uthash.h" +#include "utlist.h" + +#include "dap_common.h" +#include "dap_string.h" +#include "dap_strfuncs.h" +#include "dap_file_utils.h" +#include "dap_config.h" +#include "dap_hash.h" +#include "dap_cert.h" +#include "dap_chain_common.h" +#include "dap_chain_net.h" +#include "dap_chain_node_client.h" +#include "dap_chain_node_cli.h" +#include "dap_chain_node_cli_cmd.h" +#include "dap_chain_ledger.h" + +#include "dap_chain_global_db.h" +#include "dap_chain_global_db_remote.h" + +#include "dap_stream_ch_chain_net_pkt.h" +#include "dap_stream_ch_chain_net.h" +#include "dap_stream_ch_chain.h" +#include "dap_stream_ch_chain_pkt.h" +#include "dap_stream_ch.h" +#include "dap_stream_ch_pkt.h" + +#include "dap_module.h" + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> + +#define _XOPEN_SOURCE /* See feature_test_macros(7) */ +#define __USE_XOPEN +#define _GNU_SOURCE +#include <time.h> + +#define LOG_TAG "chain_net" + +#define F_DAP_CHAIN_NET_SHUTDOWN ( 1 << 9 ) +#define F_DAP_CHAIN_NET_GO_SYNC ( 1 << 10 ) + +// maximum number of connections +static size_t s_max_links_count = 5;// by default 5 +// number of required connections +static size_t s_required_links_count = 3;// by default 3 + +/** + * @struct dap_chain_net_pvt + * @details Private part of chain_net dap object + */ +typedef struct dap_chain_net_pvt{ + pthread_t proc_tid; +#ifndef _WIN32 + pthread_cond_t state_proc_cond; +#else + HANDLE state_proc_cond; +#endif + dap_chain_node_role_t node_role; + uint32_t flags; + time_t last_sync; + + dap_chain_node_addr_t * node_addr; + dap_chain_node_info_t * node_info; // Current node's info + + dap_chain_node_client_t * links; + size_t links_count; + + dap_chain_node_addr_t *links_addrs; + size_t links_addrs_count; + + size_t addr_request_attempts; + bool load_mode; + uint8_t padding2[7]; + char ** seed_aliases; + + uint16_t gdb_sync_groups_count; + uint16_t gdb_sync_nodes_addrs_count; + char **gdb_sync_groups; + dap_chain_node_addr_t *gdb_sync_nodes_addrs; + + uint8_t padding3[6]; + uint16_t seed_aliases_count; + + dap_chain_net_state_t state; + dap_chain_net_state_t state_target; + dap_chain_net_state_t state_new; +} dap_chain_net_pvt_t; + +typedef struct dap_chain_net_item{ + char name [DAP_CHAIN_NET_NAME_MAX]; + dap_chain_net_id_t net_id; + dap_chain_net_t * chain_net; + UT_hash_handle hh; +} dap_chain_net_item_t; + +#define PVT(a) ( (dap_chain_net_pvt_t *) (void*) a->pvt ) +#define PVT_S(a) ( (dap_chain_net_pvt_t *) (void*) a.pvt ) + +static dap_chain_net_item_t * s_net_items = NULL; +static dap_chain_net_item_t * s_net_items_ids = NULL; + + +static const char * c_net_states[]={ + [NET_STATE_OFFLINE] = "NET_STATE_OFFLINE", + [NET_STATE_LINKS_PREPARE ] = "NET_STATE_LINKS_PREPARE", + [NET_STATE_LINKS_CONNECTING] = "NET_STATE_LINKS_CONNECTING", + [NET_STATE_LINKS_ESTABLISHED]= "NET_STATE_LINKS_ESTABLISHED", + [NET_STATE_SYNC_GDB]= "NET_STATE_SYNC_GDB", + [NET_STATE_SYNC_CHAINS]= "NET_STATE_SYNC_CHAINS", + [NET_STATE_ADDR_REQUEST]= "NET_STATE_ADDR_REQUEST", + [NET_STATE_ONLINE]= "NET_STATE_ONLINE" +}; + +static dap_chain_net_t * s_net_new(const char * a_id, const char * a_name , const char * a_node_role); +inline static const char * s_net_state_to_str(dap_chain_net_state_t l_state); +static int s_net_states_proc(dap_chain_net_t * l_net); +static void * s_net_proc_thread ( void * a_net); +static void s_net_proc_thread_start( dap_chain_net_t * a_net ); +static void s_net_proc_kill( dap_chain_net_t * a_net ); +int s_net_load(const char * a_net_name); + +static void s_gbd_history_callback_notify (void * a_arg,const char a_op_code, const char * a_prefix, const char * a_group, + const char * a_key, const void * a_value, + const size_t a_value_len); +static void s_chain_callback_notify(void * a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id); + +static int s_cli_net(int argc, char ** argv, void *arg_func, char **str_reply); + +static bool s_seed_mode = false; + + +/** + * @brief s_net_set_go_sync + * @param a_net + * @return + */ +void s_net_set_go_sync(dap_chain_net_t * a_net) +{ + if(!a_net) + return; + dap_chain_net_start(a_net); +} + +/** + * @brief s_net_state_to_str + * @param l_state + * @return + */ +inline static const char * s_net_state_to_str(dap_chain_net_state_t l_state) +{ + return c_net_states[l_state]; +} + +/** + * @brief dap_chain_net_state_go_to + * @param a_net + * @param a_new_state + */ +int dap_chain_net_state_go_to(dap_chain_net_t * a_net, dap_chain_net_state_t a_new_state) +{ + if (PVT(a_net)->state_new == a_new_state){ + log_it(L_WARNING,"Already going to state %s",s_net_state_to_str(a_new_state)); + } + PVT(a_net)->state_new = a_new_state; + return 0; +} + +/** + * @brief s_gbd_history_callback_notify + * @param a_arg + * @param a_op_code + * @param a_prefix + * @param a_group + * @param a_key + * @param a_value + * @param a_value_len + */ +static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, const char * a_prefix, const char * a_group, + const char * a_key, const void * a_value, + const size_t a_value_len) +{ + (void) a_op_code; + + if (a_arg) { + dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg; + s_net_set_go_sync(l_net); + /*if (!PVT (l_net)->load_mode ){ + if( pthread_mutex_trylock( &PVT (l_net)->state_mutex) == 0 ){ + if ( PVT(l_net)->state == NET_STATE_ONLINE || PVT(l_net)->state == NET_STATE_ONLINE ) + dap_chain_net_sync_all(l_net); + pthread_mutex_unlock( &PVT (l_net)->state_mutex); + } + }*/ + } +} + +/** + * @brief s_chain_callback_notify + * @param a_arg + * @param a_chain + * @param a_id + */ +static void s_chain_callback_notify(void * a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id) +{ + if(!a_arg) + return; + dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg; + s_net_set_go_sync(l_net); +} + + +/** + * @brief s_net_states_proc + * @param l_net + */ +static int s_net_states_proc(dap_chain_net_t * l_net) +{ +#if DAP_DEBUG + dap_chain_net_pvt_t *pvt_debug = PVT(l_net); +#endif + int ret=0; + + switch ( PVT(l_net)->state ){ + case NET_STATE_OFFLINE:{ + PVT(l_net)->state_target = PVT(l_net)->state_new; + // reset current link + PVT(l_net)->links_count = 0; + // delete all links + dap_chain_node_client_close(PVT(l_net)->links); + PVT(l_net)->links_addrs_count = 0; + if ( PVT(l_net)->links_addrs ) + DAP_DELETE(PVT(l_net)->links_addrs); + PVT(l_net)->links_addrs = NULL; + + if ( PVT(l_net)->state_target != NET_STATE_OFFLINE ){ + PVT(l_net)->state = NET_STATE_LINKS_PREPARE; + break; + } + // disable SYNC_GDB + PVT(l_net)->flags &= ~F_DAP_CHAIN_NET_GO_SYNC; + PVT(l_net)->last_sync = 0; + } break; + case NET_STATE_LINKS_PREPARE:{ + log_it(L_NOTICE,"%s.state: NET_STATE_LINKS_PREPARE",l_net->pub.name); + switch (PVT(l_net)->node_role.enums) { + case NODE_ROLE_ROOT: + case NODE_ROLE_ROOT_MASTER: + case NODE_ROLE_ARCHIVE: + case NODE_ROLE_CELL_MASTER:{ + // This roles load predefined links from global_db + if ( PVT(l_net)->node_info ) { + if (PVT(l_net)->links_addrs ) + DAP_DELETE(PVT(l_net)->links_addrs); + PVT(l_net)->links_addrs_count = PVT(l_net)->node_info->hdr.links_number; + PVT(l_net)->links_addrs = DAP_NEW_Z_SIZE( dap_chain_node_addr_t, + PVT(l_net)->links_addrs_count * sizeof(dap_chain_node_addr_t)); + for (size_t i =0 ; i < PVT(l_net)->node_info->hdr.links_number; i++ ){ + PVT(l_net)->links_addrs[i].uint64 = PVT(l_net)->node_info->links[i].uint64; + } + }else { + log_it(L_WARNING,"No nodeinfo in global_db to prepare links for connecting, find nearest 3 links and fill global_db"); + } + + // add other root nodes for connect + //if(!PVT(l_net)->links_addrs_count) + { + // use no more then 4 root node + int l_use_root_nodes = min(4, PVT(l_net)->seed_aliases_count); + if(!PVT(l_net)->links_addrs_count) { + PVT(l_net)->links_addrs = DAP_NEW_Z_SIZE(dap_chain_node_addr_t, + l_use_root_nodes * sizeof(dap_chain_node_addr_t)); + } + else{ + PVT(l_net)->links_addrs = DAP_REALLOC(PVT(l_net)->links_addrs, + (PVT(l_net)->links_addrs_count+l_use_root_nodes) * sizeof(dap_chain_node_addr_t)); + memset(PVT(l_net)->links_addrs + PVT(l_net)->links_addrs_count, 0, + l_use_root_nodes * sizeof(dap_chain_node_addr_t)); + } + + for(uint16_t i = 0; i < l_use_root_nodes; i++) { + dap_chain_node_addr_t * l_node_addr = dap_chain_node_alias_find(l_net, PVT(l_net)->seed_aliases[i]); + if(l_node_addr) { + PVT(l_net)->links_addrs[PVT(l_net)->links_addrs_count].uint64 = l_node_addr->uint64; + PVT(l_net)->links_addrs_count++; + } + } + } + // shuffle the order of the nodes + for(size_t i = 0; i < PVT(l_net)->links_addrs_count; i++) { + unsigned int l_new_node_pos = rand() % (PVT(l_net)->links_addrs_count); + if(i == l_new_node_pos) + continue; + uint64_t l_tmp_uint64 = PVT(l_net)->links_addrs[i].uint64; + PVT(l_net)->links_addrs[i].uint64 = PVT(l_net)->links_addrs[l_new_node_pos].uint64; + PVT(l_net)->links_addrs[l_new_node_pos].uint64 = l_tmp_uint64; + } + + + } break; + case NODE_ROLE_FULL: + case NODE_ROLE_MASTER: + case NODE_ROLE_LIGHT:{ + // If we haven't any assigned shard - connect to root-0 + if(1) { //if(l_net->pub.cell_id.uint64 == 0) { + + //dap_chain_net_pvt_t *pvt_debug = PVT(l_net); + // get current node address + dap_chain_node_addr_t l_address; + l_address.uint64 = dap_chain_net_get_cur_addr(l_net) ? + dap_chain_net_get_cur_addr(l_net)->uint64 : + dap_db_get_cur_node_addr(l_net->pub.name); + + // get current node info + dap_chain_node_info_t *l_cur_node_info = dap_chain_node_info_read(l_net, &l_address); + + if ( l_cur_node_info ) { + uint16_t l_links_addrs_count = l_cur_node_info->hdr.links_number + PVT(l_net)->seed_aliases_count; + PVT(l_net)->links_addrs = DAP_NEW_Z_SIZE(dap_chain_node_addr_t, + l_links_addrs_count * sizeof(dap_chain_node_addr_t)); + + // add linked nodes for connect + for(uint16_t i = 0; i < min(4, l_cur_node_info->hdr.links_number); i++) { + dap_chain_node_addr_t *l_addr = l_cur_node_info->links + i; + //dap_chain_node_addr_t link_addr = l_cur_node_info->links[i]; + dap_chain_node_info_t *l_remore_node_info = dap_chain_node_info_read(l_net, l_addr); + if(l_remore_node_info) { + // if only nodes from the same cell of cell=0 + if(!l_cur_node_info->hdr.cell_id.uint64 || + l_cur_node_info->hdr.cell_id.uint64 == l_remore_node_info->hdr.cell_id.uint64) { + PVT(l_net)->links_addrs[PVT(l_net)->links_addrs_count].uint64 = + l_remore_node_info->hdr.address.uint64; + PVT(l_net)->links_addrs_count++; + } + DAP_DELETE(l_remore_node_info); + } + } + } + // if no links then add root nodes for connect + if(!PVT(l_net)->links_addrs_count){ + // use no more then 4 root node + int l_use_root_nodes = min(4, PVT(l_net)->seed_aliases_count); + PVT(l_net)->links_addrs = DAP_NEW_Z_SIZE(dap_chain_node_addr_t, + l_use_root_nodes * sizeof(dap_chain_node_addr_t)); + + for(uint16_t i = 0; i < l_use_root_nodes; i++) { + dap_chain_node_addr_t * l_node_addr = dap_chain_node_alias_find(l_net, PVT(l_net)->seed_aliases[i]); + if(l_node_addr) { + PVT(l_net)->links_addrs[PVT(l_net)->links_addrs_count].uint64 = l_node_addr->uint64; + PVT(l_net)->links_addrs_count++; + } + } + } + // shuffle the order of the nodes + for(size_t i = 0; i < PVT(l_net)->links_addrs_count; i++) { + unsigned int l_new_node_pos = rand() % (PVT(l_net)->links_addrs_count); + if(i==l_new_node_pos) + continue; + uint64_t l_tmp_uint64 = PVT(l_net)->links_addrs[i].uint64; + PVT(l_net)->links_addrs[i].uint64 = PVT(l_net)->links_addrs[l_new_node_pos].uint64; + PVT(l_net)->links_addrs[l_new_node_pos].uint64 = l_tmp_uint64; + } + DAP_DELETE(l_cur_node_info); + }else { + // TODO read cell's nodelist and populate array with it + } + } break; + } + if ( PVT(l_net)->state_target > NET_STATE_LINKS_PREPARE ){ + if ( PVT(l_net)->links_addrs_count>0 ) { // If links are present + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + log_it(L_DEBUG,"Prepared %u links, start to establish them", PVT(l_net)->links_addrs_count ); + } else { + log_it(L_WARNING,"No links for connecting, return back to OFFLINE state"); + PVT(l_net)->state = NET_STATE_OFFLINE; + // remove looping + PVT(l_net)->state_target = NET_STATE_OFFLINE; + } + }else { + log_it(L_WARNING,"Target state is NET_STATE_LINKS_PREPARE? Realy?"); + PVT(l_net)->state = NET_STATE_OFFLINE; + } + } + break; + + case NET_STATE_LINKS_CONNECTING:{ + size_t l_links_count = PVT(l_net)->links_count; + if(l_links_count >= s_required_links_count || (l_links_count + 1) >= s_max_links_count) { + // TODO what if other failed and we want more? + } + if (l_links_count < PVT(l_net)->links_addrs_count) { + PVT(l_net)->links_count++; + } else { + log_it(L_NOTICE, "Can't establish enough links, go to offline"); + PVT(l_net)->state = NET_STATE_OFFLINE; + PVT(l_net)->state_target = NET_STATE_OFFLINE; + break; + } + log_it(L_DEBUG, "%s.state: NET_STATE_LINKS_CONNECTING",l_net->pub.name); + log_it(L_DEBUG, "Establishing connection with " NODE_ADDR_FP_STR, + NODE_ADDR_FP_ARGS_S( PVT(l_net)->links_addrs[l_links_count]) ); + dap_chain_node_info_t *l_link_node_info = dap_chain_node_info_read(l_net, &PVT(l_net)->links_addrs[l_links_count]); + if ( l_link_node_info ) { + dap_chain_node_client_t *l_node_client = dap_chain_node_client_connect(l_link_node_info); + if(!l_node_client) { + DAP_DELETE(l_link_node_info); + ret = -1; + break; + } + // wait connected + int timeout_ms = 5000; //5 sec = 5000 ms + int res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_CONNECTED, timeout_ms); + if (res == 0 ){ + log_it(L_DEBUG, "Connected link %u", l_links_count); + PVT(l_net)->links = l_node_client; + PVT(l_net)->state = NET_STATE_LINKS_ESTABLISHED; + }else { + log_it(L_DEBUG, "Cant establish link %u", l_links_count); + dap_chain_node_client_close(l_node_client); + } + } + } + break; + + case NET_STATE_LINKS_ESTABLISHED:{ + log_it(L_DEBUG,"%s.state: NET_STATE_LINKS_ESTABLISHED",l_net->pub.name); + switch (PVT(l_net)->state_target) { + case NET_STATE_ONLINE:{ // Online + switch ( PVT(l_net)->node_role.enums ){ + case NODE_ROLE_ROOT_MASTER: + case NODE_ROLE_ROOT:{ + /*dap_chain_node_client_t * l_node_client = NULL, *l_node_client_tmp = NULL; + + // Send everybody your address when linked + HASH_ITER(hh,PVT(l_net)->links,l_node_client,l_node_client_tmp){ + dap_stream_ch_chain_net_pkt_write(dap_client_get_stream_ch( + l_node_client->client, dap_stream_ch_chain_net_get_id()), + DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR, l_net->pub.id, + dap_chain_net_get_cur_addr(l_net), + sizeof (dap_chain_node_addr_t) ); + }*/ + PVT(l_net)->state = NET_STATE_SYNC_GDB; + }break; + case NODE_ROLE_CELL_MASTER: + case NODE_ROLE_MASTER:{ + PVT(l_net)->state = NET_STATE_SYNC_GDB;//NET_STATE_ADDR_REQUEST; + } break; + default:{ + // get addr for current node if it absent + if(!dap_chain_net_get_cur_addr_int(l_net)) + PVT(l_net)->state = NET_STATE_ADDR_REQUEST; + else + PVT( l_net)->state = NET_STATE_SYNC_GDB; + } + } + } + break; + + case NET_STATE_SYNC_GDB: // we need only to sync gdb + PVT(l_net)->state = NET_STATE_SYNC_GDB ; + if ( PVT(l_net)->addr_request_attempts >=10 && PVT(l_net)->state == NET_STATE_ADDR_REQUEST){ + PVT(l_net)->addr_request_attempts = 0; + switch( PVT(l_net)->state_target){ + case NET_STATE_ONLINE: + case NET_STATE_SYNC_GDB: + PVT(l_net)->state = NET_STATE_SYNC_GDB; + break; + + case NET_STATE_SYNC_CHAINS: + PVT(l_net)->state = NET_STATE_SYNC_CHAINS; + break; + default: { + PVT(l_net)->state = NET_STATE_OFFLINE; + PVT(l_net)->state_target = NET_STATE_OFFLINE; + } + } + } + break; + + case NET_STATE_SYNC_CHAINS: + PVT(l_net)->state = (PVT(l_net)->node_info && PVT(l_net)->node_info->hdr.address.uint64)? + NET_STATE_SYNC_CHAINS : NET_STATE_ADDR_REQUEST; + break; + + case NET_STATE_ADDR_REQUEST : + PVT(l_net)->state = NET_STATE_ADDR_REQUEST; + break; + default: break; + } + } break; + // get addr for remote node + case NET_STATE_ADDR_REQUEST: { + int l_is_addr_leased = 0; + dap_chain_node_client_t *l_node_client = PVT(l_net)->links; + uint8_t l_ch_id = dap_stream_ch_chain_net_get_id(); // Channel id for chain net request + dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch(l_node_client->client, l_ch_id); + // set callback for l_ch_id + dap_chain_node_client_set_callbacks(l_node_client->client, l_ch_id); + // send request for new address + size_t res = dap_stream_ch_chain_net_pkt_write(l_ch_chain, + DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR_LEASE_REQUEST, + //DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR_REQUEST, + l_net->pub.id, NULL, 0); + if (res == 0) { + log_it(L_WARNING,"Can't send NODE_ADDR_REQUEST packet"); + dap_chain_node_client_close(l_node_client); + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + break; // try with another link + } + // wait for finishing of request + int timeout_ms = 5000; // 5 sec = 5 000 ms + // TODO add progress info to console + PVT(l_net)->addr_request_attempts++; + int l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_NODE_ADDR_LEASED, timeout_ms); + switch (l_res) { + case -1: + log_it(L_WARNING,"Timeout with addr leasing"); + // try again 3 times + if (PVT(l_net)->addr_request_attempts < 3) { + break; + } + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + break; // try with another link + case 0: + log_it(L_INFO, "Node address leased"); + l_is_addr_leased++; + PVT(l_net)->addr_request_attempts = 0; + break; + default: + if (l_node_client->last_error[0]) { + log_it(L_INFO, "Node address request error %d: \"%s\"", l_res, l_node_client->last_error); + l_node_client->last_error[0] = '\0'; + } + log_it(L_INFO, "Node address request error %d", l_res); + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + break; + } + if (l_is_addr_leased > 0) { + PVT(l_net)->state = NET_STATE_SYNC_GDB; + } + } + break; + case NET_STATE_SYNC_GDB:{ + // send request + dap_chain_node_client_t *l_node_client = PVT(l_net)->links; + dap_stream_ch_chain_sync_request_t l_sync_gdb = {}; + // Get last timestamp in log + l_sync_gdb.id_start = (uint64_t) dap_db_log_get_last_id_remote(l_node_client->remote_node_addr.uint64); + // no limit + l_sync_gdb.id_end = (uint64_t)0; + + l_sync_gdb.node_addr.uint64 = dap_chain_net_get_cur_addr(l_net) ? + dap_chain_net_get_cur_addr(l_net)->uint64 : + dap_db_get_cur_node_addr(l_net->pub.name); + + dap_chain_id_t l_chain_id_null = {}; + dap_chain_cell_id_t l_chain_cell_id_null = {}; + l_chain_id_null.uint64 = l_net->pub.id.uint64; + l_chain_cell_id_null.uint64 = dap_chain_net_get_cur_cell(l_net) ? dap_chain_net_get_cur_cell(l_net)->uint64 : 0; + + log_it(L_DEBUG, "Prepared request to gdb sync from %llu to %llu", l_sync_gdb.id_start, l_sync_gdb.id_end); + // find dap_chain_id_t + dap_chain_t *l_chain = dap_chain_net_get_chain_by_name(l_net, "gdb"); + dap_chain_id_t l_chain_id = l_chain ? l_chain->id : (dap_chain_id_t ) {}; + + size_t l_res = dap_stream_ch_chain_pkt_write(dap_client_get_stream_ch(l_node_client->client, dap_stream_ch_chain_get_id()), + DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_GLOBAL_DB, l_net->pub.id, l_chain_id, + l_net->pub.cell_id, &l_sync_gdb, sizeof(l_sync_gdb)); + if (l_res == 0) { + log_it(L_WARNING, "Can't send GDB sync request"); + dap_chain_node_client_close(l_node_client); + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + break; //try another link + } + + // wait for finishing of request + int timeout_ms = 300000; // 5 min = 300 sec = 300 000 ms + // TODO add progress info to console + l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); + switch (l_res) { + case -1: + log_it(L_WARNING,"Timeout with link sync"); + break; + case 0: + log_it(L_INFO, "Node sync completed"); + break; + default: + log_it(L_INFO, "Node sync error %d",l_res); + } + if (l_res) { // try another link + break; + } + if(PVT(l_net)->state_target >= NET_STATE_SYNC_CHAINS){ + PVT(l_net)->state = NET_STATE_SYNC_CHAINS; + } else { + PVT(l_net)->state = NET_STATE_ONLINE; + } + } + break; + + case NET_STATE_SYNC_CHAINS: { + dap_chain_node_client_t *l_node_client = PVT(l_net)->links; + uint8_t l_ch_id = dap_stream_ch_chain_get_id(); // Channel id for global_db and chains sync + dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch(l_node_client->client, l_ch_id); + if(!l_ch_chain) { + log_it(L_DEBUG,"Can't get stream_ch for id='%c' ", l_ch_id); + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + break; + } + dap_chain_t * l_chain = NULL; + int l_sync_errors = 0; + DL_FOREACH(l_net->pub.chains, l_chain ){ + //size_t l_lasts_size = 0; + //dap_chain_atom_ptr_t * l_lasts; + //dap_chain_atom_iter_t * l_atom_iter = l_chain->callback_atom_iter_create(l_chain); + //l_lasts = l_chain->callback_atom_iter_get_lasts(l_atom_iter, &l_lasts_size); + //if( l_lasts ) { + l_node_client->state = NODE_CLIENT_STATE_CONNECTED; + dap_stream_ch_chain_sync_request_t l_request = { { 0 } }; + //dap_hash_fast(l_lasts[0], l_chain->callback_atom_get_size(l_lasts[0]), &l_request.hash_from); + dap_stream_ch_chain_pkt_write(l_ch_chain, + DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_CHAINS, l_net->pub.id, l_chain->id, + l_net->pub.cell_id, &l_request, sizeof(l_request)); + // + // dap_chain_node_client_send_ch_pkt(l_node_client,l_ch_id, + // DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_CHAINS, + // &l_request,sizeof (l_request) ); + + // wait for finishing of request + int timeout_ms = 120000; // 2 min = 120 sec = 120 000 ms + // TODO add progress info to console + int l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); + switch (l_res) { + case -1: + log_it(L_WARNING,"Timeout with sync of chain '%s' ", l_chain->name); + break; + case 0: + // flush global_db + dap_chain_global_db_flush(); + log_it(L_INFO, "sync of chain '%s' completed ", l_chain->name); + // set time of last sync + { + struct timespec l_to; + clock_gettime( CLOCK_MONOTONIC, &l_to); + PVT(l_net)->last_sync = l_to.tv_sec; + } + break; + default: + log_it(L_INFO, "sync of chain '%s' error %d", l_chain->name,l_res); + } + if (l_res) { + l_sync_errors++; + } + //DAP_DELETE( l_lasts ); + //} + //DAP_DELETE( l_atom_iter ); + } + dap_chain_node_client_close(l_node_client); + if (l_sync_errors) { + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + break; + } + log_it(L_INFO, "Synchronization done"); + PVT(l_net)->state = NET_STATE_ONLINE; + PVT(l_net)->links_count = 0; + } + break; + + case NET_STATE_ONLINE: { + PVT(l_net)->state_target = PVT(l_net)->state_new; + switch ( PVT(l_net)->state_target) { + // disconnect + case NET_STATE_OFFLINE: + PVT(l_net)->state = NET_STATE_OFFLINE; + log_it(L_NOTICE, "Going to disconnet"); + break; + case NET_STATE_ONLINE: + if((PVT(l_net)->flags & F_DAP_CHAIN_NET_GO_SYNC) == 0) + break; + case NET_STATE_SYNC_GDB: + case NET_STATE_SYNC_CHAINS: + // if flag set then go to SYNC_GDB + PVT(l_net)->state = NET_STATE_LINKS_CONNECTING; + break; + default: break; + } + } + break; + default: log_it (L_DEBUG, "Unprocessed state"); + } + return ret; +} + +// Global +/*static void s_net_proc_thread_callback_update_db(void) +{ + dap_chain_net_item_t *l_net_item, *l_net_item_tmp; + printf("update0\n"); + HASH_ITER(hh, s_net_items, l_net_item, l_net_item_tmp) + { + s_net_set_go_sync(l_net_item->chain_net); + } + printf("update1\n"); +}*/ + +/** + * @brief s_net_proc_thread + * @details Brings up and check the Dap Chain Network + * @param a_cfg Network1 configuration + * @return + */ +static void *s_net_proc_thread ( void *a_net ) +{ + dap_chain_net_t *l_net = (dap_chain_net_t *)a_net; + dap_chain_net_pvt_t *p_net = (dap_chain_net_pvt_t *)(void *)l_net->pvt; + + // set callback to update data + //dap_chain_global_db_set_callback_for_update_base(s_net_proc_thread_callback_update_db); + + while( !(p_net->flags & F_DAP_CHAIN_NET_SHUTDOWN) ) { + // check or start sync + s_net_states_proc( l_net ); + // checking whether new sync is needed + struct timespec l_to; + time_t l_sync_timeout = 300; // 300 sec = 5 min + clock_gettime(CLOCK_MONOTONIC, &l_to); + // start sync every l_sync_timeout sec + if(p_net->last_sync && l_to.tv_sec >= p_net->last_sync + l_sync_timeout) { + p_net->flags |= F_DAP_CHAIN_NET_GO_SYNC; + } + } + + return NULL; +} + +/** + * @brief net_proc_start + * @param a_cfg + */ +static void s_net_proc_thread_start( dap_chain_net_t * a_net ) +{ + if ( pthread_create(& PVT(a_net)->proc_tid ,NULL, s_net_proc_thread, a_net) == 0 ){ + log_it (L_NOTICE,"Network processing thread started"); + } +} + +/** + * @brief s_net_proc_kill + * @param a_net + */ +static void s_net_proc_kill( dap_chain_net_t * a_net ) +{ + if ( !PVT(a_net)->proc_tid ) + return; + + log_it( L_NOTICE,"Sent KILL signal to the net process thread %d, waiting for shutdown...", PVT(a_net)->proc_tid ); + + PVT(a_net)->flags |= F_DAP_CHAIN_NET_SHUTDOWN; + +#ifndef _WIN32 + pthread_cond_signal( &PVT(a_net)->state_proc_cond ); +#else + SetEvent( PVT(a_net)->state_proc_cond ); +#endif + + pthread_join( PVT(a_net)->proc_tid , NULL ); + log_it( L_NOTICE,"Net process thread %d shutted down", PVT(a_net)->proc_tid ); + + PVT(a_net)->proc_tid = 0; + + return; +} + +dap_chain_node_role_t dap_chain_net_get_role(dap_chain_net_t * a_net) +{ + return PVT(a_net)->node_role; +} + +/** + * @brief dap_chain_net_new + * @param a_id + * @param a_name + * @param a_node_role + * @param a_node_name + * @return + */ +static dap_chain_net_t *s_net_new(const char * a_id, const char * a_name , + const char * a_node_role) +{ + dap_chain_net_t *ret = DAP_NEW_Z_SIZE( dap_chain_net_t, sizeof(ret->pub) + sizeof(dap_chain_net_pvt_t) ); + ret->pub.name = strdup( a_name ); + +#ifndef _WIN32 + pthread_condattr_t l_attr; + pthread_condattr_init( &l_attr ); + pthread_condattr_setclock( &l_attr, CLOCK_MONOTONIC ); + pthread_cond_init( &PVT(ret)->state_proc_cond, &l_attr ); +#else + PVT(ret)->state_proc_cond = CreateEventA( NULL, FALSE, FALSE, NULL ); +#endif + + // if ( sscanf(a_id,"0x%016lx", &ret->pub.id.uint64 ) == 1 ){ + if ( sscanf(a_id,"0x%016llx", &ret->pub.id.uint64 ) == 1 ){ + if (strcmp (a_node_role, "root_master")==0){ + PVT(ret)->node_role.enums = NODE_ROLE_ROOT_MASTER; + log_it (L_NOTICE, "Node role \"root master\" selected"); + } else if (strcmp( a_node_role,"root") == 0){ + PVT(ret)->node_role.enums = NODE_ROLE_ROOT; + log_it (L_NOTICE, "Node role \"root\" selected"); + + } else if (strcmp( a_node_role,"archive") == 0){ + PVT(ret)->node_role.enums = NODE_ROLE_ARCHIVE; + log_it (L_NOTICE, "Node role \"archive\" selected"); + + } else if (strcmp( a_node_role,"cell_master") == 0){ + PVT(ret)->node_role.enums = NODE_ROLE_CELL_MASTER; + log_it (L_NOTICE, "Node role \"cell master\" selected"); + + }else if (strcmp( a_node_role,"master") == 0){ + PVT(ret)->node_role.enums = NODE_ROLE_MASTER; + log_it (L_NOTICE, "Node role \"master\" selected"); + + }else if (strcmp( a_node_role,"full") == 0){ + PVT(ret)->node_role.enums = NODE_ROLE_FULL; + log_it (L_NOTICE, "Node role \"full\" selected"); + + }else if (strcmp( a_node_role,"light") == 0){ + PVT(ret)->node_role.enums = NODE_ROLE_LIGHT; + log_it (L_NOTICE, "Node role \"light\" selected"); + + }else{ + log_it(L_ERROR,"Unknown node role \"%s\"",a_node_role); + DAP_DELETE(ret); + return NULL; + } + } else { + log_it (L_ERROR, "Wrong id format (\"%s\"). Must be like \"0x0123456789ABCDE\"" , a_id ); + DAP_DELETE(ret); + return NULL; + } + return ret; + +} + +/** + * @brief dap_chain_net_delete + * @param a_net + */ +void dap_chain_net_delete( dap_chain_net_t * a_net ) +{ + if(PVT(a_net)->seed_aliases) { + DAP_DELETE(PVT(a_net)->seed_aliases); + PVT(a_net)->seed_aliases = NULL; + } + DAP_DELETE( PVT(a_net) ); +} + + +/** + * @brief dap_chain_net_init + * @return + */ +int dap_chain_net_init() +{ + dap_chain_node_cli_cmd_item_create ("net", s_cli_net, NULL, "Network commands", + "net -net <chain net name> go < online | offline >\n" + "\tFind and establish links and stay online\n" + "net -net <chain net name> get status\n" + "\tLook at current status\n" + "net -net <chain net name> stats tx [-from <From time>] [-to <To time>] [-prev_sec <Seconds>] \n" + "\tTransactions statistics. Time format is <Year>-<Month>-<Day>_<Hours>:<Minutes>:<Seconds> or just <Seconds> \n" + "net -net <chain net name> sync < all | gdb | chains >\n" + "\tSyncronyze gdb, chains or everything\n\n" + "net -net <chain net name> link < list | add | del | info | establish >\n" + "\tList,add,del, dump or establish links\n\n" + ); + s_seed_mode = dap_config_get_item_bool_default(g_config,"general","seed_mode",false); + dap_chain_global_db_add_history_group_prefix("global", GROUP_LOCAL_HISTORY); + + dap_chain_global_db_add_history_callback_notify("global", s_gbd_history_callback_notify, NULL ); + + // maximum number of connections to other nodes + s_max_links_count = dap_config_get_item_int32_default(g_config, "general", "max_links", s_max_links_count); + // required number of connections to other nodes + s_required_links_count = dap_config_get_item_int32_default(g_config, "general", "require_links", s_required_links_count); + + dap_chain_net_load_all(); + return 0; +} + +void dap_chain_net_load_all() +{ + char * l_net_dir_str = dap_strdup_printf("%s/network", dap_config_path()); + DIR * l_net_dir = opendir( l_net_dir_str); + if ( l_net_dir ){ + struct dirent * l_dir_entry; + while ( (l_dir_entry = readdir(l_net_dir) )!= NULL ){ + if (l_dir_entry->d_name[0]=='\0' || l_dir_entry->d_name[0]=='.') + continue; + // don't search in directories + char * l_full_path = dap_strdup_printf("%s/%s", l_net_dir_str, l_dir_entry->d_name); + if(dap_dir_test(l_full_path)) { + DAP_DELETE(l_full_path); + continue; + } + DAP_DELETE(l_full_path); + // search only ".cfg" files + if(strlen(l_dir_entry->d_name) > 4) { // It has non zero name excluding file extension + if(strncmp(l_dir_entry->d_name + strlen(l_dir_entry->d_name) - 4, ".cfg", 4) != 0) { + // its not .cfg file + continue; + } + } + log_it(L_DEBUG,"Network config %s try to load", l_dir_entry->d_name); + //char* l_dot_pos = rindex(l_dir_entry->d_name,'.'); + char* l_dot_pos = strchr(l_dir_entry->d_name,'.'); + if ( l_dot_pos ) + *l_dot_pos = '\0'; + s_net_load(l_dir_entry->d_name ); + } + closedir(l_net_dir); + } + DAP_DELETE (l_net_dir_str); +} + +/** + * @brief s_cli_net + * @param argc + * @param argv + * @param arg_func + * @param str_reply + * @return + */ +static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply) +{ + int arg_index = 1; + dap_chain_net_t * l_net = NULL; + + // command 'net list' + if(dap_chain_node_cli_find_option_val(argv, arg_index, argc, "list", NULL) == arg_index) { + + dap_string_t *l_string_ret = dap_string_new("list of nets: "); + // show list of nets + dap_chain_net_item_t * l_net_item, *l_net_item_tmp; + int l_net_i = 0; + HASH_ITER(hh, s_net_items, l_net_item, l_net_item_tmp) + { + if(l_net_i > 0) + dap_string_append(l_string_ret, ", "); + dap_string_append_printf(l_string_ret, "%s", l_net_item->name); + l_net_i++; + } + if(!l_net_i) + dap_string_append(l_string_ret, "-\n"); + else + dap_string_append(l_string_ret, "\n"); + + dap_chain_node_cli_set_reply_text(a_str_reply, l_string_ret->str); + dap_string_free(l_string_ret, true); + return 0; + } + + int ret = dap_chain_node_cli_cmd_values_parse_net_chain( &arg_index, argc, argv, a_str_reply, NULL, &l_net ); + + if ( l_net ) { + const char *l_sync_str = NULL; + const char *l_links_str = NULL; + const char *l_go_str = NULL; + const char *l_get_str = NULL; + const char *l_stats_str = NULL; + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "sync", &l_sync_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "link", &l_links_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "go", &l_go_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "get", &l_get_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "stats", &l_stats_str); + + if ( l_stats_str ){ + if ( strcmp(l_stats_str,"tx") == 0 ) { + const char *l_to_str = NULL; + struct tm l_to_tm = {0}; + + const char *l_from_str = NULL; + struct tm l_from_tm = {0}; + + const char *l_prev_sec_str = NULL; + time_t l_prev_sec_ts; + + const char c_time_fmt[]="%Y-%m-%d_%H:%M:%S"; + + // Read from/to time + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-from", &l_from_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-to", &l_to_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-prev_sec", &l_prev_sec_str); + + if (l_from_str ) { + strptime( (char *)l_from_str, c_time_fmt, &l_from_tm ); + } + + if (l_to_str) { + strptime( (char *)l_to_str, c_time_fmt, &l_to_tm ); + } + + if ( l_to_str == NULL ){ // If not set '-to' - we set up current time + time_t l_ts_now = time(NULL); + localtime_r(&l_ts_now, &l_to_tm); + } + + if ( l_prev_sec_str ){ + time_t l_ts_now = time(NULL); + l_ts_now -= strtol( l_prev_sec_str, NULL,10 ); + localtime_r(&l_ts_now, &l_from_tm ); + }/*else if ( l_from_str == NULL ){ // If not set '-from' we set up current time minus 10 seconds + time_t l_ts_now = time(NULL); + l_ts_now -= 10; + localtime_r(&l_ts_now, &l_from_tm ); + }*/ + + // Form timestamps from/to + time_t l_from_ts = mktime(&l_from_tm); + time_t l_to_ts = mktime(&l_to_tm); + + // Produce strings + char l_from_str_new[50]; + char l_to_str_new[50]; + strftime(l_from_str_new, sizeof(l_from_str_new), c_time_fmt,&l_from_tm ); + strftime(l_to_str_new, sizeof(l_to_str_new), c_time_fmt,&l_to_tm ); + + + dap_string_t * l_ret_str = dap_string_new("Transactions statistics:\n"); + + dap_string_append_printf( l_ret_str, "\tFrom: %s\tTo: %s\n", l_from_str_new, l_to_str_new); + log_it(L_INFO, "Calc TPS from %s to %s", l_from_str_new, l_to_str_new); + uint64_t l_tx_count = dap_chain_ledger_count_from_to ( l_net->pub.ledger, l_from_ts, l_to_ts); + long double l_tps = l_to_ts == l_from_ts ? 0 : + (long double) l_tx_count / (long double) ( l_to_ts - l_from_ts ); + dap_string_append_printf( l_ret_str, "\tSpeed: %.3Lf TPS\n", l_tps ); + dap_string_append_printf( l_ret_str, "\tTotal: %llu\n", l_tx_count ); + dap_chain_node_cli_set_reply_text( a_str_reply, l_ret_str->str ); + dap_string_free( l_ret_str, false ); + } + } else if ( l_go_str){ + if ( strcmp(l_go_str,"online") == 0 ) { + dap_chain_net_state_go_to(l_net, NET_STATE_ONLINE); + dap_chain_node_cli_set_reply_text(a_str_reply, "Network \"%s\" go from state %s to %s", + l_net->pub.name,c_net_states[PVT(l_net)->state], + c_net_states[PVT(l_net)->state_new]); + } else if ( strcmp(l_go_str,"offline") == 0 ) { + dap_chain_net_state_go_to(l_net, NET_STATE_OFFLINE); + dap_chain_node_cli_set_reply_text(a_str_reply, "Network \"%s\" go from state %s to %s", + l_net->pub.name,c_net_states[PVT(l_net)->state], + c_net_states[PVT(l_net)->state_new]); + + } + else if(strcmp(l_go_str, "sync") == 0) { + dap_chain_net_state_go_to(l_net, NET_STATE_SYNC_GDB); + dap_chain_node_cli_set_reply_text(a_str_reply, "Network \"%s\" go from state %s to %s", + l_net->pub.name, c_net_states[PVT(l_net)->state], + c_net_states[PVT(l_net)->state_target]); + + } + + } else if ( l_get_str){ + if ( strcmp(l_get_str,"status") == 0 ) { + // get current node address + dap_chain_node_addr_t l_cur_node_addr = { 0 }; + l_cur_node_addr.uint64 = dap_chain_net_get_cur_addr(l_net) ? dap_chain_net_get_cur_addr(l_net)->uint64 : dap_db_get_cur_node_addr(l_net->pub.name); + if(!l_cur_node_addr.uint64) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Network \"%s\" has state %s (target state %s), active links %u from %u, cur node address not defined", + l_net->pub.name, c_net_states[PVT(l_net)->state], + c_net_states[PVT(l_net)->state_target], PVT(l_net)->links_count, + PVT(l_net)->links_addrs_count + ); + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Network \"%s\" has state %s (target state %s), active links %u from %u, cur node address " NODE_ADDR_FP_STR, + l_net->pub.name, c_net_states[PVT(l_net)->state], + c_net_states[PVT(l_net)->state_target], PVT(l_net)->links_count, + PVT(l_net)->links_addrs_count, + NODE_ADDR_FP_ARGS_S(l_cur_node_addr) + ); + } + ret = 0; + } + } else if ( l_links_str ){ + if ( strcmp(l_links_str,"list") == 0 ) { + + } else if ( strcmp(l_links_str,"add") == 0 ) { + + } else if ( strcmp(l_links_str,"del") == 0 ) { + + } else if ( strcmp(l_links_str,"info") == 0 ) { + + } else if ( strcmp (l_links_str,"disconnect_all") == 0 ){ + ret = 0; + dap_chain_net_stop(l_net); + }else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Subcommand \"link\" requires one of parameter: list\n"); + ret = -3; + } + + } else if( l_sync_str) { + if ( strcmp(l_sync_str,"all") == 0 ) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "SYNC_ALL state requested to state machine. Current state: %s\n", + c_net_states[ PVT(l_net)->state] ); + dap_chain_net_sync_all(l_net); + } else if ( strcmp(l_sync_str,"gdb") == 0) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "SYNC_GDB state requested to state machine. Current state: %s\n", + c_net_states[ PVT(l_net)->state] ); + dap_chain_net_sync_gdb(l_net); + + } else if ( strcmp(l_sync_str,"chains") == 0) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "SYNC_CHAINS state requested to state machine. Current state: %s\n", + c_net_states[ PVT(l_net)->state] ); + dap_chain_net_sync_chains(l_net); + + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Subcommand \"sync\" requires one of parameter: all,gdb,chains\n"); + ret = -2; + } + } else { + dap_chain_node_cli_set_reply_text(a_str_reply,"Command requires one of subcomand: sync, links\n"); + ret = -1; + } + + } + return ret; +} + +// for sequential loading chains +typedef struct list_priority_{ + uint16_t prior; + char * chains_path; +}list_priority; + +static int callback_compare_prioritity_list(const void * a_item1, const void * a_item2) +{ + list_priority *l_item1 = (list_priority*) a_item1; + list_priority *l_item2 = (list_priority*) a_item2; + if(!l_item1 || !l_item2 || l_item1->prior == l_item2->prior) + return 0; + if(l_item1->prior > l_item2->prior) + return 1; + return -1; +} + +/** + * @brief s_net_load + * @param a_net_name + * @return + */ +int s_net_load(const char * a_net_name) +{ + dap_config_t *l_cfg=NULL; + dap_string_t *l_cfg_path = dap_string_new("network/"); + dap_string_append(l_cfg_path,a_net_name); + + if( ( l_cfg = dap_config_open ( l_cfg_path->str ) ) == NULL ) { + log_it(L_ERROR,"Can't open default network config"); + dap_string_free(l_cfg_path,true); + return -1; + } else { + dap_string_free(l_cfg_path,true); + dap_chain_net_t * l_net = s_net_new( + dap_config_get_item_str(l_cfg , "general" , "id" ), + dap_config_get_item_str(l_cfg , "general" , "name" ), + dap_config_get_item_str(l_cfg , "general" , "node-role" ) + ); + if(!l_net) { + log_it(L_ERROR,"Can't create l_net"); + return -1; + } + PVT(l_net)->load_mode = true; + l_net->pub.gdb_groups_prefix = dap_strdup ( + dap_config_get_item_str_default(l_cfg , "general" , "gdb_groups_prefix", + dap_config_get_item_str(l_cfg , "general" , "name" ) ) ); + dap_chain_global_db_add_history_group_prefix( l_net->pub.gdb_groups_prefix, GROUP_LOCAL_HISTORY); + dap_chain_global_db_add_history_callback_notify(l_net->pub.gdb_groups_prefix, s_gbd_history_callback_notify, l_net ); + + l_net->pub.gdb_nodes = dap_strdup_printf("%s.nodes",l_net->pub.gdb_groups_prefix); + l_net->pub.gdb_nodes_aliases = dap_strdup_printf("%s.nodes.aliases",l_net->pub.gdb_groups_prefix); + + // for sync special groups - nodes + char **l_gdb_sync_nodes_addrs = dap_config_get_array_str(l_cfg, "general", "gdb_sync_nodes_addrs", + &PVT(l_net)->gdb_sync_nodes_addrs_count); + if(l_gdb_sync_nodes_addrs && PVT(l_net)->gdb_sync_nodes_addrs_count > 0) { + PVT(l_net)->gdb_sync_nodes_addrs = (dap_chain_node_addr_t*) DAP_NEW_Z_SIZE(char**, + sizeof(dap_chain_node_addr_t)*PVT(l_net)->gdb_sync_nodes_addrs_count); + for(uint16_t i = 0; i < PVT(l_net)->gdb_sync_nodes_addrs_count; i++) { + dap_chain_node_addr_from_str(PVT(l_net)->gdb_sync_nodes_addrs + i, l_gdb_sync_nodes_addrs[i]); + } + } + // for sync special groups - groups + char **l_gdb_sync_groups = dap_config_get_array_str(l_cfg, "general", "gdb_sync_groups", &PVT(l_net)->gdb_sync_groups_count); + if(l_gdb_sync_groups && PVT(l_net)->gdb_sync_groups_count > 0) { + PVT(l_net)->gdb_sync_groups = (char **) DAP_NEW_SIZE(char**, sizeof(char*)*PVT(l_net)->gdb_sync_groups_count); + for(uint16_t i = 0; i < PVT(l_net)->gdb_sync_groups_count; i++) { + PVT(l_net)->gdb_sync_groups[i] = dap_strdup(l_gdb_sync_groups[i]); + // added group to history log + dap_list_t *l_groups0 = dap_chain_global_db_driver_get_groups_by_mask(PVT(l_net)->gdb_sync_groups[i]); + dap_list_t *l_groups = l_groups0; + while(l_groups) { + char *l_group_name = l_groups->data; + // do not use groups with names like *.del + if(fnmatch("*.del", l_group_name, 0)) { + const char *l_history_group = dap_chain_global_db_add_history_extra_group(l_group_name, PVT(l_net)->gdb_sync_nodes_addrs, + PVT(l_net)->gdb_sync_nodes_addrs_count); + dap_chain_global_db_add_history_extra_group_callback_notify(l_group_name, + s_gbd_history_callback_notify, l_net); + // create history for group + if(dap_db_log_get_group_history_last_id(l_history_group) <= 0) { + size_t l_data_size_out = 0; + dap_store_obj_t *l_obj = dap_chain_global_db_obj_gr_get(NULL, &l_data_size_out, l_group_name); + if(l_obj && l_data_size_out > 0) { + dap_db_history_add('a', l_obj, l_data_size_out, l_history_group); + dap_store_obj_free(l_obj, l_data_size_out); + } + } + } + l_groups = dap_list_next(l_groups); + } + dap_list_free_full(l_groups0, (dap_callback_destroyed_t)free); + } + } + + + // Add network to the list + dap_chain_net_item_t * l_net_item = DAP_NEW_Z( dap_chain_net_item_t); + dap_chain_net_item_t * l_net_item2 = DAP_NEW_Z( dap_chain_net_item_t); + dap_snprintf(l_net_item->name,sizeof (l_net_item->name),"%s" + ,dap_config_get_item_str(l_cfg , "general" , "name" )); + l_net_item->chain_net = l_net; + l_net_item->net_id.uint64 = l_net->pub.id.uint64; + HASH_ADD_STR(s_net_items,name,l_net_item); + + memcpy( l_net_item2,l_net_item,sizeof (*l_net_item)); + HASH_ADD(hh,s_net_items_ids,net_id,sizeof ( l_net_item2->net_id),l_net_item2); + + // LEDGER model + uint16_t l_ledger_flags = 0; + switch ( PVT( l_net )->node_role.enums ) { + case NODE_ROLE_ROOT_MASTER: + case NODE_ROLE_ROOT: + case NODE_ROLE_ARCHIVE: + l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_TOKEN_EMISSION; + case NODE_ROLE_MASTER: + l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_CELLS_DS; + case NODE_ROLE_CELL_MASTER: + l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_TOKEN_EMISSION; + case NODE_ROLE_FULL: + case NODE_ROLE_LIGHT: + l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_LOCAL_DS; + } + // init LEDGER model + l_net->pub.ledger = dap_chain_ledger_create(l_ledger_flags); + // Check if seed nodes are present in local db alias + char **l_seed_aliases = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_aliases" + ,&PVT(l_net)->seed_aliases_count); + PVT(l_net)->seed_aliases = PVT(l_net)->seed_aliases_count>0 ? + (char **)DAP_NEW_SIZE(char**, sizeof(char*)*PVT(l_net)->seed_aliases_count) : NULL; + for(size_t i = 0; i < PVT(l_net)->seed_aliases_count; i++) { + PVT(l_net)->seed_aliases[i] = dap_strdup(l_seed_aliases[i]); + } + + uint16_t l_seed_nodes_addrs_len =0; + char ** l_seed_nodes_addrs = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_addrs" + ,&l_seed_nodes_addrs_len); + + uint16_t l_seed_nodes_ipv4_len =0; + char ** l_seed_nodes_ipv4 = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_ipv4" + ,&l_seed_nodes_ipv4_len); + + uint16_t l_seed_nodes_ipv6_len =0; + char ** l_seed_nodes_ipv6 = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_ipv6" + ,&l_seed_nodes_ipv6_len); + + uint16_t l_seed_nodes_hostnames_len =0; + char ** l_seed_nodes_hostnames = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_hostnames" + ,&l_seed_nodes_hostnames_len); + + uint16_t l_seed_nodes_port_len =0; + char ** l_seed_nodes_port = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_port" + ,&l_seed_nodes_port_len); + + const char * l_node_addr_type = dap_config_get_item_str_default(l_cfg , "general" ,"node_addr_type","auto"); + + const char * l_node_addr_str = NULL; + const char * l_node_alias_str = NULL; + + // use unique addr from pub key + if(!dap_strcmp(l_node_addr_type, "auto")) { + size_t l_pub_key_data_size = 0; + uint8_t *l_pub_key_data = NULL; + + // read pub key + l_pub_key_data = dap_chain_global_db_gr_get("cur-node-addr-pkey", &l_pub_key_data_size, GROUP_LOCAL_NODE_ADDR); + // generate new pub key + if(!l_pub_key_data || !l_pub_key_data_size){ + + const char * l_certs_name_str = "node-addr"; + dap_cert_t ** l_certs = NULL; + size_t l_certs_size = 0; + dap_cert_t * l_cert = NULL; + // Load certs or create if not found + if(!dap_cert_parse_str_list(l_certs_name_str, &l_certs, &l_certs_size)) { // Load certs + const char *l_cert_folder = dap_cert_get_folder(0); + // create new cert + if(l_cert_folder) { + char *l_cert_path = dap_strdup_printf("%s/%s.dcert", l_cert_folder, l_certs_name_str); + l_cert = dap_cert_generate(l_certs_name_str, l_cert_path, DAP_ENC_KEY_TYPE_SIG_DILITHIUM); + DAP_DELETE(l_cert_path); + } + } + if(l_certs_size > 0) + l_cert = l_certs[0]; + if(l_cert) { + l_pub_key_data = dap_enc_key_serealize_pub_key(l_cert->enc_key, &l_pub_key_data_size); + // save pub key + if(l_pub_key_data && l_pub_key_data_size > 0) + dap_chain_global_db_gr_set(dap_strdup("cur-node-addr-pkey"), (uint8_t*) l_pub_key_data, l_pub_key_data_size, + GROUP_LOCAL_NODE_ADDR); + } + } + // generate addr from pub_key + dap_chain_hash_fast_t l_hash; + if(l_pub_key_data_size > 0 && dap_hash_fast(l_pub_key_data, l_pub_key_data_size, &l_hash) == 1) { + l_node_addr_str = dap_strdup_printf("%04X::%04X::%04X::%04X", + (uint16_t) *(uint16_t*) (l_hash.raw), + (uint16_t) *(uint16_t*) (l_hash.raw + 2), + (uint16_t) *(uint16_t*) (l_hash.raw + DAP_CHAIN_HASH_FAST_SIZE - 4), + (uint16_t) *(uint16_t*) (l_hash.raw + DAP_CHAIN_HASH_FAST_SIZE - 2)); + } + DAP_DELETE(l_pub_key_data); + } + // use static addr from setting + else if(!dap_strcmp(l_node_addr_type, "static")) { + //const char * l_node_ipv4_str = dap_config_get_item_str(l_cfg , "general" ,"node-ipv4"); + l_node_addr_str = dap_strdup(dap_config_get_item_str(l_cfg, "general", "node-addr")); + l_node_alias_str = dap_config_get_item_str(l_cfg, "general", "node-alias"); + } + + log_it (L_DEBUG, "Read %u aliases, %u address and %u ipv4 addresses, check them", + PVT(l_net)->seed_aliases_count,l_seed_nodes_addrs_len, l_seed_nodes_ipv4_len ); + // save new nodes from cfg file to db + for ( size_t i = 0; i < PVT(l_net)->seed_aliases_count && + i < l_seed_nodes_addrs_len && + ( + ( l_seed_nodes_ipv4_len && i < l_seed_nodes_ipv4_len ) || + ( l_seed_nodes_ipv6_len && i < l_seed_nodes_ipv6_len ) || + ( l_seed_nodes_hostnames_len && i < l_seed_nodes_hostnames_len ) + ) + ; i++ ){ + dap_chain_node_addr_t * l_seed_node_addr; + l_seed_node_addr = dap_chain_node_alias_find(l_net, PVT(l_net)->seed_aliases[i] ); + if (l_seed_node_addr == NULL){ + log_it(L_NOTICE, "Not found alias %s in database, prefill it",PVT(l_net)->seed_aliases[i]); + dap_chain_node_info_t * l_node_info = DAP_NEW_Z(dap_chain_node_info_t); + l_seed_node_addr = DAP_NEW_Z(dap_chain_node_addr_t); + dap_snprintf( l_node_info->hdr.alias,sizeof ( l_node_info->hdr.alias),"%s",PVT(l_net)->seed_aliases[i]); + if (sscanf(l_seed_nodes_addrs[i],NODE_ADDR_FP_STR, NODE_ADDR_FPS_ARGS(l_seed_node_addr) ) != 4 ){ + log_it(L_ERROR,"Wrong address format, should be like 0123::4567::890AB::CDEF"); + DAP_DELETE(l_seed_node_addr); + DAP_DELETE(l_node_info); + l_seed_node_addr = NULL; + continue; + } + if( l_seed_node_addr ){ + if ( l_seed_nodes_ipv4_len ) + inet_pton( AF_INET, l_seed_nodes_ipv4[i],&l_node_info->hdr.ext_addr_v4); + if ( l_seed_nodes_ipv6_len ) + inet_pton( AF_INET6, l_seed_nodes_ipv6[i],&l_node_info->hdr.ext_addr_v6); + if(l_seed_nodes_port_len && l_seed_nodes_port_len >= i) + l_node_info->hdr.ext_port = strtoul(l_seed_nodes_port[i], NULL, 10); + else + l_node_info->hdr.ext_port = 8079; + + if ( l_seed_nodes_hostnames_len ){ + struct addrinfo l_hints={0}, *l_servinfo, *p; + int rv; + l_hints.ai_family = AF_UNSPEC ; /* Allow IPv4 or IPv6 */ + //l_hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + + log_it( L_DEBUG, "Resolve %s addr", l_seed_nodes_hostnames[i]); + struct hostent *l_he; + + if ( l_he = gethostbyname (l_seed_nodes_hostnames[i]) ){ + struct in_addr **l_addr_list = (struct in_addr **) l_he->h_addr_list; + for(int i = 0; l_addr_list[i] != NULL; i++ ) { + log_it( L_NOTICE, "Resolved %s to %s (ipv4)", l_seed_nodes_hostnames[i] , + inet_ntoa( *l_addr_list[i] ) ); + l_node_info->hdr.ext_addr_v4.s_addr = l_addr_list[i]->s_addr; + } + } else { + herror("gethostname"); + } + } + + l_node_info->hdr.address.uint64 = l_seed_node_addr->uint64; + if ( l_node_info->hdr.ext_addr_v4.s_addr || + l_node_info->hdr.ext_addr_v6.s6_addr32[0] ){ + int l_ret; + if ( (l_ret = dap_chain_node_info_save(l_net, l_node_info)) ==0 ){ + if (dap_chain_node_alias_register(l_net,PVT(l_net)->seed_aliases[i],l_seed_node_addr)) + log_it(L_NOTICE,"Seed node "NODE_ADDR_FP_STR" added to the curent list",NODE_ADDR_FP_ARGS(l_seed_node_addr) ); + else { + log_it(L_WARNING,"Cant register alias %s for address "NODE_ADDR_FP_STR,NODE_ADDR_FP_ARGS(l_seed_node_addr)); + } + }else{ + log_it(L_WARNING,"Cant save node info for address "NODE_ADDR_FP_STR" return code %d", + NODE_ADDR_FP_ARGS(l_seed_node_addr), l_ret); + } + } + DAP_DELETE( l_seed_node_addr); + }else + log_it(L_WARNING,"No address for seed node, can't populate global_db with it"); + DAP_DELETE( l_node_info); + }else{ + log_it(L_DEBUG,"Seed alias %s is present",PVT(l_net)->seed_aliases[i]); + DAP_DELETE( l_seed_node_addr); + } + + } + //DAP_DELETE( l_seed_nodes_ipv4); + //DAP_DELETE(l_seed_nodes_addrs); + if ( l_node_addr_str || l_node_alias_str ){ + dap_chain_node_addr_t * l_node_addr; + if ( l_node_addr_str == NULL) + l_node_addr = dap_chain_node_alias_find(l_net, l_node_alias_str); + else{ + l_node_addr = DAP_NEW_Z(dap_chain_node_addr_t); + bool parse_succesfully = false; + if ( sscanf(l_node_addr_str, "0x%016llx",&l_node_addr->uint64 ) == 1 ){ + log_it(L_DEBUG, "Parse node address with format 0x016llx"); + parse_succesfully = true; + } + if ( !parse_succesfully && dap_chain_node_addr_from_str(l_node_addr, l_node_addr_str) == 0) { + log_it(L_DEBUG, "Parse node address with format 04hX::04hX::04hX::04hX"); + parse_succesfully = true; + } + + if (!parse_succesfully){ + log_it(L_ERROR,"Can't parse node address %s", l_node_addr_str); + DAP_DELETE(l_node_addr); + l_node_addr = NULL; + } + log_it(L_NOTICE, "Parse node addr " NODE_ADDR_FP_STR " successfully", NODE_ADDR_FP_ARGS(l_node_addr)); + PVT(l_net)->node_addr = l_node_addr; + //} + } + if ( l_node_addr ) { + char *l_addr_hash_str = dap_chain_node_addr_to_hash_str(l_node_addr); + // save current node address + dap_db_set_cur_node_addr(l_node_addr->uint64, l_net->pub.name); + if(!l_addr_hash_str){ + log_it(L_ERROR,"Can't get hash string for node address!"); + } else { + PVT(l_net)->node_info = dap_chain_node_info_read (l_net, l_node_addr); + if ( PVT(l_net)->node_info ) { + log_it(L_NOTICE,"GDB Info: node_addr: " NODE_ADDR_FP_STR" links: %u cell_id: 0x%016X ", + NODE_ADDR_FP_ARGS(l_node_addr), + PVT(l_net)->node_info->hdr.links_number, + PVT(l_net)->node_info->hdr.cell_id.uint64); + // save cell_id + l_net->pub.cell_id.uint64 = PVT(l_net)->node_info->hdr.cell_id.uint64; + }else { + log_it(L_WARNING, "Not present node_info in database for our own address " NODE_ADDR_FP_STR, + NODE_ADDR_FP_ARGS(l_node_addr) ); + } + } + } + else{ + log_it(L_WARNING, "Not present our own address %s in database", (l_node_alias_str) ? l_node_alias_str: ""); + } + + + } +/* // if present 'l_node_ipv4_str' and no 'l_node_addr_str' and 'l_node_alias_str' + if(!PVT(l_net)->node_info && l_node_ipv4_str) { + dap_chain_node_info_t *l_node_info = DAP_NEW_Z(dap_chain_node_info_t); + inet_pton( AF_INET, l_node_ipv4_str, &l_node_info->hdr.ext_addr_v4); + PVT(l_net)->node_info = l_node_info; + }*/ + + // Init chains + //size_t l_chains_path_size =strlen(dap_config_path())+1+strlen(l_net->pub.name)+1+strlen("network")+1; + //char * l_chains_path = DAP_NEW_Z_SIZE (char,l_chains_path_size); + //dap_snprintf(l_chains_path,l_chains_path_size,"%s/network/%s",dap_config_path(),l_net->pub.name); + char * l_chains_path = dap_strdup_printf("%s/network/%s", dap_config_path(), l_net->pub.name); + DIR * l_chains_dir = opendir(l_chains_path); + DAP_DELETE (l_chains_path); + if ( l_chains_dir ){ + // for sequential loading chains + dap_list_t *l_prior_list = NULL; + + struct dirent * l_dir_entry; + while ( (l_dir_entry = readdir(l_chains_dir) )!= NULL ){ + if (l_dir_entry->d_name[0]=='\0') + continue; + char * l_entry_name = strdup(l_dir_entry->d_name); + if (strlen (l_entry_name) > 4 ){ // It has non zero name excluding file extension + if ( strncmp (l_entry_name+ strlen(l_entry_name)-4,".cfg",4) == 0 ) { // its .cfg file + l_entry_name [strlen(l_entry_name)-4] = 0; + log_it(L_DEBUG,"Open chain config \"%s\"...",l_entry_name); + l_chains_path = dap_strdup_printf("network/%s/%s",l_net->pub.name,l_entry_name); + dap_config_t * l_cfg = dap_config_open(l_chains_path); + if(l_cfg) { + list_priority *l_chain_prior = DAP_NEW_Z(list_priority); + l_chain_prior->prior = dap_config_get_item_uint16_default(l_cfg, "chain", "load_priority", 100); + l_chain_prior->chains_path = l_chains_path; + // add chain to load list; + l_prior_list = dap_list_append(l_prior_list, l_chain_prior); + } + // Create chain object +// dap_chain_t * l_chain = dap_chain_load_from_cfg(l_net->pub.ledger, l_net->pub.name, +// l_net->pub.id, l_chains_path); +// if(l_chain) { +// DL_APPEND(l_net->pub.chains, l_chain); +// if(l_chain->callback_created) +// l_chain->callback_created(l_chain, l_cfg); +// } +// DAP_DELETE (l_chains_path); + } + } + DAP_DELETE (l_entry_name); + } + closedir(l_chains_dir); + + // sort list with chains names by priority + l_prior_list = dap_list_sort(l_prior_list, callback_compare_prioritity_list); + // load chains by priority + dap_list_t *l_list = l_prior_list; + while(l_list){ + list_priority *l_chain_prior = l_list->data; + // Create chain object + dap_chain_t * l_chain = dap_chain_load_from_cfg(l_net->pub.ledger, l_net->pub.name, + l_net->pub.id, l_chain_prior->chains_path); + if(l_chain) { + DL_APPEND(l_net->pub.chains, l_chain); + if(l_chain->callback_created) + l_chain->callback_created(l_chain, l_cfg); + // add a callback to monitor changes in the chain + dap_chain_add_callback_notify(l_chain, s_chain_callback_notify, l_net); + } + DAP_DELETE (l_chain_prior->chains_path); + l_list = dap_list_next(l_list); + } + dap_list_free(l_prior_list); + + } else { + log_it(L_ERROR,"Can't any chains for network %s",l_net->pub.name); + PVT(l_net)->load_mode = false; + + return -2; + } + // Do specific role actions post-chain created + switch ( PVT( l_net )->node_role.enums ) { + case NODE_ROLE_ROOT_MASTER:{ + // Set to process everything in datum pool + dap_chain_t * l_chain = NULL; + DL_FOREACH(l_net->pub.chains, l_chain ) l_chain->is_datum_pool_proc = true; + log_it(L_INFO,"Root master node role established"); + } // Master root includes root + case NODE_ROLE_ROOT:{ + // Set to process only zerochain + dap_chain_id_t l_chain_id = {{0}}; + dap_chain_t * l_chain = dap_chain_find_by_id(l_net->pub.id,l_chain_id); + if (l_chain ) + l_chain->is_datum_pool_proc = true; + + PVT(l_net)->state_target = NET_STATE_ONLINE; + log_it(L_INFO,"Root node role established"); + } break; + case NODE_ROLE_CELL_MASTER: + case NODE_ROLE_MASTER:{ + + uint16_t l_proc_chains_count=0; + char ** l_proc_chains = dap_config_get_array_str(l_cfg,"role-master" , "proc_chains", &l_proc_chains_count ); + for ( size_t i = 0; i< l_proc_chains_count ; i++){ + dap_chain_id_t l_chain_id = {{0}}; + if(dap_sscanf( l_proc_chains[i], "0x%16lX", &l_chain_id.uint64) ==1 || dap_scanf("0x%16lx", &l_chain_id.uint64) == 1){ + dap_chain_t * l_chain = dap_chain_find_by_id(l_net->pub.id, l_chain_id ); + if ( l_chain ){ + l_chain->is_datum_pool_proc = true; + }else{ + log_it( L_WARNING, "Can't find chain id " ); + } + } + DAP_DELETE( l_proc_chains[i]); + l_proc_chains[i] = NULL; + } + //if ( l_proc_chains ) + // DAP_DELETE (l_proc_chains); + //l_proc_chains = NULL; + + PVT(l_net)->state_target = NET_STATE_ONLINE; + log_it(L_INFO,"Master node role established"); + } break; + case NODE_ROLE_FULL:{ + log_it(L_INFO,"Full node role established"); + PVT(l_net)->state_target = NET_STATE_ONLINE; + } break; + case NODE_ROLE_LIGHT: + default: + log_it(L_INFO,"Light node role established"); + + } + + if (s_seed_mode || !dap_config_get_item_bool_default(g_config ,"general", "auto_online",false ) ) { // If we seed we do everything manual. First think - prefil list of node_addrs and its aliases + PVT(l_net)->state_target = NET_STATE_OFFLINE; + } + PVT(l_net)->load_mode = false; + + // Start the proc thread + s_net_proc_thread_start(l_net); + log_it(L_NOTICE, "Сhain network \"%s\" initialized",l_net_item->name); + dap_config_close(l_cfg); + } + return 0; +} + +/** + * @brief dap_chain_net_deinit + */ +void dap_chain_net_deinit() +{ +} + +dap_chain_net_t **dap_chain_net_list(size_t *a_size) +{ + *a_size = HASH_COUNT(s_net_items); + dap_chain_net_t **l_net_list = DAP_NEW_SIZE(dap_chain_net_t *, (*a_size) * sizeof(dap_chain_net_t *)); + dap_chain_net_item_t *l_current_item, *l_tmp; + int i = 0; + HASH_ITER(hh, s_net_items, l_current_item, l_tmp) { + l_net_list[i++] = l_current_item->chain_net; + } + return l_net_list; +} + +/** + * @brief dap_chain_net_by_name + * @param a_name + * @return + */ +dap_chain_net_t * dap_chain_net_by_name( const char * a_name) +{ + dap_chain_net_item_t * l_net_item = NULL; + if(a_name) + HASH_FIND_STR(s_net_items,a_name,l_net_item ); + if ( l_net_item ) + return l_net_item->chain_net; + else + return NULL; +} + +/** + * @brief dap_chain_ledger_by_net_name + * @param a_net_name + * @return + */ +dap_ledger_t * dap_chain_ledger_by_net_name( const char * a_net_name) +{ + dap_chain_net_t *l_net = dap_chain_net_by_name(a_net_name); + if(l_net) + return l_net->pub.ledger; + return NULL; +} + +/** + * @brief dap_chain_net_by_id + * @param a_id + * @return + */ +dap_chain_net_t * dap_chain_net_by_id( dap_chain_net_id_t a_id) +{ + dap_chain_net_item_t * l_net_item = NULL; + HASH_FIND(hh,s_net_items_ids,&a_id,sizeof (a_id), l_net_item ); + if ( l_net_item ) + return l_net_item->chain_net; + else + return NULL; + +} + + +/** + * @brief dap_chain_net_id_by_name + * @param a_name + * @return + */ +dap_chain_net_id_t dap_chain_net_id_by_name( const char * a_name) +{ + dap_chain_net_t *l_net = dap_chain_net_by_name( a_name ); + dap_chain_net_id_t l_ret = {0}; + if (l_net) + l_ret.uint64 = l_net->pub.id.uint64; + return l_ret; +} + +/** + * @brief dap_chain_net_get_chain_by_name + * @param l_net + * @param a_name + * @return + */ +dap_chain_t * dap_chain_net_get_chain_by_name( dap_chain_net_t * l_net, const char * a_name) +{ + dap_chain_t * l_chain; + DL_FOREACH(l_net->pub.chains, l_chain){ + if(dap_strcmp(l_chain->name,a_name) == 0) + return l_chain; + } + return NULL; +} + +/** + * @brief dap_chain_net_get_chain_by_chain_type + * @param a_datum_type + * @return + */ +dap_chain_t * dap_chain_net_get_chain_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type) +{ + dap_chain_t * l_chain; + if(!l_net) + return NULL; + DL_FOREACH(l_net->pub.chains, l_chain) + { + for(uint16_t i = 0; i < l_chain->datum_types_count; i++) { + if(l_chain->datum_types[i] == a_datum_type) + return l_chain; + } + } + return NULL; +} + +/** + * @brief dap_chain_net_get_gdb_group_mempool_by_chain_type + * @param a_datum_type + * @return + */ +char * dap_chain_net_get_gdb_group_mempool_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type) +{ + dap_chain_t * l_chain; + if(!l_net) + return NULL; + DL_FOREACH(l_net->pub.chains, l_chain) + { + for(uint16_t i = 0; i < l_chain->datum_types_count; i++) { + if(l_chain->datum_types[i] == a_datum_type) + return dap_chain_net_get_gdb_group_mempool(l_chain); + } + } + return NULL; +} + + +/** + * @brief dap_chain_net_get_cur_addr + * @param l_net + * @return + */ +dap_chain_node_addr_t * dap_chain_net_get_cur_addr( dap_chain_net_t * l_net) +{ + return PVT(l_net)->node_info? &PVT(l_net)->node_info->hdr.address: PVT(l_net)->node_addr; +} + +uint64_t dap_chain_net_get_cur_addr_int(dap_chain_net_t * l_net) +{ + return dap_chain_net_get_cur_addr(l_net) ? dap_chain_net_get_cur_addr(l_net)->uint64 : + dap_db_get_cur_node_addr(l_net->pub.name); +} + +dap_chain_cell_id_t * dap_chain_net_get_cur_cell( dap_chain_net_t * l_net) +{ + return PVT(l_net)->node_info? &PVT(l_net)->node_info->hdr.cell_id: 0; +} + + +/** + * Get nodes list (list of dap_chain_node_addr_t struct) + */ +dap_list_t* dap_chain_net_get_link_node_list(dap_chain_net_t * l_net, bool a_is_only_cur_cell) +{ + dap_list_t *l_node_list = NULL; + // get cur node address + dap_chain_node_addr_t l_cur_node_addr = { 0 }; + l_cur_node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net); + + dap_chain_node_info_t *l_cur_node_info = dap_chain_node_info_read(l_net, &l_cur_node_addr); + // add links to nodes list only from the same cell + if(l_cur_node_info) { + for(unsigned int i = 0; i < l_cur_node_info->hdr.links_number; i++) { + bool l_is_add = true; + dap_chain_node_addr_t *l_remote_address = l_cur_node_info->links + i; + if(a_is_only_cur_cell) { + // get remote node list + dap_chain_node_info_t *l_remote_node_info = dap_chain_node_info_read(l_net, l_remote_address); + if(!l_remote_node_info || l_remote_node_info->hdr.cell_id.uint64 != l_cur_node_info->hdr.cell_id.uint64) + l_is_add = false; + DAP_DELETE(l_remote_node_info); + } + if(l_is_add) { + dap_chain_node_addr_t *l_address = DAP_NEW(dap_chain_node_addr_t); + l_address->uint64 = l_cur_node_info->links[i].uint64; + l_node_list = dap_list_append(l_node_list, l_address); + } + } + + } + DAP_DELETE(l_cur_node_info); + return l_node_list; +} + +/** + * Get remote nodes list (list of dap_chain_node_addr_t struct) + */ +dap_list_t* dap_chain_net_get_node_list(dap_chain_net_t * l_net) +{ + dap_list_t *l_node_list = NULL; + /* + dap_chain_net_pvt_t *l_net_pvt = PVT(l_net); + // get nodes from seed_nodes + for(uint16_t i = 0; i < l_net_pvt->seed_aliases_count; i++) { + dap_chain_node_addr_t *l_node_address = dap_chain_node_alias_find(l_net, l_net_pvt->seed_aliases[i]); + l_node_list = dap_list_append(l_node_list, l_node_address); + }*/ + + // get nodes list from global_db + dap_global_db_obj_t *l_objs = NULL; + size_t l_nodes_count = 0; + // read all node + l_objs = dap_chain_global_db_gr_load(l_net->pub.gdb_nodes, &l_nodes_count); + if(!l_nodes_count || !l_objs) + return l_node_list; + for(size_t i = 0; i < l_nodes_count; i++) { + dap_chain_node_info_t *l_node_info = (dap_chain_node_info_t *) l_objs[i].value; + dap_chain_node_addr_t *l_address = DAP_NEW(dap_chain_node_addr_t); + l_address->uint64 = l_node_info->hdr.address.uint64; + l_node_list = dap_list_append(l_node_list, l_address); + } + dap_chain_global_db_objs_delete(l_objs, l_nodes_count); + return l_node_list; + + // get remote node list + /*dap_chain_node_info_t *l_node_info = dap_chain_node_info_read(l_net, l_node_address); + if(!l_node_info) + continue; + // start connect + //debug inet_pton( AF_INET, "192.168.100.93", &l_node_info->hdr.ext_addr_v4); + dap_chain_node_client_t *l_node_client = dap_chain_node_client_connect(l_node_info); + //dap_chain_node_client_t *l_node_client = dap_chain_client_connect(l_node_info, l_stage_target, l_active_channels); + if(!l_node_client) { + DAP_DELETE(l_node_info); + continue; + } + // wait connected + int timeout_ms = 5000; //5 sec = 5000 ms + int res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_CONNECTED, timeout_ms); + if(res) { + // clean client struct + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_node_info); + continue; + } + res = dap_chain_node_client_send_nodelist_req(l_node_client); + if(res) { + // clean client struct + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_node_info); + continue; + } + res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_NODELIST_GOT, timeout_ms); + if(res) { + // clean client struct + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_node_info); + continue; + } + DAP_DELETE(l_node_info); + */ +} + +/** + * @brief dap_chain_net_proc_datapool + * @param a_net + */ +void dap_chain_net_proc_mempool (dap_chain_net_t * a_net) +{ + + dap_string_t * l_str_tmp = dap_string_new(NULL); + dap_chain_t *l_chain; + DL_FOREACH(a_net->pub.chains, l_chain) { + char *l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + + size_t l_objs_size = 0; + dap_global_db_obj_t * l_objs = dap_chain_global_db_gr_load(l_gdb_group_mempool, &l_objs_size); + if(l_objs_size) { + log_it(L_INFO, "%s.%s: Found %u records :", a_net->pub.name, l_chain->name, + l_objs_size); + size_t l_datums_size = l_objs_size; + dap_chain_datum_t ** l_datums = DAP_NEW_Z_SIZE(dap_chain_datum_t*, + sizeof(dap_chain_datum_t*) * l_datums_size); + size_t l_objs_size_tmp = (l_objs_size > 15) ? min(l_objs_size, 10) : l_objs_size; + for(size_t i = 0; i < l_objs_size; i++) { + dap_chain_datum_t * l_datum = (dap_chain_datum_t*) l_objs[i].value; + l_datums[i] = l_datum; + if(i < l_objs_size_tmp) { + char buf[50]; + time_t l_ts_create = (time_t) l_datum->header.ts_create; + log_it(L_INFO, "\t\t0x%s: type_id=%s ts_create=%s data_size=%u", + l_objs[i].key, c_datum_type_str[l_datum->header.type_id], + ctime_r(&l_ts_create, buf), l_datum->header.data_size); + } + } + size_t l_objs_processed = l_chain->callback_datums_pool_proc(l_chain, l_datums, l_datums_size); + // Delete processed objects + size_t l_objs_processed_tmp = (l_objs_processed > 15) ? min(l_objs_processed, 10) : l_objs_processed; + for(size_t i = 0; i < l_objs_processed; i++) { + dap_chain_global_db_gr_del(dap_strdup(l_objs[i].key), l_gdb_group_mempool); + if(i < l_objs_processed_tmp) { + dap_string_append_printf(l_str_tmp, "New event created, removed datum 0x%s from mempool \n", + l_objs[i].key); + } + } + if(l_objs_processed < l_datums_size) + log_it(L_WARNING, "%s.%s: %d records not processed", a_net->pub.name, l_chain->name, + l_datums_size - l_objs_processed); + dap_chain_global_db_objs_delete(l_objs, l_objs_size); + } + else { + log_it(L_INFO, "%s.%s: No records in mempool", a_net->pub.name, l_chain ? l_chain->name : "[no chain]"); + } + DAP_DELETE(l_gdb_group_mempool); + + } +} + +/** + * @brief dap_chain_net_tx_get_by_hash + * @param a_net + * @param a_tx_hash + * @param a_search_type + * @return + */ +dap_chain_datum_tx_t * dap_chain_net_get_tx_by_hash(dap_chain_net_t * a_net, dap_chain_hash_fast_t * a_tx_hash, + dap_chain_net_tx_search_type_t a_search_type) +{ + dap_ledger_t * l_ledger = dap_chain_ledger_by_net_name( a_net->pub.name ); + dap_chain_datum_tx_t * l_tx = NULL; + + switch (a_search_type) { + case TX_SEARCH_TYPE_NET: + case TX_SEARCH_TYPE_CELL: + case TX_SEARCH_TYPE_LOCAL:{ + if ( ! l_tx ){ + // pass all chains + for ( dap_chain_t * l_chain = a_net->pub.chains; l_chain; l_chain = l_chain->next){ + if ( l_chain->callback_tx_find_by_hash ){ + // try to find transaction in chain ( inside shard ) + l_tx = l_chain->callback_tx_find_by_hash( l_chain, a_tx_hash ); + if (l_tx) + break; + } + } + } + }break; + + case TX_SEARCH_TYPE_NET_UNSPENT: + case TX_SEARCH_TYPE_CELL_UNSPENT:{ + l_tx = dap_chain_ledger_tx_find_by_hash( l_ledger, a_tx_hash ); + }break; + } + return l_tx; +} + +/** + * @brief dap_chain_net_get_add_gdb_group + * @param a_net + * @param a_node_addr + * @return + */ +dap_list_t * dap_chain_net_get_add_gdb_group(dap_chain_net_t * a_net, dap_chain_node_addr_t a_node_addr) +{ + dap_list_t *l_list_groups = NULL; + if(!PVT(a_net)->gdb_sync_nodes_addrs || !a_net) + return NULL; + for(uint16_t i = 0; i < PVT(a_net)->gdb_sync_nodes_addrs_count; i++) { + if(a_node_addr.uint64 == PVT(a_net)->gdb_sync_nodes_addrs[i].uint64) { + for(uint16_t j = 0; j < PVT(a_net)->gdb_sync_groups_count; j++) + l_list_groups = dap_list_append(l_list_groups, PVT(a_net)->gdb_sync_groups[j]); + } + } + return l_list_groups; +} diff --git a/libdap-chain-net/dap_chain_net.h b/libdap-chain-net/dap_chain_net.h new file mode 100644 index 0000000000000000000000000000000000000000..8b76bfcf1b80bab2dd9ba984004fa09228b40d0b --- /dev/null +++ b/libdap-chain-net/dap_chain_net.h @@ -0,0 +1,153 @@ +/* +* Authors: +* Dmitriy Gerasimov <naeper@demlabs.net> +* Aleksandr Lysikov <alexander.lysikov@demlabs.net> +* Cellframe https://cellframe.net +* DeM Labs Inc. https://demlabs.net +* Copyright (c) 2017-2019 +* All rights reserved. + +This file is part of CellFrame SDK the open source project + +CellFrame SDK 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. + +CellFrame SDK 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 any CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#else +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> + +#include "win32/ip.h" +#include "win32/iphdr.h" +#define s6_addr32 s6_addr +#define herror perror +#endif + + +#include <stdint.h> +#include <string.h> +#include "dap_strfuncs.h" +#include "dap_chain_common.h" +#include "dap_chain_node.h" +#include "dap_chain.h" +#include "dap_chain_ledger.h" + + +#define DAP_CHAIN_NET_NAME_MAX 32 + +typedef enum dap_chain_net_state{ + NET_STATE_OFFLINE = 0, + NET_STATE_LINKS_PREPARE, + NET_STATE_LINKS_CONNECTING, + NET_STATE_LINKS_ESTABLISHED, + NET_STATE_ADDR_REQUEST, // Waiting for address assign + NET_STATE_SYNC_GDB, + NET_STATE_SYNC_CHAINS, + NET_STATE_ONLINE +} dap_chain_net_state_t; + +typedef struct dap_chain_net{ + struct { + dap_chain_net_id_t id; + dap_chain_cell_id_t cell_id; // Cell where the node is connected to. {{0}} if not celled(sharder) blockchain + char * name; + char * gdb_groups_prefix; + char * gdb_nodes_aliases; + char * gdb_nodes; + + dap_chain_t * chains; // double-linked list of chains + dap_ledger_t *ledger; + } pub; + uint8_t pvt[]; +} dap_chain_net_t; + + +int dap_chain_net_init(void); +void dap_chain_net_deinit(void); + +void dap_chain_net_load_all(); + +void s_net_set_go_sync(dap_chain_net_t * a_net); +int dap_chain_net_state_go_to(dap_chain_net_t * a_net, dap_chain_net_state_t a_new_state); + +inline static int dap_chain_net_start(dap_chain_net_t * a_net){ return dap_chain_net_state_go_to(a_net,NET_STATE_ONLINE); } +inline static int dap_chain_net_stop(dap_chain_net_t * a_net) { return dap_chain_net_state_go_to(a_net,NET_STATE_OFFLINE); } +inline static int dap_chain_net_links_establish(dap_chain_net_t * a_net) { return dap_chain_net_state_go_to(a_net,NET_STATE_LINKS_ESTABLISHED); } +inline static int dap_chain_net_sync_chains(dap_chain_net_t * a_net) { return dap_chain_net_state_go_to(a_net,NET_STATE_SYNC_CHAINS); } +inline static int dap_chain_net_sync_gdb(dap_chain_net_t * a_net) { return dap_chain_net_state_go_to(a_net,NET_STATE_SYNC_GDB); } +inline static int dap_chain_net_sync_all(dap_chain_net_t * a_net) { return dap_chain_net_state_go_to(a_net,NET_STATE_SYNC_CHAINS); }//NET_STATE_ONLINE + +void dap_chain_net_delete( dap_chain_net_t * a_net); +void dap_chain_net_proc_mempool (dap_chain_net_t * a_net); + +dap_chain_net_t * dap_chain_net_by_name( const char * a_name); +dap_chain_net_t * dap_chain_net_by_id( dap_chain_net_id_t a_id); +dap_chain_net_id_t dap_chain_net_id_by_name( const char * a_name); +dap_ledger_t * dap_chain_ledger_by_net_name( const char * a_net_name); + +dap_chain_t * dap_chain_net_get_chain_by_name( dap_chain_net_t * l_net, const char * a_name); + +dap_chain_node_addr_t * dap_chain_net_get_cur_addr( dap_chain_net_t * l_net); +uint64_t dap_chain_net_get_cur_addr_int(dap_chain_net_t * l_net); +dap_chain_cell_id_t * dap_chain_net_get_cur_cell( dap_chain_net_t * l_net); + +dap_list_t* dap_chain_net_get_link_node_list(dap_chain_net_t * l_net, bool a_is_only_cur_cell); +dap_list_t* dap_chain_net_get_node_list(dap_chain_net_t * l_net); + +void dap_chain_net_links_connect(dap_chain_net_t * a_net); + +typedef enum dap_chain_net_tx_search_type { + /// Search local, in memory, possible load data from drive to memory + TX_SEARCH_TYPE_LOCAL, + /// Do the request to the network if its not full node, search inside shard + TX_SEARCH_TYPE_CELL, + /// Do the request for unspent txs in cell + TX_SEARCH_TYPE_CELL_UNSPENT, + /// Do the search in whole network and request tx from others cells if need + TX_SEARCH_TYPE_NET, + /// Do the search in whole network but search only unspent + TX_SEARCH_TYPE_NET_UNSPENT +}dap_chain_net_tx_search_type_t; + +dap_chain_datum_tx_t * dap_chain_net_get_tx_by_hash(dap_chain_net_t * a_net, dap_chain_hash_fast_t * a_tx_hash, + dap_chain_net_tx_search_type_t a_search_type); + +dap_chain_node_role_t dap_chain_net_get_role(dap_chain_net_t * a_net); + +/** + * @brief dap_chain_net_get_gdb_group_mempool + * @param l_chain + * @return + */ +DAP_STATIC_INLINE char * dap_chain_net_get_gdb_group_mempool(dap_chain_t * l_chain) +{ + dap_chain_net_t * l_net = l_chain ? dap_chain_net_by_id(l_chain->net_id) : NULL; + if ( l_net ) { + const char c_mempool_group_str[]="mempool"; + return dap_strdup_printf("%s.chain-%s.%s",l_net->pub.gdb_groups_prefix,l_chain->name,c_mempool_group_str); + } + return NULL; +} + +dap_chain_t * dap_chain_net_get_chain_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type); +char * dap_chain_net_get_gdb_group_mempool_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type); +dap_chain_net_t **dap_chain_net_list(size_t *a_size); +dap_list_t * dap_chain_net_get_add_gdb_group(dap_chain_net_t * a_net, dap_chain_node_addr_t a_node_addr); + diff --git a/libdap-chain-net/dap_chain_net_bugreport.c b/libdap-chain-net/dap_chain_net_bugreport.c new file mode 100644 index 0000000000000000000000000000000000000000..8354eccbe2b0a97fe7332ce55f14a6f474a47750 --- /dev/null +++ b/libdap-chain-net/dap_chain_net_bugreport.c @@ -0,0 +1,116 @@ +/* + * Authors: + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2020 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> + +#include "dap_common.h" +#include "dap_string.h" +#include "dap_strfuncs.h" +#include "dap_file_utils.h" +#include "dap_config.h" + +#include "http_status_code.h" +#include "dap_http_simple.h" +#include "dap_enc_http.h" +#include "dap_chain_net_bugreport.h" + +#define LOG_TAG "chain_net_bugreport" + +#define BUGREPORT_URL "/bugreport" + +static int bugreport_write_to_file(byte_t *a_request_byte, size_t a_request_size) +{ + int l_ret = -2; + if(!a_request_byte || !a_request_size) + return -1; + char *l_dir_str = dap_strdup_printf("%s/var/bugreport", g_sys_dir_path); + dap_mkdir_with_parents(l_dir_str); + + const time_t l_timer = time(NULL); + struct tm l_tm; + localtime_r(&l_timer, &l_tm); + char *l_filename_str = dap_strdup_printf("%s/%02d-%02d-%02d_%02d:%02d:%02d.brt", l_dir_str, + l_tm.tm_year - 100, l_tm.tm_mon, l_tm.tm_mday, + l_tm.tm_hour, l_tm.tm_min, l_tm.tm_sec); + FILE *l_fp; + if((l_fp = fopen(l_filename_str, "wb")) != NULL) { + if(fwrite(a_request_byte, 1, a_request_size, l_fp) == a_request_size) + l_ret = 0; + else + l_ret = -3; + fclose(l_fp); + } + DAP_DELETE(l_filename_str); + DAP_DELETE(l_dir_str); + return l_ret; +} + +/** + * @brief bugreport_http_proc + * @param a_http_simple + * @param a_arg + */ +static void bugreport_http_proc(struct dap_http_simple *a_http_simple, void * a_arg) +{ + // data:text/html,<form action=http://192.168.100.92:8079/bugreport/ method=post><input name=a></form> + log_it(L_DEBUG, "bugreport_http_proc request"); + http_status_code_t * return_code = (http_status_code_t*) a_arg; + //if(dap_strcmp(cl_st->http->url_path, BUGREPORT_URL) == 0 ) + if(dap_strcmp(a_http_simple->http->action, "POST") == 0) + { + a_http_simple->request_byte; + a_http_simple->request_size; + a_http_simple->http->in_content_length; + if(!bugreport_write_to_file(a_http_simple->request_byte, a_http_simple->request_size)) { + a_http_simple->reply = dap_strdup_printf("Bug Report saved successfully)"); + } + else { + a_http_simple->reply = dap_strdup_printf("Bug Report not saved("); + } + a_http_simple->reply_size = strlen(a_http_simple->reply); + *return_code = Http_Status_OK; + + } else { + log_it(L_ERROR, "Wrong action '%s' for the request. Must be 'POST'", a_http_simple->http->action); + a_http_simple->reply = dap_strdup_printf("Wrong action '%s' for the request. Must be 'POST'", + a_http_simple->http->action); + a_http_simple->reply_size = strlen(a_http_simple->reply); + *return_code = Http_Status_NotFound; + } +} + +/** + * @brief dap_chain_net_bugreport_add_proc + * @param sh HTTP server instance + */ +void dap_chain_net_bugreport_add_proc(struct dap_http * sh) +{ + const char * url = BUGREPORT_URL; + dap_http_simple_proc_add(sh, url, 14096, bugreport_http_proc); +} + diff --git a/libdap-chain-net/dap_chain_net_bugreport.h b/libdap-chain-net/dap_chain_net_bugreport.h new file mode 100644 index 0000000000000000000000000000000000000000..00f4c2d9f6d8d5271b8f80a76591e107232a1020 --- /dev/null +++ b/libdap-chain-net/dap_chain_net_bugreport.h @@ -0,0 +1,27 @@ +/* + * Authors: + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2020 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "dap_http.h" + +void dap_chain_net_bugreport_add_proc(struct dap_http * sh); diff --git a/libdap-chain-net/dap_chain_net_remote.c b/libdap-chain-net/dap_chain_net_remote.c new file mode 100644 index 0000000000000000000000000000000000000000..65ecddbf07ec61c836dcb3f52cca6c7c35d12f88 --- /dev/null +++ b/libdap-chain-net/dap_chain_net_remote.c @@ -0,0 +1,27 @@ +/* +* Authors: +* Dmitriy Gerasimov <naeper@demlabs.net> +* Cellframe https://cellframe.net +* DeM Labs Inc. https://demlabs.net +* Copyright (c) 2017-2019 +* All rights reserved. + +This file is part of CellFrame SDK the open source project + +CellFrame SDK 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. + +CellFrame SDK 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 any CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "dap_common.h" +#include "dap_chain_net_remote.h" + +#define LOG_TAG "dap_chain_net_remote" diff --git a/libdap-chain-net/dap_chain_net_remote.h b/libdap-chain-net/dap_chain_net_remote.h new file mode 100644 index 0000000000000000000000000000000000000000..e3e35be850eb3ec77cc4bc2d27ee4867abc52230 --- /dev/null +++ b/libdap-chain-net/dap_chain_net_remote.h @@ -0,0 +1,31 @@ +/* +* Authors: +* Dmitriy Gerasimov <naeper@demlabs.net> +* Cellframe https://cellframe.net +* DeM Labs Inc. https://demlabs.net +* Copyright (c) 2017-2019 +* All rights reserved. + +This file is part of CellFrame SDK the open source project + +CellFrame SDK 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. + +CellFrame SDK 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 any CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include "dap_chain_common.h" + +typedef struct dap_chain_net_remote { + dap_chain_node_addr_t remote_addr; + char remote_channel; +} dap_chain_net_remote_t; diff --git a/libdap-chain-net/dap_chain_node.c b/libdap-chain-net/dap_chain_node.c new file mode 100644 index 0000000000000000000000000000000000000000..1f95a00da8600135fe02c8bcdb3db84648405c5a --- /dev/null +++ b/libdap-chain-net/dap_chain_node.c @@ -0,0 +1,329 @@ +/* + * Authors: + * Dmitriy A. Gerasimov <naeper@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#include <pthread.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#include "utlist.h" +#include "dap_hash.h" +#include "rand/dap_rand.h" +#include "dap_chain_net.h" +#include "dap_chain_global_db.h" +#include "dap_chain_node.h" + +#define LOG_TAG "chain_node" + +/** + * Generate node address by shard id + */ +dap_chain_node_addr_t* dap_chain_node_gen_addr(dap_chain_net_t * a_net,dap_chain_cell_id_t *shard_id) +{ + if(!shard_id) + return NULL; + dap_chain_node_addr_t *a_addr = DAP_NEW_Z(dap_chain_node_addr_t); + dap_chain_hash_fast_t a_hash; + dap_hash_fast(shard_id, sizeof(dap_chain_cell_id_t), &a_hash); + // first 4 bytes is last 4 bytes of shard id hash + memcpy(a_addr->raw, a_hash.raw + sizeof(a_hash.raw) - sizeof(uint64_t) / 2, sizeof(uint64_t) / 2); + // last 4 bytes is random + randombytes(a_addr->raw + sizeof(uint64_t) / 2, sizeof(uint64_t) / 2); + // for LITTLE_ENDIAN (Intel), do nothing, otherwise swap bytes + a_addr->uint64 = le64toh(a_addr->uint64); // a_addr->raw the same a_addr->uint64 + return a_addr; +} + +/** + * Check the validity of the node address by cell id + */ +bool dap_chain_node_check_addr(dap_chain_net_t * a_net,dap_chain_node_addr_t *addr, dap_chain_cell_id_t *shard_id) +{ + bool ret = false; + if(!addr || !shard_id) + ret= false; + + return ret; +} + +/** + * Register alias in base + */ +bool dap_chain_node_alias_register(dap_chain_net_t * a_net,const char *alias, dap_chain_node_addr_t *addr) +{ + char *a_key = strdup(alias); + dap_chain_node_addr_t *l_addr = DAP_NEW_Z(dap_chain_node_addr_t); + memcpy(l_addr,addr,sizeof (*l_addr)); +// char a_value[2 * sizeof(dap_chain_node_addr_t) + 1]; +// if(bin2hex(a_value, (const unsigned char *) addr, sizeof(dap_chain_node_addr_t)) == -1) +// return false; +// a_value[2 * sizeof(dap_chain_node_addr_t)] = '\0'; + bool res = dap_chain_global_db_gr_set( dap_strdup(a_key), l_addr, sizeof(dap_chain_node_addr_t) + , a_net->pub.gdb_nodes_aliases); + DAP_DELETE(l_addr); + DAP_DELETE(a_key); + return res; +} + +/** + * @brief dap_chain_node_alias_find + * @param alias + * @return + */ +dap_chain_node_addr_t * dap_chain_node_alias_find(dap_chain_net_t * a_net,const char *a_alias) +{ + size_t l_addr_size =0; + dap_chain_node_addr_t * l_addr = (dap_chain_node_addr_t *) + dap_chain_global_db_gr_get(a_alias, &l_addr_size, a_net->pub.gdb_nodes_aliases); + return l_addr; +} + +/** + * Delete alias from base + */ +bool dap_chain_node_alias_delete(dap_chain_net_t * a_net,const char *a_alias) +{ + char *a_key = strdup(a_alias); + bool res = dap_chain_global_db_gr_del(a_key, a_net->pub.gdb_nodes_aliases); + return res; +} + +/** + * Calculate size of struct dap_chain_node_info_t + */ +size_t dap_chain_node_info_get_size(dap_chain_node_info_t *node_info) +{ + if(!node_info) + return 0; + return (sizeof(dap_chain_node_info_t) + node_info->hdr.links_number * sizeof(dap_chain_node_addr_t)); +} + +/** + * @brief dap_chain_node_info_save + * @param node_info + * @return + */ +int dap_chain_node_info_save(dap_chain_net_t * a_net, dap_chain_node_info_t *a_node_info) +{ + if(!a_node_info || !a_node_info->hdr.address.uint64){ + log_it(L_ERROR,"Can't save node info: %s", a_node_info? "null address":"null object" ); + return -1; + } + char *l_key = dap_chain_node_addr_to_hash_str(&a_node_info->hdr.address); + + if(!l_key){ + log_it(L_ERROR,"Can't produce key to save node info "); + return -2; + } + //char *a_value = dap_chain_node_info_serialize(node_info, NULL); + size_t l_node_info_size = dap_chain_node_info_get_size(a_node_info); + dap_chain_node_info_t *l_node_info = DAP_NEW_Z_SIZE(dap_chain_node_info_t, l_node_info_size); + memcpy(l_node_info, a_node_info, sizeof (*a_node_info) ); + bool res = dap_chain_global_db_gr_set( dap_strdup(l_key), l_node_info, l_node_info_size, a_net->pub.gdb_nodes); + DAP_DELETE(l_key); + //DAP_DELETE(a_value); + return res?0:-3; +} + +/** + * Read node from base + */ +dap_chain_node_info_t* dap_chain_node_info_read( dap_chain_net_t * a_net,dap_chain_node_addr_t *l_address) +{ + char *l_key = dap_chain_node_addr_to_hash_str(l_address); + if(!l_key) { + log_it(L_WARNING,"Can't calculate hash of addr"); + return NULL; + } + size_t node_info_size = 0; + dap_chain_node_info_t *l_node_info; + // read node + l_node_info = (dap_chain_node_info_t *) dap_chain_global_db_gr_get(l_key, &node_info_size, a_net->pub.gdb_nodes); + + if(!l_node_info) { + log_it(L_ERROR, "node with key %s (addr " NODE_ADDR_FP_STR ") not found in base",l_key, NODE_ADDR_FP_ARGS(l_address)); + DAP_DELETE(l_key); + return NULL; + } + + size_t node_info_size_must_be = dap_chain_node_info_get_size(l_node_info); + if(node_info_size_must_be != node_info_size) { + log_it(L_ERROR, "Node has bad size in base=%u (must be %u)", node_info_size, node_info_size_must_be); + DAP_DELETE(l_node_info); + DAP_DELETE(l_key); + return NULL; + } + +// dap_chain_node_info_t *node_info = dap_chain_node_info_deserialize(str, (str) ? strlen(str) : 0); +// if(!node_info) { +// set_reply_text(str_reply, "node has invalid format in base"); +// } +// DAP_DELETE(str); + DAP_DELETE(l_key); + return l_node_info; +} + + +/** + * Serialize dap_chain_node_info_t + * size[out] - length of output string + * return data or NULL if error + */ +/*uint8_t* dap_chain_node_info_serialize(dap_chain_node_info_t *node_info, size_t *size) +{ + if(!node_info) + return NULL; + size_t node_info_size = dap_chain_node_info_get_size(node_info); + size_t node_info_str_size = 2 * node_info_size + 1; + uint8_t *node_info_str = DAP_NEW_Z_SIZE(uint8_t, node_info_str_size); + if(bin2hex(node_info_str, (const unsigned char *) node_info, node_info_size) == -1) { + DAP_DELETE(node_info_str); + return NULL; + } + + if(size) + *size = node_info_str_size; + return node_info_str; +}*/ + +/** + * Deserialize dap_chain_node_info_t + * size[in] - length of input string + * return data or NULL if error + */ +/*dap_chain_node_info_t* dap_chain_node_info_deserialize(uint8_t *node_info_str, size_t size) +{ + if(!node_info_str || size<=0) + return NULL; + dap_chain_node_info_t *node_info = DAP_NEW_Z_SIZE(dap_chain_node_info_t, (size / 2 + 1)); + if(hex2bin((char*) node_info, (const unsigned char *) node_info_str, size) == -1 || + (size / 2) != dap_chain_node_info_get_size(node_info)) { + log_it(L_ERROR, "node_info_deserialize - incorrect node_info size (%ld!=%ld)", + size / 2, dap_chain_node_info_get_size(node_info)); + DAP_DELETE(node_info); + return NULL; + } + return node_info; +}*/ + +int dap_chain_node_mempool_process(dap_chain_t *a_chain) +{ + char *l_gdb_group_mempool = NULL; + if (!a_chain) { + return -1; + } + l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(a_chain); + size_t l_objs_size = 0; + dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(l_gdb_group_mempool, &l_objs_size); + if (l_objs_size) { + for (size_t i = 0; i < l_objs_size; i++) { + dap_chain_datum_t *l_datum = (dap_chain_datum_t *)l_objs[i].value; + if (l_datum->header.type_id != DAP_CHAIN_DATUM_TX) { + continue; + } + if (a_chain->callback_datums_pool_proc(a_chain, &l_datum, 1) != 1) { + continue; + } // Delete processed objects + dap_chain_global_db_gr_del(dap_strdup(l_objs[i].key), l_gdb_group_mempool); + } + dap_chain_global_db_objs_delete(l_objs, l_objs_size); + } + DAP_DELETE(l_gdb_group_mempool); + return 0; +} + +void dap_chain_node_mempool_periodic(void *a_param) +{ + UNUSED(a_param); + size_t l_net_count; + bool l_mempool_auto; + dap_chain_net_t **l_net_list = dap_chain_net_list(&l_net_count); + for (int i = 0; i < l_net_count; i++) { + l_mempool_auto = dap_config_get_item_bool_default(g_config, "mempool", "auto_proc", false); + if (!l_mempool_auto) { + dap_chain_node_role_t l_role = dap_chain_net_get_role(l_net_list[i]); + switch (l_role.enums) { + case NODE_ROLE_ROOT: + case NODE_ROLE_MASTER: + case NODE_ROLE_ROOT_MASTER: + case NODE_ROLE_CELL_MASTER: + l_mempool_auto = true; + break; + default: + break; + } + } + if (l_mempool_auto) { + dap_chain_t *l_chain; + DL_FOREACH(l_net_list[i]->pub.chains, l_chain) { + for (uint16_t i = 0; i < l_chain->datum_types_count; i++) { + if (l_chain->datum_types[i] == CHAIN_TYPE_TX) { + dap_chain_node_mempool_process(l_chain); + } + } + } + } + } + DAP_DELETE(l_net_list); +} + +static void *s_mempool_timer = NULL; + +/** + * @brief dap_chain_node_mempool_init + * @return + */ +int dap_chain_node_mempool_init() +{ + s_mempool_timer = dap_interval_timer_create(DAP_CHAIN_NODE_MEMPOOL_INTERVAL, dap_chain_node_mempool_periodic, 0); + if (s_mempool_timer) { + return 0; + } else { + return -1; + } +} + +/** + * @brief dap_chain_node_mempool_deinit + */ +void dap_chain_node_mempool_deinit() +{ + if (s_mempool_timer) { + dap_interval_timer_delete(s_mempool_timer); + s_mempool_timer = NULL; + } +} + diff --git a/libdap-chain-net/dap_chain_node.h b/libdap-chain-net/dap_chain_node.h new file mode 100644 index 0000000000000000000000000000000000000000..1e5f0b6d7f4fe06e8c98923151ca79c10a256ea5 --- /dev/null +++ b/libdap-chain-net/dap_chain_node.h @@ -0,0 +1,146 @@ +/* + * Authors: + * Dmitriy A. Gerasimov <naeper@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#else +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> + +#include "win32/ip.h" +#include "win32/iphdr.h" +#endif + + +#include "dap_common.h" +#include "dap_chain_common.h" +#include "dap_chain_global_db.h" +#include "dap_chain.h" +#include "dap_chain_net.h" + +typedef struct dap_chain_net dap_chain_net_t; +/** + * Node Declaration request + * + */ + +#define DAP_CHAIN_NODE_DECL_REQ_INFO_SIZE 32 +typedef struct dap_chain_node_delc_req{ + dap_chain_node_addr_t node_address; + uint64_t create_ts; + union{ + uint8_t raw[DAP_CHAIN_NODE_DECL_REQ_INFO_SIZE]; + char str[DAP_CHAIN_NODE_DECL_REQ_INFO_SIZE]; + } info; +} DAP_ALIGN_PACKED dap_chain_decl_req_t; + +/** + * @struct dap_chain_node decl + * @details New node declaration + * + */ +#define DAP_CHAIN_NODE_DECL_ACCEPT_INFO_SIZE 32 +typedef struct dap_chain_node_decl{ + dap_chain_decl_req_t request; + uint64_t accept_ts; + struct in_addr accept_addr_v4; + struct in6_addr accept_addr_v6; + union{ + uint8_t raw[DAP_CHAIN_NODE_DECL_ACCEPT_INFO_SIZE]; + char str[DAP_CHAIN_NODE_DECL_ACCEPT_INFO_SIZE]; + } accept_info; +} DAP_ALIGN_PACKED dap_chain_node_decl_t; + +typedef struct dap_chain_node_info +{ + struct { + dap_chain_node_addr_t address; + dap_chain_cell_id_t cell_id; + uint32_t links_number; + struct in_addr ext_addr_v4; + struct in6_addr ext_addr_v6; + uint16_t ext_port; // Port thats node listening + char alias[256]; + } DAP_ALIGN_PACKED hdr; + dap_chain_node_addr_t links[]; // dap_chain_addr_t +} DAP_ALIGN_PACKED dap_chain_node_info_t; + +typedef struct dap_chain_node_publ{ + dap_chain_hash_fast_t decl_hash; + dap_chain_node_info_t node_info; +} DAP_ALIGN_PACKED dap_chain_node_publ_t; + +#define DAP_CHAIN_NODE_MEMPOOL_INTERVAL 1000 // milliseconds + +/** + * Calculate size of struct dap_chain_node_info_t + */ +size_t dap_chain_node_info_get_size(dap_chain_node_info_t *node_info); + +/** + * Serialize dap_chain_node_info_t + * size[out] - length of output string + * return data or NULL if error + */ +//uint8_t* dap_chain_node_info_serialize(dap_chain_node_info_t *node_info, size_t *size); + +/** + * Deserialize dap_chain_node_info_t + * size[in] - length of input string + * return data or NULL if error + */ +//dap_chain_node_info_t* dap_chain_node_info_deserialize(uint8_t *node_info_str, size_t size); + +/** + * Generate node addr by shard id + */ +dap_chain_node_addr_t* dap_chain_node_gen_addr(dap_chain_net_t * l_net,dap_chain_cell_id_t *a_cell_id); + +/** + * Check the validity of the node address by shard id + */ +bool dap_chain_node_check_addr(dap_chain_net_t * l_net,dap_chain_node_addr_t *addr, dap_chain_cell_id_t *a_cell_id); + +dap_chain_node_addr_t * dap_chain_node_alias_find(dap_chain_net_t * l_net,const char *alias); +bool dap_chain_node_alias_register(dap_chain_net_t * l_net,const char *alias, dap_chain_node_addr_t *addr); +bool dap_chain_node_alias_delete(dap_chain_net_t * l_net,const char *alias); + +int dap_chain_node_info_save(dap_chain_net_t * l_net,dap_chain_node_info_t *node_info); +dap_chain_node_info_t* dap_chain_node_info_read(dap_chain_net_t * l_net, dap_chain_node_addr_t *address); + +inline static char* dap_chain_node_addr_to_hash_str(dap_chain_node_addr_t *address) +{ + char *a_key = dap_chain_global_db_hash((const uint8_t*) address, sizeof(dap_chain_node_addr_t)); + return a_key; +} + +int dap_chain_node_mempool_process(dap_chain_t *a_chain); +int dap_chain_node_mempool_init(); +void dap_chain_node_mempool_deinit(); diff --git a/libdap-chain-net/dap_chain_node_cli.c b/libdap-chain-net/dap_chain_node_cli.c new file mode 100644 index 0000000000000000000000000000000000000000..ddbf5bdb16ee72b539f02eea05e1819313c343b2 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_cli.c @@ -0,0 +1,1139 @@ +/* + * Authors: + * Dmitriy A. Gerasimov <gerasimov.dmitriy@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +//#include <glib.h> + +#ifndef _WIN32 +#include <poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <arpa/inet.h> +//#include <unistd.h> // for close +#include <fcntl.h> +//#include <sys/poll.h> +//#include <sys/select.h> +#include <netinet/in.h> +#include <sys/un.h> +//#define closesocket close +//typedef int SOCKET; +//#define SOCKET_ERROR -1 // for win32 = (-1) +//#define INVALID_SOCKET -1 // for win32 = (SOCKET)(~0) +// for Windows +#else +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#endif + +#include <pthread.h> + +#include "iputils/iputils.h" +#include "dap_common.h" +#include "dap_config.h" +#include "dap_strfuncs.h" +#include "dap_list.h" +#include "dap_chain_node_cli_cmd.h" +#include "dap_chain_node_client.h" +#include "dap_chain_node_cli.h" + +//#include "dap_chain_node_cli.h" + +#define LOG_TAG "chain_node_cli" + +#define MAX_CONSOLE_CLIENTS 16 + +static SOCKET server_sockfd = -1; // network or local unix +uint32_t l_listen_port = 0; + +#ifdef _WIN32 + #define poll WSAPoll +#endif + +static dap_chain_node_cmd_item_t * s_commands = NULL; + + +/** + * Wait for data + * timeout - timeout in ms + * [Specifying a negative value in timeout means an infinite timeout.] + * [Specifying a timeout of zero causes poll() to return immediately, even if no file descriptors are ready.] + * return zero if the time limit expired + * return: >0 if data is present to read + * return: -1 if error + */ +static int s_poll( int socket, int timeout ) +{ + struct pollfd fds; + int res; + fds.fd = socket; + // POLLIN - received data + // POLLNVAL - closed the socket on our side + // POLLHUP - closed the socket on another side (does not work! Received POLLIN and the next reading returns 0 bytes) + fds.events = POLLIN; // | | POLLNVAL | POLLHUP | POLLERR | POLLPRI + res = poll(&fds, 1, timeout); + + // since POLLIN=(POLLRDNORM | POLLRDBAND), then maybe revents=POLLRDNORM + if(res == 1 && !(fds.revents & POLLIN)) //if(res==1 && fds.revents!=POLLIN) + return -1; + return res; +} + +/** + * Check socket for validity + */ +static bool is_valid_socket(SOCKET sock) +{ + struct pollfd fds; + fds.fd = sock; + fds.events = POLLIN; + // return: -1 err, 0 timeout, 1 waited + int count_desc = poll(&fds, 1, 0); + // error + if(count_desc == -1) + return false; + // event with an error code + if(count_desc > 0) + { + // feature of disconnection under Windows + // under Windows, with socket closed fds.revents=POLLHUP, in Unix fds.events = POLLIN + if(fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + return false; + // feature of disconnection under Unix (QNX) + // under Windows, with socket closed res = 0, in Unix res = -1 + char buf[2]; + long res = recv(sock, buf, 1, MSG_PEEK); // MSG_PEEK The data is treated as unread and the next recv() function shall still return this data. + if(res < 0) + return false; + // data in the buffer must be(count_desc>0), but read 0 bytes(res=0) + if(!res && (fds.revents & POLLIN)) + return false; + } + return true; +} + +/** + * Read from socket + * + * timeout in milliseconds + * return the number of read bytes (-1 err or -2 timeout) + */ +long s_recv(SOCKET sock, unsigned char *buf, size_t bufsize, int timeout) +{ + struct pollfd fds; + long res; + fds.fd = sock; + fds.events = POLLIN; // | POLLNVAL | POLLHUP | POLLERR | POLLPRI;// | POLLRDNORM;//POLLOUT | + res = poll(&fds, 1, timeout); + if(res == 1 && !(fds.revents & POLLIN)) + return -1; + if(!res) // timeout + return -2; + if(res < 1) { + return -1; + } + // res = read(sock, (char*) buf, bufsize); + res = recv(sock, (char*) buf, bufsize, 0); //MSG_WAITALL + if(res <= 0) { //EINTR=4 ENOENT=2 EINVAL=22 ECONNRESET=254 + printf("[s_recv] recv()=%ld errno=%d\n", res, errno); + } + return res; +} + +/** + * Reading from the socket till arrival the specified string + * + * stop_str - string to which reading will continue + * del_stop_str - удалÑÑ‚ÑŒ ли Ñтроку Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка в конце + * timeout - in ms + * return: string (if waited for final characters) or NULL, if the string requires deletion + */ +char* s_get_next_str( SOCKET nSocket, int *dwLen, const char *stop_str, bool del_stop_str, int timeout ) +{ + bool bSuccess = false; + long nRecv = 0; // count of bytes received + size_t stop_str_len = (stop_str) ? strlen(stop_str) : 0; + // if there is nothing to look for + if(!stop_str_len) + return NULL; + size_t lpszBuffer_len = 256; + char *lpszBuffer = DAP_NEW_Z_SIZE(char, lpszBuffer_len); + // received string will not be larger than MAX_REPLY_LEN + + while(1) //nRecv < MAX_REPLY_LEN) + { + // read one byte + long ret = s_recv(nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout); + //int ret = recv(nSocket,lpszBuffer+nRecv,1, 0); + if(ret <= 0) + { + break; + } + nRecv += ret; + //printf("**debug** socket=%d read %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv)); + while((nRecv + 1) >= (long) lpszBuffer_len) + { + lpszBuffer_len *= 2; + lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len); + } + // search for the required string + if(nRecv >= (long) stop_str_len) { + // found the required string + if(!strncasecmp(lpszBuffer + nRecv - stop_str_len, stop_str, stop_str_len)) { + bSuccess = true; + break; + } + } + }; + + // end reading + + if(bSuccess) { + // delete the searched string + if(del_stop_str) { + lpszBuffer[nRecv - (long) stop_str_len] = '\0'; + if(dwLen) + *dwLen =(int) nRecv - (int) stop_str_len; + } + else { + lpszBuffer[nRecv] = '\0'; + if(dwLen) + *dwLen = (int) nRecv; + } + lpszBuffer = DAP_REALLOC(lpszBuffer,(size_t) *dwLen + 1); + return lpszBuffer; + } + + // in case of an error or missing string + + if(dwLen) + *dwLen = 0; + + free(lpszBuffer); + + return NULL; +} + +/** + * threading function for processing a request from a client + */ +static void* thread_one_client_func(void *args) +{ + SOCKET newsockfd = (SOCKET) (intptr_t) args; + log_it(L_INFO, "new connection sockfd=%d", newsockfd); + + int str_len, marker = 0; + int timeout = 5000; // 5 sec + int argc = 0; + dap_list_t *cmd_param_list = NULL; + while(1) + { + // wait data from client + int is_data = s_poll(newsockfd, timeout); + // timeout + if(!is_data) + continue; + // error (may be socket closed) + if(is_data < 0) + break; + + int is_valid = is_valid_socket(newsockfd); + if(!is_valid) + { + break; + } + // receiving http header + char *str_header = s_get_next_str(newsockfd, &str_len, "\r\n", true, timeout); + // bad format + if(!str_header) + break; + if(str_header && strlen(str_header) == 0) { + marker++; + if(marker == 1) + continue; + } + // filling parameters of command + if(marker == 1) { + cmd_param_list = dap_list_append(cmd_param_list, str_header); + //printf("g_list_append argc=%d command=%s ", argc, str_header); + argc++; + } + else + free(str_header); + if(marker == 2) { + dap_list_t *list = cmd_param_list; + // form command + unsigned int argc = dap_list_length(list); + // command is found + if(argc >= 1) { + int l_verbose = 0; + char *cmd_name = list->data; + list = dap_list_next(list); + // execute command + char *str_cmd = dap_strdup_printf("%s", cmd_name); + dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_find(cmd_name); + int res = -1; + char *str_reply = NULL; + if(l_cmd){ + while(list) { + char *str_cmd_prev = str_cmd; + str_cmd = dap_strdup_printf("%s;%s", str_cmd, list->data); + list = dap_list_next(list); + DAP_DELETE(str_cmd_prev); + } + log_it(L_INFO, "execute command=%s", str_cmd); + // exec command + + char **l_argv = dap_strsplit(str_cmd, ";", -1); + // Call the command function + if(l_cmd && l_argv && l_cmd->func) + res = (*(l_cmd->func))(argc, l_argv, l_cmd->arg_func, &str_reply); + else if (l_cmd){ + log_it(L_WARNING,"NULL arguments for input for command \"%s\"", str_cmd); + }else { + log_it(L_WARNING,"No function for command \"%s\" but it registred?!", str_cmd); + } + // find '-verbose' command + l_verbose = dap_chain_node_cli_find_option_val(l_argv, 1, argc, "-verbose", NULL); + dap_strfreev(l_argv); + } else { + str_reply = dap_strdup_printf("can't recognize command=%s", str_cmd); + log_it(L_ERROR, str_reply); + } + char *reply_body; + if(l_verbose) + reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, (str_reply) ? str_reply : ""); + else + reply_body = dap_strdup_printf("%d\r\n%s\r\n", res, (str_reply) ? str_reply : ""); + // return the result of the command function + char *reply_str = dap_strdup_printf("HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n\r\n" + "%s", + strlen(reply_body), reply_body); + /*int ret = */ send(newsockfd, reply_str, strlen(reply_str) ,0); + DAP_DELETE(str_reply); + DAP_DELETE(reply_str); + DAP_DELETE(reply_body); + + DAP_DELETE(str_cmd); + } + dap_list_free_full(cmd_param_list, free); + break; + } + } + // close connection + int cs = closesocket(newsockfd); + log_it(L_INFO, "close connection=%d sockfd=%d", cs, newsockfd); + return NULL; +} + +#ifdef _WIN32 + +char *p_get_next_str( HANDLE hPipe, int *dwLen, const char *stop_str, bool del_stop_str, int timeout ) +{ + bool bSuccess = false; + long nRecv = 0; // count of bytes received + size_t stop_str_len = (stop_str) ? strlen(stop_str) : 0; + // if there is nothing to look for + + if(!stop_str_len) + return NULL; + + size_t lpszBuffer_len = 256; + char *lpszBuffer = DAP_NEW_Z_SIZE(char, lpszBuffer_len); + // received string will not be larger than MAX_REPLY_LEN + + while( 1 ) //nRecv < MAX_REPLY_LEN) + { + long ret = 0; + // read one byte +// long ret = s_recv( nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout); + + bSuccess = ReadFile( hPipe, lpszBuffer + nRecv, + lpszBuffer_len - nRecv, (LPDWORD)&ret, NULL ); + + //int ret = recv(nSocket,lpszBuffer+nRecv,1, 0); + if ( ret <= 0 || !bSuccess ) + break; + + nRecv += ret; + //printf("**debug** socket=%d read %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv)); + + while((nRecv + 1) >= (long) lpszBuffer_len) + { + lpszBuffer_len *= 2; + lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len); + } + + // search for the required string + if(nRecv >= (long) stop_str_len) { + // found the required string + if(!strncasecmp(lpszBuffer + nRecv - stop_str_len, stop_str, stop_str_len)) { + bSuccess = true; + break; + } + } + }; + + // end reading + + if(bSuccess) { + // delete the searched string + if(del_stop_str) { + lpszBuffer[nRecv - (long) stop_str_len] = '\0'; + if(dwLen) + *dwLen =(int) nRecv - (int) stop_str_len; + } + else { + lpszBuffer[nRecv] = '\0'; + if(dwLen) + *dwLen = (int) nRecv; + } + lpszBuffer = DAP_REALLOC(lpszBuffer,(size_t) *dwLen + 1); + return lpszBuffer; + } + + // in case of an error or missing string + + if(dwLen) + *dwLen = 0; + + free(lpszBuffer); + + return NULL; +} + + +/** + * threading function for processing a request from a client + */ +static void *thread_pipe_client_func( void *args ) +{ + HANDLE hPipe = (HANDLE)args; + +// SOCKET newsockfd = (SOCKET) (intptr_t) args; + log_it(L_INFO, "new connection pipe = %X", hPipe ); + + int str_len, marker = 0; + int timeout = 5000; // 5 sec + int argc = 0; + + dap_list_t *cmd_param_list = NULL; + + while( 1 ) + { + // wait data from client +// int is_data = s_poll( newsockfd, timeout ); + // timeout +// if(!is_data) +// continue; + // error (may be socket closed) +// if(is_data < 0) +// break; + +// int is_valid = is_valid_socket(newsockfd); +// if(!is_valid) +// { +// break; +// } + + // receiving http header + char *str_header = p_get_next_str( hPipe, &str_len, "\r\n", true, timeout ); + + // bad format + if(!str_header) + break; + + if ( str_header && strlen(str_header) == 0) { + marker++; + if(marker == 1) + continue; + } + + // filling parameters of command + if ( marker == 1 ) { + cmd_param_list = dap_list_append( cmd_param_list, str_header ); + //printf("g_list_append argc=%d command=%s ", argc, str_header); + argc ++; + } + else + free( str_header ); + + if ( marker == 2 ) { + + dap_list_t *list = cmd_param_list; + // form command + + unsigned int argc = dap_list_length( list ); + // command is found + + if ( argc >= 1) { + + int l_verbose = 0; + char *cmd_name = list->data; + list = dap_list_next( list ); + + // execute command + char *str_cmd = dap_strdup_printf( "%s", cmd_name ); + dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_find( cmd_name ); + int res = -1; + char *str_reply = NULL; + + if ( l_cmd ) { + + while( list ) { + str_cmd = dap_strdup_printf( "%s;%s", str_cmd, list->data ); + list = dap_list_next(list); + } + + log_it(L_INFO, "execute command = %s", str_cmd ); + // exec command + + char **l_argv = dap_strsplit( str_cmd, ";", -1 ); + // Call the command function + + if ( l_cmd && l_argv && l_cmd->func ) + res = (* (l_cmd->func))( argc, l_argv, NULL, &str_reply ); + + else if ( l_cmd ) { + log_it(L_WARNING,"NULL arguments for input for command \"%s\"", str_cmd ); + }else { + log_it(L_WARNING,"No function for command \"%s\" but it registred?!", str_cmd ); + } + + // find '-verbose' command + l_verbose = dap_chain_node_cli_find_option_val( l_argv, 1, argc, "-verbose", NULL ); + dap_strfreev( l_argv ); + + } else { + str_reply = dap_strdup_printf("can't recognize command = %s", str_cmd ); + log_it( L_ERROR, str_reply ); + } + + char *reply_body; + + if(l_verbose) + reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, (str_reply) ? str_reply : ""); + else + reply_body = dap_strdup_printf("%d\r\n%s\r\n", res, (str_reply) ? str_reply : ""); + + // return the result of the command function + char *reply_str = dap_strdup_printf( "HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n\r\n" + "%s", + strlen(reply_body), reply_body ); + + int ret;// = send( newsockfd, reply_str, strlen(reply_str) ,0 ); + + WriteFile( hPipe, reply_str, strlen(reply_str), (LPDWORD)&ret, NULL ); + + DAP_DELETE(str_reply); + DAP_DELETE(reply_str); + DAP_DELETE(reply_body); + + DAP_DELETE(str_cmd); + } + dap_list_free_full(cmd_param_list, free); + break; + } + } + + // close connection +// int cs = closesocket(newsockfd); + + log_it( L_INFO, "close connection pipe = %X", hPipe ); + + FlushFileBuffers( hPipe ); + DisconnectNamedPipe( hPipe ); + CloseHandle( hPipe ); + + return NULL; +} + + +/** + * main threading server function pipe win32 + */ +static void* thread_pipe_func( void *args ) +{ + BOOL fConnected = FALSE; + pthread_t threadId; + HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL; + static const char *cPipeName = "\\\\.\\pipe\\node_cli.pipe"; + + for (;;) + { +/// printf( "\nPipe Server: Main thread awaiting client connection on %s\n", lpszPipename ); + + hPipe = CreateNamedPipe( + cPipeName, // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + PIPE_TYPE_MESSAGE | // message type pipe + PIPE_READMODE_MESSAGE | // message-read mode + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + 4096, // output buffer size + 4096, // input buffer size + 0, // client time-out + NULL ); // default security attribute + + if ( hPipe == INVALID_HANDLE_VALUE ) { + log_it( L_ERROR, "CreateNamedPipe failed, GLE = %d.\n", GetLastError() ); + return NULL; + } + + fConnected = ConnectNamedPipe( hPipe, NULL ) ? TRUE : ( GetLastError() == ERROR_PIPE_CONNECTED ); + + if ( fConnected ) + { + log_it( L_INFO, "Client %X connected, creating a processing thread.\n", hPipe ); + + pthread_create( &threadId, NULL, thread_pipe_client_func, hPipe ); + pthread_detach( threadId ); + } + else + CloseHandle( hPipe ); + } + + return NULL; +} +#endif + +/** + * main threading server function + */ +static void* thread_main_func(void *args) +{ + SOCKET sockfd = (SOCKET) (intptr_t) args; + SOCKET newsockfd; + + log_it( L_INFO, "Server start socket = %s", dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path") ); + // wait of clients + while(1) + { + pthread_t threadId; + struct sockaddr_in peer; + socklen_t size = sizeof(peer); + // received a new connection request + if((newsockfd = accept(sockfd, (struct sockaddr*) &peer, &size)) == (SOCKET) -1) { + log_it(L_ERROR, "new connection break newsockfd=%d", newsockfd); + break; + } + // create child thread for a client connection + pthread_create(&threadId, NULL, thread_one_client_func, (void*) (intptr_t) newsockfd); + // in order to thread not remain in state "dead" after completion + pthread_detach(threadId); + }; + // close connection + int cs = closesocket(sockfd); + log_it(L_INFO, "Exit server thread=%d socket=%d", cs, sockfd); + return NULL; +} + +/** + * Write text to reply string + */ +void dap_chain_node_cli_set_reply_text(char **str_reply, const char *str, ...) +{ + if(str_reply) { + if(*str_reply) { + assert(! *str_reply ); + DAP_DELETE(*str_reply); + *str_reply = NULL; + } + va_list args; + va_start(args, str); + *str_reply = dap_strdup_vprintf(str, args); //*str_reply = dap_strdup(str); + va_end(args); + } +} + +/** + * find option value + * + * return index of string in argv, or 0 if not found + */ +int dap_chain_node_cli_find_option_val( char** argv, int arg_start, int arg_end, const char *opt_name, const char **opt_value) +{ + int arg_index = arg_start; + const char *arg_string; + + while(arg_index < arg_end) + { + char * l_argv_cur = argv[arg_index]; + arg_string = l_argv_cur; + // find opt_name + if(arg_string && opt_name && arg_string[0] && opt_name[0] && !strcmp(arg_string, opt_name)) { + // find opt_value + if(opt_value) { + arg_string = argv[++arg_index]; + if(arg_string) { + *opt_value = arg_string; + return arg_index; + } + } + else + // need only opt_name + return arg_index; + } + arg_index++; + } + return 0; +} + +/** + * @brief s_cmd_item_create + * @param a_name + * @param func + * @param a_arg_func + * @param doc + * @param doc_ex + * @return + */ +void dap_chain_node_cli_cmd_item_create(const char * a_name, cmdfunc_t *a_func, void *a_arg_func, const char *a_doc, const char *a_doc_ex) +{ + dap_chain_node_cmd_item_t *l_cmd_item = DAP_NEW_Z(dap_chain_node_cmd_item_t); + dap_snprintf(l_cmd_item->name,sizeof (l_cmd_item->name),"%s",a_name); + l_cmd_item->doc = strdup( a_doc); + l_cmd_item->doc_ex = strdup( a_doc_ex); + l_cmd_item->func = a_func; + l_cmd_item->arg_func = a_arg_func; + HASH_ADD_STR(s_commands,name,l_cmd_item); + log_it(L_DEBUG,"Added command %s",l_cmd_item->name); +} + +/** + * @brief dap_chain_node_cli_command_get_first + * @return + */ +dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_get_first() +{ + return s_commands; +} + +/** + * @brief dap_chain_node_cli_command_find + * @param a_name + * @return + */ +dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_find(const char *a_name) +{ + dap_chain_node_cmd_item_t *l_cmd_item = NULL; + HASH_FIND_STR(s_commands,a_name,l_cmd_item); + return l_cmd_item; +} + + +/** + * Initialization of the server side of the interaction + * with the console kelvin-node-cli + * + * return 0 if OK, -1 error + */ +int dap_chain_node_cli_init(dap_config_t * g_config) +{ +#ifndef _WIN32 + struct sockaddr_un l_server_addr={0}; + l_server_addr.sun_family = AF_UNIX; + snprintf(l_server_addr.sun_path,sizeof(l_server_addr.sun_path), "%s", dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path") ); +#else + pthread_t threadId; +#endif + + struct sockaddr_in server_addr; + SOCKET sockfd = -1; + + bool l_conserver_enabled = dap_config_get_item_bool_default( g_config, "conserver", "enabled", true ); + + if ( !l_conserver_enabled ) { + + log_it( L_WARNING, "Console Server is dissabled." ); + return 0; + } + +#ifdef __WIN32 + WSADATA wsaData; + int ret = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (ret != 0) { + log_it(L_CRITICAL, "Couldn't init Winsock DLL, error: %d", ret); + return 2; + } +#endif + + dap_chain_node_cli_cmd_item_create("global_db", com_global_db, NULL, "Work with global database", + "global_db cells add -cell <cell id> \n" + "global_db flush \n\n" +// "global_db wallet_info set -addr <wallet address> -cell <cell id> \n\n" + ); + + dap_chain_node_cli_cmd_item_create("node", com_node, NULL, "Work with node", + "node add -net <net name> -addr {<node address> | -alias <node alias>} {-port <port>} -cell <cell id> {-ipv4 <ipv4 external address> | -ipv6 <ipv6 external address>}\n\n" + "node del -net <net name> -addr <node address> | -alias <node alias>\n\n" + "node link {add|del} -net <net name> {-addr <node address> | -alias <node alias>} -link <node address>\n\n" + "node alias -addr <node address> -alias <node alias>\n\n" + "node connect {<node address> | -alias <node alias> | auto}\n\n" + "node handshake {<node address> | -alias <node alias>}\n" + "node dump -net <net name> [ -addr <node address> | -alias <node alias>] [-full]\n\n" + ); + dap_chain_node_cli_cmd_item_create ("ping", com_ping, NULL, "Send ICMP ECHO_REQUEST to network hosts", + "ping [-c <count>] host\n"); + dap_chain_node_cli_cmd_item_create ("traceroute", com_traceroute, NULL, "Print the hops and time of packets trace to network host", + "traceroute host\n"); + dap_chain_node_cli_cmd_item_create ("tracepath", com_tracepath, NULL, "Traces path to a network host along this path", + "tracepath host\n"); + dap_chain_node_cli_cmd_item_create ("help", com_help, NULL, "Description of command parameters", + "help [<command>]\n" + "\tObtain help for <command> or get the total list of the commands\n" + ); + dap_chain_node_cli_cmd_item_create ("?", com_help, NULL, "Synonym for \"help\"", + "? [<command>]\n" + "\tObtain help for <command> or get the total list of the commands\n" + ); + dap_chain_node_cli_cmd_item_create("wallet", com_tx_wallet, NULL, "Wallet operations", + "wallet [new -w <wallet_name> [-sign <sign_type>] [-restore <hex value>] [-net <net_name>] [-force]| list | info -addr <addr> -w <wallet_name> -net <net_name>]\n"); + + // Token commands + dap_chain_node_cli_cmd_item_create ("token_decl_update", com_token_decl_update, NULL, "Token declaration update", + "\nPrivate token declaration update\n" + "\t token_decl_update -net <net name> -chain <chain name> -token <token ticker> -type private -flags [<Flag 1>][,<Flag 2>]...[,<Flag N>]... [-<Param name 1> <Param Value 1>] [-Param name 2> <Param Value 2>] ...[-<Param Name N> <Param Value N>]\n" + "\t Update private token <token ticker> for <netname>:<chain name> with flags <Flag 1>,<Flag2>...<Flag N>" + "\t and custom parameters list <Param 1>, <Param 2>...<Param N>." + "\n" + "==Flags==" + "\t ALL_BLOCKED:\t Blocked all permissions, usefull add it first and then add allows what you want to allow\n" + "\t ALL_ALLOWED:\t Allowed all permissions if not blocked them. Be careful with this mode\n" + "\t ALL_FROZEN:\t All permissions are temprorary frozen\n" + "\t ALL_UNFROZEN:\t Unfrozen permissions\n" + "\t STATIC_ALL:\t No token manipulations after declarations at all. Token declares staticly and can't variabed after\n" + "\t STATIC_FLAGS:\t No token manipulations after declarations with flags\n" + "\t STATIC_PERMISSIONS_ALL:\t No all permissions lists manipulations after declarations\n" + "\t STATIC_PERMISSIONS_DATUM_TYPE:\t No datum type permissions lists manipulations after declarations\n" + "\t STATIC_PERMISSIONS_TX_SENDER:\t No tx sender permissions lists manipulations after declarations\n" + "\t STATIC_PERMISSIONS_TX_RECEIVER:\t No tx receiver permissions lists manipulations after declarations\n" + "\n" + "==Params==\n" + "General:\n" + "\t -flags_set <value>:\t Set list of flags from <value> to token declaration\n" + "\t -flags_unset <value>:\t Unset list of flags from <value> from token declaration\n" + "\t -total_supply <value>:\t Set total supply - emission's maximum - to the <value>\n" + "\t -signs_valid <value>:\t Set valid signatures count's minimum\n" + "\t -signs_add <value>:\t Add signature's pkey fingerprint to the list of owners\n" + "\t -signs_remove <value>:\t Remove signature's pkey fingerprint from the owners\n" + "\nDatum type allowed/blocked updates:\n" + "\t -datum_type_allowed_add <value>:\t Add allowed datum type(s)\n" + "\t -datum_type_allowed_remove <value>:\t Remove datum type(s) from allowed\n" + "\t -datum_type_allowed_clear:\t Remove all datum types from allowed\n" + "\t -datum_type_blocked_add <value>:\t Add blocked datum type(s)\n" + "\t -datum_type_blocked_remove <value>:\t Remove datum type(s) from blocked\n" + "\t -datum_type_blocked_clear:\t Remove all datum types from blocked\n" + "\nTx receiver addresses allowed/blocked updates:\n" + "\t -tx_receiver_allowed_add <value>:\t Add allowed tx receiver(s)\n" + "\t -tx_receiver_allowed_remove <value>:\t Remove tx receiver(s) from allowed\n" + "\t -tx_receiver_allowed_clear:\t Remove all tx receivers from allowed\n" + "\t -tx_receiver_blocked_add <value>:\t Add blocked tx receiver(s)\n" + "\t -tx_receiver_blocked_remove <value>:\t Remove tx receiver(s) from blocked\n" + "\t -tx_receiver_blocked_clear:\t Remove all tx receivers from blocked\n" + "\nTx sender addresses allowed/blocked updates:\n" + "\t -tx_sender_allowed_add <value>:\t Add allowed tx sender(s)\n" + "\t -tx_sender_allowed_remove <value>:\t Remove tx sender(s) from allowed\n" + "\t -tx_sender_allowed_clear:\t Remove all tx senders from allowed\n" + "\t -tx_sender_blocked_add <value>:\t Add allowed tx sender(s)\n" + "\t -tx_sender_blocked_remove <value>:\t Remove tx sender(s) from blocked\n" + "\t -tx_sender_blocked_clear:\t Remove all tx sender(s) from blocked\n" + "\n" + ); + // Token commands + dap_chain_node_cli_cmd_item_create ("token_decl", com_token_decl, NULL, "Token declaration", + "Simple token declaration:\n" + "\t token_decl -net <net name> -chain <chain name> -token <token ticker> -total_supply <total supply> -signs_total <sign total> -signs_emission <signs for emission> -certs <certs list>\n" + "\t\ Declare new simple token for <netname>:<chain name> with ticker <token ticker>, maximum emission <total supply> and <signs for emission> from <signs total> signatures on valid emission\n" + "\nExtended private token declaration\n" + "\t token_decl -net <net name> -chain <chain name> -token <token ticker> -type private -flags [<Flag 1>][,<Flag 2>]...[,<Flag N>]... [-<Param name 1> <Param Value 1>] [-Param name 2> <Param Value 2>] ...[-<Param Name N> <Param Value N>]\n" + "\t Declare new token for <netname>:<chain name> with ticker <token ticker>, flags <Flag 1>,<Flag2>...<Flag N>" + "\t and custom parameters list <Param 1>, <Param 2>...<Param N>." + "\n" + "==Flags==" + "\t ALL_BLOCKED:\t Blocked all permissions, usefull add it first and then add allows what you want to allow\n" + "\t ALL_ALLOWED:\t Allowed all permissions if not blocked them. Be careful with this mode\n" + "\t ALL_FROZEN:\t All permissions are temprorary frozen\n" + "\t ALL_UNFROZEN:\t Unfrozen permissions\n" + "\t STATIC_ALL:\t No token manipulations after declarations at all. Token declares staticly and can't variabed after\n" + "\t STATIC_FLAGS:\t No token manipulations after declarations with flags\n" + "\t STATIC_PERMISSIONS_ALL:\t No all permissions lists manipulations after declarations\n" + "\t STATIC_PERMISSIONS_DATUM_TYPE:\t No datum type permissions lists manipulations after declarations\n" + "\t STATIC_PERMISSIONS_TX_SENDER:\t No tx sender permissions lists manipulations after declarations\n" + "\t STATIC_PERMISSIONS_TX_RECEIVER:\t No tx receiver permissions lists manipulations after declarations\n" + "\n" + "==Params==\n" + "General:\n" + "\t -flags <value>:\t List of flags from <value> to token declaration\n" + "\t -total_supply <value>:\t Set total supply - emission's maximum - to the <value>\n" + "\t -signs_valid <value>:\t Set valid signatures count's minimum\n" + "\t -signs <value>:\t Signature's fingerprint list\n" + "\nDatum type allowed/blocked:\n" + "\t -datum_type_allowed <value>:\t Set allowed datum type(s)\n" + "\t -datum_type_blocked <value>:\t Set blocked datum type(s)\n" + "\nTx receiver addresses allowed/blocked:\n" + "\t -tx_receiver_allowed <value>:\t Set allowed tx receiver(s)\n" + "\t -tx_receiver_blocked <value>:\t Set blocked tx receiver(s)\n" + "\nTx sender addresses allowed/blocked:\n" + "\t -tx_sender_allowed <value>:\t Set allowed tx sender(s)\n" + "\t -tx_sender_blocked <value>:\t Set allowed tx sender(s)\n" + "\n" + ); + /// -------- General tsd types ---- + // Flags set/unsed + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_SET_FLAGS 0x0001 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_UNSET_FLAGS 0x0002 + + // Total supply limits + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY 0x0003 + + // Set total signs count value to set to be valid + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID 0x0004 + + // Add owner signature's pkey fingerprint + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_ADD 0x0006 + + // Remove owner signature by pkey fingerprint + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_REMOVE 0x0007 + + + + /// ------- Permissions list flags, grouped by update-remove-clear operations -------- + // Allowed datum types list update, remove or clear + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_UPDATE 0x0010 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_REMOVE 0x0011 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_CLEAR 0x0012 + + // Blocked datum types list update, remove or clear + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_UPDATE 0x0013 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_REMOVE 0x0014 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_CLEAR 0x0015 + + //Allowed tx receiver addres list update, remove or clear + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_UPDATE 0x0014 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_REMOVE 0x0015 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_CLEAR 0x0016 + + //Blocked tx receiver addres list update, remove or clear + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_UPDATE 0x0017 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_REMOVE 0x0018 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_CLEAR 0x0019 + + + //Allowed tx sender addres list update, remove or clear + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_UPDATE 0x0020 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_REMOVE 0x0021 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_CLEAR 0x0022 + + //Blocked tx sender addres list update, remove or clear + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_UPDATE 0x0023 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_REMOVE 0x0024 + #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_CLEAR 0x0025 + + + dap_chain_node_cli_cmd_item_create ("token_decl_sign", com_token_decl_sign, NULL, "Token declaration add sign", + "token_decl_sign -net <net name> -chain <chain name> -datum <datum_hash> -certs <certs list>\n" + "\t Sign existent <datum hash> in mempool with <certs list>\n" + ); + + dap_chain_node_cli_cmd_item_create ("token_emit", com_token_emit, NULL, "Token emission", + "token_emit -net <net name> -chain_emission <chain for emission> -chain_base_tx <chain for base tx> -addr <addr> -token <token ticker> -certs <cert> -emission_value <val>\n"); + + dap_chain_node_cli_cmd_item_create ("mempool_list", com_mempool_list, NULL, "List mempool entries for selected chain network and chain id", + "mempool_list -net <net name> -chain <chain name>\n"); + + dap_chain_node_cli_cmd_item_create ("mempool_proc", com_mempool_proc, NULL, "Proc mempool entries for selected chain network and chain id", + "mempool_proc -net <net name> -chain <chain name>\n"); + + dap_chain_node_cli_cmd_item_create ("mempool_delete", com_mempool_delete, NULL, "Delete datum with hash <datum hash>", + "mempool_delete -net <net name> -chain <chain name> -datum <datum hash>\n"); + + dap_chain_node_cli_cmd_item_create ("mempool_add_ca", com_mempool_add_ca, NULL, + "Add pubic certificate into the mempool to prepare its way to chains", + "mempool_add_ca -net <net name> [-chain <chain name>] -ca_name <Certificate name>\n"); + + + // Transaction commands + dap_chain_node_cli_cmd_item_create ("tx_create", com_tx_create, NULL, "Make transaction", + "tx_create -net <net name> -chain <chain name> -from_wallet <name> -to_addr <addr> -token <token ticker> -value <value> [-fee <addr> -value_fee <val>]\n" ); + dap_chain_node_cli_cmd_item_create ("tx_cond_create", com_tx_cond_create, NULL, "Make cond transaction", + "tx_cond_create todo\n" ); + dap_chain_node_cli_cmd_item_create ("tx_verify", com_tx_verify, NULL, "Verifing transaction", + "tx_verify -wallet <wallet name> \n" ); + + // Transaction history + dap_chain_node_cli_cmd_item_create("tx_history", com_tx_history, NULL, "Transaction history (for address or by hash)", + "tx_history [-addr <addr> | -w <wallet name> | -tx <tx_hash>] -net <net name> -chain <chain name>\n"); + + // vpn client + dap_chain_node_cli_cmd_item_create ("vpn_client", com_vpn_client, NULL, "VPN client control", + "vpn_client [start -addr <server address> -port <server port>| stop | status]\n"); + + + // Log + dap_chain_node_cli_cmd_item_create ("print_log", com_print_log, NULL, "Print log info", + "print_log [ts_after <timestamp >] [limit <line numbers>]\n" ); + + // Statisticss + dap_chain_node_cli_cmd_item_create("stats", com_stats, NULL, "Print statistics", + "stats cpu"); + + // Exit + dap_chain_node_cli_cmd_item_create ("exit", com_exit, NULL, "Stop application and exit", + "exit\n" ); + + // create thread for waiting of clients + pthread_t l_thread_id; + + l_listen_port = dap_config_get_item_uint16_default( g_config, "conserver", "listen_port_tcp",0); + + const char * l_listen_unix_socket_path = dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path"); + const char * l_listen_unix_socket_permissions_str = dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_permissions"); + mode_t l_listen_unix_socket_permissions = 0770; + + if ( l_listen_unix_socket_path && l_listen_unix_socket_permissions ) { + if ( l_listen_unix_socket_permissions_str ) { + dap_sscanf(l_listen_unix_socket_permissions_str,"%ou", &l_listen_unix_socket_permissions ); + } + log_it( L_INFO, "Console interace on path %s (%04o) ", l_listen_unix_socket_path, l_listen_unix_socket_permissions ); + + #ifndef _WIN32 + + if ( server_sockfd >= 0 ) { + dap_chain_node_cli_delete( ); + server_sockfd = 0; + } + + // create socket + sockfd = socket( AF_UNIX, SOCK_STREAM, 0 ); + if( sockfd == INVALID_SOCKET ) + return -1; + + //int gdsg = sizeof(struct sockaddr_un); + + if ( access( l_listen_unix_socket_path , R_OK) != -1 ) + unlink( l_listen_unix_socket_path ); + + // connecting the address with a socket + if( bind(sockfd, (const struct sockaddr*) &l_server_addr, sizeof(struct sockaddr_un)) == SOCKET_ERROR) { + // errno = EACCES 13 Permission denied + if ( errno == EACCES ) // EACCES=13 + log_it( L_ERROR, "Server can't start(err=%d). Can't create file=%s [Permission denied]", errno, + l_listen_unix_socket_path ); + else + log_it( L_ERROR, "Server can't start(err=%d). May be problem with file=%s?", errno, l_listen_unix_socket_path ); + closesocket( sockfd ); + return -2; + } + chmod(l_listen_unix_socket_path,l_listen_unix_socket_permissions); + + #else + +// Sleep( 3000 ); + + if( pthread_create(&threadId, NULL, thread_pipe_func, (void*) (intptr_t) sockfd) != 0 ) { + closesocket( sockfd ); + return -7; + } + + return 0; + #endif + + } + else if (l_listen_port ){ + + const char *l_listen_addr_str = dap_config_get_item_str(g_config, "conserver", "listen_address"); + + log_it( L_INFO, "Console interace on addr %s port %u ", l_listen_addr_str, l_listen_port ); + + server_addr.sin_family = AF_INET; +#ifdef _WIN32 + struct in_addr _in_addr = { { .S_addr = htonl(INADDR_LOOPBACK) } }; + server_addr.sin_addr = _in_addr; + server_addr.sin_port = l_listen_port; +#else + inet_pton( AF_INET, l_listen_addr_str, &server_addr.sin_addr ); + server_addr.sin_port = htons( (uint16_t)l_listen_port ); +#endif + // create socket + if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET ) { +#ifdef __WIN32 + _set_errno(WSAGetLastError()); +#endif + log_it( L_ERROR, "Console Server: can't create socket, err %d", errno ); + return -3; + } + + // connecting the address with a socket + if ( bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == SOCKET_ERROR ) { +#ifdef __WIN32 + _set_errno(WSAGetLastError()); +#endif + log_it( L_ERROR, "Console Server: can't bind socket, err %d", errno ); + closesocket( sockfd ); + return -4; + } + }else { + log_it (L_INFO, "Not defined console interface"); + return 0; + } + + // turn on reception of connections + if( listen(sockfd, MAX_CONSOLE_CLIENTS) == SOCKET_ERROR ) + return -5; + + if( pthread_create(&l_thread_id, NULL, thread_main_func, (void*) (intptr_t) sockfd) != 0 ) { + closesocket( sockfd ); + return -6; + } + + // in order to thread not remain in state "dead" after completion + pthread_detach( l_thread_id ); + server_sockfd = sockfd; + + return 0; +} + +/** + * Deinitialization of the server side + * + */ +void dap_chain_node_cli_delete(void) +{ + if(server_sockfd >= 0) + closesocket(server_sockfd); +#ifdef __WIN32 + WSACleanup(); +#endif + // deinit client for handshake + dap_chain_node_client_deinit(); +} diff --git a/libdap-chain-net/dap_chain_node_cli.h b/libdap-chain-net/dap_chain_node_cli.h new file mode 100644 index 0000000000000000000000000000000000000000..e9df748e7ebb56c7c81a2436e018ef33c4c14a18 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_cli.h @@ -0,0 +1,78 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "dap_common.h" +#include "dap_config.h" +#include "uthash.h" + +#ifndef _WIN32 +#include <unistd.h> // for close +#define closesocket close +typedef int SOCKET; +#define SOCKET_ERROR -1 // for win32 = (-1) +#define INVALID_SOCKET -1 // for win32 = (SOCKET)(~0) +#endif + + +typedef int cmdfunc_t(int argc, char ** argv, void *arg_func, char **str_reply); + +typedef struct dap_chain_node_cmd_item{ + char name[32]; /* User printable name of the function. */ + cmdfunc_t *func; /* Function to call to do the job. */ + void *arg_func; /* additional argument of function*/ + char *doc; /* Documentation for this function. */ + char *doc_ex; /* Full documentation for this function. */ + UT_hash_handle hh; +} dap_chain_node_cmd_item_t; + + +// Read from socket +long s_recv(SOCKET sock, unsigned char *buf, size_t bufsize, int timeout); + +/** + * Look up NAME as the name of a command, and return a pointer to that + * command. Return a NULL pointer if NAME isn't a command name. + */ +dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_get_first(); +dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_find(const char *a_name); +void dap_chain_node_cli_cmd_item_create(const char * a_name, cmdfunc_t *a_func, void *a_arg_func, const char *a_doc, const char *a_doc_ex); + +void dap_chain_node_cli_set_reply_text(char **str_reply, const char *str, ...); + +int dap_chain_node_cli_find_option_val( char** argv, int arg_start, int arg_end, const char *opt_name, const char **opt_value); + + +/** + * Initialization of the server side of the interaction + * with the console kelvin-node-cli + */ +int dap_chain_node_cli_init(dap_config_t * g_config); + +/** + * Deinitialization of the server side + */ +void dap_chain_node_cli_delete(void); diff --git a/libdap-chain-net/dap_chain_node_cli_cmd.c b/libdap-chain-net/dap_chain_node_cli_cmd.c new file mode 100644 index 0000000000000000000000000000000000000000..1f15bd943d939d884a44935711ef1ac8ff4c48b0 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_cli_cmd.c @@ -0,0 +1,3488 @@ +/* + * Authors: + * Dmitriy A. Gerasimov <gerasimov.dmitriy@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> +#include <dirent.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#include <wepoll.h> +#else +#include <sys/types.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <signal.h> +#endif +#include <pthread.h> + +#include "iputils/iputils.h" + +#include "uthash.h" +#include "utlist.h" + +#include "dap_string.h" +#include "dap_hash.h" +#include "dap_chain_common.h" +#include "dap_strfuncs.h" +#include "dap_list.h" +#include "dap_string.h" +#include "dap_cert.h" +#include "dap_cert_file.h" +#include "dap_chain_wallet.h" +#include "dap_chain_node.h" +#include "dap_chain_global_db.h" +#include "dap_chain_node_client.h" +#include "dap_chain_node_remote.h" +#include "dap_chain_node_cli_cmd.h" +#include "dap_chain_node_cli_cmd_tx.h" +#include "dap_chain_node_ping.h" +#include "dap_chain_net_srv.h" +#include "dap_chain_cell.h" + +#include "dap_chain_common.h" +#include "dap_chain_datum.h" +#include "dap_chain_datum_token.h" +#include "dap_chain_datum_tx_items.h" +#include "dap_chain_ledger.h" +#include "dap_chain_mempool.h" +#include "dap_chain_global_db.h" +#include "dap_chain_global_db_remote.h" +//#include "dap_chain_gdb.h" + +#include "dap_stream_ch_chain_net.h" +#include "dap_stream_ch_chain.h" +#include "dap_stream_ch_chain_pkt.h" +#include "dap_stream_ch_chain_net_pkt.h" + +#define LOG_TAG "chain_node_cli_cmd" + +/** + * Find in base addr by alias + * + * return addr, NULL if not found + */ +dap_chain_node_addr_t* dap_chain_node_addr_get_by_alias(dap_chain_net_t * a_net, const char *a_alias) +{ + dap_chain_node_addr_t *l_addr = NULL; + if(!a_alias) + return NULL; + const char *a_key = a_alias; + size_t l_addr_size = 0; + l_addr = (dap_chain_node_addr_t*) (void*) dap_chain_global_db_gr_get(a_key, &l_addr_size, + a_net->pub.gdb_nodes_aliases); + if(l_addr_size != sizeof(dap_chain_node_addr_t)) { +// l_addr = DAP_NEW_Z(dap_chain_node_addr_t); +// if(hex2bin((char*) l_addr, (const unsigned char *) addr_str, sizeof(dap_chain_node_addr_t) * 2) == -1) { + DAP_DELETE(l_addr); +// l_addr = NULL; +// } + } +// DAP_DELETE(addr_str); + return l_addr; +} + +/** + * Find in base alias by addr + * + * return list of addr, NULL if not found + */ +static dap_list_t* get_aliases_by_name(dap_chain_net_t * l_net, dap_chain_node_addr_t *a_addr) +{ + if(!a_addr) + return NULL; + dap_list_t *list_aliases = NULL; + size_t data_size = 0; + // read all aliases + dap_global_db_obj_t *objs = dap_chain_global_db_gr_load(l_net->pub.gdb_nodes_aliases, &data_size); + if(!objs || !data_size) + return NULL; + for(size_t i = 0; i < data_size; i++) { + //dap_chain_node_addr_t addr_i; + dap_global_db_obj_t *obj = objs + i; + if(!obj) + break; + dap_chain_node_addr_t *l_addr = (dap_chain_node_addr_t*) (void*) obj->value; + if(l_addr && obj->value_len == sizeof(dap_chain_node_addr_t) && a_addr->uint64 == l_addr->uint64) { + list_aliases = dap_list_prepend(list_aliases, strdup(obj->key)); + } + } + dap_chain_global_db_objs_delete(objs, data_size); + return list_aliases; +} + +static dap_chain_node_addr_t* s_node_info_get_addr(dap_chain_net_t * a_net, dap_chain_node_info_t *a_node_info, + dap_chain_node_addr_t *a_addr, const char *a_alias_str) +{ + dap_chain_node_addr_t *l_address = NULL; + if(a_alias_str && !a_addr->uint64) { + l_address = dap_chain_node_addr_get_by_alias(a_net, a_alias_str); + } + if(a_addr->uint64) { + l_address = DAP_NEW(dap_chain_node_addr_t); + l_address->uint64 = a_addr->uint64; + } + return l_address; +} + +/** + * Read node from base + */ +static dap_chain_node_info_t* node_info_read_and_reply(dap_chain_net_t * a_net, dap_chain_node_addr_t *a_address, + char **a_str_reply) +{ + char *l_key = dap_chain_node_addr_to_hash_str(a_address); + if(!l_key) + { + dap_chain_node_cli_set_reply_text(a_str_reply, "can't calculate hash of addr"); + return NULL; + } + size_t node_info_size = 0; + dap_chain_node_info_t *node_info; + // read node + node_info = (dap_chain_node_info_t *) dap_chain_global_db_gr_get(l_key, &node_info_size, a_net->pub.gdb_nodes); + + if(!node_info) { + dap_chain_node_cli_set_reply_text(a_str_reply, "node not found in base"); + DAP_DELETE(l_key); + return NULL; + } + if(!node_info->hdr.ext_port) + node_info->hdr.ext_port = 8079; + size_t node_info_size_must_be = dap_chain_node_info_get_size(node_info); + if(node_info_size_must_be != node_info_size) { + dap_chain_node_cli_set_reply_text(a_str_reply, "node has bad size in base=%u (must be %u)", node_info_size, + node_info_size_must_be); + DAP_DELETE(node_info); + DAP_DELETE(l_key); + return NULL; + } + +// dap_chain_node_info_t *node_info = dap_chain_node_info_deserialize(str, (str) ? strlen(str) : 0); +// if(!node_info) { +// set_reply_text(str_reply, "node has invalid format in base"); +// } +// DAP_DELETE(str); + DAP_DELETE(l_key); + return node_info; +} + +/** + * Save node to base + */ +static bool node_info_save_and_reply(dap_chain_net_t * a_net, dap_chain_node_info_t *a_node_info, char **str_reply) +{ + if(!a_node_info || !a_node_info->hdr.address.uint64) { + dap_chain_node_cli_set_reply_text(str_reply, "node addr not found"); + return false; + } + char *a_key = dap_chain_node_addr_to_hash_str(&a_node_info->hdr.address); + if(!a_key) + { + dap_chain_node_cli_set_reply_text(str_reply, "can't calculate hash for addr"); + return NULL; + } + //char *a_value = dap_chain_node_info_serialize(node_info, NULL); + size_t l_node_info_size = dap_chain_node_info_get_size(a_node_info); + //dap_chain_node_info_t *l_node_info = DAP_NEW_Z_SIZE(dap_chain_node_info_t, l_node_info_size); + //memcpy(l_node_info, a_node_info, l_node_info_size ); + + //size_t data_len_out = 0; + //dap_chain_node_info_t *a_node_info1 = dap_chain_global_db_gr_get(a_key, &data_len_out, a_net->pub.gdb_nodes); + + bool res = dap_chain_global_db_gr_set(a_key, (uint8_t *) a_node_info, l_node_info_size, a_net->pub.gdb_nodes); + + //data_len_out = 0; + //dap_chain_node_info_t *a_node_info2 = dap_chain_global_db_gr_get(a_key, &data_len_out, a_net->pub.gdb_nodes); + //DAP_DELETE(a_key); + //DAP_DELETE(a_value); + return res; +} + +/** + * Handler of command 'global_db node add' + * + * str_reply[out] for reply + * return 0 Ok, -1 error + */ +static int node_info_add_with_reply(dap_chain_net_t * a_net, dap_chain_node_info_t *a_node_info, + const char *a_alias_str, + const char *a_cell_str, const char *a_ipv4_str, const char *a_ipv6_str, char **a_str_reply) +{ + + if(!a_node_info->hdr.address.uint64) { + dap_chain_node_cli_set_reply_text(a_str_reply, "not found -addr parameter"); + return -1; + } + if(!a_cell_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "not found -cell parameter"); + return -1; + } + if(a_ipv4_str) + inet_pton(AF_INET, a_ipv4_str, &(a_node_info->hdr.ext_addr_v4)); + if(a_ipv6_str) + inet_pton(AF_INET6, a_ipv6_str, &(a_node_info->hdr.ext_addr_v6)); + + // check match addr to cell or no + /*dap_chain_node_addr_t *addr = dap_chain_node_gen_addr(&node_info->hdr.cell_id); + if(!dap_chain_node_check_addr(&node_info->hdr.address, &node_info->hdr.cell_id)) { + set_reply_text(str_reply, "cell does not match addr"); + return -1; + }*/ + if(a_alias_str) { + // add alias + if(!dap_chain_node_alias_register(a_net, a_alias_str, &a_node_info->hdr.address)) { + log_it(L_WARNING, "can't save alias %s", a_alias_str); + dap_chain_node_cli_set_reply_text(a_str_reply, "alias '%s' can't be mapped to addr=0x%lld", + a_alias_str, a_node_info->hdr.address.uint64); + return -1; + } + } + + // write to base + bool res = node_info_save_and_reply(a_net, a_node_info, a_str_reply); + if(res) + dap_chain_node_cli_set_reply_text(a_str_reply, "node added"); + else + return -1; + if(res) + return 0; + return -1; +} + +/** + * Handler of command 'global_db node add' + * + * str_reply[out] for reply + * return 0 Ok, -1 error + */ +static int node_info_del_with_reply(dap_chain_net_t * a_net, dap_chain_node_info_t *a_node_info, const char *alias_str, + char **str_reply) +{ + if(!a_node_info->hdr.address.uint64 && !alias_str) { + dap_chain_node_cli_set_reply_text(str_reply, "addr not found"); + return -1; + } + // check, current node have this addr or no + uint64_t l_cur_addr = dap_db_get_cur_node_addr(a_net->pub.name); + if(l_cur_addr && l_cur_addr == a_node_info->hdr.address.uint64) { + dap_chain_node_cli_set_reply_text(str_reply, "current node cannot be deleted"); + return -1; + } + + // find addr by alias or addr_str + dap_chain_node_addr_t *address = s_node_info_get_addr(a_net, a_node_info, &a_node_info->hdr.address, alias_str); + if(!address) { + dap_chain_node_cli_set_reply_text(str_reply, "alias not found"); + return -1; + } + char *a_key = dap_chain_node_addr_to_hash_str(address); + if(a_key) + { + // delete node + bool res = dap_chain_global_db_gr_del(dap_strdup(a_key), a_net->pub.gdb_nodes); + if(res) { + // delete all aliases for node address + { + dap_list_t *list_aliases = get_aliases_by_name(a_net, address); + dap_list_t *list = list_aliases; + while(list) + { + const char *alias = (const char *) list->data; + dap_chain_node_alias_delete(a_net, alias); + list = dap_list_next(list); + } + dap_list_free_full(list_aliases, (dap_callback_destroyed_t) free); + } + // set text response + dap_chain_node_cli_set_reply_text(str_reply, "node deleted"); + } + else + dap_chain_node_cli_set_reply_text(str_reply, "node not deleted"); + DAP_DELETE(a_key); + DAP_DELETE(address); + if(res) + return 0; + return -1; + } + dap_chain_node_cli_set_reply_text(str_reply, "addr to delete can't be defined"); + DAP_DELETE(address); + return -1; +} + +/** + * Handler of command 'global_db node link' + * + * cmd 'add' or 'del' + * str_reply[out] for reply + * return 0 Ok, -1 error + */ +static int link_add_or_del_with_reply(dap_chain_net_t * a_net, dap_chain_node_info_t *a_node_info, const char *cmd, + const char *a_alias_str, + dap_chain_node_addr_t *link, char **a_str_reply) +{ + if(!a_node_info->hdr.address.uint64 && !a_alias_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "addr not found"); + return -1; + } + if(!link->uint64) { + dap_chain_node_cli_set_reply_text(a_str_reply, "link not found"); + return -1; + } + // TODO check the presence of link in the node base +#ifdef DAP_CHAIN_NODE_CHECK_PRESENSE + dap_chain_node_cli_set_reply_text(a_str_reply, "node 0x%016llx not found in base", link->uint64); + return -1; +#endif + + // find addr by alias or addr_str + dap_chain_node_addr_t *l_address = s_node_info_get_addr(a_net, a_node_info, &a_node_info->hdr.address, a_alias_str); + if(!l_address) { + dap_chain_node_cli_set_reply_text(a_str_reply, "alias not found"); + return -1; + } + + dap_chain_node_info_t * l_node_info_read = node_info_read_and_reply(a_net, l_address, a_str_reply); + size_t l_node_info_read_size = dap_chain_node_info_get_size(l_node_info_read); + if(!l_node_info_read) + return -1; + + int cmd_int = 0; + if(!strcmp(cmd, "add")) + cmd_int = 1; + else if(!strcmp(cmd, "del")) + cmd_int = 2; + + // find link in node_info_read + int index_link = -1; + for(size_t i = 0; i < l_node_info_read->hdr.links_number; i++) { + if(l_node_info_read->links[i].uint64 == link->uint64) { + // link already present + index_link = (int) i; + break; + } + } + bool res_successful = false; // is successful whether add/del + // add link + if(cmd_int == 1) { + if(index_link == -1) { + l_node_info_read->hdr.links_number++; + l_node_info_read_size = dap_chain_node_info_get_size(l_node_info_read); + l_node_info_read = DAP_REALLOC(l_node_info_read, l_node_info_read_size); + memcpy(&(l_node_info_read->links[l_node_info_read->hdr.links_number-1]), link, sizeof(dap_chain_node_addr_t)); + res_successful = true; + } + } + // delete link + else if(cmd_int == 2) { + // move link list to one item prev + if(index_link >= 0) { + for(unsigned int j = (unsigned int) index_link; j < (l_node_info_read->hdr.links_number - 1); j++) { + memcpy(&(l_node_info_read->links[j]), &(l_node_info_read->links[j + 1]), sizeof(dap_chain_node_addr_t)); + } + l_node_info_read->hdr.links_number--; + res_successful = true; + l_node_info_read = DAP_REALLOC(l_node_info_read, l_node_info_read_size -= sizeof(*link)); + } + } + // save edited node_info + if(res_successful) { + bool res = node_info_save_and_reply(a_net, l_node_info_read, a_str_reply); + if(res) { + res_successful = true; + if(cmd_int == 1) + dap_chain_node_cli_set_reply_text(a_str_reply, "link added"); + if(cmd_int == 2) + dap_chain_node_cli_set_reply_text(a_str_reply, "link deleted"); + } + else { + res_successful = false; + } + } + else { + if(cmd_int == 1) { + if(index_link >= 0) + dap_chain_node_cli_set_reply_text(a_str_reply, "link not added because it is already present"); + else + dap_chain_node_cli_set_reply_text(a_str_reply, "link not added"); + } + if(cmd_int == 2) { + if(index_link == -1) + dap_chain_node_cli_set_reply_text(a_str_reply, "link not deleted because not found"); + else + dap_chain_node_cli_set_reply_text(a_str_reply, "link not deleted"); + } + } + + DAP_DELETE(l_address); + DAP_DELETE(l_node_info_read); + if(res_successful) + return 0; + return -1; +} + +/** + * Handler of command 'node dump' + * + * str_reply[out] for reply + * return 0 Ok, -1 error + */ +static int node_info_dump_with_reply(dap_chain_net_t * a_net, dap_chain_node_addr_t * a_addr, bool a_is_full, + const char *a_alias, char **a_str_reply) +{ + int l_ret = 0; + dap_string_t *l_string_reply = dap_string_new("Node dump:"); + + if((a_addr && a_addr->uint64) || a_alias) { + dap_chain_node_addr_t *l_addr = NULL; + if(a_addr && a_addr->uint64) { + l_addr = DAP_NEW(dap_chain_node_addr_t); + l_addr->uint64 = a_addr->uint64; + } else if(a_alias) { + l_addr = dap_chain_node_alias_find(a_net, a_alias); + } + if(!l_addr) { + dap_chain_node_cli_set_reply_text(a_str_reply, "addr not valid"); + dap_string_free(l_string_reply, true); + return -1; + } + // read node + dap_chain_node_info_t *node_info_read = node_info_read_and_reply(a_net, l_addr, a_str_reply); + if(!node_info_read) { + DAP_DELETE(l_addr); + dap_string_free(l_string_reply, true); + return -2; + } + + // get aliases in form of string + dap_string_t *aliases_string = dap_string_new(NULL); + dap_list_t *list_aliases = get_aliases_by_name(a_net, l_addr); + if(list_aliases) + { + dap_list_t *list = list_aliases; + while(list) + { + const char *alias = (const char *) list->data; + dap_string_append_printf(aliases_string, "\nalias %s", alias); + list = dap_list_next(list); + } + dap_list_free_full(list_aliases, (dap_callback_destroyed_t) free); + } + else + dap_string_append(aliases_string, "\nno aliases"); + + const int hostlen = 128; + char *host4 = (char*) alloca(hostlen); + char *host6 = (char*) alloca(hostlen); + struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = node_info_read->hdr.ext_addr_v4 }; + const char* str_ip4 = inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host4, hostlen); + + struct sockaddr_in6 sa6 = { .sin6_family = AF_INET6, .sin6_addr = node_info_read->hdr.ext_addr_v6 }; + const char* str_ip6 = inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) &sa6)->sin6_addr), host6, hostlen); + + // get links in form of string + dap_string_t *links_string = dap_string_new(NULL); + for(unsigned int i = 0; i < node_info_read->hdr.links_number; i++) { + dap_chain_node_addr_t link_addr = node_info_read->links[i]; + dap_string_append_printf(links_string, "\nlink%02d address : " NODE_ADDR_FP_STR, i, + NODE_ADDR_FP_ARGS_S(link_addr)); + } + + dap_string_append_printf(l_string_reply, "\n"); + char l_port_str[10]; + sprintf(l_port_str,"%d",node_info_read->hdr.ext_port); + + // set short reply with node param + if(!a_is_full) + dap_string_append_printf(l_string_reply, + "node address "NODE_ADDR_FP_STR"\tcell 0x%016llx\tipv4 %s\tport: %s\tnumber of links %u", + NODE_ADDR_FP_ARGS_S(node_info_read->hdr.address), + node_info_read->hdr.cell_id.uint64, str_ip4, + node_info_read->hdr.ext_port ? l_port_str : "default", + node_info_read->hdr.links_number); + else + // set full reply with node param + dap_string_append_printf(l_string_reply, + "node address " NODE_ADDR_FP_STR "\ncell 0x%016llx\nipv4 %s\nipv6 %s\nport: %s%s\nlinks %u%s", + NODE_ADDR_FP_ARGS_S(node_info_read->hdr.address), + node_info_read->hdr.cell_id.uint64, + str_ip4, str_ip6, + node_info_read->hdr.ext_port ? l_port_str : "default", + aliases_string->str, + node_info_read->hdr.links_number, links_string->str); + dap_string_free(aliases_string, true); + dap_string_free(links_string, true); + + DAP_DELETE(l_addr); + DAP_DELETE(node_info_read); + + } else { // Dump list + dap_global_db_obj_t *l_objs = NULL; + size_t l_nodes_count = 0; + dap_string_append(l_string_reply, "\n"); + // read all node + l_objs = dap_chain_global_db_gr_load(a_net->pub.gdb_nodes, &l_nodes_count); + + if(!l_nodes_count || !l_objs) { + dap_string_append_printf(l_string_reply, "No records\n"); + dap_chain_node_cli_set_reply_text(a_str_reply, l_string_reply->str); + dap_string_free(l_string_reply, true); + dap_chain_global_db_objs_delete(l_objs, l_nodes_count); + return -1; + } else { + size_t l_nodes_count_real = 0; + dap_string_append_printf(l_string_reply, "Got %u records:\n", l_nodes_count); + for(size_t i = 0; i < l_nodes_count; i++) { + dap_chain_node_info_t *l_node_info = (dap_chain_node_info_t *) l_objs[i].value; + // find addr by alias or addr_str + dap_chain_node_addr_t *address = s_node_info_get_addr(a_net, l_node_info, &l_node_info->hdr.address, a_alias); + if(!address) { + dap_chain_node_cli_set_reply_text(a_str_reply, "alias not found"); + dap_string_free(l_string_reply, true); + dap_chain_global_db_objs_delete(l_objs, l_nodes_count); + return -1; + } + // read node + dap_chain_node_info_t *node_info_read = node_info_read_and_reply(a_net, address, NULL); + if(!node_info_read) { + DAP_DELETE(address); + continue; + //dap_string_free(l_string_reply, true); + //dap_chain_global_db_objs_delete(l_objs, l_nodes_count); + //return -1; + } + + const int hostlen = 128; + char *host4 = (char*) alloca(hostlen); + char *host6 = (char*) alloca(hostlen); + struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = node_info_read->hdr.ext_addr_v4 }; + const char* str_ip4 = inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host4, hostlen); + + struct sockaddr_in6 sa6 = { .sin6_family = AF_INET6, .sin6_addr = node_info_read->hdr.ext_addr_v6 }; + const char* str_ip6 = inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) &sa6)->sin6_addr), host6, hostlen); + + // get aliases in form of string + dap_string_t *aliases_string = dap_string_new(NULL); + dap_list_t *list_aliases = get_aliases_by_name(a_net, address); + if(list_aliases) + { + dap_list_t *list = list_aliases; + while(list) + { + const char *alias = (const char *) list->data; + dap_string_append_printf(aliases_string, "\nalias %s", alias); + list = dap_list_next(list); + } + dap_list_free_full(list_aliases, (dap_callback_destroyed_t) free); + } + else + dap_string_append(aliases_string, "\nno aliases"); + + // get links in form of string + dap_string_t *links_string = dap_string_new(NULL); + for(unsigned int i = 0; i < node_info_read->hdr.links_number; i++) { + dap_chain_node_addr_t link_addr = node_info_read->links[i]; + dap_string_append_printf(links_string, "\nlink%02d address : " NODE_ADDR_FP_STR, i, + NODE_ADDR_FP_ARGS_S(link_addr)); + } + + if(i) + dap_string_append_printf(l_string_reply, "\n"); + char l_port_str[10]; + sprintf(l_port_str,"%d",node_info_read->hdr.ext_port); + // set short reply with node param + if(!a_is_full) + dap_string_append_printf(l_string_reply, + "node address "NODE_ADDR_FP_STR"\tcell 0x%016llx\tipv4 %s\tport: %s\tnumber of links %u", + NODE_ADDR_FP_ARGS_S(node_info_read->hdr.address), + node_info_read->hdr.cell_id.uint64, str_ip4, + node_info_read->hdr.ext_port ? l_port_str : "default", + node_info_read->hdr.links_number); + else + // set full reply with node param + dap_string_append_printf(l_string_reply, + "node address " NODE_ADDR_FP_STR "\ncell 0x%016llx\nipv4 %s\nipv6 %s\nport: %s%s\nlinks %u%s", + NODE_ADDR_FP_ARGS_S(node_info_read->hdr.address), + node_info_read->hdr.cell_id.uint64, + str_ip4, str_ip6, + node_info_read->hdr.ext_port ? l_port_str : "default", + aliases_string->str, + node_info_read->hdr.links_number, links_string->str); + dap_string_free(aliases_string, true); + dap_string_free(links_string, true); + + DAP_DELETE(address); + DAP_DELETE(node_info_read); + } + } + dap_chain_global_db_objs_delete(l_objs, l_nodes_count); + } + dap_chain_node_cli_set_reply_text(a_str_reply, l_string_reply->str); + dap_string_free(l_string_reply, true); + return l_ret; +} + +/** + * global_db command + * + * return 0 OK, -1 Err + */ + +int com_global_db(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply) +{ + enum { + CMD_NONE, CMD_NAME_CELL, CMD_ADD, CMD_FLUSH + }; + int arg_index = 1; + int cmd_name = CMD_NONE; + // find 'cells' as first parameter only + if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "cells", NULL)) + cmd_name = CMD_NAME_CELL; + else if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "flush", NULL)) + cmd_name = CMD_FLUSH; + switch (cmd_name) { + case CMD_NAME_CELL: + { + if(!arg_index || a_argc < 3) { + dap_chain_node_cli_set_reply_text(a_str_reply, "parameters are not valid"); + return -1; + } + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + if(dap_chain_node_cli_cmd_values_parse_net_chain(&arg_index, a_argc, a_argv, a_str_reply, &l_chain, &l_net) < 0) + return -11; + + const char *l_cell_str = NULL, *l_chain_str = NULL; + // find cell and chain + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-cell", &l_cell_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain", &l_chain_str); + + // Check for chain + if(!l_chain_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "%s requires parameter 'chain' to be valid"); + return -12; + } + + int arg_index_n = ++arg_index; + // find command (add, delete, etc) as second parameter only + int cmd_num = CMD_NONE; + switch (cmd_name) { + case CMD_NAME_CELL: + if((arg_index_n = dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "add", NULL)) + != 0) { + cmd_num = CMD_ADD; + } + dap_chain_cell_id_t l_cell_id = { {0} }; + if(l_cell_str) { + dap_digit_from_string(l_cell_str, (uint8_t*) &l_cell_id.raw, sizeof(l_cell_id.raw)); //DAP_CHAIN_CELL_ID_SIZE); + } + + switch (cmd_num) + { + // add new node to global_db + case CMD_ADD: + if(!arg_index || a_argc < 7) { + dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameters"); + return -1; + } + dap_chain_cell_t *l_cell = dap_chain_cell_create(); + l_cell->chain = l_chain; + l_cell->id.uint64 = l_cell_id.uint64; + l_cell->file_storage_path = dap_strdup_printf("%0llx.dchaincell",l_cell->id.uint64); + int l_ret = dap_chain_cell_file_update(l_cell); + if(!l_ret) + dap_chain_node_cli_set_reply_text(a_str_reply, "cell added successfully"); + else + dap_chain_node_cli_set_reply_text(a_str_reply, "can't create file for cell 0x%016X ( %s )", + l_cell->id.uint64,l_cell->file_storage_path); + dap_chain_cell_delete(l_cell); + return l_ret; + + //case CMD_NONE: + default: + dap_chain_node_cli_set_reply_text(a_str_reply, "command %s not recognized", a_argv[1]); + return -1; + } + } + } + case CMD_FLUSH: + { + int res_flush = dap_chain_global_db_flush(); + switch (res_flush) { + case 0: + dap_chain_node_cli_set_reply_text(a_str_reply, "Commit data base and filesystem caches to disk completed.\n\n"); + break; + case -1: + dap_chain_node_cli_set_reply_text(a_str_reply, "Couldn't open db directory. Can't init cdb\n" + "Reboot the node.\n\n"); + break; + case -2: + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't init cdb\n" + "Reboot the node.\n\n"); + break; + case -3: + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't init sqlite\n" + "Reboot the node.\n\n"); + break; + default: + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't commit data base caches to disk completed.\n" + "Reboot the node.\n\n"); + break; + } + return 0; + } + default: + dap_chain_node_cli_set_reply_text(a_str_reply, "parameters are not valid"); + return -1; + } + return -555; +} + +/** + * Node command + */ +int com_node(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply) +{ + enum { + CMD_NONE, CMD_ADD, CMD_DEL, CMD_LINK, CMD_ALIAS, CMD_HANDSHAKE, CMD_CONNECT, CMD_DUMP + }; + int arg_index = 1; + int cmd_num = CMD_NONE; + if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "add", NULL)) { + cmd_num = CMD_ADD; + } + else if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "del", NULL)) { + cmd_num = CMD_DEL; + } + else if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "link", NULL)) { + cmd_num = CMD_LINK; + } + else + // find add parameter ('alias' or 'handshake') + if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "handshake", NULL)) { + cmd_num = CMD_HANDSHAKE; + } + else if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "connect", NULL)) { + cmd_num = CMD_CONNECT; + } + else if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "alias", NULL)) { + cmd_num = CMD_ALIAS; + } + else if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "dump", NULL)) { + cmd_num = CMD_DUMP; + } + arg_index++; + if(cmd_num == CMD_NONE) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command %s not recognized", a_argv[1]); + return -1; + } + const char *l_addr_str = NULL, *l_port_str = NULL, *alias_str = NULL; + const char *l_cell_str = NULL, *l_link_str = NULL, *a_ipv4_str = NULL, *a_ipv6_str = NULL; + + // find net + dap_chain_net_t *l_net = NULL; + + if(dap_chain_node_cli_cmd_values_parse_net_chain(&arg_index, a_argc, a_argv, a_str_reply, NULL, &l_net) < 0) + return -11; + + // find addr, alias + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-addr", &l_addr_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-port", &l_port_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-alias", &alias_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-cell", &l_cell_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-ipv4", &a_ipv4_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-ipv6", &a_ipv6_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-link", &l_link_str); + + // struct to write to the global db + dap_chain_node_addr_t l_node_addr = { 0 }; + dap_chain_node_addr_t l_link = { 0 }; + dap_chain_node_info_t *l_node_info = NULL; + size_t l_node_info_size = sizeof(l_node_info->hdr) + sizeof(l_link); + if(cmd_num >= CMD_ADD && cmd_num <= CMD_LINK) + l_node_info = DAP_NEW_Z_SIZE(dap_chain_node_info_t, l_node_info_size); + + if(l_addr_str) { + if(dap_chain_node_addr_from_str(&l_node_addr, l_addr_str) != 0) { + dap_digit_from_string(l_addr_str, l_node_addr.raw, sizeof(l_node_addr.raw)); + } + if(l_node_info) + memcpy(&l_node_info->hdr.address, &l_node_addr, sizeof(dap_chain_node_addr_t)); + } + if(l_port_str) { + uint16_t l_node_port = 0; + dap_digit_from_string(l_port_str, &l_node_port, sizeof(uint16_t)); + if(l_node_info) + l_node_info->hdr.ext_port = l_node_port; + } + if(l_cell_str && l_node_info) { + dap_digit_from_string(l_cell_str, l_node_info->hdr.cell_id.raw, sizeof(l_node_info->hdr.cell_id.raw)); //DAP_CHAIN_CELL_ID_SIZE); + } + if(l_link_str) { + if(dap_chain_node_addr_from_str(&l_link, l_link_str) != 0) { + dap_digit_from_string(l_link_str, l_link.raw, sizeof(l_link.raw)); + } + } + + switch (cmd_num) + { + case CMD_ADD: + if(!arg_index || a_argc < 8) { + dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameters"); + return -1; + } + // handler of command 'node add' + int l_ret = node_info_add_with_reply(l_net, l_node_info, alias_str, l_cell_str, a_ipv4_str, a_ipv6_str, + a_str_reply); + DAP_DELETE(l_node_info); + return l_ret; + //break; + + case CMD_DEL: + // handler of command 'node del' + { + int l_ret = node_info_del_with_reply(l_net, l_node_info, alias_str, a_str_reply); + DAP_DELETE(l_node_info); + return l_ret; + } + case CMD_LINK: + if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "add", NULL)) { + // handler of command 'node link add -addr <node address> -link <node address>' + int l_ret = link_add_or_del_with_reply(l_net, l_node_info, "add", alias_str, &l_link, a_str_reply); + DAP_DELETE(l_node_info); + return l_ret; + } + else if(dap_chain_node_cli_find_option_val(a_argv, arg_index, min(a_argc, arg_index + 1), "del", NULL)) { + // handler of command 'node link del -addr <node address> -link <node address>' + int l_ret = link_add_or_del_with_reply(l_net, l_node_info, "del", alias_str, &l_link, a_str_reply); + DAP_DELETE(l_node_info); + return l_ret; + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, "command not recognize, supported format:\n" + "global_db node link <add|del] [-addr <node address> | -alias <node alias>] -link <node address>"); + DAP_DELETE(l_node_info); + return -1; + } + + case CMD_DUMP: { + // handler of command 'node dump' + bool l_is_full = dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-full", NULL); + return node_info_dump_with_reply(l_net, &l_node_addr, l_is_full, alias_str, a_str_reply); + } + // add alias + case CMD_ALIAS: + if(alias_str) { + if(l_addr_str) { + // add alias + if(!dap_chain_node_alias_register(l_net, alias_str, &l_node_addr)) + log_it(L_WARNING, "can't save alias %s", alias_str); + else { + dap_chain_node_cli_set_reply_text(a_str_reply, "alias mapped successfully"); + } + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, "alias can't be mapped because -addr is not found"); + return -1; + } + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, "alias can't be mapped because -alias is not found"); + return -1; + } + + break; + // make connect + case CMD_CONNECT: { + // get address from alias if addr not defined + if(alias_str && !l_node_addr.uint64) { + dap_chain_node_addr_t *address_tmp = dap_chain_node_addr_get_by_alias(l_net, alias_str); + if(address_tmp) { + memcpy(&l_node_addr, address_tmp, sizeof(*address_tmp)); + DAP_DELETE(address_tmp); + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, "no address found by alias"); + return -1; + } + } + // for auto mode + int l_is_auto = 0; + // list of dap_chain_node_addr_t struct + unsigned int l_nodes_count = 0; + dap_list_t *l_node_list = NULL; + dap_chain_node_addr_t *l_remote_node_addr = NULL; + if(!l_node_addr.uint64) { + // check whether auto mode + l_is_auto = dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "auto", NULL); + if(!l_is_auto) { + dap_chain_node_cli_set_reply_text(a_str_reply, "addr not found"); + return -1; + } + // if auto mode, then looking for the node address + + // get cur node links + bool a_is_only_cur_cell = false; + dap_list_t *l_node_link_list = dap_chain_net_get_link_node_list(l_net, a_is_only_cur_cell); + // get all nodes list if no links + if(!l_node_link_list) + l_node_list = dap_chain_net_get_node_list(l_net); + else + l_node_list = dap_list_concat(l_node_link_list, l_node_list); + + // select random node from the list + l_nodes_count = dap_list_length(l_node_list); + if(l_nodes_count > 0) { + unsigned int l_node_pos = rand() % l_nodes_count; + dap_list_t *l_tmp = dap_list_nth(l_node_list, l_node_pos); + l_remote_node_addr = l_tmp->data; + l_node_addr.uint64 = l_remote_node_addr->uint64; + } + + if(!l_node_addr.uint64) { + dap_chain_node_cli_set_reply_text(a_str_reply, "no node is available"); + return -1; + } + } + dap_chain_node_info_t *l_remote_node_info; + dap_chain_node_client_t *l_node_client; + int res; + do { + l_remote_node_info = node_info_read_and_reply(l_net, &l_node_addr, a_str_reply); + if(!l_remote_node_info) { + return -1; + } + // start connect + l_node_client = dap_chain_node_client_connect(l_remote_node_info); + if(!l_node_client) { + dap_chain_node_cli_set_reply_text(a_str_reply, "can't connect"); + DAP_DELETE(l_remote_node_info); + return -1; + } + // wait connected + int timeout_ms = 7000; // 7 sec = 7000 ms + res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_CONNECTED, timeout_ms); + // select new node addr + if(l_is_auto && res){ + if(l_remote_node_addr && l_nodes_count>1){ + l_nodes_count--; + l_node_list = dap_list_remove(l_node_list, l_remote_node_addr); + DAP_DELETE(l_remote_node_addr); + unsigned int l_node_pos = rand() % l_nodes_count; + dap_list_t *l_tmp = dap_list_nth(l_node_list, l_node_pos); + l_remote_node_addr = l_tmp->data; + l_node_addr.uint64 = l_remote_node_addr->uint64; + + // clean client struct + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_remote_node_info); + //return -1; + continue; + } + } + break; + } + while(1); + // for auto mode only + if(l_is_auto) { + //start background thread for testing connect to the nodes + dap_chain_node_ping_background_start(l_net, l_node_list); + dap_list_free_full(l_node_list, free); + } + + + + if(res) { + dap_chain_node_cli_set_reply_text(a_str_reply, "no response from remote node(s)"); + log_it(L_WARNING, "No response from remote node(s): err code %d", res); + // clean client struct + dap_chain_node_client_close(l_node_client); + //DAP_DELETE(l_remote_node_info); + return -1; + } + + log_it(L_NOTICE, "Stream connection established"); + dap_stream_ch_chain_sync_request_t l_sync_request = { { 0 } }; + dap_stream_ch_t * l_ch_chain = dap_client_get_stream_ch(l_node_client->client, dap_stream_ch_chain_get_id()); + // fill begin id + l_sync_request.id_start = (uint64_t) dap_db_log_get_last_id_remote( + l_remote_node_info->hdr.address.uint64); + // fill end id = 0 - no time limit + //l_sync_request.ts_end = 0; + // fill current node address + l_sync_request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net); + + // if need to get current node address (feature-2630) + if(!l_sync_request.node_addr.uint64 ) + { + log_it(L_NOTICE, "Now get node addr"); + uint8_t l_ch_id = dap_stream_ch_chain_net_get_id(); + dap_stream_ch_t * l_ch_chain = dap_client_get_stream_ch(l_node_client->client, l_ch_id); + + int l_res = dap_chain_node_client_set_callbacks( l_node_client->client, l_ch_id); + + size_t res = dap_stream_ch_chain_net_pkt_write(l_ch_chain, + DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR_LEASE_REQUEST, + //DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR_REQUEST, + l_net->pub.id, + NULL, 0); + if(res == 0) { + log_it(L_WARNING, "Can't send DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR_REQUEST packet"); + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_remote_node_info); + return -1; + } + int timeout_ms = 15000; // 15 sec = 15 000 ms + l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_NODE_ADDR_LEASED, timeout_ms); + switch (l_res) { + case 0: + if(l_node_client->cur_node_addr.uint64 != 0) { + + l_sync_request.node_addr.uint64 = l_node_client->cur_node_addr.uint64; + log_it(L_INFO, "Node address leased"); + l_sync_request.node_addr.uint64 = l_node_client->cur_node_addr.uint64; + // save cur address + // already saved + // dap_db_set_cur_node_addr_exp(l_sync_request.node_addr.uint64, l_net->pub.name); + } + else + log_it(L_WARNING, "Node address leased wrong!"); + break; + case -1: + log_it(L_WARNING, "Timeout with addr leasing"); + default: + if(l_res != -1) + log_it(L_WARNING, "Node address request error %d", l_res); + /*dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_remote_node_info); + return -1;*/ + } + /* if(0 == dap_stream_ch_chain_pkt_write(l_ch_chain, DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR_REQUEST, + l_net->pub.id, l_chain_id_null, l_chain_cell_id_null, &l_sync_request, + sizeof(l_sync_request))) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Error: Cant send sync chains request"); + // clean client struct + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_remote_node_info); + return -1; + } + dap_stream_ch_set_ready_to_write(l_ch_chain, true); + // wait for finishing of request + timeout_ms = 120000; // 20 min = 1200 sec = 1 200 000 ms + // TODO add progress info to console + res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); + */ + + } + log_it(L_NOTICE, "Now lets sync all"); + + dap_chain_id_t l_chain_id_null = { { 0 } }; + dap_chain_cell_id_t l_chain_cell_id_null = { { 0 } }; + l_chain_id_null.uint64 = l_net->pub.id.uint64; + l_chain_cell_id_null.uint64 = dap_chain_net_get_cur_cell(l_net) ? dap_chain_net_get_cur_cell(l_net)->uint64 : 0; + + log_it(L_INFO, "Requested GLOBAL_DB syncronizatoin, %llu:%llu period", l_sync_request.id_start, + l_sync_request.id_end); + // copy l_sync_request to current + //dap_stream_ch_chain_t * l_s_ch_chain = DAP_STREAM_CH_CHAIN(l_ch_chain); + //l_s_ch_chain->request_net_id.uint64 = l_net->pub.id.uint64; + //l_s_ch_chain->request_cell_id.uint64 = l_chain_cell_id_null.uint64; + //memcpy(&l_s_ch_chain->request, &l_sync_request, sizeof(l_sync_request)); + + if(0 == dap_stream_ch_chain_pkt_write(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_GLOBAL_DB, + l_net->pub.id, l_chain_id_null, l_chain_cell_id_null, &l_sync_request, + sizeof(l_sync_request))) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Error: Can't send sync chains request"); + // clean client struct + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_remote_node_info); + return -1; + } + dap_stream_ch_set_ready_to_write(l_ch_chain, true); + // wait for finishing of request + int timeout_ms = 420000; // 7 min = 420 sec = 420 000 ms + // TODO add progress info to console + res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); + if(res < 0) { + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_remote_node_info); + dap_chain_node_cli_set_reply_text(a_str_reply, "Error: can't sync with node "NODE_ADDR_FP_STR, + NODE_ADDR_FP_ARGS_S(l_node_client->remote_node_addr)); + + log_it(L_WARNING, "Gdb synced err -2"); + return -2; + + } + // flush global_db + dap_chain_global_db_flush(); + log_it(L_INFO, "Gdb synced Ok"); + + // Requesting chains + dap_chain_t *l_chain = NULL; + DL_FOREACH(l_net->pub.chains, l_chain) + { + // reset state NODE_CLIENT_STATE_SYNCED + l_node_client->state = NODE_CLIENT_STATE_CONNECTED; + // send request + dap_stream_ch_chain_sync_request_t l_sync_request = { { 0 } }; + if(0 == dap_stream_ch_chain_pkt_write(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_CHAINS, + l_net->pub.id, l_chain->id, l_remote_node_info->hdr.cell_id, &l_sync_request, + sizeof(l_sync_request))) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Error: Can't send sync chains request"); + // clean client struct + dap_chain_node_client_close(l_node_client); + DAP_DELETE(l_remote_node_info); + log_it(L_INFO, "Chain '%s' synced error: Can't send sync chains request", l_chain->name); + return -3; + } + log_it(L_NOTICE, "Requested syncronization for chain \"%s\"", l_chain->name); + dap_stream_ch_set_ready_to_write(l_ch_chain, true); + + // wait for finishing of request + timeout_ms = 120000; // 2 min = 120 sec = 120 000 ms + // TODO add progress info to console + res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); + if(res < 0) { + log_it(L_ERROR, "Error: Can't sync chain %s", l_chain->name); + } + } + log_it(L_INFO, "Chains and gdb are synced"); + DAP_DELETE(l_remote_node_info); + //dap_client_disconnect(l_node_client->client); + //l_node_client->client = NULL; + dap_chain_node_client_close(l_node_client); + dap_chain_node_cli_set_reply_text(a_str_reply, "Node sync completed: Chains and gdb are synced"); + return 0; + + } + // make handshake + case CMD_HANDSHAKE: { + // get address from alias if addr not defined + if(alias_str && !l_node_addr.uint64) { + dap_chain_node_addr_t *address_tmp = dap_chain_node_addr_get_by_alias(l_net, alias_str); + if(address_tmp) { + memcpy(&l_node_addr, address_tmp, sizeof(*address_tmp)); + DAP_DELETE(address_tmp); + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, "no address found by alias"); + return -4; + } + } + if(!l_node_addr.uint64) { + dap_chain_node_cli_set_reply_text(a_str_reply, "addr not found"); + return -5; + } + + dap_chain_node_info_t *node_info = node_info_read_and_reply(l_net, &l_node_addr, a_str_reply); + if(!node_info) + return -6; + int timeout_ms = 5000; //5 sec = 5000 ms + // start handshake + dap_chain_node_client_t *client = dap_chain_node_client_connect(node_info); + if(!client) { + dap_chain_node_cli_set_reply_text(a_str_reply, "can't connect"); + DAP_DELETE(node_info); + return -7; + } + // wait handshake + int res = dap_chain_node_client_wait(client, NODE_CLIENT_STATE_CONNECTED, timeout_ms); + if(res != 1) { + dap_chain_node_cli_set_reply_text(a_str_reply, "no response from node"); + // clean client struct + dap_chain_node_client_close(client); + DAP_DELETE(node_info); + return -8; + } + DAP_DELETE(node_info); + + //Add new established connection in the list + int ret = dap_chain_node_client_list_add(&l_node_addr, client); + switch (ret) + { + case -1: + dap_chain_node_client_close(client); + dap_chain_node_cli_set_reply_text(a_str_reply, "connection established, but not saved"); + return -9; + case -2: + dap_chain_node_client_close(client); + dap_chain_node_cli_set_reply_text(a_str_reply, "connection already present"); + return -10; + } + dap_chain_node_cli_set_reply_text(a_str_reply, "connection established"); + } + break; + } + return 0; +} + +/** + * Traceroute command + * + * return 0 OK, -1 Err + */ +int com_traceroute(int argc, char** argv, void *arg_func, char **str_reply) +{ +#ifndef _WIN32 + const char *addr = NULL; + int hops = 0, time_usec = 0; + if(argc > 1) + addr = argv[1]; + iputils_set_verbose(); + int res = (addr) ? traceroute_util(addr, &hops, &time_usec) : -EADDRNOTAVAIL; + if(res >= 0) { + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s hops=%d time=%.1lf ms", addr, hops, + time_usec * 1. / 1000); + } + else { + if(str_reply) { + switch (-res) + { + case EADDRNOTAVAIL: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", (addr) ? addr : "", + (addr) ? "Name or service not known" : "Host not defined"); + break; + case 2: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, + "Unknown traceroute module"); + break; + case 3: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, "first hop out of range"); + break; + case 4: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, + "max hops cannot be more than 255"); + break; + case 5: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, + "no more than 10 probes per hop"); + break; + case 6: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, + "bad wait specifications"); + break; + case 7: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, "too big packetlen "); + break; + case 8: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, + "IP version mismatch in addresses specified"); + break; + case 9: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, "bad sendtime"); + break; + case 10: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, "init_ip_options"); + break; + case 11: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, "calloc"); + break; + case 12: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, "parse cmdline"); + break; + case 13: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error: %s", addr, + "trace method's init failed"); + break; + default: + dap_chain_node_cli_set_reply_text(str_reply, "traceroute %s error(%d) %s", addr, res, + "trace not found"); + } + } + } + return res; +#endif + return 0; +} + +/** + * Tracepath command + * + * return 0 OK, -1 Err + */ +int com_tracepath(int argc, char** argv, void *arg_func, char **str_reply) +{ +#ifndef _WIN32 + const char *addr = NULL; + int hops = 0, time_usec = 0; + if(argc > 1) + addr = argv[1]; + iputils_set_verbose(); + int res = (addr) ? tracepath_util(addr, &hops, &time_usec) : -EADDRNOTAVAIL; + if(res >= 0) { + if(str_reply) + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s hops=%d time=%.1lf ms", addr, hops, + time_usec * 1. / 1000); + } + else { + if(str_reply) { + switch (-res) + { + case EADDRNOTAVAIL: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", (addr) ? addr : "", + (addr) ? "Name or service not known" : "Host not defined"); + break; + case ESOCKTNOSUPPORT: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't create socket"); + break; + case 2: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, + "Can't setsockopt IPV6_MTU_DISCOVER"); + break; + case 3: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, + "Can't setsockopt IPV6_RECVERR"); + break; + case 4: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, + "Can't setsockopt IPV6_HOPLIMIT"); + break; + case 5: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, + "Can't setsockopt IP_MTU_DISCOVER"); + break; + case 6: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, + "Can't setsockopt IP_RECVERR"); + break; + case 7: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, + "Can't setsockopt IP_RECVTTL"); + break; + case 8: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, "malloc"); + break; + case 9: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, + "Can't setsockopt IPV6_UNICAST_HOPS"); + break; + case 10: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IP_TTL"); + break; + default: + dap_chain_node_cli_set_reply_text(str_reply, "tracepath %s error(%d) %s", addr, res, "trace not found"); + } + } + } + return res; +#endif + return 0; +} + +/** + * Ping command + * + * return 0 OK, -1 Err + */ +int com_ping(int argc, char** argv, void *arg_func, char **str_reply) +{ +#ifndef _WIN32 + + int n = 4; + if(argc < 2) { + dap_chain_node_cli_set_reply_text(str_reply, "host not specified"); + return -1; + } + const char *n_str = NULL; + int argc_host = 1; + int argc_start = 1; + argc_start = dap_chain_node_cli_find_option_val(argv, argc_start, argc, "-n", &n_str); + if(argc_start) { + argc_host = argc_start + 1; + n = (n_str) ? atoi(n_str) : 4; + } + else { + argc_start = dap_chain_node_cli_find_option_val(argv, argc_start, argc, "-c", &n_str); + if(argc_start) { + argc_host = argc_start + 1; + n = (n_str) ? atoi(n_str) : 4; + } + } + if(n <= 1) + n = 1; + const char *addr = argv[argc_host]; + iputils_set_verbose(); + ping_handle_t *l_ping_handle = ping_handle_create(); + int res = (addr) ? ping_util(l_ping_handle, addr, n) : -EADDRNOTAVAIL; + DAP_DELETE(l_ping_handle); + if(res >= 0) { + if(str_reply) + dap_chain_node_cli_set_reply_text(str_reply, "ping %s time=%.1lf ms", addr, res * 1. / 1000); + } + else { + if(str_reply) { + switch (-res) + { + case EDESTADDRREQ: + dap_chain_node_cli_set_reply_text(str_reply, "ping %s error: %s", addr, "Destination address required"); + break; + case EADDRNOTAVAIL: + dap_chain_node_cli_set_reply_text(str_reply, "ping %s error: %s", (addr) ? addr : "", + (addr) ? "Host not found" : "Host not defined"); + break; + case EPFNOSUPPORT: + dap_chain_node_cli_set_reply_text(str_reply, "ping %s error: %s", addr, "Unknown protocol family"); + break; + default: + dap_chain_node_cli_set_reply_text(str_reply, "ping %s error(%d)", addr, -res); + } + } + } + return res; +#endif + return 0; +} + +/** + * Help command + */ +int com_help(int argc, char ** argv, void *arg_func, char **str_reply) +{ + if(argc > 1) { + log_it(L_DEBUG, "Help for command %s", argv[1]); + dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_find(argv[1]); + if(l_cmd) { + dap_chain_node_cli_set_reply_text(str_reply, "%s:\n%s", l_cmd->doc, l_cmd->doc_ex); + return 0; + } else { + dap_chain_node_cli_set_reply_text(str_reply, "command \"%s\" not recognized", argv[1]); + } + return -1; + } else { + // TODO Read list of commands & return it + log_it(L_DEBUG, "General help requested"); + dap_string_t * l_help_list_str = dap_string_new(NULL); + dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_get_first(); + dap_string_printf(l_help_list_str, ""); + while(l_cmd) { + dap_string_append_printf(l_help_list_str, "%s:\t\t\t%s\n", + l_cmd->name, l_cmd->doc ? l_cmd->doc : "(undocumented command)"); + l_cmd = (dap_chain_node_cmd_item_t*) l_cmd->hh.next; + } + dap_chain_node_cli_set_reply_text(str_reply, + "Available commands:\n\n%s\n", + l_help_list_str->len ? l_help_list_str->str : "NO ANY COMMAND WERE DEFINED"); + return 0; + } +} + +/** + * com_tx_create command + * + * Wallet info + */ +int com_tx_wallet(int argc, char ** argv, void *arg_func, char **str_reply) +{ + const char *c_wallets_path = dap_chain_wallet_get_path(g_config); + // Get address of wallet + enum { + CMD_NONE, CMD_WALLET_NEW, CMD_WALLET_LIST, CMD_WALLET_INFO + }; + int arg_index = 1; + int cmd_num = CMD_NONE; + // find add parameter ('alias' or 'handshake') + if(dap_chain_node_cli_find_option_val(argv, arg_index, min(argc, arg_index + 1), "new", NULL)) { + cmd_num = CMD_WALLET_NEW; + } + else if(dap_chain_node_cli_find_option_val(argv, arg_index, min(argc, arg_index + 1), "list", NULL)) { + cmd_num = CMD_WALLET_LIST; + } + else if(dap_chain_node_cli_find_option_val(argv, arg_index, min(argc, arg_index + 1), "info", NULL)) { + cmd_num = CMD_WALLET_INFO; + } + arg_index++; + if(cmd_num == CMD_NONE) { + dap_chain_node_cli_set_reply_text(str_reply, + "format of command: wallet [new -w <wallet_name> | list | info -addr <addr> -w <wallet_name> -net <net_name>]"); + return -1; + } + + dap_chain_node_addr_t address; + memset(&address, 0, sizeof(dap_chain_node_addr_t)); + const char *l_addr_str = NULL, *l_wallet_name = NULL, *l_net_name = NULL, *l_sign_type_str = NULL, *l_restore_str = NULL; + // find wallet addr + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-addr", &l_addr_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-w", &l_wallet_name); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-net", &l_net_name); + + dap_chain_net_t * l_net = l_net_name ? dap_chain_net_by_name( l_net_name) : NULL; + + dap_string_t *l_string_ret = dap_string_new(NULL); + switch (cmd_num) { + // new wallet + case CMD_WALLET_NEW: { + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-sign", &l_sign_type_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-restore", &l_restore_str); + // rewrite existing wallet + int l_is_force = dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-force", NULL); + + if(!l_wallet_name) { + dap_chain_node_cli_set_reply_text(str_reply, "wallet name option <-w> not defined"); + return -1; + } + // check wallet existence + if(!l_is_force) { + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path); + if(l_wallet) { + dap_chain_node_cli_set_reply_text(str_reply, "wallet already exists"); + dap_chain_wallet_close(l_wallet); + return -1; + } + } + +// dap_sign_type_t l_sign_type = { SIG_TYPE_BLISS }; + dap_sign_type_t l_sign_type = dap_sign_type_from_str(l_sign_type_str); + if(l_sign_type.type == SIG_TYPE_NULL){ + l_sign_type.type = SIG_TYPE_DILITHIUM; + l_sign_type_str = dap_sign_type_to_str(l_sign_type); + } + uint8_t *l_seed = NULL; + size_t l_seed_size = 0; + size_t l_restore_str_size = dap_strlen(l_restore_str); + if(l_restore_str && l_restore_str_size > 2 && !dap_strncmp(l_restore_str, "0x", 2)) { + l_seed_size = (l_restore_str_size - 2) / 2; + l_seed = DAP_NEW_SIZE(uint8_t, l_seed_size); + if(!dap_hex2bin(l_seed, l_restore_str + 2, l_restore_str_size - 2)){ + DAP_DELETE(l_seed); + l_seed = NULL; + l_seed_size = 0; + } + } + // Creates new wallet + dap_chain_wallet_t *l_wallet = dap_chain_wallet_create_with_seed(l_wallet_name, c_wallets_path, l_sign_type, + l_seed, l_seed_size); + dap_chain_addr_t *l_addr = l_net? dap_chain_wallet_get_addr(l_wallet,l_net->pub.id ) : NULL; + if(!l_wallet) { + dap_chain_node_cli_set_reply_text(str_reply, "wallet is not created"); + return -1; + } + char *l_addr_str = l_addr? dap_chain_addr_to_str(l_addr) : NULL; + dap_string_append_printf(l_string_ret, "wallet '%s' (type=%s) successfully created\n", l_wallet->name, l_sign_type_str); + if ( l_addr_str ) + dap_string_append_printf(l_string_ret, "new address %s", l_addr_str); + DAP_DELETE(l_addr_str); + dap_chain_wallet_close(l_wallet); + } + break; + // wallet list + case CMD_WALLET_LIST: { + DIR * l_dir = opendir(c_wallets_path); + if(l_dir) { + struct dirent * l_dir_entry; + while((l_dir_entry = readdir(l_dir)) != NULL) { + const char *l_file_name = l_dir_entry->d_name; + size_t l_file_name_len = (l_file_name) ? strlen(l_file_name) : 0; + if((l_file_name_len > 8) && (strcmp(l_file_name + l_file_name_len - 8, ".dwallet") == 0)) { + char *l_file_path_tmp = dap_strdup_printf("%s/%s", c_wallets_path, l_file_name); + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open_file(l_file_path_tmp); + if(l_wallet) { + dap_chain_addr_t *l_addr = l_net? dap_chain_wallet_get_addr(l_wallet, l_net->pub.id) : NULL; + char *l_addr_str = dap_chain_addr_to_str(l_addr); + dap_string_append_printf(l_string_ret, "\nwallet: %s\n", l_wallet->name); + if ( l_addr_str){ + dap_string_append_printf(l_string_ret, "addr: %s\n", (l_addr_str) ? l_addr_str : "-"); + DAP_DELETE(l_addr_str); + } + dap_chain_wallet_close(l_wallet); + } + DAP_DELETE(l_file_path_tmp); + } + } + closedir(l_dir); + } + } + break; + + // wallet info + case CMD_WALLET_INFO: { + dap_chain_wallet_t *l_wallet = NULL; + dap_chain_addr_t *l_addr = NULL; + + if(l_wallet_name) { + l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path); + if ( l_net ) + l_addr = (dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet, l_net->pub.id ); + } + if(!l_addr && l_addr_str) + l_addr = dap_chain_addr_from_str(l_addr_str); + + dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name((const char *) l_net_name); + if(!l_net_name) { + dap_chain_node_cli_set_reply_text(str_reply, "wallet info requires parameter 'net'"); + return -1; + } + else { + if((l_ledger = dap_chain_ledger_by_net_name(l_net_name)) == NULL) { + dap_chain_node_cli_set_reply_text(str_reply, "not found net by name '%s'", l_net_name); + return -1; + } + } + + if(l_addr) { + char *l_addr_str = dap_chain_addr_to_str((dap_chain_addr_t*) l_addr); + if(l_wallet) + dap_string_append_printf(l_string_ret, "wallet: %s\n", l_wallet->name); + dap_string_append_printf(l_string_ret, "addr: %s\n", (l_addr_str) ? l_addr_str : "-"); + + size_t l_addr_tokens_size = 0; + char **l_addr_tokens = NULL; + //dap_chain_ledger_addr_get_token_ticker_all(l_ledger, l_addr, &l_addr_tokens, &l_addr_tokens_size); + // seriously?... + dap_chain_ledger_addr_get_token_ticker_all_fast(l_ledger, l_addr, &l_addr_tokens, &l_addr_tokens_size); + if(l_addr_tokens_size > 0) + dap_string_append_printf(l_string_ret, "balance:\n"); + else + dap_string_append_printf(l_string_ret, "balance:\u00a00"); + for(size_t i = 0; i < l_addr_tokens_size; i++) { + if(l_addr_tokens[i]) { + uint64_t l_balance = dap_chain_ledger_calc_balance(l_ledger, l_addr, l_addr_tokens[i]); + long double l_balance_coins = (long double) l_balance / DATOSHI_LD ; + //dap_string_append_printf(l_string_ret, " %.3Lf (%llu) %s\n", l_balance_coins, + dap_string_append_printf(l_string_ret, "\t\u00a0%.3Lf (%llu) %s", l_balance_coins, + l_balance, l_addr_tokens[i]); + if(i < l_addr_tokens_size - 1) + dap_string_append_printf(l_string_ret, "\n"); + + } + DAP_DELETE(l_addr_tokens[i]); + } + DAP_DELETE(l_addr_tokens); + DAP_DELETE(l_addr_str); + if(l_wallet) + dap_chain_wallet_close(l_wallet); + } + else { + if(l_wallet) + dap_chain_wallet_close(l_wallet); + dap_string_free(l_string_ret, true); + dap_chain_node_cli_set_reply_text(str_reply, "wallet not found"); + return -1; + } + } + break; + } + + char *l_str_ret_tmp = dap_string_free(l_string_ret, false); + char *str_ret = dap_strdup(l_str_ret_tmp); + dap_chain_node_cli_set_reply_text(str_reply, str_ret); + DAP_DELETE(l_str_ret_tmp); + return 0; +} + +/** + * @brief s_values_parse_net_chain + * @param argc + * @param argv + * @param str_reply + * @param l_chain + * @param l_net + * @return + */ +int dap_chain_node_cli_cmd_values_parse_net_chain(int *a_arg_index, int argc, char ** argv, char ** a_str_reply, + dap_chain_t ** a_chain, dap_chain_net_t ** a_net) +{ + const char * l_chain_str = NULL; + const char * l_net_str = NULL; + + // Net name + if(a_net) + dap_chain_node_cli_find_option_val(argv, *a_arg_index, argc, "-net", &l_net_str); + else + return -100; + + // Select network + if(!l_net_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "%s requires parameter '-net'", argv[0]); + return -101; + } + + if((*a_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network + dap_chain_node_cli_set_reply_text(a_str_reply, "%s can't find network \"%s\"", argv[0], l_net_str); + return -102; + } + + // Chain name + if(a_chain) { + dap_chain_node_cli_find_option_val(argv, *a_arg_index, argc, "-chain", &l_chain_str); + + // Select chain + if(l_chain_str) { + if((*a_chain = dap_chain_net_get_chain_by_name(*a_net, l_chain_str)) == NULL) { // Can't find such chain + dap_chain_node_cli_set_reply_text(a_str_reply, + "%s requires parameter '-chain' to be valid chain name in chain net %s", + argv[0], l_net_str); + return -103; + } + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "%s requires parameter '-chain'", argv[0]); + return -104; + } + } + return 0; + +} + +/** + * @brief com_token_decl_sign + * @param argc + * @param argv + * @param arg_func + * @param str_reply + * @return + */ +int com_token_decl_sign(int argc, char ** argv, void *arg_func, char ** a_str_reply) +{ + int arg_index = 1; + + const char * l_datum_hash_str = NULL; + // Chain name + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-datum", &l_datum_hash_str); + + if(l_datum_hash_str) { + const char * l_certs_str = NULL; + dap_cert_t ** l_certs = NULL; + size_t l_certs_count = 0; + dap_chain_t * l_chain; + + dap_chain_net_t * l_net = NULL; + + dap_chain_node_cli_cmd_values_parse_net_chain(&arg_index, argc, argv, a_str_reply, &l_chain, &l_net); + if(!l_net) + return -1; + else { + if(*a_str_reply) { + DAP_DELETE(*a_str_reply); + *a_str_reply = NULL; + } + } + + // Certificates thats will be used to sign currend datum token + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-certs", &l_certs_str); + + // Load certs lists + if (l_certs_str) + dap_cert_parse_str_list(l_certs_str, &l_certs, &l_certs_count); + + if(!l_certs_count) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "token_sign command requres at least one valid certificate to sign the basic transaction of emission"); + return -7; + } + + char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + if(!l_gdb_group_mempool) { + l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TOKEN); + } + + log_it(L_DEBUG, "Requested to sign token declaration %s in gdb://%s with certs %s", + l_gdb_group_mempool, l_datum_hash_str, l_certs_str); + + dap_chain_datum_t * l_datum = NULL; + size_t l_datum_size = 0; + if((l_datum = (dap_chain_datum_t*) dap_chain_global_db_gr_get( + l_datum_hash_str, &l_datum_size, l_gdb_group_mempool)) != NULL) { + + // Check if its token declaration + if(l_datum->header.type_id == DAP_CHAIN_DATUM_TOKEN_DECL) { + dap_chain_datum_token_t * l_datum_token = (dap_chain_datum_token_t *) l_datum->data; + size_t l_datum_token_size = l_datum->header.data_size; + size_t l_signs_size = l_datum_token_size - sizeof(l_datum_token->header_private); + + // Check for signatures, are they all in set and are good enought? + size_t l_signs_count = 0; + + for(size_t l_offset = 0; l_offset < l_signs_size; l_signs_count++) { + dap_sign_t * l_sign = (dap_sign_t *) l_datum_token->data + l_offset; + l_offset += dap_sign_get_size(l_sign); + if( dap_sign_verify(l_sign, l_datum_token, sizeof(l_datum_token->header_private)) != 1) { + log_it(L_WARNING, "Wrong signature %u for datum_token with key %s in mempool!", l_signs_count, l_datum_hash_str); + dap_chain_node_cli_set_reply_text(a_str_reply, + "Datum %s with datum token has wrong signature %u, break process and exit", + l_datum_hash_str, l_signs_count ); + DAP_DELETE(l_datum); + //DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + return -666; + }else{ + log_it(L_DEBUG,"Sign %lu passed",l_signs_count); + } + } + log_it(L_DEBUG, "Datum %s with token declaration: %u signatures are verified well (sign_size = %u)",l_datum_hash_str, + l_signs_count, l_signs_size); + + // Check if all signs are present + if(l_signs_count == l_datum_token->header_private.signs_total) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Datum %s with datum token has all signs on board. Can't add anything in it"); + DAP_DELETE(l_datum); + //DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + return -7; + } // Check if more signs that could be (corrupted datum) + else if(l_signs_count > l_datum_token->header_private.signs_total) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Warning! Datum %s with datum token has more signs on board (%u) than its possible to have (%u)!", + l_signs_count, l_datum_token->header_private.signs_total); + DAP_DELETE(l_datum); + //DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + return -8; + } // Check if we have enough place to sign the datum token declaration + else if(l_datum_token->header_private.signs_total >= l_signs_count + l_certs_count) { + size_t l_offset = 0; + for(size_t i = 0; i < l_certs_count; i++) { + dap_sign_t * l_sign = dap_sign_create(l_certs[i]->enc_key, + l_datum_token, + sizeof(l_datum_token->header_private), 0); + size_t l_sign_size = dap_sign_get_size(l_sign); + + + l_signs_size+= l_sign_size; + l_datum_size += l_sign_size; + l_datum_token_size+= l_sign_size; + + if ( l_datum = DAP_REALLOC(l_datum, l_datum_size) ){ // add place for new signatures + l_datum_token = (dap_chain_datum_token_t*) l_datum->data; + l_datum->header.data_size = l_datum_token_size; + memcpy(l_datum_token->data + l_offset, l_sign, l_sign_size); + log_it(L_DEBUG, "Added datum token declaration sign with cert %s (new size %lu)", + l_certs[i]->name , l_datum_size); + DAP_DELETE(l_sign); + + l_offset += l_sign_size; + } else{ + log_it(L_ERROR, "Can't allocate more memory for datum token"); + return -81; + } + } + + + // Recalc hash, string and place new datum + + // Calc datum's hash + dap_chain_hash_fast_t l_key_hash={0}; + dap_hash_fast(l_datum, l_datum_size, &l_key_hash); + char * l_key_str = dap_chain_hash_fast_to_str_new(&l_key_hash); + + // Add datum to mempool with datum_token hash as a key + if(dap_chain_global_db_gr_set(dap_strdup(l_key_str), (uint8_t *) l_datum, l_datum_size, l_gdb_group_mempool)) { + + char* l_hash_str = strdup(l_datum_hash_str); + // Remove old datum from pool + if( dap_chain_global_db_gr_del( dap_strdup(l_hash_str) , l_gdb_group_mempool)) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "datum %s produced from %s is replacing the %s in datum pool", + l_key_str, l_datum_hash_str, l_datum_hash_str); + + DAP_DELETE(l_datum); + //DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + return 0; + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Warning! Can't remove old datum %s ( new datum %s added normaly in datum pool)", + l_datum_hash_str, l_key_str); + DAP_DELETE(l_datum); + //DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + return 1; + } + DAP_DELETE(l_hash_str); + DAP_DELETE(l_key_str); + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Error! datum %s produced from %s can't be placed in mempool", + l_key_str, l_datum_hash_str); + DAP_DELETE(l_datum); + //DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + DAP_DELETE(l_key_str); + return -2; + } + + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Error! Not enought place for new signature (%u is left when we need %u signatures)", + l_datum_token->header_private.signs_total - l_signs_count, l_certs_count); + return -6; + } + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Error! Wrong datum type. token_decl_sign sign only token declarations datum"); + return -61; + } + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "token_decl_sign can't find datum with %s hash in the mempool of %s:%s",l_datum_hash_str, l_net->pub.name, + l_chain->name); + return -5; + } + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, "token_decl_sign need datum <datum hash> argument"); + return -2; + } +} + +/** + * @brief com_token_decl_list + * @param argc + * @param argv + * @param arg_func + * @param str_reply + * @return + */ +int com_mempool_list(int argc, char ** argv, void *arg_func, char ** a_str_reply) +{ + int arg_index = 1; + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + dap_chain_node_cli_cmd_values_parse_net_chain(&arg_index, argc, argv, a_str_reply, &l_chain, &l_net); + if(!l_net) + return -1; + else { + if(*a_str_reply) { + DAP_DELETE(*a_str_reply); + *a_str_reply = NULL; + } + } + + if(l_net) { + char * l_gdb_group_mempool = NULL, *l_gdb_group_mempool_tmp; + if(l_chain) { + l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + l_gdb_group_mempool_tmp = l_gdb_group_mempool; + } + dap_string_t * l_str_tmp = dap_string_new(NULL); + + DL_FOREACH(l_net->pub.chains, l_chain) { + if(!l_gdb_group_mempool) { + l_gdb_group_mempool_tmp = dap_chain_net_get_gdb_group_mempool(l_chain); + } + size_t l_objs_size = 0; + dap_global_db_obj_t * l_objs = dap_chain_global_db_gr_load(l_gdb_group_mempool_tmp, &l_objs_size); + if(l_objs_size > 0) + dap_string_append_printf(l_str_tmp, "%s.%s: Found %u records :\n", l_net->pub.name, l_chain->name, + l_objs_size); + else + dap_string_append_printf(l_str_tmp, "%s.%s: Not found records\n", l_net->pub.name, l_chain->name); + for(size_t i = 0; i < l_objs_size; i++) { + dap_chain_datum_t * l_datum = (dap_chain_datum_t*) l_objs[i].value; + char buf[50]; + time_t l_ts_create = (time_t) l_datum->header.ts_create; + dap_string_append_printf(l_str_tmp, "%s: type_id=%s data_size=%u ts_create=%s", // \n included in timestamp + l_objs[i].key, c_datum_type_str[l_datum->header.type_id], + l_datum->header.data_size, ctime_r(&l_ts_create, buf)); + if ( l_datum->header.type_id == DAP_CHAIN_DATUM_TOKEN_DECL ){ + dap_chain_datum_token_t * l_datum_token = (dap_chain_datum_token_t *) l_datum->data; + dap_string_append_printf(l_str_tmp, + "\tDAP_CHAIN_DATUM_TOKEN_DECL: type=%u ticker=\"%s\" signs_total=%u signs_valid=%u\n", + l_datum_token->type, l_datum_token->ticker, + l_datum_token->header_private.signs_total, l_datum_token->header_private.signs_valid ); + } + } + // Clean up + dap_chain_global_db_objs_delete(l_objs, l_objs_size); + if (l_gdb_group_mempool_tmp) + DAP_DELETE(l_gdb_group_mempool_tmp); + // only one time if group defined + if(l_gdb_group_mempool) { + break; + } + } + dap_chain_node_cli_set_reply_text(a_str_reply, l_str_tmp->str); + dap_string_free(l_str_tmp, false); + + return 0; + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Error! Need both -net <network name> and -chain <chain name> params\n"); + return -2; + } +} + +/** + * @brief com_mempool_delete + * @param argc + * @param argv + * @param arg_func + * @param a_str_reply + * @return + */ +int com_mempool_delete(int argc, char ** argv, void *arg_func, char ** a_str_reply) +{ + int arg_index = 1; + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + if(dap_chain_node_cli_cmd_values_parse_net_chain(&arg_index, argc, argv, a_str_reply, &l_chain, &l_net) != 0) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Error! Need both -net <network name> and -chain <chain name> params\n"); + return -1; + } + + if(l_chain && l_net) { // UNUSED(l_net) + const char * l_datum_hash_str = NULL; + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-datum", &l_datum_hash_str); + if(l_datum_hash_str) { + char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + if(dap_chain_global_db_gr_del(strdup(l_datum_hash_str), l_gdb_group_mempool)) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Datum %s deleted", l_datum_hash_str); + return 0; + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, "Error! Can't find datum %s", l_datum_hash_str); + return -4; + } + DAP_DELETE(l_gdb_group_mempool); + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, "Error! %s requires -datum <datum hash> option", argv[0]); + return -3; + } + } else { + dap_chain_node_cli_set_reply_text(a_str_reply, + "Error! Need both -net <network name> and -chain <chain name> params\n"); + return -2; + } +} + +/** + * @brief com_mempool_proc + * @param argc + * @param argv + * @param arg_func + * @param a_str_reply + * @return + */ +int com_mempool_proc(int argc, char ** argv, void *arg_func, char ** a_str_reply) +{ + int arg_index = 1; + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + dap_chain_node_cli_cmd_values_parse_net_chain(&arg_index, argc, argv, a_str_reply, &l_chain, &l_net); + if(!l_net) + return -1; + else { + if(*a_str_reply) { + DAP_DELETE(*a_str_reply); + *a_str_reply = NULL; + } + } + char * l_gdb_group_mempool = NULL, *l_gdb_group_mempool_tmp; + if(l_chain) { + l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + l_gdb_group_mempool_tmp = l_gdb_group_mempool; + } + + dap_string_t * l_str_tmp = dap_string_new(NULL); + DL_FOREACH(l_net->pub.chains, l_chain) { + if(!l_gdb_group_mempool) { + l_gdb_group_mempool_tmp = dap_chain_net_get_gdb_group_mempool(l_chain); + } + size_t l_objs_size = 0; + dap_global_db_obj_t * l_objs = dap_chain_global_db_gr_load(l_gdb_group_mempool_tmp, &l_objs_size); + if(l_objs_size) { + dap_string_append_printf(l_str_tmp, "%s.%s: Found %u records :\n", l_net->pub.name, l_chain->name, + l_objs_size); + size_t l_datums_size = l_objs_size; + dap_chain_datum_t ** l_datums = DAP_NEW_Z_SIZE(dap_chain_datum_t*, + sizeof(dap_chain_datum_t*) * l_datums_size); + size_t l_objs_size_tmp = (l_objs_size > 15) ? min(l_objs_size, 10) : l_objs_size; + for(size_t i = 0; i < l_objs_size; i++) { + dap_chain_datum_t * l_datum = (dap_chain_datum_t*) l_objs[i].value; + l_datums[i] = l_datum; + if(i < l_objs_size_tmp) { + char buf[50]; + time_t l_ts_create = (time_t) l_datum->header.ts_create; + dap_string_append_printf(l_str_tmp, "0x%s: type_id=%s ts_create=%s data_size=%u\n", + l_objs[i].key, c_datum_type_str[l_datum->header.type_id], + ctime_r(&l_ts_create, buf), l_datum->header.data_size); + } + } + if(l_objs_size > 15) { + dap_string_append_printf(l_str_tmp, "...\n"); + } + + size_t l_objs_processed = 0; + bool l_procecced[l_objs_size]; + for(size_t i = 0; i < l_objs_size; i++) { + int l_is_processed = l_chain->callback_datums_pool_proc(l_chain, l_datums + i, 1); //l_datums_size + l_objs_processed += l_is_processed; + l_procecced[i] = l_is_processed; + } + DAP_DELETE(l_datums); + // Delete processed objects + size_t l_objs_processed_tmp = (l_objs_processed > 15) ? min(l_objs_processed, 10) : l_objs_processed; + size_t l_objs_processed_cur = 0; + for(size_t i = 0; i < l_datums_size; i++) { + if(l_procecced[i]!=1) + continue; + dap_chain_global_db_gr_del( dap_strdup(l_objs[i].key), l_gdb_group_mempool_tmp); + l_objs_processed_cur++; + if(l_objs_processed_cur < l_objs_processed_tmp) { + dap_string_append_printf(l_str_tmp, "New event created, removed datum 0x%s from mempool \n", + l_objs[i].key); + } + } + if(l_objs_processed > 15) { + dap_string_append_printf(l_str_tmp, "...\n"); + } + if(l_objs_processed < l_datums_size) + dap_string_append_printf(l_str_tmp, "%s.%s: %d records not processed\n", l_net->pub.name, l_chain->name, + l_datums_size - l_objs_processed); + dap_chain_global_db_objs_delete(l_objs, l_objs_size); + } + else { + dap_string_append_printf(l_str_tmp, "%s.%s: No records in mempool\n", l_net->pub.name, l_chain ? l_chain->name : "[no chain]"); + } + DAP_DELETE(l_gdb_group_mempool); + // only one time if group defined + if(l_gdb_group_mempool) + break; + } + dap_chain_node_cli_set_reply_text(a_str_reply, l_str_tmp->str); + dap_string_free(l_str_tmp, true); + return 0; +} + + +/** + * @brief com_token_decl_update + * @param argc + * @param argv + * @param arg_func + * @param str_reply + * @return + * @details token_decl_update -net <net name> -chain <chain name> -token <token ticker> -type private -flags [<Flag 1>][,<Flag 2>]...[,<Flag N>]... [-<Param name 1> <Param Value 1>] [-Param name 2> <Param Value 2>] ...[-<Param Name N> <Param Value N>]\n" + * \t Updatetoken for <netname>:<chain name> with ticker <token ticker>, flags <Flag 1>,<Flag2>...<Flag N>" + * \t and custom parameters list <Param 1>, <Param 2>...<Param N>." + * \n" + * ==Flags==" + * \t ALL_BLOCKED:\t Blocked all permissions, usefull add it first and then add allows what you want to allow\n" + * \t ALL_ALLOWED:\t Allowed all permissions if not blocked them. Be careful with this mode\n" + * \t ALL_FROZEN:\t All permissions are temprorary frozen\n" + * \t ALL_UNFROZEN:\t Unfrozen permissions\n" + * \t STATIC_ALL:\t No token manipulations after declarations at all. Token declares staticly and can't variabed after\n" + * \t STATIC_FLAGS:\t No token manipulations after declarations with flags\n" + * \t STATIC_PERMISSIONS_ALL:\t No all permissions lists manipulations after declarations\n" + * \t STATIC_PERMISSIONS_DATUM_TYPE:\t No datum type permissions lists manipulations after declarations\n" + * \t STATIC_PERMISSIONS_TX_SENDER:\t No tx sender permissions lists manipulations after declarations\n" + * \t STATIC_PERMISSIONS_TX_RECEIVER:\t No tx receiver permissions lists manipulations after declarations\n" + "\n" + "==Params==\n" + "General:\n" + "\t -flags_set <value>:\t Set list of flags from <value> to token declaration\n" + "\t -flags_unset <value>:\t Unset list of flags from <value> from token declaration\n" + "\t -total_supply <value>:\t Set total supply - emission's maximum - to the <value>\n" + "\t -total_signs_valid <value>:\t Set valid signatures count's minimum\n" + "\t -total_signs_add <value>:\t Add signature's pkey fingerprint to the list of owners\n" + "\t -total_signs_remove <value>:\t Remove signature's pkey fingerprint from the owners\n" + "\nDatum type allowed/blocked updates:\n" + "\t -datum_type_allowed_add <value>:\t Add allowed datum type(s)\n" + "\t -datum_type_allowed_remove <value>:\t Remove datum type(s) from allowed\n" + "\t -datum_type_allowed_clear:\t Remove all datum types from allowed\n" + "\t -datum_type_blocked_add <value>:\t Add blocked datum type(s)\n" + "\t -datum_type_blocked_remove <value>:\t Remove datum type(s) from blocked\n" + "\t -datum_type_blocked_clear:\t Remove all datum types from blocked\n" + "\nTx receiver addresses allowed/blocked updates:\n" + "\t -tx_receiver_allowed_add <value>:\t Add allowed tx receiver(s)\n" + "\t -tx_receiver_allowed_remove <value>:\t Remove tx receiver(s) from allowed\n" + "\t -tx_receiver_allowed_clear:\t Remove all tx receivers from allowed\n" + "\t -tx_receiver_blocked_add <value>:\t Add blocked tx receiver(s)\n" + "\t -tx_receiver_blocked_remove <value>:\t Remove tx receiver(s) from blocked\n" + "\t -tx_receiver_blocked_clear:\t Remove all tx receivers from blocked\n" + "\n Tx sender addresses allowed/blocked updates:\n" + "\t -tx_sender_allowed_add <value>:\t Add allowed tx sender(s)\n" + "\t -tx_sender_allowed_remove <value>:\t Remove tx sender(s) from allowed\n" + "\t -tx_sender_allowed_clear:\t Remove all tx senders from allowed\n" + "\t -tx_sender_blocked_add <value>:\t Add allowed tx sender(s)\n" + "\t -tx_sender_blocked_remove <value>:\t Remove tx sender(s) from blocked\n" + "\t -tx_sender_blocked_clear:\t Remove all tx sender(s) from blocked\n" + "\n" + */ +int com_token_decl_update(int argc, char ** argv, void *arg_func, char ** a_str_reply) +{ + return -1; +} + +/** + * @brief com_token_decl + * @param argc + * @param argv + * @param arg_func + * @param str_reply + * @return + * @details token_decl -net <net name> -chain <chain name> -token <token ticker> -total_supply <total supply> -signs_total <sign total> -signs_emission <signs for emission> -certs <certs list>\n" + * \t Declare new simple token for <netname>:<chain name> with ticker <token ticker>, maximum emission <total supply> and <signs for emission> from <signs total> signatures on valid emission\n" + * \t Extended private token declaration\n" + * \t token_decl -net <net name> -chain <chain name> -token <token ticker> -type private -flags [<Flag 1>][,<Flag 2>]...[,<Flag N>]... [-<Param name 1> <Param Value 1>] [-Param name 2> <Param Value 2>] ...[-<Param Name N> <Param Value N>]\n" + * \t Declare new token for <netname>:<chain name> with ticker <token ticker>, flags <Flag 1>,<Flag2>...<Flag N>" + * \t and custom parameters list <Param 1>, <Param 2>...<Param N>." + * \n" + * ==Flags==" + * \t ALL_BLOCKED:\t Blocked all permissions, usefull add it first and then add allows what you want to allow\n" + * \t ALL_ALLOWED:\t Allowed all permissions if not blocked them. Be careful with this mode\n" + * \t ALL_FROZEN:\t All permissions are temprorary frozen\n" + * \t ALL_UNFROZEN:\t Unfrozen permissions\n" + * \t STATIC_ALL:\t No token manipulations after declarations at all. Token declares staticly and can't variabed after\n" + * \t STATIC_FLAGS:\t No token manipulations after declarations with flags\n" + * \t STATIC_PERMISSIONS_ALL:\t No all permissions lists manipulations after declarations\n" + * \t STATIC_PERMISSIONS_DATUM_TYPE:\t No datum type permissions lists manipulations after declarations\n" + * \t STATIC_PERMISSIONS_TX_SENDER:\t No tx sender permissions lists manipulations after declarations\n" + * \t STATIC_PERMISSIONS_TX_RECEIVER:\t No tx receiver permissions lists manipulations after declarations\n" + "\n" + "==Params==\n" + "General:\n" + "\t -flags <value>:\t Set list of flags from <value> to token declaration\n" + "\t -total_supply <value>:\t Set total supply - emission's maximum - to the <value>\n" + "\t -signs_valid <value>:\t Set valid signatures count's minimum\n" + "\t -signs <value>:\t Add signature's pkey fingerprint to the list of owners\n" + "\nDatum type allowed/blocked:\n" + "\t -datum_type_allowed <value>:\t Allowed datum type(s)\n" + "\t -datum_type_blocked <value>:\t Blocked datum type(s)\n" + "\nTx receiver addresses allowed/blocked:\n" + "\t -tx_receiver_allowed <value>:\t Allowed tx receiver(s)\n" + "\t -tx_receiver_blocked <value>:\t Blocked tx receiver(s)\n" + "\n Tx sender addresses allowed/blocked:\n" + "\t -tx_sender_allowed <value>:\t Allowed tx sender(s)\n" + "\t -tx_sender_blocked <value>:\t Blocked tx sender(s)\n" + "\n" + */ +int com_token_decl(int argc, char ** argv, void *arg_func, char ** a_str_reply) +{ + int l_arg_index = 1; + + const char * l_type_str = NULL; + uint16_t l_type = DAP_CHAIN_DATUM_TOKEN_PRIVATE; + + const char * l_ticker = NULL; + + const char * l_total_supply_str = NULL; + uint64_t l_total_supply = 0; + + const char * l_signs_emission_str = NULL; + uint16_t l_signs_emission = 0; + + const char * l_signs_total_str = NULL; + uint16_t l_signs_total = 0; + + const char * l_certs_str = NULL; + + dap_cert_t ** l_certs = NULL; + size_t l_certs_size = 0; + + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + dap_chain_node_cli_cmd_values_parse_net_chain(&l_arg_index, argc, argv, a_str_reply, &l_chain, &l_net); + if(!l_net) + return -1; + else { + if(*a_str_reply) { + DAP_DELETE(*a_str_reply); + *a_str_reply = NULL; + } + } + // Token ticker + l_arg_index=dap_chain_node_cli_find_option_val(argv, l_arg_index, argc, "-token", &l_ticker); + // Check for ticker + if(!l_ticker) { + dap_chain_node_cli_set_reply_text(a_str_reply, "token_decl requires parameter 'token'"); + return -2; + } + + // Token type + l_arg_index=dap_chain_node_cli_find_option_val(argv, l_arg_index, argc, "-type", &l_type_str); + + if (strcmp( l_type_str, "private") == 0){ + l_type = DAP_CHAIN_DATUM_TOKEN_PRIVATE_DECL; + }else if (strcmp( l_type_str, "private_simple") == 0){ + l_type = DAP_CHAIN_DATUM_TOKEN_PRIVATE; + }else if (strcmp( l_type_str, "public_simple") == 0){ + l_type = DAP_CHAIN_DATUM_TOKEN_PUBLIC; + } + + dap_chain_datum_token_t * l_datum_token = NULL; + size_t l_signs_offset = 0; + + switch(l_type){ + case DAP_CHAIN_DATUM_TOKEN_PRIVATE_DECL:{ + dap_list_t *l_tsd_list = dap_list_alloc(); + size_t l_tsd_size = 0; + uint16_t l_flags = 0; + while (l_arg_index<argc){ + if ( strcmp( argv[l_arg_index],"-flags" )){ + }else if ( strcmp( argv[l_arg_index],"-total_supply" )){ + + }else if ( strcmp( argv[l_arg_index],"-signs_valid" )){ + + }else if ( strcmp( argv[l_arg_index],"-signs" )){ + + }else if ( strcmp( argv[l_arg_index],"-datum_type_allowed" )){ + + }else if ( strcmp( argv[l_arg_index],"-datum_type_blocked" )){ + + }else if ( strcmp( argv[l_arg_index],"-tx_receiver_allowed" )){ + + }else if ( strcmp( argv[l_arg_index],"-tx_receiver_blocked" )){ + + }else if ( strcmp( argv[l_arg_index],"-tx_sender_allowed" )){ + + }else if ( strcmp( argv[l_arg_index],"-tx_sender_blocked" )){ + + }else { + + } + ++l_arg_index; + } + }break; + case DAP_CHAIN_DATUM_TOKEN_PRIVATE:{ + // Total supply value + dap_chain_node_cli_find_option_val(argv, l_arg_index, argc, "-total_supply", &l_total_supply_str); + + + // Certificates thats will be used to sign currend datum token + dap_chain_node_cli_find_option_val(argv, l_arg_index, argc, "-certs", &l_certs_str); + + // Signs number thats own emissioncan't find + dap_chain_node_cli_find_option_val(argv, l_arg_index, argc, "-signs_total", &l_signs_total_str); + + // Signs minimum number thats need to authorize the emission + dap_chain_node_cli_find_option_val(argv, l_arg_index, argc, "-signs_emission", &l_signs_emission_str); + + + if(!l_total_supply_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "token_create requires parameter '-total_supply'"); + return -3; + } else { + char * l_tmp = NULL; + if((l_total_supply = strtoull(l_total_supply_str, &l_tmp, 10)) == 0) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "token_create requires parameter '-total_supply' to be unsigned integer value that fits in 8 bytes"); + return -4; + } + } + + + + // Signs emission + if(!l_signs_emission_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "token_create requires parameter '-signs_emission'"); + return -5; + } else { + char * l_tmp = NULL; + if((l_signs_emission = (uint16_t) strtol(l_signs_emission_str, &l_tmp, 10)) == 0) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "token_create requires parameter 'signs_emission' to be unsigned integer value that fits in 2 bytes"); + return -6; + } + } + + // Signs total + if(!l_signs_total_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "token_decl requires parameter 'signs_total'"); + return -7; + } else { + char * l_tmp = NULL; + if((l_signs_total = (uint16_t) strtol(l_signs_total_str, &l_tmp, 10)) == 0) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "token_create requires parameter 'signs_total' to be unsigned integer value that fits in 2 bytes"); + return -8; + } + } + + // Check certs list + if(!l_certs_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "token_decl requires parameter 'certs'"); + return -9; + } + + // Load certs lists + dap_cert_parse_str_list(l_certs_str, &l_certs, &l_certs_size); + if(!l_certs_size) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "token_decl command requres at least one valid certificate to sign the basic transaction of emission"); + return -10; + } + + // If we have more certs than we need signs - use only first part of the list + if(l_certs_size > l_signs_total) + l_certs_size = l_signs_total; + + // Create new datum token + l_datum_token = DAP_NEW_Z_SIZE(dap_chain_datum_token_t, sizeof(dap_chain_datum_token_t)); + l_datum_token->type = DAP_CHAIN_DATUM_TOKEN_PRIVATE; + dap_snprintf(l_datum_token->ticker, sizeof(l_datum_token->ticker), "%s", l_ticker); + l_datum_token->header_private.total_supply = l_total_supply; + l_datum_token->header_private.signs_total = l_signs_total; + l_datum_token->header_private.signs_valid = l_signs_emission; + + // Sign header with all certificates in the list and add signs to the end of ticker declaration + // Important: + for(size_t i = 0; i < l_certs_size; i++) { + dap_sign_t * l_sign = dap_cert_sign(l_certs[i], + l_datum_token, + sizeof(l_datum_token->header_private), + 0); + size_t l_sign_size = dap_sign_get_size(l_sign); + l_datum_token = DAP_REALLOC(l_datum_token, sizeof(dap_chain_datum_token_t) + l_signs_offset + l_sign_size); + memcpy(l_datum_token->data + l_signs_offset, l_sign, l_sign_size); + l_signs_offset += l_sign_size; + DAP_DELETE(l_sign); + } + }break; + default: + dap_chain_node_cli_set_reply_text(a_str_reply, + "Unknown token type"); + return -8; + } + + dap_chain_datum_t * l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_TOKEN_DECL, l_datum_token, + sizeof(l_datum_token->header_private) + l_signs_offset); + size_t l_datum_size = dap_chain_datum_size(l_datum); + + // Calc datum's hash + dap_chain_hash_fast_t l_key_hash; + dap_hash_fast(l_datum, l_datum_size, &l_key_hash); + char * l_key_str = dap_chain_hash_fast_to_str_new(&l_key_hash); + + // Add datum to mempool with datum_token hash as a key + char * l_gdb_group_mempool; + if(l_chain) { + l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + } + else { + l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TOKEN); + + } + if(dap_chain_global_db_gr_set(dap_strdup(l_key_str), (uint8_t *) l_datum, l_datum_size, l_gdb_group_mempool)) { + dap_chain_node_cli_set_reply_text(a_str_reply, "datum %s with token %s is placed in datum pool ", l_key_str, + l_ticker); + DAP_DELETE(l_datum); + DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + DAP_DELETE(l_key_str); + return 0; + } + else { + dap_chain_node_cli_set_reply_text(a_str_reply, "datum tx %s is not placed in datum pool ", l_key_str); + DAP_DELETE(l_datum); + DAP_DELETE(l_datum_token); + DAP_DELETE(l_gdb_group_mempool); + DAP_DELETE(l_key_str); + return -2; + } + +} + +/** + * @brief com_token_emit + * @param argc + * @param argv + * @param arg_func + * @param str_reply + * @return + */ +int com_token_emit(int argc, char ** argv, void *arg_func, char ** str_reply) +{ + int arg_index = 1; + const char *str_tmp = NULL; + char *str_reply_tmp = NULL; + uint64_t l_emission_value = 0; + + const char * l_ticker = NULL; + + const char * l_addr_str = NULL; + + const char * l_certs_str = NULL; + + dap_cert_t ** l_certs = NULL; + size_t l_certs_size = 0; + + const char * l_chain_emission_str = NULL; + dap_chain_t * l_chain_emission = NULL; + + const char * l_chain_base_tx_str = NULL; + dap_chain_t * l_chain_base_tx = NULL; + + const char * l_net_str = NULL; + dap_chain_net_t * l_net = NULL; + + // Wallet address that recieves the emission + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-certs", &l_certs_str); + + // Wallet address that recieves the emission + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-addr", &l_addr_str); + + // Token ticker + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-token", &l_ticker); + + // Token emission + if(dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-emission_value", &str_tmp)) { + l_emission_value = strtoull(str_tmp, NULL, 10); + } + + if(!l_emission_value) { + dap_chain_node_cli_set_reply_text(str_reply, "token_emit requires parameter '-emission_value'"); + return -1; + } + + if(!l_addr_str) { + dap_chain_node_cli_set_reply_text(str_reply, "token_emit requires parameter '-addr'"); + return -2; + } + + if(!l_ticker) { + dap_chain_node_cli_set_reply_text(str_reply, "token_emit requires parameter '-token'"); + return -3; + } + + if(!l_certs_str) { + dap_chain_node_cli_set_reply_text(str_reply, "token_emit requires parameter '-certs'"); + return -4; + } + + // Load certs + dap_cert_parse_str_list(l_certs_str, &l_certs, &l_certs_size); + + if(!l_certs_size) { + dap_chain_node_cli_set_reply_text(str_reply, + "token_emit command requres at least one valid certificate to sign the basic transaction of emission"); + return -5; + } + + dap_chain_addr_t * l_addr = dap_chain_addr_from_str(l_addr_str); + + if(!l_addr) { + dap_chain_node_cli_set_reply_text(str_reply, "address \"%s\" is invalid", l_addr_str); + return -4; + } + + // Net addr + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-net", &l_net_str); + + // Select chain network + if(!l_net_str) { + dap_chain_node_cli_set_reply_text(str_reply, "token_create requires parameter 'net'"); + DAP_DELETE(l_addr); + return -42; + } else { + if((l_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network + dap_chain_node_cli_set_reply_text(str_reply, + "token_create requires parameter '-net' to be valid chain network name"); + DAP_DELETE(l_addr); + return -43; + } + } + + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-chain_emission", &l_chain_emission_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-chain_base_tx", &l_chain_base_tx_str); + + // Select chain emission + /*if(!l_chain_emission_str) { + dap_chain_node_cli_set_reply_text(str_reply, "token_create requires parameter '-chain_emission'"); + return -44; + } */ + if(l_chain_emission_str) { + if((l_chain_emission = dap_chain_net_get_chain_by_name(l_net, l_chain_emission_str)) == NULL) { // Can't find such chain + dap_chain_node_cli_set_reply_text(str_reply, + "token_create requires parameter '-chain_emission' to be valid chain name in chain net %s", + l_net_str); + DAP_DELETE(l_addr); + return -45; + } + } + + // Select chain emission + /*if(!l_chain_base_tx_str) { + dap_chain_node_cli_set_reply_text(str_reply, "token_create requires parameter 'chain_base_tx'"); + return -46; + }*/ + if(l_chain_base_tx_str) { + if((l_chain_base_tx = dap_chain_net_get_chain_by_name(l_net, l_chain_base_tx_str)) == NULL) { // Can't find such chain + dap_chain_node_cli_set_reply_text(str_reply, + "token_create requires parameter '-chain_emission' to be valid chain name in chain net %s", + l_net_str); + DAP_DELETE(l_addr); + return -47; + } + } + + // Get groups for the chains + char * l_gdb_group_mempool_emission, *l_gdb_group_mempool_base_tx; + if(l_chain_emission) { + l_gdb_group_mempool_emission = dap_chain_net_get_gdb_group_mempool(l_chain_emission); + } + else { + l_gdb_group_mempool_emission = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_EMISSION); + } + if(l_chain_base_tx) { + l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool(l_chain_base_tx); + } + else { + l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TX); + } + //char * l_gdb_group_mempool_emission = dap_chain_net_get_gdb_group_mempool(l_chain_emission); + //char * l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool(l_chain_base_tx); + + // Create emission datum + // then create datum in memory + dap_chain_datum_token_emission_t * l_token_emission; + size_t l_token_emission_size = sizeof(l_token_emission->hdr) + + sizeof(l_token_emission->data.type_auth.signs_count); + + l_token_emission = DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, l_token_emission_size); + strncpy(l_token_emission->hdr.ticker, l_ticker, sizeof(l_token_emission->hdr.ticker)); + l_token_emission->hdr.value = l_emission_value; + l_token_emission->hdr.type = DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH; + memcpy(&l_token_emission->hdr.address, l_addr, sizeof(l_token_emission->hdr.address)); + // Then add signs + size_t l_offset = 0; + for(size_t i = 0; i < l_certs_size; i++) { + dap_sign_t * l_sign = dap_cert_sign(l_certs[i], &l_token_emission->hdr, + sizeof(l_token_emission->hdr), 0); + size_t l_sign_size = dap_sign_get_size(l_sign); + l_token_emission_size += l_sign_size; + l_token_emission = DAP_REALLOC(l_token_emission, l_token_emission_size); + memcpy(l_token_emission->data.type_auth.signs + l_offset, l_sign, l_sign_size); + l_offset += l_sign_size; + DAP_DELETE(l_sign); + } + + // Produce datum + dap_chain_datum_t * l_datum_emission = dap_chain_datum_create(DAP_CHAIN_DATUM_TOKEN_EMISSION, + l_token_emission, + l_token_emission_size); + size_t l_datum_emission_size = sizeof(l_datum_emission->header) + l_datum_emission->header.data_size; + + // Calc token's hash + dap_chain_hash_fast_t l_token_emission_hash; + dap_hash_fast(l_token_emission, l_token_emission_size, &l_token_emission_hash); + char * l_key_str = dap_chain_hash_fast_to_str_new(&l_token_emission_hash); + + // Delete token emission + DAP_DELETE(l_token_emission); + +// // Calc datum's hash +// dap_chain_hash_fast_t l_datum_emission_hash; +// dap_hash_fast(l_datum_emission, l_datum_emission_size, (uint8_t*) &l_datum_emission_hash); +// char * l_key_str = dap_chain_hash_fast_to_str_new(&l_datum_emission_hash); + + // Add to mempool emission token + if(dap_chain_global_db_gr_set(dap_strdup(l_key_str), (uint8_t *) l_datum_emission, l_datum_emission_size + , l_gdb_group_mempool_emission)) { + str_reply_tmp = dap_strdup_printf("datum emission %s is placed in datum pool ", l_key_str); + DAP_DELETE(l_key_str); + } + else { + dap_chain_node_cli_set_reply_text(str_reply, "datum emission %s is not placed in datum pool ", l_key_str); + DAP_DELETE(l_key_str); + DAP_DELETE(l_datum_emission); + return -1; + } + + + // create first transaction (with tx_token) + dap_chain_datum_tx_t *l_tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, sizeof(dap_chain_datum_tx_t)); + dap_chain_hash_fast_t l_tx_prev_hash = { 0 }; + // create items + dap_chain_tx_token_t *l_tx_token = dap_chain_datum_tx_item_token_create(&l_token_emission_hash, l_ticker); + dap_chain_tx_in_t *l_in = dap_chain_datum_tx_item_in_create(&l_tx_prev_hash, 0); + dap_chain_tx_out_t *l_out = dap_chain_datum_tx_item_out_create(l_addr, l_emission_value); + + // pack items to transaction + dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_tx_token); + dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_in); + dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_out); + + // Sign all that we have with certs + for(size_t i = 0; i < l_certs_size; i++) { + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_certs[i]->enc_key) < 0) { + dap_chain_node_cli_set_reply_text(str_reply, "No private key for certificate=%s", + l_certs[i]->name); + DAP_DELETE(l_addr); + return -3; + } + } + + DAP_DELETE(l_certs); + DAP_DELETE(l_tx_token); + DAP_DELETE(l_in); + DAP_DELETE(l_out); + + size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); + + // Pack transaction into the datum + dap_chain_datum_t * l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size); + size_t l_datum_tx_size = dap_chain_datum_size(l_datum_tx); + + // use l_tx hash for compatible with utho hash + //dap_hash_fast(l_tx, l_tx_size, &l_key_hash); //dap_hash_fast(l_datum_tx, l_datum_tx_size, &l_key_hash); + // calc datum hash + dap_chain_hash_fast_t l_datum_tx_hash; + dap_hash_fast(l_datum_tx, l_datum_tx_size, &l_datum_tx_hash); + l_key_str = dap_chain_hash_fast_to_str_new(&l_datum_tx_hash); + DAP_DELETE(l_tx); + + // Add to mempool tx token + if(dap_chain_global_db_gr_set(dap_strdup(l_key_str), l_datum_tx, l_datum_tx_size + , l_gdb_group_mempool_base_tx)) { + dap_chain_node_cli_set_reply_text(str_reply, "%s\ndatum tx %s is placed in datum pool ", str_reply_tmp, + l_key_str); + DAP_DELETE(l_key_str); + } else { + dap_chain_node_cli_set_reply_text(str_reply, "%s\ndatum tx %s is not placed in datum pool ", str_reply_tmp, + l_key_str); + DAP_DELETE(l_key_str); + + return -2; + } + DAP_DELETE(str_reply_tmp); + DAP_DELETE(l_datum_tx); + DAP_DELETE(l_addr); + return 0; +} + +/** + * com_tx_cond_create command + * + * Create transaction + */ +int com_tx_cond_create(int argc, char ** argv, void *arg_func, char **str_reply) +{ + (void) argc; + int arg_index = 1; + const char *c_wallets_path = dap_chain_wallet_get_path(g_config); + const char * l_token_ticker = NULL; + const char * l_wallet_from_str = NULL; + const char * l_wallet_to_str = NULL; //l_addr_to_str + const char * l_value_datoshi_str = NULL; + const char * l_net_name = NULL; + const char * l_unit_str = NULL; + const char * l_service_str = NULL; + uint64_t l_value_datoshi = 0; + + // Token ticker + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-token", &l_token_ticker); + // Wallet name - from + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-wallet_f", &l_wallet_from_str); + // Wallet address - to + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-wallet_t", &l_wallet_to_str); + // value datoshi + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-value", &l_value_datoshi_str); + // net + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-net", &l_net_name); + // unit + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-unit", &l_unit_str); + // service + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-service", &l_service_str); + + if(!l_token_ticker) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_cond_create requires parameter '-token'"); + return -1; + } + if(!l_wallet_from_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_cond_create requires parameter '-wallet_f'"); + return -2; + } + if(!l_wallet_to_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_cond_create requires parameter '-wallet_t'"); + return -3; + } + if(!l_value_datoshi_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_cond_create requires parameter '-value'"); + return -4; + } + + if(!l_net_name) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_cond_create requires parameter '-net'"); + return -5; + } + if(!l_unit_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_cond_create requires parameter '-unit={mb|kb|b|sec|day}'"); + return -6; + } + if(!l_service_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_cond_create requires parameter '-service={vpn}'"); + return -7; + } + dap_chain_net_srv_uid_t l_srv_uid = { 0 }; + if(!dap_strcmp(l_service_str, "vpn")) + l_srv_uid.uint64 = 0x0000000000000001; + //dap_chain_addr_t *addr_to = dap_chain_addr_from_str(l_addr_to_str); + if(!l_srv_uid.uint64) { + dap_chain_node_cli_set_reply_text(str_reply, "can't recognize service='%s' unit must look like {vpn}", + l_service_str); + return -8; + } + + dap_chain_net_srv_price_unit_uid_t l_price_unit = { .enm = SERV_UNIT_UNDEFINED }; + if(!dap_strcmp(l_unit_str, "mb")) + l_price_unit.enm = SERV_UNIT_MB; + else if(!dap_strcmp(l_unit_str, "sec")) + l_price_unit.enm = SERV_UNIT_SEC; + else if(!dap_strcmp(l_unit_str, "day")) + l_price_unit.enm = SERV_UNIT_DAY; + else if(!dap_strcmp(l_unit_str, "kb")) + l_price_unit.enm = SERV_UNIT_KB; + else if(!dap_strcmp(l_unit_str, "b")) + l_price_unit.enm = SERV_UNIT_B; + + if(l_price_unit.enm == SERV_UNIT_UNDEFINED) { + dap_chain_node_cli_set_reply_text(str_reply, "can't recognize unit='%s' unit must look like {mb|kb|b|sec|day}", + l_unit_str); + return -9; + } + + l_value_datoshi = strtoll(l_value_datoshi_str, NULL, 10); + if(!l_value_datoshi) { + dap_chain_node_cli_set_reply_text(str_reply, "can't recognize value='%s' as a number", l_value_datoshi_str); + return -10; + } + + dap_chain_net_t * l_net = l_net_name ? dap_chain_net_by_name(l_net_name) : NULL; + if(!l_net) { + dap_chain_node_cli_set_reply_text(str_reply, "can't find net '%s'", l_net_name); + return -11; + } + dap_chain_wallet_t *l_wallet_from = dap_chain_wallet_open(l_wallet_from_str, c_wallets_path); + if(!l_wallet_from) { + dap_chain_node_cli_set_reply_text(str_reply, "can't open wallet '%s'", l_wallet_from); + return -12; + } + dap_chain_wallet_t *l_wallet_cond = dap_chain_wallet_open(l_wallet_to_str, c_wallets_path); + if(!l_wallet_to_str) { + dap_chain_wallet_close(l_wallet_from); + dap_chain_node_cli_set_reply_text(str_reply, "can't open wallet '%s'", l_wallet_to_str); + return -13; + } + dap_enc_key_t *l_key_from = dap_chain_wallet_get_key(l_wallet_from, 0); + dap_enc_key_t *l_key_cond = dap_chain_wallet_get_key(l_wallet_cond, 0); + + + // where to take coins for service + const dap_chain_addr_t *l_addr_from = dap_chain_wallet_get_addr(l_wallet_from, l_net->pub.id); + // who will be use service, usually the same address (addr_from) + //const dap_chain_addr_t *l_addr_cond = dap_chain_wallet_get_addr(l_wallet_cond, l_net->pub.id); + + +/* //dap_chain_net_srv_abstract_t l_cond; +// dap_chain_net_srv_abstract_set(&l_cond, SERV_CLASS_PERMANENT, SERV_ID_VPN, l_value, SERV_UNIT_MB, +// "test vpn service"); +// dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name((const char *) c_net_name); + + int res = dap_chain_mempool_tx_create_cond(NULL, l_key, l_key_cond, addr_from, + addr_cond, + NULL, l_token_ticker, l_value, 0, (const void*) &l_cond, sizeof(dap_chain_net_srv_abstract_t)); +*/ + + dap_chain_hash_fast_t *l_tx_cond_hash = dap_chain_mempool_tx_create_cond(l_net, l_key_from, l_key_cond, l_addr_from, l_token_ticker, + l_value_datoshi, 0, l_price_unit, l_srv_uid, 0, NULL, 0); + + dap_chain_wallet_close(l_wallet_from); + dap_chain_wallet_close(l_wallet_cond); + + char *l_hash_str = l_tx_cond_hash ? dap_chain_hash_fast_to_str_new(l_tx_cond_hash) : NULL; + + /*dap_chain_node_cli_set_reply_text(str_reply, "cond create=%s\n", + (res == 0) ? "Ok" : (res == -2) ? "False, not enough funds for service fee" : "False"); + return res;*/ + + int l_ret; + // example: cond create succefully hash=0x4AA303EB7C10430C0AAC42F399D265BC7DD09E3983E088E02B8CED38DA22EDA9 + if(l_hash_str){ + dap_chain_node_cli_set_reply_text(str_reply, "cond create succefully hash=%s\n", l_hash_str); + l_ret = 0; + } + else{ + dap_chain_node_cli_set_reply_text(str_reply, "cond can't create\n"); + l_ret = -1; + } + + DAP_DELETE(l_hash_str); + return l_ret; +} + +/** + * @brief com_mempool_add_ca + * @details Place public CA into the mempool + * @param a_argc + * @param a_argv + * @param a_arg_func + * @param a_str_reply + * @return + */ +int com_mempool_add_ca( int a_argc, char ** a_argv, void *a_arg_func, char ** a_str_reply) +{ + UNUSED(a_arg_func); + int arg_index = 1; + + // Read params + const char * l_ca_name = NULL; + dap_chain_net_t * l_net = NULL; + dap_chain_t * l_chain = NULL; + + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-ca_name", &l_ca_name); + dap_chain_node_cli_cmd_values_parse_net_chain(&arg_index,a_argc, a_argv, a_str_reply, &l_chain, &l_net); + + // Check for network if was set or not + if ( l_net == NULL ){ + dap_chain_node_cli_set_reply_text(a_str_reply, + "mempool_add_ca_public requires parameter '-net' to specify the chain network name"); + return -1; + } + + // Chech for chain if was set or not + if ( l_chain == NULL){ + // If wasn't set - trying to auto detect + l_chain = dap_chain_net_get_chain_by_chain_type( l_net, DAP_CHAIN_DATUM_CA ); + if (l_chain == NULL) { // If can't auto detect + dap_chain_node_cli_set_reply_text(a_str_reply, + "No chains for CA datum in network \"%s\"", l_net->pub.name ); + return -2; + } + } + // Check if '-name' wasn't specified + if (l_ca_name == NULL){ + dap_chain_node_cli_set_reply_text(a_str_reply, + "mempool_add_ca_public requires parameter '-name' to specify the certificate name"); + return -3; + } + + // Find certificate with specified key + dap_cert_t * l_cert = dap_cert_find_by_name( l_ca_name ); + if( l_cert == NULL ){ + dap_chain_node_cli_set_reply_text(a_str_reply, + "Can't find \"%s\" certificate", l_ca_name ); + return -4; + } + if( l_cert->enc_key == NULL ){ + dap_chain_node_cli_set_reply_text(a_str_reply, + "Corrupted certificate \"%s\" without keys certificate", l_ca_name ); + return -5; + } + + if ( l_cert->enc_key->priv_key_data_size || l_cert->enc_key->priv_key_data){ + dap_chain_node_cli_set_reply_text(a_str_reply, + "Certificate \"%s\" has private key data. Please export public only key certificate without private keys", l_ca_name ); + return -6; + } + + // Serialize certificate into memory + uint32_t l_cert_serialized_size = 0; + byte_t * l_cert_serialized = dap_cert_mem_save( l_cert, &l_cert_serialized_size ); + if( l_cert_serialized == NULL){ + dap_chain_node_cli_set_reply_text(a_str_reply, + "Can't serialize in memory certificate \"%s\"", l_ca_name ); + return -7; + } + // Now all the chechs passed, forming datum for mempool + dap_chain_datum_t * l_datum = dap_chain_datum_create( DAP_CHAIN_DATUM_CA, l_cert_serialized , l_cert_serialized_size); + DAP_DELETE( l_cert_serialized); + if( l_datum == NULL){ + dap_chain_node_cli_set_reply_text(a_str_reply, + "Can't produce datum from certificate \"%s\"", l_ca_name ); + return -7; + } + + // Finaly add datum to mempool + if ( dap_chain_mempool_datum_add ( l_datum,l_chain ) == 0 ){ + return 0; + }else{ + DAP_DELETE( l_datum ); + return -8; + } +} + + +/** + * com_tx_create command + * + * Create transaction + */ +int com_tx_create(int argc, char ** argv, void *arg_func, char **str_reply) +{ + int arg_index = 1; +// int cmd_num = 1; +// const char *value_str = NULL; + const char *addr_base58_to = NULL; + const char *addr_base58_fee = NULL; + const char *str_tmp = NULL; + const char * l_from_wallet_name = NULL; + const char * l_token_ticker = NULL; + const char * l_net_name = NULL; + const char * l_chain_name = NULL; + const char * l_tx_num_str = NULL; + size_t l_tx_num = 0; + + uint64_t value = 0; + uint64_t value_fee = 0; + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-from_wallet", &l_from_wallet_name); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-to_addr", &addr_base58_to); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-token", &l_token_ticker); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-net", &l_net_name); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-chain", &l_chain_name); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-tx_num", &l_tx_num_str); + + if(l_tx_num_str) + l_tx_num = strtoul(l_tx_num_str, NULL, 10); + + if(dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-fee", &addr_base58_fee)) { + if(dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-value_fee", &str_tmp)) { + value_fee = strtoull(str_tmp, NULL, 10); + } + } + if(dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-value", &str_tmp)) { + value = strtoull(str_tmp, NULL, 10); + } + if(!l_from_wallet_name) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-from_wallet'"); + return -1; + } + if(!addr_base58_to) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-to_addr'"); + return -1; + } + if(!value) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-value'"); + return -1; + } + if(addr_base58_fee && !value_fee) { + dap_chain_node_cli_set_reply_text(str_reply, + "tx_create requires parameter '-value_fee' if '-fee' is specified"); + return -1; + } + + if(!l_net_name) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-net'"); + return -1; + } + dap_chain_net_t * l_net = dap_chain_net_by_name(l_net_name); + dap_ledger_t *l_ledger = l_net ? l_net->pub.ledger : NULL; + if((l_ledger = dap_chain_ledger_by_net_name(l_net_name)) == NULL) { + dap_chain_node_cli_set_reply_text(str_reply, "not found net by name '%s'", l_net_name); + return -1; + } + + /* if(!l_chain_name) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_create requires parameter '-chain'"); + return -1; + }*/ + dap_chain_t * l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_name); + if(!l_chain) { + l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_TX); + } + if(!l_chain) { + dap_chain_node_cli_set_reply_text(str_reply, "not found chain name '%s', try use parameter '-chain'", + l_chain_name); + return -1; + } + + const char *c_wallets_path = dap_chain_wallet_get_path(g_config); + dap_chain_wallet_t * l_wallet = dap_chain_wallet_open(l_from_wallet_name, c_wallets_path); + + if(!l_wallet) { + dap_chain_node_cli_set_reply_text(str_reply, "wallet %s does not exist", l_from_wallet_name); + return -1; + } + const dap_chain_addr_t *addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet, l_net->pub.id); + dap_chain_addr_t *addr_to = dap_chain_addr_from_str(addr_base58_to); + dap_chain_addr_t *addr_fee = dap_chain_addr_from_str(addr_base58_fee); + + if(!addr_from) { + dap_chain_node_cli_set_reply_text(str_reply, "source address is invalid"); + return -1; + } + if(!addr_to) { + dap_chain_node_cli_set_reply_text(str_reply, "destination address is invalid"); + return -1; + } + if(addr_base58_fee && !addr_fee) { + dap_chain_node_cli_set_reply_text(str_reply, "fee address is invalid"); + return -1; + } + + dap_string_t *string_ret = dap_string_new(NULL); + //g_string_printf(string_ret, "from=%s\nto=%s\nval=%lld\nfee=%s\nval_fee=%lld\n\n", + // addr_base58_from, addr_base58_to, value, addr_base58_fee, value_fee); + + int res = + l_tx_num ? + dap_chain_mempool_tx_create_massive(l_chain, dap_chain_wallet_get_key(l_wallet, 0), addr_from, + addr_to, addr_fee, + l_token_ticker, value, value_fee, l_tx_num) + : + dap_chain_mempool_tx_create(l_chain, dap_chain_wallet_get_key(l_wallet, 0), addr_from, addr_to, + addr_fee, + l_token_ticker, value, value_fee); + dap_string_append_printf(string_ret, "transfer=%s\n", + (res == 0) ? "Ok" : (res == -2) ? "False, not enough funds for transfer" : "False"); + + dap_chain_node_cli_set_reply_text(str_reply, string_ret->str); + dap_string_free(string_ret, false); + + DAP_DELETE(addr_to); + DAP_DELETE(addr_fee); + dap_chain_wallet_close(l_wallet); + return res; +} + +/** + * tx_verify command + * + * Verifing transaction + */ +int com_tx_verify(int argc, char ** argv, void *arg_func, char **str_reply) +{ + if(argc > 1) { + if(str_reply) + dap_chain_node_cli_set_reply_text(str_reply, "command \"%s\" not recognized", argv[1]); + } + if(str_reply) + dap_chain_node_cli_set_reply_text(str_reply, "command not defined, enter \"help <cmd name>\""); + return -1; +} + +/** + * tx_history command + * + * Transaction history for an address + */ +int com_tx_history(int argc, char ** argv, void *arg_func, char **str_reply) +{ + int arg_index = 1; + const char *l_addr_base58 = NULL; + const char *l_wallet_name = NULL; + const char *l_net_str = NULL; + const char *l_chain_str = NULL; + const char *l_tx_hash_str = NULL; + + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-addr", &l_addr_base58); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-w", &l_wallet_name); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-net", &l_net_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-chain", &l_chain_str); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-tx", &l_tx_hash_str); + + if(!l_addr_base58 && !l_wallet_name && !l_tx_hash_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_history requires parameter '-addr' or '-w' or '-tx'"); + return -1; + } + + // Select chain network + if(!l_net_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_history requires parameter '-net'"); + return -2; + } else { + if((l_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network + dap_chain_node_cli_set_reply_text(str_reply, + "tx_history requires parameter '-net' to be valid chain network name"); + return -3; + } + } + //Select chain emission + if(!l_chain_str) { + dap_chain_node_cli_set_reply_text(str_reply, "tx_history requires parameter '-chain'"); + return -4; + } else { + if((l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_str)) == NULL) { // Can't find such chain + dap_chain_node_cli_set_reply_text(str_reply, + "tx_history requires parameter '-chain' to be valid chain name in chain net %s", + l_net_str); + return -5; + } + } + //char *l_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + //const char *l_chain_group = dap_chain_gdb_get_group(l_chain); + + dap_chain_hash_fast_t l_tx_hash; + if(l_tx_hash_str) { + if(dap_chain_str_to_hash_fast(l_tx_hash_str, &l_tx_hash) < 0) { + l_tx_hash_str = NULL; + dap_chain_node_cli_set_reply_text(str_reply, "tx hash not recognized"); + return -1; + } +// char hash_str[99]; +// dap_chain_hash_fast_to_str(&l_tx_hash, hash_str,99); +// int gsdgsd=523; + } + dap_chain_addr_t *l_addr = NULL; + // if need addr + if(!l_tx_hash_str) { + if(l_wallet_name) { + const char *c_wallets_path = dap_chain_wallet_get_path(g_config); + dap_chain_wallet_t * l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path); + if(l_wallet) { + dap_chain_addr_t *l_addr_tmp = (dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet, l_net->pub.id); + l_addr = DAP_NEW_SIZE(dap_chain_addr_t, sizeof(dap_chain_addr_t)); + memcpy(l_addr, l_addr_tmp, sizeof(dap_chain_addr_t)); + dap_chain_wallet_close(l_wallet); + } + } + if(!l_addr && l_addr_base58) { + l_addr = dap_chain_addr_from_str(l_addr_base58); + } + if(!l_addr && !l_tx_hash_str) { + dap_chain_node_cli_set_reply_text(str_reply, "wallet address not recognized"); + return -1; + } + } + + char *l_str_out = l_tx_hash_str ? + dap_db_history_tx(&l_tx_hash, l_chain) : + dap_db_history_addr(l_addr, l_chain); + + char *l_str_ret = NULL; + if(l_tx_hash_str) { + l_str_ret = dap_strdup_printf("history for tx hash %s:\n%s", l_tx_hash_str, + l_str_out ? l_str_out : " empty"); + } + else if(l_addr) { + char *l_addr_str = dap_chain_addr_to_str(l_addr); + l_str_ret = dap_strdup_printf("history for addr %s:\n%s", l_addr_str, + l_str_out ? l_str_out : " empty"); + DAP_DELETE(l_addr_str); + } + dap_chain_node_cli_set_reply_text(str_reply, l_str_ret); + DAP_DELETE(l_str_out); + DAP_DELETE(l_str_ret); + return 0; +} + +/** + * stats command + */ +int com_stats(int argc, char ** argv, void *arg_func, char **str_reply) +{ + enum { + CMD_NONE, CMD_STATS_CPU + }; + int arg_index = 1; + int cmd_num = CMD_NONE; + // find add parameter ('cpu') + if (dap_chain_node_cli_find_option_val(argv, arg_index, min(argc, arg_index + 1), "cpu", NULL)) { + cmd_num = CMD_STATS_CPU; + } + switch (cmd_num) { + case CMD_NONE: + default: + dap_chain_node_cli_set_reply_text(str_reply, "format of command: stats cpu"); + return -1; + case CMD_STATS_CPU: +#if (defined DAP_OS_UNIX) || (defined __WIN32) + { + dap_cpu_monitor_init(); + usleep(500000); + char *str_reply_prev = dap_strdup_printf(""); + char *str_delimiter; + dap_cpu_stats_t s_cpu_stats = dap_cpu_get_stats(); + for (int n_cpu_num = 0; n_cpu_num < s_cpu_stats.cpu_cores_count; n_cpu_num++) { + if ((n_cpu_num % 4 == 0) && (n_cpu_num != 0)) { + str_delimiter = dap_strdup_printf("\n"); + } else if (n_cpu_num == s_cpu_stats.cpu_cores_count - 1) { + str_delimiter = dap_strdup_printf(""); + } else { + str_delimiter = dap_strdup_printf(" "); + } + *str_reply = dap_strdup_printf("%sCPU-%d: %f%%%s", str_reply_prev, n_cpu_num, s_cpu_stats.cpus[n_cpu_num].load, str_delimiter); + DAP_DELETE(str_reply_prev); + DAP_DELETE(str_delimiter); + str_reply_prev = *str_reply; + } + *str_reply = dap_strdup_printf("%s\nTotal: %f%%", str_reply_prev, s_cpu_stats.cpu_summary.load); + DAP_DELETE(str_reply_prev); + break; + } +#else + dap_chain_node_cli_set_reply_text(str_reply, "only Linux or Windows environment supported"); + return -1; +#endif // DAP_OS_UNIX + } + return 0; +} + +int com_exit(int argc, char ** argv, void *arg_func, char **str_reply) +{ + exit(0); +} + + +/** + * print_log command + * + * Print log info + * print_log [ts_after <timestamp >] [limit <line numbers>] + */ +int com_print_log(int argc, char ** argv, void *arg_func, char **str_reply) +{ + int arg_index = 1; + const char * l_str_ts_after = NULL; + const char * l_str_limit = NULL; + int64_t l_ts_after = 0; + long l_limit = 0; + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "ts_after", &l_str_ts_after); + dap_chain_node_cli_find_option_val(argv, arg_index, argc, "limit", &l_str_limit); + + l_ts_after = (l_str_ts_after) ? strtoll(l_str_ts_after, 0, 10) : -1; + l_limit = (l_str_limit) ? strtol(l_str_limit, 0, 10) : -1; + + if(l_ts_after < 0 || !l_str_ts_after) { + dap_chain_node_cli_set_reply_text(str_reply, "requires valid parameter 'l_ts_after'"); + return -1; + } + if(!l_limit) { + dap_chain_node_cli_set_reply_text(str_reply, "requires valid parameter 'limit'"); + return -1; + } + + // get logs from list + char *l_str_ret = dap_log_get_item(l_ts_after, (int) l_limit); + if(!l_str_ret) { + dap_chain_node_cli_set_reply_text(str_reply, "no logs"); + return -1; + } + dap_chain_node_cli_set_reply_text(str_reply, l_str_ret); + DAP_DELETE(l_str_ret); + return 0; +} + +/** + * vpn_client command + * + * VPN client control + */ +int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply) +{ +#ifndef _WIN32 + enum { + CMD_NONE, CMD_START, CMD_STOP, CMD_STATUS + }; + int l_arg_index = 1; + // find net + dap_chain_net_t *l_net = NULL; + if(dap_chain_node_cli_cmd_values_parse_net_chain(&l_arg_index, a_argc, a_argv, a_str_reply, NULL, &l_net) < 0) + return -2; + + int cmd_num = CMD_NONE; + if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "start", NULL)) { + cmd_num = CMD_START; + } + else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "stop", NULL)) { + cmd_num = CMD_STOP; + } + else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "status", NULL)) { + cmd_num = CMD_STATUS; + } + if(cmd_num == CMD_NONE) { + if(!a_argv[1]) + dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameters"); + else + dap_chain_node_cli_set_reply_text(a_str_reply, "parameter %s not recognized", a_argv[1]); + return -1; + } + + switch (cmd_num) + { + case CMD_START: { + const char * l_str_addr = NULL; // for example, "192.168.100.93" + const char * l_str_port = NULL; // for example, "8079" + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr", &l_str_addr); + if(!l_str_addr) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "VPN server address not defined, use -addr <vpn server ipv4 address> parameter"); + break; + } + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-port", &l_str_port); + int l_srv_port = (l_str_port) ? (int) strtoll(l_str_port, 0, 10) : 0; + if(!l_srv_port) { + dap_chain_node_cli_set_reply_text(a_str_reply, + "VPN server port not defined, use -port <vpn server port> parameter"); + break; + } + int l_res = dap_chain_net_vpn_client_start(l_net, l_str_addr, NULL, l_srv_port); + switch (l_res) { + case 0: + dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client started successfully"); + break; + case 1: + dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client already started"); + break; + case -2: + case -3: + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't connect to VPN server"); + break; + default: + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't start VPN client"); + break; + } + return l_res; + } + break; + case CMD_STOP: { + int res = dap_chain_net_vpn_client_stop(); + if(!res) + dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client stopped successfully"); + else + dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not stopped"); + return res; + } + //break; + case CMD_STATUS: + switch (dap_chain_net_vpn_client_status()) { +// switch (0){ + case 0: + dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client stopped"); + return 0; + case 1: + dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client started"); + return 0; + case -1: + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't get VPN state"); + return -1; + } + break; + } +#endif + return 0; +} diff --git a/libdap-chain-net/dap_chain_node_cli_cmd.h b/libdap-chain-net/dap_chain_node_cli_cmd.h new file mode 100644 index 0000000000000000000000000000000000000000..b03b3b1342cf495513a07d4d3f6092869327a794 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_cli_cmd.h @@ -0,0 +1,146 @@ +/* + * Authors: + * Dmitriy A. Gerasimov <gerasimov.dmitriy@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "dap_chain.h" +#include "dap_chain_net.h" +#include "dap_chain_node.h" +#include "dap_chain_node_cli.h" + +/** + * Find in base addr by alias + * + * return addr, NULL if not found + */ +dap_chain_node_addr_t* dap_chain_node_addr_get_by_alias(dap_chain_net_t * a_net, const char *alias); + + +int dap_chain_node_cli_cmd_values_parse_net_chain(int *a_arg_index,int argc, char ** argv, char ** a_str_reply, + dap_chain_t ** a_chain, dap_chain_net_t ** a_net); + +/** + * global_db command + */ +int com_global_db(int argc, char ** argv, void *arg_func, char **str_reply); + +/** + * Node command + */ +int com_node(int argc, char ** argv, void *arg_func, char **str_reply); + +/** + * Traceroute command + * + * return 0 OK, -1 Err + */ +int com_traceroute(int argc, char** argv, void *arg_func, char **str_reply); + +/** + * Tracepath command + * + * return 0 OK, -1 Err + */ +int com_tracepath(int argc, char** argv, void *arg_func, char **str_reply); + +/** + * Ping command + * + * return 0 OK, -1 Err + */ +int com_ping(int argc, char** argv, void *arg_func, char **str_reply); + +/** + * Help command + */ +int com_help(int argc, char ** argv, void *arg_func, char **str_reply); + + +/** + * Token declaration + */ +int com_token_decl ( int argc, char ** argv, void *arg_func, char ** str_reply); + +int com_token_decl_update(int argc, char ** argv, void *arg_func, char ** a_str_reply); + +/** + * Token declaration add sign + */ +int com_token_decl_sign ( int argc, char ** argv, void *arg_func, char ** str_reply); + +/** + * Token emission + */ +int com_token_emit (int argc, char ** argv, void *arg_func, char ** str_reply); + + +/** + * com_tx_create command + * + * Wallet info + */ +int com_tx_wallet(int argc, char ** argv, void *arg_func, char **str_reply); + +/** + * com_tx_create command + * + * Create transaction + */ +int com_tx_create(int argc, char ** argv, void *arg_func, char **str_reply); +int com_tx_cond_create(int argc, char ** argv, void *arg_func, char **str_reply); + +/** + * tx_verify command + * + * Verifing transaction + */ +int com_tx_verify(int argc, char ** argv, void *arg_func, char **str_reply); + +/** + * tx_history command + * + * Transaction history for an address + */ +int com_tx_history(int argc, char ** argv, void *arg_func, char **str_reply); + +// Print log info +int com_print_log(int argc, char ** argv, void *arg_func, char **str_reply); + +// Print statistics +int com_stats(int argc, char ** argv, void *arg_func, char **str_reply); + +int com_exit(int argc, char ** argv, void *arg_func, char **str_reply); + +// vpn_client command +int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply); + +int com_mempool_delete(int argc, char ** argv, void *arg_func, char ** a_str_reply); +int com_mempool_list(int argc, char ** argv, void *arg_func, char ** a_str_reply); +int com_mempool_proc(int argc, char ** argv, void *arg_func, char ** a_str_reply); + +/** + * Place public CA into the mempool + */ +int com_mempool_add_ca( int a_argc, char ** a_argv, void *a_arg_func, char ** a_str_reply); diff --git a/libdap-chain-net/dap_chain_node_cli_cmd_tx.c b/libdap-chain-net/dap_chain_node_cli_cmd_tx.c new file mode 100644 index 0000000000000000000000000000000000000000..32ea1ae2e53b6934179c906c5133317717deb78f --- /dev/null +++ b/libdap-chain-net/dap_chain_node_cli_cmd_tx.c @@ -0,0 +1,643 @@ +/* + * Authors: + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdbool.h> +#include <stddef.h> +#include <pthread.h> + +#include <dap_common.h> +#include <dap_strfuncs.h> +#include <dap_string.h> +#include <dap_list.h> +#include <dap_hash.h> + +#include "dap_chain_datum_tx_items.h" + +#include "dap_chain_node_cli_cmd_tx.h" + +#define LOG_TAG "chain_node_cli_cmd_tx" + +#include "uthash.h" +// for dap_db_history_filter() +typedef struct dap_tx_data { + dap_chain_hash_fast_t tx_hash; + char tx_hash_str[70]; + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + //size_t obj_num; + size_t pos_num; + dap_chain_datum_t *datum; + dap_chain_addr_t addr; + bool is_use_all_cur_out;// find cur addr in prev OUT items + UT_hash_handle hh; +} dap_tx_data_t; + +/*static char* dap_db_new_history_timestamp() +{ + static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER; + // get unique key + pthread_mutex_lock(&s_mutex); + static time_t s_last_time = 0; + static uint64_t s_suffix = 0; + time_t l_cur_time = time(NULL); + if(s_last_time == l_cur_time) + s_suffix++; + else { + s_suffix = 0; + s_last_time = l_cur_time; + } + char *l_str = dap_strdup_printf("%lld_%lld", (uint64_t) l_cur_time, s_suffix); + pthread_mutex_unlock(&s_mutex); + return l_str; +}*/ + +// for dap_db_history_tx & dap_db_history_addr() +static dap_chain_datum_t* get_prev_tx(dap_tx_data_t *a_tx_data) +{ + if(!a_tx_data) + return NULL; + dap_chain_datum_t *l_datum = a_tx_data->datum; + return l_datum; +} + +/** + * Get data according the history log + * + * return history string + */ +char* dap_db_history_tx(dap_chain_hash_fast_t* a_tx_hash, dap_chain_t * a_chain) +{ + dap_string_t *l_str_out = dap_string_new(NULL); + + bool l_tx_hash_found = false; + dap_tx_data_t *l_tx_data_hash = NULL; + // load transactions + dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain); + dap_chain_atom_ptr_t *l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter); + size_t l_atom_size = a_chain->callback_atom_get_size(l_atom); + + while(l_atom && l_atom_size) { + dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_atom; + if(!l_datum && l_datum->header.type_id != DAP_CHAIN_DATUM_TX) { + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + continue; + } + dap_tx_data_t *l_tx_data = NULL; + + // transaction + dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data; + + // find Token items - present in emit transaction + dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL); + + // find OUT items + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); + dap_list_t *l_list_tmp = l_list_out_items; + while(l_list_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; + // save OUT item l_tx_out - only for first OUT item + if(!l_tx_data) + { + // save tx hash + l_tx_data = DAP_NEW_Z(dap_tx_data_t); + dap_chain_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash); + memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t)); + memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t)); + dap_chain_hash_fast_to_str(&l_tx_data->tx_hash, l_tx_data->tx_hash_str, + sizeof(l_tx_data->tx_hash_str)); + //l_tx_data->pos_num = l_count; + //l_tx_data->datum = l_datum; + l_tx_data->datum = DAP_NEW_SIZE(dap_chain_datum_t, l_atom_size); + memcpy(l_tx_data->datum, l_datum, l_atom_size); + // save token name + if(l_list_tx_token) { + dap_chain_tx_token_t *tk = l_list_tx_token->data; + memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker)); + } + // take token from prev out item + else { + + // find IN items + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + dap_list_t *l_list_tmp_in = l_list_in_items; + // find token_ticker in prev OUT items + while(l_list_tmp_in) { + const dap_chain_tx_in_t *l_tx_in = + (const dap_chain_tx_in_t*) l_list_tmp_in->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), + l_tx_data_prev); + if(l_tx_data_prev != NULL) { + // fill token in l_tx_data from prev transaction + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + break; + } + l_list_tmp_in = dap_list_next(l_list_tmp_in); + } + } + if(l_list_in_items) + dap_list_free(l_list_in_items); + } + HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data); + } + l_list_tmp = dap_list_next(l_list_tmp); + } + if(l_list_out_items) + dap_list_free(l_list_out_items); + + // calc hash + dap_chain_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash); + // search tx with a_tx_hash + if(!dap_hash_fast_compare(a_tx_hash, &l_tx_hash)) { + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + continue; + } + // found a_tx_hash now + + // transaction time + if(l_tx->header.ts_created > 0) { + time_t rawtime = (time_t) l_tx->header.ts_created; + struct tm l_timeinfo = {0}; + localtime_r(&rawtime, &l_timeinfo); + dap_string_append_printf(l_str_out, " %s", asctime(&l_timeinfo)); + } + + // find all OUT items in transaction + l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); + l_list_tmp = l_list_out_items; + while(l_list_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; + dap_tx_data_t *l_tx_data_prev = NULL; + + const char *l_token_str = NULL; + if(l_tx_data) + l_token_str = l_tx_data->token_ticker; + char *l_dst_to_str = + (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : + NULL; + dap_string_append_printf(l_str_out, " OUT item %lld %s to %s\n", + l_tx_out->header.value, + dap_strlen(l_token_str) > 0 ? l_token_str : "?", + l_dst_to_str ? l_dst_to_str : "?" + ); + DAP_DELETE(l_dst_to_str); + l_list_tmp = dap_list_next(l_list_tmp); + } + // find all IN items in transaction + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + l_list_tmp = l_list_in_items; + // find cur addr in prev OUT items + while(l_list_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + char l_tx_hash_str[70]; + if(!dap_hash_fast_is_blank(&tx_prev_hash)) + dap_chain_hash_fast_to_str(&tx_prev_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); + else + strcpy(l_tx_hash_str, "Null"); + dap_string_append_printf(l_str_out, " IN item \n prev tx_hash %s\n", l_tx_hash_str); + + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + + dap_chain_datum_t *l_datum_prev = get_prev_tx(l_tx_data_prev); + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, NULL); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + // print value from prev out item + dap_string_append_printf(l_str_out, " prev OUT item value=%lld", + l_tx_prev_out ? l_tx_prev_out->header.value : 0); + } + dap_string_append_printf(l_str_out, "\n"); + l_list_tmp = dap_list_next(l_list_tmp); + } + + if(l_list_tx_token) + dap_list_free(l_list_tx_token); + if(l_list_out_items) + dap_list_free(l_list_out_items); + if(l_list_in_items) + dap_list_free(l_list_in_items); + l_tx_hash_found = true; + break; + + // go to next transaction + //l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + //l_atom_size = a_chain->callback_atom_get_size(l_atom); + } + a_chain->callback_atom_iter_delete(l_atom_iter); + + // delete hashes + dap_tx_data_t *l_iter_current, *l_item_tmp; + HASH_ITER(hh, l_tx_data_hash , l_iter_current, l_item_tmp) + { + // delete datum + DAP_DELETE(l_iter_current->datum); + // delete struct + DAP_DELETE(l_iter_current); + HASH_DEL(l_tx_data_hash, l_iter_current); + } + + // if no history + if(!l_str_out->len) + dap_string_append(l_str_out, "empty"); + char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL; + return l_ret_str; +} + +/** + * Get data according the history log + * + * return history string + */ +char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain) +{ + dap_string_t *l_str_out = dap_string_new(NULL); + + dap_tx_data_t *l_tx_data_hash = NULL; + // load transactions + dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain); + dap_chain_atom_ptr_t *l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter); + if (!l_atom) { + return NULL; + } + size_t l_atom_size = a_chain->callback_atom_get_size(l_atom); + + while(l_atom && l_atom_size) { + dap_chain_datum_t *l_datum = a_chain->callback_atom_get_datum ? a_chain->callback_atom_get_datum(l_atom) : (dap_chain_datum_t*)l_atom; + if(!l_datum || l_datum->header.type_id != DAP_CHAIN_DATUM_TX) { + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + continue; + } + // transaction + dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data; + dap_list_t *l_records_out = NULL; + // transaction time + char *l_time_str = NULL; + { + if(l_tx->header.ts_created > 0) { + time_t rawtime = (time_t) l_tx->header.ts_created; + struct tm * timeinfo; + timeinfo = localtime(&rawtime); + if(timeinfo) + l_time_str = dap_strdup(asctime(timeinfo)); + } + else + l_time_str = dap_strdup(" "); + } + + // find Token items - present in emit transaction + dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL); + + // list of dap_tx_data_t*; info about OUT item in current transaction + dap_list_t *l_list_out_info = NULL; + + // find OUT items + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); + dap_list_t *l_list_out_items_tmp = l_list_out_items; + while(l_list_out_items_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_out_items_tmp->data; + // save OUT item l_tx_out + { + // save tx hash + // info about OUT item in current transaction + dap_tx_data_t *l_tx_data = DAP_NEW_Z(dap_tx_data_t); + dap_chain_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash); + memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t)); + memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t)); + dap_chain_hash_fast_to_str(&l_tx_data->tx_hash, l_tx_data->tx_hash_str, sizeof(l_tx_data->tx_hash_str)); + l_tx_data->datum = DAP_NEW_SIZE(dap_chain_datum_t, l_atom_size); + memcpy(l_tx_data->datum, l_datum, l_atom_size); + // save token name + if(l_tx_data && l_list_tx_token) { + dap_chain_tx_token_t *tk = l_list_tx_token->data; + memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker)); + } + HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data); + + // save OUT items to list + l_records_out = dap_list_append(l_records_out, (void*) l_tx_out); + // save info about OUT items to list + l_list_out_info = dap_list_append(l_list_out_info, (void*) l_tx_data); + } + l_list_out_items_tmp = dap_list_next(l_list_out_items_tmp); + } + + // find IN items + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + dap_list_t *l_list_in_items_tmp = l_list_in_items; + // find cur addr in prev OUT items + //bool l_is_use_all_cur_out = false; + { + while(l_list_in_items_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_in_items_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + // fill token in all l_tx_data from prev transaction + + dap_list_t *l_list_out_info_tmp = l_list_out_info; + while(l_list_out_info_tmp) { + dap_tx_data_t *l_tx_data = (dap_tx_data_t*) l_list_out_info_tmp->data; + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + dap_chain_datum_t *l_datum_prev = get_prev_tx(l_tx_data_prev); + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, NULL); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_tx_data->is_use_all_cur_out = true; + + } + l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp); + } + } + l_list_in_items_tmp = dap_list_next(l_list_in_items_tmp); + } + // find prev OUT items for IN items + dap_list_t *l_list_in_items2_tmp = l_list_in_items; // go to begin of list + while(l_list_in_items2_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_in_items2_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + // if first transaction - empty prev OUT item + if(dap_hash_fast_is_blank(&tx_prev_hash)) { + + dap_tx_data_t *l_tx_data = NULL; + dap_list_t *l_list_out_info_tmp = l_list_out_info; + while(l_list_out_info_tmp) { + l_tx_data = (dap_tx_data_t*) l_list_out_info_tmp->data; + if(l_tx_data->token_ticker[0]) + break; + l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp); + } + + // add emit info to ret string + if(l_tx_data && !memcmp(&l_tx_data->addr, a_addr, sizeof(dap_chain_addr_t))) { + dap_list_t *l_records_tmp = l_records_out; + while(l_records_tmp) { + + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data; + dap_string_append_printf(l_str_out, "tx hash %s \n emit %lu %s\n", + l_tx_data->tx_hash_str, + l_tx_out->header.value, + l_tx_data->token_ticker); + l_records_tmp = dap_list_next(l_records_tmp); + } + } + //dap_list_free(l_records_out); + } + // in other transactions except first one + else { + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + char *l_src_str = NULL; + bool l_src_str_is_cur = false; + + dap_tx_data_t *l_tx_data = NULL; + dap_list_t *l_list_out_info_tmp = l_list_out_info; + while(l_list_out_info_tmp) { + l_tx_data = (dap_tx_data_t*) l_list_out_info_tmp->data; + if(l_tx_data->token_ticker[0]) + break; + l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp); + } + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + + dap_chain_datum_t *l_datum_prev = get_prev_tx(l_tx_data_prev); + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, NULL); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + // if use src addr + bool l_is_use_src_addr = false; + // find source addrs + dap_string_t *l_src_addr = dap_string_new(NULL); + { + // find IN items in prev datum - for get destination addr + dap_list_t *l_list_in_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_IN, NULL); + dap_list_t *l_list_tmp = l_list_in_prev_items; + while(l_list_tmp) { + dap_chain_tx_in_t *l_tx_prev_in = l_list_tmp->data; + dap_chain_hash_fast_t l_tx_prev_prev_hash = + l_tx_prev_in->header.tx_prev_hash; + //find prev OUT item + dap_tx_data_t *l_tx_data_prev_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &l_tx_prev_prev_hash, + sizeof(dap_chain_hash_fast_t), l_tx_data_prev_prev); + if(l_tx_data_prev_prev) { + // if use src addr + if(l_tx_data_prev_prev && + !memcmp(&l_tx_data_prev_prev->addr, a_addr, + sizeof(dap_chain_addr_t))) + l_is_use_src_addr = true; + char *l_str = dap_chain_addr_to_str(&l_tx_data_prev_prev->addr); + if(l_src_addr->len > 0) + dap_string_append_printf(l_src_addr, "\n %s", l_str); + else + dap_string_append_printf(l_src_addr, "%s", l_str); // first record + DAP_DELETE(l_str); + } + l_list_tmp = dap_list_next(l_list_tmp); + } + } + + char *l_dst_to_str = + (l_tx_prev_out) ? dap_chain_addr_to_str(&l_tx_prev_out->addr) : + NULL; + // if use dst addr + bool l_is_use_dst_addr = false; + if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_is_use_dst_addr = true; + + l_src_str_is_cur = l_is_use_src_addr; + if(l_src_addr->len <= 1) { + l_src_str = + (l_tx_data) ? dap_chain_addr_to_str(&l_tx_data->addr) : + NULL; + if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_src_str_is_cur = true; + dap_string_free(l_src_addr, true); + } + else + l_src_str = dap_string_free(l_src_addr, false); + if(l_is_use_src_addr && !l_is_use_dst_addr) { + dap_string_append_printf(l_str_out, + "tx hash %s \n %s in send %lu %s from %s\n to %s\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_prev_out->header.value, + l_tx_data->token_ticker, + l_src_str ? l_src_str : "", + l_dst_to_str); + } else if(l_is_use_dst_addr && !l_is_use_src_addr) { + if(!l_src_str_is_cur) + dap_string_append_printf(l_str_out, + "tx hash %s \n %s in recv %lu %s from %s\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_prev_out->header.value, + l_tx_data->token_ticker, + l_src_str ? l_src_str : ""); + } + + DAP_DELETE(l_dst_to_str); + dap_list_free(l_list_out_prev_items); + } + + // OUT items + dap_list_t *l_records_tmp = l_records_out; + while(l_records_tmp) { + + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data; + + if(l_tx_data->is_use_all_cur_out + || !memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) { + + char *l_addr_str = (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : NULL; + + if(!memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) { + if(!l_src_str_is_cur) + dap_string_append_printf(l_str_out, + "tx hash %s \n %s recv %lu %s from %s\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_out->header.value, + l_tx_data_prev->token_ticker, + l_src_str ? l_src_str : "?"); + // break search prev OUT items for IN items + l_list_in_items2_tmp = NULL; + } + else { + dap_string_append_printf(l_str_out, + "tx hash %s \n %s send %lu %s to %s\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_out->header.value, + l_tx_data_prev->token_ticker, + l_addr_str ? l_addr_str : ""); + l_list_in_items2_tmp = NULL; + } + DAP_DELETE(l_addr_str); + } + + l_records_tmp = dap_list_next(l_records_tmp); + } + //dap_list_free(l_records_out); + DAP_DELETE(l_src_str); + + } + } + l_list_in_items2_tmp = dap_list_next(l_list_in_items2_tmp); + } +// l_list_in_items_tmp = dap_list_next(l_list_in_items_tmp); +// } + } + + if(l_list_tx_token) + dap_list_free(l_list_tx_token); + if(l_list_out_items) + dap_list_free(l_list_out_items); + if(l_list_in_items) + dap_list_free(l_list_in_items); + dap_list_free(l_records_out); + dap_list_free(l_list_out_info); + DAP_DELETE(l_time_str); + + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = l_atom ? a_chain->callback_atom_get_size(l_atom) : 0; + } + + // delete hashes + dap_tx_data_t *l_iter_current, *l_item_tmp; + HASH_ITER(hh, l_tx_data_hash , l_iter_current, l_item_tmp) + { + // delete datum + DAP_DELETE(l_iter_current->datum); + // delete struct + DAP_DELETE(l_iter_current); + HASH_DEL(l_tx_data_hash, l_iter_current); + } + // if no history + if(!l_str_out->len) + dap_string_append(l_str_out, " empty"); + char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL; + return l_ret_str; +} diff --git a/libdap-chain-net/dap_chain_node_cli_cmd_tx.h b/libdap-chain-net/dap_chain_node_cli_cmd_tx.h new file mode 100644 index 0000000000000000000000000000000000000000..dfc1e1409f270d365a7602f79cf9f09105e38a2a --- /dev/null +++ b/libdap-chain-net/dap_chain_node_cli_cmd_tx.h @@ -0,0 +1,33 @@ +/* + * Authors: + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "dap_chain.h" +#include "dap_chain_common.h" + +/** + * + * return history string + */ +char* dap_db_history_tx(dap_chain_hash_fast_t* a_tx_hash, dap_chain_t * a_chain); +char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain); diff --git a/libdap-chain-net/dap_chain_node_client.c b/libdap-chain-net/dap_chain_node_client.c new file mode 100644 index 0000000000000000000000000000000000000000..ce016ffeaafab4a79f24cfa914f0b85ad882f2a1 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_client.c @@ -0,0 +1,671 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <naeper@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <errno.h> +#include <assert.h> +#include <string.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#include <pthread.h> +#endif + +#include "dap_common.h" +#include "dap_client.h" +#include "dap_config.h" +#include "dap_events.h" +#include "dap_hash.h" +//#include "dap_http_client_simple.h" +#include "dap_client_pvt.h" +#include "dap_chain_global_db_remote.h" +#include "dap_chain_global_db_hist.h" +#include "dap_stream_ch_pkt.h" +#include "dap_stream_ch_chain.h" +#include "dap_stream_ch_chain_pkt.h" +#include "dap_stream_ch_chain_net.h" +#include "dap_stream_ch_chain_net_pkt.h" +#include "dap_stream_pkt.h" + +//#include "dap_chain_common.h" +#include "dap_chain_node_client.h" + +#define LOG_TAG "dap_chain_node_client" + + +//static int listen_port_tcp = 8079; + +static void s_stage_connected_callback(dap_client_t *a_client, void *a_arg); +static void s_ch_chain_callback_notify_packet_out(dap_stream_ch_chain_t*, uint8_t a_pkt_type, + dap_stream_ch_chain_pkt_t *a_pkt, size_t a_pkt_data_size, + void * a_arg); +static void s_ch_chain_callback_notify_packet_in(dap_stream_ch_chain_t* a_ch_chain, uint8_t a_pkt_type, + dap_stream_ch_chain_pkt_t *a_pkt, size_t a_pkt_data_size, + void * a_arg); + +/** + * @brief dap_chain_node_client_init + * @return + */ +int dap_chain_node_client_init(void) +{ + return 0; +} + +/** + * @brief dap_chain_node_client_deinit + */ +void dap_chain_node_client_deinit() +{ + //dap_http_client_simple_deinit(); + dap_client_deinit(); +} + +/** + * @brief stage_status_callback + * @param a_client + * @param a_arg + */ +static void s_stage_status_callback(dap_client_t *a_client, void *a_arg) +{ + (void) a_client; + (void) a_arg; + + //printf("* stage_status_callback client=%x data=%x\n", a_client, a_arg); +} + +/** + * @brief s_stage_status_error_callback + * @param a_client + * @param a_arg + */ +static void s_stage_status_error_callback(dap_client_t *a_client, void *a_arg) +{ + dap_chain_node_client_t *l_node_client = DAP_CHAIN_NODE_CLIENT(a_client); + if(!l_node_client) + return; + // check for last attempt + bool l_is_last_attempt = a_arg ? true : false; + if(l_is_last_attempt){ + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_DISCONNECTED; + pthread_mutex_unlock(&l_node_client->wait_mutex); +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + } + + if(l_node_client && l_node_client->keep_connection && + ((dap_client_get_stage(a_client) != STAGE_STREAM_STREAMING) || + (dap_client_get_stage_status(a_client) == STAGE_STATUS_ERROR))) { + log_it(L_NOTICE,"Some errors happends, current state is %s but we need to return back to STAGE_STREAM_STREAMING", + dap_client_get_stage_str(a_client) ); + + log_it(L_DEBUG, "Wakeup all who waits"); + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_ERROR; + pthread_mutex_unlock(&l_node_client->wait_mutex); + +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + //dap_client_go_stage( a_client , STAGE_STREAM_STREAMING, s_stage_end_callback ); + } + //printf("* tage_status_error_callback client=%x data=%x\n", a_client, a_arg); +} + +/** + * @brief a_stage_end_callback + * @param a_client + * @param a_arg + */ +static void s_stage_connected_callback(dap_client_t *a_client, void *a_arg) +{ + dap_chain_node_client_t *l_node_client = a_client->_inheritor; + //assert(l_node_client); + if(l_node_client) { + log_it(L_NOTICE, "Stream connection with node " NODE_ADDR_FP_STR " established", + NODE_ADDR_FP_ARGS_S( l_node_client->remote_node_addr)); + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_CONNECTED; + // find current channel code + dap_client_pvt_t * l_client_internal = DAP_CLIENT_PVT(a_client); + dap_stream_ch_t * l_ch = NULL; + if(l_client_internal && l_client_internal->active_channels) + l_ch = dap_client_get_stream_ch(a_client, l_client_internal->active_channels[0]); + //dap_stream_ch_t * l_ch = dap_client_get_stream_ch(a_client, dap_stream_ch_chain_get_id()); + if(l_ch) { + dap_stream_ch_chain_t * l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch); + l_ch_chain->callback_notify_packet_out = s_ch_chain_callback_notify_packet_out; + l_ch_chain->callback_notify_packet_in = s_ch_chain_callback_notify_packet_in; + l_ch_chain->callback_notify_arg = l_node_client; + } else { + log_it(L_WARNING, "No ch_chain channel, can't init notify callback for pkt type CH_CHAIN"); + } + + pthread_mutex_unlock(&l_node_client->wait_mutex); + if(l_node_client->callback_connected) + l_node_client->callback_connected(l_node_client, a_arg); + l_node_client->keep_connection = true; + log_it(L_DEBUG, "Wakeup all who waits"); + +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + + } +} + +/** + * @brief s_ch_chain_callback_notify_packet_in2 - for dap_stream_ch_chain_net + * @param a_ch_chain_net + * @param a_pkt_type + * @param a_pkt_net + * @param a_pkt_data_size + * @param a_arg + */ +static void s_ch_chain_callback_notify_packet_in2(dap_stream_ch_chain_net_t* a_ch_chain_net, uint8_t a_pkt_type, + dap_stream_ch_chain_net_pkt_t *a_pkt_net, size_t a_pkt_net_data_size, void * a_arg) +{ + dap_chain_node_client_t * l_node_client = (dap_chain_node_client_t *) a_arg; + switch (a_pkt_type) { + // get new generated current node address + case DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR_LEASE: { + if(a_pkt_net_data_size == sizeof(dap_chain_node_addr_t)) { + dap_chain_node_addr_t *l_addr = (dap_chain_node_addr_t *) a_pkt_net->data; + memcpy(&l_node_client->cur_node_addr, l_addr, sizeof(dap_chain_node_addr_t)); + } + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_NODE_ADDR_LEASED; + pthread_mutex_unlock(&l_node_client->wait_mutex); +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); + #endif + break; + } + // get remote node address + case DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_NODE_ADDR: { + + if(a_pkt_net_data_size == sizeof(dap_chain_node_addr_t)) { + dap_chain_node_addr_t *l_addr = (dap_chain_node_addr_t *) a_pkt_net->data; + memcpy(&l_node_client->remote_node_addr, l_addr, sizeof(dap_chain_node_addr_t)); + } + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_GET_NODE_ADDR; + pthread_mutex_unlock(&l_node_client->wait_mutex); +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + break; + } + } +} + +/** + * @brief s_ch_chain_callback_notify_packet_in - for dap_stream_ch_chain + * @param a_ch_chain + * @param a_pkt_type + * @param a_pkt + * @param a_pkt_data_size + * @param a_arg + */ +static void s_ch_chain_callback_notify_packet_in(dap_stream_ch_chain_t* a_ch_chain, uint8_t a_pkt_type, + dap_stream_ch_chain_pkt_t *a_pkt, size_t a_pkt_data_size, + void * a_arg) +{ + dap_chain_node_client_t * l_node_client = (dap_chain_node_client_t *) a_arg; + switch (a_pkt_type) { + case DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_ERROR: + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_ERROR; + dap_snprintf(l_node_client->last_error, sizeof(l_node_client->last_error), + "%s", (char*) a_pkt->data); + log_it(L_WARNING, "Received packet DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_ERROR with error \"%s\"", + l_node_client->last_error); + pthread_mutex_unlock(&l_node_client->wait_mutex); + +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + + case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_ALL: + case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB: + case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS: { + dap_stream_ch_chain_sync_request_t * l_request = NULL; + if(a_pkt_data_size == sizeof(*l_request)) + l_request = (dap_stream_ch_chain_sync_request_t*) a_pkt->data; + + if(l_request) { + uint64_t l_id_last_here = 1; + // for sync chain not used time + //if(a_pkt_type != DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS) + // l_id_last_here =(uint64_t) dap_db_log_get_last_id(); + if(1) {//if(l_request->id_start < l_id_last_here) { + log_it(L_INFO, "Remote is synced but we have updates for it"); + bool l_is_sync = true; + // Get log diff + if(a_pkt_type == DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB) { + a_ch_chain->request_last_ts = dap_db_log_get_last_id(); + uint64_t l_start_item = l_request->id_start; + dap_db_log_list_t *l_db_log = NULL; + // If the current global_db has been truncated, but the remote node has not known this + uint64_t l_last_id = dap_db_log_get_last_id(); + if(l_request->id_start > a_ch_chain->request_last_ts){ + dap_chain_net_t *l_net = dap_chain_net_by_id(a_pkt->hdr.net_id); + dap_list_t *l_add_groups = dap_chain_net_get_add_gdb_group(l_net, a_ch_chain->request.node_addr); + l_db_log = dap_db_log_list_start(l_start_item + 1, l_add_groups); + if(!l_db_log) + l_start_item = 0; + } + //dap_list_t *l_list = dap_db_log_get_list(l_request->id_start + 1); + if(!l_db_log){ + dap_chain_net_t *l_net = dap_chain_net_by_id(a_pkt->hdr.net_id); + dap_list_t *l_add_groups = dap_chain_net_get_add_gdb_group(l_net, a_ch_chain->request.node_addr); + l_db_log = dap_db_log_list_start(l_start_item + 1, l_add_groups); + } + if(l_db_log) { + // Add it to outgoing list + //l_list->prev = a_ch_chain->request_global_db_trs; + a_ch_chain->request_global_db_trs = l_db_log;//l_list; + } + else + l_is_sync = false; + } + if(l_is_sync) { + a_ch_chain->request_net_id.uint64 = a_pkt->hdr.net_id.uint64; + a_ch_chain->request_cell_id.uint64 = a_pkt->hdr.cell_id.uint64; + a_ch_chain->request_chain_id.uint64 = a_pkt->hdr.chain_id.uint64; + a_ch_chain->state = dap_stream_ch_chain_pkt_type_to_dap_stream_ch_chain_state(a_pkt_type);//CHAIN_STATE_SYNC_CHAINS;//GLOBAL_DB; + + // type of first packet + uint8_t l_type = + (a_pkt_type != DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS) ? + DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB : + DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN; + if(l_type == DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN) + { + dap_chain_t * l_chain = dap_chain_find_by_id(a_pkt->hdr.net_id, a_pkt->hdr.chain_id); + dap_chain_atom_iter_t* l_iter = l_chain ? l_chain->callback_atom_iter_create(l_chain) : NULL; + //a_ch_chain->request_atom_iter = l_iter; + + dap_chain_atom_ptr_t * l_lasts = NULL; + size_t l_lasts_size = 0; + l_lasts = l_chain->callback_atom_iter_get_lasts(l_iter, &l_lasts_size); + for(size_t i = 0; i < l_lasts_size; i++) { + dap_chain_atom_item_t * l_item = NULL; + dap_chain_hash_fast_t l_atom_hash; + dap_hash_fast(l_lasts[i], l_chain->callback_atom_get_size(l_lasts[i]), &l_atom_hash); + HASH_FIND(hh, a_ch_chain->request_atoms_lasts, &l_atom_hash, sizeof(l_atom_hash), l_item); + if(l_item == NULL) { // Not found, add new lasts + l_item = DAP_NEW_Z(dap_chain_atom_item_t); + l_item->atom = l_lasts[i]; + memcpy(&l_item->atom_hash, &l_atom_hash, sizeof(l_atom_hash)); + HASH_ADD(hh, a_ch_chain->request_atoms_lasts, atom_hash, sizeof(l_atom_hash), l_item); + } + //else + // DAP_DELETE(l_lasts[i]); + } + DAP_DELETE(l_lasts); + DAP_DELETE(l_iter); + } + dap_chain_node_addr_t l_node_addr = { 0 }; + dap_chain_net_t *l_net = dap_chain_net_by_id(a_ch_chain->request_net_id); + l_node_addr.uint64 = l_net ? dap_db_get_cur_node_addr(l_net->pub.name) : 0; + dap_stream_ch_chain_pkt_write(a_ch_chain->ch, l_type, + a_ch_chain->request_net_id, a_ch_chain->request_chain_id, + a_ch_chain->request_cell_id, &l_node_addr, sizeof(dap_chain_node_addr_t)); + + log_it(L_INFO, "Sync for remote tr type=%d", a_pkt_type);//_count=%d", dap_list_length(l_list)); + dap_stream_ch_set_ready_to_write(a_ch_chain->ch, true); + } + else { + log_it(L_INFO, "Remote node has lastes timestamp for us type=%d", a_pkt_type); + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_SYNCED; + pthread_mutex_unlock(&l_node_client->wait_mutex); +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + + } + } + } else { + log_it(L_INFO, "Sync notify without request to sync back, stay in SYNCED state"); + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_SYNCED; + pthread_mutex_unlock(&l_node_client->wait_mutex); +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + } + + } + default: { + } + } +} + +/** + * @brief s_ch_chain_callback_notify_packet_in + * @param a_ch_chain + * @param a_pkt_type + * @param a_pkt + * @param a_pkt_data_size + * @param a_arg + */ +static void s_ch_chain_callback_notify_packet_out(dap_stream_ch_chain_t* a_ch_chain, uint8_t a_pkt_type, + dap_stream_ch_chain_pkt_t *a_pkt, size_t a_pkt_data_size, + void * a_arg) +{ + (void) a_pkt; + (void) a_pkt_data_size; + (void) a_ch_chain; + dap_chain_node_client_t * l_node_client = (dap_chain_node_client_t *) a_arg; + switch (a_pkt_type) { + case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_ALL: + case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB: + case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS: { + pthread_mutex_lock(&l_node_client->wait_mutex); + l_node_client->state = NODE_CLIENT_STATE_SYNCED; + pthread_mutex_unlock(&l_node_client->wait_mutex); +#ifndef _WIN32 + pthread_cond_signal(&l_node_client->wait_cond); +#else + SetEvent( l_node_client->wait_cond ); +#endif + } + break; + default: { + } + } +} + +/** + * Create connection to server + * + * return a connection handle, or NULL, if an error + */ +dap_chain_node_client_t* dap_chain_client_connect(dap_chain_node_info_t *a_node_info, dap_client_stage_t a_stage_target, + const char *a_active_channels) +{ + if(!a_node_info) { + log_it(L_ERROR, "Can't connect to the node: null object node_info"); + return NULL; + } + dap_chain_node_client_t *l_node_client = DAP_NEW_Z(dap_chain_node_client_t); + l_node_client->state = NODE_CLIENT_STATE_DISCONNECTED; + +#ifndef _WIN32 + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + pthread_cond_init(&l_node_client->wait_cond, &attr); +#else + l_node_client->wait_cond = CreateEventA( NULL, FALSE, FALSE, NULL ); +#endif + + pthread_mutex_init(&l_node_client->wait_mutex, NULL); + l_node_client->events = NULL; //dap_events_new(); + l_node_client->client = dap_client_new(l_node_client->events, s_stage_status_callback, + s_stage_status_error_callback); + l_node_client->client->_inheritor = l_node_client; + l_node_client->remote_node_addr.uint64 = a_node_info->hdr.address.uint64; + dap_client_set_active_channels(l_node_client->client, a_active_channels); + + int hostlen = 128; + char host[hostlen]; + if(a_node_info->hdr.ext_addr_v4.s_addr) + { + struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = a_node_info->hdr.ext_addr_v4 }; + inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host, hostlen); + } + else + { + struct sockaddr_in6 sa6 = { .sin6_family = AF_INET6, .sin6_addr = a_node_info->hdr.ext_addr_v6 }; + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) &sa6)->sin6_addr), host, hostlen); + } + // address not defined + if(!strcmp(host, "::")) { + dap_chain_node_client_close(l_node_client); + return NULL; + } + dap_client_set_uplink(l_node_client->client, strdup(host), a_node_info->hdr.ext_port); +// dap_client_stage_t a_stage_target = STAGE_ENC_INIT; +// dap_client_stage_t l_stage_target = STAGE_STREAM_STREAMING; + + l_node_client->state = NODE_CLIENT_STATE_CONNECT; + // ref pvt client + //dap_client_pvt_ref(DAP_CLIENT_PVT(l_node_client->client)); + // Handshake & connect + dap_client_go_stage(l_node_client->client, a_stage_target, s_stage_connected_callback); + return l_node_client; +} + +/** + * Create connection to server + * + * return a connection handle, or NULL, if an error + */ +dap_chain_node_client_t* dap_chain_node_client_connect(dap_chain_node_info_t *a_node_info) +{ + dap_client_stage_t l_stage_target = STAGE_STREAM_STREAMING; + const char *l_active_channels = "CN"; + return dap_chain_client_connect(a_node_info, l_stage_target, l_active_channels); +} + +/** + * Close connection to server, delete chain_node_client_t *client + */ +void dap_chain_node_client_close(dap_chain_node_client_t *a_client) +{ + if (a_client && a_client->client) { // block tryes to close twice + // clean client + dap_client_delete(a_client->client); +#ifndef _WIN32 + pthread_cond_destroy(&a_client->wait_cond); +#else + CloseHandle( a_client->wait_cond ); +#endif + pthread_mutex_destroy(&a_client->wait_mutex); + DAP_DELETE(a_client); + } +} + +/** + * Send stream request to server + */ +int dap_chain_node_client_send_ch_pkt(dap_chain_node_client_t *a_client, uint8_t a_ch_id, uint8_t a_type, + const void *a_pkt_data, size_t a_pkt_data_size) +{ + if(!a_client || a_client->state < NODE_CLIENT_STATE_CONNECTED) + return -1; + +// dap_stream_t *l_stream = dap_client_get_stream(a_client->client); + dap_stream_ch_t * l_ch = dap_client_get_stream_ch(a_client->client, a_ch_id); + if(l_ch) { +// dap_stream_ch_chain_net_t * l_ch_chain = DAP_STREAM_CH_CHAIN_NET(l_ch); + + dap_stream_ch_pkt_write(l_ch, a_type, a_pkt_data, a_pkt_data_size); + dap_stream_ch_set_ready_to_write(l_ch, true); + return 0; + } else + return -1; +} + +/** + * wait for the complete of request + * + * timeout_ms timeout in milliseconds + * waited_state state which we will wait, sample NODE_CLIENT_STATE_CONNECT or NODE_CLIENT_STATE_SENDED + * return -2 false, -1 timeout, 0 end of connection or sending data + */ +int dap_chain_node_client_wait(dap_chain_node_client_t *a_client, int a_waited_state, int a_timeout_ms) +{ + int ret = -1; + if(!a_client) + return -3; + + pthread_mutex_lock(&a_client->wait_mutex); + // have waited + if(a_client->state == a_waited_state) { + pthread_mutex_unlock(&a_client->wait_mutex); + return 0; + } + if(a_client->state == NODE_CLIENT_STATE_DISCONNECTED) { + pthread_mutex_unlock(&a_client->wait_mutex); + return -2; + } + +#ifndef _WIN32 + // prepare for signal waiting + struct timespec to; + clock_gettime( CLOCK_MONOTONIC, &to); + int64_t nsec_new = to.tv_nsec + a_timeout_ms * 1000000ll; + // if the new number of nanoseconds is more than a second + + if(nsec_new > (long) 1e9) { + to.tv_sec += nsec_new / (long) 1e9; + to.tv_nsec = nsec_new % (long) 1e9; + } + else + to.tv_nsec = (long) nsec_new; +#else + pthread_mutex_unlock( &a_client->wait_mutex ); +#endif + + // signal waiting + do { + +#ifndef _WIN32 + int wait = pthread_cond_timedwait(&a_client->wait_cond, &a_client->wait_mutex, &to); + if(wait == 0 && ( + a_client->state == a_waited_state || + (a_client->state == NODE_CLIENT_STATE_ERROR || a_client->state == NODE_CLIENT_STATE_DISCONNECTED)) + ) { + ret = a_client->state == a_waited_state ? 0 : -2; + break; + } + else if(wait == ETIMEDOUT) { // 110 260 + ret = -1; + break; + } +#else + int wait = WaitForSingleObject( a_client->wait_cond, (uint32_t)a_timeout_ms ); + pthread_mutex_lock( &a_client->wait_mutex ); + + if ( wait == WAIT_OBJECT_0 && ( + a_client->state == a_waited_state || + a_client->state == NODE_CLIENT_STATE_ERROR || a_client->state == NODE_CLIENT_STATE_DISCONNECTED + ) { + ret = a_client->state == a_waited_state ? 0 : -2; + break; + } + else if ( wait == WAIT_TIMEOUT || wait == WAIT_FAILED ) { + ret = -1; + break; + } +#endif + + } while(1); + + pthread_mutex_unlock(&a_client->wait_mutex); + return ret; +} + +int dap_chain_node_client_set_callbacks(dap_client_t *a_client, uint8_t a_ch_id) +{ + int l_ret = -1; + dap_chain_node_client_t *l_node_client = a_client->_inheritor; + if(l_node_client) { + pthread_mutex_lock(&l_node_client->wait_mutex); + // find current channel code + dap_client_pvt_t * l_client_internal = DAP_CLIENT_PVT(a_client); + dap_stream_ch_t * l_ch = NULL; + if(l_client_internal) + l_ch = dap_client_get_stream_ch(a_client, a_ch_id); + if(l_ch) { + if(a_ch_id == dap_stream_ch_chain_get_id()) { + dap_stream_ch_chain_t * l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch); + l_ch_chain->callback_notify_packet_out = s_ch_chain_callback_notify_packet_out; + l_ch_chain->callback_notify_packet_in = s_ch_chain_callback_notify_packet_in; + l_ch_chain->callback_notify_arg = l_node_client; + } + if(a_ch_id == dap_stream_ch_chain_net_get_id()) { + dap_stream_ch_chain_net_t *l_ch_chain = DAP_STREAM_CH_CHAIN_NET(l_ch); + l_ch_chain->notify_callback = s_ch_chain_callback_notify_packet_in2; + l_ch_chain->notify_callback_arg = l_node_client; + } + l_ret = 0; + } else { + } + pthread_mutex_unlock(&l_node_client->wait_mutex); + } + return l_ret; +} + +/*static void nodelist_response_callback(dap_client_t *a_client, void *data, size_t data_len) +{ +} + +static void nodelist_response_error_callback(dap_client_t *a_client, int a_err) +{ +}*/ + +/** + * Send nodelist request to server + */ +int dap_chain_node_client_send_nodelist_req(dap_chain_node_client_t *a_client) +{ + if(!a_client || !a_client->client || a_client->state < NODE_CLIENT_STATE_CONNECTED) + return -1; + //dap_client_pvt_t * l_client_pvt = DAP_CLIENT_PVT(a_client->client); + + //TODO send request to get nodelist + //dap_client_request_enc(a_client->client, DAP_UPLINK_PATH_NODE_LIST, "", "", "", 0, + // nodelist_response_callback, nodelist_response_error_callback); + return 1; +} diff --git a/libdap-chain-net/dap_chain_node_client.h b/libdap-chain-net/dap_chain_node_client.h new file mode 100644 index 0000000000000000000000000000000000000000..4a5eb2e307768a133a46050a280d871b4bff22e2 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_client.h @@ -0,0 +1,117 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <naeper@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <pthread.h> +#include <stdbool.h> + +#include "uthash.h" +#include "dap_client.h" +#include "dap_chain_node.h" + +// connection states +typedef enum dap_chain_node_client_state { + NODE_CLIENT_STATE_ERROR = -1, + NODE_CLIENT_STATE_DISCONNECTED = 0, + NODE_CLIENT_STATE_GET_NODE_ADDR = 1, + NODE_CLIENT_STATE_NODE_ADDR_LEASED = 2, + NODE_CLIENT_STATE_PING = 3, + NODE_CLIENT_STATE_PONG = 4, + NODE_CLIENT_STATE_CONNECT = 5, + NODE_CLIENT_STATE_CONNECTED = 100, + //NODE_CLIENT_STATE_SEND, + //NODE_CLIENT_STATE_SENDED, + NODE_CLIENT_STATE_SYNC_GDB = 101, + NODE_CLIENT_STATE_SYNC_CHAINS = 102, + NODE_CLIENT_STATE_SYNCED = 103, +} dap_chain_node_client_state_t; + +typedef struct dap_chain_node_client dap_chain_node_client_t; + +typedef void (*dap_chain_node_client_callback_t)(dap_chain_node_client_t *, void*); + +// state for a client connection +typedef struct dap_chain_node_client { + dap_chain_node_client_state_t state; + dap_chain_cell_id_t cell_id; + dap_client_t *client; + dap_events_t *events; + char last_error[128]; + + dap_chain_node_client_callback_t callback_connected; + #ifndef _WIN32 + pthread_cond_t wait_cond; + #else + HANDLE wait_cond; +#endif + pthread_mutex_t wait_mutex; + + // For hash indexing + UT_hash_handle hh; + dap_chain_node_addr_t cur_node_addr; + dap_chain_node_addr_t remote_node_addr; + struct in_addr remote_ipv4; + struct in6_addr remote_ipv6; + + bool keep_connection; +} dap_chain_node_client_t; +#define DAP_CHAIN_NODE_CLIENT(a) (a ? (dap_chain_node_client_t *) (a)->_inheritor : NULL) + +int dap_chain_node_client_init(void); + +void dap_chain_node_client_deinit(void); + +dap_chain_node_client_t* dap_chain_client_connect(dap_chain_node_info_t *a_node_info, dap_client_stage_t a_stage_target, + const char *a_active_channels); +/** + * Create handshake to server + * + * return a connection handle, or NULL, if an error + */ +dap_chain_node_client_t* dap_chain_node_client_connect(dap_chain_node_info_t *node_info); + +/** + * Close connection to server, delete chain_node_client_t *client + */ +void dap_chain_node_client_close(dap_chain_node_client_t *client); + +/** + * Send stream request to server + */ +int dap_chain_node_client_send_ch_pkt(dap_chain_node_client_t *a_client, uint8_t a_ch_id, uint8_t a_type, + const void *a_buf, size_t a_buf_size); + +/** + * wait for the complete of request + * + * timeout_ms timeout in milliseconds + * waited_state state which we will wait, sample NODE_CLIENT_STATE_CONNECT or NODE_CLIENT_STATE_SENDED + * return -1 false, 0 timeout, 1 end of connection or sending data + */ +int dap_chain_node_client_wait(dap_chain_node_client_t *a_client, int a_waited_state, int a_timeout_ms); + +int dap_chain_node_client_set_callbacks(dap_client_t *a_client, uint8_t a_ch_id); + +int dap_chain_node_client_send_nodelist_req(dap_chain_node_client_t *a_client); + + diff --git a/libdap-chain-net/dap_chain_node_ping.c b/libdap-chain-net/dap_chain_node_ping.c new file mode 100644 index 0000000000000000000000000000000000000000..660dfa97892d7da1a0588ebb8fb4299e35daa02a --- /dev/null +++ b/libdap-chain-net/dap_chain_node_ping.c @@ -0,0 +1,320 @@ +/* + * Authors: + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +//#include <sys/socket.h> +#include <time.h> +#include <errno.h> +#include <pthread.h> + +#include "dap_common.h" +#include "dap_client.h" +#include "dap_strfuncs.h" +#include "dap_list.h" +#include "dap_chain_common.h" +#include "dap_chain_node.h" +#include "dap_chain_node_ping.h" + +/* + #include <stdlib.h> + #include <stdio.h> + #include <stddef.h> + #include <stdint.h> + #include <string.h> + #include <stdbool.h> + #include <assert.h> + #include <ctype.h> + #include <dirent.h> + */ + +#ifdef WIN32 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#include <wepoll.h> +#else +#include <signal.h> +#endif +#include <pthread.h> + +#include "iputils/iputils.h" + +#include "dap_common.h" +//#include "dap_client.h" +#include "dap_strfuncs.h" +#include "dap_chain_node_cli.h" +#include "dap_chain_node_ping.h" + +#define LOG_TAG "chain_node_ping" + +static void* node_ping_proc(void *a_arg) +{ + struct in_addr l_addr = { 0 }; + int l_port = 0; + int l_count; + if(!a_arg) + return NULL ; + memcpy(&l_count, a_arg, sizeof(int)); + memcpy(&l_port, (a_arg + sizeof(int)), sizeof(int)); + memcpy(&l_addr, (a_arg + 2 * sizeof(int)), sizeof(struct in_addr)); + DAP_DELETE(a_arg); + + char *host4 = DAP_NEW_SIZE(char, INET_ADDRSTRLEN); + struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = l_addr }; + const char* str_ip4 = inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host4, INET_ADDRSTRLEN); + if(!str_ip4) + return NULL ; + //printf(" %s %d ping start\n", str_ip4, l_count); + /* + // send ping + ping_handle_t *l_ping_handle = ping_handle_create(); + //iputils_set_verbose(); + int res = ping_util(l_ping_handle, str_ip4, l_count); + DAP_DELETE(l_ping_handle); + printf(" %s %d ping=%d us\n",str_ip4,l_count,res ); + DAP_DELETE(host4); + */ + + // instead of ping to connect with server and send/recv header + long res = -1; + { + struct timespec l_time_start, l_time_stop; + struct sockaddr_in l_remote_addr = { 0 }; + //memset(&l_remote_addr, 0, sizeof(l_remote_addr)); + l_remote_addr.sin_family = AF_INET; + l_remote_addr.sin_port = htons(l_port); + l_remote_addr.sin_addr = l_addr; + + SOCKET l_socket = socket( PF_INET, SOCK_STREAM, 0); + if(l_socket == INVALID_SOCKET) { + log_it(L_ERROR, "Can't create socket"); + return (void*) -1; + } + clock_gettime(CLOCK_MONOTONIC, &l_time_start); + + if(connect(l_socket, (struct sockaddr *) &l_remote_addr, sizeof(struct sockaddr_in)) != SOCKET_ERROR) { + size_t l_buf_size = 1024; + uint8_t l_buf[l_buf_size]; + + const char* str_ip4 = inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host4, + INET_ADDRSTRLEN); + char *l_str_to_send = dap_strdup_printf("GET /%s/ping_sub_url HTTP/1.1\r\nHost: %s\r\n\r\n", + DAP_UPLINK_PATH_ENC_INIT, str_ip4); + // send data to bad suburl + int l_send_count = send(l_socket, l_str_to_send, dap_strlen(l_str_to_send), 0); + long l_recv_count = 0; + // recv data with error message + if(l_send_count > 30) + l_recv_count = s_recv(l_socket, l_buf, l_buf_size, 1000); + // connect/send/recv was successful + if(l_recv_count > 20) { + clock_gettime(CLOCK_MONOTONIC, &l_time_stop); + res = timespec_diff(&l_time_start, &l_time_stop, NULL); + } + DAP_DELETE(l_str_to_send); + } + else { + ; //log_it(L_INFO, "Can't connect to node for ping"); + } + closesocket(l_socket); + } + return (void*) res; +} + +// start sending ping +int start_node_ping(pthread_t *a_thread, struct in_addr a_addr, int a_port, int a_count) +{ + uint8_t *l_data = DAP_NEW_Z_SIZE(uint8_t, sizeof(struct in_addr) + 2 * sizeof(int)); + memcpy(l_data, &a_count, sizeof(int)); + memcpy(l_data + sizeof(int), &a_port, sizeof(int)); + memcpy(l_data + 2 * sizeof(int), &a_addr, sizeof(struct in_addr)); + pthread_create(a_thread, NULL, node_ping_proc, l_data); + return 0; +} + +// wait for ending ping within timeout_ms milliseconds +int wait_node_ping(pthread_t l_thread, int timeout_ms) +{ + int l_ping_time = 0; + struct timespec l_wait_time; + clock_gettime(CLOCK_REALTIME, &l_wait_time); + + timeout_ms *= 1000; + l_wait_time.tv_sec += timeout_ms / DAP_USEC_PER_SEC; + l_wait_time.tv_nsec += 1000 * (timeout_ms % DAP_USEC_PER_SEC); +#if !defined(_WIN32) && !defined(__ANDROID__) + int res = pthread_timedjoin_np(l_thread, (void **) &l_ping_time, &l_wait_time); +#else + int res = pthread_join(l_thread, (void **) &l_ping_time); +#endif + if(res == ETIMEDOUT) { + pthread_kill(l_thread, 3); // SIGQUIT SIGABRT + } + else if(!res) + return l_ping_time; + return -1; +} + +static dap_chain_node_addr_t *s_node_addr_tr = NULL, *s_node_addr_ping = NULL; + +static void* node_ping_background_proc(void *a_arg) +{ + dap_chain_net_t *l_net; + dap_list_t *l_node_list; + memcpy(&l_net, a_arg, sizeof(dap_chain_net_t*)); + memcpy(&l_node_list, a_arg + sizeof(dap_chain_net_t*), sizeof(dap_list_t*)); + DAP_DELETE(a_arg); + dap_chain_node_addr_t l_node_addr = { 0 }; + + + // select the nearest node from the list + unsigned int l_nodes_count = dap_list_length(l_node_list); + unsigned int l_thread_id = 0; + pthread_t *l_threads = DAP_NEW_Z_SIZE(pthread_t, sizeof(pthread_t) * l_nodes_count); + uint64_t *l_nodes_addr = DAP_NEW_Z_SIZE(uint64_t, sizeof(uint64_t) * l_nodes_count); + dap_list_t *l_node_list0 = l_node_list; + + dap_chain_node_addr_t *s_node_addr_tr = NULL, *l_node_addr_ping = NULL; + int l_min_hops = INT32_MAX; + int l_min_ping = INT32_MAX; + // send ping to all nodes + while(l_node_list) { + dap_chain_node_addr_t *l_node_addr = l_node_list->data; + dap_chain_node_info_t *l_node_info = dap_chain_node_info_read(l_net, l_node_addr); + + + char *host4 = DAP_NEW_SIZE(char, INET_ADDRSTRLEN); + struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = l_node_info->hdr.ext_addr_v4 }; + const char* str_ip4 = inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host4, INET_ADDRSTRLEN); + if(!str_ip4) + continue; + int hops = 0, time_usec = 0; +#ifndef _WIN32 + int res = traceroute_util(str_ip4, &hops, &time_usec); +#endif + DAP_DELETE(host4); + if(l_min_hops>hops) + s_node_addr_tr = l_node_list->data; + + // start sending ping + start_node_ping(&l_threads[l_thread_id], l_node_info->hdr.ext_addr_v4, l_node_info->hdr.ext_port, 1); + + l_nodes_addr[l_thread_id] = l_node_info->hdr.address.uint64; + l_thread_id++; + DAP_DELETE(l_node_info); + l_node_list = dap_list_next(l_node_list); + } + // wait for reply from nodes + int best_node_pos = -1; + int best_node_reply = INT32_MAX; + // timeout for all threads + int l_timeout_full_ms = 3000; // wait max 3 second + for(l_thread_id = 0; l_thread_id < l_nodes_count; l_thread_id++) { + if(l_timeout_full_ms < 100) + l_timeout_full_ms = 100; // make small timeout anyway, may be + struct timespec l_time_start; + clock_gettime(CLOCK_MONOTONIC, &l_time_start); + int res = wait_node_ping(l_threads[l_thread_id], l_timeout_full_ms); + if(res > 0 && res < best_node_reply) { + best_node_pos = l_thread_id; + s_node_addr_ping = l_node_list->data; + best_node_reply = res; + } + struct timespec l_time_stop; + clock_gettime(CLOCK_MONOTONIC, &l_time_stop); + l_timeout_full_ms -= timespec_diff(&l_time_start, &l_time_stop, NULL); + //printf(" thread %x ping=%d\n", l_threads[l_thread_id], res); + } + if(best_node_pos > 0) { + l_node_addr.uint64 = l_nodes_addr[best_node_pos]; + } + + // allocate memory for best node addresses + dap_chain_node_addr_t *s_node_addr_tmp; + s_node_addr_tmp = DAP_NEW(dap_chain_node_addr_t); + memcpy(s_node_addr_tmp, s_node_addr_tr, sizeof(dap_chain_node_addr_t)); + s_node_addr_tr = s_node_addr_tmp; + s_node_addr_tmp = DAP_NEW(dap_chain_node_addr_t); + memcpy(s_node_addr_tmp, s_node_addr_ping, sizeof(dap_chain_node_addr_t)); + s_node_addr_ping = s_node_addr_tmp; + + // delete memory + DAP_DELETE(l_nodes_addr); + DAP_DELETE(l_threads); + dap_list_free_full(l_node_list0, free); + return 0; +} + +static pthread_t s_thread = 0; + +// start background thread for testing connect to the nodes +int dap_chain_node_ping_background_start(dap_chain_net_t *a_net, dap_list_t *a_node_list) +{ + if(!a_node_list) + return -1; + // already started + if(s_thread) + return 0; + // copy list + dap_list_t *l_node_list = NULL; + dap_list_t *l_node_list_tmp = a_node_list; + while(l_node_list_tmp) { + dap_chain_node_addr_t *l_addr = DAP_NEW(dap_chain_node_addr_t); + memcpy(l_addr, l_node_list_tmp->data, sizeof(dap_chain_node_addr_t)); + l_node_list = dap_list_append(l_node_list, l_addr); + l_node_list_tmp = dap_list_next(l_node_list_tmp); + } + // start searching for better nodes + uint8_t *l_arg = DAP_NEW_SIZE(uint8_t, sizeof(dap_chain_net_t*) + sizeof(dap_chain_net_t*)); + memcpy(l_arg, &a_net, sizeof(dap_chain_net_t*)); + memcpy(l_arg + sizeof(dap_chain_net_t*), &l_node_list, sizeof(dap_list_t*)); + pthread_create(&s_thread, NULL, node_ping_background_proc, l_arg); + return 0; +} + +const dap_chain_node_addr_t* dap_chain_node_ping_get_node_tr(void) +{ + return s_node_addr_tr; +} + +const dap_chain_node_addr_t* dap_chain_node_ping_get_node_ping(void) +{ + return s_node_addr_ping; +} + +int dap_chain_node_ping_background_stop(void) +{ + int l_ret = wait_node_ping(s_thread, 500); + s_thread = 0; + return l_ret; +} + +int dap_chain_node_ping_background_status(void) +{ + if(s_thread) + return 1; + return 0; +} diff --git a/libdap-chain-net/dap_chain_node_ping.h b/libdap-chain-net/dap_chain_node_ping.h new file mode 100644 index 0000000000000000000000000000000000000000..a44f4fc25966047b079e9f6d15daee117daa6150 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_ping.h @@ -0,0 +1,40 @@ +/* + * Authors: + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ +#pragma once + +#ifndef _WIN32 +#include <netinet/in.h> +#else +#include <windows.h> +#endif +#include <pthread.h> + +// start sending ping +int start_node_ping(pthread_t *a_thread, struct in_addr a_addr, int a_port, int a_count); + +// wait for ending ping within timeout_ms milliseconds +int wait_node_ping(pthread_t l_thread, int timeout_ms); + + +// background thread for testing connect to the nodes +int dap_chain_node_ping_background_start(dap_chain_net_t *a_net, dap_list_t *a_node_list); +int dap_chain_node_ping_background_stop(void); +int dap_chain_node_ping_background_status(void); diff --git a/libdap-chain-net/dap_chain_node_remote.c b/libdap-chain-net/dap_chain_node_remote.c new file mode 100644 index 0000000000000000000000000000000000000000..cabf4dd34e4b5e07bcf67b93169b5a38f6a54636 --- /dev/null +++ b/libdap-chain-net/dap_chain_node_remote.c @@ -0,0 +1,149 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <naeper@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <pthread.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#endif + +#include "uthash.h" +#include "dap_common.h" +#include "dap_chain_node_remote.h" + +typedef struct dap_chain_node_link_item { + dap_chain_node_addr_t address; + dap_chain_node_client_t *client; + UT_hash_handle hh; +} dap_chain_node_link_item_t; + +// List of connections +static dap_chain_node_link_item_t *conn_list = NULL; + +// for separate access to connect_list +static pthread_mutex_t connect_list_mutex = PTHREAD_MUTEX_INITIALIZER; + + +/** + * Add new established connection to the list + * + * return 0 OK, -1 error, -2 already present + */ +int dap_chain_node_client_list_add(dap_chain_node_addr_t *a_address, dap_chain_node_client_t *a_client) +{ + int l_ret = 0; + if(!a_address || !a_client) + return -1; + dap_chain_node_link_item_t *item_tmp = NULL; + pthread_mutex_lock(&connect_list_mutex); + HASH_FIND(hh, conn_list, a_address, sizeof(dap_chain_node_addr_t), item_tmp); // address already in the hash? + if(item_tmp == NULL) { + item_tmp = DAP_NEW(dap_chain_node_link_item_t); + item_tmp->address.uint64 = a_address->uint64; + item_tmp->client = a_client; + HASH_ADD(hh, conn_list, address, sizeof(dap_chain_node_addr_t), item_tmp); // address: name of key field + l_ret = 0; + } + // connection already present + else + l_ret = -2; + //connect_list = g_list_append(connect_list, client); + pthread_mutex_unlock(&connect_list_mutex); + return l_ret; +} + +/** + * Delete established connection from the list + * + * return 0 OK, -1 error, -2 address not found + */ +int chain_node_client_list_del(dap_chain_node_addr_t *address) +{ + int ret = -1; + if(!address) + return -1; + dap_chain_node_link_item_t *item_tmp; + pthread_mutex_lock(&connect_list_mutex); + HASH_FIND(hh, conn_list, address, sizeof(dap_chain_node_addr_t), item_tmp); + if(item_tmp != NULL) { + HASH_DEL(conn_list, item_tmp); + ret = 0; + } + else + // address not found in the hash + ret = -2; + pthread_mutex_unlock(&connect_list_mutex); + if(!ret) { + // close connection + dap_chain_node_client_close(item_tmp->client); + // del struct for hash + DAP_DELETE(item_tmp); + } + return ret; +} + +/** + * Delete all established connection from the list + */ +void chain_node_client_list_del_all(void) +{ + int ret = -1; + dap_chain_node_link_item_t *iter_current, *item_tmp; + pthread_mutex_lock(&connect_list_mutex); + HASH_ITER(hh, conn_list , iter_current, item_tmp) { + // close connection + dap_chain_node_client_close(iter_current->client); + // del struct for hash + HASH_DEL(conn_list, iter_current); + } + pthread_mutex_unlock(&connect_list_mutex); +} + +/** + * Get present established connection by address + * + * return client, or NULL if the connection not found in the list + */ +const dap_chain_node_client_t* chain_node_client_find(dap_chain_node_addr_t *address) +{ + int ret = 0; + if(!address) + return NULL; + dap_chain_node_client_t *client_ret = NULL; + dap_chain_node_link_item_t *item_tmp; + pthread_mutex_lock(&connect_list_mutex); + HASH_FIND(hh, conn_list, address, sizeof(dap_chain_node_addr_t), item_tmp); // address already in the hash? + if(item_tmp != NULL) { + client_ret = item_tmp->client; + } + pthread_mutex_unlock(&connect_list_mutex); + return client_ret; +} diff --git a/libdap-chain-net/dap_chain_node_remote.h b/libdap-chain-net/dap_chain_node_remote.h new file mode 100644 index 0000000000000000000000000000000000000000..ce8aedc93262ac6e2a2bf67cb27529d78726d50c --- /dev/null +++ b/libdap-chain-net/dap_chain_node_remote.h @@ -0,0 +1,53 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <naeper@demlabs.net> + * DeM Labs Inc. https://demlabs.net + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> + +#include "dap_chain_node.h" +#include "dap_chain_node_client.h" + +/** + * Add new established connection to the list + * + * return 0 OK, -1 error, -2 already present + */ +int dap_chain_node_client_list_add(dap_chain_node_addr_t *address, dap_chain_node_client_t *client); + +/** + * Delete established connection from the list + * + * return 0 OK, -1 error, -2 address not found + */ +int chain_node_client_list_del(dap_chain_node_addr_t *address); + +/** + * Delete all established connection from the list + */ +void chain_node_client_list_del_all(void); + +/** + * Get present established connection by address + * + * return client, or NULL if the connection not found in the list + */ +const dap_chain_node_client_t* chain_node_client_find(dap_chain_node_addr_t *address); diff --git a/libdap-chain-net/iputils/iputils.c b/libdap-chain-net/iputils/iputils.c new file mode 100755 index 0000000000000000000000000000000000000000..263590139c76977ee7e4f7ce37800825fd27e542 --- /dev/null +++ b/libdap-chain-net/iputils/iputils.c @@ -0,0 +1,49 @@ +/* + * Set utilities for networking + */ + +#include <stdio.h> +#include <stdbool.h> +#include "dap_common.h" +#include "dap_strfuncs.h" + +static bool LOG_VERBOSE = false; + +/** + * Set verbose mode + */ +void iputils_set_verbose(void) +{ + LOG_VERBOSE = true; +} + +/** + * Reset verbose mode + */ +void iputils_reset_verbose(void) +{ + LOG_VERBOSE = false; +} + +// analog printf() +int log_printf(const char *format, ...) +{ + int ret = 0; + if(LOG_VERBOSE) + { + char *log_str = NULL; + va_list args; + + va_start(args, format); + log_str = dap_strdup_vprintf(format, args); + va_end(args); + + if(log_str) + { + + ret = printf("%s", log_str); + DAP_DELETE(log_str); + } + } + return ret; +} diff --git a/libdap-chain-net/iputils/iputils.h b/libdap-chain-net/iputils/iputils.h new file mode 100644 index 0000000000000000000000000000000000000000..0330b548f1a23b5b677a4218683a933a169669e5 --- /dev/null +++ b/libdap-chain-net/iputils/iputils.h @@ -0,0 +1,189 @@ +/* + * Set utilities for networking + */ + +#ifndef _IPUTILS_H +#define _IPUTILS_H + +#include <stdint.h> +#include <stdlib.h> +#ifndef _WIN32 +#include <netinet/ip.h> +#else +#include <winsock2.h> +#include <windows.h> +#include <mswsock.h> +#include <ws2tcpip.h> +#include <io.h> +#include "win32/iphdr.h" +#include "win32/ip.h" +#define uid_t uint32_t +#endif +#include <setjmp.h> +#include <sys/time.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAXPACKET 128000 /* max packet size */ + +#define MAX_DUP_CHK 0x10000 + +#ifdef USE_BITMAP64 +typedef uint64_t bitmap_t; +# define BITMAP_SHIFT 6 +#else +typedef uint32_t bitmap_t; +# define BITMAP_SHIFT 5 +#endif + +struct rcvd_table { + bitmap_t bitmap[MAX_DUP_CHK / (sizeof(bitmap_t) * 8)]; +}; + +typedef struct ping_handle{ + int ts_type; + int nroute; + uint32_t route[10]; + + struct sockaddr_in whereto; /* who to ping */ + int optlen; + int settos; /* Set TOS, Precendence or other QOS options */ + + int broadcast_pings; + + struct sockaddr_in source; + char *device; + int pmtudisc; + struct { + + int options; + + int mark; + int sndbuf; + int ttl; + int rtt; + int rtt_addend; + uint16_t acked; + + unsigned char outpack[MAXPACKET]; + struct rcvd_table rcvd_tbl; + + // counters + long npackets; // max packets to transmit + long nreceived; // # of packets we got back + long nrepeats; // number of duplicates + long ntransmitted; // sequence # for outbound packets = #sent + long nchecksum; // replies with bad checksum + long nerrors; // icmp errors + int interval; // interval between packets (msec) + int preload; + int deadline; // time to die + int lingertime; + struct timeval start_time, cur_time; + //volatile int exiting; + //volatile int status_snapshot; + int confirm; + volatile int in_pr_addr; // pr_addr() is executing + jmp_buf pr_addr_jmp; + + /* Stupid workarounds for bugs/missing functionality in older linuces. + * confirm_flag fixes refusing service of kernels without MSG_CONFIRM. + * i.e. for linux-2.2 */ + int confirm_flag; + + // timing + int timing; // flag to do timing + long tmin; // minimum round trip time + long tmax; // maximum round trip time + /* Message for rpm maintainers: have _shame_. If you want + * to fix something send the patch to me for sanity checking. + * "sparcfix" patch is a complete non-sense, apparenly the person + * prepared it was stoned. + */ + long long tsum; // sum of all times, for doing average + long long tsum2; + int pipesize; + + int datalen; + + char *hostname; + int uid; + uid_t euid; + int ident; // process id to identify our packets + + int screen_width; + + #ifdef HAVE_LIBCAP + cap_value_t cap_raw; + cap_value_t cap_admin; + #endif + } ping_common; +}ping_handle_t; + +ping_handle_t* ping_handle_create(void); + +/** + * Send ping for ipv4 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util(ping_handle_t *a_ping_handle, const char *addr, int count); + +/** + * Send ping for ipv6 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util6(ping_handle_t *a_ping_handle, const char *addr, int count); + + +/** + * Tracepath host + * + * @addr[in] host name or IP address + * @hops[out] hops count + * @time_usec[out] latency in microseconds + * @return 0 Ok, -1 error + */ +int tracepath_util(const char *addr, int *hops, int *time_usec); + +/** + * Traceroute host + * + * @addr[in] host name or IP address + * @hops[out] hops count + * @time_usec[out] latency in microseconds + * @return 0 Ok, -1 error + */ +int traceroute_util(const char *addr, int *hops, int *time_usec); + + +/** + * Set verbose mode + */ +void iputils_set_verbose(void); +/** + * Reset verbose mode + */ +void iputils_reset_verbose(void); + + +// analog printf() +int log_printf(const char *format, ...); + +#define PACKAGE_NAME "iputils" +#define PACKAGE_VERSION "0.1" +#define IPUTILS_VERSION(_prog) "%s from %s %s\n", _prog, PACKAGE_NAME, PACKAGE_VERSION +#define UNUSED(x) (void)(x) + +#ifdef __cplusplus +} +#endif + +#endif // _IPUTILS_H diff --git a/libdap-chain-net/iputils/ping.c b/libdap-chain-net/iputils/ping.c new file mode 100644 index 0000000000000000000000000000000000000000..e634a606e8bde2c67697719a6d1295cbc4960a1d --- /dev/null +++ b/libdap-chain-net/iputils/ping.c @@ -0,0 +1,1694 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * If kernel does not support non-raw ICMP sockets, + * this program has to run SUID to ROOT or with + * net_cap_raw enabled. + */ + +#include "ping.h" + +#include <assert.h> +#include <netinet/ip_icmp.h> +#include <ifaddrs.h> +#include <math.h> +#include "dap_common.h" +#include "dap_strfuncs.h" +#include "iputils.h" + +#ifndef ICMP_FILTER +#define ICMP_FILTER 1 +struct icmp_filter { + uint32_t data; +}; +#endif + +ping_func_set_st ping4_func_set = { + .send_probe = ping4_send_probe, + .receive_error_msg = ping4_receive_error_msg, + .parse_reply = ping4_parse_reply, + .install_filter = ping4_install_filter +}; + +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define NROUTES 9 /* number of record route slots */ +#define TOS_MAX 255 /* 8-bit TOS field */ + +/* +static int ts_type; +static int nroute = 0; +static uint32_t route[10]; + +static struct sockaddr_in whereto; // who to ping +static int optlen = 0; +static int settos = 0; // Set TOS, Precendence or other QOS options + +static int broadcast_pings = 0; + +static struct sockaddr_in source = { .sin_family = AF_INET }; +char *device; +int pmtudisc = -1; +*/ + +static void pr_options(ping_handle_t *a_ping_handle, unsigned char * cp, int hlen); +static void pr_iph(ping_handle_t *a_ping_handle, struct iphdr *ip); +static unsigned short in_cksum(const unsigned short *addr, int len, unsigned short salt); +static void pr_icmph(ping_handle_t *a_ping_handle, uint8_t type, uint8_t code, uint32_t info, struct icmphdr *icp); +static int parsetos(char *str); +static int parseflow(char *str); + +ping_handle_t* ping_handle_create(void) +{ + ping_handle_t *lping = DAP_NEW_Z(ping_handle_t); + lping->source.sin_family = AF_INET; + lping->pmtudisc = -1; + + lping->ping_common.interval = 1000; // interval between packets (msec) + lping->ping_common.preload = 1; + lping->ping_common.lingertime = MAXWAIT * 1000; + + + lping->ping_common.confirm_flag = MSG_CONFIRM; + + lping->ping_common.tmin = LONG_MAX; // minimum round trip time + lping->ping_common.pipesize = -1; + + lping->ping_common.datalen = DEFDATALEN; + + lping->ping_common.screen_width = INT_MAX; + +#ifdef HAVE_LIBCAP + lping->ping_common.cap_raw = CAP_NET_RAW; + lping->ping_common.cap_admin = CAP_NET_ADMIN; +#endif + + return lping; +} + +static void create_socket(ping_handle_t *a_ping_handle, socket_st *sock, int family, int socktype, int protocol, int requisite) +{ + int do_fallback = 0; + + errno = 0; + + assert(sock->fd == -1); + assert(socktype == SOCK_DGRAM || socktype == SOCK_RAW); + + /* Attempt to create a ping socket if requested. Attempt to create a raw + * socket otherwise or as a fallback. Well known errno values follow. + * + * 1) EACCES + * + * Kernel returns EACCES for all ping socket creation attempts when the + * user isn't allowed to use ping socket. A range of group ids is + * configured using the `net.ipv4.ping_group_range` sysctl. Fallback + * to raw socket is necessary. + * + * Kernel returns EACCES for all raw socket creation attempts when the + * process doesn't have the `CAP_NET_RAW` capability. + * + * 2) EAFNOSUPPORT + * + * Kernel returns EAFNOSUPPORT for IPv6 ping or raw socket creation + * attempts when run with IPv6 support disabled (e.g. via `ipv6.disable=1` + * kernel command-line option. + * + * https://github.com/iputils/iputils/issues/32 + * + * OpenVZ 2.6.32-042stab113.11 and possibly other older kernels return + * EAFNOSUPPORT for all IPv4 ping socket creation attempts due to lack + * of support in the kernel. Fallback to raw socket is necessary. + * + * https://github.com/iputils/iputils/issues/54 + * + * 3) EPROTONOSUPPORT + * + * OpenVZ 2.6.32-042stab113.11 and possibly other older kernels return + * EPROTONOSUPPORT for all IPv6 ping socket creation attempts due to lack + * of support in the kernel [1]. Debian 9.5 based container with kernel 4.10 + * returns EPROTONOSUPPORT also for IPv4 [2]. Fallback to raw socket is + * necessary. + * + * [1] https://github.com/iputils/iputils/issues/54 + * [2] https://github.com/iputils/iputils/issues/129 + */ + if(socktype == SOCK_DGRAM) + sock->fd = socket(family, socktype, protocol); + + /* Kernel doesn't support ping sockets. */ + if(sock->fd == -1 && errno == EAFNOSUPPORT && family == AF_INET) + do_fallback = 1; + if(sock->fd == -1 && errno == EPROTONOSUPPORT) + do_fallback = 1; + + /* User is not allowed to use ping sockets. */ + if(sock->fd == -1 && errno == EACCES) + do_fallback = 1; + + if(socktype == SOCK_RAW || do_fallback) { + socktype = SOCK_RAW; + sock->fd = socket(family, SOCK_RAW, protocol); + } + + if(sock->fd == -1) { + /* Report error related to disabled IPv6 only when IPv6 also failed or in + * verbose mode. Report other errors always. + */ + if((errno == EAFNOSUPPORT && socktype == AF_INET6) || (a_ping_handle->ping_common.options & F_VERBOSE) || requisite) + error(0, errno, "socket"); + if(requisite) + exit(2); + } else + sock->socktype = socktype; +} + +static void set_socket_option(socket_st *sock, int level, int optname, const void *optval, socklen_t olen) +{ + if(sock->fd == -1) + return; + + if(setsockopt(sock->fd, level, optname, optval, olen) == -1) + error(2, errno, "setsockopt"); +} + +/* Much like stdtod(3, but will fails if str is not valid number. */ +static double ping_strtod(const char *str, const char *err_msg) +{ + double num; + char *end = NULL; + + if(str == NULL || *str == '\0') + goto err; + errno = 0; +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + num = strtod(str, &end); +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + if(errno || str == end || (end && *end)) + goto err; + switch (fpclassify(num)) { + case FP_NORMAL: + case FP_ZERO: + break; + default: + errno = ERANGE; + goto err; + } + return num; + err: + error(2, errno, "%s: %s", err_msg, str); + abort(); /* cannot be reached, above error() will exit */ + return 0.0; +} + +static int ping_main(ping_handle_t *a_ping_handle, int argc, char **argv) +{ + struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_protocol = IPPROTO_UDP, .ai_socktype = SOCK_DGRAM, .ai_flags = + getaddrinfo_flags }; + struct addrinfo *result, *ai; + int status; + int ch; + socket_st sock4 = { .fd = -1 }; + socket_st sock6 = { .fd = -1 }; + char *target; + + limit_capabilities(a_ping_handle); + +#ifdef USE_IDN + setlocale(LC_ALL, ""); + if (!strcmp(setlocale(LC_ALL, NULL), "C")) + hints.ai_flags &= ~ AI_CANONIDN; +#endif + + /* Support being called using `ping4` or `ping6` symlinks */ + if(argv[0][strlen(argv[0]) - 1] == '4') + hints.ai_family = AF_INET; + else if(argv[0][strlen(argv[0]) - 1] == '6') + hints.ai_family = AF_INET6; + + /* Parse command line options */ + while((ch = getopt(argc, argv, "h?" "4bRT:" "6F:N:" "aABc:dDfi:I:l:Lm:M:nOp:qQ:rs:S:t:UvVw:W:")) != EOF) { + switch (ch) { + /* IPv4 specific options */ + case '4': + if(hints.ai_family != AF_UNSPEC) + error(2, 0, "only one -4 or -6 option may be specified"); + hints.ai_family = AF_INET; + break; + case 'b': + a_ping_handle->broadcast_pings = 1; + break; + case 'R': + if(a_ping_handle->ping_common.options & F_TIMESTAMP) + error(2, 0, "only one of -T or -R may be used"); + a_ping_handle->ping_common.options |= F_RROUTE; + break; + case 'T': + if(a_ping_handle->ping_common.options & F_RROUTE) + error(2, 0, "only one of -T or -R may be used"); + a_ping_handle->ping_common.options |= F_TIMESTAMP; + if(strcmp(optarg, "tsonly") == 0) + a_ping_handle->ts_type = IPOPT_TS_TSONLY; + else if(strcmp(optarg, "tsandaddr") == 0) + a_ping_handle->ts_type = IPOPT_TS_TSANDADDR; + else if(strcmp(optarg, "tsprespec") == 0) + a_ping_handle->ts_type = IPOPT_TS_PRESPEC; + else + error(2, 0, "invalid timestamp type: %s", optarg); + break; + /* IPv6 specific options */ + case '6': + if(hints.ai_family != AF_UNSPEC) + error(2, 0, "only one -4 or -6 option may be specified"); + hints.ai_family = AF_INET6; + break; + case 'F': + flowlabel = parseflow(optarg); + a_ping_handle->ping_common.options |= F_FLOWINFO; + break; + case 'N': + if(niquery_option_handler(optarg) < 0) + usage(); + hints.ai_socktype = SOCK_RAW; + break; + /* Common options */ + case 'a': + a_ping_handle->ping_common.options |= F_AUDIBLE; + break; + case 'A': + a_ping_handle->ping_common.options |= F_ADAPTIVE; + break; + case 'B': + a_ping_handle->ping_common.options |= F_STRICTSOURCE; + break; + case 'c': + a_ping_handle->ping_common.npackets = atoi(optarg); + if(a_ping_handle->ping_common.npackets <= 0) + error(2, 0, "bad number of packets to transmit: %ld", a_ping_handle->ping_common.npackets); + break; + case 'd': + a_ping_handle->ping_common.options |= F_SO_DEBUG; + break; + case 'D': + a_ping_handle->ping_common.options |= F_PTIMEOFDAY; + break; + case 'i': + { + double optval; + + optval = ping_strtod(optarg, "bad timing interval"); + if(isgreater(optval, (double)(INT_MAX / 1000))) + error(2, 0, "bad timing interval: %s", optarg); + a_ping_handle->ping_common.interval = (int) (optval * 1000); + a_ping_handle->ping_common.options |= F_INTERVAL; + } + break; + case 'I': + /* IPv6 */ + if(strchr(optarg, ':')) { + char *p, *addr = strdup(optarg); + + if(!addr) + error(2, errno, "cannot copy: %s", optarg); + + p = strchr(addr, SCOPE_DELIMITER); + if(p) { + *p = '\0'; + a_ping_handle->device = optarg + (p - addr) + 1; + } + + if(inet_pton(AF_INET6, addr, (char*) &source6.sin6_addr) <= 0) + error(2, 0, "invalid source address: %s", optarg); + + a_ping_handle->ping_common.options |= F_STRICTSOURCE; + + free(addr); + } else if(inet_pton(AF_INET, optarg, &a_ping_handle->source.sin_addr) > 0) { + a_ping_handle->ping_common.options |= F_STRICTSOURCE; + } else { + a_ping_handle->device = optarg; + } + break; + case 'l': + a_ping_handle->ping_common.preload = atoi(optarg); + if(a_ping_handle->ping_common.preload <= 0) + error(2, 0, "bad preload value: %s, should be 1..%d", optarg, MAX_DUP_CHK); + if(a_ping_handle->ping_common.preload > MAX_DUP_CHK) + a_ping_handle->ping_common.preload = MAX_DUP_CHK; + if(a_ping_handle->ping_common.uid && a_ping_handle->ping_common.preload > 3) + error(2, 0, "cannot set preload to value greater than 3: %d", a_ping_handle->ping_common.preload); + break; + case 'L': + a_ping_handle->ping_common.options |= F_NOLOOP; + break; + case 'm': + { + char *endp; + a_ping_handle->ping_common.mark = (int) strtoul(optarg, &endp, 10); + if(a_ping_handle->ping_common.mark < 0 || *endp != '\0') + error(2, 0, "mark cannot be negative: %s", optarg); + a_ping_handle->ping_common.options |= F_MARK; + break; + } + case 'M': + if(strcmp(optarg, "do") == 0) + a_ping_handle->pmtudisc = IP_PMTUDISC_DO; + else if(strcmp(optarg, "dont") == 0) + a_ping_handle->pmtudisc = IP_PMTUDISC_DONT; + else if(strcmp(optarg, "want") == 0) + a_ping_handle->pmtudisc = IP_PMTUDISC_WANT; + else + error(2, 0, "invalid -M argument: %s", optarg); + break; + case 'n': + a_ping_handle->ping_common.options |= F_NUMERIC; + break; + case 'O': + a_ping_handle->ping_common.options |= F_OUTSTANDING; + break; + case 'f': + /* avoid `getaddrinfo()` during flood */ + a_ping_handle->ping_common.options |= F_FLOOD | F_NUMERIC; + setbuf(stdout, (char *) NULL); + break; + case 'p': + a_ping_handle->ping_common.options |= F_PINGFILLED; + fill(a_ping_handle, optarg, a_ping_handle->ping_common.outpack, sizeof(a_ping_handle->ping_common.outpack)); + break; + case 'q': + a_ping_handle->ping_common.options |= F_QUIET; + break; + case 'Q': + a_ping_handle->settos = parsetos(optarg); /* IPv4 */ + tclass = a_ping_handle->settos; /* IPv6 */ + break; + case 'r': + a_ping_handle->ping_common.options |= F_SO_DONTROUTE; + break; + case 's': + a_ping_handle->ping_common.datalen = atoi(optarg); + if(a_ping_handle->ping_common.datalen < 0) + error(2, 0, "illegal packet size: %d", a_ping_handle->ping_common.datalen); + if(a_ping_handle->ping_common.datalen > MAXPACKET - 8) + error(2, 0, "packet size too large: %d", a_ping_handle->ping_common.datalen); + break; + case 'S': + a_ping_handle->ping_common.sndbuf = atoi(optarg); + if(a_ping_handle->ping_common.sndbuf <= 0) + error(2, 0, "bad sndbuf value: %s", optarg); + break; + case 't': + a_ping_handle->ping_common.options |= F_TTL; + a_ping_handle->ping_common.ttl = atoi(optarg); + if(a_ping_handle->ping_common.ttl < 0 || a_ping_handle->ping_common.ttl > 255) + error(2, 0, "ttl out of range: %s", optarg); + break; + case 'U': + a_ping_handle->ping_common.options |= F_LATENCY; + break; + case 'v': + a_ping_handle->ping_common.options |= F_VERBOSE; + break; + case 'V': + printf(IPUTILS_VERSION("ping")); + exit(0); + case 'w': + a_ping_handle->ping_common.deadline = atoi(optarg); + if(a_ping_handle->ping_common.deadline < 0) + error(2, 0, "bad wait time: %s", optarg); + break; + case 'W': + { + double optval; + + optval = ping_strtod(optarg, "bad linger time"); + if(isless(optval, 0.001) || isgreater(optval, (double)(INT_MAX / 1000))) + error(2, 0, "bad linger time: %s", optarg); + /* lingertime will be converted to usec later */ + a_ping_handle->ping_common.lingertime = (int) (optval * 1000); + } + break; + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if(!argc) + { + //error(1, EDESTADDRREQ, "usage error"); + return -EDESTADDRREQ;// 89 Destination address required + } + + target = argv[argc - 1]; + + /* Create sockets */ + enable_capability_raw(a_ping_handle); + if(hints.ai_family != AF_INET6) + create_socket(a_ping_handle, &sock4, AF_INET, hints.ai_socktype, IPPROTO_ICMP, hints.ai_family == AF_INET); + if(hints.ai_family != AF_INET) { + create_socket(a_ping_handle, &sock6, AF_INET6, hints.ai_socktype, IPPROTO_ICMPV6, sock4.fd == -1); + /* This may not be needed if both protocol versions always had the same value, but + * since I don't know that, it's better to be safe than sorry. */ + a_ping_handle->pmtudisc = a_ping_handle->pmtudisc == IP_PMTUDISC_DO ? IPV6_PMTUDISC_DO : + a_ping_handle->pmtudisc == IP_PMTUDISC_DONT ? IPV6_PMTUDISC_DONT : + a_ping_handle->pmtudisc == IP_PMTUDISC_WANT ? IPV6_PMTUDISC_WANT : a_ping_handle->pmtudisc; + } + disable_capability_raw(a_ping_handle); + + /* Limit address family on single-protocol systems */ + if(hints.ai_family == AF_UNSPEC) { + if(sock4.fd == -1) + hints.ai_family = AF_INET6; + else if(sock6.fd == -1) + hints.ai_family = AF_INET; + } + + /* Set socket options */ + if(a_ping_handle->settos) + set_socket_option(&sock4, IPPROTO_IP, IP_TOS, &a_ping_handle->settos, sizeof (a_ping_handle->settos)); + if(tclass) + set_socket_option(&sock6, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof tclass); + + status = getaddrinfo(target, NULL, &hints, &result); + if(status) + { + //error(2, 0, "%s: %s", target, gai_strerror(status)); + return -EADDRNOTAVAIL;// + } + + for(ai = result; ai; ai = ai->ai_next) { + switch (ai->ai_family) { + case AF_INET: + status = ping4_run(a_ping_handle, argc, argv, ai, &sock4); + break; + case AF_INET6: + status = ping6_run(a_ping_handle, argc, argv, ai, &sock6); + break; + default: + { + //error(2, 0, "unknown protocol family: %d", ai->ai_family); + return -EPFNOSUPPORT; + } + } + + if(status == 0) + break; + } + + freeaddrinfo(result); + + return status; +} + +/** + * Send ping + * + * @type for ipv4=4, for ipv6=6 + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util_common(ping_handle_t *a_ping_handle, int type, const char *addr, int count) +{ + + /* + rights for /bin/ping: -rwsr-xr-x 1 root root + current parametr: + # sysctl net.ipv4.ping_group_range + net.ipv4.ping_group_range = 1 0 + Need change range for other users: + # sysctl net.ipv4.ping_group_range="1 65000" + */ + a_ping_handle->ping_common.tsum = a_ping_handle->ping_common.ntransmitted = a_ping_handle->ping_common.nreceived = exiting = 0; + int argc = 3; + const char *argv[argc]; + if(type != 4) + argv[0] = "ping6"; + else + argv[0] = "ping4"; + argv[1] = dap_strdup_printf("-c%d", count); + argv[2] = addr; + int status = ping_main(a_ping_handle, argc, (char**) argv); + DAP_DELETE((char*) argv[1]); + if(a_ping_handle->ping_common.ntransmitted >= 1 && a_ping_handle->ping_common.nreceived >= 1) + return a_ping_handle->ping_common.tsum; + return status; +} + +/** + * Send ping for ipv4 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util(ping_handle_t *a_ping_handle, const char *addr, int count) +{ + return ping_util_common(a_ping_handle, 4, addr, count); +} + +/** + * Send ping for ipv6 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util6(ping_handle_t *a_ping_handle, const char *addr, int count) +{ + return ping_util_common(a_ping_handle, 6, addr, count); +} + +int ping4_run(ping_handle_t *a_ping_handle, int argc, char **argv, struct addrinfo *ai, socket_st *sock) +{ + static const struct addrinfo hints = { .ai_family = AF_INET, .ai_protocol = IPPROTO_UDP, .ai_flags = + getaddrinfo_flags }; + int hold, packlen; + unsigned char *packet; + char *target; + char hnamebuf[NI_MAXHOST]; + unsigned char rspace[3 + 4 * NROUTES + 1]; /* record route space */ + uint32_t *tmp_rspace; + + if(argc > 1) { + if(a_ping_handle->ping_common.options & F_RROUTE) + usage(); + else if(a_ping_handle->ping_common.options & F_TIMESTAMP) { + if(a_ping_handle->ts_type != IPOPT_TS_PRESPEC) + usage(); + if(argc > 5) + usage(); + } else { + if(argc > 10) + usage(); + a_ping_handle->ping_common.options |= F_SOURCEROUTE; + } + } + while(argc > 0) { + target = *argv; + + memset((char *) &a_ping_handle->whereto, 0, sizeof(a_ping_handle->whereto)); + a_ping_handle->whereto.sin_family = AF_INET; + if(inet_aton(target, &a_ping_handle->whereto.sin_addr) == 1) { + a_ping_handle->ping_common.hostname = target; + if(argc == 1) + a_ping_handle->ping_common.options |= F_NUMERIC; + } else { + struct addrinfo *result = NULL; + int status; + + if(argc > 1 || !ai) { + status = getaddrinfo(target, NULL, &hints, &result); + if(status) + error(2, 0, "%s: %s", target, gai_strerror(status)); + ai = result; + } + + memcpy(&a_ping_handle->whereto, ai->ai_addr, sizeof (a_ping_handle->whereto)); + memset(hnamebuf, 0, sizeof hnamebuf); + if(ai->ai_canonname) + strncpy(hnamebuf, ai->ai_canonname, sizeof hnamebuf - 1); + a_ping_handle->ping_common.hostname = hnamebuf; + + if(result) + freeaddrinfo(result); + } + if(argc > 1) + a_ping_handle->route[a_ping_handle->nroute++] = a_ping_handle->whereto.sin_addr.s_addr; + argc--; + argv++; + } + + if(a_ping_handle->source.sin_addr.s_addr == 0) { + socklen_t alen; + struct sockaddr_in dst = a_ping_handle->whereto; + int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if(probe_fd < 0) + error(2, errno, "socket"); + if(a_ping_handle->device) { + struct ifreq ifr; + int i; + int fds[2] = { probe_fd, sock->fd }; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, a_ping_handle->device, IFNAMSIZ - 1); + + for(i = 0; i < 2; i++) { + int fd = fds[i]; + int rc; + int errno_save; + + enable_capability_raw(a_ping_handle); + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, a_ping_handle->device, strlen(a_ping_handle->device) + 1); + errno_save = errno; + disable_capability_raw(a_ping_handle); + + if(rc == -1) { + if(IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) { + struct ip_mreqn imr; + if(ioctl(fd, SIOCGIFINDEX, &ifr) < 0) + error(2, 0, "unknown iface: %s", a_ping_handle->device); + memset(&imr, 0, sizeof(imr)); + imr.imr_ifindex = ifr.ifr_ifindex; + if(setsockopt(fd, SOL_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) + error(2, errno, "IP_MULTICAST_IF"); + } else + error(2, errno_save, "SO_BINDTODEVICE %s", a_ping_handle->device); + } + } + } + + if(a_ping_handle->settos && + setsockopt(probe_fd, IPPROTO_IP, IP_TOS, (char *) &a_ping_handle->settos, sizeof(int)) < 0) + error(0, errno, "warning: QOS sockopts"); + + dst.sin_port = htons(1025); + if(a_ping_handle->nroute) + dst.sin_addr.s_addr = a_ping_handle->route[0]; + if(connect(probe_fd, (struct sockaddr*) &dst, sizeof(dst)) == -1) { + if(errno == EACCES) { + if(a_ping_handle->broadcast_pings == 0) + error(2, 0, + "Do you want to ping broadcast? Then -b. If not, check your local firewall rules"); + fprintf(stderr, "WARNING: pinging broadcast address\n"); + if(setsockopt(probe_fd, SOL_SOCKET, SO_BROADCAST, + &a_ping_handle->broadcast_pings, sizeof(a_ping_handle->broadcast_pings)) < 0) + error(2, errno, "cannot set broadcasting"); + if(connect(probe_fd, (struct sockaddr*) &dst, sizeof(dst)) == -1) + error(2, errno, "connect"); + } else + error(2, errno, "connect"); + } + alen = sizeof(a_ping_handle->source); + if(getsockname(probe_fd, (struct sockaddr*) &a_ping_handle->source, &alen) == -1) + error(2, errno, "getsockname"); + a_ping_handle->source.sin_port = 0; + + if(a_ping_handle->device) { + struct ifaddrs *ifa0, *ifa; + int ret; + + ret = getifaddrs(&ifa0); + if(ret) + error(2, errno, "gatifaddrs failed"); + for(ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if(!ifa->ifa_name || !ifa->ifa_addr || + ifa->ifa_addr->sa_family != AF_INET) + continue; + if(!strcmp(ifa->ifa_name, a_ping_handle->device) && + !memcmp(&((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, + &a_ping_handle->source.sin_addr, sizeof(a_ping_handle->source.sin_addr))) + break; + } + if(ifa && !memcmp(&((struct sockaddr_in *) ifa->ifa_addr)->sin_addr, + &dst.sin_addr, sizeof(a_ping_handle->source.sin_addr))) { + enable_capability_raw(a_ping_handle); + setsockopt(sock->fd, SOL_SOCKET, SO_BINDTODEVICE, "", 0); + disable_capability_raw(a_ping_handle); + } + freeifaddrs(ifa0); + if(!ifa) + error(0, 0, "Warning: source address might be selected on device other than: %s", a_ping_handle->device); + } + close(probe_fd); + } + while(0) + ; + + if(a_ping_handle->whereto.sin_addr.s_addr == 0) + a_ping_handle->whereto.sin_addr.s_addr = a_ping_handle->source.sin_addr.s_addr; + + if(a_ping_handle->device) { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, a_ping_handle->device, IFNAMSIZ - 1); + if(ioctl(sock->fd, SIOCGIFINDEX, &ifr) < 0) + error(2, 0, "unknown iface: %s", a_ping_handle->device); + } + + if(a_ping_handle->broadcast_pings || IN_MULTICAST(ntohl(a_ping_handle->whereto.sin_addr.s_addr))) { + if(a_ping_handle->ping_common.uid) { + if(a_ping_handle->ping_common.interval < 1000) + error(2, 0, "broadcast ping with too short interval: %d", a_ping_handle->ping_common.interval); + if(a_ping_handle->pmtudisc >= 0 && a_ping_handle->pmtudisc != IP_PMTUDISC_DO) + error(2, 0, "broadcast ping does not fragment"); + } + if(a_ping_handle->pmtudisc < 0) + a_ping_handle->pmtudisc = IP_PMTUDISC_DO; + } + + if(a_ping_handle->pmtudisc >= 0) { + if(setsockopt(sock->fd, SOL_IP, IP_MTU_DISCOVER, &a_ping_handle->pmtudisc, sizeof (a_ping_handle->pmtudisc)) == -1) + error(2, errno, "IP_MTU_DISCOVER"); + } + + if((a_ping_handle->ping_common.options & F_STRICTSOURCE) && + bind(sock->fd, (struct sockaddr *) &a_ping_handle->source, sizeof (a_ping_handle->source)) == -1) + error(2, errno, "bind"); + + if(sock->socktype == SOCK_RAW) { + struct icmp_filter filt; + filt.data = ~((1 << ICMP_SOURCE_QUENCH) | + (1 << ICMP_DEST_UNREACH) | + (1 << ICMP_TIME_EXCEEDED) | + (1 << ICMP_PARAMETERPROB) | + (1 << ICMP_REDIRECT) | + (1 << ICMP_ECHOREPLY)); + if(setsockopt(sock->fd, SOL_RAW, ICMP_FILTER, &filt, sizeof filt) == -1) + error(0, errno, "WARNING: setsockopt(ICMP_FILTER)"); + } + + hold = 1; + if(setsockopt(sock->fd, SOL_IP, IP_RECVERR, &hold, sizeof hold)) + error(0, 0, "WARNING: your kernel is veeery old. No problems."); + + if(sock->socktype == SOCK_DGRAM) { + if(setsockopt(sock->fd, SOL_IP, IP_RECVTTL, &hold, sizeof hold)) + error(0, errno, "WARNING: setsockopt(IP_RECVTTL)"); + if(setsockopt(sock->fd, SOL_IP, IP_RETOPTS, &hold, sizeof hold)) + error(0, errno, "WARNING: setsockopt(IP_RETOPTS)"); + } + + /* record route option */ + if(a_ping_handle->ping_common.options & F_RROUTE) { + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_NOP; + rspace[1 + IPOPT_OPTVAL] = IPOPT_RR; + rspace[1 + IPOPT_OLEN] = sizeof(rspace) - 1; + rspace[1 + IPOPT_OFFSET] = IPOPT_MINOFF; + a_ping_handle->optlen = 40; + if(setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, sizeof rspace) < 0) + error(2, errno, "record route"); + } + if(a_ping_handle->ping_common.options & F_TIMESTAMP) { + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_TIMESTAMP; + rspace[1] = (a_ping_handle->ts_type == IPOPT_TS_TSONLY ? 40 : 36); + rspace[2] = 5; + rspace[3] = a_ping_handle->ts_type; + if(a_ping_handle->ts_type == IPOPT_TS_PRESPEC) { + int i; + rspace[1] = 4 + a_ping_handle->nroute * 8; + for(i = 0; i < a_ping_handle->nroute; i++) { + tmp_rspace = (uint32_t*) &rspace[4 + i * 8]; + *tmp_rspace = a_ping_handle->route[i]; + } + } + if(setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) { + rspace[3] = 2; + if(setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) + error(2, errno, "ts option"); + } + a_ping_handle->optlen = 40; + } + if(a_ping_handle->ping_common.options & F_SOURCEROUTE) { + int i; + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_NOOP; + rspace[1 + IPOPT_OPTVAL] = (a_ping_handle->ping_common.options & F_SO_DONTROUTE) ? IPOPT_SSRR + : + IPOPT_LSRR; + rspace[1 + IPOPT_OLEN] = 3 + a_ping_handle->nroute * 4; + rspace[1 + IPOPT_OFFSET] = IPOPT_MINOFF; + for(i = 0; i < a_ping_handle->nroute; i++) { + tmp_rspace = (uint32_t*) &rspace[4 + i * 4]; + *tmp_rspace = a_ping_handle->route[i]; + } + + if(setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, 4 + a_ping_handle->nroute * 4) < 0) + error(2, errno, "record route"); + a_ping_handle->optlen = 40; + } + + /* Estimate memory eaten by single packet. It is rough estimate. + * Actually, for small datalen's it depends on kernel side a lot. */ + hold = a_ping_handle->ping_common.datalen + 8; + hold += ((hold + 511) / 512) * (a_ping_handle->optlen + 20 + 16 + 64 + 160); + sock_setbufs(a_ping_handle, sock, hold); + + if(a_ping_handle->broadcast_pings) { + if(setsockopt(sock->fd, SOL_SOCKET, SO_BROADCAST, &a_ping_handle->broadcast_pings, sizeof (a_ping_handle->broadcast_pings)) < 0) + error(2, errno, "cannot set broadcasting"); + } + + if(a_ping_handle->ping_common.options & F_NOLOOP) { + int loop = 0; + if(setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof loop) == -1) + error(2, errno, "cannot disable multicast loopback"); + } + if(a_ping_handle->ping_common.options & F_TTL) { + int ittl = a_ping_handle->ping_common.ttl; + if(setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_TTL, &a_ping_handle->ping_common.ttl, sizeof (a_ping_handle->ping_common.ttl)) == -1) + error(2, errno, "cannot set multicast time-to-live"); + if(setsockopt(sock->fd, IPPROTO_IP, IP_TTL, &ittl, sizeof ittl) == -1) + error(2, errno, "cannot set unicast time-to-live"); + } + + if(a_ping_handle->ping_common.datalen > 0xFFFF - 8 - a_ping_handle->optlen - 20) + error(2, 0, "packet size %d is too large. Maximum is %d", + a_ping_handle->ping_common.datalen, 0xFFFF - 8 - 20 - a_ping_handle->optlen); + + if(a_ping_handle->ping_common.datalen >= (int) sizeof(struct timeval)) /* can we time transfer */ + a_ping_handle->ping_common.timing = 1; + packlen = a_ping_handle->ping_common.datalen + MAXIPLEN + MAXICMPLEN; + if(!(packet = (unsigned char *) malloc((unsigned int) packlen))) + error(2, errno, "memory allocation failed"); + +//printf("PING %s (%s) ", hostname, inet_ntoa(whereto.sin_addr)); + if(a_ping_handle->device || (a_ping_handle->ping_common.options & F_STRICTSOURCE)) + printf("from %s %s: ", inet_ntoa(a_ping_handle->source.sin_addr), a_ping_handle->device ? a_ping_handle->device : ""); +//printf("%d(%d) bytes of data.\n", datalen, datalen + 8 + optlen + 20); + + setup(a_ping_handle, sock); + log_printf("main_loop start %s (%s)\n", a_ping_handle->ping_common.hostname, inet_ntoa(a_ping_handle->whereto.sin_addr)); + main_loop(a_ping_handle, &ping4_func_set, sock, packet, packlen); + log_printf("main_loop end\n"); + return 0; +} + +int ping4_receive_error_msg(ping_handle_t *a_ping_handle, socket_st *sock) +{ + ssize_t res; + char cbuf[512]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsgh; + struct sock_extended_err *e; + struct icmphdr icmph; + struct sockaddr_in target; + int net_errors = 0; + int local_errors = 0; + int saved_errno = errno; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void*) ⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + if(!sock) + return net_errors; + res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if(res < 0) + goto out; + + e = NULL; + for(cmsgh = CMSG_FIRSTHDR(&msg); cmsgh; cmsgh = CMSG_NXTHDR(&msg, cmsgh)) { + if(cmsgh->cmsg_level == SOL_IP) { + if(cmsgh->cmsg_type == IP_RECVERR) + e = (struct sock_extended_err *) CMSG_DATA(cmsgh); + } + } + if(e == NULL) + abort(); + + if(e->ee_origin == SO_EE_ORIGIN_LOCAL) { + local_errors++; + if(a_ping_handle->ping_common.options & F_QUIET) + goto out; + if(a_ping_handle->ping_common.options & F_FLOOD) + write_stdout("E", 1); + else if(e->ee_errno != EMSGSIZE) + error(0, 0, "local error: %s", strerror(e->ee_errno)); + else + error(0, 0, "local error: message too long, mtu=%u", e->ee_info); + a_ping_handle->ping_common.nerrors++; + } else if(e->ee_origin == SO_EE_ORIGIN_ICMP) { + struct sockaddr_in *sin = (struct sockaddr_in*) (e + 1); + + if(res < (ssize_t) sizeof(icmph) || + target.sin_addr.s_addr != a_ping_handle->whereto.sin_addr.s_addr || + icmph.type != ICMP_ECHO || + !is_ours(a_ping_handle, sock, icmph.un.echo.id)) { + /* Not our error, not an error at all. Clear. */ + saved_errno = 0; + goto out; + } + + acknowledge(a_ping_handle, ntohs(icmph.un.echo.sequence)); + + net_errors++; + a_ping_handle->ping_common.nerrors++; + if(a_ping_handle->ping_common.options & F_QUIET) + goto out; + if(a_ping_handle->ping_common.options & F_FLOOD) { + write_stdout("\bE", 2); + } else { + print_timestamp(a_ping_handle); + printf("From %s icmp_seq=%u ", pr_addr(a_ping_handle, sin, sizeof *sin), ntohs(icmph.un.echo.sequence)); + pr_icmph(a_ping_handle, e->ee_type, e->ee_code, e->ee_info, NULL); + fflush(stdout); + } + } + + out: + errno = saved_errno; + return net_errors ? net_errors : -local_errors; +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first several bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +int ping4_send_probe(ping_handle_t *a_ping_handle, socket_st *sock, void *packet, unsigned packet_size __attribute__((__unused__))) +{ + struct icmphdr *icp; + int cc; + int i; + + icp = (struct icmphdr *) packet; + icp->type = ICMP_ECHO; + icp->code = 0; + icp->checksum = 0; + icp->un.echo.sequence = htons(a_ping_handle->ping_common.ntransmitted + 1); + icp->un.echo.id = a_ping_handle->ping_common.ident; /* ID */ + + rcvd_clear(a_ping_handle, a_ping_handle->ping_common.ntransmitted + 1); + + if(a_ping_handle->ping_common.timing) { + if(a_ping_handle->ping_common.options & F_LATENCY) { + struct timeval tmp_tv; + gettimeofday(&tmp_tv, NULL); + memcpy(icp + 1, &tmp_tv, sizeof(tmp_tv)); + } else { + memset(icp + 1, 0, sizeof(struct timeval)); + } + } + + cc = a_ping_handle->ping_common.datalen + 8; /* skips ICMP portion */ + + /* compute ICMP checksum here */ + icp->checksum = in_cksum((unsigned short *) icp, cc, 0); + + if(a_ping_handle->ping_common.timing && !(a_ping_handle->ping_common.options & F_LATENCY)) { + struct timeval tmp_tv; + gettimeofday(&tmp_tv, NULL); + memcpy(icp + 1, &tmp_tv, sizeof(tmp_tv)); + icp->checksum = in_cksum((unsigned short *) &tmp_tv, sizeof(tmp_tv), ~icp->checksum); + } + + i = sendto(sock->fd, icp, cc, 0, (struct sockaddr*) &a_ping_handle->whereto, sizeof(a_ping_handle->whereto)); + //log_printf("**sendto(fd=%d,icp=0x%x,cc=%d)=%d\n",sock->fd,&icp,cc,i); + return (cc == i ? 0 : i); +} + +/* + * parse_reply -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +static +void pr_echo_reply(uint8_t *_icp, int len __attribute__((__unused__))) +{ + struct icmphdr *icp = (struct icmphdr *) _icp; + log_printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence)); +} + +int +ping4_parse_reply(ping_handle_t *a_ping_handle, struct socket_st *sock, struct msghdr *msg, int cc, void *addr, struct timeval *tv) +{ + struct sockaddr_in *from = addr; + uint8_t *buf = msg->msg_iov->iov_base; + struct icmphdr *icp; + struct iphdr *ip; + int hlen; + int csfailed; + struct cmsghdr *cmsgh; + int reply_ttl; + uint8_t *opts, *tmp_ttl; + int olen; + + /* Check the IP header */ + ip = (struct iphdr *) buf; + if(sock->socktype == SOCK_RAW) { + hlen = ip->ihl * 4; + if(cc < hlen + 8 || ip->ihl < 5) { + if(a_ping_handle->ping_common.options & F_VERBOSE) + error(0, 0, "packet too short (%d bytes) from %s", cc, + pr_addr(a_ping_handle, from, sizeof *from)); + return 1; + } + reply_ttl = ip->ttl; + opts = buf + sizeof(struct iphdr); + olen = hlen - sizeof(struct iphdr); + } else { + hlen = 0; + reply_ttl = 0; + opts = buf; + olen = 0; + for(cmsgh = CMSG_FIRSTHDR(msg); cmsgh; cmsgh = CMSG_NXTHDR(msg, cmsgh)) { + if(cmsgh->cmsg_level != SOL_IP) + continue; + if(cmsgh->cmsg_type == IP_TTL) { + if(cmsgh->cmsg_len < sizeof(int)) + continue; + tmp_ttl = (uint8_t *) CMSG_DATA(cmsgh); + reply_ttl = (int) *tmp_ttl; + } else if(cmsgh->cmsg_type == IP_RETOPTS) { + opts = (uint8_t *) CMSG_DATA(cmsgh); + olen = cmsgh->cmsg_len; + } + } + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmphdr *) (buf + hlen); + csfailed = in_cksum((unsigned short *) icp, cc, 0); + + if(icp->type == ICMP_ECHOREPLY) { + //log_printf("in ping4_parse_reply00\n"); + if(!is_ours(a_ping_handle, sock, icp->un.echo.id)) + return 1; /* 'Twas not our ECHO */ + if(!contains_pattern_in_payload(a_ping_handle, (uint8_t*) (icp + 1))) + return 1; /* 'Twas really not our ECHO */ + if(gather_statistics(a_ping_handle, (uint8_t*) icp, sizeof(*icp), cc, + ntohs(icp->un.echo.sequence), + reply_ttl, 0, tv, pr_addr(a_ping_handle, from, sizeof *from), + pr_echo_reply)) { + fflush(stdout); + return 0; + } + //log_printf("in ping4_parse_reply01\n"); + } else { + /* We fall here when a redirect or source quench arrived. */ + switch (icp->type) { + case ICMP_ECHO: + /* MUST NOT */ + return 1; + case ICMP_SOURCE_QUENCH: + case ICMP_REDIRECT: + case ICMP_DEST_UNREACH: + case ICMP_TIME_EXCEEDED: + case ICMP_PARAMETERPROB: + { + struct iphdr * iph = (struct iphdr *) (&icp[1]); + struct icmphdr *icp1 = (struct icmphdr*) ((unsigned char *) iph + iph->ihl * 4); + int error_pkt; + if(cc < (int) (8 + sizeof(struct iphdr) + 8) || + cc < 8 + iph->ihl * 4 + 8) + return 1; + if(icp1->type != ICMP_ECHO || + iph->daddr != a_ping_handle->whereto.sin_addr.s_addr || + !is_ours(a_ping_handle, sock, icp1->un.echo.id)) + return 1; + error_pkt = (icp->type != ICMP_REDIRECT && + icp->type != ICMP_SOURCE_QUENCH); + if(error_pkt) { + acknowledge(a_ping_handle, ntohs(icp1->un.echo.sequence)); + return 0; + } + if(a_ping_handle->ping_common.options & (F_QUIET | F_FLOOD)) + return 1; + print_timestamp(a_ping_handle); + log_printf("From %s: icmp_seq=%u ", + pr_addr(a_ping_handle, from, sizeof *from), + ntohs(icp1->un.echo.sequence)); + if(csfailed) + log_printf("(BAD CHECKSUM)"); + pr_icmph(a_ping_handle, icp->type, icp->code, ntohl(icp->un.gateway), icp); + return 1; + } + default: + /* MUST NOT */ + break; + } + if((a_ping_handle->ping_common.options & F_FLOOD) && !(a_ping_handle->ping_common.options & (F_VERBOSE | F_QUIET))) { + if(!csfailed) + write_stdout("!E", 2); + else + write_stdout("!EC", 3); + return 0; + } + if(!(a_ping_handle->ping_common.options & F_VERBOSE) || a_ping_handle->ping_common.uid) + return 0; + if(a_ping_handle->ping_common.options & F_PTIMEOFDAY) { + struct timeval recv_time; + gettimeofday(&recv_time, NULL); + log_printf("%lu.%06lu ", (unsigned long) recv_time.tv_sec, (unsigned long) recv_time.tv_usec); + } + printf("From %s: ", pr_addr(a_ping_handle, from, sizeof *from)); + if(csfailed) { + log_printf("(BAD CHECKSUM)\n"); + return 0; + } + pr_icmph(a_ping_handle, icp->type, icp->code, ntohl(icp->un.gateway), icp); + return 0; + } + + if(a_ping_handle->ping_common.options & F_AUDIBLE) { + log_printf("\a"); //putchar('\a'); + if(a_ping_handle->ping_common.options & F_FLOOD) + fflush(stdout); + } + if(!(a_ping_handle->ping_common.options & F_FLOOD)) { + pr_options(a_ping_handle, opts, olen + sizeof(struct iphdr)); + + log_printf("\n"); //putchar('\n'); + fflush(stdout); + } + return 0; +} + +#if BYTE_ORDER == LITTLE_ENDIAN +# define ODDBYTE(v) (v) +#elif BYTE_ORDER == BIG_ENDIAN +# define ODDBYTE(v) ((unsigned short)(v) << 8) +#else +# define ODDBYTE(v) htons((unsigned short)(v) << 8) +#endif + +unsigned short +in_cksum(const unsigned short *addr, int len, unsigned short csum) +{ + int nleft = len; + const unsigned short *w = addr; + unsigned short answer; + int sum = csum; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while(nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if(nleft == 1) + sum += ODDBYTE(*(unsigned char * )w); /* le16toh() may be unavailable on old systems */ + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +void pr_icmph(ping_handle_t *a_ping_handle, uint8_t type, uint8_t code, uint32_t info, struct icmphdr *icp) +{ + switch (type) { + case ICMP_ECHOREPLY: + printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_DEST_UNREACH: + switch (code) { + case ICMP_NET_UNREACH: + printf("Destination Net Unreachable\n"); + break; + case ICMP_HOST_UNREACH: + printf("Destination Host Unreachable\n"); + break; + case ICMP_PROT_UNREACH: + printf("Destination Protocol Unreachable\n"); + break; + case ICMP_PORT_UNREACH: + printf("Destination Port Unreachable\n"); + break; + case ICMP_FRAG_NEEDED: + printf("Frag needed and DF set (mtu = %u)\n", info); + break; + case ICMP_SR_FAILED: + printf("Source Route Failed\n"); + break; + case ICMP_NET_UNKNOWN: + printf("Destination Net Unknown\n"); + break; + case ICMP_HOST_UNKNOWN: + printf("Destination Host Unknown\n"); + break; + case ICMP_HOST_ISOLATED: + printf("Source Host Isolated\n"); + break; + case ICMP_NET_ANO: + printf("Destination Net Prohibited\n"); + break; + case ICMP_HOST_ANO: + printf("Destination Host Prohibited\n"); + break; + case ICMP_NET_UNR_TOS: + printf("Destination Net Unreachable for Type of Service\n"); + break; + case ICMP_HOST_UNR_TOS: + printf("Destination Host Unreachable for Type of Service\n"); + break; + case ICMP_PKT_FILTERED: + printf("Packet filtered\n"); + break; + case ICMP_PREC_VIOLATION: + printf("Precedence Violation\n"); + break; + case ICMP_PREC_CUTOFF: + printf("Precedence Cutoff\n"); + break; + default: + printf("Dest Unreachable, Bad Code: %d\n", code); + break; + } + if(icp && (a_ping_handle->ping_common.options & F_VERBOSE)) + pr_iph(a_ping_handle, (struct iphdr*) (icp + 1)); + break; + case ICMP_SOURCE_QUENCH: + printf("Source Quench\n"); + if(icp && (a_ping_handle->ping_common.options & F_VERBOSE)) + pr_iph(a_ping_handle, (struct iphdr*) (icp + 1)); + break; + case ICMP_REDIRECT: + switch (code) { + case ICMP_REDIR_NET: + printf("Redirect Network"); + break; + case ICMP_REDIR_HOST: + printf("Redirect Host"); + break; + case ICMP_REDIR_NETTOS: + printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIR_HOSTTOS: + printf("Redirect Type of Service and Host"); + break; + default: + printf("Redirect, Bad Code: %d", code); + break; + } + { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { icp ? icp->un.gateway : info } }; + + printf("(New nexthop: %s)\n", pr_addr(a_ping_handle, &sin, sizeof sin)); + } + if(icp && (a_ping_handle->ping_common.options & F_VERBOSE)) + pr_iph(a_ping_handle, (struct iphdr*) (icp + 1)); + break; + case ICMP_ECHO: + printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIME_EXCEEDED: + switch (code) { + case ICMP_EXC_TTL: + printf("Time to live exceeded\n"); + break; + case ICMP_EXC_FRAGTIME: + printf("Frag reassembly time exceeded\n"); + break; + default: + printf("Time exceeded, Bad Code: %d\n", code); + break; + } + if(icp && (a_ping_handle->ping_common.options & F_VERBOSE)) + pr_iph(a_ping_handle, (struct iphdr*) (icp + 1)); + break; + case ICMP_PARAMETERPROB: + printf("Parameter problem: pointer = %u\n", icp ? (ntohl(icp->un.gateway) >> 24) : info); + if(icp && (a_ping_handle->ping_common.options & F_VERBOSE)) + pr_iph(a_ping_handle, (struct iphdr*) (icp + 1)); + break; + case ICMP_TIMESTAMP: + printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TIMESTAMPREPLY: + printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_INFO_REQUEST: + printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_INFO_REPLY: + printf("Information Reply\n"); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + printf("Address Mask Request\n"); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + printf("Address Mask Reply\n"); + break; +#endif + default: + printf("Bad ICMP type: %d\n", type); + } +} + +void pr_options(ping_handle_t *a_ping_handle, unsigned char * cp, int hlen) +{ + int i, j; + int olen, totlen; + unsigned char * optptr; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + + totlen = hlen - sizeof(struct iphdr); + optptr = cp; + + while(totlen > 0) { + if(*optptr == IPOPT_EOL) + break; + if(*optptr == IPOPT_NOP) { + totlen--; + optptr++; + printf("\nNOP"); + continue; + } + cp = optptr; + olen = optptr[1]; + if(olen < 2 || olen > totlen) + break; + + switch (*cp) { + case IPOPT_SSRR: + case IPOPT_LSRR: + printf("\n%cSRR: ", *cp == IPOPT_SSRR ? 'S' : 'L'); + j = *++cp; + cp++; + if(j > IPOPT_MINOFF) { + for(;;) { + uint32_t address; + memcpy(&address, cp, 4); + cp += 4; + if(address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { address } }; + + printf("\t%s", pr_addr(a_ping_handle, &sin, sizeof sin)); + } + j -= 4; + putchar('\n'); + if(j <= IPOPT_MINOFF) + break; + } + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + if(i > j) + i = j; + i -= IPOPT_MINOFF; + if(i <= 0) + break; + if(i == old_rrlen + && !memcmp(cp, old_rr, i) + && !(a_ping_handle->ping_common.options & F_FLOOD)) { + printf("\t(same route)"); + break; + } + old_rrlen = i; + memcpy(old_rr, (char *) cp, i); + printf("\nRR: "); + cp++; + for(;;) { + uint32_t address; + memcpy(&address, cp, 4); + cp += 4; + if(address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { address } }; + + printf("\t%s", pr_addr(a_ping_handle, &sin, sizeof sin)); + } + i -= 4; + putchar('\n'); + if(i <= 0) + break; + } + break; + case IPOPT_TS: + { + int stdtime = 0, nonstdtime = 0; + uint8_t flags; + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + if(i > j) + i = j; + i -= 5; + if(i <= 0) + break; + flags = *++cp; + printf("\nTS: "); + cp++; + for(;;) { + long l; + + if((flags & 0xF) != IPOPT_TS_TSONLY) { + uint32_t address; + memcpy(&address, cp, 4); + cp += 4; + if(address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { address } }; + + printf("\t%s", pr_addr(a_ping_handle, &sin, sizeof sin)); + } + i -= 4; + if(i <= 0) + break; + } + l = *cp++; + l = (l << 8) + *cp++; + l = (l << 8) + *cp++; + l = (l << 8) + *cp++; + + if(l & 0x80000000) { + if(nonstdtime == 0) + printf("\t%ld absolute not-standard", l & 0x7fffffff); + else + printf("\t%ld not-standard", (l & 0x7fffffff) - nonstdtime); + nonstdtime = l & 0x7fffffff; + } else { + if(stdtime == 0) + printf("\t%ld absolute", l); + else + printf("\t%ld", l - stdtime); + stdtime = l; + } + i -= 4; + putchar('\n'); + if(i <= 0) + break; + } + if(flags >> 4) + printf("Unrecorded hops: %d\n", flags >> 4); + break; + } + default: + printf("\nunknown option %x", *cp); + break; + } + totlen -= olen; + optptr += olen; + } +} + +/* + * pr_iph -- + * Print an IP header with options. + */ +void pr_iph(ping_handle_t *a_ping_handle, struct iphdr *ip) +{ + int hlen; + unsigned char *cp; + + hlen = ip->ihl << 2; + cp = (unsigned char *) ip + 20; /* point to options */ + + printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); + printf(" %1x %1x %02x %04x %04x", + ip->version, ip->ihl, ip->tos, ip->tot_len, ip->id); + printf(" %1x %04x", ((ip->frag_off) & 0xe000) >> 13, + (ip->frag_off) & 0x1fff); + printf(" %02x %02x %04x", ip->ttl, ip->protocol, ip->check); + printf(" %s ", inet_ntoa(*(struct in_addr *) &ip->saddr)); + printf(" %s ", inet_ntoa(*(struct in_addr *) &ip->daddr)); + printf("\n"); + pr_options(a_ping_handle, cp, hlen); +} + +/* + * pr_addr -- + * + * Return an ascii host address optionally with a hostname. + */ +char * +pr_addr(ping_handle_t *a_ping_handle, void *sa, socklen_t salen) +{ + static char buffer[4096] = ""; + static struct sockaddr_storage last_sa = { 0, { 0 }, 0 }; + static socklen_t last_salen = 0; + char name[NI_MAXHOST] = ""; + char address[NI_MAXHOST] = ""; + + if(salen == last_salen && !memcmp(sa, &last_sa, salen)) + return buffer; + + memcpy(&last_sa, sa, (last_salen = salen)); + + a_ping_handle->ping_common.in_pr_addr = !setjmp(a_ping_handle->ping_common.pr_addr_jmp); + + getnameinfo(sa, salen, address, sizeof address, NULL, 0, getnameinfo_flags | NI_NUMERICHOST); + if(!exiting && !(a_ping_handle->ping_common.options & F_NUMERIC)) + getnameinfo(sa, salen, name, sizeof name, NULL, 0, getnameinfo_flags); + + if(*name) + snprintf(buffer, sizeof buffer, "%s (%s)", name, address); + else + snprintf(buffer, sizeof buffer, "%s", address); + + a_ping_handle->ping_common.in_pr_addr = 0; + + return (buffer); +} + +/* Set Type of Service (TOS) and other Quality of Service relating bits */ +int parsetos(char *str) +{ + const char *cp; + int tos; + char *ep; + + /* handle both hex and decimal values */ + if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + tos = (int) strtol(cp, &ep, 16); + } else + tos = (int) strtol(str, &ep, 10); + + /* doesn't look like decimal or hex, eh? */ + if(*ep != '\0') + error(2, 0, "bad TOS value: %s", str); + + if(tos > TOS_MAX) + error(2, 0, "the decimal value of TOS bits must be in range 0-255: %d", tos); + return (tos); +} + +int parseflow(char *str) +{ + const char *cp; + unsigned long val; + char *ep; + + /* handle both hex and decimal values */ + if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + val = (int) strtoul(cp, &ep, 16); + } else + val = (int) strtoul(str, &ep, 10); + + /* doesn't look like decimal or hex, eh? */ + if(*ep != '\0') + error(2, 0, "bad value for flowinfo: %s", str); + + if(val & ~IPV6_FLOWINFO_FLOWLABEL) + error(2, 0, "flow value is greater than 20 bits: %s", str); + return (val); +} + +void ping4_install_filter(ping_handle_t *a_ping_handle, socket_st *sock) +{ + static int once; + static struct sock_filter insns[] = { + BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* Skip IP header. F..g BSD... Look into ping6. */ + BPF_STMT(BPF_LD|BPF_H|BPF_IND, 4), /* Load icmp echo ident */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ + BPF_STMT(BPF_LD|BPF_B|BPF_IND, 0), /* Load icmp type */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */ + BPF_STMT(BPF_RET|BPF_K, 0xFFFFFFF), /* No. It passes. */ + BPF_STMT(BPF_RET|BPF_K, 0) /* Echo with wrong ident. Reject. */ + }; + static struct sock_fprog filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + if(once) + return; + once = 1; + + /* Patch bpflet for current identifier. */ + insns[2] = (struct sock_filter )BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(a_ping_handle->ping_common.ident), 0, 1); + + if(setsockopt(sock->fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) + error(0, errno, "WARNING: failed to install socket filter"); +} diff --git a/libdap-chain-net/iputils/ping.h b/libdap-chain-net/iputils/ping.h new file mode 100644 index 0000000000000000000000000000000000000000..651455289f18f42a956cd9cfc8cf61d4f059ecfa --- /dev/null +++ b/libdap-chain-net/iputils/ping.h @@ -0,0 +1,436 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <signal.h> +#include <poll.h> + +#include <sys/param.h> +#include <sys/socket.h> + +#define __USE_GNU + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> + +#include <linux/filter.h> +#include <linux/types.h> +#include <linux/sockios.h> + +#include <sys/file.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <sys/uio.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <setjmp.h> + +#include <asm/byteorder.h> +#include <sched.h> +#include <math.h> + +#include <resolv.h> + +#include "iputils.h" + +#ifdef HAVE_ERROR_H +#include <error.h> +#else +#include <stdarg.h> +#endif + +#ifdef HAVE_LIBCAP +#include <sys/prctl.h> +#include <sys/capability.h> +#endif + +#ifdef USE_IDN +#include <locale.h> +#include <idn2.h> + +#ifndef AI_IDN +#define AI_IDN 0x0040 +#endif +#ifndef AI_CANONIDN +#define AI_CANONIDN 0x0080 +#endif +#ifndef NI_IDN +#define NI_IDN 32 +#endif + +#define getaddrinfo_flags (AI_CANONNAME | AI_IDN | AI_CANONIDN) +#define getnameinfo_flags NI_IDN +#else +#define getaddrinfo_flags (AI_CANONNAME) +#define getnameinfo_flags 0 +#endif + +#include <ifaddrs.h> +#include <netinet/in.h> +#include <linux/ipv6.h> +#include <arpa/inet.h> +#include <linux/types.h> +#include <linux/errqueue.h> +#include <linux/in6.h> + +#ifndef SCOPE_DELIMITER +#define SCOPE_DELIMITER '%' +#endif + +#define DEFDATALEN (64 - 8) /* default data length */ + +#define MAXWAIT 10 /* max seconds to wait for response */ +#define MININTERVAL 10 /* Minimal interpacket gap */ +#define MINUSERINTERVAL 200 /* Minimal allowed interval for non-root */ + +#define SCHINT(a) (((a) <= MININTERVAL) ? MININTERVAL : (a)) + +/* various options */ +//extern int options; +#define F_FLOOD 0x001 +#define F_INTERVAL 0x002 +#define F_NUMERIC 0x004 +#define F_PINGFILLED 0x008 +#define F_QUIET 0x010 +#define F_RROUTE 0x020 +#define F_SO_DEBUG 0x040 +#define F_SO_DONTROUTE 0x080 +#define F_VERBOSE 0x100 +#define F_TIMESTAMP 0x200 +#define F_SOURCEROUTE 0x400 +#define F_FLOOD_POLL 0x800 +#define F_LATENCY 0x1000 +#define F_AUDIBLE 0x2000 +#define F_ADAPTIVE 0x4000 +#define F_STRICTSOURCE 0x8000 +#define F_NOLOOP 0x10000 +#define F_TTL 0x20000 +#define F_MARK 0x40000 +#define F_PTIMEOFDAY 0x80000 +#define F_OUTSTANDING 0x100000 +#define F_FLOWINFO 0x200000 +#define F_TCLASS 0x400000 + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. + */ + + +#if defined(__WORDSIZE) && __WORDSIZE == 64 +# define USE_BITMAP64 +#endif + + + +#if ((MAX_DUP_CHK >> (BITMAP_SHIFT + 3)) << (BITMAP_SHIFT + 3)) != MAX_DUP_CHK +# error Please MAX_DUP_CHK and/or BITMAP_SHIFT +#endif + + + +//extern struct rcvd_table rcvd_tbl; + +//#define A(a_ping_handle, bit) (a_ping_handle->ping_common.rcvd_tbl.bitmap[(bit) >> BITMAP_SHIFT]) /* identify word in array */ +#define B(bit) (((bitmap_t)1) << ((bit) & ((1 << BITMAP_SHIFT) - 1))) /* identify bit in word */ + +static inline void rcvd_set(ping_handle_t *a_ping_handle, uint16_t seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + a_ping_handle->ping_common.rcvd_tbl.bitmap[(bit) >> BITMAP_SHIFT] |= B(bit); + //A(a_ping_handle, bit) |= B(bit); +} + +static inline void rcvd_clear(ping_handle_t *a_ping_handle, uint16_t seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + a_ping_handle->ping_common.rcvd_tbl.bitmap[(bit) >> BITMAP_SHIFT] &= ~B(bit); + //A(a_ping_handle, bit) &= ~B(bit); +} + +static inline bitmap_t rcvd_test(ping_handle_t *a_ping_handle, uint16_t seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + return (a_ping_handle->ping_common.rcvd_tbl.bitmap[(bit) >> BITMAP_SHIFT] & B(bit)); + //return A(a_ping_handle, bit) & B(bit); +} + +#ifndef HAVE_ERROR_H +static void error(int status, int errnum, const char *format, ...) +{ + va_list ap; + + fprintf(stderr, "ping: "); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + if (errnum) + fprintf(stderr, ": %s\n", strerror(errnum)); + else + fprintf(stderr, "\n"); + if (status) + exit(status); +} +#endif + +/* +extern int datalen; +extern char *hostname; +extern int uid; +extern int ident; // process id to identify our packets + +extern int sndbuf; +extern int ttl; + +extern long npackets; // max packets to transmit +extern long nreceived; // # of packets we got back +extern long nrepeats; // number of duplicates +extern long ntransmitted; // sequence # for outbound packets = #sent +extern long nchecksum; // replies with bad checksum +extern long nerrors; // icmp errors +extern int interval; // interval between packets (msec) +extern int preload; +extern int deadline; // time to die +extern int lingertime; +extern struct timeval start_time, cur_time; +extern int confirm; +extern int confirm_flag; +extern char *device; +extern int pmtudisc; + +extern volatile int in_pr_addr; // pr_addr() is executing +extern jmp_buf pr_addr_jmp; +*/ + +extern volatile int exiting; +extern volatile int status_snapshot; + +#ifndef MSG_CONFIRM +#define MSG_CONFIRM 0 +#endif + + +/* timing */ +/* +extern int timing; // flag to do timing +extern long tmin; // minimum round trip time +extern long tmax; // maximum round trip time +extern long long tsum; // sum of all times, for doing average +extern long long tsum2; +extern int rtt; +extern uint16_t acked; +extern int pipesize; +*/ + +/* + * Write to stdout + */ +static inline void write_stdout(const char *str, size_t len) +{ + size_t o = 0; + ssize_t cc; + do { + cc = write(STDOUT_FILENO, str + o, len - o); + o += cc; + } while (len > o || cc < 0); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +static inline void tvsub(struct timeval *out, struct timeval *in) +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +static inline void set_signal(int signo, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = (void (*)(int))handler; + sigaction(signo, &sa, NULL); +} + +extern int __schedule_exit(ping_handle_t *a_ping_handle, int next); + +static inline int schedule_exit(ping_handle_t *a_ping_handle, int next) +{ + if (a_ping_handle->ping_common.npackets && a_ping_handle->ping_common.ntransmitted >= a_ping_handle->ping_common.npackets + && !a_ping_handle->ping_common.deadline) + next = __schedule_exit(a_ping_handle, next); + return next; +} + +static inline int in_flight(ping_handle_t *a_ping_handle) +{ + uint16_t diff = (uint16_t)a_ping_handle->ping_common.ntransmitted - a_ping_handle->ping_common.acked; + return (diff<=0x7FFF) ? diff : a_ping_handle->ping_common.ntransmitted-a_ping_handle->ping_common.nreceived-a_ping_handle->ping_common.nerrors; +} + +static inline void acknowledge(ping_handle_t *a_ping_handle, uint16_t seq) +{ + uint16_t diff = (uint16_t)a_ping_handle->ping_common.ntransmitted - seq; + if (diff <= 0x7FFF) { + if ((int)diff+1 > a_ping_handle->ping_common.pipesize) + a_ping_handle->ping_common.pipesize = (int)diff+1; + if ((int16_t)(seq - a_ping_handle->ping_common.acked) > 0 || + (uint16_t)a_ping_handle->ping_common.ntransmitted - a_ping_handle->ping_common.acked > 0x7FFF) + a_ping_handle->ping_common.acked = seq; + } +} + +static inline void advance_ntransmitted(ping_handle_t *a_ping_handle) +{ + a_ping_handle->ping_common.ntransmitted++; + /* Invalidate acked, if 16 bit seq overflows. */ + if ((uint16_t)a_ping_handle->ping_common.ntransmitted - a_ping_handle->ping_common.acked > 0x7FFF) + a_ping_handle->ping_common.acked = (uint16_t)a_ping_handle->ping_common.ntransmitted + 1; +} + +extern void usage(void) __attribute__((noreturn)); +extern void limit_capabilities(ping_handle_t *a_ping_handle); +static int enable_capability_raw(ping_handle_t *a_ping_handle); +static int disable_capability_raw(ping_handle_t *a_ping_handle); +static int enable_capability_admin(ping_handle_t *a_ping_handle); +static int disable_capability_admin(ping_handle_t *a_ping_handle); +#ifdef HAVE_LIBCAP +extern int modify_capability(cap_value_t, cap_flag_value_t); +static inline int enable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_SET); } +static inline int disable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_CLEAR); } +static inline int enable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_SET); } +static inline int disable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_CLEAR); } +#else +extern int modify_capability(ping_handle_t *a_ping_handle, int); +static inline int enable_capability_raw(ping_handle_t *a_ping_handle) { return modify_capability(a_ping_handle, 1); } +static inline int disable_capability_raw(ping_handle_t *a_ping_handle) { return modify_capability(a_ping_handle, 0); } +static inline int enable_capability_admin(ping_handle_t *a_ping_handle) { return modify_capability(a_ping_handle, 1); } +static inline int disable_capability_admin(ping_handle_t *a_ping_handle) { return modify_capability(a_ping_handle, 0); } +#endif +extern void drop_capabilities(void); + +typedef struct socket_st { + int fd; + int socktype; +} socket_st; + +char *pr_addr(ping_handle_t *a_ping_handle, void *sa, socklen_t salen); + +int is_ours(ping_handle_t *a_ping_handle, socket_st *sock, uint16_t id); + +int ping4_run(ping_handle_t *a_ping_handle, int argc, char **argv, struct addrinfo *ai, socket_st *sock); +int ping4_send_probe(ping_handle_t *a_ping_handle, socket_st *, void *packet, unsigned packet_size); +int ping4_receive_error_msg(ping_handle_t *a_ping_handle, socket_st *sock); +int ping4_parse_reply(ping_handle_t *a_ping_handle, socket_st *, struct msghdr *msg, int len, void *addr, struct timeval *); +void ping4_install_filter( ping_handle_t *a_ping_handle, socket_st *); + +typedef struct ping_func_set_st { + int (*send_probe)(ping_handle_t *a_ping_handle, socket_st *, void *packet, unsigned packet_size); + int (*receive_error_msg)(ping_handle_t *a_ping_handle, socket_st *sock); + int (*parse_reply)(ping_handle_t *a_ping_handle, socket_st *, struct msghdr *msg, int len, void *addr, struct timeval *); + void (*install_filter)(ping_handle_t *a_ping_handle, socket_st *); +} ping_func_set_st; + +extern ping_func_set_st ping4_func_set; + +extern int pinger(ping_handle_t *a_ping_handle, ping_func_set_st *fset, socket_st *sock); +extern void sock_setbufs(ping_handle_t *a_ping_handle, socket_st*, int alloc); +extern void setup(ping_handle_t *a_ping_handle, socket_st *); +extern int contains_pattern_in_payload(ping_handle_t *a_ping_handle, uint8_t *ptr); +extern void main_loop(ping_handle_t *a_ping_handle, ping_func_set_st *fset, socket_st*, uint8_t *buf, int buflen);// __attribute__((noreturn)); +extern void finish(ping_handle_t *a_ping_handle);// __attribute__((noreturn)); +extern void status(ping_handle_t *a_ping_handle); +extern void common_options(int ch); +extern int gather_statistics(ping_handle_t *a_ping_handle, uint8_t *ptr, int icmplen, + int cc, uint16_t seq, int hops, + int csfailed, struct timeval *tv, char *from, + void (*pr_reply)(uint8_t *ptr, int cc)); +extern void print_timestamp(ping_handle_t *a_ping_handle); +void fill(ping_handle_t *a_ping_handle, char *patp, unsigned char *packet, unsigned packet_size); + +//extern int mark; +//extern unsigned char outpack[MAXPACKET]; + +/* IPv6 */ + +int ping6_run(ping_handle_t *a_ping_handle, int argc, char **argv, struct addrinfo *ai, socket_st *sock); +void ping6_usage(unsigned from_ping); + +int ping6_send_probe(ping_handle_t *a_ping_handle, socket_st *sockets, void *packet, unsigned packet_size); +int ping6_receive_error_msg(ping_handle_t *a_ping_handle, socket_st *sock); +int ping6_parse_reply(ping_handle_t *a_ping_handle, socket_st *, struct msghdr *msg, int len, void *addr, struct timeval *); +void ping6_install_filter(ping_handle_t *a_ping_handle, socket_st *sockets); + +extern ping_func_set_st ping6_func_set; + +int niquery_option_handler(const char *opt_arg); + +extern uint32_t tclass; +extern uint32_t flowlabel; +extern struct sockaddr_in6 source6; +extern struct sockaddr_in6 whereto6; +extern struct sockaddr_in6 firsthop6; + +/* IPv6 node information query */ + +#define NI_NONCE_SIZE 8 + +struct ni_hdr { + struct icmp6_hdr ni_u; + uint8_t ni_nonce[NI_NONCE_SIZE]; +}; + +#define ni_type ni_u.icmp6_type +#define ni_code ni_u.icmp6_code +#define ni_cksum ni_u.icmp6_cksum +#define ni_qtype ni_u.icmp6_data16[0] +#define ni_flags ni_u.icmp6_data16[1] + +/* Types */ +#ifndef ICMPV6_NI_QUERY +# define ICMPV6_NI_QUERY 139 +# define ICMPV6_NI_REPLY 140 +#endif + +/* Query Codes */ +#define NI_SUBJ_IPV6 0 +#define NI_SUBJ_NAME 1 +#define NI_SUBJ_IPV4 2 + +/* Reply Codes */ +#define NI_SUCCESS 0 +#define NI_REFUSED 1 +#define NI_UNKNOWN 2 + +/* Qtypes */ +#define NI_QTYPE_NOOP 0 +#define NI_QTYPE_NAME 2 +#define NI_QTYPE_IPV6ADDR 3 +#define NI_QTYPE_IPV4ADDR 4 + +/* Flags */ +#define NI_IPV6ADDR_F_TRUNCATE __constant_cpu_to_be16(0x0001) +#define NI_IPV6ADDR_F_ALL __constant_cpu_to_be16(0x0002) +#define NI_IPV6ADDR_F_COMPAT __constant_cpu_to_be16(0x0004) +#define NI_IPV6ADDR_F_LINKLOCAL __constant_cpu_to_be16(0x0008) +#define NI_IPV6ADDR_F_SITELOCAL __constant_cpu_to_be16(0x0010) +#define NI_IPV6ADDR_F_GLOBAL __constant_cpu_to_be16(0x0020) + +#define NI_IPV4ADDR_F_TRUNCATE NI_IPV6ADDR_F_TRUNCATE +#define NI_IPV4ADDR_F_ALL NI_IPV6ADDR_F_ALL + + diff --git a/libdap-chain-net/iputils/ping6_common.c b/libdap-chain-net/iputils/ping6_common.c new file mode 100644 index 0000000000000000000000000000000000000000..3bb143fadd3c39f6bc58b146e380892f50e365aa --- /dev/null +++ b/libdap-chain-net/iputils/ping6_common.c @@ -0,0 +1,1398 @@ +/* + * + * Modified for AF_INET6 by Pedro Roque + * + * <roque@di.fc.ul.pt> + * + * Original copyright notice included bellow + */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * If kernel does not support non-raw ICMP sockets or + * if -N option is used, this program has to run SUID to ROOT or + * with net_cap_raw enabled. + */ + +#include "ping.h" + +/* IPv6 packet information. */ +//struct in6_pktinfo +// { +// struct in6_addr ipi6_addr; /* src/dst IPv6 address */ +// unsigned int ipi6_ifindex; /* send/recv interface index */ +// }; + +ping_func_set_st ping6_func_set = { + .send_probe = ping6_send_probe, + .receive_error_msg = ping6_receive_error_msg, + .parse_reply = ping6_parse_reply, + .install_filter = ping6_install_filter +}; + +#ifndef SCOPE_DELIMITER +# define SCOPE_DELIMITER '%' +#endif + +uint32_t flowlabel; +uint32_t tclass; + +static struct sockaddr_in6 whereto; +static struct sockaddr_in6 firsthop; + +static unsigned char cmsgbuf[4096]; +static size_t cmsglen = 0; + +static int pr_icmph(uint8_t type, uint8_t code, uint32_t info); + +struct sockaddr_in6 source6 = { .sin6_family = AF_INET6 }; +//extern char *device; + +#if defined(USE_GCRYPT) || defined(USE_OPENSSL) || defined(USE_NETTLE) +#include "iputils_md5dig.h" +#define USE_CRYPTO +#endif + +/* Node Information query */ +int ni_query = -1; +int ni_flag = 0; +void *ni_subject = NULL; +int ni_subject_len = 0; +int ni_subject_type = -1; +char *ni_group; + +static inline int ntohsp(uint16_t *p) +{ + uint16_t v; + memcpy(&v, p, sizeof(v)); + return ntohs(v); +} + +unsigned int if_name2index(const char *ifname) +{ + unsigned int i = if_nametoindex(ifname); + if(!i) + error(2, 0, "unknown iface: %s", ifname); + return i; +} + +struct niquery_option { + char *name; + int namelen; + int has_arg; + int data; + int (*handler)(int index, const char *arg); +}; + +#define NIQUERY_OPTION(_name, _has_arg, _data, _handler) \ + { \ + .name = _name, \ + .namelen = sizeof(_name) - 1, \ + .has_arg = _has_arg, \ + .data = _data, \ + .handler = _handler \ + } + +static int niquery_option_name_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))); +static int niquery_option_ipv6_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))); +static int niquery_option_ipv6_flag_handler(int index, const char *arg); +static int niquery_option_ipv4_handler(int index, const char *arg); +static int niquery_option_ipv4_flag_handler(int index, const char *arg); +static int niquery_option_subject_addr_handler(int index, const char *arg); +static int niquery_option_subject_name_handler(int index, const char *arg); +static int niquery_option_help_handler(int index, const char *arg); + +struct niquery_option niquery_options[] = { +NIQUERY_OPTION("name", 0, 0, niquery_option_name_handler), +NIQUERY_OPTION("fqdn", 0, 0, niquery_option_name_handler), +NIQUERY_OPTION("ipv6", 0, 0, niquery_option_ipv6_handler), +NIQUERY_OPTION("ipv6-all", 0, NI_IPV6ADDR_F_ALL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-compatible", 0, NI_IPV6ADDR_F_COMPAT, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-linklocal", 0, NI_IPV6ADDR_F_LINKLOCAL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-sitelocal", 0, NI_IPV6ADDR_F_SITELOCAL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-global", 0, NI_IPV6ADDR_F_GLOBAL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv4", 0, 0, niquery_option_ipv4_handler), +NIQUERY_OPTION("ipv4-all", 0, NI_IPV4ADDR_F_ALL, niquery_option_ipv4_flag_handler), +NIQUERY_OPTION("subject-ipv6", 1, NI_SUBJ_IPV6, niquery_option_subject_addr_handler), +NIQUERY_OPTION("subject-ipv4", 1, NI_SUBJ_IPV4, niquery_option_subject_addr_handler), +NIQUERY_OPTION("subject-name", 1, 0, niquery_option_subject_name_handler), +NIQUERY_OPTION("subject-fqdn", 1, -1, niquery_option_subject_name_handler), +NIQUERY_OPTION("help", 0, 0, niquery_option_help_handler), + { NULL, 0, 0, 0, NULL } +}; + +static inline int niquery_is_enabled(void) +{ + return ni_query >= 0; +} + +#if PING6_NONCE_MEMORY +uint8_t *ni_nonce_ptr; +#else +struct { + struct timeval tv; + pid_t pid; +} ni_nonce_secret; +#endif + +static void niquery_init_nonce(void) +{ +#if PING6_NONCE_MEMORY + struct timeval tv; + unsigned long seed; + + seed = (unsigned long)getpid(); + if (!gettimeofday(&tv, NULL)) + seed ^= tv.tv_usec; + srand(seed); + + ni_nonce_ptr = calloc(NI_NONCE_SIZE, MAX_DUP_CHK); + if (!ni_nonce_ptr) + error(2, errno, "calloc"); + + ni_nonce_ptr[0] = ~0; +#else + gettimeofday(&ni_nonce_secret.tv, NULL); + ni_nonce_secret.pid = getpid(); +#endif +} + +#if !PING6_NONCE_MEMORY +static int niquery_nonce(uint8_t *nonce, int fill) +{ +# ifdef USE_CRYPTO + static uint8_t digest[MD5_DIGEST_LENGTH]; + static int seq = -1; + + if (fill || seq != *(uint16_t *)nonce || seq < 0) { + MD5_CTX ctxt; + + MD5_Init(&ctxt); + MD5_Update(&ctxt, &ni_nonce_secret, sizeof(ni_nonce_secret)); + MD5_Update(&ctxt, nonce, sizeof(uint16_t)); + MD5_Final(digest, &ctxt); + + seq = *(uint16_t *)nonce; + } + + if (fill) { + memcpy(nonce + sizeof(uint16_t), digest, NI_NONCE_SIZE - sizeof(uint16_t)); + return 0; + } else { + if (memcmp(nonce + sizeof(uint16_t), digest, NI_NONCE_SIZE - sizeof(uint16_t))) + return -1; + return ntohsp((uint16_t *)nonce); + } +# else + error(3, ENOSYS, "niquery_nonce() crypto disabled"); +# endif + if(nonce || fill) + return -1; + return -1; +} +#endif + +static inline void niquery_fill_nonce(uint16_t seq, uint8_t *nonce) +{ + uint16_t v = htons(seq); +#if PING6_NONCE_MEMORY + int i; + + memcpy(&ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], &v, sizeof(v)); + + for (i = sizeof(v); i < NI_NONCE_SIZE; i++) + ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK) + i] = 0x100 * (rand() / (RAND_MAX + 1.0)); + + memcpy(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE); +#else + memcpy(nonce, &v, sizeof(v)); + niquery_nonce(nonce, 1); +#endif +} + +static inline int niquery_check_nonce(uint8_t *nonce) +{ +#if PING6_NONCE_MEMORY + uint16_t seq = ntohsp((uint16_t *)nonce); + if (memcmp(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE)) + return -1; + return seq; +#else + return niquery_nonce(nonce, 0); +#endif +} + +static int niquery_set_qtype(int type) +{ + if(niquery_is_enabled() && ni_query != type) { + printf("Qtype conflict\n"); + return -1; + } + ni_query = type; + return 0; +} + +static int niquery_option_name_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))) +{ + if(niquery_set_qtype(NI_QTYPE_NAME) < 0) + return -1; + return 0; +} + +static int niquery_option_ipv6_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))) +{ + if(niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) + return -1; + return 0; +} + +static int niquery_option_ipv6_flag_handler(int index, const char *arg __attribute__((__unused__))) +{ + if(niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) + return -1; + ni_flag |= niquery_options[index].data; + return 0; +} + +static int niquery_option_ipv4_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))) +{ + if(niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) + return -1; + return 0; +} + +static int niquery_option_ipv4_flag_handler(int index, const char *arg __attribute__((__unused__))) +{ + if(niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) + return -1; + ni_flag |= niquery_options[index].data; + return 0; +} + +static inline int niquery_is_subject_valid(void) +{ + return ni_subject_type >= 0 && ni_subject; +} + +static int niquery_set_subject_type(int type) +{ + if(niquery_is_subject_valid() && ni_subject_type != type) { + printf("Subject type conflict\n"); + return -1; + } + ni_subject_type = type; + return 0; +} + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#define OFFSET_OF(type,elem) ((size_t)&((type *)0)->elem) + +static int niquery_option_subject_addr_handler(int index, const char *arg) +{ + struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_flags = getaddrinfo_flags }; + struct addrinfo *result, *ai; + int status; + int offset; + + if(niquery_set_subject_type(niquery_options[index].data) < 0) + return -1; + + ni_subject_type = niquery_options[index].data; + + switch (niquery_options[index].data) { + case NI_SUBJ_IPV6: + ni_subject_len = sizeof(struct in6_addr); + offset = OFFSET_OF(struct sockaddr_in6, sin6_addr); + hints.ai_family = AF_INET6; + break; + case NI_SUBJ_IPV4: + ni_subject_len = sizeof(struct in_addr); + offset = OFFSET_OF(struct sockaddr_in, sin_addr); + hints.ai_family = AF_INET; + break; + default: + /* should not happen. */ + offset = -1; + } + + status = getaddrinfo(arg, 0, &hints, &result); + if(status) { + error(0, 0, "%s: %s", arg, gai_strerror(status)); + return -1; + } + + for(ai = result; ai; ai = ai->ai_next) { + void *p = malloc(ni_subject_len); + if(!p) + continue; + memcpy(p, (uint8_t *) ai->ai_addr + offset, ni_subject_len); + free(ni_subject); + ni_subject = p; + break; + } + freeaddrinfo(result); + + return 0; +} + +#ifdef USE_IDN +# if IDN2_VERSION_NUMBER >= 0x02000000 +# define IDN2_FLAGS IDN2_NONTRANSITIONAL +# else +# define IDN2_FLAGS 0 +# endif +#endif + +#ifdef USE_CRYPTO +static int niquery_option_subject_name_handler(int index, const char *name) +{ + static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ]; + unsigned char *dnptrs[2], **dpp, **lastdnptr; + int n; + size_t i; + char *p; + char *canonname = NULL, *idn = NULL; + unsigned char *buf = NULL; + size_t namelen; + size_t buflen; + int dots, fqdn = niquery_options[index].data; + MD5_CTX ctxt; + uint8_t digest[MD5_DIGEST_LENGTH]; +#ifdef USE_IDN + int rc; +#endif + + if (niquery_set_subject_type(NI_SUBJ_NAME) < 0) + return -1; + +#ifdef USE_IDN + rc = idn2_lookup_ul(name, &idn, IDN2_FLAGS); + if (rc) + error(2, 0, "IDN encoding error: %s", idn2_strerror(rc)); +#else + idn = strdup(name); + if (!idn) + goto oomexit; +#endif + + p = strchr(idn, SCOPE_DELIMITER); + if (p) { + *p = '\0'; + if (strlen(p + 1) >= IFNAMSIZ) + error(1, 0, "too long scope name"); + } + + namelen = strlen(idn); + canonname = malloc(namelen + 1); + if (!canonname) + goto oomexit; + + dots = 0; + for (i = 0; i < namelen + 1; i++) { + canonname[i] = isupper(idn[i]) ? tolower(idn[i]) : idn[i]; + if (idn[i] == '.') + dots++; + } + + if (fqdn == 0) { + /* guess if hostname is FQDN */ + fqdn = dots ? 1 : -1; + } + + buflen = namelen + 3 + 1; /* dn_comp() requrires strlen() + 3, + plus non-fqdn indicator. */ + buf = malloc(buflen); + if (!buf) { + error(0, errno, "memory allocation failed"); + goto errexit; + } + + dpp = dnptrs; + lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)]; + + *dpp++ = (unsigned char *)buf; + *dpp++ = NULL; + + n = dn_comp(canonname, (unsigned char *)buf, buflen, dnptrs, lastdnptr); + if (n < 0) { + error(0, 0, "inappropriate subject name: %s", canonname); + goto errexit; + } else if ((size_t) n >= buflen) { + error(0, 0, "dn_comp() returned too long result"); + goto errexit; + } + + MD5_Init(&ctxt); + MD5_Update(&ctxt, buf, buf[0]); + MD5_Final(digest, &ctxt); + + sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x%s%s", + digest[0], digest[1], digest[2], digest[3], + p ? "%" : "", + p ? p + 1 : ""); + + if (fqdn < 0) + buf[n] = 0; + + free(ni_subject); + + ni_group = nigroup_buf; + ni_subject = buf; + ni_subject_len = n + (fqdn < 0); + ni_group = nigroup_buf; + + free(canonname); + free(idn); + + return 0; + oomexit: + error(0, errno, "memory allocation failed"); + errexit: + free(buf); + free(canonname); + free(idn); + exit(1); +} +#else +static int niquery_option_subject_name_handler(int index __attribute__((__unused__)), + const char *name __attribute__((__unused__))) +{ + error(3, ENOSYS, "niquery_option_subject_name_handler() crypto disabled"); + return -1; +} +#endif + +int niquery_option_help_handler(int index __attribute__((__unused__)), const char *arg __attribute__((__unused__))) +{ + fprintf(stderr, "ping -6 -N <nodeinfo opt>\n" + "Help:\n" + " help\n" + "Query:\n" + " name\n" + " ipv6\n" + " ipv6-all\n" + " ipv6-compatible\n" + " ipv6-global\n" + " ipv6-linklocal\n" + " ipv6-sitelocal\n" + " ipv4\n" + " ipv4-all\n" + "Subject:\n" + " subject-ipv6=addr\n" + " subject-ipv4=addr\n" + " subject-name=name\n" + " subject-fqdn=name\n" + ); + exit(2); +} + +int niquery_option_handler(const char *opt_arg) +{ + struct niquery_option *p; + int i; + int ret = -1; + for(i = 0, p = niquery_options; p->name; i++, p++) { + if(strncmp(p->name, opt_arg, p->namelen)) + continue; + if(!p->has_arg) { + if(opt_arg[p->namelen] == '\0') { + ret = p->handler(i, NULL); + if(ret >= 0) + break; + } + } else { + if(opt_arg[p->namelen] == '=') { + ret = p->handler(i, &opt_arg[p->namelen] + 1); + if(ret >= 0) + break; + } + } + } + if(!p->name) + ret = niquery_option_help_handler(0, NULL); + return ret; +} + +int ping6_run(ping_handle_t *a_ping_handle, int argc, char **argv, struct addrinfo *ai, struct socket_st *sock) +{ + static const struct addrinfo hints = { .ai_family = AF_INET6, .ai_flags = getaddrinfo_flags }; + struct addrinfo *result = NULL; + int status; + int hold, packlen; + unsigned char *packet; + char *target; + struct icmp6_filter filter; + int err; + static uint32_t scope_id = 0; + + if(niquery_is_enabled()) { + niquery_init_nonce(); + + if(!niquery_is_subject_valid()) { + ni_subject = &whereto.sin6_addr; + ni_subject_len = sizeof(whereto.sin6_addr); + ni_subject_type = NI_SUBJ_IPV6; + } + } + + if(argc > 1) { + usage(); + } else if(argc == 1) { + target = *argv; + } else { + if(ni_query < 0 && ni_subject_type != NI_SUBJ_NAME) + usage(); + target = ni_group; + } + + if(!ai) { + status = getaddrinfo(target, NULL, &hints, &result); + if(status) + error(2, 0, "%s: %s", target, gai_strerror(status)); + ai = result; + } + + memcpy(&whereto, ai->ai_addr, sizeof(whereto)); + whereto.sin6_port = htons(IPPROTO_ICMPV6); + + if(result) + freeaddrinfo(result); + + if(memchr(target, ':', strlen(target))) + a_ping_handle->ping_common.options |= F_NUMERIC; + + if(IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { + memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16); + firsthop.sin6_scope_id = whereto.sin6_scope_id; + /* Verify scope_id is the same as intermediate nodes */ + if(firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) + error(2, 0, "scope discrepancy among the nodes"); + else if(!scope_id) + scope_id = firsthop.sin6_scope_id; + } + + a_ping_handle->ping_common.hostname = target; + + if(IN6_IS_ADDR_UNSPECIFIED(&source6.sin6_addr)) { + socklen_t alen; + int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); + + if(probe_fd < 0) + error(2, errno, "socket"); + if(a_ping_handle->device) { + unsigned int iface = if_name2index(a_ping_handle->device); +#ifdef IPV6_RECVPKTINFO + struct in6_pktinfo ipi; + + memset(&ipi, 0, sizeof(ipi)); + ipi.ipi6_ifindex = iface; +#endif + + if(IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) + firsthop.sin6_scope_id = iface; + enable_capability_raw(a_ping_handle); +#ifdef IPV6_RECVPKTINFO + if( + setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1 || + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1) { + perror("setsockopt(IPV6_PKTINFO)"); + exit(2); + } +#endif + if( + setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, a_ping_handle->device, strlen(a_ping_handle->device) + 1) == -1 || + setsockopt(sock->fd, SOL_SOCKET, SO_BINDTODEVICE, a_ping_handle->device, strlen(a_ping_handle->device) + 1) == -1) { + error(2, errno, "setsockopt(SO_BINDTODEVICE) %s", a_ping_handle->device); + } + disable_capability_raw(a_ping_handle); + } + + if(!IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) && + !IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) + firsthop.sin6_family = AF_INET6; + + firsthop.sin6_port = htons(1025); + if(connect(probe_fd, (struct sockaddr*) &firsthop, sizeof(firsthop)) == -1) + error(2, errno, "connect"); + alen = sizeof source6; + if(getsockname(probe_fd, (struct sockaddr *) &source6, &alen) == -1) + error(2, errno, "getsockname"); + source6.sin6_port = 0; + close(probe_fd); + + if(a_ping_handle->device) { + struct ifaddrs *ifa0, *ifa; + + if(getifaddrs(&ifa0)) + error(2, errno, "getifaddrs"); + + for(ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if(!ifa->ifa_name || !ifa->ifa_addr || + ifa->ifa_addr->sa_family != AF_INET6) + continue; + if(!strcmp(ifa->ifa_name, a_ping_handle->device) && + IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 * )ifa->ifa_addr)->sin6_addr, + &source6.sin6_addr)) + break; + } + if(!ifa) + error(0, 0, "Warning: source address might be selected on device other than: %s", a_ping_handle->device); + + freeifaddrs(ifa0); + } + } + else if(a_ping_handle->device && (IN6_IS_ADDR_LINKLOCAL(&source6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&source6.sin6_addr))) + source6.sin6_scope_id = if_name2index(a_ping_handle->device); + + if(a_ping_handle->device) { + struct cmsghdr *cmsg; + struct in6_pktinfo *ipi; + + cmsg = (struct cmsghdr*) (cmsgbuf + cmsglen); + cmsglen += CMSG_SPACE(sizeof(*ipi)); + cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + ipi = (struct in6_pktinfo*) CMSG_DATA(cmsg); + memset(ipi, 0, sizeof(*ipi)); + ipi->ipi6_ifindex = if_name2index(a_ping_handle->device); + } + + if((whereto.sin6_addr.s6_addr16[0] & htons(0xff00)) == htons(0xff00)) { + if(a_ping_handle->ping_common.uid) { + if(a_ping_handle->ping_common.interval < 1000) + error(2, 0, "multicast ping with too short interval: %d", a_ping_handle->ping_common.interval); + if(a_ping_handle->pmtudisc >= 0 && a_ping_handle->pmtudisc != IPV6_PMTUDISC_DO) + error(2, 0, "multicast ping does not fragment"); + } + if(a_ping_handle->pmtudisc < 0) + a_ping_handle->pmtudisc = IPV6_PMTUDISC_DO; + } + + if(a_ping_handle->pmtudisc >= 0) { + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &a_ping_handle->pmtudisc, sizeof (a_ping_handle->pmtudisc)) == -1) + error(2, errno, "IPV6_MTU_DISCOVER"); + } + + if((a_ping_handle->ping_common.options & F_STRICTSOURCE) && + bind(sock->fd, (struct sockaddr *) &source6, sizeof source6) == -1) + error(2, errno, "bind icmp socket"); + + if((ssize_t) a_ping_handle->ping_common.datalen >= (ssize_t) sizeof(struct timeval) && (ni_query < 0)) { + /* can we time transfer */ + a_ping_handle->ping_common.timing = 1; + } + packlen = a_ping_handle->ping_common.datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */ + if(!(packet = (unsigned char *) malloc((unsigned int) packlen))) + error(2, errno, "memory allocation failed"); + + hold = 1; + + /* Estimate memory eaten by single packet. It is rough estimate. + * Actually, for small datalen's it depends on kernel side a lot. */ + hold = a_ping_handle->ping_common.datalen + 8; + hold += ((hold + 511) / 512) * (40 + 16 + 64 + 160); + sock_setbufs(a_ping_handle,sock, hold); + +#ifdef __linux__ + if(sock->socktype == SOCK_RAW) { + int csum_offset = 2; + int sz_opt = sizeof(int); + + err = setsockopt(sock->fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt); + if(err < 0) { + /* checksum should be enabled by default and setting this + * option might fail anyway. + */ + error(0, errno, "setsockopt(RAW_CHECKSUM) failed - try to continue"); + } +#else + { +#endif + + /* + * select icmp echo reply as icmp type to receive + */ + + ICMP6_FILTER_SETBLOCKALL(&filter); + + ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter); + ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter); + ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter); + ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter); + + if(niquery_is_enabled()) + ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter); + else + ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter); + + err = setsockopt(sock->fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof filter); + + if(err < 0) + error(2, errno, "setsockopt(ICMP6_FILTER)"); + } + + if(a_ping_handle->ping_common.options & F_NOLOOP) { + int loop = 0; + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof loop) == -1) + error(2, errno, "can't disable multicast loopback"); + } + if(a_ping_handle->ping_common.options & F_TTL) { + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &a_ping_handle->ping_common.ttl, sizeof (a_ping_handle->ping_common.ttl)) == -1) + error(2, errno, "can't set multicast hop limit"); + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &a_ping_handle->ping_common.ttl, sizeof (a_ping_handle->ping_common.ttl)) == -1) + error(2, errno, "can't set unicast hop limit"); + } + + const int on = 1; + if( + #ifdef IPV6_RECVHOPLIMIT + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof on) == -1 && + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof on) == -1 + #else + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof on) == -1 +#endif + ) + error(2, errno, "can't receive hop limit"); + + if(a_ping_handle->ping_common.options & F_TCLASS) { +#ifdef IPV6_TCLASS + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof tclass) == -1) + error(2, errno, "setsockopt(IPV6_TCLASS)"); +#else + error(0, 0, "traffic class is not supported"); +#endif + } + + if(a_ping_handle->ping_common.options & F_FLOWINFO) { +#ifdef IPV6_FLOWLABEL_MGR + char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen]; + struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf; + int freq_len = sizeof(*freq); + memset(freq, 0, sizeof(*freq)); + freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL); + freq->flr_action = IPV6_FL_A_GET; + freq->flr_flags = IPV6_FL_F_CREATE; + freq->flr_share = IPV6_FL_S_EXCL; + memcpy(&freq->flr_dst, &whereto.sin6_addr, 16); + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) == -1) + error(2, errno, "can't set flowlabel"); + flowlabel = freq->flr_label; +#else + error(2, 0, "flow labels are not supported"); +#endif + +#ifdef IPV6_FLOWINFO_SEND + whereto.sin6_flowinfo = flowlabel; + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &on, sizeof on) == -1) + error(2, errno, "can't send flowinfo"); +#else + error(2, 0, "flowinfo is not supported"); +#endif + } + + printf("PING %s(%s) ", a_ping_handle->ping_common.hostname, pr_addr(a_ping_handle, &whereto, sizeof whereto)); + if(flowlabel) + printf(", flow 0x%05x, ", (unsigned) ntohl(flowlabel)); + if(a_ping_handle->device || (a_ping_handle->ping_common.options & F_STRICTSOURCE)) { + int saved_options = a_ping_handle->ping_common.options; + + a_ping_handle->ping_common.options |= F_NUMERIC; + printf("from %s %s: ", pr_addr(a_ping_handle, &source6, sizeof source6), a_ping_handle->device ? a_ping_handle->device : ""); + a_ping_handle->ping_common.options = saved_options; + } + printf("%d data bytes\n", a_ping_handle->ping_common.datalen); + + setup(a_ping_handle, sock); + + drop_capabilities(); + + main_loop(a_ping_handle, &ping6_func_set, sock, packet, packlen); + return 0; +} + +int ping6_receive_error_msg(ping_handle_t *a_ping_handle, socket_st *sock) +{ + ssize_t res; + char cbuf[512]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sock_extended_err *e; + struct icmp6_hdr icmph; + struct sockaddr_in6 target; + int net_errors = 0; + int local_errors = 0; + int saved_errno = errno; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void*) ⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if(res < 0) + goto out; + + e = NULL; + for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if(cmsg->cmsg_level == IPPROTO_IPV6) { + if(cmsg->cmsg_type == IPV6_RECVERR) + e = (struct sock_extended_err *) CMSG_DATA(cmsg); + } + } + if(e == NULL) + abort(); + + if(e->ee_origin == SO_EE_ORIGIN_LOCAL) { + local_errors++; + if(a_ping_handle->ping_common.options & F_QUIET) + goto out; + if(a_ping_handle->ping_common.options & F_FLOOD) + write_stdout("E", 1); + else if(e->ee_errno != EMSGSIZE) + error(0, e->ee_errno, "local error"); + else + error(0, 0, "local error: message too long, mtu: %u", e->ee_info); + a_ping_handle->ping_common.nerrors++; + } else if(e->ee_origin == SO_EE_ORIGIN_ICMP6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*) (e + 1); + + if((size_t) res < sizeof(icmph) || + memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) || + icmph.icmp6_type != ICMP6_ECHO_REQUEST || + !is_ours(a_ping_handle, sock, icmph.icmp6_id)) { + /* Not our error, not an error at all. Clear. */ + saved_errno = 0; + goto out; + } + + net_errors++; + a_ping_handle->ping_common.nerrors++; + if(a_ping_handle->ping_common.options & F_QUIET) + goto out; + if(a_ping_handle->ping_common.options & F_FLOOD) { + write_stdout("\bE", 2); + } else { + print_timestamp(a_ping_handle); + printf("From %s icmp_seq=%u ", pr_addr(a_ping_handle, sin6, sizeof *sin6), ntohs(icmph.icmp6_seq)); + pr_icmph(e->ee_type, e->ee_code, e->ee_info); + putchar('\n'); + fflush(stdout); + } + } + + out: + errno = saved_errno; + return net_errors ? net_errors : -local_errors; +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first several bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +static int build_echo(ping_handle_t *a_ping_handle, uint8_t *_icmph, unsigned packet_size __attribute__((__unused__))) +{ + struct icmp6_hdr *icmph; + int cc; + + icmph = (struct icmp6_hdr *) _icmph; + icmph->icmp6_type = ICMP6_ECHO_REQUEST; + icmph->icmp6_code = 0; + icmph->icmp6_cksum = 0; + icmph->icmp6_seq= htons(a_ping_handle->ping_common.ntransmitted+1); + icmph->icmp6_id= a_ping_handle->ping_common.ident; + + if(a_ping_handle->ping_common.timing) + gettimeofday((struct timeval *) &_icmph[8], + (struct timezone *) NULL); + + cc = a_ping_handle->ping_common.datalen + 8; /* skips ICMP portion */ + + return cc; +} + +static int build_niquery(ping_handle_t *a_ping_handle, uint8_t *_nih, unsigned packet_size __attribute__((__unused__))) +{ + struct ni_hdr *nih; + int cc; + + nih = (struct ni_hdr *) _nih; + nih->ni_cksum = 0; + + nih->ni_type = ICMPV6_NI_QUERY; + cc = sizeof(*nih); + a_ping_handle->ping_common.datalen = 0; + + niquery_fill_nonce(a_ping_handle->ping_common.ntransmitted + 1, nih->ni_nonce); + nih->ni_code = ni_subject_type; + nih->ni_qtype= htons(ni_query); + nih->ni_flags= ni_flag; + memcpy(nih + 1, ni_subject, ni_subject_len); + cc += ni_subject_len; + + return cc; +} + +int ping6_send_probe(ping_handle_t *a_ping_handle, socket_st *sock, void *packet, unsigned packet_size) +{ + int len, cc; + + rcvd_clear(a_ping_handle, a_ping_handle->ping_common.ntransmitted + 1); + + if(niquery_is_enabled()) + len = build_niquery(a_ping_handle, packet, packet_size); + else + len = build_echo(a_ping_handle, packet, packet_size); + + if(cmsglen == 0) { + cc = sendto(sock->fd, (char *) packet, len, a_ping_handle->ping_common.confirm, + (struct sockaddr *) &whereto, + sizeof(struct sockaddr_in6)); + } else { + struct msghdr mhdr; + struct iovec iov; + + iov.iov_len = len; + iov.iov_base = packet; + + memset(&mhdr, 0, sizeof(mhdr)); + mhdr.msg_name = &whereto; + mhdr.msg_namelen = sizeof(struct sockaddr_in6); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = cmsgbuf; + mhdr.msg_controllen = cmsglen; + + cc = sendmsg(sock->fd, &mhdr, a_ping_handle->ping_common.confirm); + } + a_ping_handle->ping_common.confirm = 0; + + return (cc == len ? 0 : cc); +} + +void pr_echo_reply(uint8_t *_icmph, int cc __attribute__((__unused__))) +{ + struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph; + log_printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq)); +} + +static void putchar_safe(char c) +{ + if(isprint(c)) + putchar(c); + else + printf("\\%03o", c); +} + +static +void pr_niquery_reply_name(struct ni_hdr *nih, int len) +{ + uint8_t *h = (uint8_t *) (nih + 1); + uint8_t *p = h + 4; + uint8_t *end = (uint8_t *) nih + len; + int continued = 0; + char buf[1024]; + int ret; + + len -= sizeof(struct ni_hdr) + 4; + + if(len < 0) { + printf(" parse error (too short)"); + return; + } + while(p < end) { + int fqdn = 1; + size_t i; + + memset(buf, 0xff, sizeof(buf)); + + if(continued) + putchar(','); + + ret = dn_expand(h, end, p, buf, sizeof(buf)); + if(ret < 0) { + printf(" parse error (truncated)"); + break; + } + if(p + ret < end && *(p + ret) == '\0') + fqdn = 0; + + putchar(' '); + for(i = 0; i < strlen(buf); i++) + putchar_safe(buf[i]); + if(fqdn) + putchar('.'); + + p += ret + !fqdn; + + continued = 1; + } +} + +static +void pr_niquery_reply_addr(struct ni_hdr *nih, int len) +{ + uint8_t *h = (uint8_t *) (nih + 1); + uint8_t *p; + uint8_t *end = (uint8_t *) nih + len; + int af; + int aflen; + int continued = 0; + int truncated; + char buf[1024]; + + switch (ntohs(nih->ni_qtype)) { + case NI_QTYPE_IPV4ADDR: + af = AF_INET; + aflen = sizeof(struct in_addr); + truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE; + break; + case NI_QTYPE_IPV6ADDR: + af = AF_INET6; + aflen = sizeof(struct in6_addr); + truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE; + break; + default: + /* should not happen */ + af = aflen = truncated = 0; + } + p = h; + if(len < 0) { + printf(" parse error (too short)"); + return; + } + + while(p < end) { + if(continued) + putchar(','); + + if(p + sizeof(uint32_t) + aflen > end) { + printf(" parse error (truncated)"); + break; + } + if(!inet_ntop(af, p + sizeof(uint32_t), buf, sizeof(buf))) + printf(" unexpeced error in inet_ntop(%s)", + strerror(errno)); + else + printf(" %s", buf); + p += sizeof(uint32_t) + aflen; + + continued = 1; + } + if(truncated) + printf(" (truncated)"); +} + +static +void pr_niquery_reply(uint8_t *_nih, int len) +{ + struct ni_hdr *nih = (struct ni_hdr *) _nih; + + switch (nih->ni_code) { + case NI_SUCCESS: + switch (ntohs(nih->ni_qtype)) { + case NI_QTYPE_NAME: + pr_niquery_reply_name(nih, len); + break; + case NI_QTYPE_IPV4ADDR: + case NI_QTYPE_IPV6ADDR: + pr_niquery_reply_addr(nih, len); + break; + default: + printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype)); + } + break; + case NI_REFUSED: + printf(" refused"); + break; + case NI_UNKNOWN: + printf(" unknown"); + break; + default: + printf(" unknown code(%02x)", ntohs(nih->ni_code)); + } + printf("; seq=%u;", ntohsp((uint16_t*) nih->ni_nonce)); +} + +/* + * parse_reply -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +int +ping6_parse_reply(ping_handle_t *a_ping_handle, socket_st *sock, struct msghdr *msg, int cc, void *addr, struct timeval *tv) +{ + struct sockaddr_in6 *from = addr; + uint8_t *buf = msg->msg_iov->iov_base; + struct cmsghdr *c; + struct icmp6_hdr *icmph; + int hops = -1; + + for(c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { + if(c->cmsg_level != IPPROTO_IPV6) + continue; + switch (c->cmsg_type) { + case IPV6_HOPLIMIT: + #ifdef IPV6_2292HOPLIMIT + case IPV6_2292HOPLIMIT: + #endif + if(c->cmsg_len < CMSG_LEN(sizeof(int))) + continue; + memcpy(&hops, CMSG_DATA(c), sizeof(hops)); + } + } + + /* Now the ICMP part */ + + icmph = (struct icmp6_hdr *) buf; + if(cc < 8) { + if(a_ping_handle->ping_common.options & F_VERBOSE) + error(0, 0, "packet too short: %d bytes", cc); + return 1; + } + + if(icmph->icmp6_type == ICMP6_ECHO_REPLY) { + if(!is_ours(a_ping_handle, sock, icmph->icmp6_id)) + return 1; + if (!contains_pattern_in_payload(a_ping_handle, (uint8_t*)(icmph+1))) + return 1; /* 'Twas really not our ECHO */ + if (gather_statistics(a_ping_handle, (uint8_t*)icmph, sizeof(*icmph), cc, + ntohs(icmph->icmp6_seq), + hops, 0, tv, pr_addr(a_ping_handle, from, sizeof *from), + pr_echo_reply)) { + fflush(stdout); + return 0; + } + } else if (icmph->icmp6_type == ICMPV6_NI_REPLY) { + struct ni_hdr *nih = (struct ni_hdr *)icmph; + int seq = niquery_check_nonce(nih->ni_nonce); + if (seq < 0) + return 1; + if (gather_statistics(a_ping_handle, (uint8_t*)icmph, sizeof(*icmph), cc, + seq, + hops, 0, tv, pr_addr(a_ping_handle, from, sizeof *from), + pr_niquery_reply)) + return 0; + } else { + int nexthdr; + struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1); + struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1); + + /* We must not ever fall here. All the messages but + * echo reply are blocked by filter and error are + * received with IPV6_RECVERR. Ugly code is preserved + * however, just to remember what crap we avoided + * using RECVRERR. :-) + */ + + if (cc < (int) (8 + sizeof(struct ip6_hdr) + 8)) + return 1; + + if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16)) + return 1; + + nexthdr = iph1->ip6_nxt; + + if (nexthdr == 44) { + nexthdr = *(uint8_t*)icmph1; + icmph1++; + } + if (nexthdr == IPPROTO_ICMPV6) { + if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST || + !is_ours(a_ping_handle, sock, icmph1->icmp6_id)) + return 1; + acknowledge(a_ping_handle, ntohs(icmph1->icmp6_seq)); + a_ping_handle->ping_common.nerrors++; + if (a_ping_handle->ping_common.options & F_FLOOD) { + write_stdout("\bE", 2); + return 0; + } + print_timestamp(a_ping_handle); + printf("From %s: icmp_seq=%u ", pr_addr(a_ping_handle, from, sizeof *from), ntohs(icmph1->icmp6_seq)); + } else { + /* We've got something other than an ECHOREPLY */ + if (!(a_ping_handle->ping_common.options & F_VERBOSE) || a_ping_handle->ping_common.uid) + return 1; + print_timestamp(a_ping_handle); + printf("From %s: ", pr_addr(a_ping_handle, from, sizeof *from)); + } + pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu)); + } + + if(a_ping_handle->ping_common.options & F_AUDIBLE) { + putchar('\a'); + if(a_ping_handle->ping_common.options & F_FLOOD) + fflush(stdout); + } + if(!(a_ping_handle->ping_common.options & F_FLOOD)) { + putchar('\n'); + fflush(stdout); + } + return 0; +} + +int pr_icmph(uint8_t type, uint8_t code, uint32_t info) +{ + switch (type) { + case ICMP6_DST_UNREACH: + printf("Destination unreachable: "); + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + printf("No route"); + break; + case ICMP6_DST_UNREACH_ADMIN: + printf("Administratively prohibited"); + break; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + printf("Beyond scope of source address"); + break; + case ICMP6_DST_UNREACH_ADDR: + printf("Address unreachable"); + break; + case ICMP6_DST_UNREACH_NOPORT: + printf("Port unreachable"); + break; + default: + printf("Unknown code %d", code); + break; + } + break; + case ICMP6_PACKET_TOO_BIG: + printf("Packet too big: mtu=%u", info); + if(code) + printf(", code=%d", code); + break; + case ICMP6_TIME_EXCEEDED: + printf("Time exceeded: "); + if(code == ICMP6_TIME_EXCEED_TRANSIT) + printf("Hop limit"); + else if(code == ICMP6_TIME_EXCEED_REASSEMBLY) + printf("Defragmentation failure"); + else + printf("code %d", code); + break; + case ICMP6_PARAM_PROB: + printf("Parameter problem: "); + if(code == ICMP6_PARAMPROB_HEADER) + printf("Wrong header field "); + else if(code == ICMP6_PARAMPROB_NEXTHEADER) + printf("Unknown header "); + else if(code == ICMP6_PARAMPROB_OPTION) + printf("Unknown option "); + else + printf("code %d ", code); + printf("at %u", info); + break; + case ICMP6_ECHO_REQUEST: + printf("Echo request"); + break; + case ICMP6_ECHO_REPLY: + printf("Echo reply"); + break; + case MLD_LISTENER_QUERY: + printf("MLD Query"); + break; + case MLD_LISTENER_REPORT: + printf("MLD Report"); + break; + case MLD_LISTENER_REDUCTION: + printf("MLD Reduction"); + break; + default: + printf("unknown icmp type: %u", type); + + } + return 0; +} + +void ping6_install_filter(ping_handle_t *a_ping_handle, socket_st *sock) +{ + static int once; + static struct sock_filter insns[] = { + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4), /* Load icmp echo ident */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ + BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), /* Load icmp type */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */ + BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */ + }; + static struct sock_fprog filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + if(once) + return; + once = 1; + + /* Patch bpflet for current identifier. */ + insns[1] = (struct sock_filter )BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(a_ping_handle->ping_common.ident), 0, 1); + + if(setsockopt(sock->fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) + error(0, errno, "WARNING: failed to install socket filter"); +} diff --git a/libdap-chain-net/iputils/ping_common.c b/libdap-chain-net/iputils/ping_common.c new file mode 100644 index 0000000000000000000000000000000000000000..566fdc755ee685ccff49f24f34d3450fb8590779 --- /dev/null +++ b/libdap-chain-net/iputils/ping_common.c @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "ping.h" + +#ifndef HZ +#define HZ sysconf(_SC_CLK_TCK) +#endif + +/* +int options; + +int mark; +int sndbuf; +int ttl; +int rtt; +int rtt_addend; +uint16_t acked; + +unsigned char outpack[MAXPACKET]; +struct rcvd_table rcvd_tbl; + +// counters +long npackets; // max packets to transmit +long nreceived; // # of packets we got back +long nrepeats; // number of duplicates +long ntransmitted; // sequence # for outbound packets = #sent +long nchecksum; // replies with bad checksum +long nerrors; // icmp errors +int interval = 1000; // interval between packets (msec) +int preload = 1; +int deadline = 0; // time to die +int lingertime = MAXWAIT * 1000; +struct timeval start_time, cur_time; +int confirm = 0; +volatile int in_pr_addr = 0; // pr_addr() is executing +jmp_buf pr_addr_jmp; +*/ + +volatile int exiting; +volatile int status_snapshot; + +/* Stupid workarounds for bugs/missing functionality in older linuces. + * confirm_flag fixes refusing service of kernels without MSG_CONFIRM. + * i.e. for linux-2.2 */ +/* + +int confirm_flag = MSG_CONFIRM; + +// timing +int timing; // flag to do timing +long tmin = LONG_MAX; // minimum round trip time +long tmax; // maximum round trip time +*/ +/* Message for rpm maintainers: have _shame_. If you want + * to fix something send the patch to me for sanity checking. + * "sparcfix" patch is a complete non-sense, apparenly the person + * prepared it was stoned. + */ +/* +long long tsum; // sum of all times, for doing average +long long tsum2; +int pipesize = -1; + +int datalen = DEFDATALEN; + +char *hostname; +int uid; +uid_t euid; +int ident; // process id to identify our packets + +static int screen_width = INT_MAX; + +#ifdef HAVE_LIBCAP +static cap_value_t cap_raw = CAP_NET_RAW; +static cap_value_t cap_admin = CAP_NET_ADMIN; +#endif +*/ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + + +void usage(void) +{ + fprintf(stderr, + "\nUsage\n" + " ping [options] <destination>\n" + "\nOptions:\n" + " <destination> dns name or ip address\n" + " -a use audible ping\n" + " -A use adaptive ping\n" + " -B sticky source address\n" + " -c <count> stop after <count> replies\n" + " -D print timestamps\n" + " -d use SO_DEBUG socket option\n" + " -f flood ping\n" + " -h print help and exit\n" + " -I <interface> either interface name or address\n" + " -i <interval> seconds between sending each packet\n" + " -L suppress loopback of multicast packets\n" + " -l <preload> send <preload> number of packages while waiting replies\n" + " -m <mark> tag the packets going out\n" + " -M <pmtud opt> define mtu discovery, can be one of <do|dont|want>\n" + " -n no dns name resolution\n" + " -O report outstanding replies\n" + " -p <pattern> contents of padding byte\n" + " -q quiet output\n" + " -Q <tclass> use quality of service <tclass> bits\n" + " -s <size> use <size> as number of data bytes to be sent\n" + " -S <size> use <size> as SO_SNDBUF socket option value\n" + " -t <ttl> define time to live\n" + " -U print user-to-user latency\n" + " -v verbose output\n" + " -V print version and exit\n" + " -w <deadline> reply wait <deadline> in seconds\n" + " -W <timeout> time to wait for response\n" + "\nIPv4 options:\n" + " -4 use IPv4\n" + " -b allow pinging broadcast\n" + " -R record route\n" + " -T <timestamp> define timestamp, can be one of <tsonly|tsandaddr|tsprespec>\n" + "\nIPv6 options:\n" + " -6 use IPv6\n" + " -F <flowlabel> define flow label, default is random\n" + " -N <nodeinfo opt> use icmp6 node info query, try <help> as argument\n" + "\nFor more details see ping(8).\n" + ); + //exit(2); + exit(2); +} + +void limit_capabilities(ping_handle_t *a_ping_handle) +{ +#ifdef HAVE_LIBCAP + cap_t cap_cur_p; + cap_t cap_p; + cap_flag_value_t cap_ok; + + cap_cur_p = cap_get_proc(); + if (!cap_cur_p) + error(-1, errno, "cap_get_proc"); + cap_p = cap_init(); + if (!cap_p) + error(-1, errno, "cap_init"); + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_ADMIN, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_admin, CAP_SET); + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_raw, CAP_SET); + if (cap_set_proc(cap_p) < 0) + error(-1, errno, "cap_set_proc"); + if (prctl(PR_SET_KEEPCAPS, 1) < 0) + error(-1, errno, "prctl"); + if (setuid(getuid()) < 0) + error(-1, errno, "setuid"); + if (prctl(PR_SET_KEEPCAPS, 0) < 0) + error(-1, errno, "prctl"); + cap_free(cap_p); + cap_free(cap_cur_p); +#endif + a_ping_handle->ping_common.uid = getuid(); + a_ping_handle->ping_common.euid = geteuid(); +#ifndef HAVE_LIBCAP + if(seteuid(a_ping_handle->ping_common.uid)) + error(-1, errno, "setuid"); +#endif +} + +#ifdef HAVE_LIBCAP +int modify_capability(cap_value_t cap, cap_flag_value_t on) +{ + cap_t cap_p = cap_get_proc(); + cap_flag_value_t cap_ok; + int rc = -1; + + if (!cap_p) { + error(0, errno, "cap_get_proc"); + goto out; + } + + cap_ok = CAP_CLEAR; + cap_get_flag(cap_p, cap, CAP_PERMITTED, &cap_ok); + if (cap_ok == CAP_CLEAR) { + rc = on ? -1 : 0; + goto out; + } + + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on); + + if (cap_set_proc(cap_p) < 0) { + error(0, errno, "cap_set_proc"); + goto out; + } + + cap_free(cap_p); + cap_p = NULL; + + rc = 0; + out: + if (cap_p) + cap_free(cap_p); + return rc; +} +#else +int modify_capability(ping_handle_t *a_ping_handle, int on) +{ + if(seteuid(on ? a_ping_handle->ping_common.euid : getuid())) { + error(0, errno, "seteuid"); + return -1; + } + + return 0; +} +#endif + +void drop_capabilities(void) +{ +#ifdef HAVE_LIBCAP + cap_t cap = cap_init(); + if (cap_set_proc(cap) < 0) + error(-1, errno, "cap_set_proc"); + cap_free(cap); +#else + if(setuid(getuid())) + error(-1, errno, "setuid"); +#endif +} + +/* Fills all the outpack, excluding ICMP header, but _including_ + * timestamp area with supplied pattern. + */ +void fill(ping_handle_t *a_ping_handle, char *patp, unsigned char *packet, unsigned packet_size) +{ + int ii, jj; + unsigned int pat[16]; + char *cp; + unsigned char *bp = packet + 8; + +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + + for(cp = patp; *cp; cp++) { + if(!isxdigit(*cp)) + error(2, 0, "patterns must be specified as hex digits: %s", cp); + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if(ii > 0) { + unsigned kk; + for(kk = 0; kk <= packet_size - (8 + ii); kk += ii) + for(jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + } + if(!(a_ping_handle->ping_common.options & F_QUIET)) { + printf("PATTERN: 0x"); + for(jj = 0; jj < ii; ++jj) + printf("%02x", bp[jj] & 0xFF); + printf("\n"); + } + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif +} + +static void sigexit(int signo __attribute__((__unused__))) +{ + //exiting = 1; + //if(in_pr_addr) + // longjmp(pr_addr_jmp, 0); +} + +static void sigstatus(int signo __attribute__((__unused__))) +{ + //status_snapshot = 1; +} + +int __schedule_exit(ping_handle_t *a_ping_handle, int next) +{ + static unsigned long waittime; + struct itimerval it; + + if(waittime) + return next; + + if(a_ping_handle->ping_common.nreceived) { + waittime = 2 * a_ping_handle->ping_common.tmax; + if(waittime < (unsigned long) (1000 * a_ping_handle->ping_common.interval)) + waittime = 1000 * a_ping_handle->ping_common.interval; + } else + waittime = a_ping_handle->ping_common.lingertime * 1000; + + if(next < 0 || (unsigned long) next < waittime / 1000) + next = waittime / 1000; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = waittime / 1000000; + it.it_value.tv_usec = waittime % 1000000; + setitimer(ITIMER_REAL, &it, NULL); + return next; +} + +static inline void update_interval(ping_handle_t *a_ping_handle) +{ + int est = a_ping_handle->ping_common.rtt ? a_ping_handle->ping_common.rtt / 8 : a_ping_handle->ping_common.interval * 1000; + + a_ping_handle->ping_common.interval = (est + a_ping_handle->ping_common.rtt_addend + 500) / 1000; + if(a_ping_handle->ping_common.uid && a_ping_handle->ping_common.interval < MINUSERINTERVAL) + a_ping_handle->ping_common.interval = MINUSERINTERVAL; +} + +/* + * Print timestamp + */ +void print_timestamp(ping_handle_t *a_ping_handle) +{ + if(a_ping_handle->ping_common.options & F_PTIMEOFDAY) { + struct timeval tv; + gettimeofday(&tv, NULL); + printf("[%lu.%06lu] ", + (unsigned long) tv.tv_sec, (unsigned long) tv.tv_usec); + } +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first several bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +int pinger(ping_handle_t *a_ping_handle, ping_func_set_st *fset, socket_st *sock) +{ + static int oom_count; + static int tokens; + int i; + + /* Have we already sent enough? If we have, return an arbitrary positive value. */ + if(exiting || (a_ping_handle->ping_common.npackets && a_ping_handle->ping_common.ntransmitted >= a_ping_handle->ping_common.npackets && !a_ping_handle->ping_common.deadline)) + return 1000; + + /* Check that packets < rate*time + preload */ + if(a_ping_handle->ping_common.cur_time.tv_sec == 0) { + gettimeofday(&a_ping_handle->ping_common.cur_time, NULL); + tokens = a_ping_handle->ping_common.interval * (a_ping_handle->ping_common.preload - 1); + } else { + long ntokens; + struct timeval tv; + + gettimeofday(&tv, NULL); + ntokens = (tv.tv_sec - a_ping_handle->ping_common.cur_time.tv_sec) * 1000 + + (tv.tv_usec - a_ping_handle->ping_common.cur_time.tv_usec) / 1000; + if(!a_ping_handle->ping_common.interval) { + /* Case of unlimited flood is special; + * if we see no reply, they are limited to 100pps */ + if(ntokens < MININTERVAL && in_flight(a_ping_handle) >= a_ping_handle->ping_common.preload) + return MININTERVAL - ntokens; + } + ntokens += tokens; + if(ntokens > a_ping_handle->ping_common.interval * a_ping_handle->ping_common.preload) + ntokens = a_ping_handle->ping_common.interval * a_ping_handle->ping_common.preload; + if(ntokens < a_ping_handle->ping_common.interval) + return a_ping_handle->ping_common.interval - ntokens; + + a_ping_handle->ping_common.cur_time = tv; + tokens = ntokens - a_ping_handle->ping_common.interval; + } + + if(a_ping_handle->ping_common.options & F_OUTSTANDING) { + if(a_ping_handle->ping_common.ntransmitted > 0 && !rcvd_test(a_ping_handle, a_ping_handle->ping_common.ntransmitted)) { + print_timestamp(a_ping_handle); + printf("no answer yet for icmp_seq=%lu\n", (a_ping_handle->ping_common.ntransmitted % MAX_DUP_CHK)); + fflush(stdout); + } + } + + resend: + i = fset->send_probe(a_ping_handle, sock, a_ping_handle->ping_common.outpack, sizeof(a_ping_handle->ping_common.outpack)); + + if(i == 0) { + oom_count = 0; + advance_ntransmitted(a_ping_handle); + if(!(a_ping_handle->ping_common.options & F_QUIET) && (a_ping_handle->ping_common.options & F_FLOOD)) { + /* Very silly, but without this output with + * high preload or pipe size is very confusing. */ + if((a_ping_handle->ping_common.preload < a_ping_handle->ping_common.screen_width && a_ping_handle->ping_common.pipesize < a_ping_handle->ping_common.screen_width) || + in_flight(a_ping_handle) < a_ping_handle->ping_common.screen_width) + write_stdout(".", 1); + } + return a_ping_handle->ping_common.interval - tokens; + } + + /* And handle various errors... */ + if(i > 0) { + /* Apparently, it is some fatal bug. */ + abort(); + } else if(errno == ENOBUFS || errno == ENOMEM) { + int nores_interval; + + /* Device queue overflow or OOM. Packet is not sent. */ + tokens = 0; + /* Slowdown. This works only in adaptive mode (option -A) */ + a_ping_handle->ping_common.rtt_addend += (a_ping_handle->ping_common.rtt < 8 * 50000 ? a_ping_handle->ping_common.rtt / 8 : 50000); + if(a_ping_handle->ping_common.options & F_ADAPTIVE) + update_interval(a_ping_handle); + nores_interval = SCHINT(a_ping_handle->ping_common.interval / 2); + if(nores_interval > 500) + nores_interval = 500; + oom_count++; + if(oom_count * nores_interval < a_ping_handle->ping_common.lingertime) + return nores_interval; + i = 0; + /* Fall to hard error. It is to avoid complete deadlock + * on stuck output device even when dealine was not requested. + * Expected timings are screwed up in any case, but we will + * exit some day. :-) */ + } else if(errno == EAGAIN) { + /* Socket buffer is full. */ + tokens += a_ping_handle->ping_common.interval; + return MININTERVAL; + } else { + if((i = fset->receive_error_msg(a_ping_handle, sock)) > 0) { + /* An ICMP error arrived. In this case, we've received + * an error from sendto(), but we've also received an + * ICMP message, which means the packet did in fact + * send in some capacity. So, in this odd case, report + * the more specific errno as the error, and treat this + * as a hard local error. */ + i = 0; + goto hard_local_error; + } + /* Compatibility with old linuces. */ + if(i == 0 && a_ping_handle->ping_common.confirm_flag && errno == EINVAL) { + a_ping_handle->ping_common.confirm_flag = 0; + errno = 0; + } + if(!errno) + goto resend; + } + + hard_local_error: + /* Hard local error. Pretend we sent packet. */ + advance_ntransmitted(a_ping_handle); + + if(i == 0 && !(a_ping_handle->ping_common.options & F_QUIET)) { + if(a_ping_handle->ping_common.options & F_FLOOD) + write_stdout("E", 1); + else + perror("ping: sendmsg"); + } + tokens = 0; + return SCHINT(a_ping_handle->ping_common.interval); +} + +/* Set socket buffers, "alloc" is an estimate of memory taken by single packet. */ + +void sock_setbufs(ping_handle_t *a_ping_handle, socket_st *sock, int alloc) +{ + int rcvbuf, hold; + socklen_t tmplen = sizeof(hold); + + if(!a_ping_handle->ping_common.sndbuf) + a_ping_handle->ping_common.sndbuf = alloc; + setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, (char *) &a_ping_handle->ping_common.sndbuf, sizeof(a_ping_handle->ping_common.sndbuf)); + + rcvbuf = hold = alloc * a_ping_handle->ping_common.preload; + if(hold < 65536) + hold = 65536; + setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *) &hold, sizeof(hold)); + if(getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *) &hold, &tmplen) == 0) { + if(hold < rcvbuf) + error(0, 0, "WARNING: probably, rcvbuf is not enough to hold preload"); + } +} + +/* Protocol independent setup and parameter checks. */ + +void setup(ping_handle_t *a_ping_handle, socket_st *sock) +{ + int hold; + struct timeval tv; + sigset_t sset; + + if((a_ping_handle->ping_common.options & F_FLOOD) && !(a_ping_handle->ping_common.options & F_INTERVAL)) + a_ping_handle->ping_common.interval = 0; + + if(a_ping_handle->ping_common.uid && a_ping_handle->ping_common.interval < MINUSERINTERVAL) + error(2, 0, "cannot flood; minimal interval allowed for user is %dms", MINUSERINTERVAL); + + if(a_ping_handle->ping_common.interval >= INT_MAX / a_ping_handle->ping_common.preload) + error(2, 0, "illegal preload and/or interval: %d", a_ping_handle->ping_common.interval); + + hold = 1; + if(a_ping_handle->ping_common.options & F_SO_DEBUG) + setsockopt(sock->fd, SOL_SOCKET, SO_DEBUG, (char *) &hold, sizeof(hold)); + if(a_ping_handle->ping_common.options & F_SO_DONTROUTE) + setsockopt(sock->fd, SOL_SOCKET, SO_DONTROUTE, (char *) &hold, sizeof(hold)); + +#ifdef SO_TIMESTAMP + if(!(a_ping_handle->ping_common.options & F_LATENCY)) { + int on = 1; + if(setsockopt(sock->fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) + error(0, 0, "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"); + } +#endif +#ifdef SO_MARK + if(a_ping_handle->ping_common.options & F_MARK) { + int ret; + int errno_save; + + enable_capability_admin(a_ping_handle); + ret = setsockopt(sock->fd, SOL_SOCKET, SO_MARK, &a_ping_handle->ping_common.mark, sizeof(a_ping_handle->ping_common.mark)); + errno_save = errno; + disable_capability_admin(a_ping_handle); + + if(ret == -1) { + /* we probably dont wanna exit since old kernels + * dont support mark .. + */ + error(0, errno_save, "Warning: Failed to set mark: %d", a_ping_handle->ping_common.mark); + } + } +#endif + + /* Set some SNDTIMEO to prevent blocking forever + * on sends, when device is too slow or stalls. Just put limit + * of one second, or "interval", if it is less. + */ + tv.tv_sec = 1; + tv.tv_usec = 0; + if(a_ping_handle->ping_common.interval < 1000) { + tv.tv_sec = 0; + tv.tv_usec = 1000 * SCHINT(a_ping_handle->ping_common.interval); + } + setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (char*) &tv, sizeof(tv)); + + /* Set RCVTIMEO to "interval". Note, it is just an optimization + * allowing to avoid redundant poll(). */ + tv.tv_sec = SCHINT(a_ping_handle->ping_common.interval) / 1000; + tv.tv_usec = 1000 * (SCHINT(a_ping_handle->ping_common.interval) % 1000); + if(setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (char*) &tv, sizeof(tv))) + a_ping_handle->ping_common.options |= F_FLOOD_POLL; + + if(!(a_ping_handle->ping_common.options & F_PINGFILLED)) { + int i; + unsigned char *p = a_ping_handle->ping_common.outpack + 8; + + /* Do not forget about case of small datalen, + * fill timestamp area too! + */ + for(i = 0; i < a_ping_handle->ping_common.datalen; ++i) + *p++ = i; + } + + if(sock->socktype == SOCK_RAW) + a_ping_handle->ping_common.ident = htons(getpid() & 0xFFFF); + + set_signal(SIGINT, sigexit); + set_signal(SIGALRM, sigexit); + set_signal(SIGQUIT, sigstatus); + + sigemptyset(&sset); + sigprocmask(SIG_SETMASK, &sset, NULL); + + gettimeofday(&a_ping_handle->ping_common.start_time, NULL); + + if(a_ping_handle->ping_common.deadline) { + struct itimerval it; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = a_ping_handle->ping_common.deadline; + it.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &it, NULL); + } + + if(isatty(STDOUT_FILENO)) { + struct winsize w; + + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { + if(w.ws_col > 0) + a_ping_handle->ping_common.screen_width = w.ws_col; + } + } +} + +/* + * Return 0 if pattern in payload point to be ptr did not match the pattern that was sent + */ +int contains_pattern_in_payload(ping_handle_t *a_ping_handle, uint8_t *ptr) +{ + int i; + uint8_t *cp, *dp; + + /* check the data */ + cp = ((u_char*) ptr) + sizeof(struct timeval); + dp = &a_ping_handle->ping_common.outpack[8 + sizeof(struct timeval)]; + for(i = sizeof(struct timeval); i < a_ping_handle->ping_common.datalen; ++i, ++cp, ++dp) { + if(*cp != *dp) + return 0; + } + return 1; +} + +void main_loop(ping_handle_t *a_ping_handle, ping_func_set_st *fset, socket_st *sock, uint8_t *packet, int packlen) +{ + char addrbuf[128]; + char ans_data[4096]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *c; + int cc; + int next; + int polling; + int recv_error; + + iov.iov_base = (char *) packet; + + for(;;) { + /* Check exit conditions. */ + if(exiting) + break; + if(a_ping_handle->ping_common.npackets && a_ping_handle->ping_common.nreceived + a_ping_handle->ping_common.nerrors >= a_ping_handle->ping_common.npackets) + break; + if(a_ping_handle->ping_common.deadline && a_ping_handle->ping_common.nerrors) + break; + /* Check for and do special actions. */ + if(status_snapshot) + status(a_ping_handle); + + /* Send probes scheduled to this time. */ + do { + next = pinger(a_ping_handle, fset, sock); + next = schedule_exit(a_ping_handle, next); + } while(next <= 0); + + /* "next" is time to send next probe, if positive. + * If next<=0 send now or as soon as possible. */ + + /* Technical part. Looks wicked. Could be dropped, + * if everyone used the newest kernel. :-) + * Its purpose is: + * 1. Provide intervals less than resolution of scheduler. + * Solution: spinning. + * 2. Avoid use of poll(), when recvmsg() can provide + * timed waiting (SO_RCVTIMEO). */ + polling = 0; + recv_error = 0; + if((a_ping_handle->ping_common.options & (F_ADAPTIVE | F_FLOOD_POLL)) || next < SCHINT(a_ping_handle->ping_common.interval)) { + int recv_expected = in_flight(a_ping_handle); + + /* If we are here, recvmsg() is unable to wait for + * required timeout. */ + if(1000 % HZ == 0 ? next <= 1000 / HZ : (next < INT_MAX / HZ && next * HZ <= 1000)) { + /* Very short timeout... So, if we wait for + * something, we sleep for MININTERVAL. + * Otherwise, spin! */ + if(recv_expected) { + next = MININTERVAL; + } else { + next = 0; + /* When spinning, no reasons to poll. + * Use nonblocking recvmsg() instead. */ + polling = MSG_DONTWAIT; + /* But yield yet. */ + sched_yield(); + } + } + + if(!polling && + ((a_ping_handle->ping_common.options & (F_ADAPTIVE | F_FLOOD_POLL)) || a_ping_handle->ping_common.interval)) { + struct pollfd pset; + pset.fd = sock->fd; + pset.events = POLLIN; + pset.revents = 0; + if(poll(&pset, 1, next) < 1 || + !(pset.revents & (POLLIN | POLLERR))) + continue; + polling = MSG_DONTWAIT; + recv_error = pset.revents & POLLERR; + } + } + + for(;;) { + struct timeval *recv_timep = NULL; + struct timeval recv_time; + int not_ours = 0; /* Raw socket can receive messages + * destined to other running pings. */ + + iov.iov_len = packlen; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = addrbuf; + msg.msg_namelen = sizeof(addrbuf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ans_data; + msg.msg_controllen = sizeof(ans_data); + + cc = recvmsg(sock->fd, &msg, polling); + //log_printf("**recvmsg(fd=%d,msg=0x%x,polling=%d)=%d\n",sock->fd,&msg,polling,cc); + polling = MSG_DONTWAIT; + + if(cc < 0) { + /* If there was a POLLERR and there is no packet + * on the socket, try to read the error queue. + * Otherwise, give up. + */ + if((errno == EAGAIN && !recv_error) || + errno == EINTR) + break; + recv_error = 0; + if(!fset->receive_error_msg(a_ping_handle, sock)) { + if(errno) { + error(0, errno, "recvmsg"); + break; + } + not_ours = 1; + } + } else { + +#ifdef SO_TIMESTAMP + for(c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) { + if(c->cmsg_level != SOL_SOCKET || + c->cmsg_type != SO_TIMESTAMP) + continue; + if(c->cmsg_len < CMSG_LEN(sizeof(struct timeval))) + continue; + recv_timep = (struct timeval*) CMSG_DATA(c); + } +#endif + + if((a_ping_handle->ping_common.options & F_LATENCY) || recv_timep == NULL) { + if((a_ping_handle->ping_common.options & F_LATENCY) || + ioctl(sock->fd, SIOCGSTAMP, &recv_time)) + gettimeofday(&recv_time, NULL); + recv_timep = &recv_time; + } + not_ours = fset->parse_reply(a_ping_handle, sock, &msg, cc, addrbuf, recv_timep); + } + + /* See? ... someone runs another ping on this host. */ + if(not_ours && sock->socktype == SOCK_RAW) + fset->install_filter(a_ping_handle, sock); + + /* If nothing is in flight, "break" returns us to pinger. */ + if(in_flight(a_ping_handle) == 0) + break; + + /* Otherwise, try to recvmsg() again. recvmsg() + * is nonblocking after the first iteration, so that + * if nothing is queued, it will receive EAGAIN + * and return to pinger. */ + } + } + // here present exit() from app + finish(a_ping_handle); +} + +int gather_statistics(ping_handle_t *a_ping_handle, uint8_t *icmph, int icmplen, + int cc, uint16_t seq, int hops, + int csfailed, struct timeval *tv, char *from, + void (*pr_reply)(uint8_t *icmph, int cc)) +{ + int dupflag = 0; + long triptime = 0; + uint8_t *ptr = icmph + icmplen; + + ++a_ping_handle->ping_common.nreceived; + if(!csfailed) + acknowledge(a_ping_handle, seq); + + if(a_ping_handle->ping_common.timing && cc >= (int) (8 + sizeof(struct timeval))) { + struct timeval tmp_tv; + memcpy(&tmp_tv, ptr, sizeof(tmp_tv)); + + restamp: + tvsub(tv, &tmp_tv); + triptime = tv->tv_sec * 1000000 + tv->tv_usec; + if(triptime < 0) { + error(0, 0, "Warning: time of day goes back (%ldus), taking countermeasures", triptime); + triptime = 0; + if(!(a_ping_handle->ping_common.options & F_LATENCY)) { + gettimeofday(tv, NULL); + a_ping_handle->ping_common.options |= F_LATENCY; + goto restamp; + } + } + if(!csfailed) { + a_ping_handle->ping_common.tsum += triptime; + a_ping_handle->ping_common.tsum2 += (long long) triptime * (long long) triptime; + if(triptime < a_ping_handle->ping_common.tmin) + a_ping_handle->ping_common.tmin = triptime; + if(triptime > a_ping_handle->ping_common.tmax) + a_ping_handle->ping_common.tmax = triptime; + if(!a_ping_handle->ping_common.rtt) + a_ping_handle->ping_common.rtt = triptime * 8; + else + a_ping_handle->ping_common.rtt += triptime - a_ping_handle->ping_common.rtt / 8; + if(a_ping_handle->ping_common.options & F_ADAPTIVE) + update_interval(a_ping_handle); + } + } + + if(csfailed) { + ++a_ping_handle->ping_common.nchecksum; + --a_ping_handle->ping_common.nreceived; + } else if(rcvd_test(a_ping_handle, seq)) { + ++a_ping_handle->ping_common.nrepeats; + --a_ping_handle->ping_common.nreceived; + dupflag = 1; + } else { + rcvd_set(a_ping_handle, seq); + dupflag = 0; + } + a_ping_handle->ping_common.confirm = a_ping_handle->ping_common.confirm_flag; + + if(a_ping_handle->ping_common.options & F_QUIET) + return 1; + + if(a_ping_handle->ping_common.options & F_FLOOD) { + if(!csfailed) + write_stdout("\b \b", 3); + else + write_stdout("\bC", 2); + } else { + int i; + uint8_t *cp, *dp; + + print_timestamp(a_ping_handle); + log_printf("%d bytes from %s:", cc, from); + + if(pr_reply) + pr_reply(icmph, cc); + + if(hops >= 0) + log_printf(" ttl=%d", hops); + + if(cc < a_ping_handle->ping_common.datalen + 8) { + log_printf(" (truncated)\n"); + return 1; + } + if(a_ping_handle->ping_common.timing) { + if(triptime >= 100000) + log_printf(" time=%ld ms", (triptime + 500) / 1000); + else if(triptime >= 10000) + log_printf(" time=%ld.%01ld ms", (triptime + 50) / 1000, + ((triptime + 50) % 1000) / 100); + else if(triptime >= 1000) + log_printf(" time=%ld.%02ld ms", (triptime + 5) / 1000, + ((triptime + 5) % 1000) / 10); + else + log_printf(" time=%ld.%03ld ms", triptime / 1000, + triptime % 1000); + log_printf(" tsum=%d ", a_ping_handle->ping_common.tsum); + } + if(dupflag) + log_printf(" (DUP!)"); + if(csfailed) + log_printf(" (BAD CHECKSUM!)"); + + /* check the data */ + cp = ((unsigned char*) ptr) + sizeof(struct timeval); + dp = &a_ping_handle->ping_common.outpack[8 + sizeof(struct timeval)]; + for(i = sizeof(struct timeval); i < a_ping_handle->ping_common.datalen; ++i, ++cp, ++dp) { + if(*cp != *dp) { + log_printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (unsigned char*) ptr + sizeof(struct timeval); + for(i = sizeof(struct timeval); i < a_ping_handle->ping_common.datalen; ++i, ++cp) { + if((i % 32) == sizeof(struct timeval)) + log_printf("\n#%d\t", i); + log_printf("%x ", *cp); + } + break; + } + } + } + return 0; +} +#ifdef PING_DBG +static long llsqrt(long long a) +{ + long long prev = LLONG_MAX; + long long x = a; + + if (x > 0) { + while (x < prev) { + prev = x; + x = (x+(a/x))/2; + } + } + + return (long)x; +} +#endif + +/* + * finish -- + * Print out statistics, and give up. + */ +void finish(ping_handle_t *a_ping_handle) +{ + struct timeval tv = a_ping_handle->ping_common.cur_time; +#ifdef PING_DBG + char *comma = ""; +#endif + + tvsub(&tv, &a_ping_handle->ping_common.start_time); +#ifdef PING_DBG + putchar('\n'); + fflush(stdout); + printf("--- %s ping statistics ---\n", hostname); + printf("%ld packets transmitted, ", ntransmitted); + printf("%ld received", nreceived); + if (nrepeats) + log_printf(", +%ld duplicates", nrepeats); + if (nchecksum) + printf(", +%ld corrupted", nchecksum); + if (nerrors) + printf(", +%ld errors", nerrors); + if (ntransmitted) { +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + printf(", %g%% packet loss", + (float) ((((long long)(ntransmitted - nreceived)) * 100.0) / + ntransmitted)); + printf(", time %ldms", 1000*tv.tv_sec+(tv.tv_usec+500)/1000); + } + putchar('\n'); +#endif + + if(a_ping_handle->ping_common.nreceived && a_ping_handle->ping_common.timing) { + + a_ping_handle->ping_common.tsum /= a_ping_handle->ping_common.nreceived + a_ping_handle->ping_common.nrepeats; + a_ping_handle->ping_common.tsum2 /= a_ping_handle->ping_common.nreceived + a_ping_handle->ping_common.nrepeats; +#ifdef PING_DBG + long tmdev; + tmdev = llsqrt(tsum2 - tsum * tsum); + printf("rtt min/avg/max/mdev = %ld.%03ld/%lu.%03ld/%ld.%03ld/%ld.%03ld ms", + (long)tmin/1000, (long)tmin%1000, + (unsigned long)(tsum/1000), (long)(tsum%1000), + (long)tmax/1000, (long)tmax%1000, + (long)tmdev/1000, (long)tmdev%1000 + ); + comma = ", "; +#endif + } +#ifdef PING_DBG + if (pipesize > 1) { + printf("%spipe %d", comma, pipesize); + comma = ", "; + } + if (nreceived && (!interval || (a_ping_handle->ping_common.options&(F_FLOOD|F_ADAPTIVE))) && ntransmitted > 1) { + int ipg = (1000000*(long long)tv.tv_sec+tv.tv_usec)/(ntransmitted-1); + printf("%sipg/ewma %d.%03d/%d.%03d ms", + comma, ipg/1000, ipg%1000, rtt/8000, (rtt/8)%1000); + } + putchar('\n'); + //exit(!nreceived || (deadline && nreceived < npackets)); +#endif +} + +void status(ping_handle_t *a_ping_handle) +{ + int loss = 0; + long tavg = 0; + + status_snapshot = 0; + + if(a_ping_handle->ping_common.ntransmitted) + loss = (((long long) (a_ping_handle->ping_common.ntransmitted - a_ping_handle->ping_common.nreceived)) * 100) / a_ping_handle->ping_common.ntransmitted; + + fprintf(stderr, "\r%ld/%ld packets, %d%% loss", a_ping_handle->ping_common.nreceived, a_ping_handle->ping_common.ntransmitted, loss); + + if(a_ping_handle->ping_common.nreceived && a_ping_handle->ping_common.timing) { + tavg = a_ping_handle->ping_common.tsum / (a_ping_handle->ping_common.nreceived + a_ping_handle->ping_common.nrepeats); + + fprintf(stderr, ", min/avg/ewma/max = %ld.%03ld/%lu.%03ld/%d.%03d/%ld.%03ld ms", + (long) a_ping_handle->ping_common.tmin / 1000, (long) a_ping_handle->ping_common.tmin % 1000, + tavg / 1000, tavg % 1000, + a_ping_handle->ping_common.rtt / 8000, (a_ping_handle->ping_common.rtt / 8) % 1000, + (long) a_ping_handle->ping_common.tmax / 1000, (long) a_ping_handle->ping_common.tmax % 1000 + ); + } + fprintf(stderr, "\n"); +} + +inline int is_ours(ping_handle_t *a_ping_handle, socket_st *sock, uint16_t id) { + return sock->socktype == SOCK_DGRAM || id == a_ping_handle->ping_common.ident; +} diff --git a/libdap-chain-net/iputils/tracepath.c b/libdap-chain-net/iputils/tracepath.c new file mode 100755 index 0000000000000000000000000000000000000000..2dce76420ca7396496ba18068819f85011b0a0a7 --- /dev/null +++ b/libdap-chain-net/iputils/tracepath.c @@ -0,0 +1,783 @@ +/* + * tracepath.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 + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <limits.h> +#include <time.h> +#include <linux/errqueue.h> +#include <linux/icmp.h> +#include <linux/icmpv6.h> +#include <linux/types.h> +#include <netdb.h> +#include <netinet/in.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <unistd.h> +//#include <glib.h> + +#include "iputils.h" + +#ifdef USE_IDN +# include <locale.h> +# ifndef AI_IDN +# define AI_IDN 0x0040 +# endif +# ifndef NI_IDN +# define NI_IDN 32 +# endif +# define getnameinfo_flags NI_IDN +#else +# define getnameinfo_flags 0 +#endif + +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif + +#ifndef IP_PMTUDISC_DO +# define IP_PMTUDISC_DO 3 +#endif +#ifndef IPV6_PMTUDISC_DO +# define IPV6_PMTUDISC_DO 3 +#endif + +enum { + MAX_PROBES = 10, + + MAX_HOPS_DEFAULT = 30, + MAX_HOPS_LIMIT = 255, + + HOST_COLUMN_SIZE = 52, + + HIS_ARRAY_SIZE = 64, + + DEFAULT_OVERHEAD_IPV4 = 28, + DEFAULT_OVERHEAD_IPV6 = 48, + + DEFAULT_MTU_IPV4 = 65535, + DEFAULT_MTU_IPV6 = 128000, + + DEFAULT_BASEPORT = 44444, + + ANCILLARY_DATA_LEN = 512, +}; + +struct hhistory { + int hops; + struct timeval sendtime; + struct timeval deltatime; + int hop; + char *host_namea; + char *host_nameb; +}; + +struct probehdr { + uint32_t ttl; + struct timeval tv; +}; + +struct run_state { + struct hhistory his[HIS_ARRAY_SIZE]; + int hisptr; + struct sockaddr_storage target; + struct addrinfo *ai; + int socket_fd; + socklen_t targetlen; + uint16_t base_port; + uint8_t ttl; + int max_hops; + int overhead; + int mtu; + void *pktbuf; + int hops_to; + int hops_from; + unsigned int + no_resolve :1, + show_both :1, + mapped :1; +}; + +/* + * All includes, definitions, struct declarations, and global variables are + * above. After this comment all you can find is functions. + */ + +static void data_wait(struct run_state const * const ctl) +{ + fd_set fds; + struct timeval tv = { + .tv_sec = 1, + .tv_usec = 0 + }; + + FD_ZERO(&fds); + FD_SET(ctl->socket_fd, &fds); + select(ctl->socket_fd + 1, &fds, NULL, NULL, &tv); +} + +static void print_host(struct run_state const * const ctl, char const * const a, + char const * const b) +{ + int plen; + + plen = log_printf("%s", a); + if(ctl->show_both) + plen += log_printf(" (%s)", b); + if(plen >= HOST_COLUMN_SIZE) + plen = HOST_COLUMN_SIZE - 1; + log_printf("%*s", HOST_COLUMN_SIZE - plen, ""); +} + +static int recverr(struct run_state * const ctl) +{ + ssize_t recv_size; + struct probehdr rcvbuf; + char cbuf[ANCILLARY_DATA_LEN]; + struct cmsghdr *cmsg; + struct sock_extended_err *e; + struct sockaddr_storage addr; + struct timeval tv; + struct timeval *rettv; + struct timeval *deltatv; + int slot = 0; + int rethops; + int sndhops; + int progress = -1; + int broken_router; + char hnamebuf[NI_MAXHOST] = ""; + struct iovec iov = { + .iov_base = &rcvbuf, + .iov_len = sizeof(rcvbuf) + }; + struct msghdr msg; + const struct msghdr reset = { + .msg_name = (uint8_t *) &addr, + .msg_namelen = sizeof(addr), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + 0 + }; + + restart: + memset(&rcvbuf, -1, sizeof(rcvbuf)); + msg = reset; + + gettimeofday(&tv, NULL); + recv_size = recvmsg(ctl->socket_fd, &msg, MSG_ERRQUEUE); + if(recv_size < 0) { + if(errno == EAGAIN) + return progress; + goto restart; + } + + progress = ctl->mtu; + + rethops = -1; + sndhops = -1; + e = NULL; + rettv = NULL; + deltatv = NULL; + broken_router = 0; + + slot = -ctl->base_port; + switch (ctl->ai->ai_family) { + case AF_INET6: + slot += ntohs(((struct sockaddr_in6 *) &addr)->sin6_port); + break; + case AF_INET: + slot += ntohs(((struct sockaddr_in *) &addr)->sin_port); + break; + } + + if(slot >= 0 && slot < (HIS_ARRAY_SIZE - 1) && ctl->his[slot].hops) { + sndhops = ctl->his[slot].hops; + rettv = &ctl->his[slot].sendtime; + deltatv = &ctl->his[slot].deltatime; + ctl->his[slot].hop = sndhops; + ctl->his[slot].hops = 0; + } + if(recv_size == sizeof(rcvbuf)) { + if(rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) + broken_router = 1; + else { + sndhops = rcvbuf.ttl; + rettv = &rcvbuf.tv; + } + } + + for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + switch (cmsg->cmsg_level) { + case SOL_IPV6: + switch (cmsg->cmsg_type) { + case IPV6_RECVERR: + e = (struct sock_extended_err *) CMSG_DATA(cmsg); + break; + case IPV6_HOPLIMIT: + #ifdef IPV6_2292HOPLIMIT + case IPV6_2292HOPLIMIT: + #endif + memcpy(&rethops, CMSG_DATA(cmsg), sizeof(rethops)); + break; + default: + log_printf("cmsg6:%d\n ", cmsg->cmsg_type); + } + break; + case SOL_IP: + switch (cmsg->cmsg_type) { + case IP_RECVERR: + e = (struct sock_extended_err *) CMSG_DATA(cmsg); + break; + case IP_TTL: + rethops = *(uint8_t *) CMSG_DATA(cmsg); + break; + default: + log_printf("cmsg4:%d\n ", cmsg->cmsg_type); + } + } + } + if(e == NULL) { + log_printf("no info\n"); + return 0; + } + if(e->ee_origin == SO_EE_ORIGIN_LOCAL) + log_printf("%2d?: %-32s ", ctl->ttl, "[LOCALHOST]"); + else if(e->ee_origin == SO_EE_ORIGIN_ICMP6 || + e->ee_origin == SO_EE_ORIGIN_ICMP) { + char abuf[NI_MAXHOST]; + struct sockaddr *sa = (struct sockaddr *) (e + 1); + socklen_t salen; + + if(sndhops > 0) + log_printf("%2d: ", sndhops); + else + log_printf("%2d?: ", ctl->ttl); + + switch (sa->sa_family) { + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + break; + case AF_INET: + salen = sizeof(struct sockaddr_in); + break; + default: + salen = 0; + } + + if(ctl->no_resolve || ctl->show_both) + { + if(getnameinfo(sa, salen, abuf, sizeof(abuf), NULL, 0, + NI_NUMERICHOST)) + strcpy(abuf, "???"); + ctl->his[slot].host_namea = strdup(abuf); + } else + abuf[0] = 0; + + if(!ctl->no_resolve || ctl->show_both) + { + fflush(stdout); + if(getnameinfo(sa, salen, hnamebuf, sizeof hnamebuf, NULL, 0, + getnameinfo_flags)) + strcpy(hnamebuf, "???"); + ctl->his[slot].host_nameb = strdup(hnamebuf); + } else + hnamebuf[0] = 0; + + if(ctl->no_resolve) { + print_host(ctl, abuf, hnamebuf); + } + else { + print_host(ctl, hnamebuf, abuf); + } + } + + if(rettv) { + struct timeval res; + + timersub(&tv, rettv, &res); + if(deltatv) + memcpy(deltatv, &res, sizeof(struct timeval)); + log_printf("%3ld.%03ldms ", res.tv_sec * 1000 + res.tv_usec / 1000, res.tv_usec % 1000); + if(broken_router) + log_printf("(This broken router returned corrupted payload) "); + } + + if(rethops <= 64) + rethops = 65 - rethops; + else if(rethops <= 128) + rethops = 129 - rethops; + else + rethops = 256 - rethops; + + //log_printf("ERROR=%d ", e->ee_errno); + switch (e->ee_errno) { + case ETIMEDOUT: + log_printf("\n"); + break; + case EMSGSIZE: + log_printf("pmtu %d\n", e->ee_info); + ctl->mtu = e->ee_info; + progress = ctl->mtu; + break; + case ECONNREFUSED: + log_printf("reached\n"); + ctl->hops_to = sndhops < 0 ? ctl->ttl : sndhops; + ctl->hops_from = rethops; + return 0; + case EPROTO: + log_printf("!P\n"); + return 0; + case EHOSTUNREACH: + if((e->ee_origin == SO_EE_ORIGIN_ICMP && + e->ee_type == ICMP_TIME_EXCEEDED && + e->ee_code == ICMP_EXC_TTL) || + (e->ee_origin == SO_EE_ORIGIN_ICMP6 && + e->ee_type == ICMPV6_TIME_EXCEED && + e->ee_code == ICMPV6_EXC_HOPLIMIT)) { + if(rethops >= 0) { + if(sndhops >= 0 && rethops != sndhops) + log_printf("asymm %2d ", rethops); + else if(sndhops < 0 && rethops != ctl->ttl) + log_printf("asymm %2d ", rethops); + } + //log_printf("hops=->%2d, <-%2d ", sndhops, rethops); + log_printf("\n"); + break; + } + printf("!H\n"); + return 0; + case ENETUNREACH: + log_printf("!N\n"); + return 0; + case EACCES: + log_printf("!A\n"); + return 0; + default: + printf("\n"); + errno = e->ee_errno; + perror("NET ERROR"); + return 0; + } + goto restart; + return 0; +} + +static int probe_ttl(struct run_state * const ctl) +{ + int i; + struct probehdr *hdr = ctl->pktbuf; + + memset(ctl->pktbuf, 0, ctl->mtu); + restart: + for(i = 0; i < MAX_PROBES; i++) { + int res; + + hdr->ttl = ctl->ttl; + switch (ctl->ai->ai_family) { + case AF_INET6: + ((struct sockaddr_in6 *) &ctl->target)->sin6_port = + htons(ctl->base_port + ctl->hisptr); + break; + case AF_INET: + ((struct sockaddr_in *) &ctl->target)->sin_port = + htons(ctl->base_port + ctl->hisptr); + break; + } + gettimeofday(&hdr->tv, NULL); + ctl->his[ctl->hisptr].hops = ctl->ttl; + ctl->his[ctl->hisptr].sendtime = hdr->tv; + if(sendto(ctl->socket_fd, ctl->pktbuf, ctl->mtu - ctl->overhead, 0, + (struct sockaddr *) &ctl->target, ctl->targetlen) > 0) + break; + res = recverr(ctl); + ctl->his[ctl->hisptr].hops = 0; + if(res == 0) + return 0; + if(res > 0) + goto restart; + } + ctl->hisptr = (ctl->hisptr + 1) & (HIS_ARRAY_SIZE - 1); + + if(i < MAX_PROBES) { + data_wait(ctl); + if(recv(ctl->socket_fd, ctl->pktbuf, ctl->mtu, MSG_DONTWAIT) > 0) { + log_printf("%2d?: reply received 8)\n", ctl->ttl); + return 0; + } + return recverr(ctl); + } + + log_printf("%2d: send failed\n", ctl->ttl); + return 0; +} + +static void usage(void) +{ + fprintf(stderr, + "\nUsage\n" + " tracepath [options] <destination>\n" + "\nOptions:\n" + " -4 use IPv4\n" + " -6 use IPv6\n" + " -b print both name and ip\n" + " -l <length> use packet <length>\n" + " -m <hops> use maximum <hops>\n" + " -n no dns name resolution\n" + " -p <port> use destination <port>\n" + " -V print version and exit\n" + " <destination> dns name or ip address\n" + "\nFor more details see tracepath(8).\n"); + exit(-1); +} + +int tracepath_main(int argc, char **argv, struct run_state *ctl) +{ + int ret = -1; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, +#ifdef USE_IDN + .ai_flags = AI_IDN | AI_CANONNAME, +#endif + }; + struct addrinfo *result; + int ch; + int status; + int on; + char *p; + char pbuf[NI_MAXSERV]; + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + + /* Support being called using `tracepath4` or `tracepath6` symlinks */ + if(argv[0][strlen(argv[0]) - 1] == '4') + hints.ai_family = AF_INET; + else if(argv[0][strlen(argv[0]) - 1] == '6') + hints.ai_family = AF_INET6; + + while((ch = getopt(argc, argv, "46nbh?l:m:p:V")) != EOF) { + switch (ch) { + case '4': + if(hints.ai_family == AF_INET6) { + fprintf(stderr, + "tracepath: Only one -4 or -6 option may be specified\n"); + return -1; //exit(2); + } + hints.ai_family = AF_INET; + break; + case '6': + if(hints.ai_family == AF_INET) { + fprintf(stderr, + "tracepath: Only one -4 or -6 option may be specified\n"); + return -1; //exit(2); + } + hints.ai_family = AF_INET6; + break; + case 'n': + ctl->no_resolve = 1; + break; + case 'b': + ctl->show_both = 1; + break; + case 'l': + if((ctl->mtu = atoi(optarg)) <= ctl->overhead) { + fprintf(stderr, + "Error: pktlen must be > %d and <= %d.\n", + ctl->overhead, INT_MAX); + return -1; //exit(1); + } + break; + case 'm': + ctl->max_hops = atoi(optarg); + if(ctl->max_hops < 0 || ctl->max_hops > MAX_HOPS_LIMIT) { + fprintf(stderr, + "Error: max hops must be 0 .. %d (inclusive).\n", + MAX_HOPS_LIMIT); + return -1; //exit(1); + } + break; + case 'p': + ctl->base_port = atoi(optarg); + break; + case 'V': + printf(IPUTILS_VERSION("tracepath")); + return 0; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if(argc != 1) + usage(); + + /* Backward compatibility */ + if(!ctl->base_port) { + p = strchr(argv[0], '/'); + if(p) { + *p = 0; + ctl->base_port = atoi(p + 1); + } else + ctl->base_port = DEFAULT_BASEPORT; + } + sprintf(pbuf, "%u", ctl->base_port); + + status = getaddrinfo(argv[0], pbuf, &hints, &result); + if(status) { + fprintf(stderr, "tracepath: %s: %s\n", argv[0], + gai_strerror(status)); + return -EADDRNOTAVAIL;//exit(1); + } + + for(ctl->ai = result; ctl->ai; ctl->ai = ctl->ai->ai_next) { + if(ctl->ai->ai_family != AF_INET6 && ctl->ai->ai_family != AF_INET) + continue; + ctl->socket_fd = socket(ctl->ai->ai_family, ctl->ai->ai_socktype, ctl->ai->ai_protocol); + if(ctl->socket_fd < 0) + continue; + memcpy(&ctl->target, ctl->ai->ai_addr, ctl->ai->ai_addrlen); + ctl->targetlen = ctl->ai->ai_addrlen; + break; + } + if(ctl->socket_fd < 0) { + perror("socket/connect"); + return -ESOCKTNOSUPPORT;//exit(1); + } + + switch (ctl->ai->ai_family) { + case AF_INET6: + ctl->overhead = DEFAULT_OVERHEAD_IPV6; + if(!ctl->mtu) + ctl->mtu = DEFAULT_MTU_IPV6; + if(ctl->mtu <= ctl->overhead) + goto pktlen_error; + + on = IPV6_PMTUDISC_DO; + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on)) && + (on = IPV6_PMTUDISC_DO, setsockopt(ctl->socket_fd, SOL_IPV6, + IPV6_MTU_DISCOVER, &on, sizeof(on)))) { + perror("IPV6_MTU_DISCOVER"); + return -2;//exit(1); + } + on = 1; + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) { + perror("IPV6_RECVERR"); + return -3;//exit(1); + } + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) + #ifdef IPV6_RECVHOPLIMIT + && setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)) + #endif + ) { + perror("IPV6_HOPLIMIT"); + return -4;//exit(1); + } + if(!IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 * )&ctl->target)->sin6_addr))) + break; + ctl->mapped = 1; + /*FALLTHROUGH*/ + case AF_INET: + ctl->overhead = DEFAULT_OVERHEAD_IPV4; + if(!ctl->mtu) + ctl->mtu = DEFAULT_MTU_IPV4; + if(ctl->mtu <= ctl->overhead) + goto pktlen_error; + + on = IP_PMTUDISC_DO; + if(setsockopt(ctl->socket_fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) { + perror("IP_MTU_DISCOVER"); + return -5;//exit(1); + } + on = 1; + if(setsockopt(ctl->socket_fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) { + perror("IP_RECVERR"); + return -6;//exit(1); + } + if(setsockopt(ctl->socket_fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { + perror("IP_RECVTTL"); + return -7;//exit(1); + } + } + + ctl->pktbuf = malloc(ctl->mtu); + if(!ctl->pktbuf) { + perror("malloc"); + return -8; //exit(1); + } + +// struct sockaddr sa; +// int salen6 = sizeof(struct sockaddr_in6); +// int salen = sizeof(struct sockaddr_in); + char target_ip[NI_MAXHOST]; + char *target_name = argv[0]; + memset(&target_ip, 0, sizeof(target_ip)); + getnameinfo((struct sockaddr *) &ctl->target, ctl->targetlen, target_ip, sizeof(target_ip), NULL, 0, + NI_NUMERICHOST); + log_printf("START target_name=%s target_ip=%s\n", target_name, target_ip); + + for(ctl->ttl = 1; ctl->ttl <= ctl->max_hops; ctl->ttl++) { + int res; + int i; + + on = ctl->ttl; + switch (ctl->ai->ai_family) { + case AF_INET6: + if(setsockopt(ctl->socket_fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) { + perror("IPV6_UNICAST_HOPS"); + return -9; //exit(1); + } + if(!ctl->mapped) + break; + /*FALLTHROUGH*/ + case AF_INET: + if(setsockopt(ctl->socket_fd, SOL_IP, IP_TTL, &on, sizeof(on))) { + perror("IP_TTL"); + return -10; //exit(1); + } + } + + restart: + for(i = 0; i < 2; i++) { + int old_mtu; + + old_mtu = ctl->mtu; + res = probe_ttl(ctl); + if(ctl->mtu != old_mtu) + goto restart; + if(res == 0) + goto done; + if(res > 0) + break; + } + // if already find need name, make ret>=0 and break + { + int i = ctl->hisptr - 1; + //for(int i = 0; i < MAX_HOPS_DEFAULT; i++) + { + char *host_namea = (ctl->his[i].host_namea); + char *host_nameb = (ctl->his[i].host_nameb); + if(host_namea) { + if(!strcmp(host_namea, target_name) || !strcmp(host_namea, target_ip)) { + ctl->ttl = ctl->max_hops; + ret = i; + break; + } + } + if(host_nameb) { + if(!strcmp(host_nameb, target_name) || !strcmp(host_nameb, target_ip)) { + ctl->ttl = ctl->max_hops; + ret = i; + break; + } + } + } + } + if(res < 0) + log_printf("%2d: no reply\n", ctl->ttl); + } + log_printf(" Too many hops: pmtu %d\n", ctl->mtu); + + done: + freeaddrinfo(result); + + log_printf(" Resume: pmtu %d ", ctl->mtu); + if(ctl->hops_to >= 0) + { + ret = ctl->hisptr - 1; + log_printf("hops %d ", ctl->hops_to); + } + if(ctl->hops_from >= 0) + log_printf("back %d ", ctl->hops_from); + log_printf("\n"); + return ret; //exit(0); + + pktlen_error: + fprintf(stderr, "Error: pktlen must be > %d and <= %d\n", + ctl->overhead, INT_MAX); + return -1; //exit(1); +} + +/** + * Tracepath host + * + * @addr[in] host name or IP address + * @hops[out] hops count + * @time_usec[out] latency in microseconds + * @return 0 Ok, -1 error + */ +int tracepath_util(const char *addr, int *hops, int *time_usec) +{ + int type = 4; // 4 or 6 + int total_hops = 0; + long int total_time_usec = 0; // latency in microseconds + int argc = 4; + const char *argv[argc]; + if(type != 4) + argv[0] = "tracepath6"; + else + argv[0] = "tracepath4"; + argv[1] = "-b"; // print both name and ip + argv[2] = "-m 26"; // -n -m 16 + argv[3] = addr; + struct run_state ctl = { + .max_hops = MAX_HOPS_DEFAULT, + .hops_to = -1, + .hops_from = -1, + 0 + }; + int ret = tracepath_main(argc, (char**) argv, &ctl); + for(int i = 0; i < MAX_HOPS_DEFAULT; i++) { + free(ctl.his[i].host_namea); + free(ctl.his[i].host_nameb); + } + if(ret >= 0) + { + struct timeval *deltatime = &(ctl.his[ret].deltatime); + total_hops = ctl.his[ret].hop; + total_time_usec = deltatime->tv_sec * 1000000 + deltatime->tv_usec; + } + else + for(int i = 0; i < MAX_HOPS_DEFAULT; i++) { + struct timeval *deltatime = &(ctl.his[i].deltatime); + if(ctl.his[i].hop) { //if(!ctl.his[i].hops && (deltatime->tv_sec > 0 || deltatime->tv_usec > 0)) { + total_hops = ctl.his[i].hop; + total_time_usec = deltatime->tv_sec * 1000000 + deltatime->tv_usec; + } + /*if(ctl.his[i].hop > 0) + { + char *host_name = (ctl.his[i].host_name) ? ctl.his[i].host_name : "-"; + printf("%d %d: %s %ld\n", ctl.his[i].hops, ctl.his[i].hop, host_name, + deltatime->tv_sec * 1000000 + deltatime->tv_usec); + }*/ + } + if(hops) { + *hops = total_hops; + } + if(time_usec) { + *time_usec = total_time_usec; + } + return (ret >= 0) ? 0 : ret; + +} + diff --git a/libdap-chain-net/iputils/traceroute/as_lookups.c b/libdap-chain-net/iputils/traceroute/as_lookups.c new file mode 100644 index 0000000000000000000000000000000000000000..873180375802bce154e491966ea68f81eab98522 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/as_lookups.c @@ -0,0 +1,128 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include "traceroute.h" + + +#define DEF_RADB_SERVER "whois.radb.net" +#define DEF_RADB_SERVICE "nicname" + + +static sockaddr_any ra_addr = {{ 0, }, }; +static char ra_buf[512] = { 0, }; + + +const char *get_as_path (const char *query) { + int sk, n; + FILE *fp; + char buf[1024]; + int prefix = 0, best_prefix = 0; + char *rb, *re = &ra_buf[sizeof (ra_buf) / sizeof (*ra_buf) - 1]; + + + if (!ra_addr.sa.sa_family) { + const char *server, *service; + struct addrinfo *res; + int ret; + + server = getenv ("RA_SERVER"); + if (!server) server = DEF_RADB_SERVER; + + service = getenv ("RA_SERVICE"); + if (!service) service = DEF_RADB_SERVICE; + + + ret = getaddrinfo (server, service, NULL, &res); + if (ret) { + fprintf (stderr, "%s/%s: %s\n", server, service, + gai_strerror(ret)); + exit (2); + } + + memcpy (&ra_addr, res->ai_addr, res->ai_addrlen); + + freeaddrinfo (res); + } + + + sk = socket (ra_addr.sa.sa_family, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + if (connect (sk, &ra_addr.sa, sizeof (ra_addr)) < 0) + goto err_sk; + + n = snprintf (buf, sizeof (buf), "%s\r\n", query); + if (n >= (int)sizeof (buf)) goto err_sk; + + if (write (sk, buf, n) < n) + goto err_sk; + + fp = fdopen (sk, "r"); + if (!fp) goto err_sk; + + + strcpy (ra_buf, "*"); + rb = ra_buf; + + while (fgets (buf, sizeof (buf), fp) != NULL) { + + if (!strncmp (buf, "route:", sizeof ("route:") - 1) || + !strncmp (buf, "route6:", sizeof ("route6:") - 1) + ) { + char *p = strchr (buf, '/'); + + if (p) prefix = strtoul (++p, NULL, 10); + else prefix = 0; /* Hmmm... */ + + } + else if (!strncmp (buf, "origin:", sizeof ("origin:") -1)) { + char *p, *as; + + p = buf + (sizeof ("origin:") - 1); + + while (isspace (*p)) p++; + as = p; + while (*p && !isspace (*p)) p++; + *p = '\0'; + + if (prefix > best_prefix) { + best_prefix = prefix; + + rb = ra_buf; + while (rb < re && (*rb++ = *as++)) ; + } + else if (prefix == best_prefix) { + char *q = strstr (ra_buf, as); + + if (!q || (*(q += strlen (as)) != '\0' && *q != '/')) { + if (rb > ra_buf) rb[-1] = '/'; + while (rb < re && (*rb++ = *as++)) ; + } + } + /* else just ignore it */ + } + } + + fclose (fp); + + return ra_buf; + + +err_sk: + close (sk); + return "!!"; +} diff --git a/libdap-chain-net/iputils/traceroute/clif.c b/libdap-chain-net/iputils/traceroute/clif.c new file mode 100644 index 0000000000000000000000000000000000000000..4380e8ecd061e5becee1f296c89a8c8bbf127b51 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/clif.c @@ -0,0 +1,1284 @@ +/* + Copyright (c) 2000, 2003 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: LGPL v2.1 or any later + + See COPYING.LIB for the status of this software. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#include "../iputils.h" +#include "clif.h" + +#if 1 /* Bad idea, anyway... */ +#define MAX_ARGC_NUMBER 256 +typedef unsigned char _CLIF_index; +#else +#define MAX_ARGC_NUMBER (4096 / 5 + 1) /* POSIX ARG_MAX >= 4096 ... */ +typedef unsigned short _CLIF_index; +#endif + +/* This is needed for some print info functions. + This is ugly for thread-safe (is it really actual on program invoking?), + and for several CLIF_parse_cmdline invoking... But foo on this. Yeah... + */ +static struct { + int argc; + char **argv; + CLIF_option *option_list; + CLIF_argument *argument_list; + unsigned int parse_flags; +} curr = { 0, }; + +static void err_report(const char *format, ...) { + va_list ap; + + if(curr.parse_flags & CLIF_SILENT) + return; + + va_start(ap, format); + + vfprintf(stderr, format, ap); + + va_end(ap); + + fprintf(stderr, "\n"); + + return; +} + +/* info generation stuff... */ + +#define SHORT_PLUS_MINUS "+/-" +#define LONG_PLUS_MINUS "++/--" +#define EXCL_DLM " | " + +static char *show_short(const CLIF_option *optn) { + static char buf[80]; + char *p = buf; + unsigned int flags = optn->flags | curr.parse_flags; + + if(optn->function_plus) { + if(!optn->function) + *p++ = '+'; + else { + strcpy(p, SHORT_PLUS_MINUS); + p += sizeof(SHORT_PLUS_MINUS) - 1; + } + } else + *p++ = '-'; + + *p++ = optn->short_opt[0]; + + if(optn->arg_name) { + char *endp = buf + sizeof(buf) - sizeof(",...]"); + const char *s; + + if(!(flags & _CLIF_STRICT_JOIN_ARG)) + *p++ = ' '; + if(flags & CLIF_OPTARG) + *p++ = '['; + + s = optn->arg_name; + while(*s && p < endp) + *p++ = *s++; + + if(flags & CLIF_SEVERAL) { + strcpy(p, ",..."); + p += sizeof(",...") - 1; /* last '\0' ... */ + } + + if(flags & CLIF_OPTARG) + *p++ = ']'; + } + + *p = '\0'; + + return buf; +} + +static char *show_long(const CLIF_option *optn) { + static char buf[80]; + char *p = buf; + char *endp; + const char *s; + unsigned int flags = optn->flags | curr.parse_flags; + + if(!(flags & _CLIF_STRICT_KEYWORD)) { + + if(!(flags & _CLIF_STRICT_ONEDASH)) { + if(optn->function_plus) { + if(!optn->function) { + *p++ = '+'; + *p++ = '+'; + } + else { + strcpy(p, LONG_PLUS_MINUS); + p += sizeof(LONG_PLUS_MINUS) - 1; + } + } else { + *p++ = '-'; + *p++ = '-'; + } + + } else { + if(optn->function_plus) { + if(!optn->function) + *p++ = '+'; + else { + strcpy(p, SHORT_PLUS_MINUS); + p += sizeof(SHORT_PLUS_MINUS) - 1; + } + } else + *p++ = '-'; + } + } + + s = optn->long_opt; + endp = buf + sizeof(buf) - sizeof(" ["); + while(*s && p < endp) + *p++ = *s++; + + if(optn->arg_name) { + + if(flags & _CLIF_STRICT_NOEQUAL) { + *p++ = ' '; + if(flags & CLIF_OPTARG) + *p++ = '['; + } else { + if(flags & CLIF_OPTARG) + *p++ = '['; + *p++ = '='; + } + + s = optn->arg_name; + endp = buf + sizeof(buf) - sizeof(",...]"); + while(*s && p < endp) + *p++ = *s++; + + if(flags & CLIF_SEVERAL) { + strcpy(p, ",..."); + p += sizeof(",...") - 1; /* last '\0' ... */ + } + + if(flags & CLIF_OPTARG) + *p++ = ']'; + } + + *p = '\0'; + + return buf; +} + +static char *show_excl(const CLIF_option *option_list, int *cnt_p) { + static char buf[256]; + const CLIF_option *optn; + char *p = buf; + char *endp = buf + sizeof(buf) - sizeof(EXCL_DLM); + int excl_cnt = 0; + + *p = '\0'; + if(cnt_p) + *cnt_p = 0; + if(!option_list) + return buf; + + for(optn = option_list; optn->short_opt || optn->long_opt; optn++) { + char *s; + + if(!(optn->flags & CLIF_EXCL)) + continue; + + if(optn->short_opt) + s = show_short(optn); + else + s = show_long(optn); + + if(excl_cnt > 0) { /* i.e., second etc... */ + strcpy(p, EXCL_DLM); + p += sizeof(EXCL_DLM) - 1; + } + + while(*s && p < endp) + *p++ = *s++; + + excl_cnt++; + } + + *p = '\0'; + + if(cnt_p) + *cnt_p = excl_cnt; + + return buf; +} + +static int is_keyword(const CLIF_option *optn) { + unsigned int flags = optn->flags | curr.parse_flags; + + return (flags & _CLIF_STRICT_KEYWORD) != 0; +} + +static void err_bad_opt(const char *arg, char c, int n) { + char sym = (*arg == '+') ? '+' : '-'; + + if(c) + err_report("Bad option `%c%c' (argc %d)", sym, c, n); + else { + char *p = strchr(arg, '='); + const char *type = (*arg == sym) ? "option" : "keyword"; + + if(p) + err_report("Bad %s `%s' (with arg `%s') (argc %d)", + type, arg, p + 1, n); + else + err_report("Bad %s `%s' (argc %d)", type, arg, n); + } +} + +static void err_bad_arg(const CLIF_option *optn, char c, int n) { + CLIF_option tmp = *optn; + char ss[80]; + char *s; + + tmp.arg_name = NULL; + + if(c) { + s = show_short(&tmp); /* always without arg... */ + strncpy(ss, s, sizeof(ss)); + s = show_short(optn); + } else { + s = show_long(&tmp); /* always without arg... */ + strncpy(ss, s, sizeof(ss)); + s = show_long(optn); + } + + err_report("%s `%s' (argc %d) requires an argument: `%s'", + (c || !is_keyword(optn)) ? "Option" : "Keyword", ss, n, s); +} + +static void err_bad_res(const CLIF_option *optn, char c, + const char *opt_arg, int n) { + CLIF_option tmp = *optn; + char *ss; + const char *type; + + tmp.arg_name = NULL; + + if(c) { + ss = show_short(&tmp); + type = "option"; + } else { + ss = show_long(&tmp); + type = is_keyword(optn) ? "keyword" : "option"; + } + + if(optn->arg_name) + err_report("Cannot handle `%s' %s with arg `%s' (argc %d)", + ss, type, opt_arg, n); + else + err_report("Cannot handle `%s' %s (argc %d)", ss, type, n); +} + +static void err_bad_excl(const CLIF_option *optn, char c, int n) { + CLIF_option tmp = *optn; + char *ss; + char *excl = show_excl(curr.option_list, 0); + /* Note: show_(short|long)() nested!!! */ + + tmp.arg_name = NULL; + + if(c) + ss = show_short(&tmp); + else + ss = show_long(&tmp); + + err_report("%s `%s' (argc %d): Only one of:\n %s\n" + "may be specified.", + (c || !is_keyword(optn)) ? "Option" : "Keyword", + ss, n, excl); +} + +static CLIF_option *find_long(char *arg, char **arg_p, + unsigned int match, unsigned int nomatch) { + CLIF_option *optn; + CLIF_option *abbrev = NULL; + char *abbrev_arg = NULL; + int abbrev_found = 0; + + for(optn = curr.option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + char *a; + const char *o; + unsigned int flags; + + if(!optn->long_opt) + continue; + + flags = curr.parse_flags | optn->flags; + if(flags & nomatch) + continue; + if(match && !(flags & match)) + continue; /* XXX: optimize it */ + + for(a = arg, o = optn->long_opt; *o && *a == *o; a++, o++) + ; + + if(*a == '\0' || + (*a == '=' && optn->arg_name && !(flags & _CLIF_STRICT_NOEQUAL)) + ) { /* looks like end of option... */ + + if(!*o) { /* explicit match found */ + if(*a == '=' && arg_p) + *arg_p = a + 1; + return optn; + } + + if((flags & CLIF_ABBREV) && + (a - arg >= CLIF_MIN_ABBREV) + ) { + if(!abbrev_found) { + abbrev_found = 1; + abbrev = optn; + if(*a == '=') + abbrev_arg = a + 1; + } else + /* several possibility case... */ + abbrev = NULL; + } + } + } + + if(abbrev) { /* implicit match found */ + if(abbrev_arg && arg_p) + *arg_p = abbrev_arg; + return abbrev; + } else + /* no match found */ + return NULL; +} + +static int check_sym(const CLIF_option *optn, char sym) { + + if(sym == '+') { + if(!optn->function_plus) + return -1; + } + else if(sym == '-') { + if(!optn->function && optn->function_plus) + return -1; + } + + return 0; +} + +static int call_function(CLIF_option *optn, char *opt_arg, char sym) { + int (*function)(CLIF_option *, char *); + + function = (sym == '+') ? optn->function_plus : optn->function; + + if(!function) + return 0; + + if(opt_arg && ((optn->flags | curr.parse_flags) & CLIF_SEVERAL)) { + char tmp[80]; + char *t; + char *endt = tmp + sizeof(tmp); + + while(*opt_arg) { + + t = tmp; + while(t < endt && *opt_arg && + *opt_arg != ' ' && *opt_arg != '\t' && *opt_arg != ',' + ) + *t++ = *opt_arg++; + + if(t >= endt) + return -1; + + *t = '\0'; + + if(function(optn, tmp) < 0) + return -1; + + while(*opt_arg == ' ' || *opt_arg == '\t' || *opt_arg == ',') + opt_arg++; + } + + return 0; + } + + return function(optn, opt_arg); +} + +int CLIF_parse_cmdline (int argc, char *argv[], + CLIF_option *option_list, + CLIF_argument *argument_list, + unsigned int parse_flags){ +int i, j; +CLIF_option *optn; +CLIF_argument *argm; +int num_args = 0; +int num_argm = 0, strict_beg = 0, strict_end = 0; +_CLIF_index arg_n[MAX_ARGC_NUMBER]; +unsigned int dirty_flags = 0; +int dirty_plus = 0; +int exclusive_cnt = 0; +int posix = getenv ("POSIXLY_CORRECT") != NULL || +(parse_flags & CLIF_POSIX); + +curr.argc = argc; +curr.argv = argv; +curr.option_list = option_list; +curr.argument_list = argument_list; +curr.parse_flags = parse_flags; + +if (argc <= 1 && (parse_flags & CLIF_HELP_EMPTY)) { + CLIF_current_help (); + exit (0); +} + +/* Scan argument_list for check and some info. */ + +if (argument_list) { + enum stages {STRICT_BEG, OPTIONAL, STRICT_END}; + int stage = STRICT_BEG; + + for (argm = argument_list; argm->name; argm++) { + + if (argm->flags & CLIF_STRICT) { + + if (stage == STRICT_BEG) strict_beg++; + else if (stage == OPTIONAL) { + stage = STRICT_END; + strict_end++; + } + else if (stage == STRICT_END) + strict_end++; + } else { + if (stage == STRICT_BEG) stage = OPTIONAL; + else if (stage == STRICT_END) { + err_report ("Incorrect argument list set in program " + "source: more than one optional area."); + return -1; + } + } + + num_argm++; + } +} + +/* Scan option_list for some info. */ +if (option_list) { + + dirty_flags = parse_flags; + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + dirty_flags |= optn->flags; + if (optn->function_plus) dirty_plus = 1; + } +} + +if (dirty_flags & CLIF_EXCL) +exclusive_cnt = 1; /* only one is allowed... */ + +/* Go ! Store arguments, parse options. */ + +for (i = 1; i < argc; i++) { + char *arg = argv[i]; + char *opt_arg = NULL; + char sym = '-'; + + if (!option_list) + goto handle_arg; + + if (*arg == '+' && dirty_plus) + sym = '+'; + + if (*arg != sym) { /* argument or keyword */ + + if (dirty_flags & CLIF_MAY_KEYWORD) { + optn = find_long (arg, &opt_arg, CLIF_MAY_KEYWORD, 0); + if (optn) goto long_found; + } + + if (num_args == 0 && (parse_flags & CLIF_FIRST_GROUP)) { + /* ugly... */ + parse_flags &= ~CLIF_FIRST_GROUP; + dirty_flags &= ~CLIF_FIRST_GROUP; /* to be correct */ + + goto handle_short; + } + + /* else it is an argument */ + goto handle_arg; + + } + else if (*++arg == sym) { /* `--' - long option */ + arg++; + + if (*arg == sym || /* `---' - let it be not option... */ + (parse_flags & (_CLIF_STRICT_KEYWORD|_CLIF_STRICT_ONEDASH)) + ) { + arg -= 2; + goto handle_arg; /* not option anyway */ + } + + optn = find_long (arg, &opt_arg, 0, + _CLIF_STRICT_KEYWORD | _CLIF_STRICT_ONEDASH); + if (optn) goto long_found; + + /* XXX: May be allow only for `--', not `++' too... */ + if (!*arg && sym == '-') { /* `--' and no empty longoption */ + option_list = NULL; /* POSIX way... */ + continue; + } + + /* XXX: or treat as an argument sometimes??? */ + err_bad_opt (argv[i], 0, i); + return -1; + } + else { /* short option, or several short options... */ + + if (dirty_flags & CLIF_MAY_ONEDASH) { + optn = find_long (arg, &opt_arg, CLIF_MAY_ONEDASH, 0); + if (optn) goto long_found; + } + + if (!*arg) { /* POSIX say: only "stdout specification"... */ + arg--; + goto handle_arg; + } + + goto handle_short; + } + + long_found: + if (check_sym (optn, sym) < 0) { /* Oops... */ + err_bad_opt (argv[i], 0, i); + return -1; + } + + if (optn->flags & CLIF_EXCL) { + if (!exclusive_cnt) { + err_bad_excl (optn, 0, i); + return -1; + } + exclusive_cnt--; + } + + if (optn->arg_name && !opt_arg) { + unsigned int flags = optn->flags | parse_flags; + + if (++i >= argc || + !(flags & CLIF_MAY_NOEQUAL) + ) { /* missing opt arg */ + i--; + + if (!(flags & CLIF_OPTARG)) { + err_bad_arg (optn, 0, i); + return -1; + } + + opt_arg = NULL; + } else + opt_arg = argv[i]; + + } + + if (call_function (optn, opt_arg, sym) < 0) { + err_bad_res (optn, 0, opt_arg, i); + return -1; + } + + if (optn->flags & CLIF_EXIT) + exit (0); + + continue; + + handle_arg: + if (argument_list) { + if (i < MAX_ARGC_NUMBER) /* XXX: ugly, better report */ + arg_n[num_args++] = i; + } else { + err_report ("`%s' (argc %d): arguments are not allowed", + argv[i], i); + return -1; + } + + /* POSIX say: No more options after args... */ + if (posix) option_list = NULL; /* geniously... */ + + continue; + + handle_short: + + opt_arg = NULL; + + do { + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + if (optn->short_opt && optn->short_opt[0] == *arg) + break; + } + if (!optn->short_opt || + check_sym (optn, sym) < 0 + ) { + err_bad_opt (argv[i], *arg, i); + return -1; + } + + if (optn->flags & CLIF_EXCL) { + if (!exclusive_cnt) { + err_bad_excl (optn, *arg, i); + return -1; + } + exclusive_cnt--; + } + + if (optn->arg_name) { + unsigned int flags = parse_flags | optn->flags; + + if (arg[1] == '\0') { /* a last one */ + + /* POSIX say: an option with arg cannot be grouped. */ + if (posix && arg != argv[i] && arg[-1] != sym) { + err_bad_arg (optn, *arg, i); /* good way? */ + return -1; + } + + if (++i >= argc || + (flags & _CLIF_STRICT_JOIN_ARG) + ) { + i--; + + if (!(flags & CLIF_OPTARG)) { + err_bad_arg (optn, *arg, i); + return -1; + } + + opt_arg = NULL; + } else + opt_arg = argv[i]; + } + else if ((arg == argv[i] || arg[-1] == sym) && + (flags & CLIF_MAY_JOIN_ARG) + ) { + opt_arg = ++arg; + } + else { /* inside a group... */ + if (!(flags & CLIF_OPTARG) || + (flags & CLIF_MAY_JOIN_ARG) + ) { + err_bad_arg (optn, *arg, i); + return -1; + } + + opt_arg = NULL; + } + } + + if (call_function (optn, opt_arg, sym) < 0) { + err_bad_res (optn, optn->short_opt[0], opt_arg, i); + return -1; + } + + if (optn->flags & CLIF_EXIT) + exit (0); + + }while (!opt_arg && *++arg); + +} /* for ( ... ) */ + +if ((parse_flags & CLIF_STRICT_EXCL) && exclusive_cnt != 0) { + err_report ("One of these must be specified:\n %s\n", + show_excl (option_list, 0)); + return -1; +} + +/* Now, after *ALL* options, handle arguments, if any. */ + +if (num_args < strict_beg + strict_end) { + /* Missing some needed arguments. */ + + if (num_args < strict_beg) argm = argument_list + num_args; + else + argm = argument_list + + ((num_args - strict_beg) + (num_argm - strict_end)); + + if (num_args == strict_beg + strict_end - 1) + err_report ("Specify \"%s\" missing argument.", argm->name); + else + err_report ("Specify \"%s\" and other missing arguments.", + argm->name); + return -1; +} + +if (num_args > 0) { + _CLIF_index argm_index[MAX_ARGC_NUMBER]; + + /* assing argm (by index) for each arg... */ + + for (i = 0, j = 0; i < strict_beg; i++, j++) + argm_index[i] = j; + for (i = num_args - strict_end, j = num_argm - strict_end; + i < num_args; i++, j++ + ) argm_index[i] = j; + for (i = strict_beg, j = strict_beg; + i < num_args - strict_end && j < num_argm - strict_end; + i++ + ) { + argm_index[i] = j; + if (!(argument_list[j].flags & CLIF_MORE)) + j++; + } + + if (i < num_args - strict_end) { /* there are extra args... */ + err_report ("Extra arg `%s' (position %d, argc %d)", + argv[arg_n[i]], i + 1, arg_n[i]); + return -1; + } + + if (j < num_argm - strict_end && + !(argument_list[j].flags & CLIF_MORE) && + /* ...i.e, there are some missing optional args... */ + (argument_list[j].flags & CLIF_ACC_PREV) + ) { + if (j == 0) + err_report ("Incorrect argument list set: first arg " + "cannot be `accompanied with previous'."); + else + err_report ("Arg \"%s\" must be specified because " + "\"%s\" `%s' is used.", argument_list[j].name, + argument_list[j - 1].name, argv[arg_n[i - 1]]); + return -1; + } + + if (argm_index[--i] == j && + /* above is true only after OPTIONAL area scan + and when `j' is stopped on CLIF_MORE */ + ++j < num_argm - strict_end + /* i.e: there is a *last* one (after CLIF_MORE) + in the OPTIONAL area */ + ) argm_index[i] = j; /* *last* is better than *more* */ + + /* ...and work now */ + + for (i = 0; i < num_args; i++) { + argm = argument_list + argm_index[i]; + + if (argm->function && + argm->function (argm, argv[arg_n[i]], i) < 0 + ) { + err_report ("Cannot handle \"%s\" cmdline arg `%s' " + "on position %d (argc %d)", + argm->name, argv[arg_n[i]], i + 1, arg_n[i]); + return -1; + } + } + + /* That`s all. */ +} + +return 0; +} + +static void box_output(int start, int left, int width, const char *str, + const char *arg_name) { + char *p, *endp, *s; + int l; + char buf[1024]; + char spacer[128]; /* assume it is enough */ + + if(left > (int) sizeof(spacer) - 2) + left = (int) sizeof(spacer) - 2; + if(width > (int) sizeof(buf) - 1) + width = (int) sizeof(buf) - 1; + + spacer[0] = '\n'; + memset(spacer + 1, ' ', left); + spacer[left + 1] = '\0'; + + l = left - start; + if(l > 0) { + memset(buf, ' ', l); + buf[l] = '\0'; + fprintf(stderr, "%s", buf); + } else + fprintf(stderr, "%s", spacer); + + endp = buf + width; + + p = buf; + + while(*str) { + + while(*str && p < endp) { + + if(*str == '%' && arg_name) { + if(str[1] == '%') { + *p++ = '%'; + str += 2; + continue; + } + else if(str[1] == 's') { + const char *a = arg_name; + + while(*a && p < endp) + *p++ = *a++; + str += 2; + continue; + } + } + + *p++ = *str++; + } + + *p = '\0'; + + if(p < endp) + break; + + while(p > buf && *p != ' ' && *p != '\t') + p--; + if(p <= buf) + return; /* foo on you */ + + *p = '\0'; + fprintf(stderr, "%s", buf); + fprintf(stderr, "%s", spacer); + + p++; + for(s = buf; *p; *s++ = *p++) + ; + *s = '\0'; + p = s; + } + + fprintf(stderr, "%s", buf); + + return; +} + +#define SHORT_LONG_DLM " " +#define OPT_START_DLM " " +#define OPT_FIELD_WIDTH 30 + +#define ARG_MARK_STRICT "+ " +#define ARG_MARK_GROUP0 " . " +#define ARG_MARK_GROUP " ' " +#define ARG_MARK_OPT " " +#define ARG_FIELD_WIDTH 20 + +#define SCREEN_WIDTH 80 + +void CLIF_print_options(const char *header, + const CLIF_option *option_list) { + const CLIF_option *optn; + char *excl; + int excl_cnt = 0; + + /* Print a header string, if present... */ + if(header) + fprintf(stderr, "%s\n", header); + + if(!option_list) + return; + + for(optn = option_list; optn->short_opt || optn->long_opt; optn++) { + int len; + + /* generate and print an option usage */ + + if(optn->short_opt) { + if(optn->long_opt) + len = fprintf(stderr, OPT_START_DLM "%s" + SHORT_LONG_DLM "%s", + show_short(optn), show_long(optn)); + else + len = fprintf(stderr, OPT_START_DLM "%s", + show_short(optn)); + } else + len = fprintf(stderr, OPT_START_DLM "%s", show_long(optn)); + + /* print a help string, if present */ + + if(optn->help_string) + box_output(len, OPT_FIELD_WIDTH, + SCREEN_WIDTH - OPT_FIELD_WIDTH, + optn->help_string, optn->arg_name); + + fprintf(stderr, "\n"); /* a last one */ + } + + excl = show_excl(option_list, &excl_cnt); + if(excl_cnt > 0) { + + if(excl_cnt == 1) { + if((curr.parse_flags & CLIF_STRICT_EXCL) && + curr.option_list == option_list + ) + fprintf(stderr, "Anyway `%s' must be specified.\n", excl); + //else /* simple ordinary option, because excl_cnt == 1 ... */ + } else + fprintf(stderr, "Only one of these may be specified:\n" + " %s\n", excl); + } + + return; +} + +void CLIF_print_arguments(const char *header, + const CLIF_argument *argument_list) { + const CLIF_argument *argm; + + if(!argument_list) + return; + + /* Print a header string, if present... */ + if(header) + fprintf(stderr, "%s\n", header); + + for(argm = argument_list; argm->name; argm++) { + int len; + + if(argm->flags & CLIF_STRICT) + len = fprintf(stderr, ARG_MARK_STRICT "%s", argm->name); + else if(argm->flags & CLIF_MORE) + len = fprintf(stderr, ARG_MARK_OPT "%s ...", argm->name); + else if(argm->flags & CLIF_ACC_PREV) + len = fprintf(stderr, ARG_MARK_GROUP "%s", argm->name); + else if((argm + 1)->name && ((argm + 1)->flags & CLIF_ACC_PREV)) + len = fprintf(stderr, ARG_MARK_GROUP0 "%s", argm->name); + else + len = fprintf(stderr, ARG_MARK_OPT "%s", argm->name); + + if(argm->help_string) + box_output(len, ARG_FIELD_WIDTH, + SCREEN_WIDTH - ARG_FIELD_WIDTH, + argm->help_string, argm->name); + + fprintf(stderr, "\n"); + } + + return; +} + +void CLIF_print_usage(const char *header, const char *progname, + const CLIF_option *option_list, + const CLIF_argument *argument_list) { + + if(!progname && curr.argv) + progname = curr.argv[0]; + + if(!header) { + if(progname) + fprintf(stderr, "Usage: %s", progname); + else + fprintf(stderr, "Command line options:"); + } else { + if(progname) + fprintf(stderr, "%s\n" OPT_START_DLM "%s", header, progname); + else + fprintf(stderr, "%s", header); + } + + if(option_list) { + const CLIF_option *optn; + char m_buf[256], p_buf[256], mp_buf[256]; + char *m = m_buf, *p = p_buf, *mp = mp_buf; + char *end_m = m_buf + sizeof(m_buf) - 1; + char *end_p = p_buf + sizeof(p_buf) - 1; + char *end_mp = mp_buf + sizeof(mp_buf) - 1; + char *excl; + int excl_cnt = 0; + + /* first, show exclusive option list, if any... */ + + excl = show_excl(option_list, &excl_cnt); + if(excl_cnt > 0) { + if((curr.parse_flags & CLIF_STRICT_EXCL) && + curr.option_list == option_list + ) { + if(excl_cnt == 1) + fprintf(stderr, " %s", excl); + else + fprintf(stderr, " { %s }", excl); + } else + fprintf(stderr, " [ %s ]", excl); + } + + /* second, find short options without arguments... */ + + for(optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + /* We don`t exclude CLIF_EXTRA hear: + simple one char don`t eat a lot of space... + */ + + if(!optn->short_opt || + optn->arg_name || + (optn->flags & CLIF_EXCL) + ) + continue; + + if(optn->function_plus) { + if(optn->function) { + if(mp < end_mp) + *mp++ = optn->short_opt[0]; + } else { + if(p < end_p) + *p++ = optn->short_opt[0]; + } + } else { + if(m < end_m) + *m++ = optn->short_opt[0]; + } + } + + if(m > (char *) m_buf) { + *m = '\0'; + fprintf(stderr, " [ -%s ]", m_buf); + } + if(p > (char *) p_buf) { + *p = '\0'; + fprintf(stderr, " [ +%s ]", p_buf); + } + if(mp > (char *) mp_buf) { + *mp = '\0'; + fprintf(stderr, " [ " SHORT_PLUS_MINUS "%s ]", mp_buf); + } + + /* third, print all another... */ + + for(optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + if(optn->flags & CLIF_EXTRA) + continue; + + if(optn->flags & CLIF_EXCL) + continue; /* already handled */ + + if(optn->short_opt) { + if(optn->arg_name) + fprintf(stderr, " [ %s ]", show_short(optn)); + //else + /* already handled */ + } else + fprintf(stderr, " [ %s ]", show_long(optn)); + } + } + + if(argument_list) { + const CLIF_argument *argm; + int deep = 0; + + for(argm = argument_list; argm->name; argm++) { + + if(argm->flags & CLIF_STRICT) { + if(deep > 0) { + fputc(' ', stderr); + while(deep--) + fputc(']', stderr); + deep = 0; + } + + fprintf(stderr, " %s", argm->name); + } else { + if(argm->flags & CLIF_MORE) + fprintf(stderr, " [ %s ...", argm->name); + else if(argm->flags & CLIF_ACC_PREV) { + fprintf(stderr, " %s", argm->name); + --deep; /* ugly, but easy */ + } else + fprintf(stderr, " [ %s", argm->name); + + deep++; + } + } + + if(deep > 0) { + fputc(' ', stderr); + while(deep--) + fputc(']', stderr); + } + } + + fprintf(stderr, "\n"); +} + +int CLIF_current_help(void) { + + if(!curr.argc) + return -1; /* i.e., not inited... */ + + CLIF_print_usage("Usage:", curr.argv[0], curr.option_list, + curr.argument_list); + + if(curr.option_list) + CLIF_print_options("Options:", curr.option_list); + + if(curr.argument_list) + CLIF_print_arguments("\nArguments:", curr.argument_list); + + return 0; +} + +/* Common useful option handlers. */ + +int CLIF_version_handler(CLIF_option *optn, char *arg) { + UNUSED(arg); + if(!optn->data) + return -1; + + fprintf(stderr, "%s\n", ((char *) optn->data)); + + return 0; /* be happy */ +} + +int CLIF_set_flag(CLIF_option *optn, char *arg) { + UNUSED(arg); + if(!optn->data) + return -1; + + *((int *) optn->data) = 1; + + return 0; +} + +int CLIF_unset_flag(CLIF_option *optn, char *arg) { + UNUSED(arg); + if(!optn->data) + return -1; + + *((int *) optn->data) = 0; + + return 0; +} + +static int set_string(char **data, char *arg) { + + if(!data) + return -1; + + *data = arg; + + return 0; +} + +int CLIF_set_string(CLIF_option *optn, char *arg) { + + return set_string(optn->data, arg); +} + +int CLIF_arg_string(CLIF_argument *argm, char *arg, int index) { + UNUSED(index); + return set_string(argm->data, arg); +} + +static int set_int(int *data, char *arg) { + char *q; + + if(!data) + return -1; + + *data = (int) strtol(arg, &q, 0); + + return (q == arg || *q) ? -1 : 0; +} + +static int set_uint(unsigned int *data, char *arg) { + char *q; + + if(!data) + return -1; + + *data = (unsigned int) strtoul(arg, &q, 0); + + return (q == arg || *q) ? -1 : 0; +} + +static int set_double(double *data, char *arg) { + char *q; + + if(!data) + return -1; + + *data = strtod(arg, &q); + + return (q == arg || *q) ? -1 : 0; +} + +int CLIF_set_int(CLIF_option *optn, char *arg) { + + return set_int(optn->data, arg); +} + +int CLIF_set_uint(CLIF_option *optn, char *arg) { + + return set_uint(optn->data, arg); +} + +int CLIF_set_double(CLIF_option *optn, char *arg) { + + return set_double(optn->data, arg); +} + +int CLIF_arg_int(CLIF_argument *argm, char *arg, int index) { + UNUSED(index); + return set_int(argm->data, arg); +} + +int CLIF_arg_uint(CLIF_argument *argm, char *arg, int index) { + UNUSED(index); + return set_uint(argm->data, arg); +} + +int CLIF_arg_double(CLIF_argument *argm, char *arg, int index) { + UNUSED(index); + return set_double(argm->data, arg); +} + +int CLIF_call_func(CLIF_option *optn, char *arg) { + + if(!optn->data) + return -1; + + if(optn->arg_name) { + int (*func)(char *) = optn->data; + + return func(arg); + } else { + int (*func)(void) = optn->data; + + return func(); + } +} + +int CLIF_arg_func(CLIF_argument *argm, char *arg, int index) { + int (*func)(char *, int); + + if(!argm->data) + return -1; + + func = (int (*)(char *, int)) argm->data; + + return func(arg, index); +} + diff --git a/libdap-chain-net/iputils/traceroute/clif.h b/libdap-chain-net/iputils/traceroute/clif.h new file mode 100644 index 0000000000000000000000000000000000000000..c798a8df8fbfee7b7450b6e6ec0a46c08443c66f --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/clif.h @@ -0,0 +1,121 @@ +/* + Copyright (c) 2000, 2003 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: LGPL v2.1 or any later + + See COPYING.LIB for the status of this software. +*/ + +#ifndef _CLIF_H +#define _CLIF_H + + +typedef struct CLIF_option_struct CLIF_option; +struct CLIF_option_struct { + const char *short_opt; + const char *long_opt; + const char *arg_name; + const char *help_string; + int (*function) (CLIF_option *optn, char *arg); + void *data; + int (*function_plus) (CLIF_option *optn, char *arg); + unsigned int flags; +}; +#define CLIF_END_OPTION { 0, 0, 0, 0, 0, 0, 0, 0 } + +typedef struct CLIF_argument_struct CLIF_argument; +struct CLIF_argument_struct { + const char *name; + const char *help_string; + int (*function) (CLIF_argument *argm, char *arg, int index); + void *data; + unsigned int flags; +}; +#define CLIF_END_ARGUMENT { 0, 0, 0, 0, 0 } + +/* Argument flag bits. */ +#define CLIF_MORE (0x01) /* null or several */ +#define CLIF_STRICT (0x02) /* arg must be present */ +#define CLIF_ACC_PREV (0x04) /* arg must be accompanied with previous */ + + +/* Option flag bits. */ + +/* affected only by per-option flags */ +#define CLIF_EXTRA (0x0001) /* don`t show in usage line */ +#define CLIF_EXIT (0x0002) /* exit after handler return */ +#define CLIF_EXCL (0x0004) /* at exclusive area */ + +/* affected by per-option flags and by common `parse_flags' argument + of CLIF_parse_cmdline(). In last case appropriate bits are translated + for all the options. +*/ +#define CLIF_MAY_JOIN_ARG (0x0010) +#define _CLIF_STRICT_JOIN_ARG (0x0020) +#define CLIF_JOIN_ARG (CLIF_MAY_JOIN_ARG|_CLIF_STRICT_JOIN_ARG) +#define CLIF_MAY_NOEQUAL (0x0040) +#define _CLIF_STRICT_NOEQUAL (0x0080) +#define CLIF_NOEQUAL (CLIF_MAY_NOEQUAL|_CLIF_STRICT_NOEQUAL) +#define CLIF_MAY_KEYWORD (0x0100) +#define _CLIF_STRICT_KEYWORD (0x0200) +#define CLIF_KEYWORD (CLIF_MAY_KEYWORD|_CLIF_STRICT_KEYWORD) +#define CLIF_MAY_ONEDASH (0x0400) +#define _CLIF_STRICT_ONEDASH (0x0800) +#define CLIF_ONEDASH (CLIF_MAY_ONEDASH|_CLIF_STRICT_ONEDASH) +#define CLIF_OPTARG (0x1000) /* allow missing optarg */ +#define CLIF_ABBREV (0x2000) /* allow long opt abbreviation */ +#define CLIF_SEVERAL (0x4000) /* several args in one opt`s arg */ + +/* affected only by common `parse_flags' arg of CLIF_parse_cmdline() . */ +#define CLIF_HELP_EMPTY (0x10000) /* print help on empty cmdline */ +#define CLIF_POSIX (0x20000) /* follow POSIX standard */ +#define CLIF_FIRST_GROUP (0x40000) /* first arg - options` group */ +#define CLIF_STRICT_EXCL (0x80000) /* at least one exclusive */ +#define CLIF_SILENT (0x100000) /* no errors on stderr */ + +#define CLIF_MIN_ABBREV 2 /* a minimal match length in abbrev */ + + +extern int CLIF_parse (int argc, char **argv, CLIF_option *option_list, + CLIF_argument *arg_list, unsigned int parse_flags); +/* history compatibility... */ +#define CLIF_parse_cmdline(ARGC,ARGV,OPTN,ARGS,FLAGS) \ + CLIF_parse (ARGC, ARGV, OPTN, ARGS, FLAGS) + +extern void CLIF_print_options (const char *header, + const CLIF_option *option_list); +extern void CLIF_print_arguments (const char *header, + const CLIF_argument *argument_list); +extern void CLIF_print_usage (const char *header, const char *progname, + const CLIF_option *option_list, + const CLIF_argument *argument_list); + +extern int CLIF_current_help (void); + +/* Common useful option handlers. */ +extern int CLIF_version_handler (CLIF_option *optn, char *arg); +extern int CLIF_set_flag (CLIF_option *optn, char *arg); +extern int CLIF_unset_flag (CLIF_option *optn, char *arg); +extern int CLIF_set_string (CLIF_option *optn, char *arg); +extern int CLIF_set_int (CLIF_option *optn, char *arg); +extern int CLIF_set_uint (CLIF_option *optn, char *arg); +extern int CLIF_set_double (CLIF_option *optn, char *arg); +extern int CLIF_call_func (CLIF_option *optn, char *arg); + +extern int CLIF_arg_string (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_int (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_double (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_func (CLIF_argument *argm, char *arg, int index); + + +/* Some useful macros. */ + +#define CLIF_HELP_OPTION \ + { 0, "help", 0, "Read this help and exit", \ + CLIF_call_func, CLIF_current_help, 0, CLIF_EXTRA | CLIF_EXIT } +#define CLIF_VERSION_OPTION(STR) \ + { "V", "version", 0, "Print version info and exit", \ + CLIF_version_handler, STR, 0, CLIF_EXTRA | CLIF_EXIT } + +#endif /* _CLIF_H */ diff --git a/libdap-chain-net/iputils/traceroute/csum.c b/libdap-chain-net/iputils/traceroute/csum.c new file mode 100644 index 0000000000000000000000000000000000000000..ea3a50da0430d19582941d72cded69fb09e45127 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/csum.c @@ -0,0 +1,34 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> + +#include "traceroute.h" + + +uint16_t in_csum (const void *ptr, size_t len) { + const uint16_t *p = (const uint16_t *) ptr; + size_t nw = len / 2; + unsigned int sum = 0; + uint16_t res; + + while (nw--) sum += *p++; + + if (len & 0x1) + sum += htons (*((unsigned char *) p) << 8); + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + res = ~sum; + if (!res) res = ~0; + + return res; +} + diff --git a/libdap-chain-net/iputils/traceroute/extension.c b/libdap-chain-net/iputils/traceroute/extension.c new file mode 100644 index 0000000000000000000000000000000000000000..515a8dbed7da85addd25543a3bbcfd303944f667 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/extension.c @@ -0,0 +1,132 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "traceroute.h" + + +struct icmp_ext_header { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned int version:4; + unsigned int reserved:4; +#else + unsigned int reserved:4; + unsigned int version:4; +#endif + uint8_t reserved1; + uint16_t checksum; +} __attribute__ ((packed)); + + +struct icmp_ext_object { + uint16_t length; + uint8_t class; + uint8_t c_type; + uint8_t data[0]; +}; + +#define MPLS_CLASS 1 +#define MPLS_C_TYPE 1 + + +#define do_snprintf(CURR, END, FMT, ARGS...) \ + do { \ + CURR += snprintf (CURR, END - CURR, (FMT), ## ARGS);\ + if (CURR > END) CURR = END; \ + } while (0) + + +static int try_extension (probe *pb, char *buf, size_t len) { + struct icmp_ext_header *iext = (struct icmp_ext_header *) buf; + char str[1024]; + char *curr = str; + char *end = str + sizeof (str) / sizeof (*str); + + + /* a check for len >= 8 already done for all cases */ + + if (iext->version != 2) return -1; + + if (iext->checksum && + in_csum (iext, len) != (uint16_t) ~0 + ) return -1; + + buf += sizeof (*iext); + len -= sizeof (*iext); + + + while (len >= sizeof (struct icmp_ext_object)) { + struct icmp_ext_object *obj = (struct icmp_ext_object *) buf; + size_t objlen = ntohs (obj->length); + size_t data_len; + uint32_t *ui = (uint32_t *) obj->data; + int i, n; + + if (objlen < sizeof (*obj) || + objlen > len + ) return -1; + + data_len = objlen - sizeof (*obj); + if (data_len % sizeof (uint32_t)) + return -1; /* must be 32bit rounded... */ + + n = data_len / sizeof (*ui); + + + if (curr > (char *) str && curr < end) + *curr++ = ';'; /* a separator */ + + if (obj->class == MPLS_CLASS && + obj->c_type == MPLS_C_TYPE && + n >= 1 + ) { /* people prefer MPLS to be parsed... */ + + do_snprintf (curr, end, "MPLS:"); + + for (i = 0; i < n; i++, ui++) { + uint32_t mpls = ntohl (*ui); + + do_snprintf (curr, end, "%sL=%u,E=%u,S=%u,T=%u", + i ? "/" : "", + mpls >> 12, + (mpls >> 9) & 0x7, + (mpls >> 8) & 0x1, + mpls & 0xff); + } + + } + else { /* common case... */ + + do_snprintf (curr, end, "%u/%u:", obj->class, obj->c_type); + + for (i = 0; i < n && curr < end; i++, ui++) + do_snprintf (curr, end, "%s%08x", i ? "," : "", ntohl(*ui)); + } + + buf += objlen; + len -= objlen; + } + + if (len) return -1; + + + pb->ext = strdup (str); + + return 0; +} + + +void handle_extensions (probe *pb, char *buf, int len, int step) { + + if (!step) + try_extension (pb, buf, len); + else { + for ( ; len >= 8; buf += step, len -= step) + if (try_extension (pb, buf, len) == 0) + break; + } + + return; +} + diff --git a/libdap-chain-net/iputils/traceroute/flowlabel.h b/libdap-chain-net/iputils/traceroute/flowlabel.h new file mode 100755 index 0000000000000000000000000000000000000000..e6241c4f8e9cdd00558ffd3494cfe1b5d40a1c80 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/flowlabel.h @@ -0,0 +1,40 @@ +/* + It is just a stripped copy of the kernel header "linux/in6.h" + + "Flow label" things are still not defined in "netinet/in*.h" headers, + but we cannot use "linux/in6.h" immediately because it currently + conflicts with "netinet/in.h" . +*/ + +struct in6_flowlabel_req_ +{ + struct in6_addr flr_dst; + __u32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_pad; + /* Options in format of IPV6_PKTOPTIONS */ +}; + +#define IPV6_FL_A_GET 0 +#define IPV6_FL_A_PUT 1 +#define IPV6_FL_A_RENEW 2 + +#define IPV6_FL_F_CREATE 1 +#define IPV6_FL_F_EXCL 2 + +#define IPV6_FL_S_NONE 0 +#define IPV6_FL_S_EXCL 1 +#define IPV6_FL_S_PROCESS 2 +#define IPV6_FL_S_USER 3 +#define IPV6_FL_S_ANY 255 + +#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff +#define IPV6_FLOWINFO_PRIORITY 0x0ff00000 + +#define IPV6_FLOWLABEL_MGR 32 +#define IPV6_FLOWINFO_SEND 33 + diff --git a/libdap-chain-net/iputils/traceroute/mod-dccp.c b/libdap-chain-net/iputils/traceroute/mod-dccp.c new file mode 100644 index 0000000000000000000000000000000000000000..26f2c653bf0fd3af1faeb911cd943e493edd93e9 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/mod-dccp.c @@ -0,0 +1,295 @@ +/* + Copyright (c) 2012 Samuel Jero <sj323707@ohio.edu> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <linux/dccp.h> + + +#include "traceroute.h" + + +#define DEF_SERVICE_CODE 1885957735 + +#define DCCP_HEADER_LEN (sizeof (struct dccp_hdr) + \ + sizeof (struct dccp_hdr_ext) \ + + sizeof (struct dccp_hdr_request)) + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int dest_port = 0; + +static int raw_sk = -1; +static int last_ttl = 0; + +static uint8_t buf[1024]; /* enough, enough... */ +static size_t csum_len = 0; +static struct dccp_hdr *dh = NULL; +static struct dccp_hdr_ext *dhe = NULL; +static struct dccp_hdr_request *dhr = NULL; +static unsigned int service_code = DEF_SERVICE_CODE; + + +static CLIF_option dccp_options[] = { + { 0, "service", "NUM", "Set DCCP service code to %s (default is " + _TEXT (DEF_SERVICE_CODE) ")", + CLIF_set_uint, &service_code, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + + +static int dccp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + sockaddr_any src; + socklen_t len; + uint8_t *ptr; + uint16_t *lenp; + + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; /* raw sockets can be confused */ + + if (!port_seq) port_seq = DEF_DCCP_PORT; + dest_port = htons (port_seq); + + + /* Create raw socket for DCCP */ + raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); /* including bind, if any */ + + if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + len = sizeof (src); + if (getsockname (raw_sk, &src.sa, &len) < 0) + error ("getsockname"); + + + if (!raw_can_connect ()) { /* work-around for buggy kernels */ + close (raw_sk); + raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP); + if (raw_sk < 0) error ("socket"); + tune_socket (raw_sk); + /* but do not connect it... */ + } + + + use_recverr (raw_sk); + + add_poll (raw_sk, POLLIN | POLLERR); + + + /* Now create the sample packet. */ + + /* For easy checksum computing: + saddr + daddr + length + protocol + dccphdr + */ + + ptr = buf; + + if (af == AF_INET) { + len = sizeof (src.sin.sin_addr); + memcpy (ptr, &src.sin.sin_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin.sin_addr, len); + ptr += len; + } else { + len = sizeof (src.sin6.sin6_addr); + memcpy (ptr, &src.sin6.sin6_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin6.sin6_addr, len); + ptr += len; + } + + lenp = (uint16_t *) ptr; + ptr += sizeof (uint16_t); + *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_DCCP); + ptr += sizeof (uint16_t); + + + /* Construct DCCP header */ + dh = (struct dccp_hdr *) ptr; + + dh->dccph_ccval = 0; + dh->dccph_checksum = 0; + dh->dccph_cscov = 0; + dh->dccph_dport = dest_port; + dh->dccph_reserved = 0; + dh->dccph_sport = 0; /* temporary */ + dh->dccph_x = 1; + dh->dccph_type = DCCP_PKT_REQUEST; + dh->dccph_seq2 = 0; /* reserved if using 48 bit sequence numbers */ + /* high 16 bits of sequence number. Always make 0 for simplicity. */ + dh->dccph_seq = 0; + ptr += sizeof (struct dccp_hdr); + + dhe = (struct dccp_hdr_ext *) ptr; + dhe->dccph_seq_low = 0; /* temporary */ + ptr += sizeof (struct dccp_hdr_ext); + + dhr = (struct dccp_hdr_request *) ptr; + dhr->dccph_req_service = htonl (service_code); + ptr += sizeof (struct dccp_hdr_request); + + + csum_len = ptr - buf; + + if (csum_len > sizeof (buf)) + error ("impossible"); /* paranoia */ + + len = ptr - (uint8_t *) dh; + if (len & 0x03) error ("impossible"); /* as >>2 ... */ + + *lenp = htons (len); + dh->dccph_doff = len >> 2; + + + *packet_len_p = len; + + return 0; +} + + +static void dccp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t len = sizeof (addr); + + + /* To make sure we have chosen a free unused "source port", + just create, (auto)bind and hold a socket while the port is needed. + */ + + sk = socket (af, SOCK_DCCP, IPPROTO_DCCP); + if (sk < 0) error ("socket"); + + bind_socket (sk); + + if (getsockname (sk, &addr.sa, &len) < 0) + error ("getsockname"); + + /* When we reach the target host, it can send us either Reset or Response. + For Reset all is OK (we and kernel just answer nothing), but + for Response we should reply with our Close. + It is well-known "half-open technique", used by port scanners etc. + This way we do not touch remote applications at all, unlike + the ordinary connect(2) call. + As the port-holding socket neither connect() nor listen(), + it means "no such port yet" for remote ends, and kernel always + send Reset in such a situation automatically (we have to do nothing). + */ + + + dh->dccph_sport = addr.sin.sin_port; + + dhe->dccph_seq_low = random_seq (); + + dh->dccph_checksum = 0; + dh->dccph_checksum = in_csum (buf, csum_len); + + + if (ttl != last_ttl) { + set_ttl (raw_sk, ttl); + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, dh, dh->dccph_doff << 2, &dest_addr) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->seq = dh->dccph_sport; + + pb->sk = sk; + + return; +} + + +static probe *dccp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + UNUSED(sk); + probe *pb; + struct dccp_hdr *ndh = (struct dccp_hdr *) buf; + uint16_t sport, dport; + + + if (len < 8) return NULL; /* too short */ + + + if (err) { + sport = ndh->dccph_sport; + dport = ndh->dccph_dport; + } else { + sport = ndh->dccph_dport; + dport = ndh->dccph_sport; + } + + + if (dport != dest_port) + return NULL; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (sport); + if (!pb) return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void dccp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), dccp_check_reply); +} + + +static void dccp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module dccp_ops = { + .name = "dccp", + .init = dccp_init, + .send_probe = dccp_send_probe, + .recv_probe = dccp_recv_probe, + .expire_probe = dccp_expire_probe, + .options = dccp_options, +}; + +TR_MODULE (dccp_ops); + +void tr_module_dccp_insert() +{ + +} diff --git a/libdap-chain-net/iputils/traceroute/mod-icmp.c b/libdap-chain-net/iputils/traceroute/mod-icmp.c new file mode 100644 index 0000000000000000000000000000000000000000..e2194d9aa4563b22b48d66f324de9f8cedf61b00 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/mod-icmp.c @@ -0,0 +1,240 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> + +#include "traceroute.h" + +static sockaddr_any dest_addr = { { 0, }, }; +static uint16_t seq = 1; +static uint16_t ident = 0; + +static char *data; +static size_t *length_p; + +static int icmp_sk = -1; +static int last_ttl = 0; + +static int raw = 0; +static int dgram = 0; + +static CLIF_option icmp_options[] = { + { 0, "raw", 0, "Use raw sockets way only. Default is try this way " + "first (probably not allowed for unprivileged users), " + "then try dgram", + CLIF_set_flag, &raw, 0, CLIF_EXCL }, + { 0, "dgram", 0, "Use dgram sockets way only. May be not implemented " + "by old kernels or restricted by sysadmins", + CLIF_set_flag, &dgram, 0, CLIF_EXCL }, + CLIF_END_OPTION + }; + +static int icmp_init(const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int i; + int af = dest->sa.sa_family; + int protocol; + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; + + if(port_seq) + seq = port_seq; + + length_p = packet_len_p; + if(*length_p < sizeof(struct icmphdr)) + *length_p = sizeof(struct icmphdr); + + data = malloc(*length_p); + if(!data) + error("malloc"); + + for(i = (int) sizeof(struct icmphdr); i < (int)*length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + protocol = (af == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6; + + if(!raw) { + icmp_sk = socket(af, SOCK_DGRAM, protocol); + if(icmp_sk < 0 && dgram) + error("socket"); + } + + if(!dgram) { + int raw_sk = socket(af, SOCK_RAW, protocol); + if(raw_sk < 0) { + if(raw || icmp_sk < 0) + error_or_perm("socket"); + dgram = 1; + } else { + /* prefer the traditional "raw" way when possible */ + close(icmp_sk); + icmp_sk = raw_sk; + } + } + + tune_socket(icmp_sk); + + /* Don't want to catch packets from another hosts */ + if(raw_can_connect() && + connect(icmp_sk, &dest_addr.sa, sizeof(dest_addr)) < 0 + ) + error("connect"); + + use_recverr(icmp_sk); + + if(dgram) { + sockaddr_any addr; + socklen_t len = sizeof(addr); + + if(getsockname(icmp_sk, &addr.sa, &len) < 0) + error("getsockname"); + ident = ntohs(addr.sin.sin_port); /* both IPv4 and IPv6 */ + + } else + ident = getpid() & 0xffff; + + add_poll(icmp_sk, POLLIN | POLLERR); + + return 0; +} + +static void icmp_send_probe(probe *pb, int ttl) { + int af = dest_addr.sa.sa_family; + + if(ttl != last_ttl) { + + set_ttl(icmp_sk, ttl); + + last_ttl = ttl; + } + + if(af == AF_INET) { + struct icmp *icmp = (struct icmp *) data; + + icmp->icmp_type = ICMP_ECHO; + icmp->icmp_code = 0; + icmp->icmp_cksum = 0; + icmp->icmp_id = htons(ident); + icmp->icmp_seq = htons(seq); + + icmp->icmp_cksum = in_csum(data, *length_p); + } + else if(af == AF_INET6) { + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) data; + + icmp6->icmp6_type = ICMP6_ECHO_REQUEST; + icmp6->icmp6_code = 0; + icmp6->icmp6_cksum = 0; + icmp6->icmp6_id= htons (ident); + icmp6->icmp6_seq= htons(seq); + + /* icmp6->icmp6_cksum always computed by kernel internally */ + } + + pb->send_time = get_time(); + + if(do_send(icmp_sk, data, *length_p, &dest_addr) < 0) { + pb->send_time = 0; + return; + } + + pb->seq = seq; + + seq++; + + return; +} + +static probe *icmp_check_reply(int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + UNUSED(sk); + UNUSED(from); + int af = dest_addr.sa.sa_family; + int type; + uint16_t recv_id, recv_seq; + probe *pb; + + if(len < sizeof(struct icmphdr)) + return NULL; + + if(af == AF_INET) { + struct icmp *icmp = (struct icmp *) buf; + + type = icmp->icmp_type; + + recv_id = ntohs(icmp->icmp_id); + recv_seq = ntohs(icmp->icmp_seq); + + } + else { /* AF_INET6 */ + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf; + + type = icmp6->icmp6_type; + + recv_id = ntohs(icmp6->icmp6_id); + recv_seq = ntohs(icmp6->icmp6_seq); + } + + if(recv_id != ident) + return NULL; + + pb = probe_by_seq(recv_seq); + if(!pb) + return NULL; + + if(!err) { + + if(!(af == AF_INET && type == ICMP_ECHOREPLY) && + !(af == AF_INET6 && type == ICMP6_ECHO_REPLY) + ) + return NULL; + + pb->final = 1; + } + + return pb; +} + +static void icmp_recv_probe(int sk, int revents) { + + if(!(revents & (POLLIN | POLLERR))) + return; + + recv_reply(sk, !!(revents & POLLERR), icmp_check_reply); +} + +static void icmp_expire_probe(probe *pb) { + + probe_done(pb); +} + +static tr_module icmp_ops = { + .name = "icmp", + .init = icmp_init, + .send_probe = icmp_send_probe, + .recv_probe = icmp_recv_probe, + .expire_probe = icmp_expire_probe, + .options = icmp_options, +}; + +TR_MODULE(icmp_ops); + +void tr_module_icmp_insert() +{ + +} + diff --git a/libdap-chain-net/iputils/traceroute/mod-raw.c b/libdap-chain-net/iputils/traceroute/mod-raw.c new file mode 100644 index 0000000000000000000000000000000000000000..bd9f05ba612569e5fbfdb1a3aec01990a55dd8a0 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/mod-raw.c @@ -0,0 +1,172 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netdb.h> + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; +static int protocol = DEF_RAW_PROT; + +static char *data = NULL; +static size_t *length_p; + +static int raw_sk = -1; +static int last_ttl = 0; +static int seq = 0; + + +static int set_protocol (CLIF_option *optn, char *arg) { + UNUSED(optn); + char *q; + + protocol = strtoul (arg, &q, 0); + if (q == arg) { + struct protoent *p = getprotobyname (arg); + + if (!p) return -1; + protocol = p->p_proto; + } + + return 0; +} + + +static CLIF_option raw_options[] = { + { 0, "protocol", "PROT", "Use protocol %s (default is " + _TEXT (DEF_RAW_PROT) ")", + set_protocol, 0, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + + +static int raw_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int i; + int af = dest->sa.sa_family; + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; + + if (port_seq) protocol = port_seq; + + + length_p = packet_len_p; + + if (*length_p && + !(data = malloc (*length_p)) + ) error ("malloc"); + + for (i = 0; i < (int)*length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + + raw_sk = socket (af, SOCK_RAW, protocol); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); + + /* Don't want to catch packets from another hosts */ + if (raw_can_connect () && + connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0 + ) error ("connect"); + + use_recverr (raw_sk); + + + add_poll (raw_sk, POLLIN | POLLERR); + + return 0; +} + + +static void raw_send_probe (probe *pb, int ttl) { + + if (ttl != last_ttl) { + + set_ttl (raw_sk, ttl); + + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, data, *length_p, &dest_addr) < 0) { + pb->send_time = 0; + return; + } + + + pb->seq = ++seq; + + return; +} + + +static probe *raw_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + UNUSED(sk); + UNUSED(len); + UNUSED(buf); + probe *pb; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (seq); + if (!pb) return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void raw_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), raw_check_reply); +} + + +static void raw_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module raw_ops = { + .name = "raw", + .init = raw_init, + .send_probe = raw_send_probe, + .recv_probe = raw_recv_probe, + .expire_probe = raw_expire_probe, + .options = raw_options, + .one_per_time = 1, +}; + +TR_MODULE (raw_ops); + +void tr_module_raw_insert() +{ + +} diff --git a/libdap-chain-net/iputils/traceroute/mod-tcp.c b/libdap-chain-net/iputils/traceroute/mod-tcp.c new file mode 100644 index 0000000000000000000000000000000000000000..32d2f1027e88db3ff623150cfcca5b7aad7ccf8d --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/mod-tcp.c @@ -0,0 +1,515 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> + + +#include "traceroute.h" + + +#ifndef IP_MTU +#define IP_MTU 14 +#endif + + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int dest_port = 0; + +static int raw_sk = -1; +static int last_ttl = 0; + +static uint8_t buf[1024]; /* enough, enough... */ +static size_t csum_len = 0; +static struct tcphdr *th = NULL; + +#define TH_FLAGS(TH) (((uint8_t *) (TH))[13]) +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 + + +static int flags = 0; /* & 0xff == tcp_flags ... */ +static int sysctl = 0; +static int reuse = 0; +static unsigned int mss = 0; +static int info = 0; + +#define FL_FLAGS 0x0100 +#define FL_ECN 0x0200 +#define FL_SACK 0x0400 +#define FL_TSTAMP 0x0800 +#define FL_WSCALE 0x1000 + + +static struct { + const char *name; + unsigned int flag; +} tcp_flags[] = { + { "fin", TH_FIN }, + { "syn", TH_SYN }, + { "rst", TH_RST }, + { "psh", TH_PSH }, + { "ack", TH_ACK }, + { "urg", TH_URG }, + { "ece", TH_ECE }, + { "cwr", TH_CWR }, +}; + +static char *names_by_flags (unsigned int flags) { + int i; + char str[64]; /* enough... */ + char *curr = str; + char *end = str + sizeof (str) / sizeof (*str); + + for (i = 0; i < (int)(sizeof (tcp_flags) / sizeof (*tcp_flags)); i++) { + const char *p; + + if (!(flags & tcp_flags[i].flag)) continue; + + if (curr > str && curr < end) *curr++ = ','; + for (p = tcp_flags[i].name; *p && curr < end; *curr++ = *p++) ; + } + + *curr = '\0'; + + return strdup (str); +} + +static int set_tcp_flag (CLIF_option *optn, char *arg) { + UNUSED(arg); + int i; + + for (i = 0; i < (int)(sizeof (tcp_flags) / sizeof (*tcp_flags)); i++) { + if (!strcmp (optn->long_opt, tcp_flags[i].name)) { + flags |= tcp_flags[i].flag; + return 0; + } + } + + return -1; +} + +static int set_tcp_flags (CLIF_option *optn, char *arg) { + UNUSED(optn); + char *q; + unsigned long value; + + value = strtoul (arg, &q, 0); + if (q == arg) return -1; + + flags = (flags & ~0xff) | (value & 0xff) | FL_FLAGS; + return 0; +} + +static int set_flag (CLIF_option *optn, char *arg) { + UNUSED(arg); + flags |= (unsigned long) optn->data; + + return 0; +} + +static CLIF_option tcp_options[] = { + { 0, "syn", 0, "Set tcp flag SYN (default if no other " + "tcp flags specified)", set_tcp_flag, 0, 0, 0 }, + { 0, "ack", 0, "Set tcp flag ACK,", set_tcp_flag, 0, 0, 0 }, + { 0, "fin", 0, "FIN,", set_tcp_flag, 0, 0, 0 }, + { 0, "rst", 0, "RST,", set_tcp_flag, 0, 0, 0 }, + { 0, "psh", 0, "PSH,", set_tcp_flag, 0, 0, 0 }, + { 0, "urg", 0, "URG,", set_tcp_flag, 0, 0, 0 }, + { 0, "ece", 0, "ECE,", set_tcp_flag, 0, 0, 0 }, + { 0, "cwr", 0, "CWR", set_tcp_flag, 0, 0, 0 }, + { 0, "flags", "NUM", "Set tcp flags exactly to value %s", + set_tcp_flags, 0, 0, CLIF_ABBREV }, + { 0, "ecn", 0, "Send syn packet with tcp flags ECE and CWR " + "(for Explicit Congestion Notification, rfc3168)", + set_flag, (void *) FL_ECN, 0, 0 }, + { 0, "sack", 0, "Use sack,", + set_flag, (void *) FL_SACK, 0, 0 }, + { 0, "timestamps", 0, "timestamps,", + set_flag, (void *) FL_TSTAMP, 0, CLIF_ABBREV }, + { 0, "window_scaling", 0, "window_scaling option for tcp", + set_flag, (void *) FL_WSCALE, 0, CLIF_ABBREV }, + { 0, "sysctl", 0, "Use current sysctl (/proc/sys/net/*) setting " + "for the tcp options and ecn. Always set by default " + "(with \"syn\") if nothing else specified", + CLIF_set_flag, &sysctl, 0, 0 }, + { 0, "reuse", 0, "Allow to reuse local port numbers " + "for the huge workloads (SO_REUSEADDR)", + CLIF_set_flag, &reuse, 0, 0 }, + { 0, "mss", "NUM", "Use value of %s for maxseg tcp option (when syn)", + CLIF_set_uint, &mss, 0, 0 }, + { 0, "info", 0, "Print tcp flags of final tcp replies when target " + "host is reached. Useful to determine whether " + "an application listens the port etc.", + CLIF_set_flag, &info, 0, 0 }, + CLIF_END_OPTION +}; + + +#define SYSCTL_PREFIX "/proc/sys/net/ipv4/tcp_" +static int check_sysctl (const char *name) { + int fd, res; + char buf[sizeof (SYSCTL_PREFIX) + strlen (name) + 1]; + uint8_t ch; + + strcpy (buf, SYSCTL_PREFIX); + strcat (buf, name); + + fd = open (buf, O_RDONLY, 0); + if (fd < 0) return 0; + + res = read (fd, &ch, sizeof (ch)); + close (fd); + + if (res != sizeof (ch)) + return 0; + + /* since kernel 2.6.31 "tcp_ecn" can have value of '2'... */ + if (ch == '1') return 1; + + return 0; +} + + +static int tcp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + sockaddr_any src; + int mtu; + socklen_t len; + uint8_t *ptr; + uint16_t *lenp; + + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; /* raw sockets can be confused */ + + if (!port_seq) port_seq = DEF_TCP_PORT; + dest_port = htons (port_seq); + + + /* Create raw socket for tcp */ + + raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); /* including bind, if any */ + + if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + len = sizeof (src); + if (getsockname (raw_sk, &src.sa, &len) < 0) + error ("getsockname"); + + + len = sizeof (mtu); + if (getsockopt (raw_sk, af == AF_INET ? SOL_IP : SOL_IPV6, + af == AF_INET ? IP_MTU : IPV6_MTU, + &mtu, &len) < 0 || mtu < 576 + ) mtu = 576; + + /* mss = mtu - headers */ + mtu -= af == AF_INET ? sizeof (struct iphdr) : sizeof (struct ip6_hdr); + mtu -= sizeof (struct tcphdr); + + + if (!raw_can_connect ()) { /* work-around for buggy kernels */ + close (raw_sk); + raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); + if (raw_sk < 0) error ("socket"); + tune_socket (raw_sk); + /* but do not connect it... */ + } + + + use_recverr (raw_sk); + + add_poll (raw_sk, POLLIN | POLLERR); + + + /* Now create the sample packet. */ + + if (!flags) sysctl = 1; + + if (sysctl) { + if (check_sysctl ("ecn")) flags |= FL_ECN; + if (check_sysctl ("sack")) flags |= FL_SACK; + if (check_sysctl ("timestamps")) flags |= FL_TSTAMP; + if (check_sysctl ("window_scaling")) flags |= FL_WSCALE; + } + + if (!(flags & (FL_FLAGS | 0xff))) { /* no any tcp flag set */ + flags |= TH_SYN; + if (flags & FL_ECN) + flags |= TH_ECE | TH_CWR; + } + + + /* For easy checksum computing: + saddr + daddr + length + protocol + tcphdr + tcpoptions + */ + + ptr = buf; + + if (af == AF_INET) { + len = sizeof (src.sin.sin_addr); + memcpy (ptr, &src.sin.sin_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin.sin_addr, len); + ptr += len; + } else { + len = sizeof (src.sin6.sin6_addr); + memcpy (ptr, &src.sin6.sin6_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin6.sin6_addr, len); + ptr += len; + } + + lenp = (uint16_t *) ptr; + ptr += sizeof (uint16_t); + *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_TCP); + ptr += sizeof (uint16_t); + + + /* Construct TCP header */ + + th = (struct tcphdr *) ptr; + + th->source = 0; /* temporary */ + th->dest = dest_port; + th->seq = 0; /* temporary */ + th->ack_seq = 0; + th->doff = 0; /* later... */ + TH_FLAGS(th) = flags & 0xff; + th->window = htons (4 * mtu); + th->check = 0; + th->urg_ptr = 0; + + + /* Build TCP options */ + + ptr = (uint8_t *) (th + 1); + + if (flags & TH_SYN) { + *ptr++ = TCPOPT_MAXSEG; /* 2 */ + *ptr++ = TCPOLEN_MAXSEG; /* 4 */ + *((uint16_t *) ptr) = htons (mss ? mss : (unsigned int)mtu); + ptr += sizeof (uint16_t); + } + + if (flags & FL_TSTAMP) { + + if (flags & FL_SACK) { + *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ + *ptr++ = TCPOLEN_SACK_PERMITTED;/* 2 */ + } else { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_NOP; /* 1 */ + } + *ptr++ = TCPOPT_TIMESTAMP; /* 8 */ + *ptr++ = TCPOLEN_TIMESTAMP; /* 10 */ + + *((uint32_t *) ptr) = random_seq (); /* really! */ + ptr += sizeof (uint32_t); + *((uint32_t *) ptr) = (flags & TH_ACK) ? random_seq () : 0; + ptr += sizeof (uint32_t); + } + else if (flags & FL_SACK) { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ + *ptr++ = TCPOLEN_SACK_PERMITTED; /* 2 */ + } + + if (flags & FL_WSCALE) { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_WINDOW; /* 3 */ + *ptr++ = TCPOLEN_WINDOW; /* 3 */ + *ptr++ = 2; /* assume some corect value... */ + } + + + csum_len = ptr - buf; + + if (csum_len > sizeof (buf)) + error ("impossible"); /* paranoia */ + + len = ptr - (uint8_t *) th; + if (len & 0x03) error ("impossible"); /* as >>2 ... */ + + *lenp = htons (len); + th->doff = len >> 2; + + + *packet_len_p = len; + + return 0; +} + + +static void tcp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t len = sizeof (addr); + + + /* To make sure we have chosen a free unused "source port", + just create, (auto)bind and hold a socket while the port is needed. + */ + + sk = socket (af, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + if (reuse && setsockopt (sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) + error ("setsockopt SO_REUSEADDR"); + + bind_socket (sk); + + if (getsockname (sk, &addr.sa, &len) < 0) + error ("getsockname"); + + /* When we reach the target host, it can send us either RST or SYN+ACK. + For RST all is OK (we and kernel just answer nothing), but + for SYN+ACK we should reply with our RST. + It is well-known "half-open technique", used by port scanners etc. + This way we do not touch remote applications at all, unlike + the ordinary connect(2) call. + As the port-holding socket neither connect() nor listen(), + it means "no such port yet" for remote ends, and kernel always + send RST in such a situation automatically (we have to do nothing). + */ + + + th->source = addr.sin.sin_port; + + th->seq = random_seq (); + + th->check = 0; + th->check = in_csum (buf, csum_len); + + + if (ttl != last_ttl) { + + set_ttl (raw_sk, ttl); + + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, th, th->doff << 2, &dest_addr) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->seq = th->source; + + pb->sk = sk; + + return; +} + + +static probe *tcp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + UNUSED(sk); + probe *pb; + struct tcphdr *tcp = (struct tcphdr *) buf; + uint16_t sport, dport; + + + if (len < 8) return NULL; /* too short */ + + + if (err) { + sport = tcp->source; + dport = tcp->dest; + } else { + sport = tcp->dest; + dport = tcp->source; + } + + + if (dport != dest_port) + return NULL; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (sport); + if (!pb) return NULL; + + + if (!err) { + + pb->final = 1; + + if (info) + pb->ext = names_by_flags (TH_FLAGS(tcp)); + } + + return pb; +} + + +static void tcp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), tcp_check_reply); +} + + +static void tcp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module tcp_ops = { + .name = "tcp", + .init = tcp_init, + .send_probe = tcp_send_probe, + .recv_probe = tcp_recv_probe, + .expire_probe = tcp_expire_probe, + .options = tcp_options, +}; + +TR_MODULE (tcp_ops); + +void tr_module_tcp_insert() +{ + +} diff --git a/libdap-chain-net/iputils/traceroute/mod-tcpconn.c b/libdap-chain-net/iputils/traceroute/mod-tcpconn.c new file mode 100644 index 0000000000000000000000000000000000000000..13e239864756de3639330189389933df570e8bf6 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/mod-tcpconn.c @@ -0,0 +1,237 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> +#include <errno.h> + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; + +static int icmp_sk = -1; + + +static int tcp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + UNUSED(packet_len_p); + int af = dest->sa.sa_family; + + dest_addr = *dest; + dest_addr.sin.sin_port = htons (DEF_TCP_PORT); + + if (port_seq) + dest_addr.sin.sin_port = htons (port_seq); + + + /* Currently an ICMP socket is the only way + to obtain the needed info... + */ + icmp_sk = socket (af, SOCK_RAW, (af == AF_INET) ? IPPROTO_ICMP + : IPPROTO_ICMPV6); + if (icmp_sk < 0) + error_or_perm ("socket"); + + /* icmp_sk not need full tune_socket() here, just a receiving one */ + bind_socket (icmp_sk); + use_timestamp (icmp_sk); + use_recv_ttl (icmp_sk); + + add_poll (icmp_sk, POLLIN); + + return 0; +} + + +static void tcp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t length = sizeof (addr); + + + sk = socket (af, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + tune_socket (sk); /* common stuff */ + + set_ttl (sk, ttl); + + + pb->send_time = get_time (); + + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) { + if (errno != EINPROGRESS) + error ("connect"); + } + + + if (getsockname (sk, &addr.sa, &length) < 0) + error ("getsockname"); + + pb->seq = addr.sin.sin_port; /* both ipv4/ipv6 */ + + pb->sk = sk; + + add_poll (sk, POLLERR | POLLHUP | POLLOUT); + + return; +} + + +static probe *tcp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + UNUSED(sk); + UNUSED(err); + UNUSED(from); + int af = dest_addr.sa.sa_family; + int type, code, info; + probe *pb; + struct tcphdr *tcp; + + + if (len < sizeof (struct icmphdr)) + return NULL; + + + if (af == AF_INET) { + struct icmp *icmp = (struct icmp *) buf; + struct iphdr *ip; + int hlen; + + type = icmp->icmp_type; + code = icmp->icmp_code; + info = icmp->icmp_void; + + if (type != ICMP_TIME_EXCEEDED && type != ICMP_DEST_UNREACH) + return NULL; + + if (len < sizeof (struct icmphdr) + sizeof (struct iphdr) + 8) + /* `8' - rfc1122: 3.2.2 */ + return NULL; + + ip = (struct iphdr *) (((char *)icmp) + sizeof(struct icmphdr)); + hlen = ip->ihl << 2; + + if (len < sizeof (struct icmphdr) + hlen + 8) + return NULL; + if (ip->protocol != IPPROTO_TCP) + return NULL; + + tcp = (struct tcphdr *) (((char *) ip) + hlen); + + } + else { /* AF_INET6 */ + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf; + struct ip6_hdr *ip6; + + type = icmp6->icmp6_type; + code = icmp6->icmp6_code; + info = icmp6->icmp6_mtu; + + if (type != ICMP6_TIME_EXCEEDED && + type != ICMP6_DST_UNREACH && + type != ICMP6_PACKET_TOO_BIG + ) return NULL; + + if (len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + 8) + return NULL; + + ip6 = (struct ip6_hdr *) (icmp6 + 1); + if (ip6->ip6_nxt != IPPROTO_TCP) + return NULL; + + tcp = (struct tcphdr *) (ip6 + 1); + + } + + + if (tcp->dest != dest_addr.sin.sin_port) + return NULL; + + pb = probe_by_seq (tcp->source); + if (!pb) return NULL; + + + /* here only, high level has no data to do this */ + parse_icmp_res (pb, type, code, info); + + return pb; +} + + +static void tcp_recv_probe (int sk, int revents) { + + if (sk != icmp_sk) { /* a tcp socket */ + probe *pb; + + pb = probe_by_sk (sk); + if (!pb) { + del_poll (sk); + return; + } + + + /* do connect() again and check errno, regardless of revents */ + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) { + if (errno != EISCONN && errno != ECONNREFUSED) + return; /* ICMP say more */ + } + + /* we have reached the dest host (either connected or refused) */ + + memcpy (&pb->res, &dest_addr, sizeof (pb->res)); + + pb->final = 1; + + pb->recv_time = get_time (); + + probe_done (pb); + + return; + } + + + /* ICMP stuff */ + + if (!(revents & POLLIN)) + return; + + recv_reply (icmp_sk, 0, tcp_check_reply); +} + + +static void tcp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module tcp_ops = { + .name = "tcpconn", + .init = tcp_init, + .send_probe = tcp_send_probe, + .recv_probe = tcp_recv_probe, + .expire_probe = tcp_expire_probe, +}; + +TR_MODULE (tcp_ops); + +void tr_module_tcpconn_insert() +{ + +} diff --git a/libdap-chain-net/iputils/traceroute/mod-udp.c b/libdap-chain-net/iputils/traceroute/mod-udp.c new file mode 100644 index 0000000000000000000000000000000000000000..e24d0271c8c6e60f40592f0be18e3e7c7b482379 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/mod-udp.c @@ -0,0 +1,241 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/in.h> +#include <netinet/udp.h> + +#include "traceroute.h" + + +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +#ifndef UDPLITE_SEND_CSCOV +#define UDPLITE_SEND_CSCOV 10 +#define UDPLITE_RECV_CSCOV 11 +#endif + + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int curr_port = 0; +static unsigned int protocol = IPPROTO_UDP; + + +static char *data = NULL; +static size_t *length_p; + +static void fill_data (size_t *packet_len_p) { + int i; + + length_p = packet_len_p; + + if (*length_p && + !(data = malloc (*length_p)) + ) error ("malloc"); + + for (i = 0; i < (int)*length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + return; +} + + +static int udp_default_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + curr_port = port_seq ? port_seq : DEF_START_PORT; + + dest_addr = *dest; + dest_addr.sin.sin_port = htons (curr_port); + + fill_data (packet_len_p); + + return 0; +} + + +static int udp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + dest_addr = *dest; + + if (!port_seq) port_seq = DEF_UDP_PORT; + dest_addr.sin.sin_port = htons ((uint16_t) port_seq); + + fill_data (packet_len_p); + + return 0; +} + + +static unsigned int coverage = 0; +#define MIN_COVERAGE (sizeof (struct udphdr)) + +static void set_coverage (int sk) { + int val = MIN_COVERAGE; + + if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, + &coverage, sizeof (coverage)) < 0 + ) error ("UDPLITE_SEND_CSCOV"); + + if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, + &val, sizeof (val)) < 0 + ) error ("UDPLITE_RECV_CSCOV"); +} + +static CLIF_option udplite_options[] = { + { 0, "coverage", "NUM", "Set udplite send coverage to %s (default is " + _TEXT(MIN_COVERAGE) ")", + CLIF_set_uint, &coverage, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + +static int udplite_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + dest_addr = *dest; + + if (!port_seq) port_seq = DEF_UDP_PORT; /* XXX: Hmmm... */ + dest_addr.sin.sin_port = htons ((uint16_t) port_seq); + + protocol = IPPROTO_UDPLITE; + + if (!coverage) coverage = MIN_COVERAGE; + + fill_data (packet_len_p); + + return 0; +} + + +static void udp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + + + sk = socket (af, SOCK_DGRAM, protocol); + if (sk < 0) error ("socket"); + + tune_socket (sk); /* common stuff */ + + if (coverage) set_coverage (sk); /* udplite case */ + + set_ttl (sk, ttl); + + + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + use_recverr (sk); + + + pb->send_time = get_time (); + + if (do_send (sk, data, *length_p, NULL) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->sk = sk; + + add_poll (sk, POLLIN | POLLERR); + + pb->seq = dest_addr.sin.sin_port; + + if (curr_port) { /* traditional udp method */ + curr_port++; + dest_addr.sin.sin_port = htons (curr_port); /* both ipv4 and ipv6 */ + } + + return; +} + + +static probe *udp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + UNUSED(buf); + UNUSED(len); + probe *pb; + + pb = probe_by_sk (sk); + if (!pb) return NULL; + + if (pb->seq != from->sin.sin_port) + return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void udp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), udp_check_reply); +} + + +static void udp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +/* All three modules share the same methods except the init... */ + +static tr_module default_ops = { + .name = "default", + .init = udp_default_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), +}; + +TR_MODULE (default_ops); + + +static tr_module udp_ops = { + .name = "udp", + .init = udp_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), +}; + +TR_MODULE (udp_ops); + + +static tr_module udplite_ops = { + .name = "udplite", + .init = udplite_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), + .options = udplite_options, +}; + +TR_MODULE (udplite_ops); + +void tr_module_udp_insert() +{ + +} diff --git a/libdap-chain-net/iputils/traceroute/module.c b/libdap-chain-net/iputils/traceroute/module.c new file mode 100644 index 0000000000000000000000000000000000000000..f8f228ef635758a62b036a87986cdfae27f210bb --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/module.c @@ -0,0 +1,51 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#include "traceroute.h" + +void tr_module_icmp_insert(); +void tr_module_udp_insert(); +void tr_module_tcp_insert(); +void tr_module_tcpconn_insert(); +void tr_module_raw_insert(); +void tr_module_dccp_insert(); + +static tr_module *base = NULL; + +void tr_register_module(tr_module *ops) { + + ops->next = base; + base = ops; +// printf("tr_register_module name=%s\n", ops->name); +} + +const tr_module *tr_get_module(const char *name) { + const tr_module *ops; + + tr_module_icmp_insert(); + tr_module_udp_insert(); + tr_module_tcp_insert(); + tr_module_tcpconn_insert(); + tr_module_raw_insert(); + tr_module_dccp_insert(); + + if(!name) + return 0; + + for(ops = base; ops; ops = ops->next) { + if(!strcasecmp(name, ops->name)) + return ops; + } + + return NULL; +} diff --git a/libdap-chain-net/iputils/traceroute/poll.c b/libdap-chain-net/iputils/traceroute/poll.c new file mode 100644 index 0000000000000000000000000000000000000000..232a83395bc600ab54bddb1d8ea7c98e3ceb8526 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/poll.c @@ -0,0 +1,93 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <poll.h> +#include <errno.h> +#include <math.h> + +#include "traceroute.h" + + +static struct pollfd *pfd = NULL; +static unsigned int num_polls = 0; + + +void add_poll (int fd, int events) { + int i; + + for (i = 0; i < (int)num_polls && pfd[i].fd > 0; i++) ; + + if (i == (int)num_polls) { + pfd = realloc (pfd, ++num_polls * sizeof (*pfd)); + if (!pfd) error ("realloc"); + } + + pfd[i].fd = fd; + pfd[i].events = events; +} + + +void del_poll (int fd) { + int i; + + for (i = 0; i < (int)num_polls && pfd[i].fd != fd; i++) ; + + if (i < (int)num_polls) pfd[i].fd = -1; /* or just zero it... */ +} + + +static int cleanup_polls (void) { + int i; + + for (i = 0; i < (int)num_polls && pfd[i].fd > 0; i++) ; + + if (i < (int)num_polls) { /* a hole have found */ + int j; + + for (j = i + 1; j < (int)num_polls; j++) { + if (pfd[j].fd > 0) { + pfd[i++] = pfd[j]; + pfd[j].fd = -1; + } + } + } + + return i; +} + + +void do_poll (double timeout, void (*callback) (int fd, int revents)) { + int nfds; + int msecs = ceil (timeout * 1000); + + while ((nfds = cleanup_polls ()) > 0) { + int i, n; + + n = poll (pfd, nfds, msecs); + + if (n <= 0) { + if (n == 0 || errno == EINTR) + return; + error ("poll"); + } + + for (i = 0; n && i < (int)num_polls; i++) { + if (pfd[i].revents) { + callback (pfd[i].fd, pfd[i].revents); + n--; + } + } + + msecs = 0; /* no more wait, just eat all the pending */ + } + + return; +} + diff --git a/libdap-chain-net/iputils/traceroute/random.c b/libdap-chain-net/iputils/traceroute/random.c new file mode 100644 index 0000000000000000000000000000000000000000..5a8f911e1463b797559b263b5d9cd7081a82a428 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/random.c @@ -0,0 +1,28 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/times.h> + +#include "traceroute.h" + + +static void __init_random_seq (void) __attribute__ ((constructor)); +static void __init_random_seq (void) { + + srand (times (NULL) + getpid ()); +} + + +unsigned int random_seq (void) { + + /* To not worry about RANDOM_MAX and precision... */ + return (rand () << 16) ^ (rand () << 8) ^ rand () ^ (rand () >> 8); +} + diff --git a/libdap-chain-net/iputils/traceroute/time.c b/libdap-chain-net/iputils/traceroute/time.c new file mode 100644 index 0000000000000000000000000000000000000000..16ea82483cc3483ada3a62c81c8436f820667191 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/time.c @@ -0,0 +1,27 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> + +#include "traceroute.h" + + +/* Just returns current time as double, with most possible precision... */ + +double get_time (void) { + struct timeval tv; + double d; + + gettimeofday (&tv, NULL); + + d = ((double) tv.tv_usec) / 1000000. + (unsigned long) tv.tv_sec; + + return d; +} diff --git a/libdap-chain-net/iputils/traceroute/traceroute.c b/libdap-chain-net/iputils/traceroute/traceroute.c new file mode 100755 index 0000000000000000000000000000000000000000..a0dbeb2e233478034ab0b0fb6caec1a7285f74ed --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/traceroute.c @@ -0,0 +1,1758 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netdb.h> +#include <errno.h> +#include <locale.h> +#include <sys/utsname.h> +#include <linux/types.h> +#include <linux/errqueue.h> + +/* XXX: Remove this when things will be defined properly in netinet/ ... */ +#include "flowlabel.h" + +//#include "version.h" +#include <stdbool.h> +//#include <glib.h> +#include "traceroute.h" + +#ifndef ICMP6_DST_UNREACH_BEYONDSCOPE +#ifdef ICMP6_DST_UNREACH_NOTNEIGHBOR +#define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR +#else +#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 +#endif +#endif + +#ifndef IPV6_RECVHOPLIMIT +#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif + +#ifndef IP_PMTUDISC_PROBE +#define IP_PMTUDISC_PROBE 3 +#endif + +#ifndef IPV6_PMTUDISC_PROBE +#define IPV6_PMTUDISC_PROBE 3 +#endif + +#ifndef AI_IDN +#define AI_IDN 0 +#endif + +#ifndef NI_IDN +#define NI_IDN 0 +#endif + +#define MAX_HOPS 255 +#define MAX_PROBES 10 +#define MAX_GATEWAYS_4 8 +#define MAX_GATEWAYS_6 127 +#define DEF_HOPS 30 +#define DEF_SIM_PROBES 16 /* including several hops */ +#define DEF_NUM_PROBES 3 //1 +#define DEF_WAIT_SECS 5.0 +#define DEF_HERE_FACTOR 3 +#define DEF_NEAR_FACTOR 10 +#ifndef DEF_WAIT_PREC +#define DEF_WAIT_PREC 0.001 /* +1 ms to avoid precision issues */ +#endif +#define DEF_SEND_SECS 0 +#define DEF_DATA_LEN 40 /* all but IP header... */ +#define MAX_PACKET_LEN 65000 +#ifndef DEF_AF +#define DEF_AF AF_INET +#endif + +#define ttl2hops(X) (((X) <= 64 ? 65 : ((X) <= 128 ? 129 : 256)) - (X)) + +static char version_string[] = "Modern traceroute for Linux, " + "version " _TEXT(VERSION) +"\nCopyright (c) 2016 Dmitry Butskoy, " +" License: GPL v2 or any later"; +static int debug = 0; +static unsigned int first_hop = 1; +static unsigned int max_hops = DEF_HOPS; +static unsigned int sim_probes = DEF_SIM_PROBES; +static unsigned int probes_per_hop = DEF_NUM_PROBES; + +static char **gateways = NULL; +static int num_gateways = 0; +static unsigned char *rtbuf = NULL; +static size_t rtbuf_len = 0; +static unsigned int ipv6_rthdr_type = 2; /* IPV6_RTHDR_TYPE_2 */ + +static size_t header_len = 0; +static size_t data_len = 0; + +static int dontfrag = 0; +static int noresolve = 0; +static int extension = 0; +static int as_lookups = 0; +static unsigned int dst_port_seq = 0; +static unsigned int tos = 0; +static unsigned int flow_label = 0; +static int noroute = 0; +static unsigned int fwmark = 0; +static int packet_len = -1; +static double wait_secs = DEF_WAIT_SECS; +static double here_factor = DEF_HERE_FACTOR; +static double near_factor = DEF_NEAR_FACTOR; +static double send_secs = DEF_SEND_SECS; +static int mtudisc = 0; +static int backward = 0; + +static sockaddr_any dst_addr = { { 0, }, }; +static char *dst_name = NULL; +static char *device = NULL; +static sockaddr_any src_addr = { { 0, }, }; +static unsigned int src_port = 0; + +static const char *module = "default"; +static const tr_module *ops = NULL; + +static char *opts[16] = { NULL, }; /* assume enough */ +static unsigned int opts_idx = 1; /* first one reserved... */ + +static int af = 0; + +static probe *probes = NULL; +static unsigned int num_probes = 0; + +static void ex_error(const char *format, ...) { + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + //exit(2); +} + +void error(const char *str) { + + fprintf(stderr, "\n"); + + perror(str); + + exit(1); +} + +void error_or_perm(const char *str) { + + if(errno == EPERM) + fprintf(stderr, "You do not have enough privileges to use " + "this traceroute method."); + error(str); +} + +/* Set initial parameters according to how we was called */ + +static void check_progname(const char *name) { + const char *p; + int l; + + p = strrchr(name, '/'); + if(p) + p++; + else + p = name; + + l = strlen(p); + if(l <= 0) + return; + l--; + + if(p[l] == '6') + af = AF_INET6; + else if(p[l] == '4') + af = AF_INET; + + if(!strncmp(p, "tcp", 3)) + module = "tcp"; + if(!strncmp(p, "tracert", 7)) + module = "icmp"; + + return; +} + +static int getaddr(const char *name, sockaddr_any *addr) { + int ret; + struct addrinfo hints, *ai, *res = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_flags = AI_IDN; + + ret = getaddrinfo(name, NULL, &hints, &res); + if(ret) { + fprintf(stderr, "%s: %s\n", name, gai_strerror(ret)); + return -1; + } + + for(ai = res; ai; ai = ai->ai_next) { + if(ai->ai_family == af) + break; + /* when af not specified, choose DEF_AF if present */ + if(!af && ai->ai_family == DEF_AF) + break; + } + if(!ai) + ai = res; /* anything... */ + + if(ai->ai_addrlen > sizeof(*addr)) + return -1; /* paranoia */ + memcpy(addr, ai->ai_addr, ai->ai_addrlen); + + freeaddrinfo(res); + + return 0; +} + +static void make_fd_used(int fd) { + int nfd; + + if(fcntl(fd, F_GETFL) != -1) + return; + + if(errno != EBADF) + error("fcntl F_GETFL"); + + nfd = open("/dev/null", O_RDONLY); + if(nfd < 0) + error("open /dev/null"); + + if(nfd != fd) { + dup2(nfd, fd); + close(nfd); + } + + return; +} + +static char addr2str_buf[INET6_ADDRSTRLEN]; + +static const char *addr2str(const sockaddr_any *addr) { + + getnameinfo(&addr->sa, sizeof(*addr), + addr2str_buf, sizeof(addr2str_buf), 0, 0, NI_NUMERICHOST); + + return addr2str_buf; +} + +/* IP options stuff */ + +static int init_ip_options(void) { + sockaddr_any *gates; + int i, max; + + if(!num_gateways) + return 0; + + /* check for TYPE,ADDR,ADDR... form */ + if(af == AF_INET6 && num_gateways > 1 && gateways[0]) { + char *q; + unsigned int value = strtoul(gateways[0], &q, 0); + + if(!*q) { + ipv6_rthdr_type = value; + num_gateways--; + for(i = 0; i < num_gateways; i++) + gateways[i] = gateways[i + 1]; + } + } + + max = af == AF_INET ? MAX_GATEWAYS_4 : MAX_GATEWAYS_6; + if(num_gateways > max) { + ex_error("Too many gateways specified. No more than %d", max); + return -1; + } + + gates = alloca(num_gateways * sizeof(*gates)); + + for(i = 0; i < num_gateways; i++) { + + if(!gateways[i]) + error("strdup"); + + if(getaddr(gateways[i], &gates[i]) < 0) { + ex_error(""); /* already reported */ + return -1; + } + if(gates[i].sa.sa_family != af) { + ex_error("IP versions mismatch in gateway addresses"); + return -1; + } + + free(gateways[i]); + } + + free(gateways); + gateways = NULL; + + if(af == AF_INET) { + struct in_addr *in; + + rtbuf_len = 4 + (num_gateways + 1) * sizeof(*in); + rtbuf = malloc(rtbuf_len); + if(!rtbuf) + error("malloc"); + + in = (struct in_addr *) &rtbuf[4]; + for(i = 0; i < num_gateways; i++) + memcpy(&in[i], &gates[i].sin.sin_addr, sizeof(*in)); + /* final hop */ + memcpy(&in[i], &dst_addr.sin.sin_addr, sizeof(*in)); + i++; + + rtbuf[0] = IPOPT_NOP; + rtbuf[1] = IPOPT_LSRR; + rtbuf[2] = (i * sizeof(*in)) + 3; + rtbuf[3] = IPOPT_MINOFF; + + } + else if(af == AF_INET6) { + struct in6_addr *in6; + struct ip6_rthdr *rth; + + /* IPV6_RTHDR_TYPE_0 length is 8 */ + rtbuf_len = 8 + num_gateways * sizeof(*in6); + rtbuf = malloc(rtbuf_len); + if(!rtbuf) + error("malloc"); + + rth = (struct ip6_rthdr *) rtbuf; + rth->ip6r_nxt = 0; + rth->ip6r_len = 2 * num_gateways; + rth->ip6r_type = ipv6_rthdr_type; + rth->ip6r_segleft = num_gateways; + + *((uint32_t *) (rth + 1)) = 0; + + in6 = (struct in6_addr *) (rtbuf + 8); + for(i = 0; i < num_gateways; i++) + memcpy(&in6[i], &gates[i].sin6.sin6_addr, sizeof(*in6)); + } + + return 0; +} + +/* Command line stuff */ +__attribute__((unused)) +static int set_af(CLIF_option *optn, char *arg) { + UNUSED(arg); + int vers = (long) optn->data; + + if(vers == 4) + af = AF_INET; + else if(vers == 6) + af = AF_INET6; + else + return -1; + + return 0; +} + +static int add_gateway(CLIF_option *optn, char *arg) { + UNUSED(optn); + if(num_gateways >= MAX_GATEWAYS_6) { /* 127 > 8 ... :) */ + fprintf(stderr, "Too many gateways specified."); + return -1; + } + + gateways = realloc(gateways, (num_gateways + 1) * sizeof(*gateways)); + if(!gateways) + error("malloc"); + gateways[num_gateways++] = strdup(arg); + + return 0; +} + +static int set_source(CLIF_option *optn, char *arg) { + UNUSED(optn); + return getaddr(arg, &src_addr); +} + +static int set_port(CLIF_option *optn, char *arg) { + unsigned int *up = (unsigned int *) optn->data; + char *q; + + *up = strtoul(arg, &q, 0); + if(q == arg) { + struct servent *s = getservbyname(arg, NULL); + + if(!s) + return -1; + *up = ntohs(s->s_port); + } + + return 0; +} + +static int set_module(CLIF_option *optn, char *arg) { + UNUSED(arg); + module = (char *) optn->data; + + return 0; +} + +static int set_mod_option(CLIF_option *optn, char *arg) { + UNUSED(optn); + if(!strcmp(arg, "help")) { + const tr_module *mod = tr_get_module(module); + + if(mod && mod->options) { + /* just to set common keyword flag... */ + CLIF_parse(1, &arg, 0, 0, CLIF_KEYWORD); + CLIF_print_options(NULL, mod->options); + } else + fprintf(stderr, "No options for module `%s'\n", module); + + exit(0); + } + + if(opts_idx >= sizeof(opts) / sizeof(*opts)) { + fprintf(stderr, "Too many module options\n"); + return -1; + } + + opts[opts_idx] = strdup(arg); + if(!opts[opts_idx]) + error("strdup"); + opts_idx++; + + return 0; +} + +static int set_raw(CLIF_option *optn, char *arg) { + char buf[1024]; + + module = "raw"; + + snprintf(buf, sizeof(buf), "protocol=%s", arg); + return set_mod_option(optn, buf); +} + +static int set_wait_specs(CLIF_option *optn, char *arg) { + UNUSED(optn); + char *p, *q; + + here_factor = near_factor = 0; + + wait_secs = strtod(p = arg, &q); + if(q == p) + return -1; + if(!*q++) + return 0; + + here_factor = strtod(p = q, &q); + if(q == p) + return -1; + if(!*q++) + return 0; + + near_factor = strtod(p = q, &q); + if(q == p || *q) + return -1; + + return 0; +} + +static int set_host(CLIF_argument *argm, char *arg, int index) { + UNUSED(argm); + UNUSED(index); + if(getaddr(arg, &dst_addr) < 0) + return -1; + + dst_name = arg; + + /* i.e., guess it by the addr in cmdline... */ + if(!af) + af = dst_addr.sa.sa_family; + + return 0; +} + +static CLIF_option option_list[] = { + { "4", 0, 0, "Use IPv4", set_af, (void *) 4, 0, CLIF_EXTRA }, + { "6", 0, 0, "Use IPv6", set_af, (void *) 6, 0, 0 }, + { "d", "debug", 0, "Enable socket level debugging", + CLIF_set_flag, &debug, 0, 0 }, + { "F", "dont-fragment", 0, "Do not fragment packets", + CLIF_set_flag, &dontfrag, 0, CLIF_ABBREV }, + { "f", "first", "first_ttl", "Start from the %s hop (instead from 1)", + CLIF_set_uint, &first_hop, 0, 0 }, + { "g", "gateway", "gate", "Route packets through the specified gateway " + "(maximum " _TEXT(MAX_GATEWAYS_4) " for IPv4 and " + _TEXT(MAX_GATEWAYS_6) " for IPv6)", + add_gateway, 0, 0, CLIF_SEVERAL }, + { "I", "icmp", 0, "Use ICMP ECHO for tracerouting", + set_module, "icmp", 0, 0 }, + { "T", "tcp", 0, "Use TCP SYN for tracerouting (default " + "port is " _TEXT(DEF_TCP_PORT) ")", + set_module, "tcp", 0, 0 }, + { "i", "interface", "device", "Specify a network interface " + "to operate with", + CLIF_set_string, &device, 0, 0 }, + { "m", "max-hops", "max_ttl", "Set the max number of hops (max TTL " + "to be reached). Default is " _TEXT(DEF_HOPS), + CLIF_set_uint, &max_hops, 0, 0 }, + { "N", "sim-queries", "squeries", "Set the number of probes " + "to be tried simultaneously (default is " + _TEXT(DEF_SIM_PROBES) ")", + CLIF_set_uint, &sim_probes, 0, 0 }, + { "n", 0, 0, "Do not resolve IP addresses to their domain names", + CLIF_set_flag, &noresolve, 0, 0 }, + { "p", "port", "port", "Set the destination port to use. " + "It is either initial udp port value for " + "\"default\" method (incremented by each probe, " + "default is " _TEXT(DEF_START_PORT) "), " + "or initial seq for \"icmp\" (incremented as well, " + "default from 1), or some constant destination port" + " for other methods (with default of " + _TEXT(DEF_TCP_PORT) " for \"tcp\", " + _TEXT(DEF_UDP_PORT) " for \"udp\", etc.)", + set_port, &dst_port_seq, 0, 0 }, + { "t", "tos", "tos", "Set the TOS (IPv4 type of service) or TC " + "(IPv6 traffic class) value for outgoing packets", + CLIF_set_uint, &tos, 0, 0 }, + { "l", "flowlabel", "flow_label", "Use specified %s for IPv6 packets", + CLIF_set_uint, &flow_label, 0, 0 }, + { "w", "wait", "MAX,HERE,NEAR", "Wait for a probe no more than HERE " + "(default " _TEXT(DEF_HERE_FACTOR) ") times longer " + "than a response from the same hop, or no more " + "than NEAR (default " _TEXT(DEF_NEAR_FACTOR) ") " + "times than some next hop, or MAX (default " + _TEXT(DEF_WAIT_SECS) ") seconds " + "(float point values allowed too)", + set_wait_specs, 0, 0, 0 }, + { "q", "queries", "nqueries", "Set the number of probes per each hop. " + "Default is " _TEXT(DEF_NUM_PROBES), + CLIF_set_uint, &probes_per_hop, 0, 0 }, + { "r", 0, 0, "Bypass the normal routing and send directly to a host " + "on an attached network", + CLIF_set_flag, &noroute, 0, 0 }, + { "s", "source", "src_addr", "Use source %s for outgoing packets", + set_source, 0, 0, 0 }, + { "z", "sendwait", "sendwait", "Minimal time interval between probes " + "(default " _TEXT(DEF_SEND_SECS) "). If the value " + "is more than 10, then it specifies a number " + "in milliseconds, else it is a number of seconds " + "(float point values allowed too)", + CLIF_set_double, &send_secs, 0, 0 }, + { "e", "extensions", 0, "Show ICMP extensions (if present), " + "including MPLS", + CLIF_set_flag, &extension, 0, CLIF_ABBREV }, + { "A", "as-path-lookups", 0, "Perform AS path lookups in routing " + "registries and print results directly after " + "the corresponding addresses", + CLIF_set_flag, &as_lookups, 0, 0 }, + { "M", "module", "name", "Use specified module (either builtin or " + "external) for traceroute operations. Most methods " + "have their shortcuts (`-I' means `-M icmp' etc.)", + CLIF_set_string, &module, 0, CLIF_EXTRA }, + { "O", "options", "OPTS", "Use module-specific option %s for the " + "traceroute module. Several %s allowed, separated " + "by comma. If %s is \"help\", print info about " + "available options", + set_mod_option, 0, 0, CLIF_SEVERAL | CLIF_EXTRA }, + { 0, "sport", "num", "Use source port %s for outgoing packets. " + "Implies `-N 1'", + set_port, &src_port, 0, CLIF_EXTRA }, + #ifdef SO_MARK + { 0, "fwmark", "num", "Set firewall mark for outgoing packets", + CLIF_set_uint, &fwmark, 0, 0 }, + #endif + { "U", "udp", 0, "Use UDP to particular port for tracerouting " + "(instead of increasing the port per each probe), " + "default port is " _TEXT(DEF_UDP_PORT), + set_module, "udp", 0, CLIF_EXTRA }, + { 0, "UL", 0, "Use UDPLITE for tracerouting (default dest port is " + _TEXT(DEF_UDP_PORT) ")", + set_module, "udplite", 0, CLIF_ONEDASH | CLIF_EXTRA }, + { "D", "dccp", 0, "Use DCCP Request for tracerouting (default " + "port is " _TEXT(DEF_DCCP_PORT) ")", + set_module, "dccp", 0, CLIF_EXTRA }, + { "P", "protocol", "prot", "Use raw packet of protocol %s " + "for tracerouting", + set_raw, 0, 0, CLIF_EXTRA }, + { 0, "mtu", 0, "Discover MTU along the path being traced. " + "Implies `-F -N 1'", + CLIF_set_flag, &mtudisc, 0, CLIF_EXTRA }, + { 0, "back", 0, "Guess the number of hops in the backward path " + "and print if it differs", + CLIF_set_flag, &backward, 0, CLIF_EXTRA }, + CLIF_VERSION_OPTION (version_string), + CLIF_HELP_OPTION, + CLIF_END_OPTION + }; + +static CLIF_argument arg_list[] = { + { "host", "The host to traceroute to", + set_host, 0, CLIF_STRICT }, + { "packetlen", "The full packet length (default is the length of " + "an IP header plus " _TEXT(DEF_DATA_LEN) "). Can be " + "ignored or increased to a minimal allowed value", + CLIF_arg_int, &packet_len, 0 }, + CLIF_END_ARGUMENT + }; + +static void do_it(void); + +int traceroute_main(int argc, char *argv[]) { + + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); /* avoid commas in msec printed */ + + check_progname(argv[0]); + + if(CLIF_parse(argc, argv, option_list, arg_list, + CLIF_MAY_JOIN_ARG | CLIF_HELP_EMPTY) < 0 + ) + return -EADDRNOTAVAIL; //exit(2); + + ops = tr_get_module(module); + if(!ops) { + ex_error("Unknown traceroute module %s", module); + return -2; + } + + if(!first_hop || first_hop > max_hops) { + ex_error("first hop out of range"); + return -3; + } + if(max_hops > MAX_HOPS) { + ex_error("max hops cannot be more than " _TEXT(MAX_HOPS)); + return -4; + } + if(!probes_per_hop || probes_per_hop > MAX_PROBES) { + ex_error("no more than " _TEXT(MAX_PROBES) " probes per hop"); + return -5; + } + if(wait_secs < 0 || here_factor < 0 || near_factor < 0) { + ex_error("bad wait specifications `%g,%g,%g' used", + wait_secs, here_factor, near_factor); + return -6; + } + if(packet_len > MAX_PACKET_LEN) { + ex_error("too big packetlen %d specified", packet_len); + return -7; + } + if(src_addr.sa.sa_family && src_addr.sa.sa_family != af) { + ex_error("IP version mismatch in addresses specified"); + return -8; + } + if(send_secs < 0) { + ex_error("bad sendtime `%g' specified", send_secs); + return -9; + } + if(send_secs >= 10) /* it is milliseconds */ + send_secs /= 1000; + + if(af == AF_INET6 && (tos || flow_label)) + dst_addr.sin6.sin6_flowinfo = + htonl(((tos & 0xff) << 20) | (flow_label & 0x000fffff)); + + if(src_port) { + src_addr.sin.sin_port = htons((uint16_t) src_port); + src_addr.sa.sa_family = af; + } + + if(src_port || ops->one_per_time) { + sim_probes = 1; + here_factor = near_factor = 0; + } + + /* make sure we don't std{in,out,err} to open sockets */ + make_fd_used(0); + make_fd_used(1); + make_fd_used(2); + + if(init_ip_options() == -1) + return -10; + + header_len = (af == AF_INET ? sizeof(struct iphdr) + : + sizeof(struct ip6_hdr)) + + rtbuf_len + ops->header_len; + + if(mtudisc) { + dontfrag = 1; + sim_probes = 1; + if(packet_len < 0) + packet_len = MAX_PACKET_LEN; + } + + if(packet_len < 0) { + if(DEF_DATA_LEN >= ops->header_len) + data_len = DEF_DATA_LEN - ops->header_len; + } else { + if(packet_len >= (int) header_len) + data_len = packet_len - header_len; + } + + num_probes = max_hops * probes_per_hop; + probes = calloc(num_probes, sizeof(*probes)); + if(!probes) { + error("calloc"); + return -11; + } + + if(ops->options && opts_idx > 1) { + opts[0] = strdup(module); /* aka argv[0] ... */ + if(CLIF_parse(opts_idx, opts, ops->options, 0, CLIF_KEYWORD) < 0) + return -12; //exit(2); + } + + if(ops->init(&dst_addr, dst_port_seq, &data_len) < 0) { + ex_error("trace method's init failed"); + return -13; + } + + do_it(); + return 0; +} + +/* PRINT STUFF */ + +static void print_header(void) { + + /* Note, without ending new-line! */ + log_printf("traceroute to %s (%s), %u hops max, %zu byte packets", + dst_name, addr2str(&dst_addr), max_hops, + header_len + data_len); + fflush(stdout); +} + +static bool print_addr(sockaddr_any *res) { + bool is_final = false; + const char *str; + + if(!res->sa.sa_family) + return is_final; + + str = addr2str(res); + + if(noresolve) { + log_printf("%s", str); + if(str && !strcmp(dst_name, str)) + is_final = true; + } + else { + char buf[1024]; + + buf[0] = '\0'; + getnameinfo(&res->sa, sizeof(*res), buf, sizeof(buf), + 0, 0, NI_IDN); + log_printf(" %s (%s)", buf[0] ? buf : str, str); + if(buf[0] && !strcmp(dst_name, buf)) + is_final = true; + else + if(str && !strcmp(dst_name, str)) + is_final = true; + } + + if(as_lookups) + log_printf(" [%s]", get_as_path(str)); + + return is_final; +} + +static bool print_probe(probe *pb) { + bool is_final = false; + unsigned int idx = (pb - probes); + unsigned int ttl = idx / probes_per_hop + 1; + unsigned int np = idx % probes_per_hop; + + if(np == 0) + log_printf("\n%2u ", ttl); + + if(!pb->res.sa.sa_family) + log_printf(" *"); + else { + int prn = !np; /* print if the first... */ + + if(np) { /* ...and if differs with previous */ + probe *p; + + /* skip expired */ + for(p = pb - 1; np && !p->res.sa.sa_family; p--, np--) + ; + + if(!np || + !equal_addr(&p->res, &pb->res) || + (p->ext != pb->ext && + !(p->ext && pb->ext && !strcmp(p->ext, pb->ext))) || + (backward && p->recv_ttl != pb->recv_ttl) + ) + prn = 1; + } + + if(prn) { + is_final = print_addr(&pb->res); + if(pb->ext) + log_printf(" <%s>", pb->ext); + + if(backward && pb->recv_ttl) { + int hops = ttl2hops(pb->recv_ttl); + if(hops != (int) ttl) + log_printf(" '-%d'", hops); + } + } + } + + if(pb->recv_time) { + double diff = pb->recv_time - pb->send_time; + + log_printf(" %.3f ms", diff * 1000); + } + + if(pb->err_str[0]) + log_printf(" %s", pb->err_str); + + fflush(stdout); + + return is_final; +} + +static void print_end(void) { + + log_printf("\n"); +} + +/* Compute timeout stuff */ + +static double get_timeout(probe *pb) { + double value; + + if(here_factor) { + /* check for already replied from the same hop */ + int i, idx = (pb - probes); + probe *p = &probes[idx - (idx % probes_per_hop)]; + + for(i = 0; i < (int) probes_per_hop; i++, p++) { + /* `p == pb' skipped since !pb->done */ + + if(p->done && (value = p->recv_time - p->send_time) > 0) { + value += DEF_WAIT_PREC; + value *= here_factor; + return value < wait_secs ? value : wait_secs; + } + } + } + + if(near_factor) { + /* check forward for already replied */ + probe *p, *endp = probes + num_probes; + + for(p = pb + 1; p < endp && p->send_time; p++) { + + if(p->done && (value = p->recv_time - p->send_time) > 0) { + value += DEF_WAIT_PREC; + value *= near_factor; + return value < wait_secs ? value : wait_secs; + } + } + } + + return wait_secs; +} + +/* Check expiration stuff */ + +static void check_expired(probe *pb) { + int idx = (pb - probes); + probe *p, *endp = probes + num_probes; + probe *fp = NULL, *pfp = NULL; + + if(!pb->done) /* an ops method still not release it */ + return; + + /* check all the previous in the same hop */ + for(p = &probes[idx - (idx % probes_per_hop)]; p < pb; p++) { + + if(!p->done || /* too early to decide something */ + !p->final /* already ttl-exceeded in the same hop */ + ) + return; + + pfp = p; /* some of the previous probes is final */ + } + + /* check forward all the sent probes */ + for(p = pb + 1; p < endp && p->send_time; p++) { + + if(p->done) { /* some next probe already done... */ + if(!p->final) /* ...was ttl-exceeded. OK, we are expired. */ + return; + else { + fp = p; + break; + } + } + } + + if(!fp) /* no any final probe found. Assume expired. */ + return; + + /* Well. There is a situation "*(this) * * * * ... * * final" + We cannot guarantee that "final" is in its right place. + We've sent "sim_probes" simultaneously, and the final hop + can drop some of them and answer only for latest ones. + If we can detect/assume that it so, then just put "final" + to the (pseudo-expired) "this" place. + */ + + /* It seems that the case of "answers for latest ones only" + occurs mostly with icmp_unreach error answers ("!H" etc.). + Icmp_echoreply, tcp_reset and even icmp_port_unreach looks + like going in the right order. + */ + if(!fp->err_str[0]) /* not an icmp_unreach error report... */ + return; + + if(pfp || + (idx % probes_per_hop) + (fp - pb) < probes_per_hop + ) { + /* Either some previous (pfp) or some next probe + in this hop is final. It means that the whole hop is final. + Do the replace (it also causes further "final"s to be shifted + here too). + */ + goto replace_by_final; + } + + /* If the final probe is an icmp_unreachable report + (either in a case of some error, like "!H", or just port_unreach), + it could follow the "time-exceed" report from the *same* hop. + */ + for(p = pb - 1; p >= probes; p--) { + if(equal_addr(&p->res, &fp->res)) { + /* ...Yes. Put "final" to the "this" place. */ + goto replace_by_final; + } + } + + if(fp->recv_ttl) { + /* Consider the ttl value of the report packet and guess where + the "final" should be. If it seems that it should be + in the same hop as "this", then do replace. + */ + int back_hops, ttl; + + /* We assume that the reporting one has an initial ttl value + of either 64, or 128, or 255. It is most widely used + in the modern routers and computers. + The idea comes from tracepath(1) routine. + */ + back_hops = ttl2hops(fp->recv_ttl); + + /* It is possible that the back path differs from the forward + and therefore has different number of hops. To minimize + such an influence, get the nearest previous time-exceeded + probe and compare with it. + */ + for(p = pb - 1; p >= probes; p--) { + if(p->done && !p->final && p->recv_ttl) { + int hops = ttl2hops(p->recv_ttl); + + if(hops < back_hops) { + ttl = (p - probes) / probes_per_hop + 1; + back_hops = (back_hops - hops) + ttl; + break; + } + } + } + + ttl = idx / probes_per_hop + 1; + if(back_hops == ttl) + /* Yes! It seems that "final" should be at "this" place */ + goto replace_by_final; + else if(back_hops < ttl) + /* Hmmm... Assume better to replace here too... */ + goto replace_by_final; + + } + + /* No idea what to do. Assume expired. */ + + return; + + replace_by_final: + + *pb = *fp; + + memset(fp, 0, sizeof(*fp)); + /* block extra re-send */ + fp->send_time = 1.; + + return; +} + +probe *probe_by_seq(int seq) { + int n; + + if(seq <= 0) + return NULL; + + for(n = 0; n < (int) num_probes; n++) { + if(probes[n].seq == seq) + return &probes[n]; + } + + return NULL; +} + +probe *probe_by_sk(int sk) { + int n; + + if(sk <= 0) + return NULL; + + for(n = 0; n < (int) num_probes; n++) { + if(probes[n].sk == sk) + return &probes[n]; + } + + return NULL; +} + +static void poll_callback(int fd, int revents) { + + ops->recv_probe(fd, revents); +} + +static void do_it(void) { + int start = (first_hop - 1) * probes_per_hop; + int end = num_probes; + double last_send = 0; + + print_header(); + + while(start < end) { + int n, num = 0; + double next_time = 0; + double now_time = get_time(); + + for(n = start; n < end; n++) { + probe *pb = &probes[n]; + + if(n == start && /* probably time to print... */ + !pb->done && pb->send_time /* ...but yet not replied */ + ) { + double expire_time = pb->send_time + get_timeout(pb); + + if(expire_time > now_time) + next_time = expire_time; + else { + ops->expire_probe(pb); + check_expired(pb); + } + } + + if(pb->done) { + + if(n == start) { /* can print it now */ + if(print_probe(pb)) { + pb->final = 1; + } + //log_printf("(host=%s,%s recv_ttl=%d)", dst_name, (host) ? host : "-", pb->recv_ttl); + start++; + } + + /* { + char buf[1024]; + buf[0] = '\0'; + getnameinfo(&(pb->res.sa), sizeof((pb->res)), buf, sizeof(buf),0, 0, NI_IDN); + if(buf[0] && !strcmp(dst_name, buf)) + pb->real_final = true; + //else + //if(str && !strcmp(dst_name, str)) + // pb->real_final = true; + }*/ + + if(pb->final) + end = (n / probes_per_hop + 1) * probes_per_hop; + + continue; + } + + if(!pb->send_time) { + int ttl; + double next; + + if(send_secs && (next = last_send + send_secs) > now_time) { + next_time = next; + break; + } + + ttl = n / probes_per_hop + 1; + + ops->send_probe(pb, ttl); + + if(!pb->send_time) { + if(next_time) + break; /* have chances later */ + else + error("send probe"); + } + + last_send = pb->send_time; + } + + if(!next_time) + next_time = pb->send_time + get_timeout(pb); + + num++; + if(num >= (int) sim_probes) + break; + } + + if(next_time) { + double timeout = next_time - get_time(); + + if(timeout < 0) + timeout = 0; + + do_poll(timeout, poll_callback); + } + + } + + print_end(); + + return; +} + +void tune_socket(int sk) { + int i = 0; + + if(debug) { + i = 1; + if(setsockopt(sk, SOL_SOCKET, SO_DEBUG, &i, sizeof(i)) < 0) + error("setsockopt SO_DEBUG"); + } + +#ifdef SO_MARK + if(fwmark) { + if(setsockopt(sk, SOL_SOCKET, SO_MARK, + &fwmark, sizeof(fwmark)) < 0 + ) + error("setsockopt SO_MARK"); + } +#endif + + if(rtbuf && rtbuf_len) { + if(af == AF_INET) { + if(setsockopt(sk, IPPROTO_IP, IP_OPTIONS, + rtbuf, rtbuf_len) < 0 + ) + error("setsockopt IP_OPTIONS"); + } + else if(af == AF_INET6) { + if(setsockopt(sk, IPPROTO_IPV6, IPV6_RTHDR, + rtbuf, rtbuf_len) < 0 + ) + error("setsockopt IPV6_RTHDR"); + } + } + + bind_socket(sk); + + if(af == AF_INET) { + + i = dontfrag ? IP_PMTUDISC_PROBE : IP_PMTUDISC_DONT; + if(setsockopt(sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0 && + (!dontfrag || (i = IP_PMTUDISC_DO, + setsockopt(sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0)) + ) + error("setsockopt IP_MTU_DISCOVER"); + + if(tos) { + i = tos; + if(setsockopt(sk, SOL_IP, IP_TOS, &i, sizeof(i)) < 0) + error("setsockopt IP_TOS"); + } + + } + else if(af == AF_INET6) { + + i = dontfrag ? IPV6_PMTUDISC_PROBE : IPV6_PMTUDISC_DONT; + if(setsockopt(sk, SOL_IPV6, IPV6_MTU_DISCOVER, &i, sizeof(i)) < 0 && + (!dontfrag || (i = IPV6_PMTUDISC_DO, + setsockopt(sk, SOL_IPV6, IPV6_MTU_DISCOVER, &i, sizeof(i)) < 0)) + ) + error("setsockopt IPV6_MTU_DISCOVER"); + + if(flow_label) { + struct in6_flowlabel_req_ flr; + + memset(&flr, 0, sizeof(flr)); + flr.flr_label = htonl(flow_label & 0x000fffff); + flr.flr_action = IPV6_FL_A_GET; + flr.flr_flags = IPV6_FL_F_CREATE; + flr.flr_share = IPV6_FL_S_ANY; + memcpy(&flr.flr_dst, &dst_addr.sin6.sin6_addr, + sizeof(flr.flr_dst)); + + if(setsockopt(sk, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, + &flr, sizeof(flr)) < 0 + ) + error("setsockopt IPV6_FLOWLABEL_MGR"); + } + + if(tos) { + i = tos; + if(setsockopt(sk, IPPROTO_IPV6, IPV6_TCLASS, + &i, sizeof(i)) < 0 + ) + error("setsockopt IPV6_TCLASS"); + } + + if(tos || flow_label) { + i = 1; + if(setsockopt(sk, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, + &i, sizeof(i)) < 0 + ) + error("setsockopt IPV6_FLOWINFO_SEND"); + } + } + + if(noroute) { + i = noroute; + if(setsockopt(sk, SOL_SOCKET, SO_DONTROUTE, &i, sizeof(i)) < 0) + error("setsockopt SO_DONTROUTE"); + } + + use_timestamp(sk); + + use_recv_ttl(sk); + + fcntl(sk, F_SETFL, O_NONBLOCK); + + return; +} + +void parse_icmp_res(probe *pb, int type, int code, int info) { + char *str = NULL; + char buf[sizeof(pb->err_str)]; + + if(af == AF_INET) { + + if(type == ICMP_TIME_EXCEEDED) { + if(code == ICMP_EXC_TTL) + return; + } + else if(type == ICMP_DEST_UNREACH) { + + switch (code) { + case ICMP_UNREACH_NET: + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_ISOLATED: + case ICMP_UNREACH_TOSNET: + str = "!N"; + break; + + case ICMP_UNREACH_HOST: + case ICMP_UNREACH_HOST_UNKNOWN: + case ICMP_UNREACH_TOSHOST: + str = "!H"; + break; + + case ICMP_UNREACH_NET_PROHIB: + case ICMP_UNREACH_HOST_PROHIB: + case ICMP_UNREACH_FILTER_PROHIB: + str = "!X"; + break; + + case ICMP_UNREACH_PORT: + /* dest host is reached */ + str = ""; + break; + + case ICMP_UNREACH_PROTOCOL: + str = "!P"; + break; + + case ICMP_UNREACH_NEEDFRAG: + snprintf(buf, sizeof(buf), "!F-%d", info); + str = buf; + break; + + case ICMP_UNREACH_SRCFAIL: + str = "!S"; + break; + + case ICMP_UNREACH_HOST_PRECEDENCE: + str = "!V"; + break; + + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + str = "!C"; + break; + + default: + snprintf(buf, sizeof(buf), "!<%u>", code); + str = buf; + break; + } + } + + } + else if(af == AF_INET6) { + + if(type == ICMP6_TIME_EXCEEDED) { + if(code == ICMP6_TIME_EXCEED_TRANSIT) + return; + } + else if(type == ICMP6_DST_UNREACH) { + + switch (code) { + + case ICMP6_DST_UNREACH_NOROUTE: + str = "!N"; + break; + + case ICMP6_DST_UNREACH_BEYONDSCOPE: + case ICMP6_DST_UNREACH_ADDR: + str = "!H"; + break; + + case ICMP6_DST_UNREACH_ADMIN: + str = "!X"; + break; + + case ICMP6_DST_UNREACH_NOPORT: + /* dest host is reached */ + str = ""; + break; + + default: + snprintf(buf, sizeof(buf), "!<%u>", code); + str = buf; + break; + } + } + else if(type == ICMP6_PACKET_TOO_BIG) { + snprintf(buf, sizeof(buf), "!F-%d", info); + str = buf; + } + } + + if(!str) { + snprintf(buf, sizeof(buf), "!<%u-%u>", type, code); + str = buf; + } + + if(*str) { + strncpy(pb->err_str, str, sizeof(pb->err_str)); + pb->err_str[sizeof(pb->err_str) - 1] = '\0'; + } + // final present + pb->final = 1; + + return; +} + +static void parse_local_res(probe *pb, int ee_errno, int info) { + + if(ee_errno == EMSGSIZE && info != 0) { + snprintf(pb->err_str, sizeof(pb->err_str) - 1, "!F-%d", info); + pb->final = 1; + return; + } + + errno = ee_errno; + error("local recverr"); +} + +void probe_done(probe *pb) { + + if(pb->sk) { + del_poll(pb->sk); + close(pb->sk); + pb->sk = 0; + } + + pb->seq = 0; + + pb->done = 1; +} + +void recv_reply(int sk, int err, check_reply_t check_reply) { + struct msghdr msg; + sockaddr_any from; + struct iovec iov; + int n; + probe *pb; + char buf[1280]; /* min mtu for ipv6 ( >= 576 for ipv4) */ + char *bufp = buf; + char control[1024]; + struct cmsghdr *cm; + double recv_time = 0; + int recv_ttl = 0; + struct sock_extended_err *ee = NULL; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &from; + msg.msg_namelen = sizeof(from); + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + n = recvmsg(sk, &msg, err ? MSG_ERRQUEUE : 0); + if(n < 0) + return; + + /* when not MSG_ERRQUEUE, AF_INET returns full ipv4 header + on raw sockets... + */ + + if(!err && + af == AF_INET && + /* XXX: Assume that the presence of an extra header means + that it is not a raw socket... + */ + ops->header_len == 0 + ) { + struct iphdr *ip = (struct iphdr *) bufp; + int hlen; + + if(n < (int) sizeof(struct iphdr)) + return; + + hlen = ip->ihl << 2; + if(n < hlen) + return; + + bufp += hlen; + n -= hlen; + } + + pb = check_reply(sk, err, &from, bufp, n); + if(!pb) { + + /* for `frag needed' case at the local host, + kernel >= 3.13 sends local error (no more icmp) + */ + if(!n && err && dontfrag) { + pb = &probes[(first_hop - 1) * probes_per_hop]; + if(pb->done) + return; + } else + return; + } + + /* Parse CMSG stuff */ + + for(cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) { + void *ptr = CMSG_DATA(cm); + + if(cm->cmsg_level == SOL_SOCKET) { + + if(cm->cmsg_type == SO_TIMESTAMP) { + struct timeval *tv = (struct timeval *) ptr; + + recv_time = tv->tv_sec + tv->tv_usec / 1000000.; + } + } + else if(cm->cmsg_level == SOL_IP) { + + if(cm->cmsg_type == IP_TTL) + recv_ttl = *((int *) ptr); + else if(cm->cmsg_type == IP_RECVERR) { + + ee = (struct sock_extended_err *) ptr; + + if(ee->ee_origin != SO_EE_ORIGIN_ICMP && + ee->ee_origin != SO_EE_ORIGIN_LOCAL + ) + return; + + /* dgram icmp sockets might return extra things... */ + if(ee->ee_origin == SO_EE_ORIGIN_ICMP && + (ee->ee_type == ICMP_SOURCE_QUENCH || + ee->ee_type == ICMP_REDIRECT) + ) + return; + } + } + else if(cm->cmsg_level == SOL_IPV6) { + + if(cm->cmsg_type == IPV6_HOPLIMIT) + recv_ttl = *((int *) ptr); + else if(cm->cmsg_type == IPV6_RECVERR) { + + ee = (struct sock_extended_err *) ptr; + + if(ee->ee_origin != SO_EE_ORIGIN_ICMP6 && + ee->ee_origin != SO_EE_ORIGIN_LOCAL + ) + return; + } + } + } + + if(!recv_time) + recv_time = get_time(); + + if(!err) + memcpy(&pb->res, &from, sizeof(pb->res)); + + pb->recv_time = recv_time; + + pb->recv_ttl = recv_ttl; + + if(ee && ee->ee_origin != SO_EE_ORIGIN_LOCAL) { /* icmp or icmp6 */ + memcpy(&pb->res, SO_EE_OFFENDER(ee), sizeof(pb->res)); + parse_icmp_res(pb, ee->ee_type, ee->ee_code, ee->ee_info); + } + + if(ee && ee->ee_origin == SO_EE_ORIGIN_LOCAL) + parse_local_res(pb, ee->ee_errno, ee->ee_info); + + if(ee && + mtudisc && + ee->ee_info >= header_len && + ee->ee_info < header_len + data_len + ) { + data_len = ee->ee_info - header_len; + + probe_done(pb); + + /* clear this probe (as actually the previous hop answers here) + but fill its `err_str' by the info obtained. Ugly, but easy... + */ + memset(pb, 0, sizeof(*pb)); + snprintf(pb->err_str, sizeof(pb->err_str) - 1, "F=%d", ee->ee_info); + + return; + } + + if(ee && + extension && + header_len + n >= (128 + 8) && /* at least... (rfc4884) */ + header_len <= 128 && /* paranoia */ + ((af == AF_INET && (ee->ee_type == ICMP_TIME_EXCEEDED || + ee->ee_type == ICMP_DEST_UNREACH || + ee->ee_type == ICMP_PARAMETERPROB)) || + (af == AF_INET6 && (ee->ee_type == ICMP6_TIME_EXCEEDED || + ee->ee_type == ICMP6_DST_UNREACH)) + ) + ) { + int step; + int offs = 128 - header_len; + + if(n > (int) data_len) + step = 0; /* guaranteed at 128 ... */ + else + step = af == AF_INET ? 4 : 8; + + handle_extensions(pb, bufp + offs, n - offs, step); + } + + probe_done(pb); +} + +int equal_addr(const sockaddr_any *a, const sockaddr_any *b) { + + if(!a->sa.sa_family) + return 0; + + if(a->sa.sa_family != b->sa.sa_family) + return 0; + + if(a->sa.sa_family == AF_INET6) + return !memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, + sizeof(a->sin6.sin6_addr)); + else + return !memcmp(&a->sin.sin_addr, &b->sin.sin_addr, + sizeof(a->sin.sin_addr)); + return 0; /* not reached */ +} + +void bind_socket(int sk) { + sockaddr_any *addr, tmp; + + if(device) { + if(setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, + device, strlen(device) + 1) < 0 + ) + error("setsockopt SO_BINDTODEVICE"); + } + + if(!src_addr.sa.sa_family) { + memset(&tmp, 0, sizeof(tmp)); + tmp.sa.sa_family = af; + addr = &tmp; + } else + addr = &src_addr; + + if(bind(sk, &addr->sa, sizeof(*addr)) < 0) + error("bind"); + + return; +} + +void use_timestamp(int sk) { + int n = 1; + + setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &n, sizeof(n)); + /* foo on errors... */ +} + +void use_recv_ttl(int sk) { + int n = 1; + + if(af == AF_INET) + setsockopt(sk, SOL_IP, IP_RECVTTL, &n, sizeof(n)); + else if(af == AF_INET6) + setsockopt(sk, SOL_IPV6, IPV6_RECVHOPLIMIT, &n, sizeof(n)); + /* foo on errors */ +} + +void use_recverr(int sk) { + int val = 1; + + if(af == AF_INET) { + if(setsockopt(sk, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) + error("setsockopt IP_RECVERR"); + } + else if(af == AF_INET6) { + if(setsockopt(sk, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) + error("setsockopt IPV6_RECVERR"); + } +} + +void set_ttl(int sk, int ttl) { + + if(af == AF_INET) { + if(setsockopt(sk, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) + error("setsockopt IP_TTL"); + } + else if(af == AF_INET6) { + if(setsockopt(sk, SOL_IPV6, IPV6_UNICAST_HOPS, + &ttl, sizeof(ttl)) < 0 + ) + error("setsockopt IPV6_UNICAST_HOPS"); + } +} + +int do_send(int sk, const void *data, size_t len, const sockaddr_any *addr) { + int res; + + if(!addr || raw_can_connect()) + res = send(sk, data, len, 0); + else + res = sendto(sk, data, len, 0, &addr->sa, sizeof(*addr)); + + if(res < 0) { + if(errno == ENOBUFS || errno == EAGAIN) + return res; + if(errno == EMSGSIZE) + return 0; /* recverr will say more... */ + error("send"); /* not recoverable */ + } + + return res; +} + +/* There is a bug in the kernel before 2.6.25, which prevents icmp errors + to be obtained by MSG_ERRQUEUE for ipv6 connected raw sockets. + */ +static int can_connect = -1; + +#define VER(A,B,C,D) (((((((A) << 8) | (B)) << 8) | (C)) << 8) | (D)) + +int raw_can_connect(void) { + + if(can_connect < 0) { + + if(af == AF_INET) + can_connect = 1; + else { /* AF_INET6 */ + struct utsname uts; + int n; + unsigned int a, b, c, d = 0; + + if(uname(&uts) < 0) + return 0; + + n = sscanf(uts.release, "%u.%u.%u.%u", &a, &b, &c, &d); + can_connect = (n >= 3 && VER (a, b, c, d) >= VER(2, 6, 25, 0)); + } + } + + return can_connect; +} + +/** + * Traceroute host + * + * @addr[in] host name or IP address + * @hops[out] hops count + * @time_usec[out] latency in microseconds + * @return 0 Ok, -1 error + */ +int traceroute_util(const char *addr, int *hops, int *time_usec) +{ + UNUSED(hops); + int type = 6; // ipv4 or ipv6 + int argc = 3; + const char *argv[argc]; + if(type != 4) + argv[0] = "traceroute6"; + else + argv[0] = "traceroute4"; + argv[1] = "-4"; // ipv4 + argv[2] = addr; + + *hops = 0; + *time_usec = 0; + + int ret = traceroute_main(argc, (char**) argv); + if(!ret) { + ret = -1; + for(int i = 0; i < (int) num_probes; i++) { + probe *one_probe = probes + i; + if(one_probe->done && one_probe->final && one_probe->recv_ttl) { + *hops = i / DEF_NUM_PROBES + 1; + *time_usec = (int) ((one_probe->recv_time - one_probe->send_time) * 1000000); + // if error -> not found host + if(!strlen(one_probe->err_str)) + ret = 1; + break; + } + } + /*if(one_probe->done) + printf("%d(%d) dseq=%d sk=%d done=%d final=%d recv_ttl=%d dt=%lf err='%s'\n", i + 1, + i / DEF_NUM_PROBES + 1, + one_probe->seq, one_probe->sk, one_probe->done, + one_probe->final, one_probe->recv_ttl, one_probe->recv_time - one_probe->send_time, + one_probe->err_str);*/ + } + free(probes); + + return (ret == 1) ? 0 : ret; +} diff --git a/libdap-chain-net/iputils/traceroute/traceroute.h b/libdap-chain-net/iputils/traceroute/traceroute.h new file mode 100644 index 0000000000000000000000000000000000000000..0db40c544ed51febe2379ba64de5bbfe1b689057 --- /dev/null +++ b/libdap-chain-net/iputils/traceroute/traceroute.h @@ -0,0 +1,107 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <netinet/in.h> + +#include "clif.h" +#include "../iputils.h" + +union common_sockaddr { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +typedef union common_sockaddr sockaddr_any; + +struct probe_struct { + int done; + int final; + sockaddr_any res; + double send_time; + double recv_time; + int recv_ttl; + int sk; + int seq; + char *ext; + char err_str[16]; /* assume enough */ +}; +typedef struct probe_struct probe; + + +struct tr_module_struct { + struct tr_module_struct *next; + const char *name; + int (*init) (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len); + void (*send_probe) (probe *pb, int ttl); + void (*recv_probe) (int fd, int revents); + void (*expire_probe) (probe *pb); + CLIF_option *options; /* per module options, if any */ + int one_per_time; /* no simultaneous probes */ + size_t header_len; /* additional header length (aka for udp) */ +}; +typedef struct tr_module_struct tr_module; + + +#define __TEXT(X) #X +#define _TEXT(X) __TEXT(X) + +#define DEF_START_PORT 33434 /* start for traditional udp method */ +#define DEF_UDP_PORT 53 /* dns */ +#define DEF_TCP_PORT 80 /* web */ +#define DEF_DCCP_PORT DEF_START_PORT /* is it a good choice?... */ +#define DEF_RAW_PROT 253 /* for experimentation and testing, rfc3692 */ + + +void error (const char *str) __attribute__((noreturn)); +void error_or_perm (const char *str) __attribute__((noreturn)); + +double get_time (void); +void tune_socket (int sk); +void parse_icmp_res (probe *pb, int type, int code, int info); +void probe_done (probe *pb); + +typedef probe *(*check_reply_t) (int sk, int err, sockaddr_any *from, + char *buf, size_t len); +void recv_reply (int sk, int err, check_reply_t check_reply); + +int equal_addr (const sockaddr_any *a, const sockaddr_any *b); + +probe *probe_by_seq (int seq); +probe *probe_by_sk (int sk); + +void bind_socket (int sk); +void use_timestamp (int sk); +void use_recv_ttl (int sk); +void use_recverr (int sk); +void set_ttl (int sk, int ttl); +int do_send (int sk, const void *data, size_t len, const sockaddr_any *addr); + +void add_poll (int fd, int events); +void del_poll (int fd); +void do_poll (double timeout, void (*callback) (int fd, int revents)); + +void handle_extensions (probe *pb, char *buf, int len, int step); +const char *get_as_path (const char *query); + +int raw_can_connect (void); + +unsigned int random_seq (void); +uint16_t in_csum (const void *ptr, size_t len); + + +void tr_register_module (tr_module *module); +const tr_module *tr_get_module (const char *name); + +#define TR_MODULE(MOD) \ +static void __init_ ## MOD (void) __attribute__ ((constructor)); \ +static void __init_ ## MOD (void) { \ + \ + tr_register_module (&MOD); \ +} + diff --git a/libdap-chain-net/win32/ip.h b/libdap-chain-net/win32/ip.h new file mode 100644 index 0000000000000000000000000000000000000000..df3d54a8345b77e0aa00585583c68806b6b8fa0e --- /dev/null +++ b/libdap-chain-net/win32/ip.h @@ -0,0 +1,301 @@ +#pragma once + +/* Copyright (C) 1991-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef __NETINET_IP_H +#define __NETINET_IP_H 1 + +//#include <features.h> +//#include <sys/types.h> + +//#include <netinet/in.h> + +struct timestamp + { + uint8_t len; + uint8_t ptr; +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int flags:4; + unsigned int overflow:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned int overflow:4; + unsigned int flags:4; +#else +# error "Please fix <bits/endian.h>" +#endif + uint32_t data[9]; + }; + +struct iphdr + { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int ihl:4; + unsigned int version:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned int version:4; + unsigned int ihl:4; +#else +# error "Please fix <bits/endian.h>" +#endif + uint8_t tos; + uint16_t tot_len; + uint16_t id; + uint16_t frag_off; + uint8_t ttl; + uint8_t protocol; + uint16_t check; + uint32_t saddr; + uint32_t daddr; + /*The options start here. */ + }; + +#ifdef __USE_MISC +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Definitions for internet protocol version 4. + * Per RFC 791, September 1981. + */ + +/* + * Structure of an internet header, naked of options. + */ +struct ip + { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int ip_hl:4; /* header length */ + unsigned int ip_v:4; /* version */ +#endif +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned int ip_v:4; /* version */ + unsigned int ip_hl:4; /* header length */ +#endif + uint8_t ip_tos; /* type of service */ + unsigned short ip_len; /* total length */ + unsigned short ip_id; /* identification */ + unsigned short ip_off; /* fragment offset field */ +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + unsigned short ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ + }; + +/* + * Time stamp option structure. + */ +struct ip_timestamp + { + uint8_t ipt_code; /* IPOPT_TS */ + uint8_t ipt_len; /* size of structure (variable) */ + uint8_t ipt_ptr; /* index of current entry */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int ipt_flg:4; /* flags, see below */ + unsigned int ipt_oflw:4; /* overflow counter */ +#endif +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned int ipt_oflw:4; /* overflow counter */ + unsigned int ipt_flg:4; /* flags, see below */ +#endif + uint32_t data[9]; + }; +#endif /* __USE_MISC */ + +#define IPVERSION 4 /* IP version number */ +#define IP_MAXPACKET 65535 /* maximum packet size */ + +/* + * Definitions for Explicit Congestion Notification (ECN) + * + * Taken from RFC-3168, Section 5. + */ + +#define IPTOS_ECN_MASK 0x03 +#define IPTOS_ECN(x) ((x) & IPTOS_ECN_MASK) +#define IPTOS_ECN_NOT_ECT 0x00 +#define IPTOS_ECN_ECT1 0x01 +#define IPTOS_ECN_ECT0 0x02 +#define IPTOS_ECN_CE 0x03 + +/* + * Definitions for IP differentiated services code points (DSCP) + * + * Taken from RFC-2597, Section 6 and RFC-2598, Section 2.3. + */ + +#define IPTOS_DSCP_MASK 0xfc +#define IPTOS_DSCP(x) ((x) & IPTOS_DSCP_MASK) +#define IPTOS_DSCP_AF11 0x28 +#define IPTOS_DSCP_AF12 0x30 +#define IPTOS_DSCP_AF13 0x38 +#define IPTOS_DSCP_AF21 0x48 +#define IPTOS_DSCP_AF22 0x50 +#define IPTOS_DSCP_AF23 0x58 +#define IPTOS_DSCP_AF31 0x68 +#define IPTOS_DSCP_AF32 0x70 +#define IPTOS_DSCP_AF33 0x78 +#define IPTOS_DSCP_AF41 0x88 +#define IPTOS_DSCP_AF42 0x90 +#define IPTOS_DSCP_AF43 0x98 +#define IPTOS_DSCP_EF 0xb8 + +/* + * In RFC 2474, Section 4.2.2.1, the Class Selector Codepoints subsume + * the old ToS Precedence values. + */ + +#define IPTOS_CLASS_MASK 0xe0 +#define IPTOS_CLASS(class) ((class) & IPTOS_CLASS_MASK) +#define IPTOS_CLASS_CS0 0x00 +#define IPTOS_CLASS_CS1 0x20 +#define IPTOS_CLASS_CS2 0x40 +#define IPTOS_CLASS_CS3 0x60 +#define IPTOS_CLASS_CS4 0x80 +#define IPTOS_CLASS_CS5 0xa0 +#define IPTOS_CLASS_CS6 0xc0 +#define IPTOS_CLASS_CS7 0xe0 + +#define IPTOS_CLASS_DEFAULT IPTOS_CLASS_CS0 + +/* + * Definitions for IP type of service (ip_tos) [deprecated; use DSCP + * and CS definitions above instead.] + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * Definitions for IP precedence (also in ip_tos) [also deprecated.] + */ +#define IPTOS_PREC_MASK IPTOS_CLASS_MASK +#define IPTOS_PREC(tos) IPTOS_CLASS(tos) +#define IPTOS_PREC_NETCONTROL IPTOS_CLASS_CS7 +#define IPTOS_PREC_INTERNETCONTROL IPTOS_CLASS_CS6 +#define IPTOS_PREC_CRITIC_ECP IPTOS_CLASS_CS5 +#define IPTOS_PREC_FLASHOVERRIDE IPTOS_CLASS_CS4 +#define IPTOS_PREC_FLASH IPTOS_CLASS_CS3 +#define IPTOS_PREC_IMMEDIATE IPTOS_CLASS_CS2 +#define IPTOS_PREC_PRIORITY IPTOS_CLASS_CS1 +#define IPTOS_PREC_ROUTINE IPTOS_CLASS_CS0 + +/* + * Definitions for options. + */ +#define IPOPT_COPY 0x80 +#define IPOPT_CLASS_MASK 0x60 +#define IPOPT_NUMBER_MASK 0x1f + +#define IPOPT_COPIED(o) ((o) & IPOPT_COPY) +#define IPOPT_CLASS(o) ((o) & IPOPT_CLASS_MASK) +#define IPOPT_NUMBER(o) ((o) & IPOPT_NUMBER_MASK) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_DEBMEAS 0x40 +#define IPOPT_MEASUREMENT IPOPT_DEBMEAS +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_EOL 0 /* end of option list */ +#define IPOPT_END IPOPT_EOL +#define IPOPT_NOP 1 /* no operation */ +#define IPOPT_NOOP IPOPT_NOP + +#define IPOPT_RR 7 /* record packet route */ +#define IPOPT_TS 68 /* timestamp */ +#define IPOPT_TIMESTAMP IPOPT_TS +#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ +#define IPOPT_SEC IPOPT_SECURITY +#define IPOPT_LSRR 131 /* loose source route */ +#define IPOPT_SATID 136 /* satnet id */ +#define IPOPT_SID IPOPT_SATID +#define IPOPT_SSRR 137 /* strict source route */ +#define IPOPT_RA 148 /* router alert */ + +/* + * Offsets to fields in options other than EOL and NOP. + */ +#define IPOPT_OPTVAL 0 /* option ID */ +#define IPOPT_OLEN 1 /* option length */ +#define IPOPT_OFFSET 2 /* offset within option */ +#define IPOPT_MINOFF 4 /* min value of above */ + +#define MAX_IPOPTLEN 40 + +/* flag bits for ipt_flg */ +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +/* bits for security (not byte swapped) */ +#define IPOPT_SECUR_UNCLASS 0x0000 +#define IPOPT_SECUR_CONFID 0xf135 +#define IPOPT_SECUR_EFTO 0x789a +#define IPOPT_SECUR_MMMM 0xbc4d +#define IPOPT_SECUR_RESTR 0xaf13 +#define IPOPT_SECUR_SECRET 0xd788 +#define IPOPT_SECUR_TOPSECRET 0x6bc5 + +/* + * Internet implementation parameters. + */ +#define MAXTTL 255 /* maximum time to live (seconds) */ +#define IPDEFTTL 64 /* default ttl, from RFC 1340 */ +#define IPFRAGTTL 60 /* time to live for frags, slowhz */ +#define IPTTLDEC 1 /* subtracted when forwarding */ + +#define IP_MSS 576 /* default maximum segment size */ + + +#endif /* netinet/ip.h */ diff --git a/libdap-chain-net/win32/iphdr.h b/libdap-chain-net/win32/iphdr.h new file mode 100644 index 0000000000000000000000000000000000000000..f2533f910cce0e5c6f35a56018a4bea307313b27 --- /dev/null +++ b/libdap-chain-net/win32/iphdr.h @@ -0,0 +1,112 @@ +#pragma once + +// Align on a 1-byte boundary +//#include <pshpack1.h> + +#pragma pack(push,1) + +// IPv4 header +typedef struct ip_hdr +{ + unsigned char ip_verlen; // 4-bit IPv4 version + // 4-bit header length (in 32-bit words) + unsigned char ip_tos; // IP type of service + unsigned short ip_totallength; // Total length + unsigned short ip_id; // Unique identifier + unsigned short ip_offset; // Fragment offset field + unsigned char ip_ttl; // Time to live + unsigned char ip_protocol; // Protocol(TCP,UDP etc) + unsigned short ip_checksum; // IP checksum + unsigned int ip_srcaddr; // Source address + unsigned int ip_destaddr; // Source address + +} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR; + +// IPv4 option header +typedef struct ipv4_option_hdr +{ + unsigned char opt_code; // option type + unsigned char opt_len; // length of the option header + unsigned char opt_ptr; // offset into options + unsigned long opt_addr[9]; // list of IPv4 addresses +} IPV4_OPTION_HDR, *PIPV4_OPTION_HDR, FAR *LPIPV4_OPTION_HDR; + +// ICMP header +typedef struct icmp_hdr +{ + unsigned char icmp_type; + unsigned char icmp_code; + unsigned short icmp_checksum; + unsigned short icmp_id; + unsigned short icmp_sequence; +} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR; + +// IPv6 protocol header +typedef struct ipv6_hdr +{ + unsigned long ipv6_vertcflow; // 4-bit IPv6 version + // 8-bit traffic class + // 20-bit flow label + unsigned short ipv6_payloadlen; // payload length + unsigned char ipv6_nexthdr; // next header protocol value + unsigned char ipv6_hoplimit; // TTL + struct in6_addr ipv6_srcaddr; // Source address + struct in6_addr ipv6_destaddr; // Destination address +} IPV6_HDR, *PIPV6_HDR, FAR * LPIPV6_HDR; + +// IPv6 fragment header +typedef struct ipv6_fragment_hdr +{ + unsigned char ipv6_frag_nexthdr; + unsigned char ipv6_frag_reserved; + unsigned short ipv6_frag_offset; + unsigned long ipv6_frag_id; +} IPV6_FRAGMENT_HDR, *PIPV6_FRAGMENT_HDR, FAR * LPIPV6_FRAGMENT_HDR; + +// ICMPv6 header +typedef struct icmpv6_hdr { + unsigned char icmp6_type; + unsigned char icmp6_code; + unsigned short icmp6_checksum; +} ICMPV6_HDR; + +// ICMPv6 echo request body +typedef struct icmpv6_echo_request +{ + unsigned short icmp6_echo_id; + unsigned short icmp6_echo_sequence; +} ICMPV6_ECHO_REQUEST; + +// Define the UDP header +typedef struct udp_hdr +{ + unsigned short src_portno; // Source port no. + unsigned short dst_portno; // Dest. port no. + unsigned short udp_length; // Udp packet length + unsigned short udp_checksum; // Udp checksum (optional) +} UDP_HDR, *PUDP_HDR; + +// IPv4 option for record route +#define IP_RECORD_ROUTE 0x7 + +// ICMP6 protocol value (used in the socket call and IPv6 header) +#define IPPROTO_ICMP6 58 + +// ICMP types and codes +#define ICMPV4_ECHO_REQUEST_TYPE 8 +#define ICMPV4_ECHO_REQUEST_CODE 0 +#define ICMPV4_ECHO_REPLY_TYPE 0 +#define ICMPV4_ECHO_REPLY_CODE 0 +#define ICMPV4_MINIMUM_HEADER 8 + +// ICPM6 types and codes +#define ICMPV6_ECHO_REQUEST_TYPE 128 +#define ICMPV6_ECHO_REQUEST_CODE 0 +#define ICMPV6_ECHO_REPLY_TYPE 129 +#define ICMPV6_ECHO_REPLY_CODE 0 + +#pragma pack(pop) + +// Restore byte alignment back to default + +//#include <poppack.h>