Repository: robertdavidgraham/masscan Branch: master Commit: b99d433505f9 Files: 259 Total size: 2.2 MB Directory structure: gitextract_saggdmr8/ ├── .gitattributes ├── .github/ │ └── workflows/ │ └── unittests.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── VULNINFO.md ├── bin/ │ └── .gitignore ├── data/ │ ├── afl-http.pcap │ ├── exclude.conf │ └── flush_all.pcap ├── debian/ │ ├── .gitignore │ ├── README.Debian │ ├── changelog │ ├── compat │ ├── control │ ├── copyright │ ├── masscan.dirs │ ├── masscan.docs │ ├── masscan.install │ ├── masscan.manpages │ ├── rules │ ├── source/ │ │ └── format │ └── watch ├── doc/ │ ├── algorithm.js │ ├── bot.html │ ├── faq/ │ │ ├── FAQ0001-slow.md │ │ ├── FAQ0002-drops.md │ │ ├── FAQ0003-excludelist.md │ │ ├── FAQ0004-serverlogs.md │ │ └── README.md │ ├── howto-afl.md │ ├── masscan.8 │ └── masscan.8.markdown ├── src/ │ ├── crypto-base64.c │ ├── crypto-base64.h │ ├── crypto-blackrock.c │ ├── crypto-blackrock.h │ ├── crypto-blackrock2.c │ ├── crypto-lcg.c │ ├── crypto-lcg.h │ ├── crypto-primegen.c │ ├── crypto-primegen.h │ ├── crypto-siphash24.c │ ├── crypto-siphash24.h │ ├── event-timeout.c │ ├── event-timeout.h │ ├── in-binary.c │ ├── in-binary.h │ ├── in-filter.c │ ├── in-filter.h │ ├── in-report.c │ ├── in-report.h │ ├── main-conf.c │ ├── main-dedup.c │ ├── main-dedup.h │ ├── main-globals.h │ ├── main-initadapter.c │ ├── main-listscan.c │ ├── main-ptrace.c │ ├── main-ptrace.h │ ├── main-readrange.c │ ├── main-readrange.h │ ├── main-status.c │ ├── main-status.h │ ├── main-throttle.c │ ├── main-throttle.h │ ├── main.c │ ├── masscan-app.c │ ├── masscan-app.h │ ├── masscan-status.h │ ├── masscan-version.h │ ├── masscan.h │ ├── massip-addr.c │ ├── massip-addr.h │ ├── massip-parse.c │ ├── massip-parse.h │ ├── massip-port.h │ ├── massip-rangesv4.c │ ├── massip-rangesv4.h │ ├── massip-rangesv6.c │ ├── massip-rangesv6.h │ ├── massip.c │ ├── massip.h │ ├── misc-rstfilter.c │ ├── misc-rstfilter.h │ ├── out-binary.c │ ├── out-certs.c │ ├── out-grepable.c │ ├── out-hostonly.c │ ├── out-json.c │ ├── out-ndjson.c │ ├── out-null.c │ ├── out-record.h │ ├── out-redis.c │ ├── out-tcp-services.c │ ├── out-tcp-services.h │ ├── out-text.c │ ├── out-unicornscan.c │ ├── out-xml.c │ ├── output.c │ ├── output.h │ ├── pixie-backtrace.c │ ├── pixie-backtrace.h │ ├── pixie-file.c │ ├── pixie-file.h │ ├── pixie-sockets.h │ ├── pixie-threads.c │ ├── pixie-threads.h │ ├── pixie-timer.c │ ├── pixie-timer.h │ ├── proto-arp.c │ ├── proto-arp.h │ ├── proto-banner1.c │ ├── proto-banner1.h │ ├── proto-banout.c │ ├── proto-banout.h │ ├── proto-coap.c │ ├── proto-coap.h │ ├── proto-dns-parse.h │ ├── proto-dns.c │ ├── proto-dns.h │ ├── proto-ftp.c │ ├── proto-ftp.h │ ├── proto-http.c │ ├── proto-http.h │ ├── proto-icmp.c │ ├── proto-icmp.h │ ├── proto-imap4.c │ ├── proto-imap4.h │ ├── proto-isakmp.c │ ├── proto-isakmp.h │ ├── proto-mc.c │ ├── proto-mc.h │ ├── proto-memcached.c │ ├── proto-memcached.h │ ├── proto-netbios.c │ ├── proto-netbios.h │ ├── proto-ntlmssp.c │ ├── proto-ntlmssp.h │ ├── proto-ntp.c │ ├── proto-ntp.h │ ├── proto-oproto.c │ ├── proto-oproto.h │ ├── proto-pop3.c │ ├── proto-pop3.h │ ├── proto-preprocess.c │ ├── proto-preprocess.h │ ├── proto-sctp.c │ ├── proto-sctp.h │ ├── proto-smb.c │ ├── proto-smb.h │ ├── proto-smtp.c │ ├── proto-smtp.h │ ├── proto-snmp.c │ ├── proto-snmp.h │ ├── proto-spnego.h │ ├── proto-ssh.c │ ├── proto-ssh.h │ ├── proto-ssl-test.c │ ├── proto-ssl.c │ ├── proto-ssl.h │ ├── proto-tcp-rdp.c │ ├── proto-tcp-rdp.h │ ├── proto-tcp-telnet.c │ ├── proto-tcp-telnet.h │ ├── proto-udp.c │ ├── proto-udp.h │ ├── proto-versioning.c │ ├── proto-versioning.h │ ├── proto-vnc.c │ ├── proto-vnc.h │ ├── proto-x509.c │ ├── proto-x509.h │ ├── proto-zeroaccess.c │ ├── proto-zeroaccess.h │ ├── rawsock-adapter.h │ ├── rawsock-getif.c │ ├── rawsock-getip.c │ ├── rawsock-getip6.c │ ├── rawsock-getmac.c │ ├── rawsock-getroute.c │ ├── rawsock-pcapfile.c │ ├── rawsock-pcapfile.h │ ├── rawsock.c │ ├── rawsock.h │ ├── read-service-probes.c │ ├── read-service-probes.h │ ├── rte-ring.c │ ├── rte-ring.h │ ├── scripting-banner.c │ ├── scripting-masscan.c │ ├── scripting.c │ ├── scripting.h │ ├── smack.h │ ├── smack1.c │ ├── smackqueue.c │ ├── smackqueue.h │ ├── stack-arpv4.c │ ├── stack-arpv4.h │ ├── stack-if.c │ ├── stack-ndpv6.c │ ├── stack-ndpv6.h │ ├── stack-queue.c │ ├── stack-queue.h │ ├── stack-src.c │ ├── stack-src.h │ ├── stack-tcp-api.h │ ├── stack-tcp-app.c │ ├── stack-tcp-app.h │ ├── stack-tcp-core.c │ ├── stack-tcp-core.h │ ├── stub-lua.c │ ├── stub-lua.h │ ├── stub-pcap-dlt.h │ ├── stub-pcap.c │ ├── stub-pcap.h │ ├── stub-pfring.c │ ├── stub-pfring.h │ ├── syn-cookie.c │ ├── syn-cookie.h │ ├── templ-nmap-payloads.c │ ├── templ-nmap-payloads.h │ ├── templ-opts.h │ ├── templ-payloads.c │ ├── templ-payloads.h │ ├── templ-pkt.c │ ├── templ-pkt.h │ ├── templ-tcp-hdr.c │ ├── templ-tcp-hdr.h │ ├── unusedparm.h │ ├── util-bool.h │ ├── util-checksum.c │ ├── util-checksum.h │ ├── util-errormsg.c │ ├── util-errormsg.h │ ├── util-extract.c │ ├── util-extract.h │ ├── util-logger.c │ ├── util-logger.h │ ├── util-malloc.c │ ├── util-malloc.h │ ├── util-safefunc.c │ ├── util-safefunc.h │ ├── vulncheck-heartbleed.c │ ├── vulncheck-ntp-monlist.c │ ├── vulncheck-sslv3.c │ ├── vulncheck.c │ ├── vulncheck.h │ ├── xring.c │ └── xring.h ├── tmp/ │ └── .gitignore ├── vs10/ │ ├── .gitignore │ ├── masscan.sln │ ├── masscan.vcxproj │ └── masscan.vcxproj.filters └── xcode4/ ├── .gitignore └── masscan.xcodeproj/ ├── .gitignore └── project.pbxproj ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.c text eol=lf ================================================ FILE: .github/workflows/unittests.yml ================================================ name: masscan unit tests on: push: branches: [master] pull_request: # The branches below must be a subset of the branches above branches: [master] permissions: contents: read jobs: regress: name: Run regression tests runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest] steps: - name: Checkout masscan uses: actions/checkout@v3 - name: Install libpcap-dev if: ${{ vars.RUNNER_OS == 'Linux' }} run: sudo apt-get install -y libpcap-dev - name: Run regression tests run: make test ================================================ FILE: .gitignore ================================================ paused.conf .Makefile.swp .vscode vs10/.vs/ ================================================ FILE: LICENSE ================================================ GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are 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. 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. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. 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 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 work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Affero 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 Affero 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 Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. 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 AGPL, see . ================================================ FILE: Makefile ================================================ # If Windows, then assume the compiler is `gcc` for the # MinGW environment. I can't figure out how to tell if it's # actually MingGW. FIXME TODO ifeq ($(OS),Windows_NT) CC = gcc endif # Try to figure out the default compiler. I dont know the best # way to do this with `gmake`. If you have better ideas, please # submit a pull request on github. ifeq ($(CC),) ifneq (, $(shell which clang)) CC = clang else ifneq (, $(shell which gcc)) CC = gcc else CC = cc endif endif PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin SYS := $(shell $(CC) -dumpmachine) GITVER := $(shell git describe --tags) INSTALL_DATA := -pDm755 ifeq ($(GITVER),) GITVER = "unknown" endif # LINUX # The automated regression tests run on Linux, so this is the one # environment where things likely will work -- as well as anything # works on the bajillion of different Linux environments ifneq (, $(findstring linux, $(SYS))) ifneq (, $(findstring musl, $(SYS))) LIBS = else LIBS = -lm -lrt -ldl -lpthread endif INCLUDES = FLAGS2 = endif # MAC OS X # I occassionally develope code on Mac OS X, but it's not part of # my regularly regression-test environment. That means at any point # in time, something might be minorly broken in Mac OS X. ifneq (, $(findstring darwin, $(SYS))) LIBS = -lm INCLUDES = -I. FLAGS2 = INSTALL_DATA = -pm755 endif # MinGW on Windows # I develope on Visual Studio 2010, so that's the Windows environment # that'll work. However, 'git' on Windows runs under MingGW, so one # day I acccidentally typed 'make' instead of 'git, and felt compelled # to then fix all the errors, so this kinda works now. It's not the # intended environment, so it make break in the future. ifneq (, $(findstring mingw, $(SYS))) INCLUDES = -Ivs10/include LIBS = -L vs10/lib -lIPHLPAPI -lWs2_32 #FLAGS2 = -march=i686 endif # Cygwin # I hate Cygwin, use Visual Studio or MingGW instead. I just put this # second here for completeness, or in case I gate tired of hitting my # head with a hammer and want to feel a different sort of pain. ifneq (, $(findstring cygwin, $(SYS))) INCLUDES = -I. LIBS = FLAGS2 = endif # OpenBSD ifneq (, $(findstring openbsd, $(SYS))) LIBS = -lm -lpthread INCLUDES = -I. FLAGS2 = endif # FreeBSD ifneq (, $(findstring freebsd, $(SYS))) LIBS = -lm -lpthread INCLUDES = -I. FLAGS2 = endif # NetBSD ifneq (, $(findstring netbsd, $(SYS))) LIBS = -lm -lpthread INCLUDES = -I. FLAGS2 = endif DEFINES = CFLAGS = -g -ggdb $(FLAGS2) $(INCLUDES) $(DEFINES) -Wall -O2 .SUFFIXES: .c .cpp all: bin/masscan tmp/main-conf.o: src/main-conf.c src/*.h $(CC) $(CFLAGS) -c $< -o $@ -DGIT=\"$(GITVER)\" # just compile everything in the 'src' directory. Using this technique # means that include file dependencies are broken, so sometimes when # the program crashes unexpectedly, 'make clean' then 'make' fixes the # problem that a .h file was out of date tmp/%.o: src/%.c src/*.h $(CC) $(CFLAGS) -c $< -o $@ SRC = $(sort $(wildcard src/*.c)) OBJ = $(addprefix tmp/, $(notdir $(addsuffix .o, $(basename $(SRC))))) bin/masscan: $(OBJ) $(CC) $(CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(LIBS) clean: rm -f tmp/*.o rm -f bin/masscan regress: bin/masscan bin/masscan --selftest test: regress install: bin/masscan install $(INSTALL_DATA) bin/masscan $(DESTDIR)$(BINDIR)/masscan default: bin/masscan ================================================ FILE: README.md ================================================ [![unittests](https://github.com/robertdavidgraham/masscan/actions/workflows/unittests.yml/badge.svg?branch=master)](https://github.com/robertdavidgraham/masscan/actions/workflows/unittests.yml/?branch=master) # MASSCAN: Mass IP port scanner This is an Internet-scale port scanner. It can scan the entire Internet in under 5 minutes, transmitting 10 million packets per second, from a single machine. Its usage (parameters, output) is similar to `nmap`, the most famous port scanner. When in doubt, try one of those features -- features that support widespread scanning of many machines are supported, while in-depth scanning of single machines aren't. Internally, it uses asynchronous transmission, similar to port scanners like `scanrand`, `unicornscan`, and `ZMap`. It's more flexible, allowing arbitrary port and address ranges. NOTE: masscan uses its own **ad hoc TCP/IP stack**. Anything other than simple port scans may cause conflict with the local TCP/IP stack. This means you need to use either the `--src-ip` option to run from a different IP address, or use `--src-port` to configure which source ports masscan uses, then also configure the internal firewall (like `pf` or `iptables`) to firewall those ports from the rest of the operating system. This tool is free, but consider contributing money to its development: Bitcoin wallet address: 1MASSCANaHUiyTtR3bJ2sLGuMw5kDBaj4T # Building On Debian/Ubuntu, it goes something like the following. It doesn't really have any dependencies other than a C compiler (such as `gcc` or `clang`). sudo apt-get --assume-yes install git make gcc git clone https://github.com/robertdavidgraham/masscan cd masscan make This puts the program in the `masscan/bin` subdirectory. To install it (on Linux) run: make install The source consists of a lot of small files, so building goes a lot faster by using the multi-threaded build. This requires more than 2gigs on a Raspberry Pi (and breaks), so you might use a smaller number, like `-j4` rather than all possible threads. make -j While Linux is the primary target platform, the code runs well on many other systems (Windows, macOS, etc.). Here's some additional build info: * Windows w/ Visual Studio: use the VS10 project * Windows w/ MinGW: just type `make` * Windows w/ cygwin: won't work * Mac OS X /w XCode: use the XCode4 project * Mac OS X /w cmdline: just type `make` * FreeBSD: type `gmake` * other: try just compiling all the files together, `cc src/*.c -o bin/masscan` On macOS, the x86 binaries seem to work just as fast under ARM emulation. # Usage Usage is similar to `nmap`. To scan a network segment for some ports: # masscan -p80,8000-8100 10.0.0.0/8 2603:3001:2d00:da00::/112 This will: * scan the `10.x.x.x` subnet, and `2603:3001:2d00:da00::x` subnets * scans port 80 and the range 8000 to 8100, or 102 ports total, on both subnets * print output to `` that can be redirected to a file To see the complete list of options, use the `--echo` feature. This dumps the current configuration and exits. This output can be used as input back into the program: # masscan -p80,8000-8100 10.0.0.0/8 2603:3001:2d00:da00::/112 --echo > xxx.conf # masscan -c xxx.conf --rate 1000 ## Banner checking Masscan can do more than just detect whether ports are open. It can also complete the TCP connection and interaction with the application at that port in order to grab simple "banner" information. Masscan supports banner checking on the following protocols: * FTP * HTTP * IMAP4 * memcached * POP3 * SMTP * SSH * SSL * SMBv1 * SMBv2 * Telnet * RDP * VNC The problem with this is that masscan contains its own TCP/IP stack separate from the system you run it on. When the local system receives a SYN-ACK from the probed target, it responds with a RST packet that kills the connection before masscan can grab the banner. The easiest way to prevent this is to assign masscan a separate IP address. This would look like one of the following examples: # masscan 10.0.0.0/8 -p80 --banners --source-ip 192.168.1.200 # masscan 2a00:1450:4007:810::/112 -p80 --banners --source-ip 2603:3001:2d00:da00:91d7:b54:b498:859d The address you choose has to be on the local subnet and not otherwise be used by another system. Masscan will warn you that you've made a mistake, but you might've messed up the other machine's communications for several minutes, so be careful. In some cases, such as WiFi, this isn't possible. In those cases, you can firewall the port that masscan uses. This prevents the local TCP/IP stack from seeing the packet, but masscan still sees it since it bypasses the local stack. For Linux, this would look like: # iptables -A INPUT -p tcp --dport 61000 -j DROP # masscan 10.0.0.0/8 -p80 --banners --source-port 61000 You probably want to pick ports that don't conflict with ports Linux might otherwise choose for source-ports. You can see the range Linux uses, and reconfigure that range, by looking in the file: /proc/sys/net/ipv4/ip_local_port_range On the latest version of Kali Linux (2018-August), that range is 32768 to 60999, so you should choose ports either below 32768 or 61000 and above. Setting an `iptables` rule only lasts until the next reboot. You need to lookup how to save the configuration depending upon your distro, such as using `iptables-save` and/or `iptables-persistent`. On Mac OS X and BSD, there are similar steps. To find out the ranges to avoid, use a command like the following: # sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last On FreeBSD and older MacOS, use an `ipfw` command: # sudo ipfw add 1 deny tcp from any to any 40000 in # masscan 10.0.0.0/8 -p80 --banners --source-port 40000 On newer MacOS and OpenBSD, use the `pf` packet-filter utility. Edit the file `/etc/pf.conf` to add a line like the following: block in proto tcp from any to any port 40000:40015 Then to enable the firewall, run the command: # pfctl -E If the firewall is already running, then either reboot or reload the rules with the following command: # pfctl -f /etc/pf.conf Windows doesn't respond with RST packets, so neither of these techniques are necessary. However, masscan is still designed to work best using its own IP address, so you should run that way when possible, even when it is not strictly necessary. The same thing is needed for other checks, such as the `--heartbleed` check, which is just a form of banner checking. ## How to scan the entire Internet While useful for smaller, internal networks, the program is really designed with the entire Internet in mind. It might look something like this: # masscan 0.0.0.0/0 -p0-65535 Scanning the entire Internet is bad. For one thing, parts of the Internet react badly to being scanned. For another thing, some sites track scans and add you to a ban list, which will get you firewalled from useful parts of the Internet. Therefore, you want to exclude a lot of ranges. To blacklist or exclude ranges, you want to use the following syntax: # masscan 0.0.0.0/0 -p0-65535 --excludefile exclude.txt This just prints the results to the command-line. You probably want them saved to a file instead. Therefore, you want something like: # masscan 0.0.0.0/0 -p0-65535 -oX scan.xml This saves the results in an XML file, allowing you to easily dump the results in a database or something. But, this only goes at the default rate of 100 packets/second, which will take forever to scan the Internet. You need to speed it up as so: # masscan 0.0.0.0/0 -p0-65535 --max-rate 100000 This increases the rate to 100,000 packets/second, which will scan the entire Internet (minus excludes) in about 10 hours per port (or 655,360 hours if scanning all ports). The thing to notice about this command-line is that these are all `nmap` compatible options. In addition, "invisible" options compatible with `nmap` are also set for you: `-sS -Pn -n --randomize-hosts --send-eth`. Likewise, the format of the XML file is inspired by `nmap`. There are, of course, a lot of differences, because the *asynchronous* nature of the program leads to a fundamentally different approach to the problem. The above command-line is a bit cumbersome. Instead of putting everything on the command-line, it can be stored in a file instead. The above settings would look like this: # My Scan rate = 100000.00 output-format = xml output-status = all output-filename = scan.xml ports = 0-65535 range = 0.0.0.0-255.255.255.255 excludefile = exclude.txt To use this configuration file, use the `-c`: # masscan -c myscan.conf This also makes things easier when you repeat a scan. By default, masscan first loads the configuration file `/etc/masscan/masscan.conf`. Any later configuration parameters override what's in this default configuration file. That's where I put my "excludefile" parameter so that I don't ever forget it. It just works automatically. ## Getting output By default, masscan produces fairly large text files, but it's easy to convert them into any other format. There are five supported output formats: 1. xml: Just use the parameter `-oX `. Or, use the parameters `--output-format xml` and `--output-filename `. 2. binary: This is the masscan builtin format. It produces much smaller files so that when I scan the Internet my disk doesn't fill up. They need to be parsed, though. The command-line option `--readscan` will read binary scan files. Using `--readscan` with the `-oX` option will produce an XML version of the results file. 3. grepable: This is an implementation of the Nmap -oG output that can be easily parsed by command-line tools. Just use the parameter `-oG `. Or, use the parameters `--output-format grepable` and `--output-filename `. 4. json: This saves the results in JSON format. Just use the parameter `-oJ `. Or, use the parameters `--output-format json` and `--output-filename `. 5. list: This is a simple list with one host and port pair per line. Just use the parameter `-oL `. Or, use the parameters `--output-format list` and `--output-filename `. The format is: ``` open tcp 80 XXX.XXX.XXX.XXX 1390380064 ``` ## Comparison with Nmap Where reasonable, every effort has been taken to make the program familiar to `nmap` users, even though it's fundamentally different. Masscan is tuned for wide range scanning of a lot of machines, whereas nmap is designed for intensive scanning of a single machine or a small range. Two important differences are: * no default ports to scan, you must specify `-p ` * target hosts are IP addresses or simple ranges, not DNS names, nor the funky subnet ranges `nmap` can use (like `10.0.0-255.0-255`). You can think of `masscan` as having the following settings permanently enabled: * `-sS`: this does SYN scan only (currently, will change in the future) * `-Pn`: doesn't ping hosts first, which is fundamental to the async operation * `-n`: no DNS resolution happens * `--randomize-hosts`: scan completely randomized, always, you can't change this * `--send-eth`: sends using raw `libpcap` If you want a list of additional `nmap` compatible settings, use the following command: # masscan --nmap ## Transmit rate (IMPORTANT!!) This program spews out packets very fast. On Windows, or from VMs, it can do 300,000 packets/second. On Linux (no virtualization) it'll do 1.6 million packets-per-second. That's fast enough to melt most networks. Note that it'll only melt your own network. It randomizes the target IP addresses so that it shouldn't overwhelm any distant network. By default, the rate is set to 100 packets/second. To increase the rate to a million use something like `--rate 1000000`. When scanning the IPv4 Internet, you'll be scanning lots of subnets, so even though there's a high rate of packets going out, each target subnet will receive a small rate of incoming packets. However, with IPv6 scanning, you'll tend to focus on a single target subnet with billions of addresses. Thus, your default behavior will overwhelm the target network. Networks often crash under the load that masscan can generate. # Design This section describes the major design issues of the program. ## Code Layout The file `main.c` contains the `main()` function, as you'd expect. It also contains the `transmit_thread()` and `receive_thread()` functions. These functions have been deliberately flattened and heavily commented so that you can read the design of the program simply by stepping line-by-line through each of these. ## Asynchronous This is an *asynchronous* design. In other words, it is to `nmap` what the `nginx` web-server is to `Apache`. It has separate transmit and receive threads that are largely independent from each other. It's the same sort of design found in `scanrand`, `unicornscan`, and `ZMap`. Because it's asynchronous, it runs as fast as the underlying packet transmit allows. ## Randomization A key difference between Masscan and other scanners is the way it randomizes targets. The fundamental principle is to have a single index variable that starts at zero and is incremented by one for every probe. In C code, this is expressed as: for (i = 0; i < range; i++) { scan(i); } We have to translate the index into an IP address. Let's say that you want to scan all "private" IP addresses. That would be the table of ranges like: 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 In this example, the first 64k indexes are appended to 192.168.x.x to form the target address. Then, the next 16-million are appended to 10.x.x.x. The remaining indexes in the range are applied to 172.16.x.x. In this example, we only have three ranges. When scanning the entire Internet, we have in practice more than 100 ranges. That's because you have to blacklist or exclude a lot of sub-ranges. This chops up the desired range into hundreds of smaller ranges. This leads to one of the slowest parts of the code. We transmit 10 million packets per second and have to convert an index variable to an IP address for each and every probe. We solve this by doing a "binary search" in a small amount of memory. At this packet rate, cache efficiencies start to dominate over algorithm efficiencies. There are a lot of more efficient techniques in theory, but they all require so much memory as to be slower in practice. We call the function that translates from an index into an IP address the `pick()` function. In use, it looks like: for (i = 0; i < range; i++) { ip = pick(addresses, i); scan(ip); } Masscan supports not only IP address ranges, but also port ranges. This means we need to pick from the index variable both an IP address and a port. This is fairly straightforward: range = ip_count * port_count; for (i = 0; i < range; i++) { ip = pick(addresses, i / port_count); port = pick(ports, i % port_count); scan(ip, port); } This leads to another expensive part of the code. The division/modulus instructions are around 90 clock cycles, or 30 nanoseconds, on x86 CPUs. When transmitting at a rate of 10 million packets/second, we have only 100 nanoseconds per packet. I see no way to optimize this any better. Luckily, though, two such operations can be executed simultaneously, so doing two of these, as shown above, is no more expensive than doing one. There are actually some easy optimizations for the above performance problems, but they all rely upon `i++`, the fact that the index variable increases one by one through the scan. Actually, we need to randomize this variable. We need to randomize the order of IP addresses that we scan or we'll blast the heck out of target networks that aren't built for this level of speed. We need to spread our traffic evenly over the target. The way we randomize is simply by encrypting the index variable. By definition, encryption is random and creates a 1-to-1 mapping between the original index variable and the output. This means that while we linearly go through the range, the output IP addresses are completely random. In code, this looks like: range = ip_count * port_count; for (i = 0; i < range; i++) { x = encrypt(i); ip = pick(addresses, x / port_count); port = pick(ports, x % port_count); scan(ip, port); } This also has a major cost. Since the range is an unpredictable size instead of a nice even power of 2, we can't use cheap binary techniques like AND (&) and XOR (^). Instead, we have to use expensive operations like MODULUS (%). In my current benchmarks, it's taking 40 nanoseconds to encrypt the variable. This architecture allows for lots of cool features. For example, it supports "shards". You can set up 5 machines each doing a fifth of the scan or `range / shard_count`. Shards can be multiple machines, or simply multiple network adapters on the same machine, or even (if you want) multiple IP source addresses on the same network adapter. Or, you can use a 'seed' or 'key' to the encryption function, so that you get a different order each time you scan, like `x = encrypt(seed, i)`. We can also pause the scan by exiting out of the program, and simply remembering the current value of `i`, and restart it later. I do that a lot during development. I see something going wrong with my Internet scan, so I hit to stop the scan, then restart it after I've fixed the bug. Another feature is retransmits/retries. Packets sometimes get dropped on the Internet, so you can send two packets back-to-back. However, something that drops one packet may drop the immediately following packet. Therefore, you want to send the copy about 1 second apart. This is simple. We already have a 'rate' variable, which is the number of packets-per-second rate we are transmitting at, so the retransmit function is simply to use `i + rate` as the index. One of these days I'm going to do a study of the Internet, and differentiate "back-to-back", "1 second", "10 second", and "1 minute" retransmits this way in order to see if there is any difference in what gets dropped. ## C10 Scalability The asynchronous technique is known as a solution to the "c10k problem". Masscan is designed for the next level of scalability, the "C10M problem". The C10M solution is to bypass the kernel. There are three primary kernel bypasses in Masscan: * custom network driver * user-mode TCP stack * user-mode synchronization Masscan can use the PF_RING DNA driver. This driver DMAs packets directly from user-mode memory to the network driver with zero kernel involvement. That allows software, even with a slow CPU, to transmit packets at the maximum rate the hardware allows. If you put 8 10-gbps network cards in a computer, this means it could transmit at 100-million packets/second. Masscan has its own built-in TCP stack for grabbing banners from TCP connections. This means it can easily support 10 million concurrent TCP connections, assuming of course that the computer has enough memory. Masscan has no "mutex". Modern mutexes (aka. futexes) are mostly user-mode, but they have two problems. The first problem is that they cause cache-lines to bounce quickly back-and-forth between CPUs. The second is that when there is contention, they'll do a system call into the kernel, which kills performance. A mutex on the fast path of a program severely limits scalability. Instead, Masscan uses "rings" to synchronize things, such as when the user-mode TCP stack in the receive thread needs to transmit a packet without interfering with the transmit thread. ## Portability The code runs well on Linux, Windows, and Mac OS X. All the important bits are in standard C (C90). Therefore, it compiles on Visual Studio with Microsoft's compiler, the Clang/LLVM compiler on Mac OS X, and GCC on Linux. Windows and Macs aren't tuned for packet transmit, and get only about 300,000 packets-per-second, whereas Linux can do 1,500,000 packets/second. That's probably faster than you want anyway. ## Safe code A bounty is offered for vulnerabilities, see the VULNINFO.md file for more information. This project uses safe functions like `safe_strcpy()` instead of unsafe functions like `strcpy()`. This project has automated unit regression tests (`make regress`). ## Compatibility A lot of effort has gone into making the input/output look like `nmap`, which everyone who does port scans is (or should be) familiar with. ## IPv6 and IPv4 coexistence Masscan supports IPv6, but there is no special mode, both are supported at the same time. (There is no `-6` option -- it's always available). In any example you see of masscan usage, simply put an IPv6 address where you see an IPv4 address. You can include IPv4 and IPv6 addresses simultaneously in the same scan. Output includes the appropriate address at the same location, with no special marking. Just remember that IPv6 address space is really big. You probably don't want to scan for big ranges, except maybe the first 64k addresses of a subnet that were assigned via DHCPv6. Instead, you'll probably want to scan large lists of addresses stored in a file (`--include-file filename.txt`) that you got from other sources. Like everywhere else, this file can contain lists of both IPv4 and IPv6 addresses. The test file I use contains 8 million addresses. Files of that size need a couple extra seconds to be read on startup (masscan sorts the addresses and removes duplicates before scanning). Remember that masscan contains its own network stack. Thus, the local machine you run masscan from does not need to be IPv6 enabled -- though the local network needs to be able to route IPv6 packets. ## PF_RING To get beyond 2 million packets/second, you need an Intel 10-gbps Ethernet adapter and a special driver known as ["PF_RING ZC" from ntop](http://www.ntop.org/products/packet-capture/pf_ring/pf_ring-zc-zero-copy/). Masscan doesn't need to be rebuilt in order to use PF_RING. To use PF_RING, you need to build the following components: * `libpfring.so` (installed in /usr/lib/libpfring.so) * `pf_ring.ko` (their kernel driver) * `ixgbe.ko` (their version of the Intel 10-gbps Ethernet driver) You don't need to build their version of `libpcap.so`. When Masscan detects that an adapter is named something like `zc:enp1s0` instead of something like `enp1s0`, it'll automatically switch to PF_RING ZC mode. A more detail discussion can be found in **PoC||GTFO 0x15**. ## Regression testing The project contains a built-in unit test: $ make test bin/masscan --selftest selftest: success! This tests a lot of tricky bits of the code. You should do this after building. ## Performance testing To test performance, run something like the following to a throw-away address, to avoid overloading your local router: $ bin/masscan 0.0.0.0/4 -p80 --rate 100000000 --router-mac 66-55-44-33-22-11 The bogus `--router-mac` keeps packets on the local network segments so that they won't go out to the Internet. You can also test in "offline" mode, which is how fast the program runs without the transmit overhead: $ bin/masscan 0.0.0.0/4 -p80 --rate 100000000 --offline This second benchmark shows roughly how fast the program would run if it were using PF_RING, which has near zero overhead. By the way, the randomization algorithm makes heavy use of "integer arithmetic", a chronically slow operation on CPUs. Modern CPUs have doubled the speed at which they perform this calculation, making `masscan` much faster. # Authors This tool created by Robert Graham: email: robert_david_graham@yahoo.com twitter: @ErrataRob # License Copyright (c) 2013 Robert David Graham This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License. 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ================================================ FILE: VULNINFO.md ================================================ # Vulnerability Information and Policy This document contains information about robustness of this project against hacker attacks. It describes known vulnerabilities that have been found in previous versions, and describes policies how vulnerabilities are handled. ## Security contact robert_david_graham@yahoo.com @ErrataRob on twitter ## Known vulnerabilities and advisories none ## Bounty I'm offering $100, payable in cash or Bitcoin, for security vulnerabilities. This is primarily for remote vulnerabilities, such as the ability of a target to buffer-overflow the scanner, or even cause it to crash. But I'd consider other vulnerabilities as well. Does Kali ship this with suid and there's a preload bug? That's not really a vuln in this code, but if it's something I could fix, I'd consider paying a bounty for it. ## Disclosure policy If you've got a vuln, just announce it. Please send info to the contact above as well, please. I'll probably get around to fixing it within a month or so. This really isn't heavily used software, so I'm lax on this. ## Threats The primary threat is from hostile targets on the Internet sending back responses in order to: * exploit a buffer-overflow vulnerability * spoof packets trying to give fraudulent scan results (mitigated with our SYN cookies) * flood packets trying to overload bandwidth/storage * bad data, such as corrupting banners or DNS names trying to exploit downstream consumers with bad html or script tags. The secondary threat is from use of the program. For example, when a bad parameter is entered on the command-line, the program spits it back out in a helpful error message. This is fine for a command-line program that should run as `root` anyway, but if somebody tries to make it into a scriptable service, this becomes a potential vulnerability. ## Safe code policy Unsafe functions like `strcpy()` are banned. The code contains an automated regression test by running with the `--regress` option. However, currently the regression only tests a small percentage of the code. ================================================ FILE: bin/.gitignore ================================================ * !.gitignore ================================================ FILE: data/exclude.conf ================================================ # http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml # http://tools.ietf.org/html/rfc5735 # "This" network 0.0.0.0/8 # Private networks 10.0.0.0/8 # Carrier-grade NAT - RFC 6598 100.64.0.0/10 # Host loopback 127.0.0.0/8 # Link local 169.254.0.0/16 # Private networks 172.16.0.0/12 # IETF Protocol Assignments 192.0.0.0/24 # DS-Lite 192.0.0.0/29 # NAT64 192.0.0.170/32 # DNS64 192.0.0.171/32 # Documentation (TEST-NET-1) 192.0.2.0/24 # 6to4 Relay Anycast 192.88.99.0/24 # Private networks 192.168.0.0/16 # Benchmarking 198.18.0.0/15 # Documentation (TEST-NET-2) 198.51.100.0/24 # Documentation (TEST-NET-3) 203.0.113.0/24 # Reserved 240.0.0.0/4 # Limited Broadcast 255.255.255.255/32 #Received: from elbmasnwh002.us-ct-eb01.gdeb.com ([153.11.13.41] # helo=ebsmtp.gdeb.com) by mx1.gd-ms.com with esmtp (Exim 4.76) (envelope-from # ) id 1VS55c-0004qL-0F for support@erratasec.com; Fri, 04 # Oct 2013 09:06:40 -0400 #To: #CC: #Subject: Scanning and Probing our network #From: Robert Mandes #Date: Fri, 4 Oct 2013 09:06:36 -0400 # #Stop scanning and probing our network, 153.11.0.0/16. We are a defense #contractor and report to Federal law enforcement authorities when scans #and probes are directed at our network. I assume you don't want to be #part of that report. Please permanently remove our network range from #your current and future research. # #Thank you # #Robert Mandes #Information Security Officer #General Dynamics #Electric Boat # #C 860-625-0605 #P 860-433-1553 153.11.0.0/16 #Date: Mon, 7 Oct 2013 17:25:41 -0700 #Subject: Re: please stop the attack to our router #From: Di Li # #Make sure you stop the scan immediately, that's not OK for any company or #organization scan our network at all. # #If you fail to do that we will block whole traffic from ASN 10439, and we #will file a police report after that. # #Let me know when you stop, since we still receive the attack from you, and #by the way your scan are not going anywhere, it's was dropped from our edge #since the first 5 scan # #Oct 7 17:17:32:I:SNMP: Auth. failure, intruder IP: 209.126.230.72 #... #Oct 7 16:55:27:I:SNMP: Auth. failure, intruder IP: 209.126.230.72 # #Di 4.53.201.0/24 5.152.179.0/24 8.12.162.0-8.12.164.255 8.14.84.0/22 8.14.145.0-8.14.147.255 8.17.250.0-8.17.252.255 23.27.0.0/16 23.231.128.0/17 37.72.172.0/23 38.72.200.0/22 50.93.192.0-50.93.197.255 50.115.128.0/20 50.117.0.0/17 50.118.128.0/17 63.141.222.0/24 64.62.253.0/24 64.92.96.0/19 64.145.79.0/24 64.145.82.0/23 64.158.146.0/23 65.49.24.0/24 65.49.93.0/24 65.162.192.0/22 66.79.160.0/19 66.160.191.0/24 68.68.96.0/20 69.46.64.0/19 69.176.80.0/20 72.13.80.0/20 72.52.76.0/24 74.82.43.0/24 74.82.160.0/19 74.114.88.0/22 74.115.0.0/24 74.115.2.0/24 74.115.4.0/24 74.122.100.0/22 75.127.0.0/24 103.251.91.0/24 108.171.32.0/24 108.171.42.0/24 108.171.52.0/24 108.171.62.0/24 118.193.78.0/23 130.93.16.0/23 136.0.0.0/16 142.111.0.0/16 142.252.0.0/16 146.82.55.93 149.54.136.0/21 149.54.152.0/21 166.88.0.0/16 172.252.0.0/16 173.245.64.0/19 173.245.194.0/23 173.245.220.0/22 173.252.192.0/18 178.18.16.0/22 178.18.26.0-178.18.29.255 183.182.22.0/24 192.92.114.0/24 192.155.160.0/19 192.177.0.0/16 192.186.0.0/18 192.249.64.0/20 192.250.240.0/20 194.110.214.0/24 198.12.120.0-198.12.122.255 198.144.240.0/20 199.33.120.0/24 199.33.124.0/22 199.48.147.0/24 199.68.196.0/22 199.127.240.0/21 199.187.168.0/22 199.188.238.0/23 199.255.208.0/24 203.12.6.0/24 204.13.64.0/21 204.16.192.0/21 204.19.238.0/24 204.74.208.0/20 205.159.189.0/24 205.164.0.0/18 205.209.128.0/18 206.108.52.0/23 206.165.4.0/24 208.77.40.0/21 208.80.4.0/22 208.123.223.0/24 209.51.185.0/24 209.54.48.0/20 209.107.192.0/23 209.107.210.0/24 209.107.212.0/24 211.156.110.0/23 216.83.33.0-216.83.49.255 216.83.51.0-216.83.63.255 216.151.183.0/24 216.151.190.0/23 216.172.128.0/19 216.185.36.0/24 216.218.233.0/24 216.224.112.0/20 #Received: from [194.77.40.242] (HELO samba.agouros.de) # for abuse@erratasec.com; Sat, 12 Oct 2013 09:55:35 -0500 #Received: from rumba.agouros.de (rumba-internal [192.168.8.1]) by # samba.agouros.de (Postfix) with ESMTPS id 9055FBAD1D for # ; Sat, 12 Oct 2013 16:55:32 +0200 (CEST) #Received: from rumba.agouros.de (localhost [127.0.0.1]) by rumba.agouros.de # (Postfix) with ESMTP id 7B5DD206099 for ; Sat, 12 Oct # 2013 16:55:32 +0200 (CEST) #Received: from localhost.localdomain (localhost [127.0.0.1]) by # rumba.agouros.de (Postfix) with ESMTP id 5FBC420601D for # ; Sat, 12 Oct 2013 16:55:32 +0200 (CEST) #To: #Subject: Loginattempts from Your net #Message-ID: <20131012145532.5FBC420601D@rumba.agouros.de> #Date: Sat, 12 Oct 2013 16:55:32 +0200 #From: # #The address 209.126.230.72 from Your network tried to log in to #our network using Port 22 (1)/tcp. Below You will find a listing of the dates and #times the incidents occured as well as the attacked IP-Addresses. #This is a matter of concern for us and continued tries might result in #legal action. If the machine was victim to a hack take it offline, repair #the damage and use better protection next time. #The times included are in Central European (Summer) Time. #Date Sourceip port destips # #07.10.2013 22:34:40 CEST 209.126.230.72 22 194.77.40.242 (1) #08.10.2013 01:44:15 CEST 209.126.230.72 22 194.77.40.246 (1) # #Regards, #Konstantin Agouros 194.77.40.242 194.77.40.246 #Received: from [165.160.9.58] (HELO mx2.cscinfo.com) #X-Virus-Scanned: amavisd-new at cscinfo.com #Received: from mx2.cscinfo.com ([127.0.0.1]) by localhost # (plmail02.wil.csc.local [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id # GGQ7EiQaK2P0 for ; Wed, 30 Oct 2013 09:26:00 -0400 # (EDT) #Received: from casarray.cscinfo.com (pwmailch02.cscinfo.com [172.20.53.94]) by # mx2.cscinfo.com (Postfix) with ESMTPS id 4BA5E58170 for # ; Wed, 30 Oct 2013 09:26:00 -0400 (EDT) #Received: from PWMAILM02.cscinfo.com ([169.254.7.52]) by # PWMAILCH02.cscinfo.com ([172.20.53.94]) with mapi id 14.02.0247.003; Wed, 30 # Oct 2013 09:26:00 -0400 #From: "Derksen, Bill" #Subject: Unauthorized Scanning #Date: Wed, 30 Oct 2013 13:25:59 +0000 #Message-ID: <1F80316A0C861F40A9A88F18465F138E01EF885F@PWMAILM02.cscinfo.com> #x-originating-ip: [172.31.252.72] # #We have detected unauthorized activity from your systems on our public netw= #ork. Please suspend scanning of our networks immediately. # #Our network block is 165.160/16 # #Further scanning will result in reports of unauthorized activity being file= #d with law enforcement agencies. # #Corporation Service Company # # # #________________________________ # #NOTICE: This e-mail and any attachments is intended only for use by the add= #ressee(s) named herein and may contain legally privileged, proprietary or c= #onfidential information. If you are not the intended recipient of this e-ma= #il, you are hereby notified that any dissemination, distribution or copying= # of this email, and any attachments thereto, is strictly prohibited. If you= # receive this email in error please immediately notify me via reply email o= #r at (800) 927-9800 and permanently delete the original copy and any copy o= #f any e-mail, and any printout. 165.160.0.0/16 #****************************** #Greetings from the IT Security Team at Utah State University. # #We have detected network activity that might be suspicious or #malicious. We think it might be sourced from your network. We #include IP Addresses as well as description, log snippets, and #other useful information. # #Please review this information or forward to the responsible person. 129.123.0.0/16 144.39.0.0/16 204.113.91.0/24 #On Friday, November 17th 2017 starting at 03:39 EST (UTC-5:00), part of the #Physics Network at McGill University (132.206.9.0/24, 132.206.123.0/24 #and/or 132.206.125.0/24) was scanned from xxx.xxx.xxx.xxx (see syslog #snippet below). The scan targetted the domain service (port 53/udp). We #consider this scan to be an attempt to unlawfully access or abuse our #network (intentionally or as a result of virus or worm activity). 132.206.9.0/24 132.206.123.0/24 132.206.125.0/24 # # Add DOD + US Military, often not a great idea to scan military ranges. # If you desire, you can comment these ranges out. # 6.0.0.0/8 7.0.0.0/8 11.0.0.0/8 21.0.0.0/8 22.0.0.0/8 26.0.0.0/8 28.0.0.0/8 29.0.0.0/8 30.0.0.0/8 33.0.0.0/8 55.0.0.0/8 205.0.0.0/8 214.0.0.0/8 215.0.0.0/8 #****************************** #Janet is a UK research and education network! #Please DO NOT scan, you been warned! 31.25.0.0/23 31.25.2.0/23 31.25.4.0/22 37.72.112.0/21 46.254.200.0/21 81.87.0.0/16 85.12.64.0/18 89.207.208.0/21 92.245.224.0/19 128.16.0.0/16 128.40.0.0/16 128.41.0.0/18 128.86.0.0/16 128.232.0.0/16 128.240.0.0/16 128.243.0.0/16 129.11.0.0/16 129.12.0.0/16 129.31.0.0/16 129.67.0.0/16 129.169.0.0/16 129.215.0.0/16 129.234.0.0/16 130.88.0.0/16 130.159.0.0/16 130.209.0.0/16 130.246.0.0/16 131.111.0.0/16 131.227.0.0/16 131.231.0.0/16 131.251.0.0/16 134.36.0.0/16 134.83.0.0/16 134.151.0.0/16 134.219.0.0/16 134.220.0.0/16 134.225.0.0/16 136.148.0.0/16 136.156.0.0/16 137.44.0.0/16 137.50.0.0/16 137.73.0.0/16 137.108.0.0/16 137.195.0.0/16 137.222.0.0/16 137.253.0.0/16 138.38.0.0/16 138.40.0.0/16 138.250.0.0/15 138.253.0.0/16 139.133.0.0/16 139.153.0.0/16 139.166.0.0/16 139.184.0.0/16 139.222.0.0/16 140.97.0.0/16 141.163.0.0/16 141.170.64.0/19 141.170.96.0/22 141.170.100.0/23 141.241.0.0/16 143.52.0.0/15 143.117.0.0/16 143.167.0.0/16 143.210.0.0/16 143.234.0.0/16 144.32.0.0/16 144.82.0.0/16 144.124.0.0/16 144.173.0.0/16 146.87.0.0/16 146.97.0.0/16 146.169.0.0/16 146.176.0.0/16 146.179.0.0/16 146.191.0.0/16 146.227.0.0/16 147.143.0.0/16 147.188.0.0/16 147.197.0.0/16 148.79.0.0/16 148.88.0.0/16 148.197.0.0/16 149.155.0.0/16 149.170.0.0/16 150.204.0.0/16 152.71.0.0/16 152.78.0.0/16 152.105.0.0/16 155.198.0.0/16 155.245.0.0/16 157.140.0.0/16 157.228.0.0/16 158.94.0.0/16 158.125.0.0/16 158.143.0.0/16 158.223.0.0/16 159.86.128.0/18 159.92.0.0/16 160.5.0.0/16 160.9.0.0/16 161.73.0.0/16 161.74.0.0/16 161.76.0.0/16 161.112.0.0/16 163.1.0.0/16 163.119.0.0/16 163.160.0.0/16 163.167.0.0/16 164.11.0.0/16 185.83.168.0/22 192.12.72.0/24 192.18.195.0/24 192.35.172.0/24 192.41.104.0/21 192.41.112.0/20 192.41.128.0/22 192.68.153.0/24 192.76.6.0/23 192.76.8.0/21 192.76.16.0/20 192.76.32.0/22 192.82.153.0/24 192.84.5.0/24 192.84.75.0/24 192.84.76.0/22 192.84.80.0/22 192.84.212.0/24 192.88.9.0/24 192.88.10.0/24 192.94.235.0/24 192.100.78.0/24 192.100.154.0/24 192.107.168.0/24 192.108.120.0/24 192.124.46.0/24 192.133.244.0/24 192.149.111.0/24 192.150.180.0/22 192.150.184.0/24 192.153.213.0/24 192.156.162.0/24 192.160.194.0/24 192.171.128.0/18 192.171.192.0/21 192.173.1.0/24 192.173.2.0/23 192.173.4.0/24 192.173.128.0/21 192.188.157.0/24 192.188.158.0/24 192.190.201.0/24 192.190.202.0/24 192.195.42.0/23 192.195.105.0/24 192.195.116.0/23 192.195.118.0/24 193.32.22.0/24 193.37.225.0/24 193.37.240.0/21 193.38.143.0/24 193.39.80.0/21 193.39.172.0/22 193.39.212.0/24 193.60.0.0/14 193.107.116.0/22 193.130.15.0/24 193.133.28.0/23 193.138.86.0/24 194.32.32.0/20 194.35.93.0/24 194.35.186.0/24 194.35.192.0/19 194.35.241.0/24 194.36.1.0/24 194.36.2.0/23 194.36.121.0/24 194.36.152.0/21 194.60.218.0/24 194.66.0.0/16 194.80.0.0/14 194.187.32.0/22 195.194.0.0/15 212.121.0.0/19 212.121.192.0/19 212.219.0.0/16 ================================================ FILE: debian/.gitignore ================================================ files masscan masscan.debhelper.log masscan.substvars ================================================ FILE: debian/README.Debian ================================================ masscan for Debian ------------------ Initial debianisation -- Vladimir Vitkov Fri, 24 Jan 2014 11:03:38 +0200 ================================================ FILE: debian/changelog ================================================ masscan (1.0.3-46+gbbadb7b-1) precise; urgency=low * Rebuild from current git * Use propper git hash versioning -- Vladimir Vitkov (Packaging Key) Tue, 10 Jun 2014 12:11:04 +0300 masscan (1.0.1+git20140124-1) unstable; urgency=low * Rebuild from current git -- Vladimir Vitkov Fri, 24 Jan 2014 14:37:30 +0200 masscan (1.0.1+git20140106-1) unstable; urgency=low * Initial release -- Vladimir Vitkov Fri, 24 Jan 2014 11:03:38 +0200 ================================================ FILE: debian/compat ================================================ 8 ================================================ FILE: debian/control ================================================ Source: masscan Section: net Priority: extra Maintainer: Vladimir Vitkov Build-Depends: debhelper (>= 8.0.0), libpcap-dev Standards-Version: 3.9.3 Homepage: https://github.com/robertdavidgraham/masscan #Vcs-Git: https://github.com/robertdavidgraham/masscan.git #Vcs-Browser: http://git.debian.org/?p=collab-maint/masscan.git;a=summary Package: masscan Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Mass IP port scanner This is the fastest Internet port scanner. It can scan the entire Internet in under 6 minutes, transmitting 10 million packets per second. . It produces results similar to nmap, the most famous port scanner. Internally, it operates more like scanrand, unicornscan, and ZMap, using asynchronous transmission. The major difference is that it's faster than these other scanners. In addition, it's more flexible, allowing arbitrary address ranges and port ranges. ================================================ FILE: debian/copyright ================================================ Format: http://dep.debian.net/deps/dep5 Upstream-Name: masscan Source: https://github.com/robertdavidgraham/masscan Files: * Copyright: 2013 Robert David Graham License: AGPL This program, "masscan", is not completely free software. It may not be used by the United States Department of Defense (DoD) or National Security Agency (NSA), or by agents acting on their behalf, such as contractors, sub-contractors, and so on. These entitities must contact me to acquire a different license. . Barring the above exception, you can use, redistribute, and/or modify this code under the terms of the GNU Affero General Public License version 3. . 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 Affero General Public License for more details. . You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Files: debian/* Copyright: 2014 Vladimir Vitkov License: GPL-3.0+ License: GPL-3.0+ 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 package 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 . . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". ================================================ FILE: debian/masscan.dirs ================================================ usr/bin ================================================ FILE: debian/masscan.docs ================================================ README.md VULNINFO.md doc/algorithm.js doc/bot.html ================================================ FILE: debian/masscan.install ================================================ bin/masscan usr/bin/ ================================================ FILE: debian/masscan.manpages ================================================ doc/masscan.8 ================================================ FILE: debian/rules ================================================ #!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ override_dh_auto_test: $(MAKE) regress ================================================ FILE: debian/source/format ================================================ 3.0 (quilt) ================================================ FILE: debian/watch ================================================ version=3 # use the github release pages https://github.com/robertdavidgraham/masscan/releases .*/(\d\S*)\.(?:tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) ================================================ FILE: doc/algorithm.js ================================================ /* This is an implementation of the core Masscan scanning algorithm in JavaScript/NodeJS. The core scanning algorithm is what makes Masscan unique from other scanners, so it's worth highlighting separately in a sample program. REVIEW OF SCANNERS The most famous port-scanner is "nmap". However, it is a "host-at-a-time" scanner, and struggles at scanning large networks. Masscan is an asynchronous, probe-at-a-time scanner. It spews out probes to different ports, without caring if two probes happen to be send to the same host. If the user wants a list of all ports open on a single host, they have to post-process the masscan output themselves, because masscan doesn't do it. There are other asynchronous port-scanners, like scanrand, unicornscan, and zmap. However, they have limitations in the way they do randomization of their scans. They have limitations on the ranges of addresses and ports that they'll accept, try to store an individual memory record for everything scanned, or only partly randomize their scans. THE WAY MASSCAN WORKS Masscan first stores the targets as a "list of ranges". IP address ranges are stored in one structure, and port ranges are stored in another structure. Then, a single index variable is used to enumerate the set of all IP:port combinations. The scan works by simply incrementing the index variable from 0 to the total number of probes (the 'range'). Then, before the enumeration step, the index is permuted into another random index within the same range, in a 1-to-1 mapping. In other words, the algorithm is theoretically reversable: given the output of the permutation function, we can obtain the original index. EXAMPLE This program can be run like the following: node patent.js 10.0.0.0-10.0.0.5 192.168.0.0/31 80,U:161 10.0.0.0-10.0.0.5 192.168.0.0-192.168.0.1 0.0.0.80-0.0.0.80 0.1.0.161-0.1.0.161 --> 10.0.0.4 udp:161 --> 10.0.0.0 udp:161 --> 10.0.0.1 udp:161 --> 10.0.0.4 tcp:80 --> 192.168.0.1 tcp:80 --> 10.0.0.0 tcp:80 --> 10.0.0.2 udp:161 --> 10.0.0.5 udp:161 --> 192.168.0.0 tcp:80 --> 192.168.0.0 udp:161 --> 10.0.0.1 tcp:80 --> 10.0.0.3 udp:161 --> 10.0.0.2 tcp:80 --> 10.0.0.5 tcp:80 --> 192.168.0.1 udp:161 --> 10.0.0.3 tcp:80 What you see first is the target ranges being echoed back that it scans, first the IP address ranges, followed by the port ranges. The port ranges are in weird decimal-dot notation because they share the same code as for IPv4 addresses. Then we see the randomized output, where individual probes are sent to a random IP address and port. TransmitThread All the majic happens in the "TransmitThread()" function near the bottom of this file. We first see how the index variable 'i' is incremented from 0 to the total number of packets that will be sent. We then see how first this index is permuted to 'xXx', then this variable is separated into one index for the IP address and another index for the port. Then, those indexes are used to enumerate one of the IP addresses and one of the ports. Blackrock This is the permutation function. It implements an encryption algorithm based on DES (Data Encryption Standard). However, the use of real DES would impose a restricting on the range that it be an even power of 2. In the above example, with 14 total probes, this doesn't apply. Therefore, we have to change binary operators like XOR with their non-binary equivelents. The upshot is that we first initialize Blackrock with the range (and a seed/key), and then shuffle the index. The process is stateless, meaning that any time we shuffle the number '5' we always get the same result, regardless of what has happened before. Targets, RangeList, Range A Range is just a begin/end of an integer. We use this both for IPv4 addresses (which are just 32-bit integers) and ports (which are 16 bit integers). A RangeList is just an array of Ranges. In Masscan, this object sorts and combines ranges, making sure there are no duplicates, but that isn't used in this example. The RangeList object shows how an index can enumerate the individual addresses/ports. This is down by walking the list and subtracting from the index the size of each range, until we reach a range that is larger than the index. The Targets object just holds both the IP and port lists. */ function Range(begin, end) { if (typeof begin == 'undefined' && typeof end == 'undefined') { this.begin = 0xFFFFFFFF; this.end = 0; } else if (typeof end == 'undefined') { this.begin = begin; this.end = begin; } else { this.begin = begin; this.end = end; } this.toString = function () { return ((this.begin >> 24) & 0xFF) + "." + ((this.begin >> 16) & 0xFF) + "." + ((this.begin >> 8) & 0xFF) + "." + ((this.begin >> 0) & 0xFF) + "-" + ((this.end >> 24) & 0xFF) + "." + ((this.end >> 16) & 0xFF) + "." + ((this.end >> 8) & 0xFF) + "." + ((this.end >> 0) & 0xFF); } this.count = function () { return this.end - this.begin + 1; } this.pick = function (index) { return this.begin + index; } return this; } function RangeList() { this.list = []; this.total_count = 0; this.push = function (range) { this.list.push(range); this.total_count += range.count(); } this.count = function () { return this.total_count; } this.pick = function (index) { for (var i in this.list) { var item = this.list[i]; if (index < item.count()) return item.pick(index); else index -= item.count(); } return null; } } function Targets() { this.ports = new RangeList(); this.ips = new RangeList(); this.parse_ip = function (text) { var x = text.split("."); var result = 0; result |= parseInt(x[0]) << 24; result |= parseInt(x[1]) << 16; result |= parseInt(x[2]) << 8; result |= parseInt(x[3]) << 0; return result; } this.parse_ports = function (arg) { var offset = 0; if (arg.indexOf(":") !== -1) { var x = arg.split(":"); if (x[0] == "U") offset = 65536; else if (x[0] == "S") offset = 65536 * 2; arg = x[1]; } var target; if (arg.indexOf("-") !== -1) { var x = arg.split("-"); target = new Range(parseInt(x[0]), parseInt(x[1])); } else target = new Range(parseInt(arg)); target.begin += offset; target.end += offset; this.ports.push(target); } this.parse_args = function (argv) { for (var i in argv) { var arg = argv[i]; if (arg.indexOf(",") !== -1) { var x = arg.split(","); for (var j in x) this.parse_ports(x[j]); } else if (arg.indexOf("/") !== -1) { var x = arg.split("/"); var address = this.parse_ip(x[0]); var prefix = parseInt(x[1]); var mask = 0xFFFFFFFF << (32 - prefix); address = address & mask; var target = new Range(address, address | ~mask); this.ips.push(target); } else if (arg.indexOf("-") !== -1) { var x = arg.split("-"); var begin = this.parse_ip(x[0]); var end = this.parse_ip(x[1]); var target = new Range(begin, end); this.ips.push(target); } else if (arg.indexOf(".") !== -1) { var target = new Range(this.parse_ip(arg)); this.ips.push(target); } else { this.parse_ports(arg); } } } this.print = function () { var i; for (i in this.ips.list) { console.log(this.ips.list[i].toString()); } for (i in this.ports.list) { console.log(this.ports.list[i].toString()); } } return this; } function Blackrock(range, seed) { var split = Math.floor(Math.sqrt(range * 1.0)); this.rounds = 3; this.seed = seed; this.range = range; this.a = split - 1; this.b = split + 1; while (this.a * this.b <= range) this.b++; /** Inner permutation function */ this.F = function (j, R, seed) { var primes = [961752031, 982324657, 15485843, 961752031]; R = ((R << (R & 0x4)) + R + seed); return Math.abs((((primes[j] * R + 25) ^ R) + j)); } /** Outer feistal construction */ this.fe = function (r, a, b, m, seed) { var L, R; var j; var tmp; L = m % a; R = Math.floor(m / a); for (j = 1; j <= r; j++) { if (j & 1) { tmp = (L + this.F(j, R, seed)) % a; } else { tmp = (L + this.F(j, R, seed)) % b; } L = R; R = tmp; } if (r & 1) { return a * L + R; } else { return a * R + L; } } /** Outer reverse feistal construction */ this.unfe = function (r, a, b, m, seed) { var L, R; var j; var tmp; if (r & 1) { R = m % a; L = Math.floor(m / a); } else { L = m % a; R = Math.floor(m / a); } for (j = r; j >= 1; j--) { if (j & 1) { tmp = this.F(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = a - (tmp % a); if (tmp == a) tmp = 0; } else { tmp = (R - tmp); tmp %= a; } } else { tmp = this.F(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = b - (tmp % b); if (tmp == b) tmp = 0; } else { tmp = (R - tmp); tmp %= b; } } R = L; L = tmp; } return a * R + L; } this.shuffle = function (m) { var c; c = this.fe(this.rounds, this.a, this.b, m, this.seed); while (c >= this.range) c = this.fe(this.rounds, this.a, this.b, c, this.seed); return c; } this.unshuffle = function (m) { var c; c = unfe(this.rounds, this.a, this.b, m, this.seed); while (c >= this.range) c = unfe(this.rounds, this.a, this.b, c, this.seed); return c; } return this; } function TransmitThread(targets, transmit, seed) { var range = targets.ips.count() * targets.ports.count(); var b = Blackrock(range, seed); for (var i = 0; i < range; i++) { var xXx = b.shuffle(i); var ip_index = Math.floor(xXx / targets.ports.count()); var port_index = Math.floor(xXx % targets.ports.count()); var ip = targets.ips.pick(ip_index); var port = targets.ports.pick(port_index); transmit(ip, port); } } function Transmit2Thread(targets, transmit, seed, start, stop, increment) { var range = targets.ips.count() * targets.ports.count(); var b = Blackrock(range, seed); for (var i = start; i < range && i < stop; i += increment) { var xXx = b.shuffle(i); var ip_index = Math.floor(xXx / targets.ports.count()); var port_index = Math.floor(xXx % targets.ports.count()); var ip = targets.ips.pick(ip_index); var port = targets.ports.pick(port_index); transmit(ip, port); } } function transmit(ip, port) { var proto = "tcp"; if (port > 65536 * 2) { proto = "sctp"; port -= 65536 * 2; } else if (port > 65536) { proto = "udp"; port -= 65536; } var ipstring = ((ip >> 24) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + ((ip >> 0) & 0xFF) console.log("--> " + ipstring + " " + proto + ":" + port); } var targets = new Targets(); targets.parse_args(process.argv.splice(2)); targets.print(); TransmitThread(targets, transmit, 42); ================================================ FILE: doc/bot.html ================================================ Masscan/1.0 - fast port scanner

Masscan/1.0 - fast port scanner

This tool is not a web spider, but a port scanner. It'll make only one request to a website, usually for the root / webpage. It then records the Server: field from the HTTP header, the <title> from the page contents, and possibly a few other interesting fields.

This does not follow links, it doesn't scan your web pages, but the ports on your machine.

The source code for this tool is at https://github.com/robertdavidgraham/masscan/. This is an open source project, so that this means it's not me (Robert Graham) who is using this tool to scan your website, but likely somebody else. I can't speak for their intentions, but this tool is more useful at doing surveys of the Internet than trying to hack in (tools like 'nmap' or 'nessus' are more often used for that).

================================================ FILE: doc/faq/FAQ0001-slow.md ================================================ # Why is it not as fast as I expect? ## Question Why is scanning speed only around 100,000 packets-per-second instead of a million packets-per-second? ## Answer I don't know. If you have the latest Linux distro on the latest hardware, you can sometime see scanning speeds of 1 million packets-per-second, even when virtualized. However, sometimes you also see only 100,000 packets-per-second. I've spent a lot of time trying to diagnose this situation and cannot figure out what's going on. The box I use in a colo does 500,000 packets-per-second. A relatively slow machine in my home lab does 1.2 million packets-per-second. The speed is determined by the operating system. The amount of CPU used by `masscan` itself is insignificant. My theory is various configuration options within the operating system that can make packet transmission very slow. Simple features that would not otherwise impact network stacks that run at lower rates become really important at high rates. One way around this is to install `PF_RING` and dedicate a network adapter to packet transmission completely bypassing the operating system. In that case, packet transmission rates can reach 15 million packets-per-second. ================================================ FILE: doc/faq/FAQ0002-drops.md ================================================ # Why are many results missing that I expect? # Question When I do a scan, results are missing that I know are there. They show up when I repeat the scan, but then others are missing. The faster I scan, the more results are missing. # Answer Network infrastructure does not like high rates of small packets. Even though they can handle high **bit-rates** then cannot handle high **packet-rates**. This is what makes `masscan` so unique. It transmits packets at rates far higher than other things can cope with. It often crashes networks. Therefore, the faster you transmit packets, the more it overloads network equipment, causing the packets to be dropped, causing probes to fail. As the issue #546 below indicates, they are experiencing this at very low rates of less than 10,000 packets-per-second. That seems excessively low. I assume the actual reason is because of policy enforcement limiting traffic rates rather than overloading network equipment. # Issues - [#546 fast scan get result](https://github.com/robertdavidgraham/masscan/issues/546) ================================================ FILE: doc/faq/FAQ0003-excludelist.md ================================================ # How can I add my IP address to an exclude list so that people stop scanning me? # Question I hate everyone probing me all the time and want them to stop. How can I add my IP address ranges to an exclude list? # Answer You can't. First of all, nobody is going to pay attention to a sample exclude list within this project. Sure, I can add IP addresses to the list, but that won't help you. Second, there's no way I can confirm who you are. So I can't simply add to an exclude list just because you ask. Thirdly, it'll just make you more of a target, as smart hackers know to use the exclude-list as one of their first include-lists, as it marks people who have something to hide. Fourthly, and most importantly, it's Wrong Think on how to manage your network. ================================================ FILE: doc/faq/FAQ0004-serverlogs.md ================================================ # Why is masscan in my server logs? ## Question Some example questions: * Why is `masscan` appearing in my server logs? * Why are you scanning me? * Why is my server trying to connect to this github repo? ## Answer When `masscan` connections to a webserver, it puts a link back to this repo in the `User-Agent` field. Since lots of people run Internet-wide scans using this tool, and an Internet wide scan hits every public web server, you'll see this appear in your web server logs several times a day. It's the **end-to-end** principle of the Internet. Having a public webserver on the Internet means that anybody can and will try to connect to the web server. It's nothing necessarily malicious. Lots of people run Internet-wide scans to gather information about the state of the Internet. Of course, some are indeed malicious, scanning to find vulnerabilities. However, even when malicious, they probably aren't targetting you in particular, but are instead scanning everybody. ================================================ FILE: doc/faq/README.md ================================================ # FAQs (frequently asked questions) This directory contains some documents discussing frequently asked questions - 1 - [Why is it not as fast as I expect?](FAQ0001-slow.md) - 2 - [Why are many results missing that I expect?](FAQ0002-drops.md) - 3 - [How can I add my IPs to an official exclude list, to get people to stop scanning me?](FAQ0003-excludelist.md) - 4 - [Why is this in my server logs?](FAQ0004-serverlogs.md) ================================================ FILE: doc/howto-afl.md ================================================ Using afl fuzzer against masscan ================================ AFL (American-Fuzzy-Lop) is an automated *fuzzer*. It takes existing input to a program, then morphs that input in order to test new code paths through the code. It's extremely successful at finding bugs as well as *vulnerabilities*. There are two inputs to *masscan*. One is the files it reads, which come in various formats. The second is input from the network, in response to network probes, which consist of various network protocols. For AFL, there is also the issue of how *masscan* crashes. It tries to print a backtrace. This causes AFL to falsely mark this as a "hang" rather than a "crash". To fix this, run *masscan* with the *--nobacktrace* option. ## Fuzzing file formats ## The *masscan* program reads the following files. You can set the fuzzer at each one of these to fuzz how it parses the contents. *-c * *Masscan* can read its configuration either from the command-line or from a file. To create a file, run *masscan* as normal, then hit . This will save all it's settings, even default values, into a file. It's a good starting point for fuzzing. *--readscan * One of the possible outputs of *masscan* is in a proprietary binary format. You can then run *masscan* to convert to any other output format. In other words, you can run masscan to output XML like: masscan -oX scan.xml Or, in a two step process: masscan -ob scan.mass masscan --readscan scan.mass -oX scan.xml *--exclude-file * *Masscan* can scan large ragnes, like *0.0.0.0/0* (the entire Internet). You may want to exclude specific addresses or ranges. These are configured in the "exclude file". You can also read ranges using *--include-file * or *-iL *, but as far as fuzzing, I'm pretty sure it'll be the same results. *--hello-file[] * This file is read, then dumped blindly across a TCP connection, in order to say "hello" to a service. Since there's no parsing here, I'm not sure you'll find anything fuzzing this. *--nmap-payloads * This file specifies the default payloads for UDP. It's in the same file format as for *nmap*. There's some juicy parsing here that may lead to bugs. *--pcap-payloads * This is the same as *--nmap-payloads*, but reads the UDP payloads from a *libpcap* file. ## Fuzzing network protocols ## *Masscan* parses several network protocols. It also must reassemble fragmented responses over TCP for any application protocol. Remember: *masscan* has it's own stack, so must parse everything that comes over the network. AFL has no ability to read from the network at this time. Moreover, even then it wouldn't work easily, since *masscan* has a network stack rather than just an application layer to deal with. The trick is to first use *masscan* on a target that responds back on a protocol, then save just the response side of the TCP connection to a file. Then, when running *masscan* under AFL, read in that file using the option *--adapter file:*. Then, and this is critical, you must hard code all the TCP stack values to match those of the original connection. I generated the file *masscan/data/afl-http.pcap* as an example file to read for fuzzing the parsing of HTTP. The command-line parameters to use are: bin/masscan --nobacktrace --adapter file:data/afl-http.pcap --source-ip 10.20.30.200 --source-port 6000 --source-mac 00-11-22-33-44-55 --router-mac c0-c1-c0-a0-9b-9d --seed 0 --banners -p80 74.125.196.147 --nostatus The explanation are: *--nobacktrace*: so that AFL correctly marks crashes as crashes and not as hangs. *--adapter file:*: This option normally specifies the adapter, like *eth0* or *en1*. By putting the *file:* prefix on an adapter name, it'll use a file (in *libpcap* format) to use instead. In this case, transmits are dropped, and any packets are read from a file. *--source-ip*, *--source-port*, *--source-mac*, *--router-mac*: These are the hard-coded TCP/IP stack settings that must match the packets in the file. *--seed*: This must match the randomization seed in the packet file. Since everything else is hardcoded, I think the only thing this will control will be the sequence number of the TCP connection. *--banners*: this tell the scanner to not simply find open ports, but also establish a TCP connection and interact with the protocol, and report on the results. *-p*: The destination IP address to connect to. **: The IP address to connect to This should produce an output like the following. If you get the banner back, then you know you've successfully done everything correctly. Conversely, if you set *--seed 1*, then it won't work, because it'll reject responses that match the wrong seed. Starting masscan 1.0.3 (http://bit.ly/14GZzcT) at 2016-06-06 05:19:03 GMT -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth Initiating SYN Stealth Scan Scanning 1 hosts [1 port/host] Discovered open port 80/tcp on 74.125.196.147 Banner on port 80/tcp on 74.125.196.147: [title] Google Banner on port 80/tcp on 74.125.196.147: [http] HTTP/1.0 200 OK\x0d\x0a... ... (Additional output is truncated -- you get the idea). The problem with this is that *masscan* will take about 10 seconds before it produces the results. When it establishes a connection, it waits a few seconds for the other side to transmit, then sends it's "hello", then waits many seconds for all output to be received. I don't know if this messes AFL up, whether I need to add additional options to truncate any waiting. ================================================ FILE: doc/masscan.8 ================================================ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "MASSCAN" "8" "January 2014" "" "" . .SH "NAME" \fBmasscan\fR \- Fast scan of the Internet . .SH "SYNOPSIS" masscan [\fIoptions\fR] [... \-p \fIPORT\fR[,\fIPORT\fR...]] . .SH "DESCRIPTION" \fBmasscan\fR is an Internet\-scale port scanner, useful for large scale surveys of the Internet, or of internal networks\. While the default transmit rate is only 100 packets/second, it can optional go as fast as 25 million packets/second, a rate sufficient to scan the Internet in 3 minutes for one port\. . .SH "OPTIONS" . .IP "\(bu" 4 \fB\fR: anything on the command\-line not prefixed with a \'\-\' is assumed to be an IP address or range\. There are three valid formats\. The first is a single IPv4 address like "192\.168\.0\.1"\. The second is a range like "10\.0\.0\.1\-10\.0\.0\.100"\. The third is a CIDR address, like "0\.0\.0\.0/0"\. At least one target must be specified\. Multiple targets can be specified\. This can be specified as multiple options separated by space, or can be separated by a comma as a single option, such as \fB10\.0\.0\.0/8,192\.168\.0\.1\fR\. . .IP "\(bu" 4 \fB\-\-range \fR: the same as target range spec described above, except as a named parameter instead of an unnamed one\. . .IP "\(bu" 4 \fB\-p PORT[,PORT...]\fR, \fB\-\-ports PORT[,PORT...]\fR: specifies the port(s) to be scanned\. A single port can be specified, like \fB\-p80\fR\. A range of ports can be specified, like \fB\-p 20\-25\fR\. A list of ports/ranges can be specified, like \fB\-p80,20\-25\fR\. UDP ports can also be specified, like \fB\-\-ports U:161,U:1024\-1100\fR\. . .IP "\(bu" 4 \fB\-\-banners\fR: specifies that banners should be grabbed, like HTTP server versions, HTML title fields, and so forth\. Only a few protocols are supported\. . .IP "\(bu" 4 \fB\-\-rate RATE\fR: specifies the desired rate for transmitting packets\. This can be very small numbers, like \fB0\.1\fR for transmitting packets at rates of one every 10 seconds, for very large numbers like 10000000, which attempts to transmit at 10 million packets/second\. In my experience, Windows and can do 250 thousand packets per second, and latest versions of Linux can do 2\.5 million packets per second\. The PF_RING driver is needed to get to 25 million packets/second\. . .IP "\(bu" 4 \fB\-c FILE\fR, \fB\-\-conf FILE\fR: reads in a configuration file\. The format of the configuration file is described below\. . .IP "\(bu" 4 \fB\-\-resume FILE\fR: the same as \fB\-\-conf\fR, except that a few options are automatically set, such as \fB\-\-append\-output\fR\. The format of the configuration file is described below\. . .IP "\(bu" 4 \fB\-\-echo\fR: don\'t run, but instead dump the current configuration to a file\. This file can then be used with the \fB\-c\fR option\. The format of this output is described below under \'CONFIGURATION FILE\'\. . .IP "\(bu" 4 \fB\-e IFNAME\fR, \fB\-\-adapter IFNAME\fR: use the named raw network interface, such as "eth0" or "dna1"\. If not specified, the first network interface found with a default gateway will be used\. . .IP "\(bu" 4 \fB\-\-adapter\-ip IP\fR: send packets using this IP address\. If not specified, then the first IP address bound to the network interface will be used\. Instead of a single IP address, a range may be specified\. NOTE: The size of the range must be an even power of 2, such as 1, 2, 4, 8, 16, 1024 etc\. addresses\. . .IP "\(bu" 4 \fB\-\-adapter\-port PORT\fR: send packets using this port number as the source\. If not specified, a random port will be chosen in the range 40000 through 60000\. This port should be filtered by the host firewall (like iptables) to prevent the host network stack from interfering with arriving packets\. Instead of a single port, a range can be specified, like \fB40000\-40003\fR\. NOTE: The size of the range must be an even power of 2, such as the example above that has a total of 4 addresses\. . .IP "\(bu" 4 \fB\-\-adapter\-mac MAC\fR: send packets using this as the source MAC address\. If not specified, then the first MAC address bound to the network interface will be used\. . .IP "\(bu" 4 \fB\-\-router\-mac MAC\fR: send packets to this MAC address as the destination\. If not specified, then the gateway address of the network interface will be ARPed\. . .IP "\(bu" 4 \fB\-\-ping\fR: indicates that the scan should include an ICMP echo request\. This may be included with TCP and UDP scanning\. . .IP "\(bu" 4 \fB\-\-exclude \fR: blacklist an IP address or range, preventing it from being scanned\. This overrides any target specification, guaranteeing that this address/range won\'t be scanned\. This has the same format as the normal target specification\. . .IP "\(bu" 4 \fB\-\-excludefile FILE\fR: reads in a list of exclude ranges, in the same target format described above\. These ranges override any targets, preventing them from being scanned\. . .IP "\(bu" 4 \fB\-\-append\-output\fR: causes output to append to the file, rather than overwriting the file\. . .IP "\(bu" 4 \fB\-\-iflist\fR: list the available network interfaces, and then exits\. . .IP "\(bu" 4 \fB\-\-retries NUM\fR: the number of retries to send, at 1 second intervals\. Note that since this scanner is stateless, retries are sent regardless if replies have already been received\. . .IP "\(bu" 4 \fB\-\-nmap\fR: print help about nmap\-compatibility alternatives for these options\. . .IP "\(bu" 4 \fB\-\-pcap\-payloads FILE\fR: read packets from a libpcap file containing packets and extract the UDP payloads, and associate those payloads with the destination port\. These payloads will then be used when sending UDP packets with the matching destination port\. Only one payload will be remembered per port\. Similar to \fB\-\-nmap\-payloads\fR\. . .IP "\(bu" 4 \fB\-\-nmap\-payloads FILE\fR: read in a file in the same format as the nmap file \fBnmap\-payloads\fR\. This contains UDP payload, so that we can send useful UDP packets instead of empty ones\. Similar to \fB\-\-pcap\-payloads\fR\. . .IP "\(bu" 4 \fB\-\-http\-user\-agent USER_AGENT\fR: replaces the existing user\-agent field with the indicated value when doing HTTP requests\. . .IP "\(bu" 4 \fB\-\-open\-only\fR: report only open ports, not closed ports\. . .IP "\(bu" 4 \fB\-\-pcap FILE\fR: saves received packets (but not transmitted packets) to the libpcap\-format file\. . .IP "\(bu" 4 \fB\-\-packet\-trace\fR: prints a summary of those packets sent and received\. This is useful at low rates, like a few packets per second, but will overwhelm the terminal at high rates\. . .IP "\(bu" 4 \fB\-\-pfring\fR: force the use of the PF_RING driver\. The program will exit if PF_RING DNA drvers are not available\. . .IP "\(bu" 4 \fB\-\-resume\-index INDEX\fR: the point in the scan at when it was paused\. . .IP "\(bu" 4 \fB\-\-resume\-count NUM\fR: the maximum number of probes to send before exiting\. This is useful with the \fB\-\-resume\-index\fR to chop up a scan and split it among multiple instances, though the \fB\-\-shards\fR option might be better\. . .IP "\(bu" 4 \fB\-\-shards X/Y\fR: splits the scan among instances\. \fBx\fR is the id for this scan, while \fBy\fR is the total number of instances\. For example, \fB\-\-shards 1/2\fR tells an instance to send every other packet, starting with index 0\. Likewise, \fB\-\-shards 2/2\fR sends every other packet, but starting with index 1, so that it doesn\'t overlap with the first example\. . .IP "\(bu" 4 \fB\-\-rotate TIME\fR: rotates the output file, renaming it with the current timestamp, moving it to a separate directory\. The time is specified in number of seconds, like "3600" for an hour\. Or, units of time can be specified, such as "hourly", or "6hours", or "10min"\. Times are aligned on an even boundary, so if "daily" is specified, then the file will be rotated every day at midnight\. . .IP "\(bu" 4 \fB\-\-rotate\-offset TIME\fR: an offset in the time\. This is to accommodate timezones\. . .IP "\(bu" 4 \fB\-\-rotate\-dir DIR\fR: when rotating the file, this specifies which directory to move the file to\. A useful directory is \fB/var/log/masscan\fR\. . .IP "\(bu" 4 \fB\-\-seed INT\fR: an integer that seeds the random number generator\. Using a different seed will cause packets to be sent in a different random order\. Instead of an integer, the string \fBtime\fR can be specified, which seeds using the local timestamp, automatically generating a differnet random order of scans\. If no seed specified, \fBtime\fR is the default\. . .IP "\(bu" 4 \fB\-\-regress\fR: run a regression test, returns \'0\' on success and \'1\' on failure\. . .IP "\(bu" 4 \fB\-\-ttl NUM\fR: specifies the TTL of outgoing packets, defaults to 255\. . .IP "\(bu" 4 \fB\-\-wait SECONDS\fR: specifies the number of seconds after transmit is done to wait for receiving packets before exiting the program\. The default is 10 seconds\. The string \fBforever\fR can be specified to never terminate\. . .IP "\(bu" 4 \fB\-\-offline\fR: don\'t actually transmit packets\. This is useful with a low rate and \fB\-\-packet\-trace\fR to look at what packets might\'ve been transmitted\. Or, it\'s useful with \fB\-\-rate 100000000\fR in order to benchmark how fast transmit would work (assuming a zero\-overhead driver)\. PF_RING is about 20% slower than the benchmark result from offline mode\. . .IP "\(bu" 4 \fB\-sL\fR: this doesn\'t do a scan, but instead creates a list of random addresses\. This is useful for importing into other tools\. The options \fB\-\-shard\fR, \fB\-\-resume\-index\fR, and \fB\-\-resume\-count\fR can be useful with this feature\. . .IP "\(bu" 4 \fB\-\-interactive\fR: show the results in realtime on the console\. It has no effect if used with \-\-output\-format or \-\-output\-filename\. . .IP "\(bu" 4 \fB\-\-output\-format FMT\fR: indicates the format of the output file, which can be \fBxml\fR, \fBbinary\fR, \fBgrepable\fR, \fBlist\fR, or \fBJSON\fR\. The option \fB\-\-output\-filename\fR must be specified\. . .IP "\(bu" 4 \fB\-\-output\-filename FILE\fR: the file which to save results to\. If the parameter \fB\-\-output\-format\fR is not specified, then the default of \fBxml\fR will be used\. . .IP "\(bu" 4 \fB\-oB FILE\fR: sets the output format to binary and saves the output in the given filename\. This is equivelent to using the \fB\-\-output\-format\fR and \fB\-\-output\-filename\fR parameters\. The option \fB\-\-readscan\fR can then be used to read the binary file\. Binary files are much smaller than their XML equivelents, but require a separate step to convert back into XML or another readable format\. . .IP "\(bu" 4 \fB\-oX FILE\fR: sets the output format to XML and saves the output in the given filename\. This is equivelent to using the \fB\-\-output\-format xml\fR and \fB\-\-output\-filename\fR parameters\. . .IP "\(bu" 4 \fB\-oG FILE\fR: sets the output format to grepable and saves the output in the given filename\. This is equivelent to using the \-\-output\-format grepable and \-\-output\-filename parameters\. . .IP "\(bu" 4 \fB\-oJ FILE\fR: sets the output format to JSON and saves the output in the given filename\. This is equivelent to using the \-\-output\-format json and \-\-output\-filename parameters\. . .IP "\(bu" 4 \fB\-oL FILE\fR: sets the output format to a simple list format and saves the output in the given filename\. This is equivelent to using the \-\-output\-format list and \-\-output\-filename parameters\. . .IP "\(bu" 4 \fB\-\-readscan FILE\fR: reads the files created by the \fB\-oB\fR option from a scan, then outputs them in one of the other formats, depending on command\-line parameters\. In other words, it can take the binary version of the output and convert it to an XML or JSON format\. . .IP "" 0 . .SH "CONFIGURATION FILE FORMAT" The configuration file uses the same parameter names as on the commandline, but without the \fB\-\-\fR prefix, and with an \fB=\fR sign between the name and the value\. An example configuration file might be: . .IP "" 4 . .nf # targets range = 10\.0\.0\.0/8,192\.168\.0\.0/16 range = 172\.16\.0\.0/14 ports = 20\-25,80,U:53 ping = true # adapter adapter = eth0 adapter\-ip = 192\.168\.0\.1 router\-mac = 66\-55\-44\-33\-22\-11 # other exclude\-file = /etc/masscan/exludes\.txt . .fi . .IP "" 0 . .P By default, the program will read default configuration from the file \fB/etc/masscan/masscan\.conf\fR\. This is useful for system\-specific settings, such as the \fB\-\-adapter\-xxx\fR options\. This is also useful for excluded IP addresses, so that you can scan the entire Internet, while skipping dangerous addresses, like those owned by the DoD, and not make an accidental mistake\. . .SH "CONTROL\-C BEHAVIOR" When the user presses \fIctrl\-c\fR, the scan will stop, and the current state of the scan will be saved in the file \'paused\.conf\'\. The scan can be resumed with the \fB\-\-resume\fR option: . .IP "" 4 . .nf # masscan \-\-resume paused\.conf . .fi . .IP "" 0 . .P The program will not exit immediately, but will wait a default of 10 seconds to receive results from the Internet and save the results before exiting completely\. This time can be changed with the \fB\-\-wait\fR option\. . .SH "SIMPLE EXAMPLES" The following example scans all private networks for webservers, and prints all open ports that were found\. . .IP "" 4 . .nf # masscan 10\.0\.0\.0/8 192\.168\.0\.0/16 172\.16\.0\.0/12 \-p80 \-\-open\-only . .fi . .IP "" 0 . .P The following example scans the entire Internet for DNS servers, grabbing their versions, then saves the results in an XML file\. . .IP "" 4 . .nf # masscan 0\.0\.0\.0/0 \-\-excludefile no\-dod\.txt \-pU:53 \-\-banners \-\-output\-filename dns\.xml . .fi . .IP "" 0 . .P You should be able to import the XML into databases and such\. . .P The following example reads a binary scan results file called bin\-test\.scan and prints results to console\. . .IP "" 4 . .nf # masscan \-\-readscan bin\-test\.scan . .fi . .IP "" 0 . .P The following example reads a binary scan results file called bin\-test\.scan and creates an XML output file called bin\-test\.xml\. . .IP "" 4 . .nf # masscan \-\-readscan bin\-test\.scan \-oX bin\-test\.xml . .fi . .IP "" 0 . .SH "ADVANCED EXAMPLES" Let\'s say that you want to scan the entire Internet and spread the scan across three machines\. Masscan would be launched on all three machines using the following command\-lines: . .IP "" 4 . .nf # masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-shard 1/3 # masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-shard 2/3 # masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-shard 3/3 . .fi . .IP "" 0 . .P An alternative is with the "resume" feature\. A scan has an internal index that goes from zero to the number of ports times then number of IP addresses\. The following example shows splitting up a scan into chunks of a 1000 items each: . .IP "" 4 . .nf # masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 0 \-\-resume\-count 1000 # masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 1000 \-\-resume\-count 1000 # masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 2000 \-\-resume\-count 1000 # masscan 0\.0\.0\.0/0 \-p0\-65535 \-\-resume\-index 3000 \-\-resume\-count 1000 . .fi . .IP "" 0 . .P A script can use this to split smaller tasks across many other machines, such as Amazon EC2 instances\. As each instance completes a job, the script might send a request to a central coordinating server for more work\. . .SH "SPURIOUS RESETS" When scanning TCP using the default IP address of your adapter, the built\-in stack will generate RST packets\. This will prevent banner grabbing\. There are are two ways to solve this\. The first way is to create a firewall rule to block that port from being seen by the stack\. How this works is dependent on the operating system, but on Linux this looks something like: . .IP "" 4 . .nf # iptables \-A INPUT \-p tcp \-i eth0 \-\-dport 61234 \-j DROP . .fi . .IP "" 0 . .P Then, when scanning, that same port must be used as the source: . .IP "" 4 . .nf # masscan 10\.0\.0\.0/8 \-p80 \-\-banners \-\-adapter\-port 61234 . .fi . .IP "" 0 . .P An alternative is to "spoof" a different IP address\. This IP address must be within the range of the local network, but must not otherwise be in use by either your own computer or another computer on the network\. An example of this would look like: . .IP "" 4 . .nf # masscan 10\.0\.0\.0/8 \-p80 \-\-banners \-\-adapter\-ip 192\.168\.1\.101 . .fi . .IP "" 0 . .P Setting your source IP address this way is the preferred way of running this scanner\. . .SH "ABUSE COMPLAINTS" This scanner is designed for large\-scale surveys, of either an organization, or of the Internet as a whole\. This scanning will be noticed by those monitoring their logs, which will generate complaints\. . .P If you are scanning your own organization, this may lead to you being fired\. Never scan outside your local subnet without getting permission from your boss, with a clear written declaration of why you are scanning\. . .P The same applies to scanning the Internet from your employer\. This is another good way to get fired, as your IT department gets flooded with complaints as to why your organization is hacking them\. . .P When scanning on your own, such as your home Internet or ISP, this will likely cause them to cancel your account due to the abuse complaints\. . .P One solution is to work with your ISP, to be clear about precisely what we are doing, to prove to them that we are researching the Internet, not "hacking" it\. We have our ISP send the abuse complaints directly to us\. For anyone that asks, we add them to our "\-\-excludefile", blacklisting them so that we won\'t scan them again\. While interacting with such people, some instead add us to their whitelist, so that their firewalls won\'t log us anymore (they\'ll still block us, of course, they just won\'t log that fact to avoid filling up their logs with our scans)\. . .P Ultimately, I don\'t know if it\'s possible to completely solve this problem\. Despite the Internet being a public, end\-to\-end network, you are still "guilty until proven innocent" when you do a scan\. . .SH "COMPATIBILITY" While not listed in this document, a lot of parameters compatible with \fBnmap\fR will also work\. . .SH "SEE ALSO" nmap(8), pcap(3) . .SH "AUTHORS" This tool was written by Robert Graham\. The source code is available at https://github\.com/robertdavidgraham/masscan\. ================================================ FILE: doc/masscan.8.markdown ================================================ masscan(8) -- Fast scan of the Internet ======================================= ## SYNOPSIS masscan \[options\] \[... -p PORT\[,PORT...\]\] ## DESCRIPTION **masscan** is an Internet-scale port scanner, useful for large scale surveys of the Internet, or of internal networks. While the default transmit rate is only 100 packets/second, it can optionally go as fast as 25 million packets/second, a rate sufficient to scan the Internet in 3 minutes for one port. ## OPTIONS * ``: anything on the command-line not prefixed with a '-' is assumed to be an IP address or range. There are three valid formats. The first is a single IP address like `192.168.0.1` or `2001:db8::1`. The second is a range like `10.0.0.1-10.0.0.100`. The third is a CIDR address, like `0.0.0.0/0` or `2001:db8::/90`. At least one target must be specified. Multiple targets can be specified. This can be specified as multiple options separated by space, or can be separated by a comma as a single option, such as `10.0.0.0/8,192.168.0.1,2001:db8::1`. * `--range `: the same as target range spec described above, except as a named parameter instead of an unnamed one. * `-p PORT[,PORT..]`, `--ports PORT[,PORT...]`: specifies the port(s) to be scanned. A single port can be specified, like `-p80`. A range of ports can be specified, like `-p 20-25`. A list of ports/ranges can be specified, like `-p80,20-25`. UDP ports can also be specified, like `--ports U:161,U:1024-1100`. * `--banners`: specifies that banners should be grabbed after establishing a TCP connection. Protocols supported include HTTP, FTP, IMAP4, memcached, POP3, SMTP, SSH, SSL, SMB, Telnet, RDP, and VNC. * `--rate RATE`: specifies the desired rate for transmitting packets. This can be very small numbers, like `0.1` for transmitting packets at rates of one every 10 seconds, for very large numbers like 10000000, which attempts to transmit at 10 million packets/second. In my experience, Windows and can do 250 thousand packets per second, and latest versions of Linux can do 2.5 million packets per second. The PF_RING driver is needed to get to 25 million packets/second. * `-c FILE`, `--conf FILE`: reads in a configuration file. If not specified, then will read from `/etc/masscan/masscan.conf` by default. The format is described below under 'CONFIGURATION FILE'. * `--resume FILE`: the same as `--conf`, except that a few options are automatically set, such as `--append-output`. The format of the configuration file is described below. The purpose is to resume a scan saved in `paused.conf` that was interupted with [ctrl-c]. * `--echo`: don't run, but instead dump the current configuration to a file. This file can then be used with the `-c` option. The format of this output is described below under 'CONFIGURATION FILE'. * `-e IFNAME`, `--adapter IFNAME`: use the named raw network interface, such as "eth0" or "dna1". If not specified, the first network interface found with a default gateway will be used. * `--adapter-ip IP`, `--source-ip IP`: send packets using this IP address. If not specified, then the first IP address bound to the network interface will be used. Instead of a single IP address, a range may be specified. NOTE: The size of the range must be an even power of 2, such as 1, 2, 4, 8, 16, 1024 etc. addresses. * `--adapter-port PORT`: send packets using this port number as the source. If not specified, a random port will be chosen in the range 40000 through 60000. This port should be filtered by the host firewall (like iptables) to prevent the host network stack from interfering with arriving packets. Instead of a single port, a range can be specified, like `40000-40003`. NOTE: The size of the range must be an even power of 2, such as the example above that has a total of 4 addresses. * `--adapter-mac MAC`: send packets using this as the source MAC address. If not specified, then the first MAC address bound to the network interface will be used. * `--adapter-vlan VLANID`: send packets using this 802.1q VLAN ID * `--router-mac MAC`: send packets to this MAC address as the destination. If not specified, then the gateway address of the network interface will be ARPed. * `--ping`: indicates that the scan should include an ICMP echo request. This may be included with TCP and UDP scanning. * `--exclude `: blacklist an IP address or range, preventing it from being scanned. This overrides any target specification, guaranteeing that this address/range won't be scanned. This has the same format as the normal target specification. * `--excludefile FILE`: reads in a list of exclude ranges, in the same target format described above. These ranges override any targets, preventing them from being scanned. * `-iL FILE`, `--includefile FILE`: reads in a list of ranges to scan, in the same target format described above for IP addresses and ranges. This file can contain millions of addresses and ranges. * `--append-output`: causes output to append to the file, rather than overwriting the file. Useful for when resumeing scans (see `--resume`). * `--iflist`: list the available network interfaces, and then exits. The `-e IFNAME` can then be used with one of the listed adapters. * `--retries `: the number of retries to send, at 1 second intervals. Note that since this scanner is stateless, retries are sent regardless if replies have already been received. * `--nmap`: print help about nmap-compatibility alternatives for these options. * `--pcap-payloads FILE`: read packets from a libpcap file containing packets and extract the UDP payloads, and associate those payloads with the destination port. These payloads will then be used when sending UDP packets with the matching destination port. Only one payload will be remembered per port. Similar to `--nmap-payloads`. * `--nmap-payloads FILE`: read in a file in the same format as the nmap file `nmap-payloads`. This contains UDP payload, so that we can send useful UDP packets instead of empty ones. Similar to `--pcap-payloads`. * `--http-* HEADER`: replaces the existing field in the HTTP header with a new one. Fields that can be replaced are `--http-method`, `--http-url`, `--http-version`,`--http-host`, and `--http-user-agent`. Example: `--http-user-agent Keurig K575 Coffee Maker`. See also `--http-field` and `--http-cookie`. * `--http-field NAME:VALUE`: replaces the existing HTTP header field, or inserts a new one if the field doesn't exist, given as a `name:value` pair. Cannot be used to replace the fields in the request-line (method, url, version). Example: `--http-field Accept:image/gif`. * `--http-field-remove NAME`: removes the first field from the header that matches (may be needed multiple times for fields like `Cookie` that can exist multiple times) * `--http-cookie VALUE`: adds a `Cookie:` field to the HTTP header, even if other cookie fields exist. The other `--http-*` options replace existing fields in the HTTP header, this one adds more even if some already exist. * `--http-payload STR`: adds a payload string after the header; this will automatically add a `--http-field Content-Length:LEN` field to match the length of the string, but the user will have to add their own `--http-field Content-Type:TYPE` field to match the string. Presumably, the user will also change the method to something like `--http-method POST`. Common conntent types would be `application/x-www-form-urlencoded`, `application/json`, or `text/xml`. * `--show [open|closed]`: tells which port status to display, such as 'open' for those ports that respond with a SYN-ACK on TCP, or 'closed' for those ports that repsond with RST. The default is only to display 'open' ports. * `--noshow [open|closed]`: disables a port status to display, such as to no longer display 'open' ports. * `--pcap FILE`: saves received packets (but not transmitted packets) to the libpcap-format file. * `--packet-trace`: prints a summary of those packets sent and received. This is useful at low rates, like a few packets per second, but will overwhelm the terminal at high rates. * `--pfring`: force the use of the PF_RING driver. The program will exit if PF_RING DNA drvers are not available. * `--resume-index INDEX`: the point in the scan at when it was paused. * `--resume-count NUM`: the maximum number of probes to send before exiting. This is useful with the `--resume-index` to chop up a scan and split it among multiple instances, though the `--shards` option might be better. * `--shards X/Y`: splits the scan among instances. `x` is the id for this scan, while `y` is the total number of instances. For example, `--shards 1/2` tells an instance to send every other packet, starting with index 0. Likewise, `--shards 2/2` sends every other packet, but starting with index 1, so that it doesn't overlap with the first example. * `--rotate TIME`: rotates the output file, renaming it with the current timestamp, moving it to a separate directory. The time is specified in number of seconds, like "3600" for an hour. Or, units of time can be specified, such as "hourly", or "6hours", or "10min". Times are aligned on an even boundary, so if "daily" is specified, then the file will be rotated every day at midnight. * `--rotate-offset TIME`: an offset in the time. This is to accomodate timezones. * `--rotate-size SIZE`: rotates the output file when it exceeds the given size. Typical suffixes can be applied (k,m,g,t) for kilo, mega, giga, tera. * `--rotate-dir DIR`: when rotating the file, this specifies which directory to move the file to. A useful directory is `/var/log/masscan`. * `--seed INT`: an integer that seeds the random number generator. Using a different seed will cause packets to be sent in a different random order. Instead of an integer, the string `time` can be specified, which seeds using the local timestamp, automatically generating a different random order of scans. If no seed specified, `time` is the default. * `--regress`: run a regression test, returns '0' on success and '1' on failure. * `--ttl NUM`: specifies the TTL of outgoing packets, defaults to 255. * `--wait SECONDS`: specifies the number of seconds after transmit is done to wait for receiving packets before exiting the program. The default is 10 seconds. The string `forever` can be specified to never terminate. * `--offline`: don't actually transmit packets. This is useful with a low rate and `--packet-trace` to look at what packets might've been transmitted. Or, it's useful with `--rate 100000000` in order to benchmark how fast transmit would work (assuming a zero-overhead driver). PF_RING is about 20% slower than the benchmark result from offline mode. * `-sL`: this doesn't do a scan, but instead creates a list of random addresses. This is useful for importing into other tools. The options `--shard`, `--resume-index`, and `--resume-count` can be useful with this feature. * `--interactive`: show the results in realtime on the console. It has no effect if used with --output-format or --output-filename. * `--output-format FMT`: indicates the format of the output file, which can be `xml`, `binary`, `grepable`, `list`, or `JSON`. The option `--output-filename` must be specified. * `--output-filename FILE`: the file which to save results to. If the parameter `--output-format` is not specified, then the default of `xml` will be used. * `-oB FILE`: sets the output format to binary and saves the output in the given filename. This is equivalent to using the `--output-format` and `--output-filename` parameters. The option `--readscan` can then be used to read the binary file. Binary files are mush smaller than their XML equivalents, but require a separate step to convert back into XML or another readable format. * `-oX FILE`: sets the output format to XML and saves the output in the given filename. This is equivalent to using the `--output-format xml` and `--output-filename` parameters. * `-oG FILE`: sets the output format to grepable and saves the output in the given filename. This is equivalent to using the --output-format grepable and --output-filename parameters. * `-oJ FILE`: sets the output format to JSON and saves the output in the given filename. This is equivalent to using the --output-format json and --output-filename parameters. * `-oL FILE`: sets the output format to a simple list format and saves the output in the given filename. This is equivalent to using the --output-format list and --output-filename parameters. * `--readscan FILE`: reads the files created by the `-oB` option from a scan, then outputs them in one of the other formats, depending on command-line parameters. In other words, it can take the binary version of the output and convert it to an XML or JSON format. When this option is given, defaults from `/etc/masscan/masscan.conf` will not be read. * `--connection-timeout SECS`: when doing banner checks, this specifies the maximum number of seconds that a TCP connection can be held open. The default is 30 seconds. Increase this time if banners are incomplete. For example, we have to increase the timeout when downloading all the SSL certs from the Internet, because some sites take that long to deliver all the certs in the chain. However, beware that when this is set to a large value, it'll consume a lot of memory on fast scans. While the code may handle millions of open TCP connections, you may not have enough memory for that. * `--hello-file[PORT] FILE`: send the contents of the file once the TCP connection has been established with the given port. Requires that `--banners` also be set. Heuristics will be performed on the reponse in an attempt to discover what protocol, so HTTP responses will be parsed differently than other protocols. * `--hello-string[PORT] BASE64`: same as `--hello-file` except that the contents of the BASE64 encoded string are decoded, then used as the hello string that greets the server. * `--capture TYPE` or `--nocapture TYPE`: when doing banners (`--banner`), this determines what to capture from the banners. By default, only the TITLE field from HTML documents is captured, to get the entire document, use `--capture html`. By default, the entire certificate from SSL is captured, to disable this, use `--nocapture cert`. Currently, only the values `html` and `cert` are currently supported for this option, but many more will be added in the future. ## CONFIGURATION FILE FORMAT The configuration file uses the same parameter names as on the commandline, but without the `--` prefix, and with an `=` sign between the name and the value. An example configuration file might be: # targets range = 10.0.0.0/8,192.168.0.0/16 range = 172.16.0.0/12 ports = 20-25,80,U:53 ping = true # adapter adapter = eth0 adapter-ip = 192.168.0.1 router-mac = 66-55-44-33-22-11 # other exclude-file = /etc/masscan/exludes.txt By default, the program will read default configuration from the file `/etc/masscan/masscan.conf`. This is useful for system-specific settings, such as the `--adapter-xxx` options. This is also useful for excluded IP addresses, so that you can scan the entire Internet, while skipping dangerous addresses, like those owned by the DoD, and not make an accidental mistake. ## CONTROL-C BEHAVIOR When the user presses , the scan will stop, and the current state of the scan will be saved in the file 'paused.conf'. The scan can be resumed with the `--resume` option: # masscan --resume paused.conf The program will not exit immediately, but will wait a default of 10 seconds to receive results from the Internet and save the results before exiting completely. This time can be changed with the `--wait` option. ## USER-MODE STACK Masscan has a user-mode TCP/IP stack that co-exists with the operating-system's stack. Normally, this works fine but sometimes can cause problems, especially with the `--banners` option that establishes a TCP/IP connection. In some cases, all the stack's parameters will have to be specified separately: --adapter-port PORT --adapter-ip IP --adapter-mac MAC --adapter-vlan VLANID --router-mac MAC If the user-mode stack shares the same IP address as the operating-system, then the kernel will send RST packets during a scan. This can cause unnecessary traffic during a simple port scan, and will terminate TCP connections when doing a `--banners` scan. To prevent, this, the built-in firewall should be used to filter the source ports. On Linux, this can be done by doing something like: # iptables -A INPUT -i eth0 -p tcp --dport 44444 -j DROP This will prevent the Linux kernel from processing incoming packets to port 44444, but `masscan` will still see the packets. Set the maching parameter of `--adapter-port 44444` to force `masscan` to use that port instead of a random port. ## SIMPLE EXAMPLES The following example scans all private networks for webservers, and prints all open ports that were found. # masscan 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 -p80 --open-only The following example scans the entire Internet for DNS servers, grabbing their versions, then saves the results in an XML file. # masscan 0.0.0.0/0 --excludefile no-dod.txt -pU:53 --banners --output-filename dns.xml You should be able to import the XML into databases and such. The following example reads a binary scan results file called bin-test.scan and prints results to console. # masscan --readscan bin-test.scan The following example reads a binary scan results file called bin-test.scan and creates an XML output file called bin-test.xml. # masscan --readscan bin-test.scan -oX bin-test.xml ## ADVANCED EXAMPLES Let's say that you want to scan the entire Internet and spread the scan across three machines. Masscan would be launched on all three machines using the following command-lines: # masscan 0.0.0.0/0 -p0-65535 --shard 1/3 # masscan 0.0.0.0/0 -p0-65535 --shard 2/3 # masscan 0.0.0.0/0 -p0-65535 --shard 3/3 An alternative is with the "resume" feature. A scan has an internal index that goes from zero to the number of ports times the number of IP addresses. The following example shows splitting up a scan into chunks of a 1000 items each: # masscan 0.0.0.0/0 -p0-65535 --resume-index 0 --resume-count 1000 # masscan 0.0.0.0/0 -p0-65535 --resume-index 1000 --resume-count 1000 # masscan 0.0.0.0/0 -p0-65535 --resume-index 2000 --resume-count 1000 # masscan 0.0.0.0/0 -p0-65535 --resume-index 3000 --resume-count 1000 A script can use this to split smaller tasks across many other machines, such as Amazon EC2 instances. As each instance completes a job, the script might send a request to a central coordinating server for more work. ## SPURIOUS RESETS When scanning TCP using the default IP address of your adapter, the built-in stack will generate RST packets. This will prevent banner grabbing. There are are two ways to solve this. The first way is to create a firewall rule to block that port from being seen by the stack. How this works is dependent on the operating system, but on Linux this looks something like: # iptables -A INPUT -p tcp -i eth0 --dport 61234 -j DROP Then, when scanning, that same port must be used as the source: # masscan 10.0.0.0/8 -p80 --banners --adapter-port 61234 An alternative is to "spoof" a different IP address. This IP address must be within the range of the local network, but must not otherwise be in use by either your own computer or another computer on the network. An example of this would look like: # masscan 10.0.0.0/8 -p80 --banners --adapter-ip 192.168.1.101 Setting your source IP address this way is the preferred way of running this scanner. ## ABUSE COMPLAINTS This scanner is designed for large-scale surveys, of either an organization, or of the Internet as a whole. This scanning will be noticed by those monitoring their logs, which will generate complaints. If you are scanning your own organization, this may lead to you being fired. Never scan outside your local subnet without getting permission from your boss, with a clear written declaration of why you are scanning. The same applies to scanning the Internet from your employer. This is another good way to get fired, as your IT department gets flooded with complaints as to why your organization is hacking them. When scanning on your own, such as your home Internet or ISP, this will likely cause them to cancel your account due to the abuse complaints. One solution is to work with your ISP, to be clear about precisely what we are doing, to prove to them that we are researching the Internet, not "hacking" it. We have our ISP send the abuse complaints directly to us. For anyone that asks, we add them to our "--excludefile", blacklisting them so that we won't scan them again. While interacting with such people, some instead add us to their whitelist, so that their firewalls won't log us anymore (they'll still block us, of course, they just won't log that fact to avoid filling up their logs with our scans). Ultimately, I don't know if it's possible to completely solve this problem. Despite the Internet being a public, end-to-end network, you are still "guilty until proven innocent" when you do a scan. ## COMPATIBILITY While not listed in this document, a lot of parameters compatible with `nmap` will also work. It runs on macOS, Linux, and Windows. It's compiled in fairly portable C language. It supports IPv4 and IPv6. ## SEE ALSO nmap(8), pcap(3) ## AUTHORS This tool was written by Robert Graham. The source code is available at https://github.com/robertdavidgraham/masscan. ================================================ FILE: src/crypto-base64.c ================================================ #include "crypto-base64.h" #include #include #include #include #include /***************************************************************************** *****************************************************************************/ size_t base64_encode(void *vdst, size_t sizeof_dst, const void *vsrc, size_t sizeof_src) { static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "+/"; size_t i = 0; size_t d = 0; unsigned char *dst = (unsigned char *)vdst; const unsigned char *src = (const unsigned char *)vsrc; /* encode every 3 bytes of source into 4 bytes of destination text */ while (i + 3 <= sizeof_src) { unsigned n; /* make sure there is enough space */ if (d + 4 > sizeof_dst) return d; /* convert the chars */ n = src[i]<<16 | src[i+1]<<8 | src[i+2]; dst[d+0] = b64[ (n>>18) & 0x3F ]; dst[d+1] = b64[ (n>>12) & 0x3F ]; dst[d+2] = b64[ (n>> 6) & 0x3F ]; dst[d+3] = b64[ (n>> 0) & 0x3F ]; i += 3; d += 4; } /* If the source text isn't an even multiple of 3 characters, then we'll * have to append a '=' or '==' to the output to compensate */ if (i + 2 <= sizeof_src && d + 4 <= sizeof_dst) { unsigned n = src[i]<<16 | src[i+1]<<8; dst[d+0] = b64[ (n>>18) & 0x3F ]; dst[d+1] = b64[ (n>>12) & 0x3F ]; dst[d+2] = b64[ (n>> 6) & 0x3F ]; dst[d+3] = '='; d += 4; } else if (i + 1 <= sizeof_src && d + 4 <= sizeof_dst) { unsigned n = src[i]<<16; dst[d+0] = b64[ (n>>18) & 0x3F ]; dst[d+1] = b64[ (n>>12) & 0x3F ]; dst[d+2] = '='; dst[d+3] = '='; d += 4; } return d; } /***************************************************************************** *****************************************************************************/ size_t base64_decode(void *vdst, size_t sizeof_dst, const void *vsrc, size_t sizeof_src) { static const unsigned char rstr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 62, 0xFF, 0xFF, 0xFF, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; size_t i = 0; size_t d = 0; unsigned char *dst = (unsigned char *)vdst; const unsigned char *src = (const unsigned char *)vsrc; while (i < sizeof_src) { unsigned b; unsigned c=0; /* byte#1 */ while (i 64) i++; if (src[i] == '=' || i++ >= sizeof_src) break; b = (c << 2) & 0xfc; while (i 64) i++; if (src[i] == '=' || i++ >= sizeof_src) break; b |= (c>>4) & 0x03; if (d=sizeof_src) break; /* byte#2 */ b = (c<<4) & 0xF0; while (i 64) ; if (src[i] == '=' || i++ >= sizeof_src) break; b |= (c>>2) & 0x0F; if (d=sizeof_src) break; /* byte#3*/ b = (c<<6) & 0xC0; while (i 64) ; if (src[i] == '=' || i++ >= sizeof_src) break; b |= c; if (d=sizeof_src) break; } if (d>16 & 0x7fff; } /***************************************************************************** *****************************************************************************/ int base64_selftest(void) { char buf[100]; char buf2[100]; char buf3[100]; size_t buf_len; size_t buf2_len; unsigned i; unsigned seed = (unsigned)time(0); buf_len = base64_encode(buf, sizeof(buf), "hello", 5); buf2_len = base64_decode(buf2, sizeof(buf2), buf, buf_len); if (buf2_len != 5 && memcmp(buf2, "hello", 5) != 0) { fprintf(stderr, "base64: selftest failed\n"); return 1; } /* * Generate a bunch of random strings, encode them, then decode them, * making sure the final result matches the original string */ for (i=0; i<100; i++) { unsigned j; size_t buf3_len; /* create a string of random bytes */ buf_len = r_rand(&seed) % 50; for (j=0; j size_t base64_decode(void *dst, size_t sizeof_dst, const void *src, size_t sizeof_src); size_t base64_encode(void *dst, size_t sizeof_dst, const void *src, size_t sizeof_src); int base64_selftest(void); #endif ================================================ FILE: src/crypto-blackrock.c ================================================ /* BlackRock cipher (h/t Marsh Ray @marshray for this idea) This is a randomization/reshuffling function based on a crypto "Feistel network" as described in the paper: 'Ciphers with Arbitrary Finite Domains' by John Black and Phillip Rogaway http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf This is a crypto-like construction that encrypts an arbitrary sized range. Given a number in the range [0..9999], it'll produce a mapping to a distinct different number in the same range (and back again). In other words, it randomizes the order of numbers in a sequence. For example, it can be used to randomize the sequence [0..9]: 0 -> 6 1 -> 4 2 -> 8 3 -> 1 4 -> 9 5 -> 3 6 -> 0 7 -> 5 8 -> 2 9 -> 7 As you can see on the right hand side, the numbers are in random order, and they don't repeat. This is create for port scanning. We can take an index variable and increment it during a scan, then use this function to randomize it, yet be assured that we've probed every IP and port within the range. The cryptographic strength of this construction depends upon the number of rounds, and the exact nature of the inner "READ()" function. Because it's a Feistel network, that "READ()" function can be almost anything. We don't care about cryptographic strength, just speed, so we are using a trivial READ() function. This is a class of "format-preserving encryption". There are probably better constructions than what I'm using. */ #include "crypto-blackrock.h" #include "pixie-timer.h" #include "util-malloc.h" #include #include #include #include #include #include #include #if defined(_MSC_VER) #define inline _inline #endif /*************************************************************************** * It's an s-box. You gotta have an s-box ***************************************************************************/ const unsigned char sbox[256] = { 0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88, 0x57, 0xdd, 0x8c, 0xf2, 0x29, 0x5a, 0x08, 0x9f, 0x49, 0x34, 0xce, 0x99, 0x9e, 0xbf, 0x0f, 0x81, 0xd4, 0x2f, 0x92, 0x3f, 0x95, 0xf5, 0x23, 0x00, 0x0d, 0x3e, 0xa8, 0x90, 0x98, 0xdd, 0x20, 0x00, 0x03, 0x69, 0x0a, 0xca, 0xba, 0x12, 0x08, 0x41, 0x6e, 0xb9, 0x86, 0xe4, 0x50, 0xf0, 0x84, 0xe2, 0xb3, 0xb3, 0xc8, 0xb5, 0xb2, 0x2d, 0x18, 0x70, 0x0a, 0xd7, 0x92, 0x90, 0x9e, 0x1e, 0x0c, 0x1f, 0x08, 0xe8, 0x06, 0xfd, 0x85, 0x2f, 0xaa, 0x5d, 0xcf, 0xf9, 0xe3, 0x55, 0xb9, 0xfe, 0xa6, 0x7f, 0x44, 0x3b, 0x4a, 0x4f, 0xc9, 0x2f, 0xd2, 0xd3, 0x8e, 0xdc, 0xae, 0xba, 0x4f, 0x02, 0xb4, 0x76, 0xba, 0x64, 0x2d, 0x07, 0x9e, 0x08, 0xec, 0xbd, 0x52, 0x29, 0x07, 0xbb, 0x9f, 0xb5, 0x58, 0x6f, 0x07, 0x55, 0xb0, 0x34, 0x74, 0x9f, 0x05, 0xb2, 0xdf, 0xa9, 0xc6, 0x2a, 0xa3, 0x5d, 0xff, 0x10, 0x40, 0xb3, 0xb7, 0xb4, 0x63, 0x6e, 0xf4, 0x3e, 0xee, 0xf6, 0x49, 0x52, 0xe3, 0x11, 0xb3, 0xf1, 0xfb, 0x60, 0x48, 0xa1, 0xa4, 0x19, 0x7a, 0x2e, 0x90, 0x28, 0x90, 0x8d, 0x5e, 0x8c, 0x8c, 0xc4, 0xf2, 0x4a, 0xf6, 0xb2, 0x19, 0x83, 0xea, 0xed, 0x6d, 0xba, 0xfe, 0xd8, 0xb6, 0xa3, 0x5a, 0xb4, 0x48, 0xfa, 0xbe, 0x5c, 0x69, 0xac, 0x3c, 0x8f, 0x63, 0xaf, 0xa4, 0x42, 0x25, 0x50, 0xab, 0x65, 0x80, 0x65, 0xb9, 0xfb, 0xc7, 0xf2, 0x2d, 0x5c, 0xe3, 0x4c, 0xa4, 0xa6, 0x8e, 0x07, 0x9c, 0xeb, 0x41, 0x93, 0x65, 0x44, 0x4a, 0x86, 0xc1, 0xf6, 0x2c, 0x97, 0xfd, 0xf4, 0x6c, 0xdc, 0xe1, 0xe0, 0x28, 0xd9, 0x89, 0x7b, 0x09, 0xe2, 0xa0, 0x38, 0x74, 0x4a, 0xa6, 0x5e, 0xd2, 0xe2, 0x4d, 0xf3, 0xf4, 0xc6, 0xbc, 0xa2, 0x51, 0x58, 0xe8, 0xae, }; /*************************************************************************** ***************************************************************************/ void blackrock_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds) { double foo = sqrt(range * 1.0); /* This algorithm gets very non-random at small numbers, so I'm going * to try to fix some constants here to make it work. It doesn't have * to be good, since it's kinda pointless having ranges this small */ switch (range) { case 0: br->a = 0; br->b = 0; break; case 1: br->a = 1; br->b = 1; break; case 2: br->a = 1; br->b = 2; break; case 3: br->a = 2; br->b = 2; break; case 4: case 5: case 6: br->a = 2; br->b = 3; break; case 7: case 8: br->a = 3; br->b = 3; break; default: br->range = range; br->a = (uint64_t)(foo - 2); br->b = (uint64_t)(foo + 3); break; } while (br->a * br->b <= range) br->b++; br->rounds = rounds; br->seed = seed; br->range = range; } /*************************************************************************** * The inner round/mixer function. In DES, it's a series of S-box lookups, * which ***************************************************************************/ static inline uint64_t READ(uint64_t r, uint64_t R, uint64_t seed) { uint64_t r0, r1, r2, r3; #define GETBYTE(R,n) ((((R)>>(n*8))^seed^r)&0xFF) R ^= (seed << r) ^ (seed >> (64 - r)); r0 = sbox[GETBYTE(R,0)]<< 0 | sbox[GETBYTE(R,1)]<< 8; r1 = (sbox[GETBYTE(R,2)]<<16UL | sbox[GETBYTE(R,3)]<<24UL)&0x0ffffFFFFUL; r2 = sbox[GETBYTE(R,4)]<< 0 | sbox[GETBYTE(R,5)]<< 8; r3 = (sbox[GETBYTE(R,6)]<<16UL | sbox[GETBYTE(R,7)]<<24UL)&0x0ffffFFFFUL; R = r0 ^ r1 ^ r2<<23UL ^ r3<<33UL; return R; } /*************************************************************************** * * NOTE: * the names in this function are cryptic in order to match as closely * as possible the pseudocode in the following paper: * http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf * Read that paper in order to understand this code. ***************************************************************************/ static inline uint64_t ENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed) { uint64_t L, R; unsigned j; uint64_t tmp; L = m % a; R = m / a; for (j=1; j<=r; j++) { if (j & 1) { tmp = (L + READ(j, R, seed)) % a; } else { tmp = (L + READ(j, R, seed)) % b; } L = R; R = tmp; } if (r & 1) { return a * L + R; } else { return a * R + L; } } /*************************************************************************** ***************************************************************************/ static inline uint64_t UNENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed) { uint64_t L, R; unsigned j; uint64_t tmp; if (r & 1) { R = m % a; L = m / a; } else { L = m % a; R = m / a; } for (j=r; j>=1; j--) { if (j & 1) { tmp = READ(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = a - (tmp%a); if (tmp == a) tmp = 0; } else { tmp = (R - tmp); tmp %= a; } } else { tmp = READ(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = b - (tmp%b); if (tmp == b) tmp = 0; } else { tmp = (R - tmp); tmp %= b; } } R = L; L = tmp; } return a * R + L; } /*************************************************************************** ***************************************************************************/ uint64_t blackrock_shuffle(const struct BlackRock *br, uint64_t m) { uint64_t c; c = ENCRYPT(br->rounds, br->a, br->b, m, br->seed); while (c >= br->range) c = ENCRYPT(br->rounds, br->a, br->b, c, br->seed); return c; } /*************************************************************************** ***************************************************************************/ uint64_t blackrock_unshuffle(const struct BlackRock *br, uint64_t m) { uint64_t c; c = UNENCRYPT(br->rounds, br->a, br->b, m, br->seed); while (c >= br->range) c = UNENCRYPT(br->rounds, br->a, br->b, c, br->seed); return c; } /*************************************************************************** * This function called only during selftest/regression-test. ***************************************************************************/ static unsigned blackrock_verify(struct BlackRock *br, uint64_t max) { unsigned char *list; uint64_t i; unsigned is_success = 1; uint64_t range = br->range; /* Allocate a list of 1-byte counters */ list = CALLOC(1, (size_t)((range struct BlackRock { uint64_t range; uint64_t a; uint64_t b; uint64_t seed; unsigned rounds; uint64_t a_bits; uint64_t a_mask; uint64_t b_bits; uint64_t b_mask; }; /** * Initializes a structure for shuffling numbers within * a range. * * @param range * The size of the range of numbers needing to be * shuffled/randomized. */ void blackrock_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds); void blackrock2_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds); /** * Given a number within a range, produce a different number with * the same range. There is a 1-to-1 mapping between the two, * so when linearly incrementing through the range, the output * of this function won't repeat. In other words, encrypt the index variable. * @param br * The randomization parameters created with 'blackrock_init()' * @param index * An input within the specified range. We call it an 'index' variable * because that's how we intend to use this function, shuffling a * monotonically increasing index variable, but in truth, any sort * of integer can be used. This must be within the 'range' specified * during the call to blackrock_init(), or the results are undefined. * @return * A one-to-one matching index that's in the same range. */ uint64_t blackrock_shuffle(const struct BlackRock *br, uint64_t index); uint64_t blackrock2_shuffle(const struct BlackRock *br, uint64_t index); /** * The reverse of the shuffle function above: given the shuffled/encrypted * integer, return the original index value before the shuffling/encryption. */ uint64_t blackrock_unshuffle(const struct BlackRock *br, uint64_t m); uint64_t blackrock2_unshuffle(const struct BlackRock *br, uint64_t m); /** * Do a regression test. * @return * 0 of the regression test succeeds or non-zero if it fails */ int blackrock_selftest(void); int blackrock2_selftest(void); /** * Do a benchmark of this module regression test. */ void blackrock_benchmark(unsigned rounds); void blackrock2_benchmark(unsigned rounds); #endif ================================================ FILE: src/crypto-blackrock2.c ================================================ #include "crypto-blackrock.h" #include "pixie-timer.h" #include "unusedparm.h" #include "util-malloc.h" #include "util-safefunc.h" #include #include #include #include #include #include #include #if defined(_MSC_VER) #define inline _inline #endif /* * Expanded DES S-boxes */ static const uint32_t SB1[64] = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004 }; static const uint32_t SB2[64] = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000 }; static const uint32_t SB3[64] = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200 }; static const uint32_t SB4[64] = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080 }; static const uint32_t SB5[64] = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100 }; static const uint32_t SB6[64] = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010 }; static const uint32_t SB7[64] = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002 }; static const uint32_t SB8[64] = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000 }; /*************************************************************************** * It's an s-box. You gotta have an s-box ***************************************************************************/ const unsigned char sbox2[] = { 0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88, 0x57, 0xdd, 0x8c, 0xf2, 0x29, 0x5a, 0x08, 0x9f, 0x49, 0x34, 0xce, 0x99, 0x9e, 0xbf, 0x0f, 0x81, 0xd4, 0x2f, 0x92, 0x3f, 0x95, 0xf5, 0x23, 0x00, 0x0d, 0x3e, 0xa8, 0x90, 0x98, 0xdd, 0x20, 0x00, 0x03, 0x69, 0x0a, 0xca, 0xba, 0x12, 0x08, 0x41, 0x6e, 0xb9, 0x86, 0xe4, 0x50, 0xf0, 0x84, 0xe2, 0xb3, 0xb3, 0xc8, 0xb5, 0xb2, 0x2d, 0x18, 0x70, 0x0a, 0xd7, 0x92, 0x90, 0x9e, 0x1e, 0x0c, 0x1f, 0x08, 0xe8, 0x06, 0xfd, 0x85, 0x2f, 0xaa, 0x5d, 0xcf, 0xf9, 0xe3, 0x55, 0xb9, 0xfe, 0xa6, 0x7f, 0x44, 0x3b, 0x4a, 0x4f, 0xc9, 0x2f, 0xd2, 0xd3, 0x8e, 0xdc, 0xae, 0xba, 0x4f, 0x02, 0xb4, 0x76, 0xba, 0x64, 0x2d, 0x07, 0x9e, 0x08, 0xec, 0xbd, 0x52, 0x29, 0x07, 0xbb, 0x9f, 0xb5, 0x58, 0x6f, 0x07, 0x55, 0xb0, 0x34, 0x74, 0x9f, 0x05, 0xb2, 0xdf, 0xa9, 0xc6, 0x2a, 0xa3, 0x5d, 0xff, 0x10, 0x40, 0xb3, 0xb7, 0xb4, 0x63, 0x6e, 0xf4, 0x3e, 0xee, 0xf6, 0x49, 0x52, 0xe3, 0x11, 0xb3, 0xf1, 0xfb, 0x60, 0x48, 0xa1, 0xa4, 0x19, 0x7a, 0x2e, 0x90, 0x28, 0x90, 0x8d, 0x5e, 0x8c, 0x8c, 0xc4, 0xf2, 0x4a, 0xf6, 0xb2, 0x19, 0x83, 0xea, 0xed, 0x6d, 0xba, 0xfe, 0xd8, 0xb6, 0xa3, 0x5a, 0xb4, 0x48, 0xfa, 0xbe, 0x5c, 0x69, 0xac, 0x3c, 0x8f, 0x63, 0xaf, 0xa4, 0x42, 0x25, 0x50, 0xab, 0x65, 0x80, 0x65, 0xb9, 0xfb, 0xc7, 0xf2, 0x2d, 0x5c, 0xe3, 0x4c, 0xa4, 0xa6, 0x8e, 0x07, 0x9c, 0xeb, 0x41, 0x93, 0x65, 0x44, 0x4a, 0x86, 0xc1, 0xf6, 0x2c, 0x97, 0xfd, 0xf4, 0x6c, 0xdc, 0xe1, 0xe0, 0x28, 0xd9, 0x89, 0x7b, 0x09, 0xe2, 0xa0, 0x38, 0x74, 0x4a, 0xa6, 0x5e, 0xd2, 0xe2, 0x4d, 0xf3, 0xf4, 0xc6, 0xbc, 0xa2, 0x51, 0x58, 0xe8, 0xae, 0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88, }; /**************************************************************************** * Given a number, figure out the nearest power-of-two (16,32,64,128,etc.) * that can hold that number. We do this so that we can convert multiplies * into shifts. ****************************************************************************/ static uint64_t next_power_of_two(uint64_t num) { uint64_t power_of_two = 1; num++; while ((uint64_t)(1ULL << power_of_two) < num) power_of_two++; return (1ULL << power_of_two); } static uint64_t bit_count(uint64_t num) { uint64_t bits = 0; while ((num >> bits) > 1) bits++; return bits; } /*************************************************************************** ***************************************************************************/ void blackrock2_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds) { uint64_t a; uint64_t b; a = next_power_of_two( (uint64_t)sqrt(range * 1.0) ); b = next_power_of_two(range/a); //printf("a=%llu b=%llu seed = 0x%llu\n", a, b, seed); br->range = range; br->a = a; br->a_bits = bit_count(br->a); br->a_mask = br->a - 1ULL; br->b = b; br->b_bits = bit_count(br->b); br->b_mask = br->b - 1ULL; //printf("a: 0x%llx / %llu\n", br->a_mask, br->a_bits); //printf("b: 0x%llx / %llu\n", br->b_mask, br->b_bits); br->rounds = rounds; br->seed = seed; br->range = range; } /*************************************************************************** * The inner round/mixer function. In DES, it's a series of S-box lookups, * which ***************************************************************************/ static inline uint64_t ROUND(uint64_t r, uint64_t R, uint64_t seed) { #define GETBYTE(R,n) ((uint64_t)(((((R)>>(n*8ULL)))&0xFFULL))) #if 0 uint64_t r0, r1, r2, r3; #endif uint64_t T, Y; T = R ^ ((seed>>r) | (seed<<(64-r))); if (r & 1) { Y = SB8[ (T ) & 0x3F ] ^ \ SB6[ (T >> 8) & 0x3F ] ^ \ SB4[ (T >> 16) & 0x3F ] ^ \ SB2[ (T >> 24) & 0x3F ]; \ } else { Y = SB7[ (T ) & 0x3F ] ^ \ SB5[ (T >> 8) & 0x3F ] ^ \ SB3[ (T >> 16) & 0x3F ] ^ \ SB1[ (T >> 24) & 0x3F ]; } return Y; #if 0 r0 = sbox2[GETBYTE(R,0)]<< 6 | sbox2[GETBYTE(R,1)]<< 0; r1 = sbox2[GETBYTE(R,2)]<< 6 | sbox2[GETBYTE(R,5)]<< 0; r2 = sbox2[GETBYTE(R,4)]<< 6 | sbox2[GETBYTE(R,5)]<< 0; r3 = sbox2[GETBYTE(R,6)]<< 6 | sbox2[GETBYTE(R,7)]<< 0; R = r0 ^ (r1<<12) * (r2 << 24) ^ (r3 << 36) * r; return R; /*return((uint64_t)sbox2[GETBYTE(R,7ULL)]<< 0ULL) | ((uint64_t)sbox2[GETBYTE(R,6ULL)]<< 8ULL) | ((uint64_t)sbox2[GETBYTE(R,5ULL)]<<16ULL) | ((uint64_t)sbox2[GETBYTE(R,4ULL)]<<24ULL) | ((uint64_t)sbox2[GETBYTE(R,3ULL)]<<32ULL) | ((uint64_t)sbox2[GETBYTE(R,2ULL)]<<40ULL) | ((uint64_t)sbox2[GETBYTE(R,1ULL)]<<48ULL) | ((uint64_t)sbox2[GETBYTE(R,0ULL)]<<56ULL) ;*/ return R; #endif } /*************************************************************************** ***************************************************************************/ static inline uint64_t ENCRYPT(unsigned r, uint64_t a_bits, uint64_t a_mask, uint64_t b_bits, uint64_t b_mask, uint64_t m, uint64_t seed) { uint64_t L, R; unsigned j = 1; uint64_t tmp; UNUSEDPARM(b_bits); L = m & a_mask; R = m >> a_bits; for (j=1; j<=r; j++) { tmp = (L + ROUND(j, R, seed)) & a_mask; L = R; R = tmp; j++; tmp = (L + ROUND(j, R, seed)) & b_mask; L = R; R = tmp; } if ((j-1) & 1) { return (L << (a_bits)) + R; } else { return (R << (a_bits)) + L; } } static inline uint64_t DECRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed) { uint64_t L, R; unsigned j; uint64_t tmp; if (r & 1) { R = m % a; L = m / a; } else { L = m % a; R = m / a; } for (j=r; j>=1; j--) { if (j & 1) { tmp = ROUND(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = a - (tmp%a); if (tmp == a) tmp = 0; } else { tmp = (R - tmp); tmp %= a; } } else { tmp = ROUND(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = b - (tmp%b); if (tmp == b) tmp = 0; } else { tmp = (R - tmp); tmp %= b; } } R = L; L = tmp; } return a * R + L; } /*************************************************************************** ***************************************************************************/ uint64_t blackrock2_shuffle(const struct BlackRock *br, uint64_t m) { uint64_t c; c = ENCRYPT(br->rounds, br->a_bits, br->a_mask, br->b_bits, br->b_mask, m, br->seed); while (c >= br->range) c = ENCRYPT(br->rounds, br->a_bits, br->a_mask, br->b_bits, br->b_mask, c, br->seed); return c; } /*************************************************************************** ***************************************************************************/ uint64_t blackrock2_unshuffle(const struct BlackRock *br, uint64_t m) { uint64_t c; c = DECRYPT(br->rounds, br->a, br->b, m, br->seed); while (c >= br->range) c = DECRYPT(br->rounds, br->a, br->b, c, br->seed); return c; } /*************************************************************************** * This function called only during selftest/regression-test. ***************************************************************************/ static unsigned verify(struct BlackRock *br, uint64_t max) { unsigned char *list; uint64_t i; unsigned is_success = 1; uint64_t range = br->range; /* Allocate a list of 1-byte counters */ list = CALLOC(1, (size_t)((range /* for 'sqrt()', may need -lm for gcc */ #include #include #include #include #include /** * A 64 bit number can't have more than 16 prime factors. The first factors * are: * 2*3*5*7*11*13*17*19*23*29*31*37*41*43*47*53 = 0xC443F2F861D29C3A * 0123456789abcdef * We zero termiante this list, so we are going to reserve 20 slots. */ typedef uint64_t PRIMEFACTORS[20]; /**************************************************************************** * Break down the number into prime factors using DJB's sieve code, which * is about 5 to 10 times faster than the Sieve of Eratosthenes. * * @param number * The integer that we are factoring. It can be any value up to 64 bits * in size. * @param factors * The list of all the prime factors, zero terminated. * @param non_factors * A list of smallest numbers that aren't prime factors. We return * this because we are going to use prime non-factors for finding * interesting numbers. ****************************************************************************/ static unsigned sieve_prime_factors(uint64_t number, PRIMEFACTORS factors, PRIMEFACTORS non_factors, double *elapsed) { primegen pg; clock_t start; clock_t stop; uint64_t prime; uint64_t max; unsigned factor_count = 0; unsigned non_factor_count = 0; /* * We only need to sieve up to the square-root of the target number. Only * one prime factor can be bigger than the square root, so once we find * all the other primes, the square root is the only one left. * Note: you have to link to the 'm' math library for some gcc platforms. */ max = (uint64_t)sqrt(number + 1.0); /* * Init the DJB primegen library. */ primegen_init(&pg); /* * Enumerate all the primes starting with 2 */ start = clock(); for (;;) { /* Sieve the next prime */ prime = primegen_next(&pg); /* If we've reached the square root, then that's as far as we need * to go */ if (prime > max) break; /* If this prime is not a factor (evenly divisible with no remainder) * then loop back and get the next prime */ if ((number % prime) != 0) { if (non_factor_count < 12) non_factors[non_factor_count++] = prime; continue; } /* Else we've found a prime factor, so add this to the list of primes */ factors[factor_count++] = prime; /* At the end, we may have one prime factor left that's bigger than the * sqrt. Therefore, as we go along, divide the original number * (possibly several times) by the prime factor so that this large * remaining factor will be the only one left */ while ((number % prime) == 0) number /= prime; /* exit early if we've found all prime factors. comment out this * code if you want to benchmark it */ if (number == 1 && non_factor_count > 10) break; } /* * See if there is one last prime that's bigger than the square root. * Note: This is the only number that can be larger than 32-bits in the * way this code is written. */ if (number != 1) factors[factor_count++] = number; /* * Zero terminate the results. */ factors[factor_count] = 0; non_factors[non_factor_count] = 0; /* * Since prime factorization takes a long time, especially on slow * CPUs, we benchmark it to keep track of performance. */ stop = clock(); if (elapsed) *elapsed = ((double)stop - (double)start)/(double)CLOCKS_PER_SEC; /* should always be at least 1, because if the number itself is prime, * then that's it's only prime factor */ return factor_count; } /**************************************************************************** * Do a pseudo-random 1-to-1 translation of a number within a range to * another number in that range. * * The constants 'a' and 'c' must be chosen to match the LCG algorithm * to fit 'm' (range). * * This the same as the function 'rand()', except all the constants and * seeds are specified as parameters. * * @param index * The index within the range that we are randomizing. * @param a * The 'multiplier' of the LCG algorithm. * @param c * The 'increment' of the LCG algorithm. * @param range * The 'modulus' of the LCG algorithm. ****************************************************************************/ uint64_t lcg_rand(uint64_t index, uint64_t a, uint64_t c, uint64_t range) { return (index * a + c) % range; } /**************************************************************************** * Verify the LCG algorithm. You shouldn't do this for large ranges, * because we'll run out of memory. Therefore, this algorithm allocates * a buffer only up to a smaller range. We still have to traverse the * entire range of numbers, but we only need store values for a smaller * range. If 10% of the range checks out, then there's a good chance * it applies to the other 90% as well. * * This works by counting the results of rand(), which should be produced * exactly once. ****************************************************************************/ static unsigned lcg_verify(uint64_t a, uint64_t c, uint64_t range, uint64_t max) { unsigned char *list; uint64_t i; unsigned is_success = 1; /* Allocate a list of 1-byte counters */ list = CALLOC(1, (size_t)((range= 70) { count = 0; printf("\n"); } } printf("\n"); } } *out_a = a; *inout_c = c; } /*************************************************************************** ***************************************************************************/ int lcg_selftest(void) { unsigned i; int is_success = 0; uint64_t m, a, c; m = 3015 * 3; for (i=0; i<5; i++) { a = 0; c = 0; m += 10 + i; lcg_calculate_constants(m, &a, &c, 0); is_success = lcg_verify(a, c, m, m); if (!is_success) { fprintf(stderr, "LCG: randomization failed\n"); return 1; /*fail*/ } } return 0; /*success*/ } ================================================ FILE: src/crypto-lcg.h ================================================ #ifndef RAND_LCG_H #define RAND_LCG_H #include void lcg_calculate_constants(uint64_t m, uint64_t *out_a, uint64_t *inout_c, int is_debug); uint64_t lcg_rand(uint64_t index, uint64_t a, uint64_t c, uint64_t range); /** * Performs a regression test on this module. * @return * 0 on success, or a positive integer on failure */ int lcg_selftest(void); #endif ================================================ FILE: src/crypto-primegen.c ================================================ /* This is DJB's code for calculating primes, with a few modifications, such as making it work with Microsoft's compiler on Windows, and getting rid of warnings. */ #include "crypto-primegen.h" /* B is 32 times X. Total memory use for one generator is 2B bytes = 64X bytes. Covers primes in an interval of length 1920X. Working set size for one generator is B bits = 4X bytes. Speedup by a factor of 2 or 3 for L1 cache instead of L2 cache. Slowdown by a factor of roughly n for primes past (nB)^2. Possible choices of X: 2002 to fit inside an 8K L1 cache (e.g., Pentium). 4004 to fit inside a 16K L1 cache (e.g., Pentium II). 64064 to fit inside a 256K L2 cache. There are various word-size limits on X; 1000000 should still be okay. */ #define B32 PRIMEGEN_WORDS #define B (PRIMEGEN_WORDS * 32) #ifdef _MSC_VER #pragma warning(disable:4244) #endif static const uint32_t two[32] = { 0x00000001 , 0x00000002 , 0x00000004 , 0x00000008 , 0x00000010 , 0x00000020 , 0x00000040 , 0x00000080 , 0x00000100 , 0x00000200 , 0x00000400 , 0x00000800 , 0x00001000 , 0x00002000 , 0x00004000 , 0x00008000 , 0x00010000 , 0x00020000 , 0x00040000 , 0x00080000 , 0x00100000 , 0x00200000 , 0x00400000 , 0x00800000 , 0x01000000 , 0x02000000 , 0x04000000 , 0x08000000 , 0x10000000 , 0x20000000 , 0x40000000 , 0x80000000 } ; static void clear(register uint32_t (*buf)[B32]) { register int i; register int j; for (j = 0;j < 16;++j) { for (i = 0;i < B32;++i) (*buf)[i] = (uint32_t)~0; ++buf; } } static void doit4(register uint32_t *a,register long x,register long y,int64_t start) { long i0; long y0; register long i; register uint32_t data; register uint32_t pos; register uint32_t bits; x += x; x += 15; y += 15; start += 1000000000; while (start < 0) { start += x; x += 30; } start -= 1000000000; i = start; while (i < B) { i += x; x += 30; } for (;;) { x -= 30; if (x <= 15) return; i -= x; while (i < 0) { i += y; y += 30; } i0 = i; y0 = y; while (i < B) { pos = (uint32_t)i; data = (uint32_t)i; pos >>= 5; data &= 31; i += y; y += 30; bits = a[pos]; data = two[data]; bits ^= data; a[pos] = bits; } i = i0; y = y0; } } static void doit6(register uint32_t *a,register long x,register long y,int64_t start) { long i0; long y0; register long i; register uint32_t data; register uint32_t pos; register uint32_t bits; x += 5; y += 15; start += 1000000000; while (start < 0) { start += x; x += 10; } start -= 1000000000; i = start; while (i < B) { i += x; x += 10; } for (;;) { x -= 10; if (x <= 5) return; i -= x; while (i < 0) { i += y; y += 30; } i0 = i; y0 = y; while (i < B) { pos = (uint32_t)i; data = (uint32_t)i; pos >>= 5; data &= 31; i += y; y += 30; bits = a[pos]; data = two[data]; bits ^= data; a[pos] = bits; } i = i0; y = y0; } } static void doit12(register uint32_t *a,register long x,register long y,int64_t start) { register long i; register uint32_t data; register uint32_t bits; x += 5; start += 1000000000; while (start < 0) { start += x; x += 10; } start -= 1000000000; i = start; while (i < 0) { i += x; x += 10; } y += 15; x += 10; for (;;) { long i0; long y0; while (i >= B) { if (x <= y) return; i -= y; y += 30; } i0 = i; y0 = y; while ((i >= 0) && (y < x)) { register uint32_t pos; pos = (uint32_t)i; data = (uint32_t)i; pos >>= 5; data &= 31; i -= y; y += 30; bits = a[pos]; data = two[data]; bits ^= data; a[pos] = bits; } i = i0; y = y0; i += x - 10; x += 10; } } static const int deltainverse[60] = { -1,B32 * 0,-1,-1,-1,-1,-1,B32 * 1,-1,-1,-1,B32 * 2,-1,B32 * 3,-1 ,-1,-1,B32 * 4,-1,B32 * 5,-1,-1,-1,B32 * 6,-1,-1,-1,-1,-1,B32 * 7 ,-1,B32 * 8,-1,-1,-1,-1,-1,B32 * 9,-1,-1,-1,B32 * 10,-1,B32 * 11,-1 ,-1,-1,B32 * 12,-1,B32 * 13,-1,-1,-1,B32 * 14,-1,-1,-1,-1,-1,B32 * 15 } ; static void squarefree1big(uint32_t (*buf)[B32],uint64_t base,uint32_t q,uint64_t qq) { uint64_t i; uint32_t pos; int n; uint64_t bound = base + 60 * B; while (qq < bound) { if (bound < 2000000000) i = qq - (((uint32_t) base) % ((uint32_t) qq)); else i = qq - (base % qq); if (!(i & 1)) i += qq; if (i < B * 60) { pos = (uint32_t)i; n = deltainverse[pos % 60]; if (n >= 0) { pos /= 60; (*buf)[n + (pos >> 5)] |= two[pos & 31]; } } qq += q; q += 1800; } } static void squarefree1(register uint32_t (*buf)[B32],uint64_t L,uint32_t q) { uint32_t qq; register uint32_t qqhigh; uint32_t i; register uint32_t ilow; register uint32_t ihigh; register int n; uint64_t base; base = 60 * L; qq = q * q; q = 60 * q + 900; while (qq < B * 60) { if (base < 2000000000) i = qq - (((uint32_t) base) % qq); else i = qq - (base % qq); if (!(i & 1)) i += qq; if (i < B * 60) { qqhigh = qq / 60; ilow = i % 60; ihigh = i / 60; qqhigh += qqhigh; while (ihigh < B) { n = deltainverse[ilow]; if (n >= 0) (*buf)[n + (ihigh >> 5)] |= two[ihigh & 31]; ilow += 2; ihigh += qqhigh; if (ilow >= 60) { ilow -= 60; ihigh += 1; } } } qq += q; q += 1800; } squarefree1big(buf,base,q,qq); } static void squarefree49big(uint32_t (*buf)[B32],uint64_t base,uint32_t q,uint64_t qq) { uint64_t i; uint32_t pos; int n; uint64_t bound = base + 60 * B; while (qq < bound) { if (bound < 2000000000) i = qq - (((uint32_t) base) % ((uint32_t) qq)); else i = qq - (base % qq); if (!(i & 1)) i += qq; if (i < B * 60) { pos = (uint32_t)i; n = deltainverse[pos % 60]; if (n >= 0) { pos /= 60; (*buf)[n + (pos >> 5)] |= two[pos & 31]; } } qq += q; q += 1800; } } static void squarefree49(register uint32_t (*buf)[B32],uint64_t L,uint32_t q) { uint32_t qq; register uint32_t qqhigh; uint32_t i; register uint32_t ilow; register uint32_t ihigh; register int n; uint64_t base = 60 * L; qq = q * q; q = 60 * q + 900; while (qq < B * 60) { if (base < 2000000000) i = qq - (((uint32_t) base) % qq); else i = qq - (base % qq); if (!(i & 1)) i += qq; if (i < B * 60) { qqhigh = qq / 60; ilow = i % 60; ihigh = i / 60; qqhigh += qqhigh; qqhigh += 1; while (ihigh < B) { n = deltainverse[ilow]; if (n >= 0) (*buf)[n + (ihigh >> 5)] |= two[ihigh & 31]; ilow += 38; ihigh += qqhigh; if (ilow >= 60) { ilow -= 60; ihigh += 1; } } } qq += q; q += 1800; } squarefree49big(buf,base,q,qq); } /* squares of primes >= 7, < 240 */ uint32_t qqtab[49] = { 49,121,169,289,361,529,841,961,1369,1681 ,1849,2209,2809,3481,3721,4489,5041,5329,6241,6889 ,7921,9409,10201,10609,11449,11881,12769,16129,17161,18769 ,19321,22201,22801,24649,26569,27889,29929,32041,32761,36481 ,37249,38809,39601,44521,49729,51529,52441,54289,57121 } ; /* (qq * 11 + 1) / 60 or (qq * 59 + 1) / 60 */ uint32_t qq60tab[49] = { 9,119,31,53,355,97,827,945,251,1653 ,339,405,515,3423,3659,823,4957,977,6137 ,1263,7789,1725,10031,1945,2099,11683,2341,2957 ,16875,3441,18999,21831,22421,4519,4871,5113,5487 ,31507,32215,35873,6829,7115,38941,43779,9117,9447,51567,9953,56169 } ; static void squarefreetiny(register uint32_t *a,uint32_t *Lmodqq,int d) { int j; for (j = 0;j < 49;++j) { register uint32_t k; register uint32_t qq; qq = qqtab[j]; k = qq - 1 - ((Lmodqq[j] + qq60tab[j] * d - 1) % qq); while (k < B) { register uint32_t pos; register uint32_t data; register uint32_t bits; pos = k; data = k; pos >>= 5; data &= 31; k += qq; bits = a[pos]; data = two[data]; bits |= data; a[pos] = bits; } } } typedef struct { char index; char f; char g; char k; } todo; static const todo for4[] = { {0,2,15,4} , {0,3,5,1} , {0,3,25,11} , {0,5,9,3} , {0,5,21,9} , {0,7,15,7} , {0,8,15,8} , {0,10,9,8} , {0,10,21,14} , {0,12,5,10} , {0,12,25,20} , {0,13,15,15} , {0,15,1,15} , {0,15,11,17} , {0,15,19,21} , {0,15,29,29} , {3,1,3,0} , {3,1,27,12} , {3,4,3,1} , {3,4,27,13} , {3,6,7,3} , {3,6,13,5} , {3,6,17,7} , {3,6,23,11} , {3,9,7,6} , {3,9,13,8} , {3,9,17,10} , {3,9,23,14} , {3,11,3,8} , {3,11,27,20} , {3,14,3,13} , {3,14,27,25} , {4,2,1,0} , {4,2,11,2} , {4,2,19,6} , {4,2,29,14} , {4,7,1,3} , {4,7,11,5} , {4,7,19,9} , {4,7,29,17} , {4,8,1,4} , {4,8,11,6} , {4,8,19,10} , {4,8,29,18} , {4,13,1,11} , {4,13,11,13} , {4,13,19,17} , {4,13,29,25} , {7,1,5,0} , {7,1,25,10} , {7,4,5,1} , {7,4,25,11} , {7,5,7,2} , {7,5,13,4} , {7,5,17,6} , {7,5,23,10} , {7,10,7,7} , {7,10,13,9} , {7,10,17,11} , {7,10,23,15} , {7,11,5,8} , {7,11,25,18} , {7,14,5,13} , {7,14,25,23} , {9,2,9,1} , {9,2,21,7} , {9,3,1,0} , {9,3,11,2} , {9,3,19,6} , {9,3,29,14} , {9,7,9,4} , {9,7,21,10} , {9,8,9,5} , {9,8,21,11} , {9,12,1,9} , {9,12,11,11} , {9,12,19,15} , {9,12,29,23} , {9,13,9,12} , {9,13,21,18} , {10,2,5,0} , {10,2,25,10} , {10,5,1,1} , {10,5,11,3} , {10,5,19,7} , {10,5,29,15} , {10,7,5,3} , {10,7,25,13} , {10,8,5,4} , {10,8,25,14} , {10,10,1,6} , {10,10,11,8} , {10,10,19,12} , {10,10,29,20} , {10,13,5,11} , {10,13,25,21} , {13,1,15,3} , {13,4,15,4} , {13,5,3,1} , {13,5,27,13} , {13,6,5,2} , {13,6,25,12} , {13,9,5,5} , {13,9,25,15} , {13,10,3,6} , {13,10,27,18} , {13,11,15,11} , {13,14,15,16} , {13,15,7,15} , {13,15,13,17} , {13,15,17,19} , {13,15,23,23} , {14,1,7,0} , {14,1,13,2} , {14,1,17,4} , {14,1,23,8} , {14,4,7,1} , {14,4,13,3} , {14,4,17,5} , {14,4,23,9} , {14,11,7,8} , {14,11,13,10} , {14,11,17,12} , {14,11,23,16} , {14,14,7,13} , {14,14,13,15} , {14,14,17,17} , {14,14,23,21} } ; static const todo for6[] = { {1,1,2,0} , {1,1,8,1} , {1,1,22,8} , {1,1,28,13} , {1,3,10,2} , {1,3,20,7} , {1,7,10,4} , {1,7,20,9} , {1,9,2,4} , {1,9,8,5} , {1,9,22,12} , {1,9,28,17} , {5,1,4,0} , {5,1,14,3} , {5,1,16,4} , {5,1,26,11} , {5,5,2,1} , {5,5,8,2} , {5,5,22,9} , {5,5,28,14} , {5,9,4,4} , {5,9,14,7} , {5,9,16,8} , {5,9,26,15} , {8,3,2,0} , {8,3,8,1} , {8,3,22,8} , {8,3,28,13} , {8,5,4,1} , {8,5,14,4} , {8,5,16,5} , {8,5,26,12} , {8,7,2,2} , {8,7,8,3} , {8,7,22,10} , {8,7,28,15} , {11,1,10,1} , {11,1,20,6} , {11,3,4,0} , {11,3,14,3} , {11,3,16,4} , {11,3,26,11} , {11,7,4,2} , {11,7,14,5} , {11,7,16,6} , {11,7,26,13} , {11,9,10,5} , {11,9,20,10} } ; static const todo for12[] = { {2,2,1,0} , {2,2,11,-2} , {2,2,19,-6} , {2,2,29,-14} , {2,3,4,0} , {2,3,14,-3} , {2,3,16,-4} , {2,3,26,-11} , {2,5,2,1} , {2,5,8,0} , {2,5,22,-7} , {2,5,28,-12} , {2,7,4,2} , {2,7,14,-1} , {2,7,16,-2} , {2,7,26,-9} , {2,8,1,3} , {2,8,11,1} , {2,8,19,-3} , {2,8,29,-11} , {2,10,7,4} , {2,10,13,2} , {2,10,17,0} , {2,10,23,-4} , {6,1,10,-2} , {6,1,20,-7} , {6,2,7,-1} , {6,2,13,-3} , {6,2,17,-5} , {6,2,23,-9} , {6,3,2,0} , {6,3,8,-1} , {6,3,22,-8} , {6,3,28,-13} , {6,4,5,0} , {6,4,25,-10} , {6,6,5,1} , {6,6,25,-9} , {6,7,2,2} , {6,7,8,1} , {6,7,22,-6} , {6,7,28,-11} , {6,8,7,2} , {6,8,13,0} , {6,8,17,-2} , {6,8,23,-6} , {6,9,10,2} , {6,9,20,-3} , {12,1,4,-1} , {12,1,14,-4} , {12,1,16,-5} , {12,1,26,-12} , {12,2,5,-1} , {12,2,25,-11} , {12,3,10,-2} , {12,3,20,-7} , {12,4,1,0} , {12,4,11,-2} , {12,4,19,-6} , {12,4,29,-14} , {12,6,1,1} , {12,6,11,-1} , {12,6,19,-5} , {12,6,29,-13} , {12,7,10,0} , {12,7,20,-5} , {12,8,5,2} , {12,8,25,-8} , {12,9,4,3} , {12,9,14,0} , {12,9,16,-1} , {12,9,26,-8} , {15,1,2,-1} , {15,1,8,-2} , {15,1,22,-9} , {15,1,28,-14} , {15,4,7,-1} , {15,4,13,-3} , {15,4,17,-5} , {15,4,23,-9} , {15,5,4,0} , {15,5,14,-3} , {15,5,16,-4} , {15,5,26,-11} , {15,6,7,0} , {15,6,13,-2} , {15,6,17,-4} , {15,6,23,-8} , {15,9,2,3} , {15,9,8,2} , {15,9,22,-5} , {15,9,28,-10} , {15,10,1,4} , {15,10,11,2} , {15,10,19,-2} , {15,10,29,-10} } ; void primegen_sieve(primegen *pg) { uint32_t (*buf)[B32]; uint64_t L; int i; uint32_t Lmodqq[49]; buf = pg->buf; L = pg->L; if (L > 2000000000) for (i = 0;i < 49;++i) Lmodqq[i] = L % qqtab[i]; else for (i = 0;i < 49;++i) Lmodqq[i] = ((uint32_t) L) % qqtab[i]; clear(buf); for (i = 0;i < 16;++i) doit4(buf[0],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[0],Lmodqq,1); for (;i < 32;++i) doit4(buf[3],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[3],Lmodqq,13); for (;i < 48;++i) doit4(buf[4],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[4],Lmodqq,17); for (;i < 64;++i) doit4(buf[7],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[7],Lmodqq,29); for (;i < 80;++i) doit4(buf[9],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[9],Lmodqq,37); for (;i < 96;++i) doit4(buf[10],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[10],Lmodqq,41); for (;i < 112;++i) doit4(buf[13],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[13],Lmodqq,49); for (;i < 128;++i) doit4(buf[14],for4[i].f,for4[i].g,(int64_t) for4[i].k - L); squarefreetiny(buf[14],Lmodqq,53); for (i = 0;i < 12;++i) doit6(buf[1],for6[i].f,for6[i].g,(int64_t) for6[i].k - L); squarefreetiny(buf[1],Lmodqq,7); for (;i < 24;++i) doit6(buf[5],for6[i].f,for6[i].g,(int64_t) for6[i].k - L); squarefreetiny(buf[5],Lmodqq,19); for (;i < 36;++i) doit6(buf[8],for6[i].f,for6[i].g,(int64_t) for6[i].k - L); squarefreetiny(buf[8],Lmodqq,31); for (;i < 48;++i) doit6(buf[11],for6[i].f,for6[i].g,(int64_t) for6[i].k - L); squarefreetiny(buf[11],Lmodqq,43); for (i = 0;i < 24;++i) doit12(buf[2],for12[i].f,for12[i].g,(int64_t) for12[i].k - L); squarefreetiny(buf[2],Lmodqq,11); for (;i < 48;++i) doit12(buf[6],for12[i].f,for12[i].g,(int64_t) for12[i].k - L); squarefreetiny(buf[6],Lmodqq,23); for (;i < 72;++i) doit12(buf[12],for12[i].f,for12[i].g,(int64_t) for12[i].k - L); squarefreetiny(buf[12],Lmodqq,47); for (;i < 96;++i) doit12(buf[15],for12[i].f,for12[i].g,(int64_t) for12[i].k - L); squarefreetiny(buf[15],Lmodqq,59); squarefree49(buf,L,247); squarefree49(buf,L,253); squarefree49(buf,L,257); squarefree49(buf,L,263); squarefree1(buf,L,241); squarefree1(buf,L,251); squarefree1(buf,L,259); squarefree1(buf,L,269); } void primegen_fill(primegen *pg) { int i; uint32_t mask; uint32_t bits0, bits1, bits2, bits3, bits4, bits5, bits6, bits7; uint32_t bits8, bits9, bits10, bits11, bits12, bits13, bits14, bits15; uint64_t base; i = pg->pos; if (i == B32) { primegen_sieve(pg); pg->L += B; i = 0; } pg->pos = i + 1; bits0 = ~pg->buf[0][i]; bits1 = ~pg->buf[1][i]; bits2 = ~pg->buf[2][i]; bits3 = ~pg->buf[3][i]; bits4 = ~pg->buf[4][i]; bits5 = ~pg->buf[5][i]; bits6 = ~pg->buf[6][i]; bits7 = ~pg->buf[7][i]; bits8 = ~pg->buf[8][i]; bits9 = ~pg->buf[9][i]; bits10 = ~pg->buf[10][i]; bits11 = ~pg->buf[11][i]; bits12 = ~pg->buf[12][i]; bits13 = ~pg->buf[13][i]; bits14 = ~pg->buf[14][i]; bits15 = ~pg->buf[15][i]; base = pg->base + 1920; pg->base = base; pg->num = 0; for (mask = 0x80000000;mask;mask >>= 1) { base -= 60; if (bits15 & mask) pg->p[pg->num++] = base + 59; if (bits14 & mask) pg->p[pg->num++] = base + 53; if (bits13 & mask) pg->p[pg->num++] = base + 49; if (bits12 & mask) pg->p[pg->num++] = base + 47; if (bits11 & mask) pg->p[pg->num++] = base + 43; if (bits10 & mask) pg->p[pg->num++] = base + 41; if (bits9 & mask) pg->p[pg->num++] = base + 37; if (bits8 & mask) pg->p[pg->num++] = base + 31; if (bits7 & mask) pg->p[pg->num++] = base + 29; if (bits6 & mask) pg->p[pg->num++] = base + 23; if (bits5 & mask) pg->p[pg->num++] = base + 19; if (bits4 & mask) pg->p[pg->num++] = base + 17; if (bits3 & mask) pg->p[pg->num++] = base + 13; if (bits2 & mask) pg->p[pg->num++] = base + 11; if (bits1 & mask) pg->p[pg->num++] = base + 7; if (bits0 & mask) pg->p[pg->num++] = base + 1; } } uint64_t primegen_next(primegen *pg) { while (!pg->num) primegen_fill(pg); return pg->p[--pg->num]; } uint64_t primegen_peek(primegen *pg) { while (!pg->num) primegen_fill(pg); return pg->p[pg->num - 1]; } void primegen_init(primegen *pg) { pg->L = 1; pg->base = 60; pg->pos = PRIMEGEN_WORDS; pg->p[0] = 59; pg->p[1] = 53; pg->p[2] = 47; pg->p[3] = 43; pg->p[4] = 41; pg->p[5] = 37; pg->p[6] = 31; pg->p[7] = 29; pg->p[8] = 23; pg->p[9] = 19; pg->p[10] = 17; pg->p[11] = 13; pg->p[12] = 11; pg->p[13] = 7; pg->p[14] = 5; pg->p[15] = 3; pg->p[16] = 2; pg->num = 17; } static const unsigned long pop[256] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5 ,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6 ,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6 ,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7 ,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6 ,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7 ,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7 ,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 }; uint64_t primegen_count(primegen *pg,uint64_t to) { uint64_t count = 0; for (;;) { register int pos; register int j; register uint32_t bits; register uint32_t smallcount; while (pg->num) { if (pg->p[pg->num - 1] >= to) return count; ++count; --pg->num; } smallcount = 0; pos = pg->pos; while ((pos < B32) && (pg->base + 1920 < to)) { for (j = 0;j < 16;++j) { bits = ~pg->buf[j][pos]; smallcount += pop[bits & 255]; bits >>= 8; smallcount += pop[bits & 255]; bits >>= 8; smallcount += pop[bits & 255]; bits >>= 8; smallcount += pop[bits & 255]; } pg->base += 1920; ++pos; } pg->pos = pos; count += smallcount; if (pos == B32) while (pg->base + B * 60 < to) { primegen_sieve(pg); pg->L += B; smallcount = 0; for (j = 0;j < 16;++j) for (pos = 0;pos < B32;++pos) { bits = ~pg->buf[j][pos]; smallcount += pop[bits & 255]; bits >>= 8; smallcount += pop[bits & 255]; bits >>= 8; smallcount += pop[bits & 255]; bits >>= 8; smallcount += pop[bits & 255]; } count += smallcount; pg->base += B * 60; } primegen_fill(pg); } } void primegen_skipto(primegen *pg,uint64_t to) { for (;;) { int pos; while (pg->num) { if (pg->p[pg->num - 1] >= to) return; --pg->num; } pos = pg->pos; while ((pos < B32) && (pg->base + 1920 < to)) { pg->base += 1920; ++pos; } pg->pos = pos; if (pos == B32) while (pg->base + B * 60 < to) { pg->L += B; pg->base += B * 60; } primegen_fill(pg); } } ================================================ FILE: src/crypto-primegen.h ================================================ #ifndef PRIMEGEN_H #define PRIMEGEN_H #include /** * This is B/32: the number of 32-bit words of space used in the primegen * inner loop. This should fit into the CPU's level-1 cache. * * 2048 works well on a Pentium-100. * 3600 works well on a Pentium II-350 * 4004 works well on an UltraSPARC-I/167 * * 2012-nov (Rob): This code was written 15 years ago. Processor caches * haven't really gotten any larger. A number like 8008 works slightly * better on an Ivy Bridge CPU, but works noticeably worse on an Atom * or ARM processor. The value 4004 seems to be a good compromise for * all these processors. In any case, modern CPUs will automatically * prefetch the buffers anyway, significantly lessoning the impact of * having a poor number defined here. I tried 16016, but it crashed, and * I don't know why, but I don't care because I'm not going to use such a * large size. */ #define PRIMEGEN_WORDS 4004 typedef struct { uint32_t buf[16][PRIMEGEN_WORDS]; uint64_t p[512]; /* p[num-1] ... p[0], in that order */ int num; int pos; /* next entry to use in buf; WORDS to restart */ uint64_t base; uint64_t L; } primegen; extern void primegen_sieve(primegen *); extern void primegen_fill(primegen *); extern void primegen_init(primegen *); extern uint64_t primegen_next(primegen *); extern uint64_t primegen_peek(primegen *); extern uint64_t primegen_count(primegen *,uint64_t to); extern void primegen_skipto(primegen *,uint64_t to); #endif ================================================ FILE: src/crypto-siphash24.c ================================================ /* SipHash reference C implementation Written in 2012 by Jean-Philippe Aumasson Daniel J. Bernstein To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . */ #include #include #include #include "crypto-siphash24.h" typedef uint64_t u64; typedef uint32_t u32; typedef uint8_t u8; #define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) ) #define U32TO8_LE(p, v) \ (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \ (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24); #define U64TO8_LE(p, v) \ U32TO8_LE((p), (u32)((v) )); \ U32TO8_LE((p) + 4, (u32)((v) >> 32)); #define U8TO64_LE(p) \ (((u64)((p)[0]) ) | \ ((u64)((p)[1]) << 8) | \ ((u64)((p)[2]) << 16) | \ ((u64)((p)[3]) << 24) | \ ((u64)((p)[4]) << 32) | \ ((u64)((p)[5]) << 40) | \ ((u64)((p)[6]) << 48) | \ ((u64)((p)[7]) << 56)) #define SIPROUND \ { \ v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \ v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \ v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \ v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \ } /* SipHash-2-4 */ static int crypto_auth( unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *k ) { /* "somepseudorandomlygeneratedbytes" */ u64 v0 = 0x736f6d6570736575ULL; u64 v1 = 0x646f72616e646f6dULL; u64 v2 = 0x6c7967656e657261ULL; u64 v3 = 0x7465646279746573ULL; u64 b; u64 k0 = U8TO64_LE( k ); u64 k1 = U8TO64_LE( k + 8 ); const u8 *end = in + inlen - ( inlen % sizeof( u64 ) ); const int left = inlen & 7; b = ( ( u64 )inlen ) << 56; v3 ^= k1; v2 ^= k0; v1 ^= k1; v0 ^= k0; for ( ; in != end; in += 8 ) { u64 m; m = U8TO64_LE( in ); #ifdef xxxDEBUG printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 ); printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 ); printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 ); printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 ); printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m ); #endif v3 ^= m; SIPROUND; SIPROUND; v0 ^= m; } switch( left ) { case 7: b |= ( ( u64 )in[ 6] ) << 48; case 6: b |= ( ( u64 )in[ 5] ) << 40; case 5: b |= ( ( u64 )in[ 4] ) << 32; case 4: b |= ( ( u64 )in[ 3] ) << 24; case 3: b |= ( ( u64 )in[ 2] ) << 16; case 2: b |= ( ( u64 )in[ 1] ) << 8; case 1: b |= ( ( u64 )in[ 0] ); break; case 0: break; } #ifdef xxxDEBUG printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 ); printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 ); printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 ); printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 ); printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b ); #endif v3 ^= b; SIPROUND; SIPROUND; v0 ^= b; #ifdef xxxDEBUG printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 ); printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 ); printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 ); printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 ); #endif v2 ^= 0xff; SIPROUND; SIPROUND; SIPROUND; SIPROUND; b = v0 ^ v1 ^ v2 ^ v3; U64TO8_LE( out, b ); return 0; } uint64_t siphash24(const void *in, size_t inlen, const uint64_t key[2]) { uint64_t result; crypto_auth((unsigned char*)&result, (const unsigned char *)in, inlen, (const unsigned char *)&key[0]); return result; } /* SipHash-2-4 output with k = 00 01 02 ... and in = (empty string) in = 00 (1 byte) in = 00 01 (2 bytes) in = 00 01 02 (3 bytes) ... in = 00 01 02 ... 3e (63 bytes) */ static u8 vectors[64][8] = { { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, }, { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, }, { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, }, { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, }, { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, }, { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, }, { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, }, { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, }, { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, }, { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, }, { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, }, { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, }, { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, }, { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, }, { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, }, { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, }, { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, }, { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, }, { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, }, { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, }, { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, }, { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, }, { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, }, { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, }, { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, }, { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, }, { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, }, { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, }, { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, }, { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, }, { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, }, { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, }, { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, }, { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, }, { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, }, { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, }, { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, }, { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, }, { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, }, { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, }, { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, }, { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, }, { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, }, { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, }, { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, }, { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, }, { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, }, { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, }, { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, }, { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, }, { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, }, { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, }, { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, }, { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, }, { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, }, { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, }, { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, }, { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, }, { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, }, { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, }, { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, }, { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, }, { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, }, { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, } }; static int test_vectors() { #define MAXLEN 64 u8 in[MAXLEN], out[8], k[16]; int i; int ok = 1; for( i = 0; i < 16; ++i ) k[i] = (u8)i; for( i = 0; i < MAXLEN; ++i ) { in[i] = (u8)i; crypto_auth( out, in, i, k ); if ( memcmp( out, vectors[i], 8 ) ) { printf( "test vector failed for %d bytes\n", i ); ok = 0; } } return ok; } int siphash24_selftest(void) { if (test_vectors()) return 0; else return 1; } ================================================ FILE: src/crypto-siphash24.h ================================================ #ifndef CRYPTO_SIPHASH24_H #define CRYPTO_SIPHASH24_H #include uint64_t siphash24(const void *in, size_t inlen, const uint64_t key[2]); /** * Regression-test this module. * @return * 0 on success, a positive integer otherwise. */ int siphash24_selftest(void); #endif ================================================ FILE: src/event-timeout.c ================================================ /* Event timeout This is for the user-mode TCP stack. We need to mark timeouts in the future when we'll re-visit a connection/tcb. For example, when we send a packet, we need to resend it in the future in case we don't get a response. This design creates a large "ring" of timeouts, and then cycles again and again through the ring. This is a fairly high granularity, just has hundreds, thousands, or 10 thousand entries per second. (I keep adjusting the granularity up and down). Not that at any slot in the ring, there may be entries from the far future. NOTE: a big feature of this system is that the structure that tracks the timeout is actually held within the TCB structure. In other words, each TCB can have one-and-only-one timeout. NOTE: a recurring bug is that the TCP code removes a TCB from the timeout ring and forgets to put it back somewhere else. Since the TCB is cleaned up on a timeout, such TCBs never get cleaned up, leading to a memory leak. I keep fixing this bug, then changing the code and causing the bug to come back again. */ #include "event-timeout.h" #include "util-logger.h" #include "util-malloc.h" #include #include #include #include /*************************************************************************** * The timeout system is a circular ring. We move an index around the * ring. At each slot in the ring is a linked-list of all entries at * that time index. Because the ring can wrap, not everything at a given * entry will be the same timestamp. Therefore, when doing the timeout * logic at a slot, we have to doublecheck the actual timestamp, and skip * those things that are further in the future. ***************************************************************************/ struct Timeouts { /** * This index is a monotonically increasing number, modulus the mask. * Every time we check timeouts, we simply move it forward in time. */ uint64_t current_index; /** * Counts the number of outstanding timeouts. Adding a timeout increments * this number, and removing a timeout decrements this number. The * program shouldn't exit until this number is zero. */ uint64_t outstanding_count; /** * The number of slots is a power-of-2, so the mask is just this * number minus 1 */ unsigned mask; /** * The ring of entries. */ struct TimeoutEntry *slots[1024*1024]; }; /*************************************************************************** ***************************************************************************/ struct Timeouts * timeouts_create(uint64_t timestamp) { struct Timeouts *timeouts; /* * Allocate memory and initialize it to zero */ timeouts = CALLOC(1, sizeof(*timeouts)); /* * We just mask off the low order bits to determine wrap. I'm using * a variable here because one of these days I'm going to make * the size of the ring dynamically adjustable depending upon * the speed of the scan. */ timeouts->mask = sizeof(timeouts->slots)/sizeof(timeouts->slots[0]) - 1; /* * Set the index to the current time. Note that this timestamp is * the 'time_t' value multiplied by the number of ticks-per-second, * where 'ticks' is something I've defined for scanning. Right now * I hard-code in the size of the ticks, but eventually they'll be * dynamically resized depending upon the speed of the scan. */ timeouts->current_index = timestamp; return timeouts; } /*************************************************************************** * This inserts the timeout entry into the appropriate place in the * timeout ring. ***************************************************************************/ void timeouts_add(struct Timeouts *timeouts, struct TimeoutEntry *entry, size_t offset, uint64_t timestamp) { unsigned index; /* Unlink from wherever the entry came from */ if (entry->timestamp) timeouts->outstanding_count--; timeout_unlink(entry); if (entry->prev) { LOG(1, "***CHANGE %d-seconds\n", (int)((timestamp-entry->timestamp)/TICKS_PER_SECOND)); } /* Initialize the new entry */ entry->timestamp = timestamp; entry->offset = (unsigned)offset; /* Link it into it's new location */ index = timestamp & timeouts->mask; entry->next = timeouts->slots[index]; timeouts->slots[index] = entry; entry->prev = &timeouts->slots[index]; if (entry->next) entry->next->prev = &entry->next; timeouts->outstanding_count++; } /*************************************************************************** * Remove the next event that it older than the specified timestamp ***************************************************************************/ void * timeouts_remove(struct Timeouts *timeouts, uint64_t timestamp) { struct TimeoutEntry *entry = NULL; /* Search until we find one */ while (timeouts->current_index <= timestamp) { /* Start at the current slot */ entry = timeouts->slots[timeouts->current_index & timeouts->mask]; /* enumerate through the linked list until we find a used slot */ while (entry && entry->timestamp > timestamp) entry = entry->next; if (entry) break; /* found nothing at this slot, so move to next slot */ timeouts->current_index++; } if (entry == NULL) { /* we've caught up to the current time, and there's nothing * left to timeout, so return NULL */ return NULL; } /* unlink this entry from the timeout system */ timeouts--; timeout_unlink(entry); /* return a pointer to the structure holding this entry */ return ((char*)entry) - entry->offset; } ================================================ FILE: src/event-timeout.h ================================================ #ifndef EVENT_TIMEOUT_H #define EVENT_TIMEOUT_H #include #include #include /* offsetof*/ #include "util-bool.h" /* */ #if defined(_MSC_VER) #undef inline #define inline _inline #endif struct Timeouts; /*************************************************************************** ***************************************************************************/ struct TimeoutEntry { /** * In units of 1/16384 of a second. We use power-of-two units here * to make the "modulus" operation a simple binary "and". * See the TICKS_FROM_TV() macro for getting the timestamp from * the current time. */ uint64_t timestamp; /** we build a doubly-linked list */ struct TimeoutEntry *next; struct TimeoutEntry **prev; /** The timeout entry is never allocated by itself, but instead * lives inside another data structure. This stores the value of * 'offsetof()', so given a pointer to this structure, we can find * the original structure that contains it */ unsigned offset; }; /*************************************************************************** ***************************************************************************/ static inline bool timeout_is_unlinked(const struct TimeoutEntry *entry) { if (entry->prev == 0 || entry->next == 0) return true; else return false; } /*************************************************************************** ***************************************************************************/ static inline void timeout_unlink(struct TimeoutEntry *entry) { if (entry->prev == 0 && entry->next == 0) return; *(entry->prev) = entry->next; if (entry->next) entry->next->prev = entry->prev; entry->next = 0; entry->prev = 0; entry->timestamp = 0; } /*************************************************************************** ***************************************************************************/ static inline void timeout_init(struct TimeoutEntry *entry) { entry->next = 0; entry->prev = 0; } /** * Create a timeout subsystem. * @param timestamp_now * The current timestamp indicating "now" when the thing starts. * This should be 'time(0) * TICKS_PER_SECOND'. */ struct Timeouts * timeouts_create(uint64_t timestamp_now); /** * Insert the timeout 'entry' into the future location in the timeout * ring, as determined by the timestamp. * @param timeouts * A ring of timeouts, with each slot corresponding to a specific * time in the future. * @param entry * The entry that we are going to insert into the ring. If it's * already in the ring, it'll be removed from the old location * first before inserting into the new location. * @param offset * The 'entry' field above is part of an existing structure. This * tells the offset_of() from the beginning of that structure. * In other words, this tells us the pointer to the object that * that is the subject of the timeout. * @param timestamp_expires * When this timeout will expire. This is in terms of internal * ticks, which in units of TICKS_PER_SECOND. */ void timeouts_add(struct Timeouts *timeouts, struct TimeoutEntry *entry, size_t offset, uint64_t timestamp_expires); /** * Remove an object from the timestamp system that is older than than * the specified timestamp. This function must be called repeatedly * until it returns NULL to remove all the objects that are older * than the given timestamp. * @param timeouts * A ring of timeouts. We'll walk the ring until we've caught * up with the current time. * @param timestamp_now * Usually, this timestmap will be "now", the current time, * and anything older than this will be aged out. * @return * an object older than the specified timestamp, or NULL * if there are no more objects to be found */ void * timeouts_remove(struct Timeouts *timeouts, uint64_t timestamp_now); /* * This macros convert a normal "timeval" structure into the timestamp * that we use for timeouts. The timeval structure probably will come * from the packets that we are capturing. */ #define TICKS_PER_SECOND (16384ULL) #define TICKS_FROM_SECS(secs) ((secs)*16384ULL) #define TICKS_FROM_USECS(usecs) ((usecs)/16384ULL) #define TICKS_FROM_TV(secs,usecs) (TICKS_FROM_SECS(secs)+TICKS_FROM_USECS(usecs)) #endif ================================================ FILE: src/in-binary.c ================================================ /* Read in the binary file produced by "out-binary.c". This allows you to translate the "binary" format into any of the other output formats. */ #include "massip-addr.h" #include "in-binary.h" #include "masscan.h" #include "masscan-app.h" #include "masscan-status.h" #include "main-globals.h" #include "output.h" #include "util-safefunc.h" #include "in-filter.h" #include "in-report.h" #include "util-malloc.h" #include "util-logger.h" #include #include #ifdef _MSC_VER #pragma warning(disable:4996) #endif static const size_t BUF_MAX = 1024*1024; struct MasscanRecord { unsigned timestamp; ipaddress ip; unsigned char ip_proto; unsigned short port; unsigned char reason; unsigned char ttl; unsigned char mac[6]; enum ApplicationProtocol app_proto; }; /*************************************************************************** ***************************************************************************/ static void parse_status(struct Output *out, enum PortStatus status, /* open/closed */ const unsigned char *buf, size_t buf_length) { struct MasscanRecord record; if (buf_length < 12) return; /* parse record */ record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]; record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]; record.ip.version = 4; record.port = buf[8]<<8 | buf[9]; record.reason = buf[10]; record.ttl = buf[11]; /* if ARP, then there will be a MAC address */ if (record.ip.ipv4 == 0 && buf_length >= 12+6) memcpy(record.mac, buf+12, 6); else memset(record.mac, 0, 6); if (out->when_scan_started == 0) out->when_scan_started = record.timestamp; switch (record.port) { case 53: case 123: case 137: case 161: record.ip_proto = 17; break; case 36422: case 36412: case 2905: record.ip_proto = 132; break; default: record.ip_proto = 6; break; } /* * Now report the result */ output_report_status(out, record.timestamp, status, record.ip, record.ip_proto, record.port, record.reason, record.ttl, record.mac); } /*************************************************************************** ***************************************************************************/ static void parse_status2(struct Output *out, enum PortStatus status, /* open/closed */ const unsigned char *buf, size_t buf_length, struct MassIP *filter) { struct MasscanRecord record; if (buf_length < 13) return; /* parse record */ record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]; record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]; record.ip.version = 4; record.ip_proto = buf[8]; record.port = buf[9]<<8 | buf[10]; record.reason = buf[11]; record.ttl = buf[12]; /* if ARP, then there will be a MAC address */ if (record.ip.ipv4 == 0 && buf_length >= 13+6) memcpy(record.mac, buf+13, 6); else memset(record.mac, 0, 6); if (out->when_scan_started == 0) out->when_scan_started = record.timestamp; /* Filter for known IP/ports, if specified on command-line */ if (filter && filter->count_ipv4s) { if (!massip_has_ip(filter, record.ip)) return; } if (filter && filter->count_ports) { if (!massip_has_port(filter, record.port)) return; } /* * Now report the result */ output_report_status(out, record.timestamp, status, record.ip, record.ip_proto, record.port, record.reason, record.ttl, record.mac); } static unsigned char _get_byte(const unsigned char *buf, size_t length, size_t *offset) { unsigned char result; if (*offset < length) { result = buf[*offset]; } else { result = 0xFF; } (*offset)++; return result; } static unsigned _get_integer(const unsigned char *buf, size_t length, size_t *r_offset) { unsigned result; size_t offset = *r_offset; (*r_offset) += 4; if (offset + 4 <= length) { result = buf[offset+0]<<24 | buf[offset+1]<<16 | buf[offset+2]<<8 | buf[offset+3]<<0; } else { result = 0xFFFFFFFF; } return result; } static unsigned short _get_short(const unsigned char *buf, size_t length, size_t *r_offset) { unsigned short result; size_t offset = *r_offset; (*r_offset) += 2; if (offset + 2 <= length) { result = buf[offset+0]<<8 | buf[offset+1]<<0; } else { result = 0xFFFF; } return result; } static unsigned long long _get_long(const unsigned char *buf, size_t length, size_t *r_offset) { unsigned long long result; size_t offset = *r_offset; (*r_offset) += 8; if (offset + 8 <= length) { result = (unsigned long long)buf[offset+0]<<56ULL | (unsigned long long)buf[offset+1]<<48ULL | (unsigned long long)buf[offset+2]<<40ULL | (unsigned long long)buf[offset+3]<<32ULL | (unsigned long long)buf[offset+4]<<24ULL | (unsigned long long)buf[offset+5]<<16ULL | (unsigned long long)buf[offset+6]<<8ULL | (unsigned long long)buf[offset+7]<<0ULL; } else { result = 0xFFFFFFFFffffffffULL; } return result; } /*************************************************************************** ***************************************************************************/ static void parse_status6(struct Output *out, enum PortStatus status, /* open/closed */ const unsigned char *buf, size_t length, struct MassIP *filter) { struct MasscanRecord record; size_t offset = 0; /* parse record */ record.timestamp = _get_integer(buf, length, &offset); record.ip_proto = _get_byte(buf, length, &offset); record.port = _get_short(buf, length, &offset); record.reason = _get_byte(buf, length, &offset); record.ttl = _get_byte(buf, length, &offset); record.ip.version= _get_byte(buf, length, &offset); if (record.ip.version != 6) { fprintf(stderr, "[-] corrupt record\n"); return; } record.ip.ipv6.hi = _get_long(buf, length, &offset); record.ip.ipv6.lo = _get_long(buf, length, &offset); if (out->when_scan_started == 0) out->when_scan_started = record.timestamp; /* Filter for known IP/ports, if specified on command-line */ if (filter && filter->count_ipv4s) { if (!massip_has_ip(filter, record.ip)) return; } if (filter && filter->count_ports) { if (!massip_has_port(filter, record.port)) return; } /* * Now report the result */ output_report_status(out, record.timestamp, status, record.ip, record.ip_proto, record.port, record.reason, record.ttl, record.mac); } /*************************************************************************** ***************************************************************************/ static void parse_banner6(struct Output *out, unsigned char *buf, size_t length, const struct MassIP *filter, const struct RangeList *btypes) { struct MasscanRecord record; size_t offset = 0; /* * Parse the parts that are common to most records */ record.timestamp = _get_integer(buf, length, &offset); record.ip_proto = _get_byte(buf, length, &offset); record.port = _get_short(buf, length, &offset); record.app_proto = _get_short(buf, length, &offset); record.ttl = _get_byte(buf, length, &offset); record.ip.version= _get_byte(buf, length, &offset); if (record.ip.version != 6) { fprintf(stderr, "[-] corrupt record\n"); return; } record.ip.ipv6.hi = _get_long(buf, length, &offset); record.ip.ipv6.lo = _get_long(buf, length, &offset); if (out->when_scan_started == 0) out->when_scan_started = record.timestamp; /* * Filter out records if requested */ if (!readscan_filter_pass(record.ip, record.port, record.app_proto, filter, btypes)) return; /* * Now print the output */ if (offset > length) return; output_report_banner( out, record.timestamp, record.ip, record.ip_proto, /* TCP=6, UDP=17 */ record.port, record.app_proto, /* HTTP, SSL, SNMP, etc. */ record.ttl, /* ttl */ buf+offset, (unsigned)(length-offset) ); } /*************************************************************************** * [OBSOLETE] * This parses an old version of the banner record. I've still got files * hanging around with this version, so I'm keeping it in the code for * now, but eventually I'll get rid of it. ***************************************************************************/ static void parse_banner3(struct Output *out, unsigned char *buf, size_t buf_length) { struct MasscanRecord record; /* * Parse the parts that are common to most records */ record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]; record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]; record.ip.version = 4; record.port = buf[8]<<8 | buf[9]; record.app_proto = buf[10]<<8 | buf[11]; if (out->when_scan_started == 0) out->when_scan_started = record.timestamp; /* * Now print the output */ output_report_banner( out, record.timestamp, record.ip, 6, /* this is always TCP */ record.port, record.app_proto, 0, /* ttl */ buf+12, (unsigned)buf_length-12 ); } /*************************************************************************** * Parse the BANNER record, extracting the timestamp, IP address, and port * number. We also convert the banner string into a safer form. ***************************************************************************/ static void parse_banner4(struct Output *out, unsigned char *buf, size_t buf_length) { struct MasscanRecord record; if (buf_length < 13) return; /* * Parse the parts that are common to most records */ record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]; record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]; record.ip.version = 4; record.ip_proto = buf[8]; record.port = buf[9]<<8 | buf[10]; record.app_proto = buf[11]<<8 | buf[12]; if (out->when_scan_started == 0) out->when_scan_started = record.timestamp; /* * Now print the output */ output_report_banner( out, record.timestamp, record.ip, record.ip_proto, /* TCP=6, UDP=17 */ record.port, record.app_proto, /* HTTP, SSL, SNMP, etc. */ 0, /* ttl */ buf+13, (unsigned)buf_length-13 ); } /*************************************************************************** ***************************************************************************/ static void parse_banner9(struct Output *out, unsigned char *buf, size_t buf_length, const struct MassIP *filter, const struct RangeList *btypes) { struct MasscanRecord record; unsigned char *data = buf+14; size_t data_length = buf_length-14; if (buf_length < 14) return; /* * Parse the parts that are common to most records */ record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]; record.ip.ipv4 = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]; record.ip.version = 4; record.ip_proto = buf[8]; record.port = buf[9]<<8 | buf[10]; record.app_proto = buf[11]<<8 | buf[12]; record.ttl = buf[13]; if (out->when_scan_started == 0) out->when_scan_started = record.timestamp; /* * KLUDGE: when doing SSL stuff, add a IP:name pair to a database * so we can annotate [VULN] strings with this information */ //readscan_report(record.ip, record.app_proto, &data, &data_length); /* * Filter out records if requested */ if (!readscan_filter_pass(record.ip, record.port, record.app_proto, filter, btypes)) return; /* * Now print the output */ output_report_banner( out, record.timestamp, record.ip, record.ip_proto, /* TCP=6, UDP=17 */ record.port, record.app_proto, /* HTTP, SSL, SNMP, etc. */ record.ttl, /* ttl */ data, (unsigned)data_length ); } /*************************************************************************** * Read in the file, one record at a time. ***************************************************************************/ static uint64_t _binaryfile_parse(struct Output *out, const char *filename, struct MassIP *filter, const struct RangeList *btypes) { FILE *fp = 0; unsigned char *buf = 0; size_t bytes_read; uint64_t total_records = 0; /* Allocate a buffer of up to one megabyte per record */ buf = MALLOC(BUF_MAX); /* Open the file */ fp = fopen(filename, "rb"); if (fp == NULL) { fprintf(stderr, "[-] FAIL: --readscan\n"); fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno)); goto end; } LOG(0, "[+] --readscan %s\n", filename); if (feof(fp)) { LOG(0, "[-] %s: file is empty\n", filename); goto end; } /* first record is pseudo-record */ bytes_read = fread(buf, 1, 'a'+2, fp); if (bytes_read < 'a'+2) { LOG(0, "[-] %s: %s\n", filename, strerror(errno)); goto end; } /* Make sure it's got the format string */ if (memcmp(buf, "masscan/1.1", 11) != 0) { LOG(0, "[-] %s: unknown file format (expeced \"masscan/1.1\")\n", filename); goto end; } /* * Look for start time */ if (buf[11] == '.' && strtoul((char*)buf+12,0,0) >= 2) { unsigned i; /* move to next field */ for (i=0; i<'a' && buf[i] && buf[i] != '\n'; i++) ; i++; if (buf[i] == 's') i++; if (buf[i] == ':') i++; /* extract timestamp */ if (i < 'a') out->when_scan_started = strtoul((char*)buf+i,0,0); } /* Now read all records */ for (;;) { unsigned type; unsigned length; /* [TYPE] * This is one or more bytes indicating the type of type of the * record */ bytes_read = fread(buf, 1, 1, fp); if (bytes_read != 1) break; type = buf[0] & 0x7F; while (buf[0] & 0x80) { bytes_read = fread(buf, 1, 1, fp); if (bytes_read != 1) break; type = (type << 7) | (buf[0] & 0x7F); } /* [LENGTH] * Is one byte for lengths smaller than 127 bytes, or two * bytes for lengths up to 16384. */ bytes_read = fread(buf, 1, 1, fp); if (bytes_read != 1) break; length = buf[0] & 0x7F; while (buf[0] & 0x80) { bytes_read = fread(buf, 1, 1, fp); if (bytes_read != 1) break; length = (length << 7) | (buf[0] & 0x7F); } if (length > BUF_MAX) { LOG(0, "[-] file corrupt\n"); goto end; } /* get the remainder of the record */ bytes_read = fread(buf, 1, length, fp); if (bytes_read < length) break; /* eof */ /* Depending on record type, do something different */ switch (type) { case 1: /* STATUS: open */ if (!btypes->count) parse_status(out, PortStatus_Open, buf, bytes_read); break; case 2: /* STATUS: closed */ if (!btypes->count) parse_status(out, PortStatus_Closed, buf, bytes_read); break; case 3: /* BANNER */ parse_banner3(out, buf, bytes_read); break; case 4: if (fread(buf+bytes_read,1,1,fp) != 1) { LOG(0, "[-] read() error\n"); exit(1); } bytes_read++; parse_banner4(out, buf, bytes_read); break; case 5: parse_banner4(out, buf, bytes_read); break; case 6: /* STATUS: open */ if (!btypes->count) parse_status2(out, PortStatus_Open, buf, bytes_read, filter); break; case 7: /* STATUS: closed */ if (!btypes->count) parse_status2(out, PortStatus_Closed, buf, bytes_read, filter); break; case 9: parse_banner9(out, buf, bytes_read, filter, btypes); break; case 10: /* Open6 */ if (!btypes->count) parse_status6(out, PortStatus_Open, buf, bytes_read, filter); break; case 11: /* Closed6 */ if (!btypes->count) parse_status6(out, PortStatus_Closed, buf, bytes_read, filter); break; case 13: /* Banner6 */ parse_banner6(out, buf, bytes_read, filter, btypes); break; case 'm': /* FILEHEADER */ //goto end; break; default: LOG(0, "[-] file corrupt: unknown type %u\n", type); goto end; } total_records++; if ((total_records & 0xFFFF) == 0) LOG(0, "[+] %s: %8" PRIu64 "\r", filename, total_records); } end: if (buf) free(buf); if (fp) fclose(fp); return total_records; } /***************************************************************************** * When masscan is called with the "--readscan" parameter, it doesn't * do a scan of the live network, but instead reads scan results from * a file. Those scan results can then be written out in any of the * other formats. This preserves the original timestamps. *****************************************************************************/ void readscan_binary_scanfile(struct Masscan *masscan, int arg_first, int arg_max, char *argv[]) { struct Output *out; int i; /* * Create the output system, such as XML or JSON output */ out = output_create(masscan, 0); /* * Set the start time to zero. We'll read it from the first file * that we parse */ out->when_scan_started = 0; /* * We don't parse the entire argument list, just a subrange * containing the list of files. The 'arg_first' parameter * points to the first filename after the '--readscan' * parameter, and 'arg_max' is the parameter after * the last filename. For example, consider an argument list that * looks like: * masscan --foo --readscan file1.scan file2.scan --bar * Then arg_first=3 and arg_max=5. */ for (i=arg_first; itargets, &masscan->banner_types); } /* Done! */ output_destroy(out); } ================================================ FILE: src/in-binary.h ================================================ #ifndef IN_BINARY_H #define IN_BINARY_H struct Masscan; /** * Read that output of previous scans that were saved in the binary format * (i.e. using the -oB parameter or the '--output-format binary' parameter). * The intent is that the user can then re-output in another format like * JSON or XML. */ void readscan_binary_scanfile(struct Masscan *masscan, int arg_first, int arg_max, char *argv[]); #endif ================================================ FILE: src/in-filter.c ================================================ #include "in-filter.h" #include "massip.h" int readscan_filter_pass(ipaddress ip, unsigned port, unsigned type, const struct MassIP *filter, const struct RangeList *btypes) { if (filter && filter->count_ipv4s) { if (!massip_has_ip(filter, ip)) return 0; } if (filter && filter->count_ports) { if (!massip_has_port(filter, port)) return 0; } if (btypes && btypes->count) { if (!rangelist_is_contains(btypes, type)) return 0; } return 1; } ================================================ FILE: src/in-filter.h ================================================ /* This is for filtering input in the "--readscan" feature */ #ifndef IN_FILTER_H #define IN_FILTER_H #include "massip-addr.h" struct RangeList; struct Range6List; struct MassIP; /** * Filters readscan record by IP address, port number, * or banner-type. */ int readscan_filter_pass(ipaddress ip, unsigned port, unsigned type, const struct MassIP *massip, const struct RangeList *btypes); #endif ================================================ FILE: src/in-report.c ================================================ #include "in-report.h" #include "masscan-app.h" #include "crypto-base64.h" #include "proto-x509.h" #include "proto-banout.h" #include "smack.h" #include "util-malloc.h" #include #include #include struct CNDB_Entry { unsigned ip; char *name; struct CNDB_Entry *next; }; struct CNDB_Database { struct CNDB_Entry *entries[65536]; }; /*************************************************************************** ***************************************************************************/ static struct CNDB_Database *db = NULL; /*************************************************************************** ***************************************************************************/ static const char * cndb_lookup(unsigned ip) { const struct CNDB_Entry *entry; if (db == NULL) return 0; entry = db->entries[ip&0xFFFF]; while (entry && entry->ip != ip) entry = entry->next; if (entry) return entry->name; else { return 0; } } /*************************************************************************** ***************************************************************************/ static void cndb_add(unsigned ip, const unsigned char *name, size_t name_length) { struct CNDB_Entry *entry; if (name_length == 0) return; if (db == NULL) { db = CALLOC(1, sizeof(*db)); } entry = MALLOC(sizeof(*entry)); entry->ip =ip; entry->name = MALLOC(name_length+1); memcpy(entry->name, name, name_length+1); entry->name[name_length] = '\0'; entry->next = db->entries[ip&0xFFFF]; db->entries[ip&0xFFFF] = entry; } /*************************************************************************** ***************************************************************************/ #if 0 static void cndb_add_cn(unsigned ip, const unsigned char *data, size_t length) { size_t offset = 0; size_t name_offset; size_t name_length; if (length < 7) return; /*cipher:0x39 , safe-we1.dyndns.org*/ if (memcmp(data+offset, "cipher:", 7) != 0) return; offset += 7; /* skip to name */ while (offset < length && data[offset] != ',') offset++; if (offset >= length) return; else offset++; /* skip ',' */ while (offset < length && data[offset] == ' ') offset++; if (offset >= length) return; /* we should have a good name */ name_offset = offset; while (offset < length && data[offset] != ',') offset++; name_length = offset - name_offset; /* now insert into database */ cndb_add(ip, data+name_offset, name_length); } #endif /*************************************************************************** ***************************************************************************/ #if 0 static unsigned found(const char *str, size_t str_len, const unsigned char *p, size_t length) { size_t i; if (str_len > length) return 0; for (i=0; i void readscan_report( unsigned ip, unsigned app_proto, unsigned char **data, size_t *data_length); void readscan_report_init(void); void readscan_report_print(void); #endif ================================================ FILE: src/main-conf.c ================================================ /* Read in the configuration for MASSCAN. Configuration parameters can be read either from the command-line or a configuration file. Long parameters of the --xxxx variety have the same name in both. Most of the code in this module is for 'nmap' options we don't support. That's because we support some 'nmap' options, and I wanted to give more feedback for some of them why they don't work as expected, such as reminding people that this is an asynchronous scanner. */ #include "masscan.h" #include "massip-addr.h" #include "masscan-version.h" #include "util-safefunc.h" #include "util-logger.h" #include "proto-banner1.h" #include "templ-payloads.h" #include "crypto-base64.h" #include "vulncheck.h" #include "masscan-app.h" #include "unusedparm.h" #include "read-service-probes.h" #include "util-malloc.h" #include "massip.h" #include "massip-parse.h" #include "massip-port.h" #include "templ-opts.h" #include #include #ifdef WIN32 #include #define getcwd _getcwd #else #include #endif #ifndef min #define min(a,b) ((a)<(b)?(a):(b)) #endif #if defined(_MSC_VER) #define strdup _strdup #endif /*************************************************************************** ***************************************************************************/ /*static struct Range top_ports_tcp[] = { {80, 80},{23, 23}, {443,443},{21,22},{25,25},{3389,3389},{110,110}, {445,445}, }; static struct Range top_ports_udp[] = { {161, 161}, {631, 631}, {137,138},{123,123},{1434},{445,445},{135,135}, {67,67}, }; static struct Range top_ports_sctp[] = { {7, 7},{9, 9},{20,22},{80,80},{179,179},{443,443},{1167,1167}, };*/ /*************************************************************************** ***************************************************************************/ void masscan_usage(void) { printf("usage: masscan [options] [... -pPORT[,PORT...]]\n"); printf("\n"); printf("examples:\n"); printf(" masscan -p80,8000-8100 10.0.0.0/8 --rate=10000\n"); printf(" scan some web ports on 10.x.x.x at 10kpps\n"); printf("\n"); printf(" masscan --nmap\n"); printf(" list those options that are compatible with nmap\n"); printf("\n"); printf(" masscan -p80 10.0.0.0/8 --banners -oB \n"); printf(" save results of scan in binary format to \n"); printf("\n"); printf(" masscan --open --banners --readscan -oX \n"); printf(" read binary scan results in and save them as xml in \n"); exit(1); } /*************************************************************************** ***************************************************************************/ static void print_version() { const char *cpu = "unknown"; const char *compiler = "unknown"; const char *compiler_version = "unknown"; const char *os = "unknown"; printf("\n"); printf("Masscan version %s ( %s )\n", MASSCAN_VERSION, "https://github.com/robertdavidgraham/masscan" ); printf("Compiled on: %s %s\n", __DATE__, __TIME__); #if defined(__x86_64) || defined(__x86_64__) cpu = "x86"; #endif #if defined(_MSC_VER) #if defined(_M_AMD64) || defined(_M_X64) cpu = "x86"; #elif defined(_M_IX86) cpu = "x86"; #elif defined (_M_ARM_FP) cpu = "arm"; #endif { int msc_ver = _MSC_VER; compiler = "VisualStudio"; if (msc_ver < 1500) compiler_version = "pre2008"; else if (msc_ver == 1500) compiler_version = "2008"; else if (msc_ver == 1600) compiler_version = "2010"; else if (msc_ver == 1700) compiler_version = "2012"; else if (msc_ver == 1800) compiler_version = "2013"; else compiler_version = "post-2013"; } #elif defined(__GNUC__) # if defined(__clang__) compiler = "clang"; # else compiler = "gcc"; # endif compiler_version = __VERSION__; #if defined(i386) || defined(__i386) || defined(__i386__) cpu = "x86"; #endif #if defined(__corei7) || defined(__corei7__) cpu = "x86-Corei7"; #endif #endif #if defined(WIN32) os = "Windows"; #elif defined(__linux__) os = "Linux"; #elif defined(__APPLE__) os = "Apple"; #elif defined(__MACH__) os = "MACH"; #elif defined(__FreeBSD__) os = "FreeBSD"; #elif defined(__NetBSD__) os = "NetBSD"; #elif defined(unix) || defined(__unix) || defined(__unix__) os = "Unix"; #endif printf("Compiler: %s %s\n", compiler, compiler_version); printf("OS: %s\n", os); printf("CPU: %s (%u bits)\n", cpu, (unsigned)(sizeof(void*))*8); #if defined(GIT) printf("GIT version: %s\n", GIT); #endif } /*************************************************************************** ***************************************************************************/ static void print_nmap_help(void) { printf("Masscan (https://github.com/robertdavidgraham/masscan)\n" "Usage: masscan [Options] -p{Target-Ports} {Target-IP-Ranges}\n" "TARGET SPECIFICATION:\n" " Can pass only IPv4/IPv6 address, CIDR networks, or ranges (non-nmap style)\n" " Ex: 10.0.0.0/8, 192.168.0.1, 10.0.0.1-10.0.0.254\n" " -iL : Input from list of hosts/networks\n" " --exclude : Exclude hosts/networks\n" " --excludefile : Exclude list from file\n" " --randomize-hosts: Randomize order of hosts (default)\n" "HOST DISCOVERY:\n" " -Pn: Treat all hosts as online (default)\n" " -n: Never do DNS resolution (default)\n" "SCAN TECHNIQUES:\n" " -sS: TCP SYN (always on, default)\n" "SERVICE/VERSION DETECTION:\n" " --banners: get the banners of the listening service if available. The\n" " default timeout for waiting to receive data is 30 seconds.\n" "PORT SPECIFICATION AND SCAN ORDER:\n" " -p : Only scan specified ports\n" " Ex: -p22; -p1-65535; -p 111,137,80,139,8080\n" "TIMING AND PERFORMANCE:\n" " --max-rate : Send packets no faster than per second\n" " --connection-timeout : time in seconds a TCP connection will\n" " timeout while waiting for banner data from a port.\n" "FIREWALL/IDS EVASION AND SPOOFING:\n" " -S/--source-ip : Spoof source address\n" " -e : Use specified interface\n" " -g/--source-port : Use given port number\n" " --ttl : Set IP time-to-live field\n" " --spoof-mac : Spoof your MAC address\n" "OUTPUT:\n" " --output-format : Sets output to binary/list/unicornscan/json/ndjson/grepable/xml\n" " --output-file : Write scan results to file. If --output-format is\n" " not given default is xml\n" " -oL/-oJ/-oD/-oG/-oB/-oX/-oU : Output scan in List/JSON/nDjson/Grepable/Binary/XML/Unicornscan format,\n" " respectively, to the given filename. Shortcut for\n" " --output-format --output-file \n" " -v: Increase verbosity level (use -vv or more for greater effect)\n" " -d: Increase debugging level (use -dd or more for greater effect)\n" " --open: Only show open (or possibly open) ports\n" " --packet-trace: Show all packets sent and received\n" " --iflist: Print host interfaces and routes (for debugging)\n" " --append-output: Append to rather than clobber specified output files\n" " --resume : Resume an aborted scan\n" "MISC:\n" " --send-eth: Send using raw ethernet frames (default)\n" " -V: Print version number\n" " -h: Print this help summary page.\n" "EXAMPLES:\n" " masscan -v -sS 192.168.0.0/16 10.0.0.0/8 -p 80\n" " masscan 23.0.0.0/0 -p80 --banners -output-format binary --output-filename internet.scan\n" " masscan --open --banners --readscan internet.scan -oG internet_scan.grepable\n" "SEE (https://github.com/robertdavidgraham/masscan) FOR MORE HELP\n" "\n"); } /*************************************************************************** ***************************************************************************/ static unsigned count_cidr6_bits(struct Range6 *range, bool *exact) { uint64_t i; /* for the comments of this function, see count_cidr_bits */ *exact = false; for (i=0; i<128; i++) { uint64_t mask_hi; uint64_t mask_lo; if (i < 64) { mask_hi = 0xFFFFFFFFffffffffull >> i; mask_lo = 0xFFFFFFFFffffffffull; } else { mask_hi = 0; mask_lo = 0xFFFFFFFFffffffffull >> (i - 64); } if ((range->begin.hi & mask_hi) != 0 || (range->begin.lo & mask_lo) != 0) { continue; } if ((range->begin.hi & ~mask_hi) == (range->end.hi & ~mask_hi) && (range->begin.lo & ~mask_lo) == (range->end.lo & ~mask_lo)) { if (((range->end.hi & mask_hi) == mask_hi) && ((range->end.lo & mask_lo) == mask_lo)) { *exact = true; return (unsigned) i; } } else { *exact = false; range->begin.hi = range->begin.hi + mask_hi; if (range->begin.lo >= 0xffffffffffffffff - 1 - mask_lo) { range->begin.hi += 1; } range->begin.lo = range->begin.lo + mask_lo + 1; return (unsigned) i; } } range->begin.lo = range->begin.lo + 1; if (range->begin.lo == 0) { range->begin.hi = range->begin.hi + 1; } return 128; } /*************************************************************************** * Echoes the configuration for one NIC ***************************************************************************/ static void masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i) { char idx_str[64]; /* If we have only one adapter, then don't print the array indexes. * Otherwise, we need to print the array indexes to distinguish * the NICs from each other */ if (masscan->nic_count <= 1) idx_str[0] = '\0'; else snprintf(idx_str, sizeof(idx_str), "[%u]", i); if (masscan->nic[i].ifname[0]) fprintf(fp, "adapter%s = %s\n", idx_str, masscan->nic[i].ifname); if (masscan->nic[i].src.ipv4.first != 0 || masscan->nic[i].src.ipv4.last != 0) { /** * FIX 495.1 for issue #495: Single adapter-ip is not saved at all * * The else case handles a simple invocation of one adapter-ip: * * 1. masscan ... --adapter-ip 1.2.3.1 ... [BROKEN] * * This looks like it was just copy pasta/typo. If the first ip is the same * as the last ip, it is a single adapter-ip * * This never worked as it was before so paused.conf would never save the * adapter-ip as it fell through this if/else if into nowhere. It probably * went undetected because in simple environments and/or in simple scans, * masscan is able to intelligently determine the adapter-ip and only * advanced usage requires overriding the chosen value. In addition to * that, it is probably relatively uncommon to interrupt a scan as not many * users are doing multi-hour / multi-day scans, having them paused and * then resuming them (apparently) */ if (masscan->nic[i].src.ipv4.first == masscan->nic[i].src.ipv4.last) { ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].src.ipv4.first); fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string); } /** * FIX 495.2 for issue #495: Ranges of size two don't print. When 495.1 is * added, ranges of size two print as only the first value in the range * Before 495.1, they didn't print at all, so this is not a bug that is * introduced by 495.1, just noticed while applying that fix * * The first if case here is for handling when adapter-ip is a range * * Examples of the multiple/range case: * * 1. masscan ... --adapter-ip 1.2.3.1-1.2.3.2 ... [BROKEN] * 2. masscan ... --adapter-ip 1.2.3.1-1.2.3.4 ... [OK] * * If the range spans exactly two adapter-ips, it will not hit the range * printing logic case here because of an off-by-one * * Changing it from < to <= fixes that issue and both of the above cases * now print the correct range as expected */ else if (masscan->nic[i].src.ipv4.first < masscan->nic[i].src.ipv4.last) { ipaddress_formatted_t fmt1 = ipv4address_fmt(masscan->nic[i].src.ipv4.first); ipaddress_formatted_t fmt2 = ipv4address_fmt(masscan->nic[i].src.ipv4.last); fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string); } } if (masscan->nic[i].src.ipv6.range) { if (ipv6address_is_lessthan(masscan->nic[i].src.ipv6.first, masscan->nic[i].src.ipv6.last)) { ipaddress_formatted_t fmt1 = ipv6address_fmt(masscan->nic[i].src.ipv6.first); ipaddress_formatted_t fmt2 = ipv6address_fmt(masscan->nic[i].src.ipv6.last); fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string); } else { ipaddress_formatted_t fmt = ipv6address_fmt(masscan->nic[i].src.ipv6.first); fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string); } } if (masscan->nic[i].my_mac_count) { ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].source_mac); fprintf(fp, "adapter-mac%s = %s\n", idx_str, fmt.string); } if (masscan->nic[i].router_ip) { ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].router_ip); fprintf(fp, "router-ip%s = %s\n", idx_str, fmt.string); } if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv4)) { ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv4); fprintf(fp, "router-mac-ipv4%s = %s\n", idx_str, fmt.string); } if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv6)) { ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv6); fprintf(fp, "router-mac-ipv6%s = %s\n", idx_str, fmt.string); } } /*************************************************************************** ***************************************************************************/ void masscan_save_state(struct Masscan *masscan) { char filename[512]; FILE *fp; safe_strcpy(filename, sizeof(filename), "paused.conf"); fprintf(stderr, " " " \r"); fprintf(stderr, "saving resume file to: %s\n", filename); fp = fopen(filename, "wt"); if (fp == NULL) { fprintf(stderr, "[-] FAIL: saving resume file\n"); fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno)); return; } masscan_echo(masscan, fp, 0); fclose(fp); } #if 0 /***************************************************************************** * Read in ranges from a file * * There can be multiple ranges on a line, delimited by spaces. In fact, * millions of ranges can be on a line: there is limit to the line length. * That makes reading the file a little bit squirrelly. From one perspective * this parser doesn't treat the new-line '\n' any different than other * space. But, from another perspective, it has to, because things like * comments are terminated by a newline. Also, it has to count the number * of lines correctly to print error messages. *****************************************************************************/ static void ranges_from_file(struct RangeList *ranges, const char *filename) { FILE *fp; unsigned line_number = 0; fp = fopen(filename, "rt"); if (fp) { perror(filename); exit(1); /* HARD EXIT: because if it's an exclusion file, we don't * want to continue. We don't want ANY chance of * accidentally scanning somebody */ } while (!feof(fp)) { int c = '\n'; /* remove leading whitespace */ while (!feof(fp)) { c = getc(fp); line_number += (c == '\n'); if (!isspace(c&0xFF)) break; } /* If this is a punctuation, like '#', then it's a comment */ if (ispunct(c&0xFF)) { while (!feof(fp)) { c = getc(fp); line_number += (c == '\n'); if (c == '\n') { break; } } /* Loop back to the begining state at the start of a line */ continue; } if (c == '\n') { continue; } /* * Read in a single entry */ if (!feof(fp)) { char address[64]; size_t i; struct Range range; unsigned offset = 0; /* Grab all bytes until the next space or comma */ address[0] = (char)c; i = 1; while (!feof(fp)) { c = getc(fp); if (c == EOF) break; line_number += (c == '\n'); if (isspace(c&0xFF) || c == ',') { break; } if (i+1 >= sizeof(address)) { LOG(0, "%s:%u:%u: bad address spec: \"%.*s\"\n", filename, line_number, offset, (int)i, address); exit(1); } else address[i] = (char)c; i++; } address[i] = '\0'; /* parse the address range */ range = range_parse_ipv4(address, &offset, (unsigned)i); if (range.begin == 0xFFFFFFFF && range.end == 0) { LOG(0, "%s:%u:%u: bad range spec: \"%.*s\"\n", filename, line_number, offset, (int)i, address); exit(1); } else { rangelist_add_range(ranges, range.begin, range.end); } } } fclose(fp); /* Target list must be sorted every time it's been changed, * before it can be used */ rangelist_sort(ranges); } #endif /*************************************************************************** ***************************************************************************/ static unsigned hexval(char c) { if ('0' <= c && c <= '9') return (unsigned)(c - '0'); if ('a' <= c && c <= 'f') return (unsigned)(c - 'a' + 10); if ('A' <= c && c <= 'F') return (unsigned)(c - 'A' + 10); return 0xFF; } /*************************************************************************** ***************************************************************************/ static int parse_mac_address(const char *text, macaddress_t *mac) { unsigned i; for (i=0; i<6; i++) { unsigned x; char c; while (isspace(*text & 0xFF) && ispunct(*text & 0xFF)) text++; c = *text; if (!isxdigit(c&0xFF)) return -1; x = hexval(c)<<4; text++; c = *text; if (!isxdigit(c&0xFF)) return -1; x |= hexval(c); text++; mac->addr[i] = (unsigned char)x; if (ispunct(*text & 0xFF)) text++; } return 0; } /*************************************************************************** ***************************************************************************/ static uint64_t parseInt(const char *str) { uint64_t result = 0; while (*str && isdigit(*str & 0xFF)) { result = result * 10 + (*str - '0'); str++; } return result; } /** * a stricter function for determining if something is boolean. */ static bool isBoolean(const char *str) { size_t length = str?strlen(str):0; if (length == 0) return false; /* "0" or "1" is boolean */ if (isdigit(str[0])) { if (strtoul(str,0,0) == 0) return true; else if (strtoul(str,0,0) == 1) return true; else return false; } switch (str[0]) { case 'e': case 'E': if (memcasecmp("enable", str, length)==0) return true; if (memcasecmp("enabled", str, length)==0) return true; return false; case 'd': case 'D': if (memcasecmp("disable", str, length)==0) return true; if (memcasecmp("disabled", str, length)==0) return true; return false; case 't': case 'T': if (memcasecmp("true", str, length)==0) return true; return false; case 'f': case 'F': if (memcasecmp("false", str, length)==0) return true; return false; case 'o': case 'O': if (memcasecmp("on", str, length)==0) return true; if (memcasecmp("off", str, length)==0) return true; return false; case 'Y': case 'y': if (memcasecmp("yes", str, length)==0) return true; return false; case 'n': case 'N': if (memcasecmp("no", str, length)==0) return true; return false; default: return false; } } static unsigned parseBoolean(const char *str) { if (str == NULL || str[0] == 0) return 1; if (isdigit(str[0])) { if (strtoul(str,0,0) == 0) return 0; else return 1; } switch (str[0]) { case 'e': /* enable */ case 'E': return 1; case 'd': /* disable */ case 'D': return 0; case 't': /* true */ case 'T': return 1; case 'f': /* false */ case 'F': return 0; case 'o': /* on or off */ case 'O': if (str[1] == 'f' || str[1] == 'F') return 0; else return 1; break; case 'Y': /* yes */ case 'y': return 1; case 'n': /* no */ case 'N': return 0; } return 1; } /*************************************************************************** * Parses the number of seconds (for rotating files mostly). We do a little * more than just parse an integer. We support strings like: * * hourly * daily * Week * 5days * 10-months * 3600 ***************************************************************************/ static uint64_t parseTime(const char *value) { uint64_t num = 0; unsigned is_negative = 0; while (*value == '-') { is_negative = 1; value++; } while (isdigit(value[0]&0xFF)) { num = num*10 + (value[0] - '0'); value++; } while (ispunct(value[0]) || isspace(value[0])) value++; if (isalpha(value[0]) && num == 0) num = 1; if (value[0] == '\0') return num; switch (tolower(value[0])) { case 's': num *= 1; break; case 'm': num *= 60; break; case 'h': num *= 60*60; break; case 'd': num *= 24*60*60; break; case 'w': num *= 24*60*60*7; break; default: fprintf(stderr, "--rotate-offset: unknown character\n"); exit(1); } if (num >= 24*60*60) { fprintf(stderr, "--rotate-offset: value is greater than 1 day\n"); exit(1); } if (is_negative) num = 24*60*60 - num; return num; } /*************************************************************************** * Parses a size integer, which can be suffixed with "tera", "giga", * "mega", and "kilo". These numbers are in units of 1024 so suck it. ***************************************************************************/ static uint64_t parseSize(const char *value) { uint64_t num = 0; while (isdigit(value[0]&0xFF)) { num = num*10 + (value[0] - '0'); value++; } while (ispunct(value[0]) || isspace(value[0])) value++; if (isalpha(value[0]) && num == 0) num = 1; if (value[0] == '\0') return num; switch (tolower(value[0])) { case 'k': /* kilobyte */ num *= 1024ULL; break; case 'm': /* megabyte */ num *= 1024ULL * 1024ULL; break; case 'g': /* gigabyte */ num *= 1024ULL * 1024ULL * 1024ULL; break; case 't': /* terabyte, 'cause we roll that way */ num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; break; case 'p': /* petabyte, 'cause we are awesome */ num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; break; case 'e': /* exabyte, now that's just silly */ num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; break; default: fprintf(stderr, "--rotate-size: unknown character\n"); exit(1); } return num; } /*************************************************************************** ***************************************************************************/ static int is_power_of_two(uint64_t x) { while ((x&1) == 0) x >>= 1; return x == 1; } /*************************************************************************** * Tests if the named parameter on the command-line. We do a little * more than a straight string compare, because I get confused * whether parameter have punctuation. Is it "--excludefile" or * "--exclude-file"? I don't know if it's got that dash. Screw it, * I'll just make the code so it don't care. ***************************************************************************/ static int EQUALS(const char *lhs, const char *rhs) { for (;;) { while (*lhs == '-' || *lhs == '.' || *lhs == '_') lhs++; while (*rhs == '-' || *rhs == '.' || *rhs == '_') rhs++; if (*lhs == '\0' && *rhs == '[') return 1; /*arrays*/ if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF)) return 0; if (*lhs == '\0') return 1; lhs++; rhs++; } } static int EQUALSx(const char *lhs, const char *rhs, size_t rhs_length) { for (;;) { while (*lhs == '-' || *lhs == '.' || *lhs == '_') lhs++; while (*rhs == '-' || *rhs == '.' || *rhs == '_') rhs++; if (*lhs == '\0' && *rhs == '[') return 1; /*arrays*/ if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF)) return 0; if (*lhs == '\0') return 1; lhs++; rhs++; if (--rhs_length == 0) return 1; } } static unsigned INDEX_OF(const char *str, char c) { unsigned i; for (i=0; str[i] && str[i] != c; i++) ; return i; } static unsigned ARRAY(const char *rhs) { const char *p = strchr(rhs, '['); if (p == NULL) return 0; else p++; return (unsigned)parseInt(p); } /** * Called if user specified `--top-ports` on the command-line. */ static void config_top_ports(struct Masscan *masscan, unsigned maxports) { unsigned i; static const unsigned short top_udp_ports[] = { 161, /* SNMP - should be found on all network equipment */ 135, /* MS-RPC - should be found on all modern Windows */ 500, /* ISAKMP - for establishing IPsec tunnels */ 137, /* NetBIOS-NameService - should be found on old Windows */ 138, /* NetBIOS-Datagram - should be found on old Windows */ 445, /* SMB datagram service */ 67, /* DHCP */ 53, /* DNS */ 1900, /* UPnP - Microsoft-focused local discovery */ 5353, /* mDNS - Apple-focused local discovery */ 4500, /* nat-t-ike - IPsec NAT traversal */ 514, /* syslog - all Unix machiens */ 69, /* TFTP */ 49152, /* first of modern ephemeral ports */ 631, /* IPP - printing protocol for Linux */ 123, /* NTP network time protocol */ 1434, /* MS-SQL server*/ 520, /* RIP - routers use this protocol sometimes */ 7, /* Echo */ 111, /* SunRPC portmapper */ 2049, /* SunRPC NFS */ 5683, /* COAP */ 11211, /* memcached */ 1701, /* L2TP */ 27960, /* quaked amplifier */ 1645, /* RADIUS */ 1812, /* RADIUS */ 1646, /* RADIUS */ 1813, /* RADIUS */ 3343, /* Microsoft Cluster Services */ 2535, /* MADCAP rfc2730 TODO FIXME */ }; static const unsigned short top_tcp_ports[] = { 80, 443, 8080, /* also web */ 21, 990, /* FTP, oldie but goodie */ 22, /* SSH, so much infrastructure */ 23, 992, /* Telnet, oldie but still around*/ 24, /* people put things here instead of TelnetSSH*/ 25, 465, 587, 2525, /* SMTP email*/ 5800, 5900, 5901, /* VNC */ 111, /* SunRPC */ 139, 445, /* Microsoft Windows networking */ 135, /* DCEPRC, more Microsoft Windows */ 3389, /* Microsoft Windows RDP */ 88, /* Kerberos, also Microsoft windows */ 389, 636, /* LDAP and MS Win */ 1433, /* MS SQL */ 53, /* DNS */ 2083, 2096, /* cPanel */ 9050, /* ToR */ 8140, /* Puppet */ 11211, /* memcached */ 1098, 1099, /* Java RMI */ 6000, 6001, /* XWindows */ 5060, 5061, /* SIP - session initiation protocool */ 554, /* RTSP */ 548, /* AFP */ 1,3,4,6,7,9,13,17,19,20,26,30,32,33,37,42,43,49,70, 79,81,82,83,84,85,89,90,99,100,106,109,110,113,119,125, 143,144,146,161,163,179,199,211,212,222,254,255,256,259,264,280, 301,306,311,340,366,406,407,416,417,425,427,444,458,464, 465,481,497,500,512,513,514,515,524,541,543,544,545,554,555,563, 593,616,617,625,631,646,648,666,667,668,683,687,691,700,705, 711,714,720,722,726,749,765,777,783,787,800,801,808,843,873,880,888, 898,900,901,902,903,911,912,981,987,993,995,999,1000,1001, 1002,1007,1009,1010,1011,1021,1022,1023,1024,1025,1026,1027,1028, 1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041, 1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054, 1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067, 1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080, 1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093, 1094,1095,1096,1097,1100,1102,1104,1105,1106,1107,1108, 1110,1111,1112,1113,1114,1117,1119,1121,1122,1123,1124,1126,1130, 1131,1132,1137,1138,1141,1145,1147,1148,1149,1151,1152,1154,1163, 1164,1165,1166,1169,1174,1175,1183,1185,1186,1187,1192,1198,1199, 1201,1213,1216,1217,1218,1233,1234,1236,1244,1247,1248,1259,1271, 1272,1277,1287,1296,1300,1301,1309,1310,1311,1322,1328,1334,1352, 1417,1434,1443,1455,1461,1494,1500,1501,1503,1521,1524,1533, 1556,1580,1583,1594,1600,1641,1658,1666,1687,1688,1700,1717,1718, 1719,1720,1721,1723,1755,1761,1782,1783,1801,1805,1812,1839,1840, 1862,1863,1864,1875,1900,1914,1935,1947,1971,1972,1974,1984,1998, 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2013, 2020,2021,2022,2030,2033,2034,2035,2038,2040,2041,2042,2043,2045, 2046,2047,2048,2049,2065,2068,2099,2100,2103,2105,2106,2107,2111, 2119,2121,2126,2135,2144,2160,2161,2170,2179,2190,2191,2196,2200, 2222,2251,2260,2288,2301,2323,2366,2381,2382,2383,2393,2394,2399, 2401,2492,2500,2522,2557,2601,2602,2604,2605,2607,2608,2638, 2701,2702,2710,2717,2718,2725,2800,2809,2811,2869,2875,2909,2910, 2920,2967,2968,2998,3000,3001,3003,3005,3006,3007,3011,3013,3017, 3030,3031,3052,3071,3077,3128,3168,3211,3221,3260,3261,3268,3269, 3283,3300,3301,3306,3322,3323,3324,3325,3333,3351,3367,3369,3370, 3371,3372,3389,3390,3404,3476,3493,3517,3527,3546,3551,3580,3659, 3689,3690,3703,3737,3766,3784,3800,3801,3809,3814,3826,3827,3828, 3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986, 3995,3998,4000,4001,4002,4003,4004,4005,4006,4045,4111,4125,4126, 4129,4224,4242,4279,4321,4343,4443,4444,4445,4446,4449,4550,4567, 4662,4848,4899,4900,4998,5000,5001,5002,5003,5004,5009,5030,5033, 5050,5051,5054,5080,5087,5100,5101,5102,5120,5190,5200, 5214,5221,5222,5225,5226,5269,5280,5298,5357,5405,5414,5431,5432, 5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678,5679, 5718,5730,5801,5802,5810,5811,5815,5822,5825,5850,5859,5862, 5877,5902,5903,5904,5906,5907,5910,5911,5915,5922,5925, 5950,5952,5959,5960,5961,5962,5963,5987,5988,5989,5998,5999, 6002,6003,6004,6005,6006,6007,6009,6025,6059,6100,6101,6106, 6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565,6566,6567, 6580,6646,6666,6667,6668,6669,6689,6692,6699,6779,6788,6789,6792, 6839,6881,6901,6969,7000,7001,7002,7004,7007,7019,7025,7070,7100, 7103,7106,7200,7201,7402,7435,7443,7496,7512,7625,7627,7676,7741, 7777,7778,7800,7911,7920,7921,7937,7938,7999,8000,8001,8002,8007, 8008,8009,8010,8011,8021,8022,8031,8042,8045,8080,8081,8082,8083, 8084,8085,8086,8087,8088,8089,8090,8093,8099,8100,8180,8181,8192, 8193,8194,8200,8222,8254,8290,8291,8292,8300,8333,8383,8400,8402, 8443,8500,8600,8649,8651,8652,8654,8701,8800,8873,8888,8899,8994, 9000,9001,9002,9003,9009,9010,9011,9040,9071,9080,9081,9090, 9091,9099,9100,9101,9102,9103,9110,9111,9200,9207,9220,9290,9415, 9418,9485,9500,9502,9503,9535,9575,9593,9594,9595,9618,9666,9876, 9877,9878,9898,9900,9917,9929,9943,9944,9968,9998,9999,10000,10001, 10002,10003,10004,10009,10010,10012,10024,10025,10082,10180,10215, 10243,10566,10616,10617,10621,10626,10628,10629,10778,11110,11111, 11967,12000,12174,12265,12345,13456,13722,13782,13783,14000,14238, 14441,14442,15000,15002,15003,15004,15660,15742,16000,16001,16012, 16016,16018,16080,16113,16992,16993,17877,17988,18040,18101,18988, 19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221, 20222,20828,21571,22939,23502,24444,24800,25734,25735,26214,27000, 27352,27353,27355,27356,27715,28201,30000,30718,30951,31038,31337, 32768,32769,32770,32771,32772,32773,32774,32775,32776,32777,32778, 32779,32780,32781,32782,32783,32784,32785,33354,33899,34571,34572, 34573,35500,38292,40193,40911,41511,42510,44176,44442,44443,44501, 45100,48080,49152,49153,49154,49155,49156,49157,49158,49159,49160, 49161,49163,49165,49167,49175,49176,49400,49999,50000,50001,50002, 50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822, 52848,52869,54045,54328,55055,55056,55555,55600,56737,56738,57294, 57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000, 65129,65389}; struct RangeList *ports = &masscan->targets.ports; static const unsigned max_tcp_ports = sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); static const unsigned max_udp_ports = sizeof(top_udp_ports)/sizeof(top_udp_ports[0]); if (masscan->scan_type.tcp) { LOG(2, "[+] adding TCP top-ports = %u\n", maxports); for (i=0; iscan_type.udp) { LOG(2, "[+] adding UDP top-ports = %u\n", maxports); for (i=0; iecho) { if (masscan->scan_type.arp || masscan->echo_all) fprintf(masscan->echo, "arpscan = %s\n", masscan->scan_type.arp?"true":"false"); return 0; } range.begin = Templ_ARP; range.end = Templ_ARP; rangelist_add_range(&masscan->targets.ports, range.begin, range.end); rangelist_sort(&masscan->targets.ports); masscan_set_parameter(masscan, "router-mac", "ff-ff-ff-ff-ff-ff"); masscan->scan_type.arp = 1; LOG(5, "--arpscan\n"); return CONF_OK; } static int SET_banners(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_banners || masscan->echo_all) fprintf(masscan->echo, "banners = %s\n", masscan->is_banners?"true":"false"); return 0; } masscan->is_banners = parseBoolean(value); return CONF_OK; } static int SET_banners_rawudp(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_banners_rawudp || masscan->echo_all) fprintf(masscan->echo, "rawudp = %s\n", masscan->is_banners_rawudp?"true":"false"); return 0; } masscan->is_banners_rawudp = parseBoolean(value); if (masscan->is_banners_rawudp) masscan->is_banners = true; return CONF_OK; } static int SET_capture(struct Masscan *masscan, const char *name, const char *value) { if (masscan->echo) { if (!masscan->is_capture_cert || masscan->echo_all) fprintf(masscan->echo, "%scapture = cert\n", masscan->is_capture_cert?"":"no"); if (!masscan->is_capture_servername || masscan->echo_all) fprintf(masscan->echo, "%scapture = servername\n", masscan->is_capture_servername?"":"no"); if (masscan->is_capture_html || masscan->echo_all) fprintf(masscan->echo, "%scapture = html\n", masscan->is_capture_html?"":"no"); if (masscan->is_capture_heartbleed || masscan->echo_all) fprintf(masscan->echo, "%scapture = heartbleed\n", masscan->is_capture_heartbleed?"":"no"); if (masscan->is_capture_ticketbleed || masscan->echo_all) fprintf(masscan->echo, "%scapture = ticketbleed\n", masscan->is_capture_ticketbleed?"":"no"); return 0; } if (EQUALS("capture", name)) { if (EQUALS("cert", value)) masscan->is_capture_cert = 1; else if (EQUALS("servername", value)) masscan->is_capture_servername = 1; else if (EQUALS("html", value)) masscan->is_capture_html = 1; else if (EQUALS("heartbleed", value)) masscan->is_capture_heartbleed = 1; else if (EQUALS("ticketbleed", value)) masscan->is_capture_ticketbleed = 1; else { fprintf(stderr, "FAIL: %s: unknown capture type\n", value); return CONF_ERR; } } else if (EQUALS("nocapture", name)) { if (EQUALS("cert", value)) masscan->is_capture_cert = 0; else if (EQUALS("servername", value)) masscan->is_capture_servername = 0; else if (EQUALS("html", value)) masscan->is_capture_html = 0; else if (EQUALS("heartbleed", value)) masscan->is_capture_heartbleed = 0; else if (EQUALS("ticketbleed", value)) masscan->is_capture_ticketbleed = 0; else { fprintf(stderr, "FAIL: %s: unknown nocapture type\n", value); return CONF_ERR; } } return CONF_OK; } static int SET_hello(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_hello_ssl) { fprintf(masscan->echo, "hello = ssl\n"); } else if (masscan->is_hello_smbv1) { fprintf(masscan->echo, "hello = smbv1\n"); } else if (masscan->is_hello_http) { fprintf(masscan->echo, "hello = http\n"); } return 0; } if (EQUALS("ssl", value)) masscan->is_hello_ssl = 1; else if (EQUALS("smbv1", value)) masscan->is_hello_smbv1 = 1; else if (EQUALS("http", value)) masscan->is_hello_http = 1; else { fprintf(stderr, "FAIL: %s: unknown hello type\n", value); return CONF_ERR; } return CONF_OK; } static int SET_hello_file(struct Masscan *masscan, const char *name, const char *value) { unsigned index; FILE *fp; char buf[16384]; char buf2[16384]; size_t bytes_read; size_t bytes_encoded; char foo[64]; if (masscan->echo) { //Echoed as a string "hello-string" that was originally read //from a file, not the "hello-filename" return 0; } index = ARRAY(name); if (index >= 65536) { fprintf(stderr, "%s: bad index\n", name); return CONF_ERR; } /* When connecting via TCP, send this file */ fp = fopen(value, "rb"); if (fp == NULL) { LOG(0, "[-] [FAILED] --hello-file\n"); LOG(0, "[-] %s: %s\n", value, strerror(errno)); return CONF_ERR; } bytes_read = fread(buf, 1, sizeof(buf), fp); if (bytes_read == 0) { LOG(0, "[FAILED] could not read hello file\n"); perror(value); fclose(fp); return CONF_ERR; } fclose(fp); bytes_encoded = base64_encode(buf2, sizeof(buf2)-1, buf, bytes_read); buf2[bytes_encoded] = '\0'; snprintf(foo, sizeof(foo), "hello-string[%u]", (unsigned)index); masscan_set_parameter(masscan, foo, buf2); return CONF_OK; } static int SET_hello_string(struct Masscan *masscan, const char *name, const char *value) { unsigned index; char *value2; struct TcpCfgPayloads *pay; if (masscan->echo) { for (pay = masscan->payloads.tcp; pay; pay = pay->next) { fprintf(masscan->echo, "hello-string[%u] = %s\n", pay->port, pay->payload_base64); } return 0; } index = ARRAY(name); if (index >= 65536) { fprintf(stderr, "%s: bad index\n", name); exit(1); } value2 = STRDUP(value); pay = MALLOC(sizeof(*pay)); pay->payload_base64 = value2; pay->port = index; pay->next = masscan->payloads.tcp; masscan->payloads.tcp = pay; return CONF_OK; } static int SET_hello_timeout(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->tcp_hello_timeout || masscan->echo_all) fprintf(masscan->echo, "hello-timeout = %u\n", masscan->tcp_hello_timeout); return 0; } masscan->tcp_hello_timeout = (unsigned)parseInt(value); return CONF_OK; } static int SET_http_cookie(struct Masscan *masscan, const char *name, const char *value) { unsigned char *newvalue; size_t value_length; UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.cookies_count || masscan->echo_all) { size_t i; for (i=0; ihttp.cookies_count; i++) { fprintf(masscan->echo, "http-cookie = %.*s\n", (unsigned)masscan->http.cookies[i].value_length, masscan->http.cookies[i].value); } } return 0; } /* allocate new value */ value_length = strlen(value); newvalue = MALLOC(value_length+1); memcpy(newvalue, value, value_length+1); newvalue[value_length] = '\0'; /* Add to our list of headers */ if (masscan->http.cookies_count < sizeof(masscan->http.cookies)/sizeof(masscan->http.cookies[0])) { size_t x = masscan->http.cookies_count; masscan->http.cookies[x].value = newvalue; masscan->http.cookies[x].value_length = value_length; masscan->http.cookies_count++; } return CONF_OK; } static int SET_http_header(struct Masscan *masscan, const char *name, const char *value) { unsigned name_length; char *newname; unsigned char *newvalue; size_t value_length; if (masscan->echo) { if (masscan->http.headers_count || masscan->echo_all) { size_t i; for (i=0; ihttp.headers_count; i++) { if (masscan->http.headers[i].name == 0) continue; fprintf(masscan->echo, "http-header = %s:%.*s\n", masscan->http.headers[i].name, (unsigned)masscan->http.headers[i].value_length, masscan->http.headers[i].value); } } return 0; } /* * allocate a new name */ name += 11; if (*name == '[') { /* Specified as: "--http-header[name] value" */ while (ispunct(*name)) name++; name_length = (unsigned)strlen(name); while (name_length && ispunct(name[name_length-1])) name_length--; newname = MALLOC(name_length+1); memcpy(newname, name, name_length+1); newname[name_length] = '\0'; } else if (strchr(value, ':')) { /* Specified as: "--http-header Name:value" */ name_length = INDEX_OF(value, ':'); newname = MALLOC(name_length + 1); memcpy(newname, value, name_length + 1); /* Trim the value */ value = value + name_length + 1; while (*value && isspace(*value & 0xFF)) value++; /* Trim the name */ while (name_length && isspace(newname[name_length-1]&0xFF)) name_length--; newname[name_length] = '\0'; } else { fprintf(stderr, "[-] --http-header needs both a name and value\n"); fprintf(stderr, " hint: \"--http-header Name:value\"\n"); exit(1); } /* allocate new value */ value_length = strlen(value); newvalue = MALLOC(value_length+1); memcpy(newvalue, value, value_length+1); newvalue[value_length] = '\0'; /* Add to our list of headers */ if (masscan->http.headers_count < sizeof(masscan->http.headers)/sizeof(masscan->http.headers[0])) { size_t x = masscan->http.headers_count; masscan->http.headers[x].name = newname; masscan->http.headers[x].value = newvalue; masscan->http.headers[x].value_length = value_length; masscan->http.headers_count++; } return CONF_OK; } static int SET_http_method(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.method || masscan->echo_all) fprintf(masscan->echo, "http-method = %.*s\n", (unsigned)masscan->http.method_length, masscan->http.method); return 0; } if (masscan->http.method) free(masscan->http.method); masscan->http.method_length = strlen(value); masscan->http.method = MALLOC(masscan->http.method_length+1); memcpy(masscan->http.method, value, masscan->http.method_length+1); return CONF_OK; } static int SET_http_url(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.url || masscan->echo_all) fprintf(masscan->echo, "http-url = %.*s\n", (unsigned)masscan->http.url_length, masscan->http.url); return 0; } if (masscan->http.url) free(masscan->http.url); masscan->http.url_length = strlen(value); masscan->http.url = MALLOC(masscan->http.url_length+1); memcpy(masscan->http.url, value, masscan->http.url_length+1); return CONF_OK; } static int SET_http_version(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.version || masscan->echo_all) fprintf(masscan->echo, "http-version = %.*s\n", (unsigned)masscan->http.version_length, masscan->http.version); return 0; } if (masscan->http.version) free(masscan->http.version); masscan->http.version_length = strlen(value); masscan->http.version = MALLOC(masscan->http.version_length+1); memcpy(masscan->http.version, value, masscan->http.version_length+1); return CONF_OK; } static int SET_http_host(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.host || masscan->echo_all) fprintf(masscan->echo, "http-host = %.*s\n", (unsigned)masscan->http.host_length, masscan->http.host); return 0; } if (masscan->http.host) free(masscan->http.host); masscan->http.host_length = strlen(value); masscan->http.host = MALLOC(masscan->http.host_length+1); memcpy(masscan->http.host, value, masscan->http.host_length+1); return CONF_OK; } static int SET_http_user_agent(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.user_agent || masscan->echo_all) fprintf(masscan->echo, "http-user-agent = %.*s\n", (unsigned)masscan->http.user_agent_length, masscan->http.user_agent); return 0; } if (masscan->http.user_agent) free(masscan->http.user_agent); masscan->http.user_agent_length = strlen(value); masscan->http.user_agent = MALLOC(masscan->http.user_agent_length+1); memcpy( masscan->http.user_agent, value, masscan->http.user_agent_length+1 ); return CONF_OK; } static int SET_http_payload(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.payload || masscan->echo_all) fprintf(masscan->echo, "http-payload = %.*s\n", (unsigned)masscan->http.payload_length, masscan->http.payload); return 0; } masscan->http.payload_length = strlen(value); masscan->http.payload = REALLOC(masscan->http.payload, masscan->http.payload_length+1); memcpy( masscan->http.payload, value, masscan->http.payload_length+1 ); return CONF_OK; } static int SET_status_ndjson(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->output.is_status_ndjson || masscan->echo_all) fprintf(masscan->echo, "ndjson-status = %s\n", masscan->output.is_status_ndjson?"true":"false"); return 0; } masscan->output.is_status_ndjson = parseBoolean(value); return CONF_OK; } static int SET_status_json(struct Masscan *masscan, const char *name, const char *value) { /* NOTE: this is here just to warn people they mistyped it */ UNUSEDPARM(name); UNUSEDPARM(value); if (masscan->echo) { return 0; } fprintf(stderr, "[-] FAIL: %s not supported, use --status-ndjson\n", name); fprintf(stderr, " hint: new-line delimited JSON status is what we use\n"); return CONF_ERR; } static int SET_min_packet(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->min_packet_size != 60 || masscan->echo_all) fprintf(masscan->echo, "min-packet = %u\n", masscan->min_packet_size); return 0; } masscan->min_packet_size = (unsigned)parseInt(value); return CONF_OK; } static int SET_nobanners(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { return 0; } masscan->is_banners = !parseBoolean(value); return CONF_OK; } static int SET_noreset(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_noreset || masscan->echo_all) fprintf(masscan->echo, "noreset = %s\n", masscan->is_noreset?"true":"false"); return 0; } masscan->is_noreset = parseBoolean(value); return CONF_OK; } static int SET_nmap_payloads(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->payloads.nmap_payloads_filename && masscan->payloads.nmap_payloads_filename[0]) || masscan->echo_all) fprintf(masscan->echo, "nmap-payloads = %s\n", masscan->payloads.nmap_payloads_filename); return 0; } if (masscan->payloads.nmap_payloads_filename) free(masscan->payloads.nmap_payloads_filename); masscan->payloads.nmap_payloads_filename = strdup(value); return CONF_OK; } static int SET_nmap_service_probes(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->payloads.nmap_service_probes_filename && masscan->payloads.nmap_service_probes_filename[0]) || masscan->echo_all) fprintf(masscan->echo, "nmap-service-probes = %s\n", masscan->payloads.nmap_service_probes_filename); return 0; } if (masscan->payloads.nmap_service_probes_filename) free(masscan->payloads.nmap_service_probes_filename); masscan->payloads.nmap_service_probes_filename = strdup(value); return CONF_OK; } static int SET_offline(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_offline || masscan->echo_all) fprintf(masscan->echo, "offline = %s\n", masscan->is_offline?"true":"false"); return 0; } masscan->is_offline = parseBoolean(value); return CONF_OK; } static int SET_output_append(struct Masscan *masscan, const char *name, const char *value) { if (masscan->echo) { if (masscan->output.is_append || masscan->echo_all) fprintf(masscan->echo, "output-append = %s\n", masscan->output.is_append?"true":"false"); return 0; } if (EQUALS("overwrite", name) || !parseBoolean(value)) masscan->output.is_append = 0; else masscan->output.is_append = 1; return CONF_OK; } static int SET_output_filename(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->output.filename[0] || masscan->echo_all) fprintf(masscan->echo, "output-filename = %s\n", masscan->output.filename); return 0; } if (masscan->output.format == 0) masscan->output.format = Output_XML; /*TODO: Why is the default XML?*/ safe_strcpy(masscan->output.filename, sizeof(masscan->output.filename), value); return CONF_OK; } static int SET_output_format(struct Masscan *masscan, const char *name, const char *value) { enum OutputFormat x = 0; UNUSEDPARM(name); if (masscan->echo) { FILE *fp = masscan->echo; ipaddress_formatted_t fmt; switch (masscan->output.format) { case Output_Default: if (masscan->echo_all) fprintf(fp, "output-format = interactive\n"); break; case Output_Interactive:fprintf(fp, "output-format = interactive\n"); break; case Output_List: fprintf(fp, "output-format = list\n"); break; case Output_Unicornscan:fprintf(fp, "output-format = unicornscan\n"); break; case Output_XML: fprintf(fp, "output-format = xml\n"); break; case Output_Binary: fprintf(fp, "output-format = binary\n"); break; case Output_Grepable: fprintf(fp, "output-format = grepable\n"); break; case Output_JSON: fprintf(fp, "output-format = json\n"); break; case Output_NDJSON: fprintf(fp, "output-format = ndjson\n"); break; case Output_Certs: fprintf(fp, "output-format = certs\n"); break; case Output_None: fprintf(fp, "output-format = none\n"); break; case Output_Hostonly: fprintf(fp, "output-format = hostonly\n"); break; case Output_Redis: fmt = ipaddress_fmt(masscan->redis.ip); fprintf(fp, "output-format = redis\n"); fprintf(fp, "redis = %s %u\n", fmt.string, masscan->redis.port); break; default: fprintf(fp, "output-format = unknown(%u)\n", masscan->output.format); break; } return 0; } if (EQUALS("unknown(0)", value)) x = Output_Interactive; else if (EQUALS("interactive", value)) x = Output_Interactive; else if (EQUALS("list", value)) x = Output_List; else if (EQUALS("unicornscan", value)) x = Output_Unicornscan; else if (EQUALS("xml", value)) x = Output_XML; else if (EQUALS("binary", value)) x = Output_Binary; else if (EQUALS("greppable", value)) x = Output_Grepable; else if (EQUALS("grepable", value)) x = Output_Grepable; else if (EQUALS("json", value)) x = Output_JSON; else if (EQUALS("ndjson", value)) x = Output_NDJSON; else if (EQUALS("certs", value)) x = Output_Certs; else if (EQUALS("none", value)) x = Output_None; else if (EQUALS("redis", value)) x = Output_Redis; else if (EQUALS("hostonly", value)) x = Output_Hostonly; else { LOG(0, "FAIL: unknown output-format: %s\n", value); LOG(0, " hint: 'binary', 'xml', 'grepable', ...\n"); return CONF_ERR; } masscan->output.format = x; return CONF_OK; } static int SET_output_noshow(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->echo_all) { fprintf(masscan->echo, "output-noshow = %s%s%s\n", (!masscan->output.is_show_open)?"open,":"", (!masscan->output.is_show_closed)?"closed,":"", (!masscan->output.is_show_host)?"host,":"" ); } return 0; } for (;;) { const char *val2 = value; unsigned val2_len = INDEX_OF(val2, ','); if (val2_len == 0) break; if (EQUALSx("open", val2, val2_len)) masscan->output.is_show_open = 0; else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len)) masscan->output.is_show_closed = 0; else if (EQUALSx("open", val2, val2_len)) masscan->output.is_show_host = 0; else if (EQUALSx("all",val2,val2_len)) { masscan->output.is_show_open = 0; masscan->output.is_show_host = 0; masscan->output.is_show_closed = 0; } else { LOG(0, "FAIL: unknown 'noshow' spec: %.*s\n", val2_len, val2); exit(1); } value += val2_len; while (*value == ',') value++; } return CONF_OK; } static int SET_output_show(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->echo_all) { fprintf(masscan->echo, "output-show = %s%s%s\n", masscan->output.is_show_open?"open,":"", masscan->output.is_show_closed?"closed,":"", masscan->output.is_show_host?"host,":"" ); } return 0; } for (;;) { const char *val2 = value; unsigned val2_len = INDEX_OF(val2, ','); if (val2_len == 0) break; if (EQUALSx("open", val2, val2_len)) masscan->output.is_show_open = 1; else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len)) masscan->output.is_show_closed = 1; else if (EQUALSx("open", val2, val2_len)) masscan->output.is_show_host = 1; else if (EQUALSx("all",val2,val2_len)) { masscan->output.is_show_open = 1; masscan->output.is_show_host = 1; masscan->output.is_show_closed = 1; } else { LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2); exit(1); } value += val2_len; while (*value == ',') value++; } return CONF_OK; } static int SET_output_show_open(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); UNUSEDPARM(value); if (masscan->echo) { return 0; } /* "open" "open-only" */ masscan->output.is_show_open = 1; masscan->output.is_show_closed = 0; masscan->output.is_show_host = 0; return CONF_OK; } /* Specifies a 'libpcap' file where the received packets will be written. * This is useful while debugging so that we can see what exactly is * going on. It's also an alternate mode for getting output from this * program. Instead of relying upon this program's determination of what * ports are open or closed, you can instead simply parse this capture * file yourself and make your own determination */ static int SET_pcap_filename(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->pcap_filename[0]) fprintf(masscan->echo, "pcap-filename = %s\n", masscan->pcap_filename); return 0; } if (value) safe_strcpy(masscan->pcap_filename, sizeof(masscan->pcap_filename), value); return CONF_OK; } /* Specifies a 'libpcap' file from which to read packet-payloads. The payloads found * in this file will serve as the template for spewing out custom packets. There are * other options that can set payloads as well, like "--nmap-payloads" for reading * their custom payload file, as well as the various "hello" options for specifying * the string sent to the server once a TCP connection has been established. */ static int SET_pcap_payloads(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->payloads.pcap_payloads_filename && masscan->payloads.pcap_payloads_filename[0]) || masscan->echo_all) fprintf(masscan->echo, "pcap-payloads = %s\n", masscan->payloads.pcap_payloads_filename); return 0; } if (masscan->payloads.pcap_payloads_filename) free(masscan->payloads.pcap_payloads_filename); masscan->payloads.pcap_payloads_filename = strdup(value); /* file will be loaded in "masscan_load_database_files()" */ return CONF_OK; } static int SET_randomize_hosts(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); UNUSEDPARM(value); if (masscan->echo) { //fprintf(masscan->echo, "randomize-hosts = true\n"); return 0; } return CONF_OK; } static int SET_rate(struct Masscan *masscan, const char *name, const char *value) { double rate = 0.0; double point = 10.0; unsigned i; if (masscan->echo) { if ((unsigned)(masscan->max_rate * 100000) % 100000) { /* print as floating point number, which is rare */ fprintf(masscan->echo, "rate = %f\n", masscan->max_rate); } else { /* pretty print as just an integer, which is what most people * expect */ fprintf(masscan->echo, "rate = %-10.0f\n", masscan->max_rate); } return 0; } for (i=0; value[i] && value[i] != '.'; i++) { char c = value[i]; if (c < '0' || '9' < c) { fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value); return CONF_ERR; } rate = rate * 10.0 + (c - '0'); } if (value[i] == '.') { i++; while (value[i]) { char c = value[i]; if (c < '0' || '9' < c) { fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value); return CONF_ERR; } rate += (c - '0')/point; point *= 10.0; value++; } } masscan->max_rate = rate; return CONF_OK; } static int SET_resume_count(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->resume.count || masscan->echo_all) { fprintf(masscan->echo, "resume-count = %" PRIu64 "\n", masscan->resume.count); } return 0; } masscan->resume.count = parseInt(value); return CONF_OK; } static int SET_resume_index(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->resume.index || masscan->echo_all) { fprintf(masscan->echo, "\n# resume information\n"); fprintf(masscan->echo, "resume-index = %" PRIu64 "\n", masscan->resume.index); } return 0; } masscan->resume.index = parseInt(value); return CONF_OK; } static int SET_retries(struct Masscan *masscan, const char *name, const char *value) { uint64_t x; UNUSEDPARM(name); if (masscan->echo) { if (masscan->retries || masscan->echo_all) fprintf(masscan->echo, "retries = %u\n", masscan->retries); return 0; } x = strtoul(value, 0, 0); if (x >= 1000) { fprintf(stderr, "FAIL: retries=: expected number less than 1000\n"); return CONF_ERR; } masscan->retries = (unsigned)x; return CONF_OK; } static int SET_rotate_time(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->output.rotate.timeout || masscan->echo_all) fprintf(masscan->echo, "rotate = %u\n", masscan->output.rotate.timeout); return 0; } masscan->output.rotate.timeout = (unsigned)parseTime(value); return CONF_OK; } static int SET_rotate_directory(struct Masscan *masscan, const char *name, const char *value) { char *p; UNUSEDPARM(name); if (masscan->echo) { if (memcmp(masscan->output.rotate.directory, ".",2) != 0 || masscan->echo_all) { fprintf(masscan->echo, "rotate-dir = %s\n", masscan->output.rotate.directory); } return 0; } safe_strcpy( masscan->output.rotate.directory, sizeof(masscan->output.rotate.directory), value); /* strip trailing slashes */ p = masscan->output.rotate.directory; while (*p && (p[strlen(p)-1] == '/' || p[strlen(p)-1] == '\\')) /* Fix for #561 */ p[strlen(p)-1] = '\0'; return CONF_OK; } static int SET_rotate_offset(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); /* Time offset, otherwise output files are aligned to nearest time * interval, e.g. at the start of the hour for "hourly" */ if (masscan->echo) { if (masscan->output.rotate.offset || masscan->echo_all) fprintf(masscan->echo, "rotate-offset = %u\n", masscan->output.rotate.offset); return 0; } masscan->output.rotate.offset = (unsigned)parseTime(value); return CONF_OK; } static int SET_rotate_filesize(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->output.rotate.filesize || masscan->echo_all) fprintf(masscan->echo, "rotate-size = %" PRIu64 "\n", masscan->output.rotate.filesize); return 0; } masscan->output.rotate.filesize = parseSize(value); return CONF_OK; } static int SET_script(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->scripting.name && masscan->scripting.name[0]) || masscan->echo_all) fprintf(masscan->echo, "script = %s\n", masscan->scripting.name); return 0; } if (value && value[0]) masscan->is_scripting = 1; else masscan->is_scripting = 0; if (masscan->scripting.name) free(masscan->scripting.name); masscan->scripting.name = strdup(value); return CONF_OK; } static int SET_seed(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { fprintf(masscan->echo, "seed = %" PRIu64 "\n", masscan->seed); return 0; } if (EQUALS("time", value)) masscan->seed = time(0); else masscan->seed = parseInt(value); return CONF_OK; } static int SET_space(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); UNUSEDPARM(value); if (masscan->echo) { fprintf(masscan->echo, "\n"); return 0; } return CONF_OK; } static int SET_shard(struct Masscan *masscan, const char *name, const char *value) { unsigned one = 0; unsigned of = 0; UNUSEDPARM(name); if (masscan->echo) { if (masscan->shard.of > 0 || masscan->echo_all) fprintf(masscan->echo, "shard = %u/%u\n", masscan->shard.one, masscan->shard.of); return 0; } while (isdigit(*value)) one = one*10 + (*(value++)) - '0'; while (ispunct(*value)) value++; while (isdigit(*value)) of = of*10 + (*(value++)) - '0'; if (one < 1) { LOG(0, "FAIL: shard index can't be zero\n"); LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n"); return CONF_ERR; } if (one > of) { LOG(0, "FAIL: shard spec is wrong\n"); LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n"); return CONF_ERR; } masscan->shard.one = one; masscan->shard.of = of; return CONF_OK; } static int SET_output_stylesheet(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->output.stylesheet[0] || masscan->echo_all) fprintf(masscan->echo, "stylesheet = %s\n", masscan->output.stylesheet); return 0; } if (masscan->output.format == 0) masscan->output.format = Output_XML; safe_strcpy(masscan->output.stylesheet, sizeof(masscan->output.stylesheet), value); return CONF_OK; } static int SET_topports(struct Masscan *masscan, const char *name, const char *value) { unsigned default_value = 20; if (masscan->echo) { /* don't echo: this instead triggers filling the `--port` * list, so the ports themselves will be echoed, not this * parameter */ return 0; } if (value == 0 || value[0] == '\0') { /* can be specified by itself on the command-line, alone * without a following parameter */ /* ex: `--top-ports` */ masscan->top_ports = default_value; } else if (isBoolean(value)) { /* ex: `--top-ports enable` */ if (parseBoolean(value)) masscan->top_ports = default_value; else masscan->top_ports = 0; } else if (isInteger(value)) { /* ex: `--top-ports 5` */ uint64_t num = parseInt(value); masscan->top_ports = (unsigned)num; } else { fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } return CONF_OK; } static int SET_tcp_mss(struct Masscan *masscan, const char *name, const char *value) { /* h/t @IvreRocks */ static const unsigned default_mss = 1460; if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_mss) { case Default: break; case Add: if (masscan->templ_opts->tcp.mss == default_mss) fprintf(masscan->echo, "tcp-mss = %s\n", "enable"); else fprintf(masscan->echo, "tcp-mss = %u\n", masscan->templ_opts->tcp.mss); break; case Remove: fprintf(masscan->echo, "tcp-mss = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { /* no following parameter, so interpret this to mean "enable" */ masscan->templ_opts->tcp.is_mss = Add; masscan->templ_opts->tcp.mss = default_mss; /* 1460 */ } else if (isBoolean(value)) { /* looking for "enable" or "disable", but any boolean works, * like "true/false" or "off/on" */ if (parseBoolean(value)) { masscan->templ_opts->tcp.is_mss = Add; masscan->templ_opts->tcp.mss = default_mss; /* 1460 */ } else masscan->templ_opts->tcp.is_mss = Remove; } else if (isInteger(value)) { /* A specific number was specified */ uint64_t num = parseInt(value); if (num >= 0x10000) goto fail; masscan->templ_opts->tcp.is_mss = Add; masscan->templ_opts->tcp.mss = (unsigned)num; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_tcp_wscale(struct Masscan *masscan, const char *name, const char *value) { static const unsigned default_value = 3; if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_wscale) { case Default: break; case Add: if (masscan->templ_opts->tcp.wscale == default_value) fprintf(masscan->echo, "tcp-wscale = %s\n", "enable"); else fprintf(masscan->echo, "tcp-wscale = %u\n", masscan->templ_opts->tcp.wscale); break; case Remove: fprintf(masscan->echo, "tcp-wscale = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { masscan->templ_opts->tcp.is_wscale = Add; masscan->templ_opts->tcp.wscale = default_value; } else if (isBoolean(value)) { if (parseBoolean(value)) { masscan->templ_opts->tcp.is_wscale = Add; masscan->templ_opts->tcp.wscale = default_value; } else masscan->templ_opts->tcp.is_wscale = Remove; } else if (isInteger(value)) { uint64_t num = parseInt(value); if (num >= 255) goto fail; masscan->templ_opts->tcp.is_wscale = Add; masscan->templ_opts->tcp.wscale = (unsigned)num; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_tcp_tsecho(struct Masscan *masscan, const char *name, const char *value) { static const unsigned default_value = 0x12345678; if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_tsecho) { case Default: break; case Add: if (masscan->templ_opts->tcp.tsecho == default_value) fprintf(masscan->echo, "tcp-tsecho = %s\n", "enable"); else fprintf(masscan->echo, "tcp-tsecho = %u\n", masscan->templ_opts->tcp.tsecho); break; case Remove: fprintf(masscan->echo, "tcp-tsecho = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { masscan->templ_opts->tcp.is_tsecho = Add; masscan->templ_opts->tcp.tsecho = default_value; } else if (isBoolean(value)) { if (parseBoolean(value)) { masscan->templ_opts->tcp.is_tsecho = Add; masscan->templ_opts->tcp.tsecho = default_value; } else masscan->templ_opts->tcp.is_tsecho = Remove; } else if (isInteger(value)) { uint64_t num = parseInt(value); if (num >= 255) goto fail; masscan->templ_opts->tcp.is_tsecho = Add; masscan->templ_opts->tcp.tsecho = (unsigned)num; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_tcp_sackok(struct Masscan *masscan, const char *name, const char *value) { if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_sackok) { case Default: break; case Add: fprintf(masscan->echo, "tcp-sackok = %s\n", "enable"); break; case Remove: fprintf(masscan->echo, "tcp-sackok = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { masscan->templ_opts->tcp.is_sackok = Add; } else if (isBoolean(value)) { if (parseBoolean(value)) { masscan->templ_opts->tcp.is_sackok = Add; } else masscan->templ_opts->tcp.is_sackok = Remove; } else if (isInteger(value)) { if (parseInt(value) != 0) masscan->templ_opts->tcp.is_sackok = Add; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_debug_tcp(struct Masscan *masscan, const char *name, const char *value) { extern int is_tcp_debug; /* global */ UNUSEDPARM(name); UNUSEDPARM(masscan); if (value == 0 || value[0] == '\0') is_tcp_debug = 1; else is_tcp_debug = parseBoolean(value); return CONF_OK; } struct ConfigParameter { const char *name; SET_PARAMETER set; unsigned flags; const char *alts[6]; }; enum {F_NONE, F_BOOL=1, F_NUMABLE=2}; struct ConfigParameter config_parameters[] = { {"resume-index", SET_resume_index, 0, {0}}, {"resume-count", SET_resume_count, 0, {0}}, {"seed", SET_seed, 0, {0}}, {"arpscan", SET_arpscan, F_BOOL, {"arp",0}}, {"randomize-hosts", SET_randomize_hosts, F_BOOL, {0}}, {"rate", SET_rate, 0, {"max-rate",0}}, {"shard", SET_shard, 0, {"shards",0}}, {"banners", SET_banners, F_BOOL, {"banner",0}}, /* --banners */ {"rawudp", SET_banners_rawudp, F_BOOL, {"rawudp",0}}, /* --rawudp */ {"nobanners", SET_nobanners, F_BOOL, {"nobanner",0}}, {"retries", SET_retries, 0, {"retry", "max-retries", "max-retry", 0}}, {"noreset", SET_noreset, F_BOOL, {0}}, {"nmap-payloads", SET_nmap_payloads, 0, {"nmap-payload",0}}, {"nmap-service-probes",SET_nmap_service_probes, 0, {"nmap-service-probe",0}}, {"offline", SET_offline, F_BOOL, {"notransmit", "nosend", "dry-run", 0}}, {"pcap-filename", SET_pcap_filename, 0, {"pcap",0}}, {"pcap-payloads", SET_pcap_payloads, 0, {"pcap-payload",0}}, {"hello", SET_hello, 0, {0}}, {"hello-file", SET_hello_file, 0, {"hello-filename",0}}, {"hello-string", SET_hello_string, 0, {0}}, {"hello-timeout", SET_hello_timeout, 0, {0}}, {"http-cookie", SET_http_cookie, 0, {0}}, {"http-header", SET_http_header, 0, {"http-field", 0}}, {"http-method", SET_http_method, 0, {0}}, {"http-version", SET_http_version, 0, {0}}, {"http-url", SET_http_url, 0, {"http-uri",0}}, {"http-user-agent", SET_http_user_agent, 0, {"http-useragent",0}}, {"http-host", SET_http_host, 0, {0}}, {"http-payload", SET_http_payload, 0, {0}}, {"ndjson-status", SET_status_ndjson, F_BOOL, {"status-ndjson", 0}}, {"json-status", SET_status_json, F_BOOL, {"status-json", 0}}, {"min-packet", SET_min_packet, 0, {"min-pkt",0}}, {"capture", SET_capture, 0, {0}}, {"nocapture", SET_capture, 0, {"no-capture", 0}}, {"SPACE", SET_space, 0, {0}}, {"output-filename", SET_output_filename, 0, {"output-file",0}}, {"output-format", SET_output_format, 0, {0}}, {"output-show", SET_output_show, 0, {"output-status", "show",0}}, {"output-noshow", SET_output_noshow, 0, {"noshow",0}}, {"output-show-open",SET_output_show_open, F_BOOL, {"open", "open-only", 0}}, {"output-append", SET_output_append, 0, {"append-output",0}}, {"rotate", SET_rotate_time, 0, {"output-rotate", "rotate-output", "rotate-time", 0}}, {"rotate-dir", SET_rotate_directory, 0, {"output-rotate-dir", "rotate-directory", 0}}, {"rotate-offset", SET_rotate_offset, 0, {"output-rotate-offset", 0}}, {"rotate-size", SET_rotate_filesize, 0, {"output-rotate-filesize", "rotate-filesize", 0}}, {"stylesheet", SET_output_stylesheet, 0, {0}}, {"script", SET_script, 0, {0}}, {"SPACE", SET_space, 0, {0}}, {"tcp-mss", SET_tcp_mss, F_NUMABLE, {"tcpmss",0}}, {"tcp-wscale", SET_tcp_wscale, F_NUMABLE, {0}}, {"tcp-tsecho", SET_tcp_tsecho, F_NUMABLE, {0}}, {"tcp-sackok", SET_tcp_sackok, F_BOOL, {0}}, {"top-ports", SET_topports, F_NUMABLE, {"top-port",0}}, {"debug-tcp", SET_debug_tcp, F_BOOL, {"tcp-debug", 0}}, {0} }; /*************************************************************************** * Called either from the "command-line" parser when it sees a --param, * or from the "config-file" parser for normal options. ***************************************************************************/ void masscan_set_parameter(struct Masscan *masscan, const char *name, const char *value) { unsigned index = ARRAY(name); if (index >= 65536) { fprintf(stderr, "%s: bad index\n", name); exit(1); } /* * NEW: * Go through configured list of parameters */ { size_t i; for (i=0; config_parameters[i].name; i++) { if (EQUALS(config_parameters[i].name, name)) { config_parameters[i].set(masscan, name, value); return; } else { size_t j; for (j=0; config_parameters[i].alts[j]; j++) { if (EQUALS(config_parameters[i].alts[j], name)) { config_parameters[i].set(masscan, name, value); return; } } } } } /* * OLD: * Configure the old parameters, the ones we don't have in the new config * system yet (see the NEW part above). * TODO: transition all these old params to the new system */ if (EQUALS("conf", name) || EQUALS("config", name)) { masscan_read_config_file(masscan, value); } else if (EQUALS("adapter", name) || EQUALS("if", name) || EQUALS("interface", name)) { if (masscan->nic[index].ifname[0]) { fprintf(stderr, "CONF: overwriting \"adapter=%s\"\n", masscan->nic[index].ifname); } if (masscan->nic_count < index + 1) masscan->nic_count = index + 1; snprintf( masscan->nic[index].ifname, sizeof(masscan->nic[index].ifname), "%s", value); } else if (EQUALS("adapter-ip", name) || EQUALS("source-ip", name) || EQUALS("source-address", name) || EQUALS("spoof-ip", name) || EQUALS("spoof-address", name) || EQUALS("src-ip", name)) { /* Send packets FROM this IP address */ struct Range range; struct Range6 range6; int err; /* Grab the next IPv4 or IPv6 range */ err = massip_parse_range(value, 0, 0, &range, &range6); switch (err) { case Ipv4_Address: /* If more than one IP address given, make the range is * an even power of two (1, 2, 4, 8, 16, ...) */ if (!is_power_of_two((uint64_t)range.end - range.begin + 1)) { LOG(0, "FAIL: range must be even power of two: %s=%s\n", name, value); exit(1); } masscan->nic[index].src.ipv4.first = range.begin; masscan->nic[index].src.ipv4.last = range.end; masscan->nic[index].src.ipv4.range = range.end - range.begin + 1; break; case Ipv6_Address: masscan->nic[index].src.ipv6.first = range6.begin; masscan->nic[index].src.ipv6.last = range6.end; masscan->nic[index].src.ipv6.range = 1; /* TODO: add support for more than one source */ break; default: LOG(0, "FAIL: bad source IP address: %s=%s\n", name, value); LOG(0, "hint addresses look like \"192.168.1.23\" or \"2001:db8:1::1ce9\".\n"); exit(1); } } else if (EQUALS("adapter-port", name) || EQUALS("source-port", name) || EQUALS("src-port", name)) { /* Send packets FROM this port number */ unsigned is_error = 0; struct RangeList ports = {0}; memset(&ports, 0, sizeof(ports)); rangelist_parse_ports(&ports, value, &is_error, 0); /* Check if there was an error in parsing */ if (is_error) { LOG(0, "FAIL: bad source port specification: %s\n", name); exit(1); } /* Only allow one range of ports */ if (ports.count != 1) { LOG(0, "FAIL: only one '%s' range may be specified, found %u ranges\n", name, ports.count); exit(1); } /* verify range is even power of 2 (1, 2, 4, 8, 16, ...) */ if (!is_power_of_two(ports.list[0].end - ports.list[0].begin + 1)) { LOG(0, "FAIL: source port range must be even power of two: %s=%s\n", name, value); exit(1); } masscan->nic[index].src.port.first = ports.list[0].begin; masscan->nic[index].src.port.last = ports.list[0].end; masscan->nic[index].src.port.range = ports.list[0].end - ports.list[0].begin + 1; } else if (EQUALS("adapter-mac", name) || EQUALS("spoof-mac", name) || EQUALS("source-mac", name) || EQUALS("src-mac", name)) { /* Send packets FROM this MAC address */ macaddress_t source_mac; int err; err = parse_mac_address(value, &source_mac); if (err) { LOG(0, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } /* Check for duplicates */ if (macaddress_is_equal(masscan->nic[index].source_mac, source_mac)) { /* suppresses warning message about duplicate MAC addresses if * they are in fact the same */ return; } /* Warn if we are overwriting a Mac address */ if (masscan->nic[index].my_mac_count != 0) { ipaddress_formatted_t fmt1 = macaddress_fmt(masscan->nic[index].source_mac); ipaddress_formatted_t fmt2 = macaddress_fmt(source_mac); LOG(0, "[-] WARNING: overwriting MAC address, was %s, now %s\n", fmt1.string, fmt2.string); } masscan->nic[index].source_mac = source_mac; masscan->nic[index].my_mac_count = 1; } else if (EQUALS("router-mac", name) || EQUALS("router", name) || EQUALS("dest-mac", name) || EQUALS("destination-mac", name) || EQUALS("dst-mac", name) || EQUALS("target-mac", name)) { macaddress_t router_mac; int err; err = parse_mac_address(value, &router_mac); if (err) { fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } masscan->nic[index].router_mac_ipv4 = router_mac; masscan->nic[index].router_mac_ipv6 = router_mac; } else if (EQUALS("router-mac-ipv4", name) || EQUALS("router-ipv4", name)) { macaddress_t router_mac; int err; err = parse_mac_address(value, &router_mac); if (err) { fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } masscan->nic[index].router_mac_ipv4 = router_mac; } else if (EQUALS("router-mac-ipv6", name) || EQUALS("router-ipv6", name)) { macaddress_t router_mac; int err; err = parse_mac_address(value, &router_mac); if (err) { fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } masscan->nic[index].router_mac_ipv6 = router_mac; } else if (EQUALS("router-ip", name)) { /* Send packets FROM this IP address */ struct Range range; range = range_parse_ipv4(value, 0, 0); /* Check for bad format */ if (range.begin != range.end) { LOG(0, "FAIL: bad source IPv4 address: %s=%s\n", name, value); LOG(0, "hint addresses look like \"19.168.1.23\"\n"); exit(1); } masscan->nic[index].router_ip = range.begin; } else if (EQUALS("udp-ports", name) || EQUALS("udp-port", name)) { unsigned is_error = 0; masscan->scan_type.udp = 1; rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_UDP); if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("oprotos", name) || EQUALS("oproto", name)) { unsigned is_error = 0; masscan->scan_type.oproto = 1; rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_Oproto_first); if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("tcp-ports", name) || EQUALS("tcp-port", name)) { unsigned is_error = 0; masscan->scan_type.tcp = 1; rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_TCP); if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("ports", name) || EQUALS("port", name) || EQUALS("dst-port", name) || EQUALS("dest-port", name) || EQUALS("destination-port", name) || EQUALS("target-port", name)) { unsigned defaultrange = 0; int err; if (masscan->scan_type.udp) defaultrange = Templ_UDP; else if (masscan->scan_type.sctp) defaultrange = Templ_SCTP; err = massip_add_port_string(&masscan->targets, value, defaultrange); if (err) { fprintf(stderr, "[-] FAIL: bad target port: %s\n", value); fprintf(stderr, " Hint: a port is a number [0..65535]\n"); exit(1); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("banner-types", name) || EQUALS("banner-type", name) || EQUALS("banner-apps", name) || EQUALS("banner-app", name) ) { enum ApplicationProtocol app; app = masscan_string_to_app(value); if (app) { rangelist_add_range(&masscan->banner_types, app, app); rangelist_sort(&masscan->banner_types); } else { LOG(0, "FAIL: bad banner app: %s\n", value); fprintf(stderr, "err\n"); exit(1); } } else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) { unsigned defaultrange = 0; int err; if (masscan->scan_type.udp) defaultrange = Templ_UDP; else if (masscan->scan_type.sctp) defaultrange = Templ_SCTP; err = massip_add_port_string(&masscan->exclude, value, defaultrange); if (err) { fprintf(stderr, "[-] FAIL: bad exclude port: %s\n", value); fprintf(stderr, " Hint: a port is a number [0..65535]\n"); exit(1); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("bpf", name)) { size_t len = strlen(value) + 1; if (masscan->bpf_filter) free(masscan->bpf_filter); masscan->bpf_filter = MALLOC(len); memcpy(masscan->bpf_filter, value, len); } else if (EQUALS("ping", name) || EQUALS("ping-sweep", name)) { /* Add ICMP ping request */ struct Range range; range.begin = Templ_ICMP_echo; range.end = Templ_ICMP_echo; rangelist_add_range(&masscan->targets.ports, range.begin, range.end); rangelist_sort(&masscan->targets.ports); masscan->scan_type.ping = 1; LOG(5, "--ping\n"); } else if (EQUALS("range", name) || EQUALS("ranges", name) || EQUALS("ip", name) || EQUALS("ipv4", name) || EQUALS("dst-ip", name) || EQUALS("dest-ip", name) || EQUALS("destination-ip", name) || EQUALS("target-ip", name)) { int err; err = massip_add_target_string(&masscan->targets, value); if (err) { fprintf(stderr, "ERROR: bad IP address/range: %s\n", value); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if ( EQUALS("exclude", name) || EQUALS("exclude-range", name) || EQUALS("exclude-ranges", name) || EQUALS("exclude-ip", name) || EQUALS("exclude-ipv4", name) ) { int err; err = massip_add_target_string(&masscan->exclude, value); if (err) { fprintf(stderr, "ERROR: bad exclude address/range: %s\n", value); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("badsum", name)) { masscan->nmap.badsum = 1; } else if (EQUALS("banner1", name)) { banner1_test(value); exit(1); } else if (EQUALS("blackrock-rounds", name)) { masscan->blackrock_rounds = (unsigned)parseInt(value); } else if (EQUALS("connection-timeout", name) || EQUALS("tcp-timeout", name)) { /* The timeout for banners TCP connections */ masscan->tcp_connection_timeout = (unsigned)parseInt(value); } else if (EQUALS("datadir", name)) { safe_strcpy(masscan->nmap.datadir, sizeof(masscan->nmap.datadir), value); } else if (EQUALS("data-length", name)) { unsigned x = (unsigned)strtoul(value, 0, 0); if (x >= 1514 - 14 - 40) { fprintf(stderr, "error: %s=: expected number less than 1500\n", name); } else { masscan->nmap.data_length = x; } } else if (EQUALS("debug", name)) { if (EQUALS("if", value)) { masscan->op = Operation_DebugIF; } } else if (EQUALS("dns-servers", name)) { fprintf(stderr, "nmap(%s): unsupported: DNS lookups too synchronous\n", name); exit(1); } else if (EQUALS("echo", name)) { masscan->op = Operation_Echo; } else if (EQUALS("echo-all", name)) { masscan->op = Operation_EchoAll; } else if (EQUALS("echo-cidr", name)) { masscan->op = Operation_EchoCidr; } else if (EQUALS("excludefile", name)) { unsigned count1 = masscan->exclude.ipv4.count; unsigned count2; int err; const char *filename = value; LOG(1, "EXCLUDING: %s\n", value); err = massip_parse_file(&masscan->exclude, filename); if (err) { LOG(0, "[-] FAIL: error reading from exclude file\n"); exit(1); } /* Detect if this file has made any change, otherwise don't print * a message */ count2 = masscan->exclude.ipv4.count; if (count2 - count1) fprintf(stderr, "%s: excluding %u ranges from file\n", value, count2 - count1); } else if (EQUALS("heartbleed", name)) { masscan->is_heartbleed = 1; masscan_set_parameter(masscan, "no-capture", "cert"); masscan_set_parameter(masscan, "no-capture", "heartbleed"); masscan_set_parameter(masscan, "banners", "true"); } else if (EQUALS("ticketbleed", name)) { masscan->is_ticketbleed = 1; masscan_set_parameter(masscan, "no-capture", "cert"); masscan_set_parameter(masscan, "no-capture", "ticketbleed"); masscan_set_parameter(masscan, "banners", "true"); } else if (EQUALS("host-timeout", name)) { fprintf(stderr, "nmap(%s): unsupported: this is an asynchronous tool, so no timeouts\n", name); exit(1); } else if (EQUALS("iflist", name)) { masscan->op = Operation_List_Adapters; } else if (EQUALS("includefile", name)) { int err; const char *filename = value; err = massip_parse_file(&masscan->targets, filename); if (err) { LOG(0, "[-] FAIL: error reading from include file\n"); exit(1); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("infinite", name)) { masscan->is_infinite = 1; } else if (EQUALS("interactive", name)) { masscan->output.is_interactive = 1; } else if (EQUALS("nointeractive", name)) { masscan->output.is_interactive = 0; } else if (EQUALS("status", name)) { masscan->output.is_status_updates = 1; } else if (EQUALS("nostatus", name)) { masscan->output.is_status_updates = 0; } else if (EQUALS("ip-options", name)) { fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name); exit(1); } else if (EQUALS("log-errors", name)) { fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name); exit(1); } else if (EQUALS("min-hostgroup", name) || EQUALS("max-hostgroup", name)) { fprintf(stderr, "nmap(%s): unsupported: we randomize all the groups!\n", name); exit(1); } else if (EQUALS("min-parallelism", name) || EQUALS("max-parallelism", name)) { fprintf(stderr, "nmap(%s): unsupported: we all the parallel!\n", name); exit(1); } else if (EQUALS("min-rtt-timeout", name) || EQUALS("max-rtt-timeout", name) || EQUALS("initial-rtt-timeout", name)) { fprintf(stderr, "nmap(%s): unsupported: we are asynchronous, so no timeouts, no RTT tracking!\n", name); exit(1); } else if (EQUALS("min-rate", name)) { fprintf(stderr, "nmap(%s): unsupported, we go as fast as --max-rate allows\n", name); /* no exit here, since it's just info */ } else if (EQUALS("mtu", name)) { fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", name); exit(1); } else if (EQUALS("nmap", name)) { print_nmap_help(); exit(1); } else if (EQUALS("osscan-limit", name)) { fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name); exit(1); } else if (EQUALS("osscan-guess", name)) { fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name); exit(1); } else if (EQUALS("packet-trace", name) || EQUALS("trace-packet", name)) { masscan->nmap.packet_trace = 1; } else if (EQUALS("privileged", name) || EQUALS("unprivileged", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("pfring", name)) { masscan->is_pfring = 1; } else if (EQUALS("port-ratio", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("readrange", name) || EQUALS("readranges", name)) { masscan->op = Operation_ReadRange; } else if (EQUALS("reason", name)) { masscan->output.is_reason = 1; } else if (EQUALS("redis", name)) { struct Range range; unsigned offset = 0; unsigned max_offset = (unsigned)strlen(value); unsigned port = 6379; range = range_parse_ipv4(value, &offset, max_offset); if ((range.begin == 0 && range.end == 0) || range.begin != range.end) { LOG(0, "FAIL: bad redis IP address: %s\n", value); exit(1); } if (offset < max_offset) { while (offset < max_offset && isspace(value[offset])) offset++; if (offset+1 < max_offset && value[offset] == ':' && isdigit(value[offset+1]&0xFF)) { port = (unsigned)strtoul(value+offset+1, 0, 0); if (port > 65535 || port == 0) { LOG(0, "FAIL: bad redis port: %s\n", value+offset+1); exit(1); } } } /* TODO: add support for connecting to IPv6 addresses here */ masscan->redis.ip.ipv4 = range.begin; masscan->redis.ip.version = 4; masscan->redis.port = port; masscan->output.format = Output_Redis; safe_strcpy(masscan->output.filename, sizeof(masscan->output.filename), ""); } else if(EQUALS("redis-pwd", name)) { masscan->redis.password = strdup(value); } else if (EQUALS("release-memory", name)) { fprintf(stderr, "nmap(%s): this is our default option\n", name); } else if (EQUALS("resume", name)) { masscan_read_config_file(masscan, value); masscan_set_parameter(masscan, "output-append", "true"); } else if (EQUALS("vuln", name)) { if (EQUALS("heartbleed", value)) { masscan_set_parameter(masscan, "heartbleed", "true"); return; } else if (EQUALS("ticketbleed", value)) { masscan_set_parameter(masscan, "ticketbleed", "true"); return; } else if (EQUALS("poodle", value) || EQUALS("sslv3", value)) { masscan->is_poodle_sslv3 = 1; masscan_set_parameter(masscan, "no-capture", "cert"); masscan_set_parameter(masscan, "banners", "true"); return; } if (!vulncheck_lookup(value)) { fprintf(stderr, "FAIL: vuln check '%s' does not exist\n", value); fprintf(stderr, " hint: use '--vuln list' to list available scripts\n"); exit(1); } if (masscan->vuln_name != NULL) { if (strcmp(masscan->vuln_name, value) == 0) return; /* ok */ else { fprintf(stderr, "FAIL: only one vuln check supported at a time\n"); fprintf(stderr, " hint: '%s' is existing vuln check, '%s' is new vuln check\n", masscan->vuln_name, value); exit(1); } } masscan->vuln_name = vulncheck_lookup(value)->name; } else if (EQUALS("scan-delay", name) || EQUALS("max-scan-delay", name)) { fprintf(stderr, "nmap(%s): unsupported: we do timing VASTLY differently!\n", name); exit(1); } else if (EQUALS("scanflags", name)) { fprintf(stderr, "nmap(%s): TCP scan flags not yet supported\n", name); exit(1); } else if (EQUALS("sendq", name) || EQUALS("sendqueue", name)) { masscan->is_sendq = 1; } else if (EQUALS("send-eth", name)) { fprintf(stderr, "nmap(%s): unnecessary, we always do --send-eth\n", name); } else if (EQUALS("send-ip", name)) { fprintf(stderr, "nmap(%s): unsupported, we only do --send-eth\n", name); exit(1); } else if (EQUALS("selftest", name) || EQUALS("self-test", name) || EQUALS("regress", name)) { masscan->op = Operation_Selftest; return; } else if (EQUALS("benchmark", name)) { masscan->op = Operation_Benchmark; return; } else if (EQUALS("source-port", name) || EQUALS("sourceport", name)) { masscan_set_parameter(masscan, "adapter-port", value); } else if (EQUALS("nobacktrace", name) || EQUALS("backtrace", name)) { ; } else if (EQUALS("no-stylesheet", name)) { masscan->output.stylesheet[0] = '\0'; } else if (EQUALS("system-dns", name)) { fprintf(stderr, "nmap(%s): DNS lookups will never be supported by this code\n", name); exit(1); } else if (EQUALS("top-ports", name)) { unsigned n = (unsigned)parseInt(value); if (!isInteger(value)) n = 100; LOG(2, "top-ports = %u\n", n); masscan->top_ports = n; } else if (EQUALS("traceroute", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("test", name)) { if (EQUALS("csv", value)) masscan->is_test_csv = 1; } else if (EQUALS("notest", name)) { if (EQUALS("csv", value)) masscan->is_test_csv = 0; } else if (EQUALS("ttl", name)) { unsigned x = (unsigned)strtoul(value, 0, 0); if (x >= 256) { fprintf(stderr, "error: %s=: expected number less than 256\n", name); } else { masscan->nmap.ttl = x; } } else if (EQUALS("version", name)) { print_version(); exit(1); } else if (EQUALS("version-intensity", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("version-light", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("version-all", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("version-trace", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("vlan", name) || EQUALS("adapter-vlan", name)) { masscan->nic[index].is_vlan = 1; masscan->nic[index].vlan_id = (unsigned)parseInt(value); } else if (EQUALS("wait", name)) { if (EQUALS("forever", value)) masscan->wait = INT_MAX; else masscan->wait = (unsigned)parseInt(value); } else if (EQUALS("webxml", name)) { masscan_set_parameter(masscan, "stylesheet", "http://nmap.org/svn/docs/nmap.xsl"); } else { fprintf(stderr, "CONF: unknown config option: %s=%s\n", name, value); exit(1); } } static bool is_numable(const char *name) { size_t i; for (i=0; config_parameters[i].name; i++) { if (EQUALS(config_parameters[i].name, name)) { return (config_parameters[i].flags & F_NUMABLE) == F_NUMABLE; } else { size_t j; for (j=0; config_parameters[i].alts[j]; j++) { if (EQUALS(config_parameters[i].alts[j], name)) { return (config_parameters[i].flags & F_NUMABLE) == F_NUMABLE; } } } } return false; } /*************************************************************************** * Command-line parsing code assumes every --parm is followed by a value. * This is a list of the parameters that don't follow the default. ***************************************************************************/ static int is_singleton(const char *name) { static const char *singletons[] = { "echo", "echo-all", "echo-cidr", "selftest", "self-test", "regress", "benchmark", "system-dns", "traceroute", "version", "version-light", "version-all", "version-trace", "osscan-limit", "osscan-guess", "badsum", "reason", "open", "open-only", "packet-trace", "release-memory", "log-errors", "append-output", "webxml", "no-stylesheet", "heartbleed", "ticketbleed", "send-eth", "send-ip", "iflist", "nmap", "trace-packet", "pfring", "sendq", "ping", "ping-sweep", "nobacktrace", "backtrace", "infinite", "nointeractive", "interactive", "status", "nostatus", "read-range", "read-ranges", "readrange", "read-ranges", 0}; size_t i; for (i=0; singletons[i]; i++) { if (EQUALS(singletons[i], name)) return 1; } for (i=0; config_parameters[i].name; i++) { if (EQUALS(config_parameters[i].name, name)) { return (config_parameters[i].flags & F_BOOL) == F_BOOL; } else { size_t j; for (j=0; config_parameters[i].alts[j]; j++) { if (EQUALS(config_parameters[i].alts[j], name)) { return (config_parameters[i].flags & F_BOOL) == F_BOOL; } } } } return 0; } /***************************************************************************** *****************************************************************************/ static void masscan_help() { printf( "usage: masscan [options] [... -pPORT[,PORT...]]\n" "MASSCAN is a fast port scanner. The primary input parameters are the\n" "IP addresses/ranges you want to scan, and the port numbers. An example\n" "is the following, which scans the 10.x.x.x network for web servers:\n" "\n" " masscan 10.0.0.0/8 -p80\n" "\n" "The program auto-detects network interface/adapter settings. If this\n" "fails, you'll have to set these manually. The following is an\n" "example of all the parameters that are needed:\n" "\n" " --adapter-ip 192.168.10.123\n" " --adapter-mac 00-11-22-33-44-55\n" " --router-mac 66-55-44-33-22-11\n" "\n" "Parameters can be set either via the command-line or config-file. The\n" "names are the same for both. Thus, the above adapter settings would\n" "appear as follows in a configuration file:\n" "\n" " adapter-ip = 192.168.10.123\n" " adapter-mac = 00-11-22-33-44-55\n" " router-mac = 66-55-44-33-22-11\n" "\n" "All single-dash parameters have a spelled out double-dash equivalent,\n" "so '-p80' is the same as '--ports 80' (or 'ports = 80' in config file).\n" "To use the config file, type:\n" "\n" " masscan -c \n" "\n" "To generate a config-file from the current settings, use the --echo\n" "option. This stops the program from actually running, and just echoes\n" "the current configuration instead. This is a useful way to generate\n" "your first config file, or see a list of parameters you didn't know\n" "about. I suggest you try it now:\n" "\n" " masscan -p1234 --echo\n" "\n"); exit(1); } /*************************************************************************** ***************************************************************************/ void masscan_load_database_files(struct Masscan *masscan) { const char *filename; /* * "pcap-payloads" */ filename = masscan->payloads.pcap_payloads_filename; if (filename) { if (masscan->payloads.udp == NULL) masscan->payloads.udp = payloads_udp_create(); if (masscan->payloads.oproto == NULL) masscan->payloads.oproto = payloads_udp_create(); payloads_read_pcap(filename, masscan->payloads.udp, masscan->payloads.oproto); } /* * `--nmap-payloads` */ filename = masscan->payloads.nmap_payloads_filename; if (filename) { FILE *fp; fp = fopen(filename, "rt"); if (fp == NULL) { fprintf(stderr, "[-] FAIL: --nmap-payloads\n"); fprintf(stderr, "[-] %s:%s\n", filename, strerror(errno)); } else { if (masscan->payloads.udp == NULL) masscan->payloads.udp = payloads_udp_create(); payloads_udp_readfile(fp, filename, masscan->payloads.udp); fclose(fp); } } /* * "nmap-service-probes" */ filename = masscan->payloads.nmap_service_probes_filename; if (filename) { if (masscan->payloads.probes) nmapserviceprobes_free(masscan->payloads.probes); masscan->payloads.probes = nmapserviceprobes_read_file(filename); } } /*************************************************************************** * Read the configuration from the command-line. * Called by 'main()' when starting up. ***************************************************************************/ void masscan_command_line(struct Masscan *masscan, int argc, char *argv[]) { int i; for (i=1; i sizeof(name2) - 1) { fprintf(stderr, "%.*s: name too long\n", name_length, name); name_length = sizeof(name2) - 1; } memcpy(name2, name, name_length); name2[name_length] = '\0'; masscan_set_parameter(masscan, name2, value); } else if (EQUALS("readscan", argv[i]+2)) { /* Read in a binary file instead of scanning the network*/ masscan->op = Operation_ReadScan; /* Default to reading banners */ masscan->is_banners = true; masscan->is_banners_rawudp = true; /* This option may be followed by many filenames, therefore, * skip forward in the argument list until the next * argument */ while (i+1 < argc && argv[i+1][0] != '-') i++; continue; } else { char name2[64]; char *name = argv[i] + 2; unsigned name_length; const char *value; value = strchr(&argv[i][2], '='); if (value == NULL) value = strchr(&argv[i][2], ':'); if (value == NULL) { if (is_singleton(name)) value = ""; else value = argv[++i]; name_length = (unsigned)strlen(name); } else { name_length = (unsigned)(value - name); value++; } if (i >= argc) { fprintf(stderr, "%.*s: empty parameter\n", name_length, name); break; } if (name_length > sizeof(name2) - 1) { fprintf(stderr, "%.*s: name too long\n", name_length, name); name_length = sizeof(name2) - 1; } memcpy(name2, name, name_length); name2[name_length] = '\0'; masscan_set_parameter(masscan, name2, value); } continue; } /* For for a single-dash parameter */ if (argv[i][0] == '-') { const char *arg; switch (argv[i][1]) { case '6': /* Silently ignore this: IPv6 features enabled all the time */ break; case 'A': fprintf(stderr, "nmap(%s): unsupported: this tool only does SYN scan\n", argv[i]); exit(1); case 'b': fprintf(stderr, "nmap(%s): FTP bounce scans will never be supported\n", argv[i]); exit(1); case 'c': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_read_config_file(masscan, arg); break; case 'd': /* just do same as verbosity level */ { int v; for (v=1; argv[i][v] == 'd'; v++) { LOG_add_level(1); } } break; case 'e': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter", arg); break; case 'f': fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", argv[i]); exit(1); case 'F': fprintf(stderr, "nmap(%s): unsupported, no slow/fast mode\n", argv[i]); exit(1); case 'g': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter-port", arg); break; case 'h': case '?': masscan_usage(); break; case 'i': if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) { /* This looks like an nmap option*/ switch (argv[i][2]) { case 'L': masscan_set_parameter(masscan, "includefile", argv[++i]); break; case 'R': /* -iR in nmap makes it randomize addresses completely. Thus, * it's nearest equivalent is scanning the entire Internet range */ masscan_set_parameter(masscan, "include", "0.0.0.0/0"); break; default: fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]); exit(1); } } else { if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter", arg); } break; case 'n': /* This looks like an nmap option*/ /* Do nothing: this code never does DNS lookups anyway */ break; case 'o': /* nmap output format */ switch (argv[i][2]) { case 'A': masscan->output.format = Output_All; fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]); exit(1); break; case 'B': masscan->output.format = Output_Binary; break; case 'D': masscan->output.format = Output_NDJSON; break; case 'J': masscan->output.format = Output_JSON; break; case 'N': masscan->output.format = Output_Nmap; fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]); exit(1); break; case 'X': masscan->output.format = Output_XML; break; case 'R': masscan->output.format = Output_Redis; if (i+1 < argc && argv[i+1][0] != '-') masscan_set_parameter(masscan, "redis", argv[i+1]); break; case 'S': masscan->output.format = Output_ScriptKiddie; fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]); exit(1); break; case 'G': masscan->output.format = Output_Grepable; break; case 'L': masscan_set_parameter(masscan, "output-format", "list"); break; case 'U': masscan_set_parameter(masscan, "output-format", "unicornscan"); break; case 'H': masscan_set_parameter(masscan, "output-format", "hostonly"); break; default: fprintf(stderr, "nmap(%s): unknown output format\n", argv[i]); exit(1); } ++i; if (i >= argc || (argv[i][0] == '-' && argv[i][1] != '\0')) { fprintf(stderr, "missing output filename\n"); exit(1); } masscan_set_parameter(masscan, "output-filename", argv[i]); break; case 'O': fprintf(stderr, "nmap(%s): unsupported, OS detection is too complex\n", argv[i]); exit(1); break; case 'p': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; if (i >= argc || arg[0] == 0) { // if string is empty fprintf(stderr, "%s: empty parameter\n", argv[i]); } else masscan_set_parameter(masscan, "ports", arg); break; case 'P': switch (argv[i][2]) { case 'n': /* we already do this */ break; default: fprintf(stderr, "nmap(%s): unsupported option, maybe in future\n", argv[i]); exit(1); } break; case 'r': /* This looks like an nmap option*/ fprintf(stderr, "nmap(%s): wat? randomization is our raison d'etre!! rethink prease\n", argv[i]); exit(1); break; case 'R': /* This looks like an nmap option*/ fprintf(stderr, "nmap(%s): unsupported. This code will never do DNS lookups.\n", argv[i]); exit(1); break; case 's': /* NMAP: scan type */ if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) { unsigned j; for (j=2; argv[i][j]; j++) switch (argv[i][j]) { case 'A': fprintf(stderr, "nmap(%s): ACK scan not yet supported\n", argv[i]); exit(1); case 'C': fprintf(stderr, "nmap(%s): unsupported\n", argv[i]); exit(1); case 'F': fprintf(stderr, "nmap(%s): FIN scan not yet supported\n", argv[i]); exit(1); case 'I': fprintf(stderr, "nmap(%s): Zombie scans will never be supported\n", argv[i]); exit(1); case 'L': /* List Scan - simply list targets to scan */ masscan->op = Operation_ListScan; break; case 'M': fprintf(stderr, "nmap(%s): Maimon scan not yet supported\n", argv[i]); exit(1); case 'n': /* Ping Scan - disable port scan */ fprintf(stderr, "nmap(%s): ping-sweeps not yet supported\n", argv[i]); exit(1); case 'N': fprintf(stderr, "nmap(%s): NULL scan not yet supported\n", argv[i]); exit(1); case 'O': /* Other IP protocols (not ICMP, UDP, TCP, or SCTP) */ masscan->scan_type.oproto = 1; break; case 'S': /* TCP SYN scan - THIS IS WHAT WE DO! */ masscan->scan_type.tcp = 1; break; case 'T': /* TCP connect scan */ fprintf(stderr, "nmap(%s): connect() is too synchronous for cool kids\n", argv[i]); fprintf(stderr, "WARNING: doing SYN scan (-sS) anyway, ignoring (-sT)\n"); break; case 'U': /* UDP scan */ masscan->scan_type.udp = 1; break; case 'V': fprintf(stderr, "nmap(%s): unlikely this will be supported\n", argv[i]); exit(1); case 'W': fprintf(stderr, "nmap(%s): Windows scan not yet supported\n", argv[i]); exit(1); case 'X': fprintf(stderr, "nmap(%s): Xmas scan not yet supported\n", argv[i]); exit(1); case 'Y': break; case 'Z': masscan->scan_type.sctp = 1; break; default: fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]); exit(1); } } else { fprintf(stderr, "%s: unknown parameter\n", argv[i]); exit(1); } break; case 'S': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter-ip", arg); break; case 'v': { int v; for (v=1; argv[i][v] == 'v'; v++) LOG_add_level(1); } break; case 'V': /* print version and exit */ masscan_set_parameter(masscan, "version", ""); break; case 'W': masscan->op = Operation_List_Adapters; return; case 'T': fprintf(stderr, "nmap(%s): unsupported, we do timing WAY different than nmap\n", argv[i]); exit(1); return; default: LOG(0, "FAIL: unknown option: -%s\n", argv[i]); LOG(0, " [hint] try \"--help\"\n"); LOG(0, " [hint] ...or, to list nmap-compatible options, try \"--nmap\"\n"); exit(1); } continue; } if (!isdigit(argv[i][0]) && argv[i][0] != ':' && argv[i][0] != '[') { fprintf(stderr, "FAIL: unknown command-line parameter \"%s\"\n", argv[i]); fprintf(stderr, " [hint] did you want \"--%s\"?\n", argv[i]); exit(1); } /* If parameter doesn't start with '-', assume it's an * IPv4 range */ masscan_set_parameter(masscan, "range", argv[i]); } /* * If no other "scan type" found, then default to TCP */ if (masscan->scan_type.udp == 0 && masscan->scan_type.sctp == 0 && masscan->scan_type.ping == 0 && masscan->scan_type.arp == 0 && masscan->scan_type.oproto == 0) masscan->scan_type.tcp = 1; /* * If "top-ports" specified, then add all those ports. This may be in * addition to any other ports */ if (masscan->top_ports) { config_top_ports(masscan, masscan->top_ports); } if (masscan->shard.of < masscan->shard.one) { fprintf(stderr, "[-] WARNING: the shard number must be less than the total shard count: %u/%u\n", masscan->shard.one, masscan->shard.of); } if (masscan->shard.of > 1 && masscan->seed == 0) { fprintf(stderr, "[-] WARNING: --seed is not specified\n HINT: all shards must share the same seed\n"); } } /*************************************************************************** * Prints the current configuration to the command-line then exits. * Use#1: create a template file of all settable parameters. * Use#2: make sure your configuration was interpreted correctly. ***************************************************************************/ void masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all) { unsigned i; unsigned l = 0; /* * NEW: * Print all configuration parameters */ masscan->echo = fp; masscan->echo_all = is_echo_all; for (i=0; config_parameters[i].name; i++) { config_parameters[i].set(masscan, 0, 0); } masscan->echo = 0; masscan->echo_all = 0; /* * OLD: * Things here below are the old way of echoing parameters. * TODO: cleanup this code, replacing with the new way. */ if (masscan->nic_count == 0) masscan_echo_nic(masscan, fp, 0); else { for (i=0; inic_count; i++) masscan_echo_nic(masscan, fp, i); } /** * Fix for #737, save adapter-port/source-port value or range */ if (masscan->nic[0].src.port.first != 0) { fprintf(fp, "adapter-port = %d", masscan->nic[0].src.port.first); if (masscan->nic[0].src.port.first != masscan->nic[0].src.port.last) { /* --adapter-port - */ fprintf(fp, "-%d", masscan->nic[0].src.port.last); } fprintf(fp, "\n"); } /* * Targets */ fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n"); fprintf(fp, "ports = "); /* Disable comma generation for the first element */ l = 0; for (i=0; itargets.ports.count; i++) { struct Range range = masscan->targets.ports.list[i]; do { struct Range rrange = range; unsigned done = 0; if (l) fprintf(fp, ","); l = 1; if (rrange.begin >= Templ_ICMP_echo) { rrange.begin -= Templ_ICMP_echo; rrange.end -= Templ_ICMP_echo; fprintf(fp,"I:"); done = 1; } else if (rrange.begin >= Templ_SCTP) { rrange.begin -= Templ_SCTP; rrange.end -= Templ_SCTP; fprintf(fp,"S:"); range.begin = Templ_ICMP_echo; } else if (rrange.begin >= Templ_UDP) { rrange.begin -= Templ_UDP; rrange.end -= Templ_UDP; fprintf(fp,"U:"); range.begin = Templ_SCTP; } else if (Templ_Oproto_first <= rrange.begin && rrange.begin <= Templ_Oproto_last) { rrange.begin -= Templ_Oproto_first; rrange.end -= Templ_Oproto_first; fprintf(fp, "O:"); range.begin = Templ_Oproto_first; } else range.begin = Templ_UDP; rrange.end = min(rrange.end, 65535); if (rrange.begin == rrange.end) fprintf(fp, "%u", rrange.begin); else fprintf(fp, "%u-%u", rrange.begin, rrange.end); if (done) break; } while (range.begin <= range.end); } fprintf(fp, "\n"); /* * IPv4 address targets */ for (i=0; itargets.ipv4.count; i++) { unsigned prefix_bits; struct Range range = masscan->targets.ipv4.list[i]; if (range.begin == range.end) { fprintf(fp, "range = %u.%u.%u.%u", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF ); } else if (range_is_cidr(range, &prefix_bits)) { fprintf(fp, "range = %u.%u.%u.%u/%u", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF, prefix_bits ); } else { fprintf(fp, "range = %u.%u.%u.%u-%u.%u.%u.%u", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF, (range.end>>24)&0xFF, (range.end>>16)&0xFF, (range.end>> 8)&0xFF, (range.end>> 0)&0xFF ); } fprintf(fp, "\n"); } for (i=0; itargets.ipv6.count; i++) { bool exact = false; struct Range6 range = masscan->targets.ipv6.list[i]; ipaddress_formatted_t fmt = ipv6address_fmt(range.begin); fprintf(fp, "range = %s", fmt.string); if (!ipv6address_is_equal(range.begin, range.end)) { unsigned cidr_bits = count_cidr6_bits(&range, &exact); if (exact && cidr_bits) { fprintf(fp, "/%u", cidr_bits); } else { fmt = ipv6address_fmt(range.end); fprintf(fp, "-%s", fmt.string); } } fprintf(fp, "\n"); } } /*************************************************************************** * Prints the list of CIDR to scan to the command-line then exits. * Use: provide this list to other tools. Unlike masscan -sL, it keeps * the CIDR aggretated format, and does not randomize the order of output. * For example, given the starting range of [10.0.0.1-10.0.0.255], this will * print all the CIDR ranges that make this up: * 10.0.0.1/32 * 10.0.0.2/31 * 10.0.0.4/30 * 10.0.0.8/29 * 10.0.0.16/28 * 10.0.0.32/27 * 10.0.0.64/26 * 10.0.0.128/25 ***************************************************************************/ void masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all) { unsigned i; UNUSEDPARM(is_echo_all); masscan->echo = fp; /* * For all IPv4 ranges ... */ for (i=0; itargets.ipv4.count; i++) { /* Get the next range in the list */ struct Range range = masscan->targets.ipv4.list[i]; /* If not a single CIDR range, print all the CIDR ranges * needed to completely represent this addres */ for (;;) { unsigned prefix_length; struct Range cidr; /* Find the largest CIDR range (one that can be specified * with a /prefix) at the start of this range. */ cidr = range_first_cidr(range, &prefix_length); fprintf(fp, "%u.%u.%u.%u/%u\n", (cidr.begin>>24)&0xFF, (cidr.begin>>16)&0xFF, (cidr.begin>> 8)&0xFF, (cidr.begin>> 0)&0xFF, prefix_length ); /* If this is the last range, then stop. There are multiple * ways to gets to see if we get to the end, but I think * this is the best. */ if (cidr.end >= range.end) break; /* If the CIDR range didn't cover the entire range, * then remove it from the beginning of the range * and process the remainder */ range.begin = cidr.end+1; } } /* * For all IPv6 ranges... */ for (i=0; itargets.ipv6.count; i++) { struct Range6 range = masscan->targets.ipv6.list[i]; bool exact = false; while (!exact) { ipaddress_formatted_t fmt = ipv6address_fmt(range.begin); fprintf(fp, "%s", fmt.string); if (range.begin.hi == range.end.hi && range.begin.lo == range.end.lo) { fprintf(fp, "/128"); exact = true; } else { unsigned cidr_bits = count_cidr6_bits(&range, &exact); fprintf(fp, "/%u", cidr_bits); } fprintf(fp, "\n"); } } } /*************************************************************************** * remove leading/trailing whitespace ***************************************************************************/ static void trim(char *line, size_t sizeof_line) { if (sizeof_line > strlen(line)) sizeof_line = strlen(line); while (isspace(*line & 0xFF)) memmove(line, line+1, sizeof_line--); while (*line && isspace(line[sizeof_line-1] & 0xFF)) line[--sizeof_line] = '\0'; } /*************************************************************************** ***************************************************************************/ void masscan_read_config_file(struct Masscan *masscan, const char *filename) { FILE *fp; char line[65536]; fp = fopen(filename, "rt"); if (fp == NULL) { char dir[512]; char *x; fprintf(stderr, "[-] FAIL: reading configuration file\n"); fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno)); x = getcwd(dir, sizeof(dir)); if (x) fprintf(stderr, "[-] cwd = %s\n", dir); return; } while (fgets(line, sizeof(line), fp)) { char *name; char *value; trim(line, sizeof(line)); if (ispunct(line[0] & 0xFF) || line[0] == '\0') continue; name = line; value = strchr(line, '='); if (value == NULL) continue; *value = '\0'; value++; trim(name, sizeof(line)); trim(value, sizeof(line)); masscan_set_parameter(masscan, name, value); } fclose(fp); } /*************************************************************************** ***************************************************************************/ int masscan_conf_contains(const char *x, int argc, char **argv) { int i; for (i=0; i #include #include #include "syn-cookie.h" /** * This is the number of entries in our table. More entries does a better job at the * cost of using more memory. */ #define DEDUP_ENTRIES 65536 struct DedupEntry_IPv4 { unsigned ip_them; unsigned port_them; unsigned ip_me; unsigned port_me; }; struct DedupEntry_IPv6 { ipv6address ip_them; ipv6address ip_me; unsigned short port_them; unsigned short port_me; }; /** * This is simply the array of entries. We have two arrays, one for IPv4 * and another for IPv6. */ struct DedupTable { struct DedupEntry_IPv4 entries[DEDUP_ENTRIES][4]; struct DedupEntry_IPv6 entries6[DEDUP_ENTRIES][4]; }; /** * We use the FNv1a hash algorithm, which starts with this seed value. */ const unsigned fnv1a_seed = 0x811C9DC5; /* 2166136261 */ /** * Hash one byte, the other hash functions of multiple bytes call this function. * @param hash * The current hash value that we keep updating as we repeatedly * call this function, or the `fnv1a_seed value on the first call to * this function. */ static inline unsigned fnv1a(unsigned char c, unsigned hash) { const unsigned prime = 0x01000193; /* 16777619 */ return (c ^ hash) * prime; } static unsigned fnv1a_string(const void *v_buf, size_t length, unsigned hash) { const unsigned char *buf = (const unsigned char *)v_buf; size_t i; for (i=0; i>0)&0xFF, hash); hash = fnv1a((data>>8)&0xFF, hash); return hash; } static inline unsigned fnv1a_longlong(unsigned long long data, unsigned hash) { return fnv1a_string(&data, 8, hash); } /** * Create a new table, which means simply allocating the object * and setting it to zero. */ struct DedupTable * dedup_create(void) { struct DedupTable *dedup; dedup = CALLOC(1, sizeof(*dedup)); return dedup; } /** * There's nothing special we need to do to free the structure * since it's all contained in the single allocation. */ void dedup_destroy(struct DedupTable *dedup) { free(dedup); } /** * Create a hash of the IPv6 socket. This doesn't have to be * cryptographically secure, so we are going to use the FNv1a algorithm. */ static inline unsigned dedup_hash_ipv6(ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me) { unsigned hash = fnv1a_seed; hash = fnv1a_longlong(ip_them.ipv6.hi, hash); hash = fnv1a_longlong(ip_them.ipv6.lo, hash); hash = fnv1a_short(port_them, hash); hash = fnv1a_longlong(ip_me.ipv6.hi, hash); hash = fnv1a_longlong(ip_me.ipv6.lo, hash); hash = fnv1a_short(port_me, hash); return hash; } /** * If two IPv6 addresses are equal. */ static inline int is_equal6(ipv6address lhs, ipv6address rhs) { return lhs.hi == rhs.hi && lhs.lo == rhs.lo; } /** * Swap two addresses in the table. This uses the classic XOR trick * rather than using a swap variable. */ static inline void swap6(struct DedupEntry_IPv6 *lhs, struct DedupEntry_IPv6 *rhs) { lhs->ip_them.hi ^= rhs->ip_them.hi; lhs->ip_them.lo ^= rhs->ip_them.lo; lhs->port_them ^= rhs->port_them; lhs->ip_me.hi ^= rhs->ip_me.hi; lhs->ip_me.lo ^= rhs->ip_me.lo; lhs->port_me ^= rhs->port_me; rhs->ip_them.hi ^= lhs->ip_them.hi; rhs->ip_them.lo ^= lhs->ip_them.lo; rhs->port_them ^= lhs->port_them; rhs->ip_me.hi ^= lhs->ip_me.hi; rhs->ip_me.lo ^= lhs->ip_me.lo; rhs->port_me ^= lhs->port_me; lhs->ip_them.hi ^= rhs->ip_them.hi; lhs->ip_them.lo ^= rhs->ip_them.lo; lhs->port_them ^= rhs->port_them; lhs->ip_me.hi ^= rhs->ip_me.hi; lhs->ip_me.lo ^= rhs->ip_me.lo; lhs->port_me ^= rhs->port_me; } /** * This implements the same algorithm as for IPv4 addresses, but for * IPv6 addresses instead. */ static unsigned dedup_is_duplicate_ipv6(struct DedupTable *dedup, ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me) { unsigned hash; struct DedupEntry_IPv6 *bucket; unsigned i; /* THREAT: probably need to secure this hash, though the syn-cookies * provides some protection */ hash = dedup_hash_ipv6(ip_them, port_them, ip_me, port_me); hash &= DEDUP_ENTRIES-1; /* Search in this bucket */ bucket = dedup->entries6[hash]; /* If we find the entry in our table, move it to the front, so * that it won't be aged out as quickly. We keep prepending new * addresses to front, aging older addresses that haven't been * seen in a while. */ for (i = 0; i < 4; i++) { if (is_equal6(bucket[i].ip_them, ip_them.ipv6) && bucket[i].port_them == port_them && is_equal6(bucket[i].ip_me, ip_me.ipv6) && bucket[i].port_me == port_me) { /* move to end of list so constant repeats get ignored */ if (i > 0) { swap6(&bucket[0], &bucket[i]); } return 1; } } /* We didn't find it, so add it to our list. This will push * older entries at this bucket off the list */ memmove(bucket, bucket+1, 3*sizeof(*bucket)); bucket[0].ip_them.hi = ip_them.ipv6.hi; bucket[0].ip_them.lo = ip_them.ipv6.lo; bucket[0].port_them = (unsigned short)port_them; bucket[0].ip_me.hi = ip_me.ipv6.hi; bucket[0].ip_me.lo = ip_me.ipv6.lo; bucket[0].port_me = (unsigned short)port_me; return 0; } /*************************************************************************** ***************************************************************************/ static unsigned dedup_is_duplicate_ipv4(struct DedupTable *dedup, ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me) { unsigned hash; struct DedupEntry_IPv4 *bucket; unsigned i; /* THREAT: probably need to secure this hash, though the syn-cookies * provides some protection */ hash = (ip_them.ipv4 + port_them) ^ ((ip_me.ipv4) + (ip_them.ipv4>>16)) ^ (ip_them.ipv4>>24) ^ port_me; hash &= DEDUP_ENTRIES-1; /* Search in this bucket */ bucket = dedup->entries[hash]; /* If we find the entry in our table, move it to the front, so * that it won't be aged out as quickly. We keep prepending new * addresses to front, aging older addresses that haven't been * seen in a while. */ for (i = 0; i < 4; i++) { if (bucket[i].ip_them == ip_them.ipv4 && bucket[i].port_them == port_them && bucket[i].ip_me == ip_me.ipv4 && bucket[i].port_me == port_me) { /* move to end of list so constant repeats get ignored */ if (i > 0) { bucket[i].ip_them ^= bucket[0].ip_them; bucket[i].port_them ^= bucket[0].port_them; bucket[i].ip_me ^= bucket[0].ip_me; bucket[i].port_me ^= bucket[0].port_me; bucket[0].ip_them ^= bucket[i].ip_them; bucket[0].port_them ^= bucket[i].port_them; bucket[0].ip_me ^= bucket[i].ip_me; bucket[0].port_me ^= bucket[i].port_me; bucket[i].ip_them ^= bucket[0].ip_them; bucket[i].port_them ^= bucket[0].port_them; bucket[i].ip_me ^= bucket[0].ip_me; bucket[i].port_me ^= bucket[0].port_me; } return 1; } } /* We didn't find it, so add it to our list. This will push * older entries at this bucket off the list */ memmove(bucket, bucket+1, 3*sizeof(*bucket)); bucket[0].ip_them = ip_them.ipv4; bucket[0].port_them = port_them; bucket[0].ip_me = ip_me.ipv4; bucket[0].port_me = port_me; return 0; } /*************************************************************************** ***************************************************************************/ unsigned dedup_is_duplicate(struct DedupTable *dedup, ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me) { if (ip_them.version == 6) return dedup_is_duplicate_ipv6(dedup, ip_them, port_them, ip_me, port_me); else return dedup_is_duplicate_ipv4(dedup, ip_them, port_them, ip_me, port_me); } /** * My own deterministic rand() function for testing this module */ static unsigned _rand(unsigned *seed) { static const unsigned a = 214013; static const unsigned c = 2531011; *seed = (*seed) * a + c; return (*seed)>>16 & 0x7fff; } /* * Provide a simple unit test for this module. * * This is a pretty lame test. I'm going to generate * a set of random addresses, tweaked so that they aren't * too random, so that I get around 30 to 50 expected * duplicates. If I get zero duplicates, or if I get too * many duplicates in the test, then I know it's failed. * * This is in no way a reliable test that deterministically * tests the functionality. It's a crappy non-deterministic * test. * * We also do a simple deterministic test, but this still * is insufficient testing how duplicates age out and such. */ int dedup_selftest(void) { struct DedupTable *dedup; unsigned seed = 0; size_t i; unsigned found_match = 0; unsigned line = 0; dedup = dedup_create(); /* Deterministic test. * * The first time we check on a socket combo, there should * be no duplicate. The second time we check, however, there should * be a duplicate. */ { ipaddress ip_me; ipaddress ip_them; unsigned port_me; unsigned port_them; ip_me.version = 4; ip_them.version = 4; ip_me.ipv4 = 0x12345678; ip_them.ipv4 = 0xabcdef0; port_me = 0x1234; port_them = 0xfedc; if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) { line = __LINE__; goto fail; } if (!dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) { line = __LINE__; goto fail; } ip_me.version = 6; ip_them.version = 6; ip_me.ipv6.hi = 0x12345678; ip_me.ipv6.lo = 0x12345678; ip_them.ipv6.hi = 0xabcdef0; ip_them.ipv6.lo = 0xabcdef0; if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) { line = __LINE__; goto fail; } if (!dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) { ipaddress_formatted_t fmt1 = ipaddress_fmt(ip_them); ipaddress_formatted_t fmt2 = ipaddress_fmt(ip_me); fprintf(stderr, "[-] [%s]:%u -> [%s]:%u\n", fmt1.string, port_them, fmt2.string, port_me); line = __LINE__; goto fail; } } /* Test IPv4 addresses */ for (i=0; i<100000; i++) { ipaddress ip_me; ipaddress ip_them; unsigned port_me; unsigned port_them; ip_me.version = 4; ip_them.version = 4; /* Instead of completely random numbers over the entire * range, each port/IP is restricted to just 512 * random combinations. This should statistically * give us around 10 matches*/ ip_me.ipv4 = _rand(&seed) & 0xFF800000; ip_them.ipv4 = _rand(&seed) & 0x1FF; port_me = _rand(&seed) & 0xFF80; port_them = _rand(&seed) & 0x1FF; if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) { found_match++; } } /* Approximately 30 matches should be found. If we couldn't * find any, or if we've found too many, then the test has * failed. */ if (found_match == 0 || found_match > 200) { line = __LINE__; goto fail; } /* Now do IPv6 */ found_match = 0; seed = 0; /* Test IPv4 addresses */ for (i=0; i<100000; i++) { ipaddress ip_me; ipaddress ip_them; unsigned port_me; unsigned port_them; ip_me.version = 6; ip_them.version = 6; /* Instead of completely random numbers over the entire * range, each port/IP is restricted to just 512 * random combinations. This should statistically * give us around 10 matches*/ ip_me.ipv6.hi = _rand(&seed) & 0xFF800000; ip_them.ipv6.lo = _rand(&seed) & 0x1FF; port_me = _rand(&seed) & 0xFF80; port_them = _rand(&seed) & 0x1FF; if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) { found_match++; } } /* The result should be same as for IPv4, around 30 matches found. */ if (found_match == 0 || found_match > 200) { line = __LINE__; goto fail; } /* All tests have passed */ return 0; /* success :) */ fail: fprintf(stderr, "[-] selftest: 'dedup' failed, file=%s, line=%u\n", __FILE__, line); return 1; } ================================================ FILE: src/main-dedup.h ================================================ #ifndef MAIN_DEDUP_H #define MAIN_DEDUP_H #include "massip-addr.h" struct DedupTable * dedup_create(void); void dedup_destroy(struct DedupTable *table); unsigned dedup_is_duplicate( struct DedupTable *dedup, ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me); /** * Simple unit test * @return 0 on success, 1 on failure. */ int dedup_selftest(void); #endif ================================================ FILE: src/main-globals.h ================================================ #ifndef MAIN_GLOBALS_H #define MAIN_GLOBALS_H #include extern unsigned volatile is_tx_done; extern unsigned volatile is_rx_done; extern time_t global_now; #endif ================================================ FILE: src/main-initadapter.c ================================================ #include "masscan.h" #include "util-logger.h" #include "rawsock.h" #include "rawsock-adapter.h" #include "stack-arpv4.h" #include "stack-ndpv6.h" #include "stub-pcap-dlt.h" /*************************************************************************** * Initialize the network adapter. * * This requires finding things like our IP address, MAC address, and router * MAC address. The user could configure these things manually instead. * * Note that we don't update the "static" configuration with the discovered * values, but instead return them as the "running" configuration. That's * so if we pause and resume a scan, auto discovered values don't get saved * in the configuration file. ***************************************************************************/ int masscan_initialize_adapter( struct Masscan *masscan, unsigned index, macaddress_t *source_mac, macaddress_t *router_mac_ipv4, macaddress_t *router_mac_ipv6 ) { char *ifname; char ifname2[256]; unsigned adapter_ip = 0; unsigned is_usable_ipv4 = !massip_has_ipv4_targets(&masscan->targets); /* I don't understand this line, seems opposite */ unsigned is_usable_ipv6 = !massip_has_ipv6_targets(&masscan->targets); /* I don't understand this line, seems opposite */ ipaddress_formatted_t fmt; /* * ADAPTER/NETWORK-INTERFACE * * If no network interface was configured, we need to go hunt down * the best Interface to use. We do this by choosing the first * interface with a "default route" (aka. "gateway") defined */ if (masscan->nic[index].ifname[0]) ifname = masscan->nic[index].ifname; else { /* no adapter specified, so find a default one */ int err; ifname2[0] = '\0'; err = rawsock_get_default_interface(ifname2, sizeof(ifname2)); if (err || ifname2[0] == '\0') { LOG(0, "[-] FAIL: could not determine default interface\n"); LOG(0, " [hint] try \"--interface ethX\"\n"); return -1; } ifname = ifname2; } LOG(1, "[+] interface = %s\n", ifname); /* * START ADAPTER * * Once we've figured out which adapter to use, we now need to * turn it on. */ masscan->nic[index].adapter = rawsock_init_adapter( ifname, masscan->is_pfring, masscan->is_sendq, masscan->nmap.packet_trace, masscan->is_offline, (void*)masscan->bpf_filter, masscan->nic[index].is_vlan, masscan->nic[index].vlan_id); if (masscan->nic[index].adapter == 0) { LOG(0, "[-] if:%s:init: failed\n", ifname); return -1; } masscan->nic[index].link_type = masscan->nic[index].adapter->link_type; LOG(1, "[+] interface-type = %u\n", masscan->nic[index].link_type); rawsock_ignore_transmits(masscan->nic[index].adapter, ifname); /* * MAC ADDRESS * * This is the address we send packets from. It actually doesn't really * matter what this address is, but to be a "responsible" citizen we * try to use the hardware address in the network card. */ if (masscan->nic[index].link_type == PCAP_DLT_NULL) { LOG(1, "[+] source-mac = %s\n", "none"); } else if (masscan->nic[index].link_type == PCAP_DLT_RAW) { LOG(1, "[+] source-mac = %s\n", "none"); } else { *source_mac = masscan->nic[index].source_mac; if (masscan->nic[index].my_mac_count == 0) { if (macaddress_is_zero(*source_mac)) { rawsock_get_adapter_mac(ifname, source_mac->addr); } /* If still zero, then print error message */ if (macaddress_is_zero(*source_mac)) { fprintf(stderr, "[-] FAIL: failed to detect MAC address of interface:" " \"%s\"\n", ifname); fprintf(stderr, " [hint] try something like " "\"--source-mac 00-11-22-33-44-55\"\n"); return -1; } } fmt = macaddress_fmt(*source_mac); LOG(1, "[+] source-mac = %s\n", fmt.string); } /* * IPv4 ADDRESS * * We need to figure out that IP address to send packets from. This * is done by querying the adapter (or configured by user). If the * adapter doesn't have one, then the user must configure one. */ if (massip_has_ipv4_targets(&masscan->targets)) { adapter_ip = masscan->nic[index].src.ipv4.first; if (adapter_ip == 0) { adapter_ip = rawsock_get_adapter_ip(ifname); masscan->nic[index].src.ipv4.first = adapter_ip; masscan->nic[index].src.ipv4.last = adapter_ip; masscan->nic[index].src.ipv4.range = 1; } if (adapter_ip == 0) { /* We appear to have IPv4 targets, yet we cannot find an adapter * to use for those targets. We are having trouble querying the * operating system stack. */ LOG(0, "[-] FAIL: failed to detect IP of interface \"%s\"\n", ifname); LOG(0, " [hint] did you spell the name correctly?\n"); LOG(0, " [hint] if it has no IP address, manually set with something like " "\"--source-ip 198.51.100.17\"\n"); if (massip_has_ipv4_targets(&masscan->targets)) { return -1; } } fmt = ipv4address_fmt(adapter_ip); LOG(1, "[+] source-ip = %s\n", fmt.string); if (adapter_ip != 0) is_usable_ipv4 = 1; /* * ROUTER MAC ADDRESS * * NOTE: this is one of the least understood aspects of the code. We must * send packets to the local router, which means the MAC address (not * IP address) of the router. * * Note: in order to ARP the router, we need to first enable the libpcap * code above. */ *router_mac_ipv4 = masscan->nic[index].router_mac_ipv4; if (masscan->is_offline) { /* If we are doing offline benchmarking/testing, then create * a fake MAC address fro the router */ memcpy(router_mac_ipv4->addr, "\x66\x55\x44\x33\x22\x11", 6); } else if (masscan->nic[index].link_type == PCAP_DLT_NULL) { /* If it's a VPN tunnel, then there is no Ethernet MAC address */ LOG(1, "[+] router-mac-ipv4 = %s\n", "implicit"); } else if (masscan->nic[index].link_type == PCAP_DLT_RAW) { /* If it's a VPN tunnel, then there is no Ethernet MAC address */ LOG(1, "[+] router-mac-ipv4 = %s\n", "implicit"); } else if (macaddress_is_zero(*router_mac_ipv4)) { ipv4address_t router_ipv4 = masscan->nic[index].router_ip; int err = 0; LOG(2, "[+] if(%s): looking for default gateway\n", ifname); if (router_ipv4 == 0) err = rawsock_get_default_gateway(ifname, &router_ipv4); if (err == 0) { fmt = ipv4address_fmt(router_ipv4); LOG(1, "[+] router-ip = %s\n", fmt.string); LOG(2, "[+] if(%s):arp: resolving IPv4 address\n", ifname); stack_arp_resolve( masscan->nic[index].adapter, adapter_ip, *source_mac, router_ipv4, router_mac_ipv4); } fmt = macaddress_fmt(*router_mac_ipv4); LOG(1, "[+] router-mac-ipv4 = %s\n", fmt.string); if (macaddress_is_zero(*router_mac_ipv4)) { fmt = ipv4address_fmt(masscan->nic[index].router_ip); LOG(0, "[-] FAIL: ARP timed-out resolving MAC address for router %s: \"%s\"\n", ifname, fmt.string); LOG(0, " [hint] try \"--router ip 192.0.2.1\" to specify different router\n"); LOG(0, " [hint] try \"--router-mac 66-55-44-33-22-11\" instead to bypass ARP\n"); LOG(0, " [hint] try \"--interface eth0\" to change interface\n"); return -1; } } } /* * IPv6 ADDRESS * * We need to figure out that IPv6 address to send packets from. This * is done by querying the adapter (or configured by user). If the * adapter doesn't have one, then the user must configure one. */ if (massip_has_ipv6_targets(&masscan->targets)) { ipv6address adapter_ipv6 = masscan->nic[index].src.ipv6.first; if (ipv6address_is_zero(adapter_ipv6)) { adapter_ipv6 = rawsock_get_adapter_ipv6(ifname); masscan->nic[index].src.ipv6.first = adapter_ipv6; masscan->nic[index].src.ipv6.last = adapter_ipv6; masscan->nic[index].src.ipv6.range = 1; } if (ipv6address_is_zero(adapter_ipv6)) { fprintf(stderr, "[-] FAIL: failed to detect IPv6 address of interface \"%s\"\n", ifname); fprintf(stderr, " [hint] did you spell the name correctly?\n"); fprintf(stderr, " [hint] if it has no IP address, manually set with something like " "\"--source-ip 2001:3b8::1234\"\n"); return -1; } fmt = ipv6address_fmt(adapter_ipv6); LOG(1, "[+] source-ip = [%s]\n", fmt.string); is_usable_ipv6 = 1; /* * ROUTER MAC ADDRESS */ *router_mac_ipv6 = masscan->nic[index].router_mac_ipv6; if (masscan->is_offline) { memcpy(router_mac_ipv6->addr, "\x66\x55\x44\x33\x22\x11", 6); } if (macaddress_is_zero(*router_mac_ipv6)) { /* [synchronous] * Wait for router neighbor notification. This may take * some time */ stack_ndpv6_resolve( masscan->nic[index].adapter, adapter_ipv6, *source_mac, router_mac_ipv6); } fmt = macaddress_fmt(*router_mac_ipv6); LOG(1, "[+] router-mac-ipv6 = %s\n", fmt.string); if (macaddress_is_zero(*router_mac_ipv6)) { fmt = ipv4address_fmt(masscan->nic[index].router_ip); LOG(0, "[-] FAIL: NDP timed-out resolving MAC address for router %s: \"%s\"\n", ifname, fmt.string); LOG(0, " [hint] try \"--router-mac-ipv6 66-55-44-33-22-11\" instead to bypass ARP\n"); LOG(0, " [hint] try \"--interface eth0\" to change interface\n"); return -1; } } masscan->nic[index].is_usable = (is_usable_ipv4 & is_usable_ipv6); LOG(2, "[+] if(%s): initialization done.\n", ifname); return 0; } ================================================ FILE: src/main-listscan.c ================================================ #include "masscan.h" #include "util-logger.h" #include "crypto-blackrock.h" void main_listscan(struct Masscan *masscan) { uint64_t i; uint64_t range; uint64_t start; uint64_t end; struct BlackRock blackrock; unsigned increment = masscan->shard.of; uint64_t seed = masscan->seed; /* If called with no ports, then create a pseudo-port needed * for the internal algorithm. */ if (!massip_has_target_ports(&masscan->targets)) rangelist_add_range(&masscan->targets.ports, 80, 80); massip_optimize(&masscan->targets); /* The "range" is the total number of IP/port combinations that * the scan can produce */ range = massip_range(&masscan->targets).lo; infinite: blackrock_init(&blackrock, range, seed, masscan->blackrock_rounds); start = masscan->resume.index + (masscan->shard.one-1); end = range; if (masscan->resume.count && end > start + masscan->resume.count) end = start + masscan->resume.count; end += (uint64_t)(masscan->retries * masscan->max_rate); for (i=start; itargets, xXx, &addr, &port); if (masscan->is_test_csv) { /* [KLUDGE] [TEST] * For testing randomness output, prints last two bytes of * IP address as CSV format for import into spreadsheet */ printf("%u,%u\n",(addr.ipv4>>8)&0xFF, (addr.ipv4>>0)&0xFF); } else if (masscan->targets.count_ports == 1) { ipaddress_formatted_t fmt = ipaddress_fmt(addr); /* This is the normal case */ printf("%s\n", fmt.string); } else { ipaddress_formatted_t fmt = ipaddress_fmt(addr); if (addr.version == 6) printf("[%s]:%u\n", fmt.string, port); else printf("%s:%u\n", fmt.string, port); } i += increment; /* <------ increment by 1 normally, more with shards/NICs */ } if (masscan->is_infinite) { seed++; goto infinite; } } ================================================ FILE: src/main-ptrace.c ================================================ #include "main-ptrace.h" #include "proto-preprocess.h" #include "pixie-timer.h" #include "util-safefunc.h" /*************************************************************************** * Print packet info, when using nmap-style --packet-trace option ***************************************************************************/ void packet_trace(FILE *fp, double pt_start, const unsigned char *px, size_t length, unsigned is_sent) { unsigned x; struct PreprocessedInfo parsed; char from[64]; char to[64]; char sz_type[32]; unsigned type; double timestamp = 1.0 * pixie_gettime() / 1000000.0; unsigned offset; const char *direction; ipaddress_formatted_t fmt; if (is_sent) direction = "SENT"; else direction = "RCVD"; /* parse the packet */ x = preprocess_frame(px, (unsigned)length, 1, &parsed); if (!x) return; offset = parsed.found_offset; /* format the IP addresses into fixed-width fields */ fmt = ipaddress_fmt(parsed.src_ip); snprintf(from, sizeof(from), "[%s]:%u", fmt.string, parsed.port_src); fmt = ipaddress_fmt(parsed.dst_ip); snprintf(to, sizeof(to), "[%s]:%u", fmt.string, parsed.port_dst); switch (parsed.found) { case FOUND_ARP: type = px[offset+6]<<8 | px[offset+7]; *strchr(to, ':') = '\0'; *strchr(from, ':') = '\0'; switch (type) { case 1:safe_strcpy(sz_type, sizeof(sz_type), "request"); break; case 2:safe_strcpy(sz_type, sizeof(sz_type), "response"); break; default: snprintf(sz_type, sizeof(sz_type), "unknown(%u)", type); break; } fprintf(fp, "%s (%5.4f) ARP %-21s > %-21s %s\n", direction, timestamp - pt_start, from, to, sz_type); break; case FOUND_DNS: case FOUND_UDP: fprintf(fp, "%s (%5.4f) UDP %-21s > %-21s \n", direction, timestamp - pt_start, from, to); break; case FOUND_ICMP: fprintf(fp, "%s (%5.4f) ICMP %-21s > %-21s \n", direction, timestamp - pt_start, from, to); break; case FOUND_TCP: type = px[offset+13]; switch (type) { case 0x00: safe_strcpy(sz_type, sizeof(sz_type), "NULL"); break; case 0x01: safe_strcpy(sz_type, sizeof(sz_type), "FIN"); break; case 0x11: safe_strcpy(sz_type, sizeof(sz_type), "FIN-ACK"); break; case 0x19: safe_strcpy(sz_type, sizeof(sz_type), "FIN-ACK-PSH"); break; case 0x02: safe_strcpy(sz_type, sizeof(sz_type), "SYN"); break; case 0x12: safe_strcpy(sz_type, sizeof(sz_type), "SYN-ACK"); break; case 0x04: safe_strcpy(sz_type, sizeof(sz_type), "RST"); break; case 0x14: safe_strcpy(sz_type, sizeof(sz_type), "RST-ACK"); break; case 0x15: safe_strcpy(sz_type, sizeof(sz_type), "RST-FIN-ACK"); break; case 0x10: safe_strcpy(sz_type, sizeof(sz_type), "ACK"); break; case 0x18: safe_strcpy(sz_type, sizeof(sz_type), "ACK-PSH"); break; default: snprintf(sz_type, sizeof(sz_type), "%s%s%s%s%s%s%s%s", (type&0x01)?"FIN":"", (type&0x02)?"SYN":"", (type&0x04)?"RST":"", (type&0x08)?"PSH":"", (type&0x10)?"ACK":"", (type&0x20)?"URG":"", (type&0x40)?"ECE":"", (type&0x80)?"CWR":"" ); break; } if (parsed.app_length) fprintf(fp, "%s (%5.4f) TCP %-21s > %-21s %s %u-bytes\n", direction, timestamp - pt_start, from, to, sz_type, parsed.app_length); else fprintf(fp, "%s (%5.4f) TCP %-21s > %-21s %s\n", direction, timestamp - pt_start, from, to, sz_type); break; case FOUND_IPV6: break; default: fprintf(fp, "%s (%5.4f) UNK %-21s > %-21s [%u]\n", direction, timestamp - pt_start, from, to, parsed.found); break; } } ================================================ FILE: src/main-ptrace.h ================================================ #ifndef masscan_main_ptrace_h #define masscan_main_ptrace_h #include #include void packet_trace(FILE *fp, double pt_trace, const unsigned char *px, size_t length, unsigned is_sent); #endif ================================================ FILE: src/main-readrange.c ================================================ #include "main-readrange.h" #include "masscan.h" #include /*************************************************************************** ***************************************************************************/ /*static unsigned count_cidr_bits(struct Range range) { unsigned i; for (i=0; i<32; i++) { unsigned mask = 0xFFFFFFFF >> i; if ((range.begin & ~mask) == (range.end & ~mask)) { if ((range.begin & mask) == 0 && (range.end & mask) == mask) return i; } } return 0; }*/ /*************************************************************************** ***************************************************************************/ static unsigned count_cidr6_bits(struct Range6 range) { uint64_t i; /* Kludge: can't handle more than 64-bits of CIDR ranges */ if (range.begin.hi != range.begin.lo) return 0; for (i=0; i<64; i++) { uint64_t mask = 0xFFFFFFFFffffffffull >> i; if ((range.begin.lo & ~mask) == (range.end.lo & ~mask)) { if ((range.begin.lo & mask) == 0 && (range.end.lo & mask) == mask) return (unsigned)i; } } return 0; } /*************************************************************************** ***************************************************************************/ void main_readrange(struct Masscan *masscan) { struct RangeList *list4 = &masscan->targets.ipv4; struct Range6List *list6 = &masscan->targets.ipv6; unsigned i; FILE *fp = stdout; for (i=0; icount; i++) { unsigned prefix_length; struct Range range = list4->list[i]; if (range.begin == range.end) { fprintf(fp, "%u.%u.%u.%u\n", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF ); } else if (range_is_cidr(range, &prefix_length)) { fprintf(fp, "%u.%u.%u.%u/%u\n", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF, prefix_length ); } else { fprintf(fp, "%u.%u.%u.%u-%u.%u.%u.%u\n", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF, (range.end>>24)&0xFF, (range.end>>16)&0xFF, (range.end>> 8)&0xFF, (range.end>> 0)&0xFF ); } } for (i=0; icount; i++) { struct Range6 range = list6->list[i]; ipaddress_formatted_t fmt = ipv6address_fmt(range.begin); fprintf(fp, "%s", fmt.string); if (!ipv6address_is_equal(range.begin, range.end)) { unsigned cidr_bits = count_cidr6_bits(range); if (cidr_bits) { fprintf(fp, "/%u", cidr_bits); } else { fmt = ipv6address_fmt(range.end); fprintf(fp, "-%s", fmt.string); } } fprintf(fp, "\n"); } } ================================================ FILE: src/main-readrange.h ================================================ #ifndef MAIN_READRANGE_H #define MAIN_READRANGE_H struct Masscan; void main_readrange(struct Masscan *masscan); #endif ================================================ FILE: src/main-status.c ================================================ /* prints "status" message once per second to the commandline The status message indicates: - the rate in packets-per-second - %done - estimated time remaining of the scan - number of 'tcbs' (TCP control blocks) of active TCP connections */ #include "main-status.h" #include "pixie-timer.h" #include "unusedparm.h" #include "main-globals.h" #include "util-safefunc.h" #include "util-bool.h" #include /*************************************************************************** * Print a status message about once-per-second to the command-line. This * algorithm is a little funky because checking the timestamp on EVERY * packet is slow. ***************************************************************************/ void status_print( struct Status *status, uint64_t count, uint64_t max_count, double pps, uint64_t total_tcbs, uint64_t total_synacks, uint64_t total_syns, uint64_t exiting, bool json_status) { double elapsed_time; double rate; double now; double percent_done; double time_remaining; uint64_t current_tcbs = 0; uint64_t current_synacks = 0; uint64_t current_syns = 0; double tcb_rate = 0.0; double synack_rate = 0.0; double syn_rate = 0.0; double kpps = pps / 1000; const char *fmt; /* Support for --json-status; does not impact legacy/default output */ /** * {"state":"*","rate":{"kpps":24.99,"pps":24985.49,"synps": 27763,"ackps":4,"tcbps":4},"tcb": 33,"syn":246648} */ const char* json_fmt_infinite = "{" "\"state\":\"*\"," "\"rate\":" "{" "\"kpps\":%.2f," "\"pps\":%6$.2f," "\"synps\":%.0f," "\"ackps\":%.0f," "\"tcbps\":%.0f" "}," "\"tcb\":%5$" PRIu64 "," "\"syn\":%7$" PRIu64 "}\n"; /** * {"state":"waiting","rate":{"kpps":0.00,"pps":0.00},"progress":{"percent":21.87,"seconds":4,"found":56,"syn":{"sent": 341436,"total":1561528,"remaining":1220092}}} */ const char *json_fmt_waiting = "{" "\"state\":\"waiting\"," "\"rate\":" "{" "\"kpps\":%.2f," "\"pps\":%5$.2f" "}," "\"progress\":" "{" "\"percent\":%.2f," "\"seconds\":%d," "\"found\":%" PRIu64 "," "\"syn\":" "{" "\"sent\":%6$" PRIu64 "," "\"total\":%7$" PRIu64 "," "\"remaining\":%8$" PRIu64 "}" "}" "}\n"; /** * {"state":"running","rate":{"kpps":24.92,"pps":24923.07},"progress":{"percent":9.77,"eta":{ * "hours":0,"mins":0,"seconds":55},"syn":{"sent": 152510,"total": 1561528,"remaining": 1409018},"found": 27}} */ const char *json_fmt_running = "{" "\"state\":\"running\"," "\"rate\":" "{" "\"kpps\":%.2f," "\"pps\":%7$.2f" "}," "\"progress\":" "{" "\"percent\":%.2f," "\"eta\":" "{" "\"hours\":%u," "\"mins\":%u," "\"seconds\":%u" "}," "\"syn\":" "{" "\"sent\":%8$" PRIu64 "," "\"total\":%9$" PRIu64 "," "\"remaining\":%10$" PRIu64 "}," "\"found\":%6$" PRIu64 "}" "}\n"; /* * #### FUGGLY TIME HACK #### * * PF_RING doesn't timestamp packets well, so we can't base time from * incoming packets. Checking the time ourself is too ugly on per-packet * basis. Therefore, we are going to create a global variable that keeps * the time, and update that variable whenever it's convenient. This * is one of those convenient places. */ global_now = time(0); /* Get the time. NOTE: this is CLOCK_MONOTONIC_RAW on Linux, not * wall-clock time. */ now = (double)pixie_gettime(); /* Figure how many SECONDS have elapsed, in a floating point value. * Since the above timestamp is in microseconds, we need to * shift it by 1-million */ elapsed_time = (now - status->last.clock)/1000000.0; if (elapsed_time <= 0) return; /* Figure out the "packets-per-second" number, which is just: * * rate = packets_sent / elapsed_time; */ rate = (count - status->last.count)*1.0/elapsed_time; /* * Smooth the number by averaging over the last 8 seconds */ status->last_rates[status->last_count++ & 0x7] = rate; rate = status->last_rates[0] + status->last_rates[1] + status->last_rates[2] + status->last_rates[3] + status->last_rates[4] + status->last_rates[5] + status->last_rates[6] + status->last_rates[7] ; rate /= 8; /*if (rate == 0) return;*/ /* * Calculate "percent-done", which is just the total number of * packets sent divided by the number we need to send. */ percent_done = (double)(count*100.0/max_count); /* * Calculate the time remaining in the scan */ time_remaining = (1.0 - percent_done/100.0) * (max_count / rate); /* * some other stats */ if (total_tcbs) { current_tcbs = total_tcbs - status->total_tcbs; status->total_tcbs = total_tcbs; tcb_rate = (1.0*current_tcbs)/elapsed_time; } if (total_synacks) { current_synacks = total_synacks - status->total_synacks; status->total_synacks = total_synacks; synack_rate = (1.0*current_synacks)/elapsed_time; } if (total_syns) { current_syns = total_syns - status->total_syns; status->total_syns = total_syns; syn_rate = (1.0*current_syns)/elapsed_time; } /* * Print the message to so that can be redirected * to a file ( reports what systems were found). */ if (status->is_infinite) { if (json_status == 1) fmt = json_fmt_infinite; else fmt = "rate:%6.2f-kpps, syn/s=%.0f ack/s=%.0f tcb-rate=%.0f, %" PRIu64 "-tcbs, \r"; fprintf(stderr, fmt, kpps, syn_rate, synack_rate, tcb_rate, total_tcbs, pps, count); } else { if (is_tx_done) { if (json_status == 1) fmt = json_fmt_waiting; else fmt = "rate:%6.2f-kpps, %5.2f%% done, waiting %d-secs, found=%" PRIu64 " \r"; fprintf(stderr, fmt, pps/1000.0, percent_done, (int)exiting, total_synacks, pps, count, max_count, max_count-count); } else { if (json_status == 1) fmt = json_fmt_running; else fmt = "rate:%6.2f-kpps, %5.2f%% done,%4u:%02u:%02u remaining, found=%" PRIu64 " \r"; fprintf(stderr, fmt, pps/1000.0, percent_done, (unsigned)(time_remaining/60/60), (unsigned)(time_remaining/60)%60, (unsigned)(time_remaining)%60, total_synacks, pps, count, max_count, max_count-count); } } fflush(stderr); /* * Remember the values to be diffed against the next time around */ status->last.clock = now; status->last.count = count; } /*************************************************************************** ***************************************************************************/ void status_finish(struct Status *status) { UNUSEDPARM(status); fprintf(stderr, " \r"); } /*************************************************************************** ***************************************************************************/ void status_start(struct Status *status) { memset(status, 0, sizeof(*status)); status->last.clock = clock(); status->last.time = time(0); status->last.count = 0; status->timer = 0x1; } ================================================ FILE: src/main-status.h ================================================ #ifndef MAIN_STATUS_H #define MAIN_STATUS_H #include #include #include "util-bool.h" struct Status { struct { double clock; time_t time; uint64_t count; } last; uint64_t timer; unsigned charcount; double last_rates[8]; unsigned last_count; unsigned is_infinite:1; uint64_t total_tcbs; uint64_t total_synacks; uint64_t total_syns; }; void status_print(struct Status *status, uint64_t count, uint64_t max_count, double x, uint64_t total_tcbs, uint64_t total_synacks, uint64_t total_syns, uint64_t exiting, bool json_status); void status_finish(struct Status *status); void status_start(struct Status *status); #endif ================================================ FILE: src/main-throttle.c ================================================ /* Rate-limit/throttler: stops us from transmitting too fast. We can send packets at millions of packets/second. This will melt most networks. Therefore, we need to throttle or rate-limit how fast we go. Since we are sending packet at a rate of 10-million-per-second, we the calculations need to be done in a light-weight manner. For one thing, we can't do a system-call per packet. NOTE: one complication to watch for is the difference between clock time and elapsed time, and that they change. We have to avoid a problem where somebody suspends the computer for a few days, then wake it up, at which point the system tries sending a million packets/second instead of the desired thousand packets/second. */ #include "main-throttle.h" #include "pixie-timer.h" #include "util-logger.h" #include #include /*************************************************************************** ***************************************************************************/ void throttler_start(struct Throttler *throttler, double max_rate) { unsigned i; memset(throttler, 0, sizeof(*throttler)); throttler->max_rate = max_rate; for (i=0; ibuckets)/sizeof(throttler->buckets[0]); i++) { throttler->buckets[i].timestamp = pixie_gettime(); throttler->buckets[i].packet_count = 0; } throttler->batch_size = 1; LOG(1, "[+] starting throttler: rate = %0.2f-pps\n", throttler->max_rate); } /*************************************************************************** * We return the number of packets that can be sent in a batch. Thus, * instead of trying to throttle each packet individually, which has a * high per-packet cost, we try to throttle a bunch at a time. Normally, * this function will return 1, only at high rates does it return larger * numbers. * * NOTE: The minimum value this returns is 1. When it's less than that, * it'll pause and wait until it's ready to send a packet. ***************************************************************************/ uint64_t throttler_next_batch(struct Throttler *throttler, uint64_t packet_count) { uint64_t timestamp; uint64_t index; uint64_t old_timestamp; uint64_t old_packet_count; double current_rate; double max_rate = throttler->max_rate; again: /* NOTE: this uses CLOCK_MONOTONIC_RAW on Linux, so the timstamp doesn't * move forward when the machine is suspended */ timestamp = pixie_gettime(); /* * We record that last 256 buckets, and average the rate over all of * them. */ index = (throttler->index) & 0xFF; throttler->buckets[index].timestamp = timestamp; throttler->buckets[index].packet_count = packet_count; index = (++throttler->index) & 0xFF; old_timestamp = throttler->buckets[index].timestamp; old_packet_count = throttler->buckets[index].packet_count; /* * If the delay is more than 1-second, then we should reset the system * in order to avoid transmitting too fast. */ if (timestamp - old_timestamp > 1000000) { //throttler_start(throttler, throttler->max_rate); throttler->batch_size = 1; goto again; } /* * Calculate the recent rate. * NOTE: this isn't the rate "since start", but only the "recent" rate. * That's so that if the system pauses for a while, we don't flood the * network trying to catch up. */ current_rate = 1.0*(packet_count - old_packet_count)/((timestamp - old_timestamp)/1000000.0); /* * If we've been going too fast, then for a moment, then * try again. */ if (current_rate > max_rate) { double waittime; /* calculate waittime, in seconds */ waittime = (current_rate - max_rate) / throttler->max_rate; /* At higher rates of speed, we don't actually need to wait the full * interval. It's better to have a much smaller interval, so that * we converge back on the true rate faster */ waittime *= 0.1; /* This is in case of gross failure of the system. This should never * actually happen, unless there is a bug. Really, I ought to make * this an 'assert()' instead to fail and fix the bug rather than * silently continuing, but I'm too lazy */ if (waittime > 0.1) waittime = 0.1; /* Since we've exceeded the speed limit, we should reduce the * batch size slightly. We don't do it only by a little bit to * avoid over-correcting. We want to converge on the correct * speed gradually. Note that since this happens hundreds or * thousands of times a second, the convergence is very fast * even with 0.1% adjustment */ throttler->batch_size *= 0.999; /* Now we wait for a bit */ pixie_usleep((uint64_t)(waittime * 1000000.0)); /* There are two choices here. We could either return immediately, * or we can loop around again. Right now, the code loops around * again in order to support very slow rates, such as 0.5 packets * per second. Nobody would want to run a scanner that slowly of * course, but it's great for testing */ //return (uint64_t)throttler->batch_size; goto again; } /* * Calculate how many packets are needed to catch up again to the current * rate, and return that. * * NOTE: this is almost always going to have the value of 1 (one). Only at * very high speeds (above 100,000 packets/second) will this value get * larger. */ throttler->batch_size *= 1.005; if (throttler->batch_size > 10000) throttler->batch_size = 10000; throttler->current_rate = current_rate; throttler->test_timestamp = timestamp; throttler->test_packet_count = packet_count; return (uint64_t)throttler->batch_size; } ================================================ FILE: src/main-throttle.h ================================================ #ifndef MAIN_THROTTLE_H #define MAIN_THROTTLE_H #include struct Throttler { double max_rate; double current_rate; double batch_size; unsigned index; struct { uint64_t timestamp; uint64_t packet_count; } buckets[256]; uint64_t test_timestamp; uint64_t test_packet_count; }; uint64_t throttler_next_batch(struct Throttler *throttler, uint64_t count); void throttler_start(struct Throttler *status, double max_rate); #endif ================================================ FILE: src/main.c ================================================ /* main This includes: * main() * transmit_thread() - transmits probe packets * receive_thread() - receives response packets You'll be wanting to study the transmit/receive threads, because that's where all the action is. This is the lynch-pin of the entire program, so it includes a heckuva lot of headers, and the functions have a lot of local variables. I'm trying to make this file relative "flat" this way so that everything is visible. */ #include "masscan.h" #include "masscan-version.h" #include "masscan-status.h" /* open or closed */ #include "massip-parse.h" #include "massip-port.h" #include "main-status.h" /* printf() regular status updates */ #include "main-throttle.h" /* rate limit */ #include "main-dedup.h" /* ignore duplicate responses */ #include "main-ptrace.h" /* for nmap --packet-trace feature */ #include "main-globals.h" /* all the global variables in the program */ #include "main-readrange.h" #include "crypto-siphash24.h" /* hash function, for hash tables */ #include "crypto-blackrock.h" /* the BlackRock shuffling func */ #include "crypto-lcg.h" /* the LCG randomization func */ #include "crypto-base64.h" /* base64 encode/decode */ #include "templ-pkt.h" /* packet template, that we use to send */ #include "util-logger.h" /* adjust with -v command-line opt */ #include "stack-ndpv6.h" /* IPv6 Neighbor Discovery Protocol */ #include "stack-arpv4.h" /* Handle ARP resolution and requests */ #include "rawsock.h" /* API on top of Linux, Windows, Mac OS X*/ #include "rawsock-adapter.h" /* Get Ethernet adapter configuration */ #include "rawsock-pcapfile.h" /* for saving pcap files w/ raw packets */ #include "syn-cookie.h" /* for SYN-cookies on send */ #include "output.h" /* for outputting results */ #include "rte-ring.h" /* producer/consumer ring buffer */ #include "stub-pcap.h" /* dynamically load libpcap library */ #include "smack.h" /* Aho-corasick state-machine pattern-matcher */ #include "pixie-timer.h" /* portable time functions */ #include "pixie-threads.h" /* portable threads */ #include "pixie-backtrace.h" /* maybe print backtrace on crash */ #include "templ-payloads.h" /* UDP packet payloads */ #include "in-binary.h" /* convert binary output to XML/JSON */ #include "vulncheck.h" /* checking vulns like monlist, poodle, heartblee */ #include "scripting.h" #include "read-service-probes.h" #include "misc-rstfilter.h" #include "proto-x509.h" #include "proto-arp.h" /* for responding to ARP requests */ #include "proto-banner1.h" /* for snatching banners from systems */ #include "stack-tcp-core.h" /* for TCP/IP connection table */ #include "proto-preprocess.h" /* quick parse of packets */ #include "proto-icmp.h" /* handle ICMP responses */ #include "proto-udp.h" /* handle UDP responses */ #include "proto-snmp.h" /* parse SNMP responses */ #include "proto-ntp.h" /* parse NTP responses */ #include "proto-coap.h" /* CoAP selftest */ #include "proto-zeroaccess.h" #include "proto-sctp.h" #include "proto-oproto.h" /* Other protocols on top of IP */ #include "util-malloc.h" #include "util-checksum.h" #include #include #include #include #include #include #include #if defined(WIN32) #include #if defined(_MSC_VER) #pragma comment(lib, "Ws2_32.lib") #endif #else #include #include #include #endif /* * yea I know globals suck */ unsigned volatile is_tx_done = 0; unsigned volatile is_rx_done = 0; time_t global_now; uint64_t usec_start; /*************************************************************************** * We create a pair of transmit/receive threads for each network adapter. * This structure contains the parameters we send to each pair. ***************************************************************************/ struct ThreadPair { /** This points to the central configuration. Note that it's 'const', * meaning that the thread cannot change the contents. That'd be * unsafe */ const struct Masscan *masscan; /** The adapter used by the thread-pair. Normally, thread-pairs have * their own network adapter, especially when doing PF_RING * clustering. */ struct Adapter *adapter; struct stack_t *stack; /** * The index of the network adapter that we are using for this * thread-pair. This is an index into the "masscan->nic[]" * array. * * NOTE: this is also the "thread-id", because we create one * transmit/receive thread pair per NIC. */ unsigned nic_index; /** * A copy of the master 'index' variable. This is just advisory for * other threads, to tell them how far we've gotten. */ volatile uint64_t my_index; /* This is used both by the transmit and receive thread for * formatting packets */ struct TemplateSet tmplset[1]; /** * The current IP address we are using for transmit/receive. */ struct stack_src_t _src_; macaddress_t source_mac; macaddress_t router_mac_ipv4; macaddress_t router_mac_ipv6; unsigned done_transmitting; unsigned done_receiving; double pt_start; struct Throttler throttler[1]; uint64_t *total_synacks; uint64_t *total_tcbs; uint64_t *total_syns; size_t thread_handle_xmit; size_t thread_handle_recv; }; struct source_t { unsigned ipv4; unsigned ipv4_mask; unsigned port; unsigned port_mask; ipv6address ipv6; ipv6address ipv6_mask; }; /*************************************************************************** * We support a range of source IP/port. This function converts that * range into useful variables we can use to pick things form that range. ***************************************************************************/ static void adapter_get_source_addresses(const struct Masscan *masscan, unsigned nic_index, struct source_t *src) { const struct stack_src_t *ifsrc = &masscan->nic[nic_index].src; static ipv6address mask = {~0ULL, ~0ULL}; src->ipv4 = ifsrc->ipv4.first; src->ipv4_mask = ifsrc->ipv4.last - ifsrc->ipv4.first; src->port = ifsrc->port.first; src->port_mask = ifsrc->port.last - ifsrc->port.first; src->ipv6 = ifsrc->ipv6.first; /* TODO: currently supports only a single address. This needs to * be fixed to support a list of addresses */ src->ipv6_mask = mask; } /*************************************************************************** * This thread spews packets as fast as it can * * THIS IS WHERE ALL THE EXCITEMENT HAPPENS!!!! * 90% of CPU cycles are in the function. * ***************************************************************************/ static void transmit_thread(void *v) /*aka. scanning_thread() */ { struct ThreadPair *parms = (struct ThreadPair *)v; uint64_t i; uint64_t start; uint64_t end; const struct Masscan *masscan = parms->masscan; uint64_t retries = masscan->retries; uint64_t rate = (uint64_t)masscan->max_rate; unsigned r = (unsigned)retries + 1; uint64_t range; uint64_t range_ipv6; struct BlackRock blackrock; uint64_t count_ipv4 = rangelist_count(&masscan->targets.ipv4); uint64_t count_ipv6 = range6list_count(&masscan->targets.ipv6).lo; struct Throttler *throttler = parms->throttler; struct TemplateSet pkt_template = templ_copy(parms->tmplset); struct Adapter *adapter = parms->adapter; uint64_t packets_sent = 0; unsigned increment = masscan->shard.of * masscan->nic_count; struct source_t src; uint64_t seed = masscan->seed; uint64_t repeats = 0; /* --infinite repeats */ uint64_t *status_syn_count; uint64_t entropy = masscan->seed; /* Wait to make sure receive_thread is ready */ pixie_usleep(1000000); LOG(1, "[+] starting transmit thread #%u\n", parms->nic_index); /* export a pointer to this variable outside this threads so * that the 'status' system can print the rate of syns we are * sending */ status_syn_count = MALLOC(sizeof(uint64_t)); *status_syn_count = 0; parms->total_syns = status_syn_count; /* Normally, we have just one source address. In special cases, though * we can have multiple. */ adapter_get_source_addresses(masscan, parms->nic_index, &src); /* "THROTTLER" rate-limits how fast we transmit, set with the * --max-rate parameter */ throttler_start(throttler, masscan->max_rate/masscan->nic_count); infinite: /* Create the shuffler/randomizer. This creates the 'range' variable, * which is simply the number of IP addresses times the number of * ports. * IPv6: low index will pick addresses from the IPv6 ranges, and high * indexes will pick addresses from the IPv4 ranges. */ range = count_ipv4 * rangelist_count(&masscan->targets.ports) + count_ipv6 * rangelist_count(&masscan->targets.ports); range_ipv6 = count_ipv6 * rangelist_count(&masscan->targets.ports); blackrock_init(&blackrock, range, seed, masscan->blackrock_rounds); /* Calculate the 'start' and 'end' of a scan. One reason to do this is * to support --shard, so that multiple machines can co-operate on * the same scan. Another reason to do this is so that we can bleed * a little bit past the end when we have --retries. Yet another * thing to do here is deal with multiple network adapters, which * is essentially the same logic as shards. */ start = masscan->resume.index + (masscan->shard.one-1) * masscan->nic_count + parms->nic_index; end = range; if (masscan->resume.count && end > start + masscan->resume.count) end = start + masscan->resume.count; end += retries * range; /* ----------------- * the main loop * -----------------*/ LOG(3, "THREAD: xmit: starting main loop: [%llu..%llu]\n", start, end); for (i=start; istack, adapter, &packets_sent, &batch_size); /* * Transmit a bunch of packets. At any rate slower than 100,000 * packets/second, the 'batch_size' is likely to be 1. At higher * rates, we can't afford to throttle on a per-packet basis and * instead throttle on a per-batch basis. In other words, throttle * based on 2-at-a-time, 3-at-time, and so on, with the batch * size increasing as the packet rate increases. This gives us * very precise packet-timing for low rates below 100,000 pps, * while not incurring the overhead for high packet rates. */ while (batch_size && i < end) { uint64_t xXx; uint64_t cookie; /* * RANDOMIZE THE TARGET: * This is kinda a tricky bit that picks a random IP and port * number in order to scan. We monotonically increment the * index 'i' from [0..range]. We then shuffle (randomly transmog) * that index into some other, but unique/1-to-1, number in the * same range. That way we visit all targets, but in a random * order. Then, once we've shuffled the index, we "pick" the * IP address and port that the index refers to. */ xXx = (i + (r--) * rate); if (rate > range) xXx %= range; else while (xXx >= range) xXx -= range; xXx = blackrock_shuffle(&blackrock, xXx); if (xXx < range_ipv6) { ipv6address ip_them; unsigned port_them; ipv6address ip_me; unsigned port_me; ip_them = range6list_pick(&masscan->targets.ipv6, xXx % count_ipv6); port_them = rangelist_pick(&masscan->targets.ports, xXx / count_ipv6); ip_me = src.ipv6; port_me = src.port; cookie = syn_cookie_ipv6(ip_them, port_them, ip_me, port_me, entropy); rawsock_send_probe_ipv6( adapter, ip_them, port_them, ip_me, port_me, (unsigned)cookie, !batch_size, /* flush queue on last packet in batch */ &pkt_template ); /* Our index selects an IPv6 target */ } else { /* Our index selects an IPv4 target. In other words, low numbers * index into the IPv6 ranges, and high numbers index into the * IPv4 ranges. */ ipv4address ip_them; ipv4address port_them; unsigned ip_me; unsigned port_me; xXx -= range_ipv6; ip_them = rangelist_pick(&masscan->targets.ipv4, xXx % count_ipv4); port_them = rangelist_pick(&masscan->targets.ports, xXx / count_ipv4); /* * SYN-COOKIE LOGIC * Figure out the source IP/port, and the SYN cookie */ if (src.ipv4_mask > 1 || src.port_mask > 1) { uint64_t ck = syn_cookie_ipv4((unsigned)(i+repeats), (unsigned)((i+repeats)>>32), (unsigned)xXx, (unsigned)(xXx>>32), entropy); port_me = src.port + (ck & src.port_mask); ip_me = src.ipv4 + ((ck>>16) & src.ipv4_mask); } else { ip_me = src.ipv4; port_me = src.port; } cookie = syn_cookie_ipv4(ip_them, port_them, ip_me, port_me, entropy); /* * SEND THE PROBE * This is sorta the entire point of the program, but little * exciting happens here. The thing to note that this may * be a "raw" transmit that bypasses the kernel, meaning * we can call this function millions of times a second. */ rawsock_send_probe_ipv4( adapter, ip_them, port_them, ip_me, port_me, (unsigned)cookie, !batch_size, /* flush queue on last packet in batch */ &pkt_template ); } batch_size--; packets_sent++; (*status_syn_count)++; /* * SEQUENTIALLY INCREMENT THROUGH THE RANGE * Yea, I know this is a puny 'i++' here, but it's a core feature * of the system that is linearly increments through the range, * but produces from that a shuffled sequence of targets (as * described above). Because we are linearly incrementing this * number, we can do lots of creative stuff, like doing clever * retransmits and sharding. */ if (r == 0) { i += increment; /* <------ increment by 1 normally, more with shards/nics */ r = (unsigned)retries + 1; } } /* end of batch */ /* save our current location for resuming, if the user pressed * to exit early */ parms->my_index = i; /* If the user pressed , then we need to exit. In case * the user wants to --resume the scan later, we save the current * state in a file */ if (is_tx_done) { break; } } /* * --infinite * For load testing, go around and do this again */ if (masscan->is_infinite && !is_tx_done) { seed++; repeats++; goto infinite; } /* * Flush any untransmitted packets. High-speed mechanisms like Windows * "sendq" and Linux's "PF_RING" queue packets and transmit many together, * so there may be some packets that we've queued but not yet transmitted. * This call makes sure they are transmitted. */ rawsock_flush(adapter); /* * Wait until the receive thread realizes the scan is over */ LOG(1, "[+] transmit thread #%u complete\n", parms->nic_index); /* * We are done transmitting. However, response packets will take several * seconds to arrive. Therefore, sit in short loop waiting for those * packets to arrive. Pressing a second time will exit this * prematurely. */ while (!is_rx_done) { unsigned k; uint64_t batch_size; for (k=0; k<1000; k++) { /* * Only send a few packets at a time, throttled according to the max * --max-rate set by the user */ batch_size = throttler_next_batch(throttler, packets_sent); /* Transmit packets from the receive thread */ stack_flush_packets( parms->stack, adapter, &packets_sent, &batch_size); /* Make sure they've actually been transmitted, not just queued up for * transmit */ rawsock_flush(adapter); pixie_usleep(100); } } /* Thread is about to exit */ parms->done_transmitting = 1; LOG(1, "[+] exiting transmit thread #%u \n", parms->nic_index); } /*************************************************************************** ***************************************************************************/ static unsigned is_nic_port(const struct Masscan *masscan, unsigned ip) { unsigned i; for (i=0; inic_count; i++) if (is_my_port(&masscan->nic[i].src, ip)) return 1; return 0; } static unsigned is_ipv6_multicast(ipaddress ip_me) { /* If this is an IPv6 multicast packet, one sent to the IPv6 * address with a prefix of FF02::/16 */ return ip_me.version == 6 && (ip_me.ipv6.hi>>48ULL) == 0xFF02; } /*************************************************************************** * * Asynchronous receive thread * * The transmit and receive threads run independently of each other. There * is no record what was transmitted. Instead, the transmit thread sets a * "SYN-cookie" in transmitted packets, which the receive thread will then * use to match up requests with responses. ***************************************************************************/ static void receive_thread(void *v) { struct ThreadPair *parms = (struct ThreadPair *)v; const struct Masscan *masscan = parms->masscan; struct Adapter *adapter = parms->adapter; int data_link = stack_if_datalink(adapter); struct Output *out; struct DedupTable *dedup; struct PcapFile *pcapfile = NULL; struct TCP_ConnectionTable *tcpcon = 0; uint64_t *status_synack_count; uint64_t *status_tcb_count; uint64_t entropy = masscan->seed; struct ResetFilter *rf; struct stack_t *stack = parms->stack; struct source_t src = {0}; /* For reducing RST responses, see rstfilter_is_filter() below */ rf = rstfilter_create(entropy, 16384); /* some status variables */ status_synack_count = MALLOC(sizeof(uint64_t)); *status_synack_count = 0; parms->total_synacks = status_synack_count; status_tcb_count = MALLOC(sizeof(uint64_t)); *status_tcb_count = 0; parms->total_tcbs = status_tcb_count; LOG(1, "[+] starting receive thread #%u\n", parms->nic_index); /* Lock this thread to a CPU. Transmit threads are on even CPUs, * receive threads on odd CPUs */ if (pixie_cpu_get_count() > 1) { unsigned cpu_count = pixie_cpu_get_count(); unsigned cpu = parms->nic_index * 2 + 1; while (cpu >= cpu_count) { cpu -= cpu_count; cpu++; } //TODO: //pixie_cpu_set_affinity(cpu); } /* * If configured, open a --pcap file for saving raw packets. This is * so that we can debug scans, but also so that we can look at the * strange things people send us. Note that we don't record transmitted * packets, just the packets we've received. */ if (masscan->pcap_filename[0]) { pcapfile = pcapfile_openwrite(masscan->pcap_filename, 1); } /* * Open output. This is where results are reported when saving * the --output-format to the --output-filename */ out = output_create(masscan, parms->nic_index); /* * Create deduplication table. This is so when somebody sends us * multiple responses, we only record the first one. */ dedup = dedup_create(); /* * Create a TCP connection table (per thread pair) for interacting with live * connections when doing --banners */ if (masscan->is_banners) { struct TcpCfgPayloads *pay; size_t i; /* * Create TCP connection table */ tcpcon = tcpcon_create_table( (size_t)((masscan->max_rate/5) / masscan->nic_count), parms->stack, &parms->tmplset->pkts[Proto_TCP], output_report_banner, out, masscan->tcb.timeout, masscan->seed ); /* * Initialize TCP scripting */ scripting_init_tcp(tcpcon, masscan->scripting.L); /* * Get the possible source IP addresses and ports that masscan * might be using to transmit from. */ adapter_get_source_addresses(masscan, parms->nic_index, &src); /* * Set some flags [kludge] */ tcpcon_set_banner_flags(tcpcon, masscan->is_capture_cert, masscan->is_capture_servername, masscan->is_capture_html, masscan->is_capture_heartbleed, masscan->is_capture_ticketbleed); if (masscan->is_hello_smbv1) tcpcon_set_parameter(tcpcon, "hello", 1, "smbv1"); if (masscan->is_hello_http) tcpcon_set_parameter(tcpcon, "hello", 1, "http"); if (masscan->is_hello_ssl) tcpcon_set_parameter(tcpcon, "hello", 1, "ssl"); if (masscan->is_heartbleed) tcpcon_set_parameter(tcpcon, "heartbleed", 1, "1"); if (masscan->is_ticketbleed) tcpcon_set_parameter(tcpcon, "ticketbleed", 1, "1"); if (masscan->is_poodle_sslv3) tcpcon_set_parameter(tcpcon, "sslv3", 1, "1"); if (masscan->http.payload) tcpcon_set_parameter( tcpcon, "http-payload", masscan->http.payload_length, masscan->http.payload); if (masscan->http.user_agent) tcpcon_set_parameter( tcpcon, "http-user-agent", masscan->http.user_agent_length, masscan->http.user_agent); if (masscan->http.host) tcpcon_set_parameter( tcpcon, "http-host", masscan->http.host_length, masscan->http.host); if (masscan->http.method) tcpcon_set_parameter( tcpcon, "http-method", masscan->http.method_length, masscan->http.method); if (masscan->http.url) tcpcon_set_parameter( tcpcon, "http-url", masscan->http.url_length, masscan->http.url); if (masscan->http.version) tcpcon_set_parameter( tcpcon, "http-version", masscan->http.version_length, masscan->http.version); if (masscan->tcp_connection_timeout) { char foo[64]; snprintf(foo, sizeof(foo), "%u", masscan->tcp_connection_timeout); tcpcon_set_parameter( tcpcon, "timeout", strlen(foo), foo); } if (masscan->tcp_hello_timeout) { char foo[64]; snprintf(foo, sizeof(foo), "%u", masscan->tcp_hello_timeout); tcpcon_set_parameter( tcpcon, "hello-timeout", strlen(foo), foo); } for (i=0; ihttp.headers_count; i++) { tcpcon_set_http_header(tcpcon, masscan->http.headers[i].name, masscan->http.headers[i].value_length, masscan->http.headers[i].value, http_field_replace); } for (i=0; ihttp.cookies_count; i++) { tcpcon_set_http_header(tcpcon, "Cookie", masscan->http.cookies[i].value_length, masscan->http.cookies[i].value, http_field_add); } for (i=0; ihttp.remove_count; i++) { tcpcon_set_http_header(tcpcon, masscan->http.headers[i].name, 0, 0, http_field_remove); } for (pay = masscan->payloads.tcp; pay; pay = pay->next) { char name[64]; snprintf(name, sizeof(name), "hello-string[%u]", pay->port); tcpcon_set_parameter( tcpcon, name, strlen(pay->payload_base64), pay->payload_base64); } } /* * In "offline" mode, we don't have any receive threads, so simply * wait until transmitter thread is done then go to the end */ if (masscan->is_offline) { while (!is_rx_done) pixie_usleep(10000); parms->done_receiving = 1; goto end; } /* * Receive packets. This is where we catch any responses and print * them to the terminal. */ LOG(2, "[+] THREAD: recv: starting main loop\n"); while (!is_rx_done) { int status; unsigned length; unsigned secs; unsigned usecs; const unsigned char *px; int err; unsigned x; struct PreprocessedInfo parsed; ipaddress ip_me; unsigned port_me; ipaddress ip_them; unsigned port_them; unsigned seqno_me; unsigned seqno_them; unsigned cookie; unsigned Q = 0; /* * RECEIVE * * This is the boring part of actually receiving a packet */ err = rawsock_recv_packet( adapter, &length, &secs, &usecs, &px); if (err != 0) { if (tcpcon) tcpcon_timeouts(tcpcon, (unsigned)time(0), 0); continue; } /* * Do any TCP event timeouts based on the current timestamp from * the packet. For example, if the connection has been open for * around 10 seconds, we'll close the connection. (--banners) */ if (tcpcon) { tcpcon_timeouts(tcpcon, secs, usecs); } if (length > 1514) continue; /* * "Preprocess" the response packet. This means to go through and * figure out where the TCP/IP headers are and the locations of * some fields, like IP address and port numbers. */ x = preprocess_frame(px, length, data_link, &parsed); if (!x) continue; /* corrupt packet */ ip_me = parsed.dst_ip; ip_them = parsed.src_ip; port_me = parsed.port_dst; port_them = parsed.port_src; seqno_them = TCP_SEQNO(px, parsed.transport_offset); seqno_me = TCP_ACKNO(px, parsed.transport_offset); assert(ip_me.version != 0); assert(ip_them.version != 0); switch (parsed.ip_protocol) { case 132: /* SCTP */ cookie = syn_cookie(ip_them, port_them | (Proto_SCTP<<16), ip_me, port_me, entropy) & 0xFFFFFFFF; break; default: cookie = syn_cookie(ip_them, port_them, ip_me, port_me, entropy) & 0xFFFFFFFF; } /* verify: my IP address */ if (!is_my_ip(stack->src, ip_me)) { /* NDP Neighbor Solicitations don't come to our IP address, but to * a multicast address */ if (is_ipv6_multicast(ip_me)) { if (parsed.found == FOUND_NDPv6 && parsed.opcode == 135) { stack_ndpv6_incoming_request(stack, &parsed, px, length); } } continue; } /* * Handle non-TCP protocols */ switch (parsed.found) { case FOUND_NDPv6: switch (parsed.opcode) { case 133: /* Router Solicitation */ /* Ignore router solicitations, since we aren't a router */ continue; case 134: /* Router advertisement */ /* TODO: We need to process router advertisements while scanning * so that we can print warning messages if router information * changes while scanning. */ continue; case 135: /* Neighbor Solicitation */ /* When responses come back from our scans, the router will send us * these packets. We need to respond to them, so that the router * can then forward the packets to us. If we don't respond, we'll * get no responses. */ stack_ndpv6_incoming_request(stack, &parsed, px, length); continue; case 136: /* Neighbor Advertisement */ /* TODO: If doing an --ndpscan, the scanner subsystem needs to deal * with these */ continue; case 137: /* Redirect */ /* We ignore these, since we really don't have the capability to send * packets to one router for some destinations and to another router * for other destinations */ continue; default: break; } continue; case FOUND_ARP: LOGip(2, ip_them, 0, "-> ARP [%u] \n", px[parsed.found_offset]); switch (parsed.opcode) { case 1: /* request */ /* This function will transmit a "reply" to somebody's ARP request * for our IP address (as part of our user-mode TCP/IP). * Since we completely bypass the TCP/IP stack, we have to handle ARPs * ourself, or the router will lose track of us.*/ stack_arp_incoming_request(stack, ip_me.ipv4, parms->source_mac, px, length); break; case 2: /* response */ /* This is for "arp scan" mode, where we are ARPing targets rather * than port scanning them */ /* If we aren't doing an ARP scan, then ignore ARP responses */ if (!masscan->scan_type.arp) break; /* If this response isn't in our range, then ignore it */ if (!rangelist_is_contains(&masscan->targets.ipv4, ip_them.ipv4)) break; /* Ignore duplicates */ if (dedup_is_duplicate(dedup, ip_them, 0, ip_me, 0)) continue; /* ...everything good, so now report this response */ arp_recv_response(out, secs, px, length, &parsed); break; } continue; case FOUND_UDP: case FOUND_DNS: if (!is_nic_port(masscan, port_me)) continue; if (parms->masscan->nmap.packet_trace) packet_trace(stdout, parms->pt_start, px, length, 0); handle_udp(out, secs, px, length, &parsed, entropy); continue; case FOUND_ICMP: handle_icmp(out, secs, px, length, &parsed, entropy); continue; case FOUND_SCTP: handle_sctp(out, secs, px, length, cookie, &parsed, entropy); break; case FOUND_OPROTO: /* other IP proto */ handle_oproto(out, secs, px, length, &parsed, entropy); break; case FOUND_TCP: /* fall down to below */ break; default: continue; } /* verify: my port number */ if (!is_my_port(stack->src, port_me)) continue; if (parms->masscan->nmap.packet_trace) packet_trace(stdout, parms->pt_start, px, length, 0); Q = 0; /* Save raw packet in --pcap file */ if (pcapfile) { pcapfile_writeframe( pcapfile, px, length, length, secs, usecs); } { char buf[64]; LOGip(5, ip_them, port_them, "-> TCP ackno=0x%08x flags=0x%02x(%s)\n", seqno_me, TCP_FLAGS(px, parsed.transport_offset), reason_string(TCP_FLAGS(px, parsed.transport_offset), buf, sizeof(buf))); } /* If recording --banners, create a new "TCP Control Block (TCB)" */ if (tcpcon) { struct TCP_Control_Block *tcb; /* does a TCB already exist for this connection? */ tcb = tcpcon_lookup_tcb(tcpcon, ip_me, ip_them, port_me, port_them); if (TCP_IS_SYNACK(px, parsed.transport_offset)) { if (cookie != seqno_me - 1) { ipaddress_formatted_t fmt = ipaddress_fmt(ip_them); LOG(0, "%s - bad cookie: ackno=0x%08x expected=0x%08x\n", fmt.string, seqno_me-1, cookie); continue; } if (tcb == NULL) { tcb = tcpcon_create_tcb(tcpcon, ip_me, ip_them, port_me, port_them, seqno_me, seqno_them+1, parsed.ip_ttl, NULL, secs, usecs); (*status_tcb_count)++; } Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_SYNACK, 0, 0, secs, usecs, seqno_them+1, seqno_me); } else if (tcb) { /* If this is an ACK, then handle that first */ if (TCP_IS_ACK(px, parsed.transport_offset)) { Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_ACK, 0, 0, secs, usecs, seqno_them, seqno_me); } /* If this contains payload, handle that second */ if (parsed.app_length) { Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_DATA, px + parsed.app_offset, parsed.app_length, secs, usecs, seqno_them, seqno_me); } /* If this is a FIN, handle that. Note that ACK + * payload + FIN can come together */ if (TCP_IS_FIN(px, parsed.transport_offset) && !TCP_IS_RST(px, parsed.transport_offset)) { Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_FIN, 0, 0, secs, usecs, seqno_them + parsed.app_length, /* the FIN comes after any data in the packet */ seqno_me); } /* If this is a RST, then we'll be closing the connection */ if (TCP_IS_RST(px, parsed.transport_offset)) { Q += stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_RST, 0, 0, secs, usecs, seqno_them, seqno_me); } } else if (TCP_IS_FIN(px, parsed.transport_offset)) { ipaddress_formatted_t fmt; /* * NO TCB! * This happens when we've sent a FIN, deleted our connection, * but the other side didn't get the packet. */ fmt = ipaddress_fmt(ip_them); LOG(4, "%s: received FIN but no TCB\n", fmt.string); if (TCP_IS_RST(px, parsed.transport_offset)) ; /* ignore if it's own TCP flag is set */ else { int is_suppress; is_suppress = rstfilter_is_filter(rf, ip_me, port_me, ip_them, port_them); if (!is_suppress) tcpcon_send_RST( tcpcon, ip_me, ip_them, port_me, port_them, seqno_them, seqno_me); } } } if (Q == 0) ; //printf("\nerr\n"); if (TCP_IS_SYNACK(px, parsed.transport_offset) || TCP_IS_RST(px, parsed.transport_offset)) { /* figure out the status */ status = PortStatus_Unknown; if (TCP_IS_SYNACK(px, parsed.transport_offset)) status = PortStatus_Open; if (TCP_IS_RST(px, parsed.transport_offset)) { status = PortStatus_Closed; } /* verify: syn-cookies */ if (cookie != seqno_me - 1) { ipaddress_formatted_t fmt = ipaddress_fmt(ip_them); LOG(2, "%s - bad cookie: ackno=0x%08x expected=0x%08x\n", fmt.string, seqno_me-1, cookie); continue; } /* verify: ignore duplicates */ if (dedup_is_duplicate(dedup, ip_them, port_them, ip_me, port_me)) continue; /* keep statistics on number received */ if (TCP_IS_SYNACK(px, parsed.transport_offset)) (*status_synack_count)++; /* * This is where we do the output */ output_report_status( out, global_now, status, ip_them, 6, /* ip proto = tcp */ port_them, px[parsed.transport_offset + 13], /* tcp flags */ parsed.ip_ttl, parsed.mac_src ); /* * Send RST so other side isn't left hanging (only doing this in * complete stateless mode where we aren't tracking banners) */ if (tcpcon == NULL && !masscan->is_noreset) tcp_send_RST( &parms->tmplset->pkts[Proto_TCP], parms->stack, ip_them, ip_me, port_them, port_me, 0, seqno_me); } } LOG(1, "[+] exiting receive thread #%u \n", parms->nic_index); /* * cleanup */ end: if (tcpcon) tcpcon_destroy_table(tcpcon); dedup_destroy(dedup); output_destroy(out); if (pcapfile) pcapfile_close(pcapfile); /*TODO: free stack packet buffers */ /* Thread is about to exit */ parms->done_receiving = 1; } /*************************************************************************** * We trap the so that instead of exiting immediately, we sit in * a loop for a few seconds waiting for any late response. But, the user * can press a second time to exit that waiting. ***************************************************************************/ static void control_c_handler(int x) { static unsigned control_c_pressed = 0; static unsigned control_c_pressed_again = 0; if (control_c_pressed == 0) { fprintf(stderr, "waiting several seconds to exit..." " \n" ); fflush(stderr); control_c_pressed = 1+x; is_tx_done = control_c_pressed; } else { if (is_rx_done) { fprintf(stderr, "\nERROR: threads not exiting %d\n", is_rx_done); if (is_rx_done++ > 1) exit(1); } else { control_c_pressed_again = 1; is_rx_done = control_c_pressed_again; } } } /*************************************************************************** * Called from main() to initiate the scan. * Launches the 'transmit_thread()' and 'receive_thread()' and waits for * them to exit. ***************************************************************************/ static int main_scan(struct Masscan *masscan) { struct ThreadPair parms_array[8]; uint64_t count_ips; uint64_t count_ports; uint64_t range; unsigned index; time_t now = time(0); struct Status status; uint64_t min_index = UINT64_MAX; struct MassVulnCheck *vulncheck = NULL; struct stack_t *stack; memset(parms_array, 0, sizeof(parms_array)); /* * Vuln check initialization */ if (masscan->vuln_name) { unsigned i; unsigned is_error; vulncheck = vulncheck_lookup(masscan->vuln_name); /* If no ports specified on command-line, grab default ports */ is_error = 0; if (rangelist_count(&masscan->targets.ports) == 0) rangelist_parse_ports(&masscan->targets.ports, vulncheck->ports, &is_error, 0); /* Kludge: change normal port range to vulncheck range */ for (i=0; itargets.ports.count; i++) { struct Range *r = &masscan->targets.ports.list[i]; r->begin = (r->begin&0xFFFF) | Templ_VulnCheck; r->end = (r->end & 0xFFFF) | Templ_VulnCheck; } } /* * Initialize the task size */ count_ips = rangelist_count(&masscan->targets.ipv4) + range6list_count(&masscan->targets.ipv6).lo; if (count_ips == 0) { LOG(0, "FAIL: target IP address list empty\n"); LOG(0, " [hint] try something like \"--range 10.0.0.0/8\"\n"); LOG(0, " [hint] try something like \"--range 192.168.0.100-192.168.0.200\"\n"); return 1; } count_ports = rangelist_count(&masscan->targets.ports); if (count_ports == 0) { LOG(0, "FAIL: no ports were specified\n"); LOG(0, " [hint] try something like \"-p80,8000-9000\"\n"); LOG(0, " [hint] try something like \"--ports 0-65535\"\n"); return 1; } range = count_ips * count_ports; range += (uint64_t)(masscan->retries * range); /* * If doing an ARP scan, then don't allow port scanning */ if (rangelist_is_contains(&masscan->targets.ports, Templ_ARP)) { if (masscan->targets.ports.count != 1) { LOG(0, "FAIL: cannot arpscan and portscan at the same time\n"); return 1; } } /* * If the IP address range is very big, then require that that the * user apply an exclude range */ if (count_ips > 1000000000ULL && rangelist_count(&masscan->exclude.ipv4) == 0) { LOG(0, "FAIL: range too big, need confirmation\n"); LOG(0, " [hint] to prevent accidents, at least one --exclude must be specified\n"); LOG(0, " [hint] use \"--exclude 255.255.255.255\" as a simple confirmation\n"); exit(1); } /* * trim the nmap UDP payloads down to only those ports we are using. This * makes lookups faster at high packet rates. */ payloads_udp_trim(masscan->payloads.udp, &masscan->targets); payloads_oproto_trim(masscan->payloads.oproto, &masscan->targets); #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif /* * Start scanning threats for each adapter */ for (index=0; indexnic_count; index++) { struct ThreadPair *parms = &parms_array[index]; int err; parms->masscan = masscan; parms->nic_index = index; parms->my_index = masscan->resume.index; parms->done_transmitting = 0; parms->done_receiving = 0; /* needed for --packet-trace option so that we know when we started * the scan */ parms->pt_start = 1.0 * pixie_gettime() / 1000000.0; /* * Turn the adapter on, and get the running configuration */ err = masscan_initialize_adapter( masscan, index, &parms->source_mac, &parms->router_mac_ipv4, &parms->router_mac_ipv6 ); if (err != 0) exit(1); parms->adapter = masscan->nic[index].adapter; if (!masscan->nic[index].is_usable) { LOG(0, "FAIL: failed to detect IP of interface\n"); LOG(0, " [hint] did you spell the name correctly?\n"); LOG(0, " [hint] if it has no IP address, " "manually set with \"--adapter-ip 192.168.100.5\"\n"); exit(1); } /* * Initialize the TCP packet template. The way this works is that * we parse an existing TCP packet, and use that as the template for * scanning. Then, we adjust the template with additional features, * such as the IP address and so on. */ parms->tmplset->vulncheck = vulncheck; template_packet_init( parms->tmplset, parms->source_mac, parms->router_mac_ipv4, parms->router_mac_ipv6, masscan->payloads.udp, masscan->payloads.oproto, stack_if_datalink(masscan->nic[index].adapter), masscan->seed, masscan->templ_opts); /* * Set the "source port" of everything we transmit. */ if (masscan->nic[index].src.port.range == 0) { unsigned port = 40000 + now % 20000; masscan->nic[index].src.port.first = port; masscan->nic[index].src.port.last = port + 16; masscan->nic[index].src.port.range = 16; } stack = stack_create(parms->source_mac, &masscan->nic[index].src); parms->stack = stack; /* * Set the "TTL" (IP time-to-live) of everything we send. */ if (masscan->nmap.ttl) template_set_ttl(parms->tmplset, masscan->nmap.ttl); if (masscan->nic[0].is_vlan) template_set_vlan(parms->tmplset, masscan->nic[0].vlan_id); /* * trap to pause */ signal(SIGINT, control_c_handler); } /* * Print helpful text */ { char buffer[80]; struct tm x; now = time(0); safe_gmtime(&x, &now); strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S GMT", &x); LOG(0, "Starting masscan " MASSCAN_VERSION " (http://bit.ly/14GZzcT) at %s\n", buffer); if (count_ports == 1 && \ masscan->targets.ports.list->begin == Templ_ICMP_echo && \ masscan->targets.ports.list->end == Templ_ICMP_echo) { /* ICMP only */ //LOG(0, " -- forced options: -sn -n --randomize-hosts -v --send-eth\n"); LOG(0, "Initiating ICMP Echo Scan\n"); LOG(0, "Scanning %u hosts\n",(unsigned)count_ips); } else /* This could actually also be a UDP only or mixed UDP/TCP/ICMP scan */ { //LOG(0, " -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth\n"); LOG(0, "Initiating SYN Stealth Scan\n"); LOG(0, "Scanning %u hosts [%u port%s/host]\n", (unsigned)count_ips, (unsigned)count_ports, (count_ports==1)?"":"s"); } } /* * Start all the threads */ for (index=0; indexnic_count; index++) { struct ThreadPair *parms = &parms_array[index]; /* * Start the scanning thread. * THIS IS WHERE THE PROGRAM STARTS SPEWING OUT PACKETS AT A HIGH * RATE OF SPEED. */ parms->thread_handle_xmit = pixie_begin_thread(transmit_thread, 0, parms); /* * Start the MATCHING receive thread. Transmit and receive threads * come in matching pairs. */ parms->thread_handle_recv = pixie_begin_thread(receive_thread, 0, parms); } /* * Now wait for to be pressed OR for threads to exit */ pixie_usleep(1000 * 100); LOG(1, "[+] waiting for threads to finish\n"); status_start(&status); status.is_infinite = masscan->is_infinite; while (!is_tx_done && masscan->output.is_status_updates) { unsigned i; double rate = 0; uint64_t total_tcbs = 0; uint64_t total_synacks = 0; uint64_t total_syns = 0; /* Find the minimum index of all the threads */ min_index = UINT64_MAX; for (i=0; inic_count; i++) { struct ThreadPair *parms = &parms_array[i]; if (min_index > parms->my_index) min_index = parms->my_index; rate += parms->throttler->current_rate; if (parms->total_tcbs) total_tcbs += *parms->total_tcbs; if (parms->total_synacks) total_synacks += *parms->total_synacks; if (parms->total_syns) total_syns += *parms->total_syns; } if (min_index >= range && !masscan->is_infinite) { /* Note: This is how we can tell the scan has ended */ is_tx_done = 1; } /* * update screen about once per second with statistics, * namely packets/second. */ if (masscan->output.is_status_updates) status_print(&status, min_index, range, rate, total_tcbs, total_synacks, total_syns, 0, masscan->output.is_status_ndjson); /* Sleep for almost a second */ pixie_mssleep(750); } /* * If we haven't completed the scan, then save the resume * information. */ if (min_index < count_ips * count_ports) { masscan->resume.index = min_index; /* Write current settings to "paused.conf" so that the scan can be restarted */ masscan_save_state(masscan); } /* * Now wait for all threads to exit */ now = time(0); for (;;) { unsigned transmit_count = 0; unsigned receive_count = 0; unsigned i; double rate = 0; uint64_t total_tcbs = 0; uint64_t total_synacks = 0; uint64_t total_syns = 0; /* Find the minimum index of all the threads */ min_index = UINT64_MAX; for (i=0; inic_count; i++) { struct ThreadPair *parms = &parms_array[i]; if (min_index > parms->my_index) min_index = parms->my_index; rate += parms->throttler->current_rate; if (parms->total_tcbs) total_tcbs += *parms->total_tcbs; if (parms->total_synacks) total_synacks += *parms->total_synacks; if (parms->total_syns) total_syns += *parms->total_syns; } if (time(0) - now >= masscan->wait) { is_rx_done = 1; } if (time(0) - now - 10 > masscan->wait) { LOG(0, "[-] Passed the wait window but still running, forcing exit...\n"); exit(0); } if (masscan->output.is_status_updates) { status_print(&status, min_index, range, rate, total_tcbs, total_synacks, total_syns, masscan->wait - (time(0) - now), masscan->output.is_status_ndjson); for (i=0; inic_count; i++) { struct ThreadPair *parms = &parms_array[i]; transmit_count += parms->done_transmitting; receive_count += parms->done_receiving; } pixie_mssleep(250); if (transmit_count < masscan->nic_count) continue; is_tx_done = 1; is_rx_done = 1; if (receive_count < masscan->nic_count) continue; } else { /* [AFL-fuzz] * Join the threads, which doesn't allow us to print out * status messages, but allows us to exit cleanly without * any waiting */ for (i=0; inic_count; i++) { struct ThreadPair *parms = &parms_array[i]; pixie_thread_join(parms->thread_handle_xmit); parms->thread_handle_xmit = 0; pixie_thread_join(parms->thread_handle_recv); parms->thread_handle_recv = 0; } is_tx_done = 1; is_rx_done = 1; } break; } /* * Now cleanup everything */ status_finish(&status); if (!masscan->output.is_status_updates) { uint64_t usec_now = pixie_gettime(); printf("%u milliseconds elapsed\n", (unsigned)((usec_now - usec_start)/1000)); } LOG(1, "[+] all threads have exited \n"); return 0; } /*************************************************************************** ***************************************************************************/ int main(int argc, char *argv[]) { struct Masscan masscan[1]; unsigned i; int has_target_addresses = 0; int has_target_ports = 0; usec_start = pixie_gettime(); #if defined(WIN32) {WSADATA x; WSAStartup(0x101, &x);} #endif global_now = time(0); /* Set system to report debug information on crash */ { int is_backtrace = 1; for (i=1; i<(unsigned)argc; i++) { if (strcmp(argv[i], "--nobacktrace") == 0) is_backtrace = 0; } if (is_backtrace) pixie_backtrace_init(argv[0]); } /* * Initialize those defaults that aren't zero */ memset(masscan, 0, sizeof(*masscan)); /* 14 rounds seem to give way better statistical distribution than 4 with a very low impact on scan rate */ masscan->blackrock_rounds = 14; masscan->output.is_show_open = 1; /* default: show syn-ack, not rst */ masscan->output.is_status_updates = 1; /* default: show status updates */ masscan->wait = 10; /* how long to wait for responses when done */ masscan->max_rate = 100.0; /* max rate = hundred packets-per-second */ masscan->nic_count = 1; masscan->shard.one = 1; masscan->shard.of = 1; masscan->min_packet_size = 60; masscan->redis.password = NULL; masscan->payloads.udp = payloads_udp_create(); masscan->payloads.oproto = payloads_oproto_create(); safe_strcpy( masscan->output.rotate.directory, sizeof(masscan->output.rotate.directory), "."); masscan->is_capture_cert = 1; /* * Pre-parse the command-line */ if (masscan_conf_contains("--readscan", argc, argv)) { masscan->is_readscan = 1; } /* * On non-Windows systems, read the defaults from the file in * the /etc directory. These defaults will contain things * like the output directory, max packet rates, and so on. Most * importantly, the master "--excludefile" might be placed here, * so that blacklisted ranges won't be scanned, even if the user * makes a mistake */ #if !defined(WIN32) if (!masscan->is_readscan) { if (access("/etc/masscan/masscan.conf", 0) == 0) { masscan_read_config_file(masscan, "/etc/masscan/masscan.conf"); } } #endif /* * Read in the configuration from the command-line. We are looking for * either options or a list of IPv4 address ranges. */ masscan_command_line(masscan, argc, argv); if (masscan->seed == 0) masscan->seed = get_entropy(); /* entropy for randomness */ /* * Load database files like "nmap-payloads" and "nmap-service-probes" */ masscan_load_database_files(masscan); /* * Load the scripting engine if needed and run those that were * specified. */ if (masscan->is_scripting) scripting_init(masscan); /* We need to do a separate "raw socket" initialization step. This is * for Windows and PF_RING. */ if (pcap_init() != 0) LOG(2, "libpcap: failed to load\n"); rawsock_init(); /* Init some protocol parser data structures */ snmp_init(); x509_init(); /* * Apply excludes. People ask us not to scan them, so we maintain a list * of their ranges, and when doing wide scans, add the exclude list to * prevent them from being scanned. */ has_target_addresses = massip_has_ipv4_targets(&masscan->targets) || massip_has_ipv6_targets(&masscan->targets); has_target_ports = massip_has_target_ports(&masscan->targets); massip_apply_excludes(&masscan->targets, &masscan->exclude); if (!has_target_ports && masscan->op == Operation_ListScan) massip_add_port_string(&masscan->targets, "80", 0); /* Optimize target selection so it's a quick binary search instead * of walking large memory tables. When we scan the entire Internet * our --excludefile will chop up our pristine 0.0.0.0/0 range into * hundreds of subranges. This allows us to grab addresses faster. */ massip_optimize(&masscan->targets); /* FIXME: we only support 63-bit scans at the current time. * This is big enough for the IPv4 Internet, where scanning * for all TCP ports on all IPv4 addresses results in a 48-bit * scan, but this isn't big enough even for a single port on * an IPv6 subnet (which are 64-bits in size, usually). However, * even at millions of packets per second scanning rate, you still * can't complete a 64-bit scan in a reasonable amount of time. * Nor would you want to attempt the feat, as it would overload * the target IPv6 subnet. Since implementing this would be * difficult for 32-bit processors, for now, I'm going to stick * to a simple 63-bit scan. */ if (massint128_bitcount(massip_range(&masscan->targets)) > 63) { fprintf(stderr, "[-] FAIL: scan range too large, max is 63-bits, requested is %u bits\n", massint128_bitcount(massip_range(&masscan->targets))); fprintf(stderr, " Hint: scan range is number of IP addresses times number of ports\n"); fprintf(stderr, " Hint: IPv6 subnet must be at least /66 \n"); exit(1); } /* * Once we've read in the configuration, do the operation that was * specified */ switch (masscan->op) { case Operation_Default: /* Print usage info and exit */ masscan_usage(); break; case Operation_Scan: /* * THIS IS THE NORMAL THING */ if (rangelist_count(&masscan->targets.ipv4) == 0 && massint128_is_zero(range6list_count(&masscan->targets.ipv6))) { /* We check for an empty target list here first, before the excludes, * so that we can differentiate error messages after excludes, in case * the user specified addresses, but they were removed by excludes. */ LOG(0, "FAIL: target IP address list empty\n"); if (has_target_addresses) { LOG(0, " [hint] all addresses were removed by exclusion ranges\n"); } else { LOG(0, " [hint] try something like \"--range 10.0.0.0/8\"\n"); LOG(0, " [hint] try something like \"--range 192.168.0.100-192.168.0.200\"\n"); } exit(1); } if (rangelist_count(&masscan->targets.ports) == 0) { if (has_target_ports) { LOG(0, " [hint] all ports were removed by exclusion ranges\n"); } else { LOG(0, "FAIL: no ports were specified\n"); LOG(0, " [hint] try something like \"-p80,8000-9000\"\n"); LOG(0, " [hint] try something like \"--ports 0-65535\"\n"); } return 1; } return main_scan(masscan); case Operation_ListScan: /* Create a randomized list of IP addresses */ main_listscan(masscan); return 0; case Operation_List_Adapters: /* List the network adapters we might want to use for scanning */ rawsock_list_adapters(); break; case Operation_DebugIF: for (i=0; inic_count; i++) rawsock_selftest_if(masscan->nic[i].ifname); return 0; case Operation_ReadRange: main_readrange(masscan); return 0; case Operation_ReadScan: { unsigned start; unsigned stop; /* find first file */ for (start=1; start<(unsigned)argc; start++) { if (memcmp(argv[start], "--readscan", 10) == 0) { start++; break; } } /* find last file */ for (stop=start+1; stop<(unsigned)argc && argv[stop][0] != '-'; stop++) ; /* * read the binary files, and output them again depending upon * the output parameters */ readscan_binary_scanfile(masscan, start, stop, argv); } break; case Operation_Benchmark: printf("=== benchmarking (%u-bits) ===\n\n", (unsigned)sizeof(void*)*8); blackrock_benchmark(masscan->blackrock_rounds); blackrock2_benchmark(masscan->blackrock_rounds); smack_benchmark(); exit(1); break; case Operation_Echo: masscan_echo(masscan, stdout, 0); exit(0); break; case Operation_EchoAll: masscan_echo(masscan, stdout, 0); exit(0); break; case Operation_EchoCidr: masscan_echo_cidr(masscan, stdout, 0); exit(0); break; case Operation_Selftest: /* * Do a regression test of all the significant units */ { int x = 0; extern int proto_isakmp_selftest(void); x += massip_selftest(); x += ranges6_selftest(); x += dedup_selftest(); x += checksum_selftest(); x += ipv4address_selftest(); x += ipv6address_selftest(); x += proto_coap_selftest(); x += smack_selftest(); x += sctp_selftest(); x += base64_selftest(); x += banner1_selftest(); x += output_selftest(); x += siphash24_selftest(); x += ntp_selftest(); x += snmp_selftest(); x += proto_isakmp_selftest(); x += templ_payloads_selftest(); x += blackrock_selftest(); x += rawsock_selftest(); x += lcg_selftest(); x += template_selftest(); x += ranges_selftest(); x += massip_parse_selftest(); x += pixie_time_selftest(); x += rte_ring_selftest(); x += mainconf_selftest(); x += zeroaccess_selftest(); x += nmapserviceprobes_selftest(); x += rstfilter_selftest(); x += masscan_app_selftest(); if (x != 0) { /* one of the selftests failed, so return error */ fprintf(stderr, "regression test: failed :( \n"); return 1; } else { fprintf(stderr, "regression test: success!\n"); return 0; } } break; } return 0; } ================================================ FILE: src/masscan-app.c ================================================ #include "masscan-app.h" #include "util-safefunc.h" /****************************************************************************** * When outputting results, we call this function to print out the type of * banner that we've collected ******************************************************************************/ const char * masscan_app_to_string(enum ApplicationProtocol proto) { static char tmp[64]; switch (proto) { case PROTO_NONE: return "unknown"; case PROTO_HEUR: return "unknown"; case PROTO_SSH1: return "ssh"; case PROTO_SSH2: return "ssh"; case PROTO_HTTP: return "http"; case PROTO_FTP: return "ftp"; case PROTO_DNS_VERSIONBIND: return "dns-ver"; case PROTO_SNMP: return "snmp"; case PROTO_NBTSTAT: return "nbtstat"; case PROTO_SSL3: return "ssl"; case PROTO_SMB: return "smb"; case PROTO_SMTP: return "smtp"; case PROTO_POP3: return "pop"; case PROTO_IMAP4: return "imap"; case PROTO_UDP_ZEROACCESS: return "zeroaccess"; case PROTO_X509_CERT: return "X509"; case PROTO_X509_CACERT: return "X509CA"; case PROTO_HTML_TITLE: return "title"; case PROTO_HTML_FULL: return "html"; case PROTO_NTP: return "ntp"; case PROTO_VULN: return "vuln"; case PROTO_HEARTBLEED: return "heartbleed"; case PROTO_TICKETBLEED: return "ticketbleed"; case PROTO_VNC_OLD: return "vnc"; case PROTO_SAFE: return "safe"; case PROTO_MEMCACHED: return "memcached"; case PROTO_SCRIPTING: return "scripting"; case PROTO_VERSIONING: return "versioning"; case PROTO_COAP: return "coap"; case PROTO_TELNET: return "telnet"; case PROTO_RDP: return "rdp"; case PROTO_HTTP_SERVER: return "http.server"; case PROTO_MC: return "minecraft"; case PROTO_VNC_RFB: return "vnc"; case PROTO_VNC_INFO: return "vnc-info"; case PROTO_ISAKMP: return "isakmp"; case PROTO_ERROR: return "error"; default: snprintf(tmp, sizeof(tmp), "(%u)", proto); return tmp; } } /****************************************************************************** ******************************************************************************/ enum ApplicationProtocol masscan_string_to_app(const char *str) { const static struct { const char *name; enum ApplicationProtocol value; } list[] = { {"ssh1", PROTO_SSH1}, {"ssh2", PROTO_SSH2}, {"ssh", PROTO_SSH2}, {"http", PROTO_HTTP}, {"ftp", PROTO_FTP}, {"dns-ver", PROTO_DNS_VERSIONBIND}, {"snmp", PROTO_SNMP}, {"nbtstat", PROTO_NBTSTAT}, {"ssl", PROTO_SSL3}, {"smtp", PROTO_SMTP}, {"smb", PROTO_SMB}, {"pop", PROTO_POP3}, {"imap", PROTO_IMAP4}, {"x509", PROTO_X509_CERT}, {"x509ca", PROTO_X509_CACERT}, {"zeroaccess", PROTO_UDP_ZEROACCESS}, {"title", PROTO_HTML_TITLE}, {"html", PROTO_HTML_FULL}, {"ntp", PROTO_NTP}, {"vuln", PROTO_VULN}, {"heartbleed", PROTO_HEARTBLEED}, {"ticketbleed", PROTO_TICKETBLEED}, {"vnc-old", PROTO_VNC_OLD}, {"safe", PROTO_SAFE}, {"memcached", PROTO_MEMCACHED}, {"scripting", PROTO_SCRIPTING}, {"versioning", PROTO_VERSIONING}, {"coap", PROTO_COAP}, {"telnet", PROTO_TELNET}, {"rdp", PROTO_RDP}, {"http.server", PROTO_HTTP_SERVER}, {"minecraft", PROTO_MC}, {"vnc", PROTO_VNC_RFB}, {"vnc-info", PROTO_VNC_INFO}, {"isakmp", PROTO_ISAKMP}, {0,0} }; size_t i; for (i=0; list[i].name; i++) { if (strcmp(str, list[i].name) == 0) return list[i].value; } return 0; } int masscan_app_selftest(void) { static const struct { unsigned enumid; unsigned expected; } tests[] = { {PROTO_SNMP, 7}, {PROTO_X509_CERT, 15}, {PROTO_HTTP_SERVER, 31}, {0,0} }; size_t i; /* The ENUM contains fixed values in external files, * so programmers should only add onto its end, not * the middle. This self-test will verify that * a programmer hasn't made this mistake. */ for (i=0; tests[i].enumid != 0; i++) { unsigned enumid = tests[i].enumid; unsigned expected = tests[i].expected; /* YOU ADDED AN ENUM IN THE MIDDLE INSTEAD ON THE END OF THE LIST */ if (enumid != expected) { fprintf(stderr, "[-] %s:%u fail\n", __FILE__, (unsigned)__LINE__); fprintf(stderr, "[-] enum expected=%u, found=%u\n", 30, PROTO_HTTP_SERVER); return 1; } } return 0; } ================================================ FILE: src/masscan-app.h ================================================ #ifndef MASSCAN_APP_H #define MASSCAN_APP_H /* * WARNING: these constants are used in files, so don't change the values. * Add new ones onto the end */ enum ApplicationProtocol { PROTO_NONE, PROTO_HEUR, PROTO_SSH1, PROTO_SSH2, PROTO_HTTP, PROTO_FTP, PROTO_DNS_VERSIONBIND, PROTO_SNMP, /* 7 - simple network management protocol, udp/161 */ PROTO_NBTSTAT, /* 8 - netbios, udp/137 */ PROTO_SSL3, PROTO_SMB, /* 10 - SMB tcp/139 and tcp/445 */ PROTO_SMTP, /* 11 - transfering email */ PROTO_POP3, /* 12 - fetching email */ PROTO_IMAP4, /* 13 - fetching email */ PROTO_UDP_ZEROACCESS, PROTO_X509_CERT, /* 15 - just the cert */ PROTO_X509_CACERT, PROTO_HTML_TITLE, PROTO_HTML_FULL, PROTO_NTP, /* 19 - network time protocol, udp/123 */ PROTO_VULN, PROTO_HEARTBLEED, PROTO_TICKETBLEED, PROTO_VNC_OLD, PROTO_SAFE, PROTO_MEMCACHED, /* 25 - memcached */ PROTO_SCRIPTING, PROTO_VERSIONING, PROTO_COAP, /* 28 - constrained app proto, udp/5683, RFC7252 */ PROTO_TELNET, /* 29 - ye old remote terminal */ PROTO_RDP, /* 30 - Microsoft Remote Desktop Protocol tcp/3389 */ PROTO_HTTP_SERVER, /* 31 - HTTP "Server:" field */ PROTO_MC, /* 32 - Minecraft server */ PROTO_VNC_RFB, PROTO_VNC_INFO, PROTO_ISAKMP, /* 35 - IPsec key exchange */ PROTO_ERROR, PROTO_end_of_list /* must be last one */ }; const char * masscan_app_to_string(enum ApplicationProtocol proto); enum ApplicationProtocol masscan_string_to_app(const char *str); int masscan_app_selftest(void); #endif ================================================ FILE: src/masscan-status.h ================================================ #ifndef MASSCAN_STATUS_H #define MASSCAN_STATUS_H #if 0 enum PortStatus { Port_Unknown, Port_Open, Port_Closed, Port_IcmpEchoResponse, Port_UdpOpen, Port_UdpClosed, Port_SctpOpen, Port_SctpClosed, Port_ArpOpen, }; #endif enum PortStatus { PortStatus_Unknown, PortStatus_Open, PortStatus_Closed, PortStatus_Arp, PortStatus_Count }; #endif ================================================ FILE: src/masscan-version.h ================================================ #ifndef MASSCAN_VERSION #define MASSCAN_VERSION "1.3.9-integration" #endif ================================================ FILE: src/masscan.h ================================================ #ifndef MASSCAN_H #define MASSCAN_H #include "massip-addr.h" #include "util-safefunc.h" #include "stack-src.h" #include "massip.h" #include "util-bool.h" #include #include #include #include #include "massip.h" #include "stack-queue.h" struct Adapter; struct TemplateSet; struct Banner1; struct TemplateOptions; /** * This is the "operation" to be performed by masscan, which is almost always * to "scan" the network. However, there are some lesser operations to do * instead, like run a "regression self test", or "debug", or something else * instead of scanning. We parse the command-line in order to figure out the * proper operation */ enum Operation { Operation_Default = 0, /* nothing specified, so print usage */ Operation_List_Adapters = 1, /* --listif */ Operation_Selftest = 2, /* --selftest or --regress */ Operation_Scan = 3, /* this is what you expect */ Operation_DebugIF = 4, /* --debug if */ Operation_ListScan = 5, /* -sL */ Operation_ReadScan = 6, /* --readscan */ Operation_ReadRange = 7, /* --readrange */ Operation_Benchmark = 8, /* --benchmark */ Operation_Echo = 9, /* --echo */ Operation_EchoAll = 10, /* --echo-all */ Operation_EchoCidr = 11, /* --echo-cidr */ }; /** * The format of the output. If nothing is specified, then the default will * be "--interactive", meaning that we'll print to the command-line live as * results come in. Only one output format can be specified, except that * "--interactive" can be specified alongside any of the other ones. */ enum OutputFormat { Output_Default = 0x0000, Output_Interactive = 0x0001, /* --interactive, print to cmdline */ Output_List = 0x0002, Output_Binary = 0x0004, /* -oB, "binary", the primary format */ Output_XML = 0x0008, /* -oX, "xml" */ Output_JSON = 0x0010, /* -oJ, "json" */ Output_NDJSON = 0x0011, /* -oD, "ndjson" */ Output_Nmap = 0x0020, Output_ScriptKiddie = 0x0040, Output_Grepable = 0x0080, /* -oG, "grepable" */ Output_Redis = 0x0100, Output_Unicornscan = 0x0200, /* -oU, "unicornscan" */ Output_None = 0x0400, Output_Certs = 0x0800, Output_Hostonly = 0x1000, /* -oH, "hostonly" */ Output_All = 0xFFBF, /* not supported */ }; /** * Holds the list of TCP "hello" payloads, specified with the "--hello-file" * or "--hello-string" options */ struct TcpCfgPayloads { /** The "hello" data in base64 format. This is either the base64 string * specified in the cmdline/cfgfile with "--hello-string", or the * contents of a file specified with "--hello-file" that we've converted * into base64 */ char *payload_base64; /** The TCP port that this hello belongs to */ unsigned port; /** These configuration options are stored as a linked-list */ struct TcpCfgPayloads *next; }; /** * This is the master MASSCAN configuration structure. It is created on startup * by reading the command-line and parsing configuration files. * * Once read in at the start, this structure doesn't change. The transmit * and receive threads have only a "const" pointer to this structure. */ struct Masscan { /** * What this program is doing, which is normally "Operation_Scan", but * which can be other things, like "Operation_SelfTest" */ enum Operation op; struct { unsigned tcp:1; unsigned udp:1; /* -sU */ unsigned sctp:1; unsigned ping:1; /* --ping, ICMP echo */ unsigned arp:1; /* --arp, local ARP scan */ unsigned oproto:1; /* -sO */ } scan_type; /** * After scan type has been configured, add these ports. In other words, * the user may specify `-sU` or `-sT` after the `--top-ports` parameter, * so we have to wait until after parsing arguments to fill in the ports. */ unsigned top_ports; /** * Temporary file to echo parameters to, used for saving configuration * to a file */ FILE *echo; unsigned echo_all; /** * One or more network adapters that we'll use for scanning. Each adapter * should have a separate set of IP source addresses, except in the case * of PF_RING dnaX:Y adapters. */ struct { char ifname[256]; struct Adapter *adapter; struct stack_src_t src; macaddress_t source_mac; macaddress_t router_mac_ipv4; macaddress_t router_mac_ipv6; ipv4address_t router_ip; int link_type; /* libpcap definitions */ unsigned char my_mac_count; /*is there a MAC address? */ unsigned vlan_id; unsigned is_vlan:1; unsigned is_usable:1; } nic[8]; unsigned nic_count; /** * The target ranges of IPv4 addresses that are included in the scan. * The user can specify anything here, and we'll resolve all overlaps * and such, and sort the target ranges. */ struct MassIP targets; /** * IPv4 addresses/ranges that are to be excluded from the scan. This takes * precedence over any 'include' statement. What happens is this: after * all the configuration has been read, we then apply the exclude/blacklist * on top of the target/whitelist, leaving only a target/whitelist left. * Thus, during the scan, we only choose from the target/whitelist and * don't consult the exclude/blacklist. */ struct MassIP exclude; /** * Only output these types of banners */ struct RangeList banner_types; /** * Maximum rate, in packets-per-second (--rate parameter). This can be * a fraction of a packet-per-second, or be as high as 30000000.0 (or * more actually, but I've only tested to 30megapps). */ double max_rate; /** * Number of retries (--retries or --max-retries parameter). Retries * happen a few seconds apart. */ unsigned retries; unsigned is_pfring:1; /* --pfring */ unsigned is_sendq:1; /* --sendq */ unsigned is_banners:1; /* --banners */ unsigned is_banners_rawudp:1; /* --rawudp */ unsigned is_offline:1; /* --offline */ unsigned is_noreset:1; /* --noreset, don't transmit RST */ unsigned is_gmt:1; /* --gmt, all times in GMT */ unsigned is_capture_cert:1; /* --capture cert */ unsigned is_capture_html:1; /* --capture html */ unsigned is_capture_heartbleed:1; /* --capture heartbleed */ unsigned is_capture_ticketbleed:1; /* --capture ticket */ unsigned is_test_csv:1; /* (temporary testing feature) */ unsigned is_infinite:1; /* -infinite */ unsigned is_readscan:1; /* --readscan, Operation_Readscan */ unsigned is_heartbleed:1; /* --heartbleed, scan for this vuln */ unsigned is_ticketbleed:1; /* --ticketbleed, scan for this vuln */ unsigned is_poodle_sslv3:1; /* --vuln poodle, scan for this vuln */ unsigned is_hello_ssl:1; /* --ssl, use SSL HELLO on all ports */ unsigned is_hello_smbv1:1; /* --smbv1, use SMBv1 hello, instead of v1/v2 hello */ unsigned is_hello_http:1; /* --hello=http, use HTTP on all ports */ unsigned is_scripting:1; /* whether scripting is needed */ unsigned is_capture_servername:1; /* --capture servername */ /** Packet template options, such as whether we should add a TCP MSS * value, or remove it from the packet */ struct TemplateOptions *templ_opts; /* e.g. --tcpmss */ /** * Wait forever for responses, instead of the default 10 seconds */ unsigned wait; /** * --resume * This structure contains options for pausing the scan (by exiting the * program) and restarting it later. */ struct { /** --resume-index */ uint64_t index; /** --resume-count */ uint64_t count; /** Derives the --resume-index from the target ip:port */ struct { unsigned ip; unsigned port; } target; } resume; /** * --shard n/m * This is used for distributing a scan across multiple "shards". Every * shard in the scan must know the total number of shards, and must also * know which of those shards is it's identity. Thus, shard 1/5 scans * a different range than 2/5. These numbers start at 1, so it's * 1/3 (#1 out of three), 2/3, and 3/3 (but not 0/3). */ struct { unsigned one; unsigned of; } shard; /** * The packet template set we are current using. We store a binary template * for TCP, UDP, SCTP, ICMP, and so on. All the scans using that protocol * are then scanned using that basic template. IP and TCP options can be * added to the basic template without affecting any other component * of the system. */ struct TemplateSet *pkt_template; /** * A random seed for randomization if zero, otherwise we'll use * the configured seed for repeatable tests. */ uint64_t seed; /** * This block configures what we do for the output files */ struct OutputStuff { /** * --output-format * Examples are "xml", "binary", "json", "ndjson", "grepable", and so on. */ enum OutputFormat format; /** * --output-filename * The name of the file where we are storing scan results. * Note: the filename "-" means that we should send the file to * rather than to a file. */ char filename[256]; /** * A feature of the XML output where we can insert an optional * stylesheet into the file for better rendering on web browsers */ char stylesheet[256]; /** * --append * We should append to the output file rather than overwriting it. */ unsigned is_append:1; /** * --json-status * Print each status update line to stderr as JSON ending with a newline * * This only applies to the three types of status lines that are printed * in status_print(); it does *not* apply to things like startup messages, * error messages or discovery of individual ports * */ bool is_status_ndjson; /** * --open * --open-only * --show open * Whether to show open ports */ unsigned is_show_open:1; /** * --show closed * Whether to show closed ports (i.e. RSTs) */ unsigned is_show_closed:1; /** * --show host * Whether to show host messages other than closed ports */ unsigned is_show_host:1; /** * print reason port is open, which is redundant for us */ unsigned is_reason:1; /** * --interactive * Print to command-line while also writing to output file. This isn't * needed if the output format is already 'interactive' (the default), * but only if the default output format is anything else, and the * user also wants interactivity. */ unsigned is_interactive:1; /** * Print state updates */ unsigned is_status_updates:1; struct { /** * When we should rotate output into the target directory */ unsigned timeout; /** * When doing "--rotate daily", the rotation is done at GMT. In * order to fix this, add an offset. */ unsigned offset; /** * Instead of rotating by timeout, we can rotate by filesize */ uint64_t filesize; /** * The directory to which we store rotated files */ char directory[256]; } rotate; } output; struct { unsigned data_length; /* number of bytes to randomly append */ unsigned ttl; /* starting IP TTL field */ unsigned badsum; /* bad TCP/UDP/SCTP checksum */ unsigned packet_trace:1; /* print transmit messages */ char datadir[256]; } nmap; char pcap_filename[256]; struct { unsigned timeout; } tcb; struct { char *pcap_payloads_filename; char *nmap_payloads_filename; char *nmap_service_probes_filename; struct PayloadsUDP *udp; struct PayloadsUDP *oproto; struct TcpCfgPayloads *tcp; struct NmapServiceProbeList *probes; } payloads; /** Reconfigure the HTTP header */ struct { /* Method */ unsigned char *method; size_t method_length; /* URL */ unsigned char *url; size_t url_length; /* Version */ unsigned char *version; size_t version_length; /* Host */ unsigned char *host; size_t host_length; /* User-Agent */ unsigned char *user_agent; size_t user_agent_length; /* Payload after the header*/ unsigned char *payload; size_t payload_length; /* Headers */ struct { const char *name; unsigned char *value; size_t value_length; } headers[16]; size_t headers_count; /* Cookies */ struct { unsigned char *value; size_t value_length; } cookies[16]; size_t cookies_count; /* Remove */ struct { unsigned char *name; } remove[16]; size_t remove_count; } http; unsigned tcp_connection_timeout; /** Number of seconds to wait for a 'hello' from the server before * giving up and sending a 'hello' from the client. Should be a small * value when doing scans that expect client-side hellos, like HTTP or * SSL, but should be a longer value when doing scans that expect server * hellos, such as FTP or VNC */ unsigned tcp_hello_timeout; char *bpf_filter; struct { ipaddress ip; char *password; unsigned port; } redis; /** * --min-packet */ unsigned min_packet_size; /** * Number of rounds for randomization * --blackrock-rounds */ unsigned blackrock_rounds; /** * --script */ struct { /* The name (filename) of the script to run */ char *name; /* The script VM */ struct lua_State *L; } scripting; /** * --vuln * The name of a vuln to check, like "poodle" */ const char *vuln_name; }; int mainconf_selftest(void); void masscan_read_config_file(struct Masscan *masscan, const char *filename); void masscan_command_line(struct Masscan *masscan, int argc, char *argv[]); void masscan_usage(void); void masscan_save_state(struct Masscan *masscan); void main_listscan(struct Masscan *masscan); /** * Load databases, such as: * - nmap-payloads * - nmap-service-probes * - pcap-payloads */ void masscan_load_database_files(struct Masscan *masscan); /** * Pre-scan the command-line looking for options that may affect how * previous options are handled. This is a bit of a kludge, really. */ int masscan_conf_contains(const char *x, int argc, char **argv); /** * Called to set a pair. */ void masscan_set_parameter(struct Masscan *masscan, const char *name, const char *value); /** * Discover the local network adapter parameters, such as which * MAC address we are using and the MAC addresses of the * local routers. */ int masscan_initialize_adapter( struct Masscan *masscan, unsigned index, macaddress_t *source_mac, macaddress_t *router_mac_ipv4, macaddress_t *router_mac_ipv6); /** * Echoes the settings to the command-line. By default, echoes only * non-default values. With "echo-all", everything is echoed. */ void masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all); /** * Echoes the list of CIDR ranges to scan. */ void masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all); #endif ================================================ FILE: src/massip-addr.c ================================================ #include "massip-addr.h" #include /** * Holds the output string, so that we can append to it without * overflowing buffers. The _append_xxx() functions below append * to this string. */ typedef struct stream_t { char *buf; size_t offset; size_t length; } stream_t; /** * Append a character to the output string. All the other _append_xxx() * functions call this one, so this is the only one where a * buffer-overflow can occur. */ static void _append_char(stream_t *out, char c) { if (out->offset < out->length) out->buf[out->offset++] = c; /* keep the string nul terminated as we build it */ if (out->offset < out->length) out->buf[out->offset] = '\0'; } static void _append_ipv6(stream_t *out, const unsigned char *ipv6) { static const char hex[] = "0123456789abcdef"; size_t i; int is_ellision = 0; /* An IPv6 address is printed as a series of 2-byte hex words * separated by colons :, for a total of 16-bytes */ for (i = 0; i < 16; i += 2) { unsigned n = ipv6[i] << 8 | ipv6[i + 1]; /* Handle the ellision case. A series of words with a value * of 0 can be removed completely, replaced by an extra colon */ if (n == 0 && !is_ellision) { is_ellision = 1; while (i < 13 && ipv6[i + 2] == 0 && ipv6[i + 3] == 0) i += 2; _append_char(out, ':'); /* test for all-zero address, in which case the output * will be "::". */ while (i == 14 && ipv6[i] == 0 && ipv6[i + 1] == 0){ i=16; _append_char(out, ':'); } continue; } /* Print the colon between numbers. Fence-post alert: only colons * between numbers are printed, not at the beginning or end of the * string */ if (i) _append_char(out, ':'); /* Print the digits. Leading zeroes are not printed */ if (n >> 12) _append_char(out, hex[(n >> 12) & 0xF]); if (n >> 8) _append_char(out, hex[(n >> 8) & 0xF]); if (n >> 4) _append_char(out, hex[(n >> 4) & 0xF]); _append_char(out, hex[(n >> 0) & 0xF]); } } struct ipaddress_formatted ipv6address_fmt(ipv6address a) { struct ipaddress_formatted out; unsigned char tmp[16]; size_t i; stream_t s; /* * Convert address into a sequence of bytes. Our code * here represents an IPv6 address as two 64-bit numbers, but * the formatting code above that we copied from a different * project represents it as an array of bytes. */ for (i=0; i<16; i++) { uint64_t x; if (i<8) x = a.hi; else x = a.lo; x >>= (7 - (i%8)) * 8; tmp[i] = (unsigned char)(x & 0xFF); } /* Call the formatting function */ s.buf = out.string; s.offset = 0; s.length = sizeof(out.string); _append_ipv6(&s, tmp); return out; } /** * Append a decimal integer. */ static void _append_decimal(stream_t *out, unsigned long long n) { char tmp[64]; size_t tmp_offset = 0; /* Create temporary string */ while (n >= 10) { unsigned digit = n % 10; n /= 10; tmp[tmp_offset++] = (char)('0' + digit); } /* the final digit, may be zero */ tmp[tmp_offset++] = (char)('0' + n); /* Copy the result backwards */ while (tmp_offset) _append_char(out, tmp[--tmp_offset]); } static void _append_hex2(stream_t *out, unsigned long long n) { static const char hex[17] = "0123456789abcdef"; _append_char(out, hex[(n>>4)&0xF]); _append_char(out, hex[(n>>0)&0xF]); } struct ipaddress_formatted ipv4address_fmt(ipv4address ip) { struct ipaddress_formatted out; stream_t s; /* Call the formatting function */ s.buf = out.string; s.offset = 0; s.length = sizeof(out.string); _append_decimal(&s, (ip >> 24) & 0xFF); _append_char(&s, '.'); _append_decimal(&s, (ip >> 16) & 0xFF); _append_char(&s, '.'); _append_decimal(&s, (ip >> 8) & 0xFF); _append_char(&s, '.'); _append_decimal(&s, (ip >> 0) & 0xFF); return out; } struct ipaddress_formatted macaddress_fmt(macaddress_t mac) { struct ipaddress_formatted out; stream_t s; /* Call the formatting function */ s.buf = out.string; s.offset = 0; s.length = sizeof(out.string); _append_hex2(&s, mac.addr[0]); _append_char(&s, '-'); _append_hex2(&s, mac.addr[1]); _append_char(&s, '-'); _append_hex2(&s, mac.addr[2]); _append_char(&s, '-'); _append_hex2(&s, mac.addr[3]); _append_char(&s, '-'); _append_hex2(&s, mac.addr[4]); _append_char(&s, '-'); _append_hex2(&s, mac.addr[5]); return out; } struct ipaddress_formatted ipaddress_fmt(ipaddress a) { struct ipaddress_formatted out; stream_t s; ipv4address ip = a.ipv4; if (a.version == 6) { return ipv6address_fmt(a.ipv6); } /* Call the formatting function */ s.buf = out.string; s.offset = 0; s.length = sizeof(out.string); _append_decimal(&s, (ip >> 24) & 0xFF); _append_char(&s, '.'); _append_decimal(&s, (ip >> 16) & 0xFF); _append_char(&s, '.'); _append_decimal(&s, (ip >> 8) & 0xFF); _append_char(&s, '.'); _append_decimal(&s, (ip >> 0) & 0xFF); return out; } static unsigned _count_long(uint64_t number) { unsigned i; unsigned count = 0; for (i=0; i<64; i++) { if ((number >> i) & 1) count = i + 1; } return count; } /** * Find the number of bits needed to hold the integer. In other words, * the number 0x64 would need 7 bits to store it. * * We use this to count the size of scans. We currently only support * scan sizes up to 63 bits. */ unsigned massint128_bitcount(massint128_t number) { if (number.hi) return _count_long(number.hi) + 64; else return _count_long(number.lo); } ipv6address_t ipv6address_add_uint64(ipv6address_t lhs, uint64_t rhs) { lhs.lo += rhs; if (lhs.lo < rhs) { lhs.hi += 1; } return lhs; } ipv6address_t ipv6address_subtract(ipv6address_t lhs, ipv6address_t rhs) { ipv6address_t difference; difference.hi = lhs.hi - rhs.hi; difference.lo = lhs.lo - rhs.lo; /* check for underflow */ if (difference.lo > lhs.lo) difference.hi -= 1; return difference; } ipv6address_t ipv6address_add(ipv6address_t lhs, ipv6address_t rhs) { ipv6address_t sum; sum.hi = lhs.hi + rhs.hi; sum.lo = lhs.lo - rhs.lo; /* check for underflow */ if (sum.lo > lhs.lo) sum.hi += 1; return sum; } int ipv6address_selftest(void) { struct test_pair { const char *name; // Human-readable IPv6 address string struct ipaddress ip_addr; // IP address (union) }; /* Probably overkill, added while investigating issue #796 */ struct test_pair tests[] = { {"2001:db8:ac10:fe01::2", {.ipv6 = {0x20010db8ac10fe01, 0x0000000000000002}, .version = 6}}, {"2607:f8b0:4000::1", {.ipv6 = {0x2607f8b040000000, 0x0000000000000001}, .version = 6}}, {"fd12:3456:7890:abcd:ef00::1", {.ipv6 = {0xfd1234567890abcd, 0xef00000000000001}, .version = 6}}, {"::1", {.ipv6 = {0x0000000000000000, 0x0000000000000001}, .version = 6}}, {"1::", {.ipv6 = {0x0001000000000000, 0x0000000000000000}, .version = 6}}, {"1::2", {.ipv6 = {0x0001000000000000, 0x0000000000000002}, .version = 6}}, {"2::1", {.ipv6 = {0x0002000000000000, 0x0000000000000001}, .version = 6}}, {"1:2::", {.ipv6 = {0x0001000200000000, 0x0000000000000000}, .version = 6}}, {NULL, {{0, 0}, 0}} }; int x = 0; ipaddress ip; struct ipaddress_formatted fmt; for (int i = 0; tests[i].name != NULL; i++) { fmt = ipaddress_fmt(tests[i].ip_addr); if (strcmp(fmt.string, tests[i].name) != 0) x++; } return x; } int ipv4address_selftest(void) { int x = 0; ipaddress ip; struct ipaddress_formatted fmt; ip.version = 4; ip.ipv4 = 0x01FF00A3; fmt = ipaddress_fmt(ip); if (strcmp(fmt.string, "1.255.0.163") != 0) x++; return x; } int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix) { ipv6address mask; /* If the prefix is bad, then the answer is 'no'. */ if (prefix > 128) { return 0; } /* Create the mask from the prefix */ if (prefix > 64) mask.hi = ~0ULL; else if (prefix == 0) mask.hi = 0; else mask.hi = ~0ULL << (64 - prefix); if (prefix > 64) mask.lo = ~0ULL << (128 - prefix); else mask.lo = 0; /* Mask off any non-zero bits from both addresses */ lhs.hi &= mask.hi; lhs.lo &= mask.lo; rhs.hi &= mask.hi; rhs.lo &= mask.lo; /* Now do a normal compare */ return ipv6address_is_equal(lhs, rhs); } ================================================ FILE: src/massip-addr.h ================================================ /* Simple module for handling addresses (IPv6, IPv4, MAC). Also implements a 128-bit type for dealing with addresses. This is the module that almost all the other code depends upon, because everything else deals with the IP address types defined here. */ #ifndef MASSIP_ADDR_H #define MASSIP_ADDR_H #include #include #if defined(_MSC_VER) && !defined(inline) #define inline __inline #endif #if defined(_MSC_VER) #pragma warning(disable: 4201) #endif /** * An IPv6 address is represented as two 64-bit integers instead of a single * 128-bit integer. This is because currently (year 2020) most compilers * do not support the `uint128_t` type, but all relevant ones do support * the `uint64_t` type. */ struct ipv6address {uint64_t hi; uint64_t lo;}; typedef struct ipv6address ipv6address; typedef struct ipv6address ipv6address_t; /** * IPv4 addresses are represented simply with an integer. */ typedef unsigned ipv4address; typedef ipv4address ipv4address_t; /** * MAC address (layer 2). Since we have canonical types for IPv4/IPv6 * addresses, we may as well have a canonical type for MAC addresses, * too. */ struct macaddress_t {unsigned char addr[6];}; typedef struct macaddress_t macaddress_t; /** * In many cases we need to do arithmetic on IPv6 addresses, treating * them as a large 128-bit integer. Thus, we declare our own 128-bit * integer type (and some accompanying math functions). But it's * still just the same as a 128-bit integer. */ typedef ipv6address massint128_t; /** * Most of the code in this project is agnostic to the version of IP * addresses (IPv4 or IPv6). Therefore, we represent them as a union * distinguished by a version number. The `version` is an integer * with a value of either 4 or 6. */ struct ipaddress { union { unsigned ipv4; ipv6address ipv6; }; unsigned char version; }; typedef struct ipaddress ipaddress; /** @return true if the IPv6 address is zero [::] */ static inline int ipv6address_is_zero(ipv6address_t a) { return a.hi == 0 && a.lo == 0; } #define massint128_is_zero ipv6address_is_zero /** The IPv6 address [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF] * is invalid */ static inline int ipv6address_is_invalid(ipv6address_t a) { return a.hi == ~0ULL && a.lo == ~0ULL; } /** Compare two IPv6 addresses */ static inline int ipv6address_is_equal(ipv6address_t a, ipv6address_t b) { return a.hi == b.hi && a.lo == b.lo; } static inline int ipaddress_is_equal(ipaddress a, ipaddress b) { if (a.version != b.version) return 0; if (a.version == 4) { return a.ipv4 == b.ipv4; } else if (a.version == 6) { return ipv6address_is_equal(a.ipv6, b.ipv6); } else return 0; } /** Compare two IPv6 addresses, to see which one comes frist. This is used * in sorting the addresses * @return true if a < b, false otherwise */ static inline int ipv6address_is_lessthan(ipv6address_t a, ipv6address_t b) { return (a.hi == b.hi)?(a.lo < b.lo):(a.hi < b.hi); } /** * Mask the lower bits of each address and test if the upper bits are equal */ int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix); ipv6address_t ipv6address_add_uint64(ipv6address_t lhs, uint64_t rhs); ipv6address_t ipv6address_subtract(ipv6address_t lhs, ipv6address_t rhs); ipv6address_t ipv6address_add(ipv6address_t lhs, ipv6address_t rhs); /** * Given a typical EXTERNAL representation of an IPv6 address, which is * an array of 16 bytes, convert to the canonical INTERNAL address. */ static inline ipv6address ipv6address_from_bytes(const unsigned char *buf) { ipv6address addr; addr.hi = (uint64_t)buf[ 0] << 56 | (uint64_t)buf[ 1] << 48 | (uint64_t)buf[ 2] << 40 | (uint64_t)buf[ 3] << 32 | (uint64_t)buf[ 4] << 24 | (uint64_t)buf[ 5] << 16 | (uint64_t)buf[ 6] << 8 | (uint64_t)buf[ 7] << 0; addr.lo = (uint64_t)buf[ 8] << 56 | (uint64_t)buf[ 9] << 48 | (uint64_t)buf[10] << 40 | (uint64_t)buf[11] << 32 | (uint64_t)buf[12] << 24 | (uint64_t)buf[13] << 16 | (uint64_t)buf[14] << 8 | (uint64_t)buf[15] << 0; return addr; } /** * Given a typical EXTERNAL representation of an Ethernet MAC address, * which is an array of 6 bytes, convert to the canonical INTERNAL address. */ static inline macaddress_t macaddress_from_bytes(const void *vbuf) { const unsigned char *buf = (const unsigned char *)vbuf; macaddress_t result; result.addr[0] = buf[0]; result.addr[1] = buf[1]; result.addr[2] = buf[2]; result.addr[3] = buf[3]; result.addr[4] = buf[4]; result.addr[5] = buf[5]; return result; } /** Test if the Ethernet MAC address is all zeroes */ static inline int macaddress_is_zero(macaddress_t mac) { return mac.addr[0] == 0 && mac.addr[1] == 0 && mac.addr[2] == 0 && mac.addr[3] == 0 && mac.addr[4] == 0 && mac.addr[5] == 0; } /** Compare two Ethernet MAC addresses to see if they are equal */ static inline int macaddress_is_equal(macaddress_t lhs, macaddress_t rhs) { return lhs.addr[0] == rhs.addr[0] && lhs.addr[1] == rhs.addr[1] && lhs.addr[2] == rhs.addr[2] && lhs.addr[3] == rhs.addr[3] && lhs.addr[4] == rhs.addr[4] && lhs.addr[5] == rhs.addr[5]; } /** * Return a buffer with the formatted address */ typedef struct ipaddress_formatted { char string[48]; } ipaddress_formatted_t; struct ipaddress_formatted ipv6address_fmt(ipv6address a); struct ipaddress_formatted ipv4address_fmt(ipv4address a); struct ipaddress_formatted ipaddress_fmt(ipaddress a); struct ipaddress_formatted macaddress_fmt(macaddress_t a); unsigned massint128_bitcount(massint128_t num); /** * @return 0 on success, 1 on failure */ int ipv6address_selftest(void); int ipv4address_selftest(void); #endif ================================================ FILE: src/massip-parse.c ================================================ /* massip-parse This module parses IPv4 and IPv6 addresses. It's not a typical parser. It's optimized around parsing large files containing millions of addresses and ranges using a "state-machine parser". */ #include "massip.h" #include "massip-parse.h" #include "massip-rangesv4.h" #include "massip-rangesv6.h" #include "util-logger.h" #include "util-bool.h" #include "util-malloc.h" #include "util-safefunc.h" #include "unusedparm.h" #include struct massip_parser { unsigned long long line_number; unsigned long long char_number; unsigned state; unsigned tmp; unsigned char digit_count; unsigned addr; unsigned begin; unsigned end; struct { ipv6address _begin; ipv6address _end; unsigned short tmp[8]; unsigned char index; unsigned char ellision_index; unsigned is_bracket:1; unsigned is_second:1; } ipv6; }; /*************************************************************************** ***************************************************************************/ static struct massip_parser * _parser_init(struct massip_parser *p) { memset(p, 0, sizeof(*p)); p->line_number = 1; p->ipv6.ellision_index = 8; return p; } /*************************************************************************** ***************************************************************************/ static void _parser_destroy(struct massip_parser *p) { UNUSEDPARM(p); } /*************************************************************************** ***************************************************************************/ static void _parser_err(struct massip_parser *p, unsigned long long *line_number, unsigned long long *charindex) { *line_number = p->line_number; *charindex = p->char_number; } /** * Called before parsing the first address in a pair, and also * after the first address, to prepare for parsing the next * address */ static void _init_next_address(struct massip_parser *p, int is_second) { p->tmp = 0; p->ipv6.ellision_index = 8; p->ipv6.index = 0; p->ipv6.is_bracket = 0; p->digit_count = 0; p->ipv6.is_second = is_second; } static unsigned _parser_finish_ipv6(struct massip_parser *p) { unsigned index = p->ipv6.index; unsigned ellision = p->ipv6.ellision_index; /* We must have seen 8 numbers, or an ellision */ if (index < 8 && ellision >= 8) return 1; /* Handle ellision */ memmove( &p->ipv6.tmp[8-(index-ellision)], &p->ipv6.tmp[ellision], sizeof(p->ipv6.tmp[0]) * (index-ellision) ); memset( &p->ipv6.tmp[ellision], 0, sizeof(p->ipv6.tmp[0]) * (8 - index) ); /* Copy over to begin/end. We parse the address as a series of 16-bit * integers, but return the result as two 64-bit integers */ { ipv6address a; a.hi = (uint64_t)p->ipv6.tmp[0] << 48ULL | (uint64_t)p->ipv6.tmp[1] << 32ULL | (uint64_t)p->ipv6.tmp[2] << 16ULL | (uint64_t)p->ipv6.tmp[3] << 0ULL; a.lo = (uint64_t)p->ipv6.tmp[4] << 48ULL | (uint64_t)p->ipv6.tmp[5] << 32ULL | (uint64_t)p->ipv6.tmp[6] << 16ULL | (uint64_t)p->ipv6.tmp[7] << 0ULL; if (p->ipv6.is_second) p->ipv6._end = a; else { p->ipv6._begin = a; /* Set this here in case there is no 'end' address */ p->ipv6._end = a; } } /* Reset the parser to start parsing the next address */ _init_next_address(p, 1); return 0; } /*************************************************************************** * We store the IPv6 addresses that we are building inside the 'state' * of the state-machine. This function copies them out of the opaque * state into discrete values. ***************************************************************************/ static void _parser_get_ipv6(struct massip_parser *state, ipv6address *begin, ipv6address *end) { *begin = state->ipv6._begin; *end = state->ipv6._end; } enum parser_state_t { LINE_START, ADDR_START, COMMENT, NUMBER0, NUMBER1, NUMBER2, NUMBER3, NUMBER_ERR, SECOND0, SECOND1, SECOND2, SECOND3, SECOND_ERR, IPV4_CIDR_NUM, UNIDASH1, UNIDASH2, IPV6_BEGIN, IPV6_COLON, IPV6_CIDR, IPV6_CIDR_NUM, IPV6_NEXT, IPV6_END, ERROR }; /*************************************************************************** * When we start parsing an address, we don't know whether it's going to * be IPv4 or IPv6. We assume IPv4, but when we hit a condition indicating * that it's IPv6 instead, we need change the temporary number we * are working on from decimal to hex, then move from the middle of * parsing an IPv4 address to the middle of parsing an IPv6 address. ***************************************************************************/ static int _switch_to_ipv6(struct massip_parser *p, int old_state) { unsigned num = p->tmp; num = ((num/1000)%10) * 16 * 16 * 16 + ((num/100)%10) * 16 * 16 + ((num/10)%10) * 16 + (num % 10); //printf("%u -> 0x%x\n", p->tmp, num); p->tmp = num; return old_state; } enum { IPV4_n, IPV4_nn, IPV4_nnn, IPV4_nnn_, IPV4_nnn_n, IPV4_nnn_nn, IPV4_nnn_nnn, IPV4_nnn_nnn_, IPV4_nnn_nnn_n, IPV4_nnn_nnn_nn, IPV4_nnn_nnn_nnn, IPV4_nnn_nnn_nnn_, IPV4_nnn_nnn_nnn_n, IPV4_nnn_nnn_nnn_nn, IPV4_nnn_nnn_nnn_nnn, IPV4_nnn_nnn_nnn_nnn_, IPV4e_n, IPV4e_nn, IPV4e_nnn, IPV4e_nnn_, IPV4e_nnn_n, IPV4e_nnn_nn, IPV4e_nnn_nnn, IPV4e_nnn_nnn_, IPV4e_nnn_nnn_n, IPV4e_nnn_nnn_nn, IPV4e_nnn_nnn_nnn, IPV4e_nnn_nnn_nnn_, IPV4e_nnn_nnn_nnn_n, IPV4e_nnn_nnn_nnn_nn, IPV4e_nnn_nnn_nnn_nnn, IPV4e_nnn_nnn_nnn_nnn_, }; /** * Applies a CIDR mask to an IPv4 address to create a begin/end address. */ static void _ipv4_apply_cidr(unsigned *begin, unsigned *end, unsigned bitcount) { unsigned long long mask = 0xFFFFFFFF00000000ULL >> bitcount; /* mask off low-order bits */ *begin &= (unsigned)mask; /* Set all suffix bits to 1, so that 192.168.1.0/24 has * an ending address of 192.168.1.255. */ *end = *begin | (unsigned)~mask; } /** * Given an address 'being' and a 'prefix', return the 'begin' and 'end' address of the range. * @param begin * An in/out parameter. This may have some extra bits somewhere in the range. * These will be masked off and set to zero when the function returns. * @param end * An out parameter. This will be set to the last address of the range, meaning * that all the trailing bits will be set to '1'. * @parame prefix * The number of bits of the prefix, from [0..128]. If the value is 0, * then the 'begin' address will be set to all zeroes and the 'end' * address will be set to all ones. If the value is 128, * the 'begin' address is unchanged and the 'end' address * is set to the same as 'begin'. */ static void _ipv6_apply_cidr(ipv6address *begin, ipv6address *end, unsigned prefix) { ipv6address mask; /* For bad prefixes, make sure we return an invalid address */ if (prefix > 128) { static const ipv6address invalid = {~0ULL, ~0ULL}; *begin = invalid; *end = invalid; return; }; /* Create the mask from the prefix */ if (prefix > 64) mask.hi = ~0ULL; else if (prefix == 0) mask.hi = 0; else mask.hi = ~0ULL << (64 - prefix); if (prefix > 64) mask.lo = ~0ULL << (128 - prefix); else mask.lo = 0; /* Mask off any non-zero bits from the start * TODO print warning */ begin->hi &= mask.hi; begin->lo &= mask.lo; /* Set all suffix bits to 1, so that 192.168.1.0/24 has * an ending address of 192.168.1.255. */ end->hi = begin->hi | ~mask.hi; end->lo = begin->lo | ~mask.lo; } /*************************************************************************** * Parse the next IPv4/IPv6 address from a text stream, using a * 'state-machine parser'. ***************************************************************************/ static enum {Still_Working, Found_Error, Found_IPv4, Found_IPv6} _parser_next(struct massip_parser *p, const char *buf, size_t *r_offset, size_t length, unsigned *r_begin, unsigned *r_end) { size_t i; enum parser_state_t state = p->state; int result = Still_Working; /* The 'offset' parameter is optional. If NULL, then set it to zero */ if (r_offset) i = *r_offset; else i = 0; /* For all bytes in this chunk. This loop will exit early once * we've found a complete IP address. */ while (i < length) { unsigned char c = buf[i++]; p->char_number++; switch (state) { case LINE_START: case ADDR_START: _init_next_address(p, 0); switch (c) { case ' ': case '\t': case '\r': /* ignore leading whitespace */ continue; case '\n': p->line_number++; p->char_number = 0; continue; case '#': case ';': case '/': case '-': state = COMMENT; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p->tmp = (c - '0'); p->digit_count = 1; state = NUMBER0; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': p->tmp = (c - 'a' + 10); p->digit_count = 1; state = IPV6_BEGIN; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': p->tmp = (c - 'A' + 10); p->digit_count = 1; state = IPV6_BEGIN; break; case ':': p->ipv6.tmp[p->ipv6.index++] = 0; state = IPV6_COLON; break; case '[': p->ipv6.is_bracket = 1; state = IPV6_BEGIN; break; default: state = ERROR; length = i; /* break out of loop */ break; } break; case IPV6_CIDR: p->digit_count = 0; p->tmp = 0; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p->tmp = (c - '0'); p->digit_count = 1; state = IPV6_CIDR_NUM; break; default: state = ERROR; length = i; /* break out of loop */ break; } break; case IPV6_COLON: p->digit_count = 0; p->tmp = 0; if (c == ':') { if (p->ipv6.ellision_index < 8) { state = ERROR; length = i; } else { p->ipv6.ellision_index = p->ipv6.index; state = IPV6_COLON; } break; } state = IPV6_BEGIN; /* drop down */ case IPV6_BEGIN: case IPV6_NEXT: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (p->digit_count >= 4) { state = ERROR; length = i; } else { p->tmp = p->tmp * 16 + (c - '0'); p->digit_count++; } break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': if (p->digit_count >= 4) { state = ERROR; length = i; } else { p->tmp = p->tmp * 16 + (c - 'a' + 10); p->digit_count++; } break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': if (p->digit_count >= 4) { state = ERROR; length = i; } else { p->tmp = p->tmp * 16 + (c - 'A' + 10); p->digit_count++; } break; case ':': if (p->ipv6.index >= 8) { state = ERROR; length = i; } else { p->ipv6.tmp[p->ipv6.index++] = (unsigned short)p->tmp; state = IPV6_COLON; } break; case ']': if (!p->ipv6.is_bracket) { state = ERROR; length = i; } else { state = IPV6_END; } break; case '[': if (p->ipv6.is_bracket) { state = ERROR; length = i; } else { p->ipv6.is_bracket = 1; } break; case '/': case ' ': case '\t': case '\r': case '\n': case ',': case '-': i--; /* push back */ state = IPV6_END; continue; default: state = ERROR; length = i; break; } break; case IPV6_END: /* Finish off the trailing number */ p->ipv6.tmp[p->ipv6.index++] = (unsigned short)p->tmp; /* Do the final processing of this IPv6 address and * prepare for the next one */ if (_parser_finish_ipv6(p) != 0) { state = ERROR; length = i; continue; } /* Now decide the next state, whether this is a single * address, an address range, or a CIDR address */ switch (c) { case '/': result = Still_Working; state = IPV6_CIDR; break; case '-': result = Still_Working; state = IPV6_NEXT; break; case '\n': p->line_number++; p->char_number = 0; /* drop down */ case ' ': case '\t': case '\r': case ',': result = Found_IPv6; state = 0; length = i; /* shorten the end to break out of loop */ break; default: state = ERROR; length = i; break; } break; case COMMENT: if (c == '\n') { state = LINE_START; p->line_number++; p->char_number = 0; } else state = COMMENT; break; case IPV6_CIDR_NUM: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (p->digit_count == 4) { state = ERROR; length = i; /* break out of loop */ } else { p->digit_count++; p->tmp = p->tmp * 10 + (c - '0'); if (p->tmp > 128) { state = ERROR; length = i; } continue; } break; case ':': case ',': case ' ': case '\t': case '\r': case '\n': { _ipv6_apply_cidr(&p->ipv6._begin, &p->ipv6._end, p->tmp); state = ADDR_START; length = i; /* break out of loop */ if (c == '\n') { p->line_number++; p->char_number = 0; } *r_begin = p->begin; *r_end = p->end; result = Found_IPv6; } break; default: state = ERROR; length = i; /* break out of loop */ break; } break; case IPV4_CIDR_NUM: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (p->digit_count == 3) { state = ERROR; length = i; /* break out of loop */ } else { p->digit_count++; p->tmp = p->tmp * 10 + (c - '0'); if (p->tmp > 32) { state = ERROR; length = i; } continue; } break; case ':': case ',': case ' ': case '\t': case '\r': case '\n': { _ipv4_apply_cidr(&p->begin, &p->end, p->tmp); state = ADDR_START; length = i; /* break out of loop */ if (c == '\n') { p->line_number++; p->char_number = 0; } *r_begin = p->begin; *r_end = p->end; result = Found_IPv4; } break; default: state = ERROR; length = i; /* break out of loop */ break; } break; case UNIDASH1: if (c == 0x80) state = UNIDASH2; else { state = ERROR; length = i; /* break out of loop */ } break; case UNIDASH2: /* This covers: * U+2010 HYPHEN * U+2011 NON-BREAKING HYPHEN * U+2012 FIGURE DASH * U+2013 EN DASH * U+2014 EM DASH * U+2015 HORIZONTAL BAR */ if (c < 0x90 || 0x95 < c) { state = ERROR; length = i; /* break out of loop */ } else { c = '-'; state = NUMBER3; /* drop down */ } case NUMBER0: case NUMBER1: case NUMBER2: case NUMBER3: case SECOND0: case SECOND1: case SECOND2: case SECOND3: switch (c) { case '.': p->addr = (p->addr << 8) | p->tmp; p->tmp = 0; p->digit_count = 0; if (state == NUMBER3 || state == SECOND3) { length = i; state = ERROR; } else state++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p->digit_count++; p->tmp = p->tmp * 10 + (c - '0'); if (p->tmp > 255 || p->digit_count > 3) { if (state == NUMBER0) { /* Assume that we've actually got an * IPv6 number */ _switch_to_ipv6(p, state); state = IPV6_BEGIN; } else { state = ERROR; length = i; } } continue; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': if (state == NUMBER0 || state == SECOND0) { /* Assume that we've actually got an * IPv6 number */ _switch_to_ipv6(p, state); state = IPV6_BEGIN; i--; /* go back one character */ } else { state = ERROR; length = i; /* break out of loop */ } break; case 0xe2: if (state == NUMBER3) { state = UNIDASH1; } else { state = ERROR; length = i; /* break out of loop */ } break; case '-': case 0x96: /* long dash, comes from copy/pasting into exclude files */ if (state == NUMBER3) { p->begin = (p->addr << 8) | p->tmp; p->tmp = 0; p->digit_count = 0; p->addr = 0; state = SECOND0; } else { state = NUMBER_ERR; length = i; } break; case '/': if (state == NUMBER3) { p->begin = (p->addr << 8) | p->tmp; p->tmp = 0; p->digit_count = 0; p->addr = 0; state = IPV4_CIDR_NUM; } else { state = NUMBER_ERR; length = i; /* break out of loop */ } break; case ':': if (state == NUMBER0) { /* Assume this is an IPv6 address instead of an IPv4 address */ _switch_to_ipv6(p, state); state = IPV6_BEGIN; i--; break; } case ',': case ' ': case '\t': case '\r': case '\n': if (state == NUMBER3) { p->begin = (p->addr << 8) | p->tmp; p->end = p->begin; p->tmp = 0; p->digit_count = 0; p->addr = 0; state = ADDR_START; length = i; /* break out of loop */ if (c == '\n') { p->line_number++; p->char_number = 0; } *r_begin = p->begin; *r_end = p->end; result = Found_IPv4; } else if (state == SECOND3) { p->end = (p->addr << 8) | p->tmp; p->tmp = 0; p->digit_count = 0; p->addr = 0; state = ADDR_START; length = i; /* break out of loop */ if (c == '\n') { p->line_number++; p->char_number = 0; } *r_begin = p->begin; *r_end = p->end; result = Found_IPv4; } else { state = NUMBER_ERR; length = i; } break; default: state = ERROR; length = i; /* break out of loop */ break; } break; default: case ERROR: case NUMBER_ERR: case SECOND_ERR: state = ERROR; length = i; /* break */ break; } } /* The 'offset' parameter is optional. If NULL, then * we don't return a value */ if (r_offset) *r_offset = i; p->state = state; if (state == ERROR || state == NUMBER_ERR || state == SECOND_ERR) result = Found_Error; return result; } /*************************************************************************** * Test errors. We should get exactly which line-number and which character * in the line caused the error ***************************************************************************/ static int rangefile_test_error(const char *buf, unsigned long long in_line_number, unsigned long long in_char_number, unsigned which_test) { size_t length = strlen(buf); size_t offset = 0; struct massip_parser p[1]; unsigned out_begin = 0xa3a3a3a3; unsigned out_end = 0xa3a3a3a3; unsigned long long out_line_number; unsigned long long out_char_number; int x; /* test the entire buffer */ _parser_init(p); x = _parser_next(p, buf, &offset, length, &out_begin, &out_end); if (x != Found_Error) goto fail; _parser_err(p, &out_line_number, &out_char_number); if (in_line_number != out_line_number || in_char_number != out_char_number) goto fail; /* test one byte at a time */ _parser_destroy(p); _parser_init(p); offset = 0; out_begin = 0xa3a3a3a3; out_end = 0xa3a3a3a3; x = 0; while (offset < length) { x = _parser_next(p, buf, &offset, offset+1, &out_begin, &out_end); if (x == Found_Error) break; } if (x != Found_Error) goto fail; _parser_err(p, &out_line_number, &out_char_number); if (in_line_number != out_line_number || in_char_number != out_char_number) goto fail; _parser_destroy(p); return 0; fail: _parser_destroy(p); fprintf(stderr, "[-] rangefile test fail, line=%u\n", which_test); return 1; } /*************************************************************************** ***************************************************************************/ int massip_parse_file(struct MassIP *massip, const char *filename) { struct RangeList *targets_ipv4 = &massip->ipv4; struct Range6List *targets_ipv6 = &massip->ipv6; struct massip_parser p[1]; char buf[65536]; FILE *fp = NULL; bool is_error = false; unsigned addr_count = 0; unsigned long long line_number, char_number; /* Kludge: should never happen, should fix this when reading in * config, not this deep in the code. */ if (filename == 0 || filename[0] == '\0') { fprintf(stderr, "[-] missing filename for ranges\n"); exit(1); } /* * Open the file containing IP addresses, which can potentially be * many megabytes in size */ if (strcmp(filename, "-") == 0) { fp = stdin; } else { fp = fopen(filename, "rb"); if (fp == NULL) { fprintf(stderr, "[-] FAIL: parsing IP addresses\n"); fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno)); exit(1); } } /* * Create a parser for reading in the IP addresses using a state * machine parser */ _parser_init(p); /* * Read in the data a block at a time, parsing according to the state * machine. */ while (!is_error) { size_t count; size_t offset; count = fread(buf, 1, sizeof(buf), fp); if (count <= 0) break; offset = 0; while (offset < count) { unsigned begin, end; int err; err = _parser_next(p, buf, &offset, count, &begin, &end); switch (err) { case Still_Working: if (offset < count) { /* We reached this somehow in the middle of the buffer, but * this return is only possible at the end of the buffer */ fprintf(stderr, "[-] rangeparse_next(): unknown coding failure\n"); } break; case Found_Error: default: _parser_err(p, &line_number, &char_number); fprintf(stderr, "[-] %s:%llu:%llu: invalid IP address on line #%llu\n", filename, line_number, char_number, line_number); is_error = true; count = offset; break; case Found_IPv4: rangelist_add_range(targets_ipv4, begin, end); addr_count++; break; case Found_IPv6: { ipv6address found_begin, found_end; _parser_get_ipv6(p, &found_begin, &found_end); range6list_add_range(targets_ipv6, found_begin, found_end); addr_count++; } break; } } } /* Close the file, unless we are reading from */ if (fp != stdin && fp != NULL) fclose(fp); /* In case the file doesn't end with a newline '\n', then artificially * add one to the end. This is just a repeat of the code above */ if (!is_error) { size_t offset = 0; unsigned begin, end; int err; err = _parser_next(p, "\n", &offset, 1, &begin, &end); switch (err) { case Still_Working: break; case Found_Error: default: _parser_err(p, &line_number, &char_number); fprintf(stderr, "[-] %s:%llu:%llu: invalid IP address on line #%llu\n", filename, line_number, char_number, line_number); is_error = true; break; case Found_IPv4: rangelist_add_range(targets_ipv4, begin, end); addr_count++; break; case Found_IPv6: { ipv6address found_begin, found_end; _parser_get_ipv6(p, &found_begin, &found_end); range6list_add_range(targets_ipv6, found_begin, found_end); addr_count++; } break; } } LOG(1, "[+] %s: %u addresses read\n", filename, addr_count); /* Target list must be sorted every time it's been changed, * before it can be used */ rangelist_sort(targets_ipv4); if (is_error) return -1; /* fail */ else return 0; /* success*/ } ipv6address massip_parse_ipv6(const char *line) { struct massip_parser p[1]; size_t count = strlen(line); size_t offset = 0; int err; unsigned begin, end; ipv6address result; ipv6address range; _parser_init(p); err = _parser_next(p, line, &offset, count, &begin, &end); again: switch (err) { case Still_Working: if (offset < count) { /* We reached this somehow in the middle of the buffer, but * this return is only possible at the end of the buffer */ fprintf(stderr, "[-] _parser_next(): unknown coding failure\n"); goto fail; } else { err = _parser_next(p, "\n", 0, 1, &begin, &end); if (err == Still_Working) { fprintf(stderr, "[-] _parser_next(): unknown coding failure\n"); goto fail; } else { goto again; } } break; case Found_Error: default: goto fail; case Found_IPv4: goto fail; case Found_IPv6: _parser_get_ipv6(p, &result, &range); if (!ipv6address_is_equal(result, range)) goto fail; return result; } fail: result.hi = ~0ULL; result.lo = ~0ULL; return result; } unsigned massip_parse_ipv4(const char *line) { struct massip_parser p[1]; size_t count = strlen(line); size_t offset = 0; int err; unsigned begin, end; _parser_init(p); err = _parser_next(p, line, &offset, count, &begin, &end); again: switch (err) { case Still_Working: if (offset < count) { /* We reached this somehow in the middle of the buffer, but * this return is only possible at the end of the buffer */ fprintf(stderr, "[-] _parser_next(): unknown coding failure\n"); goto fail; } else { err = _parser_next(p, "\n", 0, 1, &begin, &end); if (err == Still_Working) { fprintf(stderr, "[-] _parser_next(): unknown coding failure\n"); goto fail; } else { goto again; } } break; case Found_Error: default: goto fail; case Found_IPv6: goto fail; case Found_IPv4: if (begin != end) goto fail; return begin; } fail: return 0xFFFFFFFF; } enum RangeParseResult massip_parse_range(const char *line, size_t *offset, size_t count, struct Range *ipv4, struct Range6 *ipv6) { struct massip_parser p[1]; int err; unsigned begin, end; size_t tmp_offset = 0; /* The 'count' (length of the string) is an optional parameter. If * zero, and also the offset is NULL, then set it to the string length */ if (count == 0 && offset == NULL) count = strlen(line); /* The offset is an optional parameter. If NULL, then we set * it to point to a value on the stack instead */ if (offset == NULL) offset = &tmp_offset; /* Create e parser object */ _parser_init(p); /* Parse the next range from the input */ err = _parser_next(p, line, offset, count, &begin, &end); again: switch (err) { case Still_Working: if (*offset < count) { /* We reached this somehow in the middle of the buffer, but * this return is only possible at the end of the buffer */ fprintf(stderr, "[-] _parser_next(): unknown coding failure\n"); return Bad_Address; } else { err = _parser_next(p, "\n", 0, 1, &begin, &end); if (err == Still_Working) { fprintf(stderr, "[-] _parser_next(): unknown coding failure\n"); return Bad_Address; } else { goto again; } } break; case Found_Error: default: return Bad_Address; case Found_IPv4: ipv4->begin = begin; ipv4->end = end; return Ipv4_Address; case Found_IPv6: _parser_get_ipv6(p, &ipv6->begin, &ipv6->end); return Ipv6_Address; } } /** * This tests parsing when addresses/ranges are specified on the command-line * or configuration files, rather than the other test-cases which test parsing * when the IP addresses are specified in a file. The thing we are looking for * here is specifically when users separate addresses with things like * commas and spaces. */ static int selftest_massip_parse_range(void) { struct testcases { const char *line; union { struct Range ipv4; struct Range6 ipv6; } list[4]; } cases[] = { {"0.0.1.0/24,0.0.3.0-0.0.4.0", {{{0x100,0x1ff}}, {{0x300,0x400}}}}, {"0.0.1.0-0.0.1.255,0.0.3.0-0.0.4.0", {{{0x100,0x1ff}}, {{0x300,0x400}}}}, {"0.0.1.0/24 0.0.3.0-0.0.4.0", {{{0x100,0x1ff}}, {{0x300,0x400}}}}, {0} }; size_t i; for (i=0; cases[i].line; i++) { size_t length = strlen(cases[i].line); size_t offset = 0; size_t j = 0; struct Range6 range6; struct Range range4; while (offset < length) { int x; x = massip_parse_range(cases[i].line, &offset, length, &range4, &range6); switch (x) { default: case Bad_Address: fprintf(stdout, "[-] selftest_massip_parse_range[%u] fail\n", (unsigned)i); return 1; case Ipv4_Address: if (cases[i].list[j].ipv4.begin != range4.begin || cases[i].list[j].ipv4.end != range4.end) { fprintf(stdout, "[-] %u.%u.%u.%u - %u.%u.%u.%u\n", (unsigned char)(range4.begin>>24), (unsigned char)(range4.begin>>16), (unsigned char)(range4.begin>> 8), (unsigned char)(range4.begin>> 0), (unsigned char)(range4.end>>24), (unsigned char)(range4.end>>16), (unsigned char)(range4.end>> 8), (unsigned char)(range4.end>> 0) ); fprintf(stdout, "[-] selftest_massip_parse_range[%u] fail\n", (unsigned)i); return 1; } break; } j++; } /* Make sure we have found all the expected cases */ if (cases[i].list[j].ipv4.begin != 0) { fprintf(stdout, "[-] selftest_massip_parse_range[%u] fail\n", (unsigned)i); return 1; } } return 0; } /*************************************************************************** ***************************************************************************/ static int rangefile6_test_buffer(struct massip_parser *parser, const char *buf, ipv6address expected_begin, ipv6address expected_end) { size_t length = strlen(buf); size_t offset = 0; ipv6address found_begin = {1,2}; ipv6address found_end = {1,2}; unsigned tmp1, tmp2; int err; /* test the entire buffer */ err = _parser_next(parser, buf, &offset, length, &tmp1, &tmp2); if (err == Still_Working) err = _parser_next(parser, "\n", 0, 1, &tmp1, &tmp2); switch (err) { case Found_IPv6: /* Extract the resulting IPv6 address from the state structure */ _parser_get_ipv6(parser, &found_begin, &found_end); /* Test to see if the parsed address equals the expected address */ if (!ipv6address_is_equal(found_begin, expected_begin)) { ipaddress_formatted_t fmt1 = ipv6address_fmt(found_begin); ipaddress_formatted_t fmt2 = ipv6address_fmt(expected_begin); fprintf(stderr, "[-] begin mismatch: found=[%s], expected=[%s]\n", fmt1.string, fmt2.string); goto fail; } if (!ipv6address_is_equal(found_end, expected_end)) { ipaddress_formatted_t fmt1 = ipv6address_fmt(found_end); ipaddress_formatted_t fmt2 = ipv6address_fmt(expected_end); fprintf(stderr, "[-] end mismatch: found=[%s], expected=[%s]\n", fmt1.string, fmt2.string); goto fail; } break; case Found_IPv4: if (expected_begin.hi != 0 || expected_end.hi != 0) goto fail; if (tmp1 != expected_begin.lo || tmp2 != expected_end.lo) goto fail; break; case Still_Working: /* Found a partial address, which is a normal result in the * real world at buffer boundaries, but which is an error * here */ goto fail; case Found_Error: default: goto fail; } return 0; /* success */ fail: return 1; /* failure */ } /*************************************************************************** * List of test cases. Each test case contains three parts: * - the string representation of an address, as read from a file, meaning * that it can contain additional things like comment strings * - the first address of a range, which in the case of IPv6 addresses * will be two 64-bit numbers, but an IPv4 address have a high-order * number set to zero and the low-order number set to the IPv4 address * - the second address of a range, which in the case of individual * addresses, will be equal to the first number ***************************************************************************/ struct { const char *string; ipv6address begin; ipv6address end; } test_cases[] = { {"[1::1]/126", {0x0001000000000000ULL, 0ULL}, {0x0001000000000000ULL, 3ULL}}, {"1::1/126", {0x0001000000000000ULL, 0ULL}, {0x0001000000000000ULL, 3ULL}}, {"[1::1]-[2::3]", {0x0001000000000000ULL, 1ULL}, {0x0002000000000000ULL, 3ULL}}, {"1::1-2::3", {0x0001000000000000ULL, 1ULL}, {0x0002000000000000ULL, 3ULL}}, {"[1234:5678:9abc:def0:0fed:cba9:8765:4321]", {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}, {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}}, {"22ab::1", {0x22ab000000000000ULL, 1ULL}, {0x22ab000000000000ULL, 1ULL}}, {"240e:33c:2:c080:d08:d0e:b53:e74e", {0x240e033c0002c080ULL, 0x0d080d0e0b53e74eULL}, {0x240e033c0002c080ULL, 0x0d080d0e0b53e74eULL}}, {"2a03:90c0:105::9", {0x2a0390c001050000ULL, 9ULL}, {0x2a0390c001050000ULL, 9ULL}}, {"2a03:9060:0:400::2", {0x2a03906000000400ULL, 2ULL}, {0x2a03906000000400ULL, 2ULL}}, {"2c0f:ff00:0:a:face:b00c:0:a7", {0x2c0fff000000000aULL, 0xfaceb00c000000a7ULL}, {0x2c0fff000000000aULL, 0xfaceb00c000000a7ULL}}, {"2a01:5b40:0:4a01:0:e21d:789f:59b1", {0x2a015b4000004a01ULL, 0x0000e21d789f59b1ULL}, {0x2a015b4000004a01ULL, 0x0000e21d789f59b1ULL}}, {"2001:1200:10::1", {0x2001120000100000ULL, 1ULL}, {0x2001120000100000ULL, 1ULL}}, {"fec0:0:0:ffff::1", {0xfec000000000ffffULL, 1ULL}, {0xfec000000000ffffULL, 1ULL}}, {"1234:5678:9abc:def0:0fed:cba9:8765:4321", {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}, {0x123456789abcdef0ULL, 0x0fedcba987654321ULL}}, {"[1111:2222:3333:4444:5555:6666:7777:8888]", {0x1111222233334444ULL, 0x5555666677778888ULL}, {0x1111222233334444ULL, 0x5555666677778888ULL}}, {"1::1", {0x0001000000000000ULL, 1ULL}, {0x0001000000000000ULL, 1ULL}}, {"1.2.3.4", {0, 0x01020304}, {0, 0x01020304}}, {"#test\n 97.86.162.161" "\x96" "97.86.162.175\n", {0, 0x6156a2a1}, {0, 0x6156a2af}}, {"1.2.3.4/24\n", {0, 0x01020300}, {0, 0x010203ff}}, {" 1.2.3.4-1.2.3.5\n", {0, 0x01020304}, {0, 0x01020305}}, {0,{0,0},{0,0}} }; /*************************************************************************** * Called during "make test" to run a regression test over this module. ***************************************************************************/ int massip_parse_selftest(void) { int x = 0; size_t i; struct massip_parser parser[1]; /* Run through the test cases, stopping at the first failure */ _parser_init(parser); for (i=0; test_cases[i].string; i++) { x += rangefile6_test_buffer(parser, test_cases[i].string, test_cases[i].begin, test_cases[i].end); if (x) { fprintf(stderr, "[-] failed: %u: %s\n", (unsigned)i, test_cases[i].string); break; } } _parser_destroy(parser); /* First, do the single line test */ x += selftest_massip_parse_range(); if (x) return x; x += rangefile_test_error("#bad ipv4\n 257.1.1.1\n", 2, 5, __LINE__); x += rangefile_test_error("#bad ipv4\n 1.257.1.1.1\n", 2, 6, __LINE__); x += rangefile_test_error("#bad ipv4\n 1.10.257.1.1.1\n", 2, 9, __LINE__); x += rangefile_test_error("#bad ipv4\n 1.10.255.256.1.1.1\n", 2, 13, __LINE__); x += rangefile_test_error("#bad ipv4\n 1.1.1.1.1\n", 2, 9, __LINE__); if (x) LOG(0, "[-] rangefile_selftest: fail\n"); return x; } ================================================ FILE: src/massip-parse.h ================================================ /* massip-parse This module parses IPv4 and IPv6 addresses. It's not a typical parser. It's optimized around parsing large files containing millions of addresses and ranges using a "state-machine parser". */ #ifndef MASSIP_PARSE_H #define MASSIP_PARSE_H #include "massip-addr.h" struct MassIP; struct Range; struct Range6; /** * Parse a file, extracting all the IPv4 and IPv6 addresses and ranges. * This is optimized for speed, handling millions of entries in under * a second. This is especially tuned for IPv6 addresses, as while IPv4 * scanning is mostly done with target rnages, IPv6 scanning is mostly * done with huge lists of target addresses. * @param filename * The name of the file that we'll open, parse, and close. * @param targets_ipv4 * The list of IPv4 targets that we append any IPv4 addresses to. * @param targets_ipv6 * The list of IPv6 targets that we append any IPv6 addresses/ranges to. * @return 0 on success, any other number on failure. */ int massip_parse_file(struct MassIP *massip, const char *filename); enum RangeParseResult { Bad_Address, Ipv4_Address=4, Ipv6_Address=6, }; /** * Parse the next IPv4/IPv6 range from a string. This is called * when parsing strings from the command-line. */ enum RangeParseResult massip_parse_range(const char *line, size_t *inout_offset, size_t max, struct Range *ipv4, struct Range6 *ipv6); /** * Parse a single IPv6 address. This is called when working with * the operating system stack, when querying addresses from * the local network adapters. */ ipv6address_t massip_parse_ipv6(const char *buf); ipv4address_t massip_parse_ipv4(const char *buf); /** * Do a simplistic unit test of the parser. * @return 0 on success, 1 on failure */ int massip_parse_selftest(void); #endif ================================================ FILE: src/massip-port.h ================================================ #ifndef MASSIP_PORT_H #define MASSIP_PORT_H /* * Ports are 16-bit numbers ([0..65535], but different * transports (TCP, UDP, SCTP) are distinct port ranges. Thus, we * instead of three 64k ranges we could instead treat this internally * as a 192k port range. We can expand this range to include other * things we scan for, such as ICMP pings or ARP requests. */ enum { Templ_TCP = 0, Templ_TCP_last = 65535, Templ_UDP = 65536, Templ_UDP_last = 65536 + 65535, Templ_SCTP = 65536*2, Templ_SCTP_last = 65536*2 + 65535, Templ_ICMP_echo = 65536*3+0, Templ_ICMP_timestamp = 65536*3+1, Templ_ARP = 65536*3+2, Templ_Oproto_first = 65536*3 + 256, Templ_Oproto_last = 65536*3 + 256 + 255, Templ_VulnCheck = 65536*4, }; #endif ================================================ FILE: src/massip-rangesv4.c ================================================ /* IPv4 and port ranges This is one of the more integral concepts to how masscan works internally. We combine all the input addresses and address ranges into a sorted list of 'target' IP addresses. This allows us to enumerate all the addresses in order by incrementing a simple index. It is that index that we randomize in order to produce random output, but internally, everything is sorted. Sorting the list allows us to remove duplicates. It also allows us to apply the 'excludes' directly to the input list. In other words, other scanners typically work by selecting an IP address at random, then checking to see if it's been excluded, then skipping it. In this scanner, however, we remove all the excluded address from the targets list before we start scanning. This module has been tuned to support mass lists of millions of target IPv4 addresses and excludes. This has required: - a fast way to parse the address from a file (see range-file.c) - fast sort (just using qsort() from the standard C library) - fast application of exludes, using an optimal O(n + m) linear algorithm, where 'n' is the number of targets, and 'm' is the number of excluded ranges. Large lists can still take a bit to process. On a fast server with 7-million input ranges/addresses and 5000 exclude ranges/addresses, it takes almost 3 seconds to process everything before starting. */ #include "massip-rangesv4.h" #include "massip-port.h" #include "util-logger.h" #include "util-bool.h" #include "util-malloc.h" #include #include #include #include #include #include #ifdef _MSC_VER #pragma warning(disable:4204) #endif #define BUCKET_COUNT 16 #define REGRESS(x) if (!(x)) return (fprintf(stderr, "regression failed %s:%d\n", __FILE__, __LINE__)|1) /* An invalid range, where begin comes after the end */ static struct Range INVALID_RANGE = {2,1}; /*************************************************************************** * Does a linear search to see if the list contains the address/port. * FIXME: This should be upgraded to a binary search. However, we don't * really use it in any performance critical code, so it's okay * as a linear search. ***************************************************************************/ int rangelist_is_contains(const struct RangeList *targets, unsigned addr) { unsigned i; for (i=0; icount; i++) { struct Range *range = &targets->list[i]; if (range->begin <= addr && addr <= range->end) return 1; } return 0; } /*************************************************************************** * Returns the first CIDR range (which can be specified with prefix bits) * that fits within the input range. For example, consider the range * [10.0.0.4->10.0.0.255]. This does't match the bigger CIDR range. * The first range that would fit would be [10.0.0.04/30], or * [10.0.0.4->10.0.0.7]. * * Using this function allows us to decompose ***************************************************************************/ struct Range range_first_cidr(const struct Range range, unsigned *prefix_bits) { struct Range result = {range.begin, range.end}; unsigned zbits = 0; /* Kludge: Special Case: * All inputs work but the boundary case of [0.0.0.0/0] or * [0.0.0.0-255.255.255.255]. I can't be bothered to figure out * why the algorithm doesn't work with this range, so I'm just * going to special case it here*/ if (range.begin == 0 && range.end == 0xFFFFffff) { if (prefix_bits != NULL) *prefix_bits = 0; return range; } /* Count the number of trailing/suffix zeros, which may be range * from none (0) to 32 (all bits are 0) */ for (zbits = 0; zbits <= 32; zbits++) { if ((range.begin & (1< 0) { unsigned mask = ~(0xFFFFFFFF << zbits); if (range.begin + mask > range.end) zbits--; else break; } result.begin = range.begin; result.end = range.begin + ~(0xFFFFffff << zbits); if (prefix_bits != NULL) *prefix_bits = 32-zbits; return result; } bool range_is_cidr(const struct Range range, unsigned *prefix_bits) { struct Range out = range_first_cidr(range, prefix_bits); if (out.begin == range.begin && out.end == range.end) return true; else { if (prefix_bits != NULL) *prefix_bits = 0xFFFFFFFF; return false; } } /*************************************************************************** * Selftest for the above function. ***************************************************************************/ static int selftest_range_first_cidr(void) { static struct { struct Range in; struct Range out; unsigned prefix_bits; } tests[] = { {{0x00000000, 0xffffffff}, {0x00000000, 0xffffffff}, 0}, {{0x00000001, 0xffffffff}, {0x00000001, 0x00000001}, 32}, {{0xffffffff, 0xffffffff}, {0xffffffff, 0xffffffff}, 32}, {{0xfffffffe, 0xfffffffe}, {0xfffffffe, 0xfffffffe}, 32}, {{0x0A000000, 0x0A0000Ff}, {0x0A000000, 0x0A0000ff}, 24}, {{0x0A0000ff, 0x0A0000Ff}, {0x0A0000ff, 0x0A0000ff}, 32}, {{0x0A000000, 0x0A0000Ff}, {0x0A000000, 0x0A0000Ff}, 24}, {{0x0A000001, 0x0A0000Fe}, {0x0A000001, 0x0A000001}, 32}, {{0x0A000008, 0x0A0000Fe}, {0x0A000008, 0x0A00000f}, 29}, {{0x0A000080, 0x0A0000Fe}, {0x0A000080, 0x0A0000bf}, 26}, {{0x0A0000c0, 0x0A0000Fe}, {0x0A0000c0, 0x0A0000df}, 27}, {{0x0A0000c1, 0x0A0000Fe}, {0x0A0000c1, 0x0A0000c1}, 32}, {{0x0A0000fe, 0x0A0000Fe}, {0x0A0000fe, 0x0A0000fe}, 32}, {{0,0}, {0,0}} }; size_t i; for (i=0; tests[i].in.end != 0; i++) { unsigned prefix_bits = 0xFFFFFFFF; struct Range out = range_first_cidr(tests[i].in, &prefix_bits); if (out.begin != tests[i].out.begin || out.end != tests[i].out.end || prefix_bits != tests[i].prefix_bits) { fprintf(stderr, "[%u] 0x%08x->0x%08x /%u 0x%08x->0x%08x /%u\n", (unsigned)i, out.begin, out.end, prefix_bits, tests[i].out.begin, tests[i].out.end, tests[i].prefix_bits); return 1; } } return 0; } /*************************************************************************** * Test if two ranges overlap. * FIXME: I need to change this so that it (a) doesn't trigger on invalid * ranges (those where begin>end) and (b) use a simpler algorithm ***************************************************************************/ static int range_is_overlap(struct Range lhs, struct Range rhs) { if (lhs.begin < rhs.begin) { if (lhs.end == 0xFFFFFFFF || lhs.end + 1 >= rhs.begin) return 1; } if (lhs.begin >= rhs.begin) { if (lhs.end <= rhs.end) return 1; } if (rhs.begin < lhs.begin) { if (rhs.end == 0xFFFFFFFF || rhs.end + 1 >= lhs.begin) return 1; } if (rhs.begin >= lhs.begin) { if (rhs.end <= lhs.end) return 1; } return 0; } /*************************************************************************** * Combine two ranges, such as when they overlap. ***************************************************************************/ static void range_combine(struct Range *lhs, struct Range rhs) { if (lhs->begin > rhs.begin) lhs->begin = rhs.begin; if (lhs->end < rhs.end) lhs->end = rhs.end; } /*************************************************************************** * Callback for qsort() for comparing two ranges ***************************************************************************/ static int range_compare(const void *lhs, const void *rhs) { struct Range *left = (struct Range *)lhs; struct Range *right = (struct Range *)rhs; if (left->begin < right->begin) return -1; else if (left->begin > right->begin) return 1; else return 0; } /*************************************************************************** ***************************************************************************/ static void rangelist_remove_at(struct RangeList *targets, size_t index) { memmove(&targets->list[index], &targets->list[index+1], (targets->count - index) * sizeof(targets->list[index]) ); targets->count--; } /*************************************************************************** ***************************************************************************/ void rangelist_sort(struct RangeList *targets) { size_t i; struct RangeList newlist = {0}; unsigned original_count = targets->count; /* Empty lists are, of course, sorted. We need to set this * to avoid an error later on in the code which asserts that * the lists are sorted */ if (targets->count == 0) { targets->is_sorted = 1; return; } /* If it's already sorted, then skip this */ if (targets->is_sorted) { return; } /* First, sort the list */ LOG(3, "[+] range:sort: sorting...\n"); qsort( targets->list, /* the array to sort */ targets->count, /* number of elements to sort */ sizeof(targets->list[0]), /* size of element */ range_compare); /* Second, combine all overlapping ranges. We do this by simply creating * a new list from a sorted list, so we don't have to remove things in the * middle when collapsing overlapping entries together, which is painfully * slow. */ LOG(3, "[+] range:sort: combining...\n"); for (i=0; icount; i++) { rangelist_add_range(&newlist, targets->list[i].begin, targets->list[i].end); } LOG(3, "[+] range:sort: combined from %u elements to %u elements\n", original_count, newlist.count); free(targets->list); targets->list = newlist.list; targets->count = newlist.count; newlist.list = 0; LOG(2, "[+] range:sort: done...\n"); targets->is_sorted = 1; } /*************************************************************************** * Add the IPv4 range to our list of ranges. ***************************************************************************/ void rangelist_add_range(struct RangeList *targets, unsigned begin, unsigned end) { struct Range range; range.begin = begin; range.end = end; /* auto-expand the list if necessary */ if (targets->count + 1 >= targets->max) { targets->max = targets->max * 2 + 1; targets->list = REALLOCARRAY(targets->list, targets->max, sizeof(targets->list[0])); } /* If empty list, then add this one */ if (targets->count == 0) { targets->list[0] = range; targets->count++; targets->is_sorted = 1; return; } /* If new range overlaps the last range in the list, then combine it * rather than appending it. This is an optimization for the fact that * we often read in sequential addresses */ if (range_is_overlap(targets->list[targets->count - 1], range)) { range_combine(&targets->list[targets->count - 1], range); targets->is_sorted = 0; return; } /* append to the end of our list */ targets->list[targets->count] = range; targets->count++; targets->is_sorted = 0; } /** Use this when adding TCP ports, to avoid the comoplication of how * ports are stored */ void rangelist_add_range_tcp(struct RangeList *targets, unsigned begin, unsigned end) { rangelist_add_range(targets, Templ_TCP + begin, Templ_TCP + end); } /** Use this when adding UDP ports, to avoid the comoplication of how * ports are stored */ void rangelist_add_range_udp(struct RangeList *targets, unsigned begin, unsigned end) { rangelist_add_range(targets, Templ_UDP + begin, Templ_UDP + end); } /*************************************************************************** * This is the "free" function for the list, freeing up any memory we've * allocated. ***************************************************************************/ void rangelist_remove_all(struct RangeList *targets) { free(targets->list); free(targets->picker); memset(targets, 0, sizeof(*targets)); } /*************************************************************************** ***************************************************************************/ void rangelist_merge(struct RangeList *list1, const struct RangeList *list2) { unsigned i; for (i=0; icount; i++) { rangelist_add_range(list1, list2->list[i].begin, list2->list[i].end); } rangelist_sort(list1); } /*************************************************************************** * This searches a range list and removes that range of IP addresses, if * they exist. Since the input range can overlap multiple entries, then * more than one entry can be removed, or truncated. Since the range * can be in the middle of an entry in the list, it can actually increase * the list size by one, as that entry is split into two entries. * DEPRECATED: this function is deprecated, and will be removed at some * point. It's only remaining in order to serve as a regression test for * its replacement. ***************************************************************************/ static void rangelist_remove_range(struct RangeList *targets, unsigned begin, unsigned end) { unsigned i; struct Range x; x.begin = begin; x.end = end; /* See if the range overlaps any exist range already in the * list */ for (i = 0; i < targets->count; i++) { if (!range_is_overlap(targets->list[i], x)) continue; /* If the removal-range wholly covers the range, delete * it completely */ if (begin <= targets->list[i].begin && end >= targets->list[i].end) { rangelist_remove_at(targets, i); i--; continue; } /* If the removal-range bisects the target-rage, truncate * the lower end and add a new high-end */ if (begin > targets->list[i].begin && end < targets->list[i].end) { struct Range newrange; newrange.begin = end+1; newrange.end = targets->list[i].end; targets->list[i].end = begin-1; rangelist_add_range(targets, newrange.begin, newrange.end); i--; continue; } /* If overlap on the lower side */ if (end >= targets->list[i].begin && end < targets->list[i].end) { targets->list[i].begin = end+1; } /* If overlap on the upper side */ if (begin > targets->list[i].begin && begin <= targets->list[i].end) { targets->list[i].end = begin-1; } //assert(!"impossible"); } } static void rangelist_add_range2(struct RangeList *targets, struct Range range) { rangelist_add_range(targets, range.begin, range.end); } static void rangelist_remove_range2(struct RangeList *targets, struct Range range) { rangelist_remove_range(targets, range.begin, range.end); } /*************************************************************************** * Parse an IPv4 address from a line of text, moving the offset forward * to the first non-IPv4 character ***************************************************************************/ static int parse_ipv4(const char *line, unsigned *inout_offset, unsigned max, unsigned *ipv4) { unsigned offset = *inout_offset; unsigned result = 0; unsigned i; for (i=0; i<4; i++) { unsigned x = 0; unsigned digits = 0; if (offset >= max) return -4; if (!isdigit(line[offset]&0xFF)) return -1; /* clear leading zeros */ while (offset < max && line[offset] == '0') offset++; /* parse maximum of 3 digits */ while (offset < max && isdigit(line[offset]&0xFF)) { x = x * 10 + (line[offset] - '0'); offset++; if (++digits > 3) return -2; } if (x > 255) return -5; result = result * 256 + (x & 0xFF); if (i == 3) break; if (line[offset] != '.') return -3; offset++; /* skip dot */ } *inout_offset = offset; *ipv4 = result; return 0; /* parse OK */ } /**************************************************************************** * Parse from text an IPv4 address range. This can be in one of several * formats: * - '192.168.1.1" - a single address * - '192.168.1.0/24" - a CIDR spec * - '192.168.1.0-192.168.1.255' - a range * @param line * Part of a line of text, probably read from a commandline or conf * file. * @param inout_offset * On input, the offset from the start of the line where the address * starts. On output, the offset of the first character after the * range, or equal to 'max' if the line prematurely ended. * @param max * The maximum length of the line. * @return * The first and last address of the range, inclusive. ****************************************************************************/ struct Range range_parse_ipv4(const char *line, unsigned *inout_offset, unsigned max) { unsigned offset; struct Range result; static const struct Range badrange = {0xFFFFFFFF, 0}; int err; if (line == NULL) return badrange; if (inout_offset == NULL) { inout_offset = &offset; offset = 0; max = (unsigned)strlen(line); } else offset = *inout_offset; /* trim whitespace */ while (offset < max && isspace(line[offset]&0xFF)) offset++; /* get the first IP address */ err = parse_ipv4(line, &offset, max, &result.begin); if (err) { return badrange; } result.end = result.begin; /* trim whitespace */ while (offset < max && isspace(line[offset]&0xFF)) offset++; /* If only one IP address, return that */ if (offset >= max) goto end; /* * Handle CIDR address of the form "10.0.0.0/8" */ if (line[offset] == '/') { uint64_t prefix = 0; uint64_t mask = 0; unsigned digits = 0; /* skip slash */ offset++; if (!isdigit(line[offset]&0xFF)) { return badrange; } /* strip leading zeroes */ while (offset 2) return badrange; } if (prefix > 32) return badrange; /* Create the mask from the prefix */ mask = 0xFFFFFFFF00000000ULL >> prefix; /* Mask off any non-zero bits from the start * TODO print warning */ result.begin &= mask; /* Set all suffix bits to 1, so that 192.168.1.0/24 has * an ending address of 192.168.1.255. */ result.end = result.begin | (unsigned)~mask; goto end; } /* * Handle a dashed range like "10.0.0.100-10.0.0.200" */ if (offset>24)&0xFF), ((ip>>16)&0xFF), ((ip>>8)&0xFF), ((ip>>0)&0xFF), ((result.begin>>24)&0xFF), ((result.begin>>16)&0xFF), ((result.begin>>8)&0xFF), ((result.begin>>0)&0xFF) ); } else result.end = ip; goto end; } end: *inout_offset = offset; return result; } /*************************************************************************** * This is the old algorithm for applying exclude ranges, very slow * for large lists. We keep it around for verifying correctness of the * new replacement algorithm. ***************************************************************************/ static void rangelist_exclude2( struct RangeList *targets, const struct RangeList *excludes) { unsigned i; for (i=0; icount; i++) { struct Range range = excludes->list[i]; rangelist_remove_range(targets, range.begin, range.end); } /* Since chopping up large ranges can split ranges, this can * grow the list so we need to re-sort it */ rangelist_sort(targets); } /** * Applies the (presumably overlapping) exclude range to the target. This can have * four outcomes: * - there is no overlap, in which case 'target' is unchanged, and 'split' * is set to INVALID. * - the entire target is excluded, in which case it's set to INVALID. * - the overlap is at the beginning, in which case the 'begin' is increased. * - the overlap is at the end, in which case 'end' is reduced. * - the overlap is in the middle, in which case the target is split * in two, with 'target' becoming the low addresses, and 'split' becoming * the high addresses. */ static void range_apply_exclude(const struct Range exclude, struct Range *target, struct Range *split) { /* Set 'split' to invalid to start with */ split->begin = 2; split->end = 1; /* Case 1: no overlap */ if (target->begin > exclude.end || target->end < exclude.begin) { return; } /* Case 2: complete overlap, mark target as invalid and return */ if (target->begin >= exclude.begin && target->end <= exclude.end) { target->begin = 2; target->end = 1; return; } /* Case 3: overlap at start */ if (target->begin >= exclude.begin && target->end > exclude.end) { target->begin = exclude.end + 1; return; } /* Case 4: overlap at end */ if (target->begin < exclude.begin && target->end <= exclude.end) { target->end = exclude.begin - 1; return; } /* Case 5: this range needs to be split */ if (target->begin < exclude.begin && target->end > exclude.end) { split->end = target->end; split->begin = exclude.end + 1; target->end = exclude.begin - 1; return; } /* No other condition should be possible */ assert(!"possible"); } /*************************************************************************** ***************************************************************************/ int range_is_valid(struct Range range) { return range.begin <= range.end; } /*************************************************************************** * Apply the exclude ranges, which means removing everything from "targets" * that's also in "exclude". This can make the target list even bigger * as individually excluded address chop up large ranges. ***************************************************************************/ void rangelist_exclude( struct RangeList *targets, struct RangeList *excludes) { unsigned i; unsigned x; struct RangeList newlist = {0}; /* Both lists must be sorted */ rangelist_sort(targets); rangelist_sort(excludes); /* Go through all target ranges, apply excludes to them * (which may split into two ranges), and add them to * the new target list */ x = 0; for (i=0; icount; i++) { struct Range range = targets->list[i]; /* Move the exclude forward until we find a potentially * overlapping candidate */ while (x < excludes->count && excludes->list[x].end < range.begin) x++; /* Keep applying excludes to this range as long as there are overlaps */ while (x < excludes->count && excludes->list[x].begin <= range.end) { struct Range split = INVALID_RANGE; range_apply_exclude(excludes->list[x], &range, &split); /* If there is a split, then add the original range to our list * and then set that range to the split-ed portion */ if (range_is_valid(split)) { rangelist_add_range(&newlist, range.begin, range.end); memcpy(&range, &split, sizeof(range)); } if (excludes->list[x].begin > range.end) break; x++; } /* If the range hasn't been completely excluded, then add the remnants */ if (range_is_valid(range)) { rangelist_add_range(&newlist, range.begin, range.end); } } /* Now free the old list and move over the new list */ free(targets->list); targets->list = newlist.list; targets->count = newlist.count; newlist.list = NULL; newlist.count = 0; /* Since chopping up large ranges can split ranges, this can * grow the list so we need to re-sort it */ rangelist_sort(targets); } /*************************************************************************** * Counts the total number of addresses in all the ranges combined. * For 0.0.0.0/0, this will be 0x100000000, which means we have to use a * larger number than 32-bit to return the result. This assumes that * all overlaps have been resolved in the list (i.e. it's been sorted). ***************************************************************************/ uint64_t rangelist_count(const struct RangeList *targets) { unsigned i; uint64_t result = 0; for (i=0; icount; i++) { result += (uint64_t)targets->list[i].end - (uint64_t)targets->list[i].begin + 1UL; } return result; } /*************************************************************************** * Get's the indexed port/address. * * Note that this requires a search of all the ranges. Currently, this is * done by a learn search of the ranges. This needs to change, because * once we start adding in a lot of "exclude ranges", the address space * will get fragmented, and the linear search will take too long. ***************************************************************************/ static unsigned rangelist_pick_linearsearch(const struct RangeList *targets, uint64_t index) { unsigned i; for (i=0; icount; i++) { uint64_t range = (uint64_t)targets->list[i].end - (uint64_t)targets->list[i].begin + 1UL; if (index < range) return (unsigned)(targets->list[i].begin + index); else index -= range; } assert(!"end of list"); return 0; } /*************************************************************************** ***************************************************************************/ unsigned rangelist_pick(const struct RangeList *targets, uint64_t index) { unsigned maxmax = targets->count; unsigned min = 0; unsigned max = targets->count; unsigned mid; const unsigned *picker = targets->picker; if (!targets->is_sorted) rangelist_sort((struct RangeList *)targets); assert(targets->is_sorted); if (picker == NULL) { /* optimization wasn't done */ return rangelist_pick_linearsearch(targets, index); } for (;;) { mid = min + (max-min)/2; if (index < picker[mid]) { max = mid; continue; } if (index >= picker[mid]) { if (mid + 1 == maxmax) break; else if (index < picker[mid+1]) break; else min = mid+1; } } return (unsigned)(targets->list[mid].begin + (index - picker[mid])); } /*************************************************************************** * The normal "pick" function is a linear search, which is slow when there * are a lot of ranges. Therefore, the "pick2" creates sort of binary * search that'll be a lot faster. We choose "binary search" because * it's the most cache-efficient, having the least overhead to fit within * the cache. ***************************************************************************/ void rangelist_optimize(struct RangeList *targets) { unsigned *picker; unsigned i; unsigned total = 0; if (targets->count == 0) return; /* This technique only works when the targets are in * ascending order */ if (!targets->is_sorted) rangelist_sort(targets); if (targets->picker) free(targets->picker); picker = REALLOCARRAY(NULL, targets->count, sizeof(*picker)); for (i=0; icount; i++) { picker[i] = total; total += targets->list[i].end - targets->list[i].begin + 1; } targets->picker = picker; } /*************************************************************************** * Provide my own rand() simply to avoid static-analysis warning me that * 'rand()' is unrandom, when in fact we want the non-random properties of * rand() for regression testing. ***************************************************************************/ static unsigned r_rand(unsigned *seed) { static const unsigned a = 214013; static const unsigned c = 2531011; *seed = (*seed) * a + c; return (*seed)>>16 & 0x7fff; } /*************************************************************************** ***************************************************************************/ static int regress_pick2() { unsigned i; unsigned seed = 0; /* * Run 100 randomized regression tests */ for (i=0; i<100; i++) { unsigned j; unsigned num_targets; unsigned begin = 0; unsigned end; struct RangeList targets[1] = {{0}}; struct RangeList duplicate[1] = {{0}}; unsigned range; /* Create a new target list */ memset(targets, 0, sizeof(targets[0])); /* fill the target list with random ranges */ num_targets = r_rand(&seed)%5 + 1; for (j=0; jcount == duplicate->count); REGRESS(memcmp(targets->list, duplicate->list, targets->count*sizeof(targets->list[0])) == 0); rangelist_remove_all(targets); rangelist_remove_all(duplicate); } return 0; } /*************************************************************************** * This returns a character pointer where parsing ends so that it can * handle multiple stuff on the same line ***************************************************************************/ const char * rangelist_parse_ports(struct RangeList *ports, const char *string, unsigned *is_error, unsigned proto_offset) { char *p = (char*)string; unsigned tmp = 0; if (is_error == NULL) is_error = &tmp; *is_error = 0; while (*p) { unsigned port; unsigned end; /* skip whitespace */ while (*p && isspace(*p & 0xFF)) p++; /* end at comment */ if (*p == 0 || *p == '#') break; /* special processing. Nmap allows ports to be prefixed with a * characters to clarify TCP, UDP, or SCTP */ if (isalpha(*p&0xFF) && p[1] == ':') { switch (*p) { case 'T': case 't': proto_offset = 0; break; case 'U': case 'u': proto_offset = Templ_UDP; break; case 'S': case 's': proto_offset = Templ_SCTP; break; case 'O': case 'o': proto_offset = Templ_Oproto_first; break; case 'I': case 'i': proto_offset = Templ_ICMP_echo; break; default: LOG(0, "bad port character = %c\n", p[0]); *is_error = 1; return p; } p += 2; } /* * Get the start of the range. */ if (p[0] == '-') { /* nmap style port range spec meaning starting with 0 */ port = 1; } else if (isdigit(p[0] & 0xFF)) { port = (unsigned)strtoul(p, &p, 0); } else { break; } /* * Get the end of the range */ if (*p == '-') { p++; if (!isdigit(*p)) { /* nmap style range spec meaning end with 65535 */ end = (proto_offset == Templ_Oproto_first) ? 0xFF : 0xFFFF; } else { end = (unsigned)strtoul(p, &p, 0); } } else end = port; /* Check for out-of-range */ if (port > 0xFF && proto_offset == Templ_Oproto_first) { *is_error = 2; return p; } else if (port > 0xFFFF || end > 0xFFFF || end < port) { *is_error = 2; return p; } /* Add to our list */ rangelist_add_range(ports, port+proto_offset, end+proto_offset); /* skip trailing whitespace */ while (*p && isspace(*p & 0xFF)) p++; /* Now get the next port/range if there is one */ if (*p != ',') break; p++; } return p; } /*************************************************************************** * Deterministic random number generator for repeatable tests. ***************************************************************************/ static unsigned lcgrand(unsigned *state) { *state = 1103515245 * (*state) + 12345; return *state; } /*************************************************************************** * Create an exact duplicate range. ***************************************************************************/ static void rangelist_copy(struct RangeList *dst, const struct RangeList *src) { free(dst->list); free(dst->picker); memset(dst, 0, sizeof(*dst)); dst->list = CALLOC(src->count, sizeof(src->list[0])); memcpy(dst->list, src->list, src->count * sizeof(src->list[0])); dst->count = src->count; dst->max = dst->count; dst->is_sorted = src->is_sorted; } /*************************************************************************** * Test if two ranges are exact duplicates * @return true if equal, false if not equal ***************************************************************************/ static bool rangelist_is_equal(const struct RangeList *lhs, const struct RangeList *rhs) { unsigned i; if (lhs->count != rhs->count) return false; for (i=0; icount; i++) { if (lhs->list[i].begin != rhs->list[i].begin) { return false; } if (lhs->list[i].end != rhs->list[i].end) { return false; } } return true; } /*************************************************************************** * The old way of excluding addresses assume unsorted lists, so had to * search the entire exclude list for each included address, which is * O(n * m), and fails when we have millions of excludes and includes, * because it takes forever to apply. * This was revamped with a new version that sorts both lists first, * the applies the excludes sequentially in an O(n + m) operation. * This selftest simply creates random lists and runs the new code * against the old code, and make sure the results match. ***************************************************************************/ static int exclude_selftest(void) { unsigned seed = 0; struct RangeList includes1 = {0}; struct RangeList includes2 = {0}; struct RangeList excludes = {0}; unsigned addr = 0; size_t i; /* In my initial tests, simply using 10 as the count seems to * catch all the combinations. On the other hand, 100,000 takes * a long time to complete, because it's O(n2) quadratic time. * Therefore, I pick a thousand as a compromise, likely to catch * any possibility, yet fast enough to complete quickly even on * a Raspberry Pi */ static const unsigned MAXCOUNT = 1000; /* Fill the include list. This is designed to make short ranges * that are a short distance apart. We'll do the same for the * same for the excludes, using a different random seed. This * should create two lists that have lots and lots of overlapping * and non-overlapping ranges. */ seed = 0; addr = 0; for (i=0; i r.end); r = range_parse_ipv4("75.748.86.91", 0, 0); REGRESS(r.begin > r.end); r = range_parse_ipv4("23.75.345.200", 0, 0); REGRESS(r.begin > r.end); r = range_parse_ipv4("192.1083.0.1", 0, 0); REGRESS(r.begin > r.end); r = range_parse_ipv4("192.168.1.3", 0, 0); if (r.begin != 0xc0a80103 || r.end != 0xc0a80103) { LOG(0, "r.begin = 0x%08x r.end = 0x%08x\n", r.begin, r.end); ERROR(); return 1; } r = range_parse_ipv4("10.0.0.20-10.0.0.30", 0, 0); if (r.begin != 0x0A000000+20 || r.end != 0x0A000000+30) { LOG(0, "r.begin = 0x%08x r.end = 0x%08x\n", r.begin, r.end); ERROR(); return 1; } r = range_parse_ipv4("10.0.1.2/16", 0, 0); if (r.begin != 0x0A000000 || r.end != 0x0A00FFFF) { LOG(0, "r.begin = 0x%08x r.end = 0x%08x\n", r.begin, r.end); ERROR(); return 1; } rangelist_add_range2(targets, range_parse_ipv4("10.0.0.0/24", 0, 0)); rangelist_add_range2(targets, range_parse_ipv4("10.0.1.10-10.0.1.19", 0, 0)); rangelist_add_range2(targets, range_parse_ipv4("10.0.1.20-10.0.1.30", 0, 0)); rangelist_add_range2(targets, range_parse_ipv4("10.0.0.0-10.0.1.12", 0, 0)); rangelist_sort(targets); if (targets->count != 1) { LOG(0, "count = %u\n", targets->count); ERROR(); return 1; } if (targets->list[0].begin != 0x0a000000 || targets->list[0].end != 0x0a000100+30) { LOG(0, "r.begin = 0x%08x r.end = 0x%08x\n", targets->list[0].begin, targets->list[0].end); ERROR(); return 1; } rangelist_remove_all(targets); /* * Test removal */ memset(targets, 0, sizeof(targets[0])); rangelist_add_range2(targets, range_parse_ipv4("10.0.0.0/8", 0, 0)); rangelist_sort(targets); /* These removals shouldn't change anything */ rangelist_remove_range2(targets, range_parse_ipv4("9.255.255.255", 0, 0)); rangelist_remove_range2(targets, range_parse_ipv4("11.0.0.0/16", 0, 0)); rangelist_remove_range2(targets, range_parse_ipv4("192.168.0.0/16", 0, 0)); rangelist_sort(targets); if (targets->count != 1 || targets->list->begin != 0x0a000000 || targets->list->end != 0x0aFFFFFF) { ERROR(); return 1; } /* These removals should remove a bit from the edges */ rangelist_remove_range2(targets, range_parse_ipv4("1.0.0.0-10.0.0.0", 0, 0)); rangelist_remove_range2(targets, range_parse_ipv4("10.255.255.255-11.0.0.0", 0, 0)); rangelist_sort(targets); if (targets->count != 1 || targets->list->begin != 0x0a000001 || targets->list->end != 0x0aFFFFFE) { ERROR(); return 1; } /* remove things from the middle */ rangelist_remove_range2(targets, range_parse_ipv4("10.10.0.0/16", 0, 0)); rangelist_remove_range2(targets, range_parse_ipv4("10.20.0.0/16", 0, 0)); rangelist_sort(targets); if (targets->count != 3) { ERROR(); return 1; } rangelist_remove_range2(targets, range_parse_ipv4("10.12.0.0/16", 0, 0)); rangelist_sort(targets); if (targets->count != 4) { ERROR(); return 1; } rangelist_remove_range2(targets, range_parse_ipv4("10.10.10.10-10.12.12.12", 0, 0)); rangelist_sort(targets); if (targets->count != 3) { ERROR(); return 1; } rangelist_remove_all(targets); /* test ports */ { unsigned is_error = 0; memset(targets, 0, sizeof(targets[0])); rangelist_parse_ports(targets, "80,1000-2000,1234,4444", &is_error, 0); rangelist_sort(targets); if (targets->count != 3 || is_error) { ERROR(); return 1; } if (targets->list[0].begin != 80 || targets->list[0].end != 80 || targets->list[1].begin != 1000 || targets->list[1].end != 2000 || targets->list[2].begin != 4444 || targets->list[2].end != 4444) { ERROR(); return 1; } } if (selftest_range_first_cidr() != 0) { ERROR(); return 1; } return 0; } ================================================ FILE: src/massip-rangesv4.h ================================================ #ifndef RANGES_H #define RANGES_H #include #include #include "util-bool.h" /**/ /** * A range of either IP addresses or ports */ struct Range { unsigned begin; unsigned end; /* inclusive, so [n..m] includes both 'n' and 'm' */ }; /** * Find the first CIDR range (one that can be specified with a /prefix) * inside the current range. If the current range can already be * specified with a CIDR /prefix, then the entire range is returned. * Examples: * [10.0.0.0->10.0.0.255] returns [10.0.0.0->10.0.0.255] (no change) * [10.0.0.1->10.0.0.255] returns [10.0.0.1->10.0.0.1] * [10.0.0.2->10.0.0.255] returns [10.0.0.2->10.0.0.3] * [10.0.0.4->10.0.0.255] returns [10.0.0.4->10.0.0.7] * [10.0.0.248->10.0.0.254] returns [10.0.0.248->10.0.0.251] * [10.0.0.252->10.0.0.254] returns [10.0.0.252->10.0.0.253] * [10.0.0.254->10.0.0.254] returns [10.0.0.254->10.0.0.254] * @param range * A range specified by a starting IPv4 address and an ending * IPv4 address, like [10.0.0.4->10.0.0.255]. * @param prefix_length * An out-only parameter that receives the CIDR prefix length * (number of bits) of the resulting range. This parameter is * optional (may be NULL). * @return the smaller range, and the number of prefix bits in the range. */ struct Range range_first_cidr(const struct Range range, unsigned *prefix_length /*out*/); /** * Test if the range can instead be expressed using a CIDR /prefix. * In other words, [10.0.0.0-10.0.0.255] can be expressed as [10.0.0.0/24]. * @param range to be tested * @param prefix_length receivesoutput of the number of prefix bits if * successful, otherwise set to 0xFFFFFFFF. This is optional, may * be NULL and receive no output. * @return True if the range can be expressed in CIDR notation, in * which case `prefix_length` is set to the number of bits in the prefix * for printing in that notation. False otherwise, in which case * `prefix_length` is set to 0xFFFFFFFF (an invalid value). */ bool range_is_cidr(const struct Range range, unsigned *prefix_length /*out*/); /** * An array of ranges in sorted order */ struct RangeList { struct Range *list; unsigned count; unsigned max; unsigned *picker; unsigned is_sorted:1; }; /** * Adds the given range to the task list. The given range can be a duplicate * or overlap with an existing range, which will get combined with existing * ranges. * @param task * A list of ranges of either IPv4 addresses or port numbers. * @param begin * The first address of the range that'll be added. * @param end * The last address (inclusive) of the range that'll be added. */ void rangelist_add_range(struct RangeList *task, unsigned begin, unsigned end); void rangelist_add_range_tcp(struct RangeList *targets, unsigned begin, unsigned end); void rangelist_add_range_udp(struct RangeList *targets, unsigned begin, unsigned end); /** * Returns 'true' is the indicated port or IP address is in one of the task * ranges. * @param task * A list of ranges of either IPv4 addresses or port numbers. * @param number * Either an IPv4 address or a TCP/UDP port number. * @return * 'true' if the ranges contain the item, or 'false' otherwise */ int rangelist_is_contains(const struct RangeList *task, unsigned number); /** * Returns 'true' if the indicate range is valid, which is simple the * fact that 'begin' comes before 'end'. We mark invalid ranges * by putting 'begin' after the 'end' */ int range_is_valid(struct Range range); /** * Parses IPv4 addresses out of a string. A number of formats are allowed, * either an individual IPv4 address, a CIDR spec, or a start/stop address. * @param line * A line of text, probably read from a configuration file, or a string * probably input from the command line. It doesn't need to be nul * terminated. * @param inout_offset * The offset into the line were we are parsing. This integer will be * be incremented by the number of bytes we've parsed from the string. * @param max * The length of the line, in other words, the max value of inout_offset. */ struct Range range_parse_ipv4(const char *line, unsigned *inout_offset, unsigned max); /** * Remove things from the target list. The primary use of this is the * "exclude-file" containing a list of IP addresses that we should * not scan * @param targets * Our array of target IP address (or port) ranges that we'll be * scanning. * @param excludes * A list, probably read in from --excludefile, of things that we * should not be scanning, that will override anything we otherwise * try to scan. */ void rangelist_exclude( struct RangeList *targets, struct RangeList *excludes); /** * Counts the total number of IP addresses or ports in the target list. This * iterates over all the ranges in the table, summing up the count within * each range. * @param targets * A list of IP address or port ranges. * @return * The total number of address or ports. */ uint64_t rangelist_count(const struct RangeList *targets); /** * Given an index in a continuous range of [0...count], pick a corresponding * number (IP address or port) from a list of non-continuous ranges (not * necessarily starting from 0). In other words, given the two ranges * 10-19 50-69 * we'll have a total of 30 possible numbers. Thus, the index goes from * [0..29], with the values 0..9 picking the corresponding values from the * first range, and the values 10..29 picking the corresponding values * from the second range. * * NOTE: This is a fundamental part of this program's design, that the user * can specify non-contiguous IP and port ranges, but yet we iterate over * them using a monotonically increasing index variable. * * @param targets * A list of IP address ranges, or a list of port ranges (one or the * other, but not both). * @param index * An integer starting at 0 up to (but not including) the value returned * by 'rangelist_count()' for this target list. * @return * an IP address or port corresponding to this index. */ unsigned rangelist_pick(const struct RangeList *targets, uint64_t i); /** * Given a string like "80,8080,20-25,U:161", parse it into a structure * containing a list of port ranges. * * @param ports * The array of port ranges that's produced by this parsing function. * This structure will be used by the transmit thread when sending * probes to a target IP address. * @param string * A string from either the command-line or configuration file * in the nmap "ports" format. * @param is_error * Set to zero is no error occurred while parsing the string, or * set to a non-zero value if an error was found. * @return * the pointer in the string where the parsing ended, so that additional * things can be contained in the string, such as comments */ const char * rangelist_parse_ports( struct RangeList *ports, const char *string, unsigned *is_error, unsigned proto_offset ); /** * Remove all the ranges in the range list. */ void rangelist_remove_all(struct RangeList *list); /** * Merge two range lists */ void rangelist_merge(struct RangeList *list1, const struct RangeList *list2); /** * Optimizes the target list, so that when we call "rangelist_pick()" * from an index, it runs faster. It currently configures this for * a binary-search, though in the future some more efficient * algorithm may be chosen. */ void rangelist_optimize(struct RangeList *targets); /** * Sorts the list of target. We maintain the list of targets in sorted * order internally even though we scan the targets in random order * externally. */ void rangelist_sort(struct RangeList *targets); /** * Does a regression test of this module * @return * 0 if the regression test succeeds, or a positive value on failure */ int ranges_selftest(void); #endif ================================================ FILE: src/massip-rangesv6.c ================================================ /* for tracking IP/port ranges */ #include "massip-rangesv6.h" #include "massip-rangesv4.h" #include "util-malloc.h" #include "util-logger.h" #include "massip.h" #include "massip-parse.h" #include #include #include #include #include #include #define BUCKET_COUNT 16 #define REGRESS(i,x) if (!(x)) return (fprintf(stderr, "[-] %u: regression failed %s:%d\n", (unsigned)i, __FILE__, __LINE__)|1) #ifndef false #define false 0 #endif #ifndef true #define true 1 #endif #define EQUAL(x,y) ipv6address_is_equal(x,y) static inline ipv6address _int128_add(ipv6address x, ipv6address y) { ipv6address result; result.lo = x.lo + y.lo; result.hi = x.hi + y.hi + (result.lo < x.lo); return result; } static inline ipv6address _int128_subtract(ipv6address x, ipv6address y) { ipv6address result; result.lo = x.lo - y.lo; result.hi = x.hi - y.hi - (result.lo > x.lo); return result; } static ipv6address _int128_add64(const ipv6address lhs, uint64_t rhs) { ipv6address result = lhs; result.lo += rhs; if (result.lo < lhs.lo) result.hi++; return result; } static inline massint128_t _int128_mult64(massint128_t lhs, uint64_t rhs) { massint128_t result = {0,0}; uint64_t x; uint64_t b; uint64_t a; /* low-order 32 */ a = (rhs>>0) & 0xFFFFFFFFULL; b = (lhs.lo>>0) & 0xFFFFFFFFULL; x = (a * b); result.lo += x; b = (lhs.lo>>32ULL) & 0xFFFFFFFFULL; x = (a * b); result.lo += x<<32ULL; result.hi += x>>32ULL; b = lhs.hi; x = (a * b); result.hi += x; /* next 32 */ a = (rhs>>32ULL) & 0xFFFFFFFFULL; b = (lhs.lo>>0ULL) & 0xFFFFFFFFULL; x = (a * b); result.lo += x<<32ULL; result.hi += (x>>32ULL) + (result.lo < (x<<32ULL)); b = (lhs.lo>>32ULL) & 0xFFFFFFFFULL; x = (a * b); result.hi += x; b = lhs.hi; x = (a * b); result.hi += x<<32ULL; return result; } static int LESS(const ipv6address lhs, const ipv6address rhs) { if (lhs.hi < rhs.hi) return 1; else if (lhs.hi == rhs.hi && lhs.lo < rhs.lo) return 1; else return 0; } #define GREATEREQ(x,y) (!LESS(x,y)) static int LESSEQ(const ipv6address lhs, const ipv6address rhs) { if (lhs.hi < rhs.hi) return 1; if (lhs.hi > rhs.hi) return 0; if (lhs.lo <= rhs.lo) return 1; else return 0; } int range6_is_bad_address(const struct Range6 *range) { return LESS(range->end, range->begin); } static int _int128_is_equals(const ipv6address lhs, const ipv6address rhs) { return lhs.hi == rhs.hi && lhs.lo == rhs.lo; } static ipv6address MINUS_ONE(const ipv6address ip) { ipv6address result; if (ip.lo == 0) { result.hi = ip.hi - 1; result.lo = ~0ULL; } else { result.hi = ip.hi; result.lo = ip.lo - 1; } return result; } static ipv6address PLUS_ONE(const ipv6address ip) { ipv6address result; if (ip.lo == ~0) { result.hi = ip.hi + 1; result.lo = 0; } else { result.hi = ip.hi; result.lo = ip.lo + 1; } return result; } /*************************************************************************** ***************************************************************************/ massint128_t massip_range(struct MassIP *massip) { massint128_t result; result = range6list_count(&massip->ipv6); result = _int128_add64(result, rangelist_count(&massip->ipv4)); result = _int128_mult64(result, rangelist_count(&massip->ports)); return result; } /*************************************************************************** ***************************************************************************/ int range6list_is_contains(const struct Range6List *targets, const ipv6address ip) { unsigned i; for (i=0; icount; i++) { struct Range6 *range = &targets->list[i]; if (LESSEQ(range->begin, ip) && LESSEQ(ip, range->end)) return 1; } return 0; } /*************************************************************************** * ??? ***************************************************************************/ static void todo_remove_at(struct Range6List *targets, unsigned index) { memmove(&targets->list[index], &targets->list[index+1], (targets->count - index) * sizeof(targets->list[index]) ); targets->count--; } /*************************************************************************** * Test if two ranges overlap. * This is easiest done by testing that they don't overlap, and inverting * the result. * Note that adjacent addresses overlap. ***************************************************************************/ static int range6_is_overlap(const struct Range6 lhs, const struct Range6 rhs) { static const ipv6address FFFF = {~0ULL, ~0ULL}; if (LESS(lhs.begin, rhs.begin)) { if (EQUAL(lhs.end, FFFF) || GREATEREQ(PLUS_ONE(lhs.end), rhs.begin)) return 1; } if (GREATEREQ(lhs.begin, rhs.begin)) { if (LESSEQ(lhs.end, rhs.end)) return 1; } if (LESS(rhs.begin, lhs.begin)) { if (EQUAL(rhs.end, FFFF) || GREATEREQ(PLUS_ONE(rhs.end), lhs.begin)) return 1; } if (GREATEREQ(rhs.begin, lhs.begin)) { if (LESSEQ(rhs.end, lhs.end)) return 1; } return 0; #if 0 static const ipv6address zero = {0, 0}; ipv6address lhs_endm = MINUS_ONE(lhs.end); ipv6address rhs_endm = MINUS_ONE(rhs.end); /* llll rrrr */ if (LESS(zero, lhs.end) && LESS(lhs_endm, rhs.begin)) return 0; /* rrrr llll */ if (LESS(zero, rhs.end) && LESS(rhs_endm, lhs.begin)) return 0; return 1; #endif } /*************************************************************************** * Combine two ranges, such as when they overlap. ***************************************************************************/ static void range6_combine(struct Range6 *lhs, const struct Range6 rhs) { if (LESSEQ(rhs.begin, lhs->begin)) lhs->begin = rhs.begin; if (LESSEQ(lhs->end, rhs.end)) lhs->end = rhs.end; } /*************************************************************************** * Callback for qsort() for comparing two ranges ***************************************************************************/ static int range6_compare(const void *lhs, const void *rhs) { struct Range6 *left = (struct Range6 *)lhs; struct Range6 *right = (struct Range6 *)rhs; if (ipv6address_is_equal(left->begin, right->begin)) return 0; else if (LESS(left->begin, right->begin)) return -1; else return 1; } /*************************************************************************** ***************************************************************************/ void range6list_sort(struct Range6List *targets) { size_t i; struct Range6List newlist = {0}; size_t original_count = targets->count; /* Empty lists are, of course, sorted. We need to set this * to avoid an error later on in the code which asserts that * the lists are sorted */ if (targets->count == 0) { targets->is_sorted = 1; return; } /* If it's already sorted, then skip this */ if (targets->is_sorted) { return; } /* First, sort the list */ LOG(3, "[+] range6:sort: sorting...\n"); qsort( targets->list, /* the array to sort */ targets->count, /* number of elements to sort */ sizeof(targets->list[0]), /* size of element */ range6_compare); /* Second, combine all overlapping ranges. We do this by simply creating * a new list from a sorted list, so we don't have to remove things in the * middle when collapsing overlapping entries together, which is painfully * slow. */ LOG(3, "[+] range:sort: combining...\n"); for (i=0; icount; i++) { range6list_add_range(&newlist, targets->list[i].begin, targets->list[i].end); } LOG(3, "[+] range:sort: combined from %u elements to %u elements\n", original_count, newlist.count); free(targets->list); targets->list = newlist.list; targets->count = newlist.count; newlist.list = 0; LOG(2, "[+] range:sort: done...\n"); targets->is_sorted = 1; } void range6list_add_range(struct Range6List *targets, ipv6address begin, ipv6address end) { struct Range6 range; range.begin = begin; range.end = end; /* auto-expand the list if necessary */ if (targets->count + 1 >= targets->max) { targets->max = targets->max * 2 + 1; targets->list = REALLOCARRAY(targets->list, targets->max, sizeof(targets->list[0])); } /* If empty list, then add this one */ if (targets->count == 0) { targets->list[0] = range; targets->count++; targets->is_sorted = 1; return; } /* If new range overlaps the last range in the list, then combine it * rather than appending it. This is an optimization for the fact that * we often read in sequential addresses */ if (range6_is_overlap(targets->list[targets->count - 1], range)) { range6_combine(&targets->list[targets->count - 1], range); targets->is_sorted = 0; return; } /* append to the end of our list */ targets->list[targets->count] = range; targets->count++; targets->is_sorted = 0; } /*************************************************************************** ***************************************************************************/ void range6list_remove_all(struct Range6List *targets) { if (targets->list) free(targets->list); if (targets->picker) free(targets->picker); memset(targets, 0, sizeof(*targets)); } /*************************************************************************** ***************************************************************************/ void range6list_merge(struct Range6List *list1, const struct Range6List *list2) { unsigned i; for (i=0; icount; i++) { range6list_add_range(list1, list2->list[i].begin, list2->list[i].end); } } /*************************************************************************** ***************************************************************************/ void range6list_remove_range(struct Range6List *targets, const ipv6address begin, const ipv6address end) { unsigned i; struct Range6 x; x.begin = begin; x.end = end; /* See if the range overlaps any exist range already in the * list */ for (i = 0; i < targets->count; i++) { if (!range6_is_overlap(targets->list[i], x)) continue; /* If the removal-range wholly covers the range, delete * it completely */ if (LESSEQ(begin, targets->list[i].begin) && LESSEQ(targets->list[i].end, end)) { todo_remove_at(targets, i); i--; continue; } /* If the removal-range bisects the target-rage, truncate * the lower end and add a new high-end */ if (LESSEQ(targets->list[i].begin, begin) && LESSEQ(end, targets->list[i].end)) { struct Range6 newrange; newrange.begin = PLUS_ONE(end); newrange.end = targets->list[i].end; targets->list[i].end = MINUS_ONE(begin); range6list_add_range(targets, newrange.begin, newrange.end); i--; continue; } /* If overlap on the lower side */ if (LESSEQ(targets->list[i].begin, end) && LESSEQ(end, targets->list[i].end)) { targets->list[i].begin = PLUS_ONE(end); } /* If overlap on the upper side */ if (LESSEQ(targets->list[i].begin, begin) && LESSEQ(begin, targets->list[i].end)) { targets->list[i].end = MINUS_ONE(begin); } } } void range6list_remove_range2(struct Range6List *targets, struct Range6 range) { range6list_remove_range(targets, range.begin, range.end); } /*************************************************************************** ***************************************************************************/ ipv6address range6list_exclude( struct Range6List *targets, const struct Range6List *excludes) { ipv6address count = {0,0}; unsigned i; for (i=0; icount; i++) { struct Range6 range = excludes->list[i]; ipv6address x; x = _int128_subtract(range.end, range.begin); x = _int128_add64(x, 1); count = _int128_add(count, x); range6list_remove_range(targets, range.begin, range.end); } return count; } /*************************************************************************** ***************************************************************************/ massint128_t range6list_count(const struct Range6List *targets) { unsigned i; ipv6address result = {0,0}; for (i=0; icount; i++) { ipv6address x; x = _int128_subtract(targets->list[i].end, targets->list[i].begin); if (x.hi == ~0ULL && x.lo == ~0ULL) return x; /* overflow */ x = _int128_add64(x, 1); result = _int128_add(result, x); } return result; } /*************************************************************************** ***************************************************************************/ ipv6address range6list_pick(const struct Range6List *targets, uint64_t index) { size_t maxmax = targets->count; size_t min = 0; size_t max = targets->count; size_t mid; const size_t *picker = targets->picker; if (picker == NULL) { fprintf(stderr, "[-] ipv6 picker is null\n"); exit(1); } for (;;) { mid = min + (max-min)/2; if (index < picker[mid]) { max = mid; continue; } if (index >= picker[mid]) { if (mid + 1 == maxmax) break; else if (index < picker[mid+1]) break; else min = mid+1; } } return _int128_add64(targets->list[mid].begin, (index - picker[mid])); } /*************************************************************************** * The normal "pick" function is a linear search, which is slow when there * are a lot of ranges. Therefore, the "pick2" creates sort of binary * search that'll be a lot faster. We choose "binary search" because * it's the most cache-efficient, having the least overhead to fit within * the cache. ***************************************************************************/ void range6list_optimize(struct Range6List *targets) { size_t *picker; size_t i; ipv6address total = {0,0}; if (targets->count == 0) return; /* This technique only works when the targets are in * ascending order */ if (!targets->is_sorted) range6list_sort(targets); if (targets->picker) free(targets->picker); picker = REALLOCARRAY(NULL, targets->count, sizeof(*picker)); for (i=0; icount; i++) { ipv6address x; picker[i] = (size_t)total.lo; x = _int128_subtract(targets->list[i].end, targets->list[i].begin); x = _int128_add64(x, 1); total = _int128_add(total, x); } targets->picker = picker; } /*************************************************************************** * Provide my own rand() simply to avoid static-analysis warning me that * 'rand()' is unrandom, when in fact we want the non-random properties of * rand() for regression testing. ***************************************************************************/ static unsigned r_rand(unsigned *seed) { static const unsigned a = 214013; static const unsigned c = 2531011; *seed = (*seed) * a + c; return (*seed)>>16 & 0x7fff; } /*************************************************************************** ***************************************************************************/ static int regress_pick2() { unsigned i; unsigned seed = 0; /* */ for (i=0; i<65536; i++) { ipv6address a; ipv6address b; ipv6address c; ipv6address d; a.hi = r_rand(&seed); a.lo = (unsigned long long)r_rand(&seed)<<49ULL; b.hi = r_rand(&seed); b.lo = 0x8765432100000000ULL; c = _int128_add(a, b); d = _int128_subtract(c, b); if (!_int128_is_equals(a, d)) { fprintf(stderr, "[-] %s:%d: test failed (%u)\n", __FILE__, __LINE__, (unsigned)i); return 1; } } /* * Run 100 randomized regression tests */ for (i=3; i<100; i++) { unsigned j; unsigned num_targets; ipv6address begin = {0}; ipv6address end = {0}; struct Range6List targets[1]; struct Range6List duplicate[1]; uint64_t range; ipv6address x; seed = i; /* Create a new target list */ memset(targets, 0, sizeof(targets[0])); /* fill the target list with random ranges */ num_targets = r_rand(&seed)%5 + 1; for (j=0; jcount == duplicate->count); REGRESS(i, memcmp(targets->list, duplicate->list, targets->count*sizeof(targets->list[0])) == 0); range6list_remove_all(targets); range6list_remove_all(duplicate); } return 0; } /*************************************************************************** * Called during "make regress" to run a regression test over this module. ***************************************************************************/ int ranges6_selftest(void) { struct Range6 r; struct Range6List targets[1]; int err; REGRESS(0, regress_pick2() == 0); memset(targets, 0, sizeof(targets[0])); #define ERROR() fprintf(stderr, "selftest: failed %s:%u\n", __FILE__, __LINE__); err = massip_parse_range("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 0, 0, 0, &r); if (err != Ipv6_Address) ERROR(); /* test for the /0 CIDR block, since we'll be using that a lot to scan the entire * Internet */ if (r.begin.hi != 0x20010db885a30000ULL) return 1; if (r.begin.lo != 0x00008a2e03707334ULL) return 1; return 0; } ================================================ FILE: src/massip-rangesv6.h ================================================ /* List of IPv6 ranges. Sames as the "ranges.h" module, but for IPv6 instead of IPv4. */ #ifndef RANGES6_H #define RANGES6_H #include "massip-addr.h" #include #include struct Range; /** * A range of IPv6 ranges. * Inclusive, so [n..m] includes both 'n' and 'm'. */ struct Range6 { ipv6address begin; ipv6address end; }; /** * An array of ranges in sorted order */ struct Range6List { struct Range6 *list; size_t count; size_t max; size_t *picker; unsigned is_sorted:1; }; /** * Adds the given range to the targets list. The given range can be a duplicate * or overlap with an existing range, which will get combined with existing * ranges. * @param targets * A list of IPv6 ranges. * @param begin * The first address of the range that'll be added. * @param end * The last address (inclusive) of the range that'll be added. */ void range6list_add_range(struct Range6List *targets, ipv6address begin, ipv6address end); /** * Removes the given range from the target list. The input range doesn't * have to exist, or can partial overlap with existing ranges. * @param targets * A list of IPv6 ranges. * @param begin * The first address of the range that'll be removed. * @param end * The last address of the range that'll be removed (inclusive). */ void range6list_remove_range(struct Range6List *targets, const ipv6address begin, const ipv6address end); /** * Same as 'rangelist_remove_range()', except the input is a range * structure instead of a start/stop numbers. */ void range6list_remove_range2(struct Range6List *targets, struct Range6 range); /** * Returns 'true' if the indicated IPv6 address is in one of the target * ranges. * @param targets * A list of IPv6 ranges * @param ip * An IPv6 address that might in be in the list of ranges * @return * 'true' if the ranges contain the item, or 'false' otherwise */ int range6list_is_contains(const struct Range6List *targets, const ipv6address ip); /** * Tests if the range is bad/invalid. * @return 1 is invalid, 0 if good. */ int range6_is_bad_address(const struct Range6 *range); /** * Remove things from the target list. The primary use of this is the * "exclude-file" containing a list of IP addresses that we should * not scan * @param targets * Our array of target IP address (or port) ranges that we'll be * scanning. * @param excludes * A list, probably read in from --excludefile, of things that we * should not be scanning, that will override anything we otherwise * try to scan. * @return * the total number of IP addresses or ports removed. */ ipv6address range6list_exclude( struct Range6List *targets, const struct Range6List *excludes); /** * Counts the total number of IPv6 addresses in the target list. This * iterates over all the ranges in the table, summing up the count within * each range. * @param targets * A list of IP address or port ranges. * @return * The total number of address or ports. */ massint128_t range6list_count(const struct Range6List *targets); /** * Given an index in a continuous range of [0...count], pick a corresponding * number (IP address or port) from a list of non-continuous ranges (not * necessarily starting from 0). In other words, given the two ranges * 10-19 50-69 * we'll have a total of 30 possible numbers. Thus, the index goes from * [0..29], with the values 0..9 picking the corresponding values from the * first range, and the values 10..29 picking the corresponding values * from the second range. * * NOTE: This is a fundamental part of this program's design, that the user * can specify non-contiguous IP and port ranges, but yet we iterate over * them using a monotonically increasing index variable. * * @param targets * A list of IP address ranges, or a list of port ranges (one or the * other, but not both). * @param index * An integer starting at 0 up to (but not including) the value returned * by 'rangelist_count()' for this target list. * @return * an IP address or port corresponding to this index. */ ipv6address range6list_pick(const struct Range6List *targets, uint64_t index); /** * Remove all the ranges in the range list. */ void range6list_remove_all(struct Range6List *list); /** * Merge two range lists */ void range6list_merge(struct Range6List *list1, const struct Range6List *list2); /** * Optimizes the target list, so that when we call "rangelist_pick()" * from an index, it runs faster. It currently configures this for * a binary-search, though in the future some more efficient * algorithm may be chosen. */ void range6list_optimize(struct Range6List *targets); /** * Sorts the list of target. We maintain the list of targets in sorted * order internally even though we scan the targets in random order * externally. */ void range6list_sort(struct Range6List *targets); /** * Does a regression test of this module * @return * 0 if the regression test succeeds, or a positive value on failure */ int ranges6_selftest(void); #endif ================================================ FILE: src/massip.c ================================================ #include "massip.h" #include "massip-parse.h" #include "massip-rangesv4.h" #include "massip-rangesv6.h" #include #include void massip_apply_excludes(struct MassIP *targets, struct MassIP *exclude) { rangelist_exclude(&targets->ipv4, &exclude->ipv4); range6list_exclude(&targets->ipv6, &exclude->ipv6); rangelist_exclude(&targets->ports, &exclude->ports); } void massip_optimize(struct MassIP *targets) { rangelist_optimize(&targets->ipv4); range6list_optimize(&targets->ipv6); rangelist_optimize(&targets->ports); targets->count_ports = rangelist_count(&targets->ports); targets->count_ipv4s = rangelist_count(&targets->ipv4); targets->count_ipv6s = range6list_count(&targets->ipv6).lo; targets->ipv4_index_threshold = targets->count_ipv4s * rangelist_count(&targets->ports); } int massip_pick(const struct MassIP *massip, uint64_t index, ipaddress *addr, unsigned *port) { /* * We can return either IPv4 or IPv6 addresses */ if (index < massip->ipv4_index_threshold) { addr->version = 4; addr->ipv4 = rangelist_pick(&massip->ipv4, index % massip->count_ipv4s); *port = rangelist_pick(&massip->ports, index / massip->count_ipv4s); } else { addr->version = 6; index -= massip->ipv4_index_threshold; addr->ipv6 = range6list_pick(&massip->ipv6, index % massip->count_ipv6s); *port = rangelist_pick(&massip->ports, index / massip->count_ipv6s); } return 0; } int massip_has_ip(const struct MassIP *massip, ipaddress ip) { if (ip.version == 6) return range6list_is_contains(&massip->ipv6, ip.ipv6); else return rangelist_is_contains(&massip->ipv4, ip.ipv4); } int massip_has_port(const struct MassIP *massip, unsigned port) { return rangelist_is_contains(&massip->ports, port); } int massip_has_ipv4_targets(const struct MassIP *massip) { return massip->ipv4.count != 0; } int massip_has_target_ports(const struct MassIP *massip) { return massip->ports.count != 0; } int massip_has_ipv6_targets(const struct MassIP *massip) { return massip->ipv6.count != 0; } int massip_add_target_string(struct MassIP *massip, const char *string) { const char *ranges = string; size_t offset = 0; size_t max_offset = strlen(ranges); while (offset < max_offset) { struct Range range; struct Range6 range6; int err; /* Grab the next IPv4 or IPv6 range */ err = massip_parse_range(ranges, &offset, max_offset, &range, &range6); switch (err) { case Ipv4_Address: rangelist_add_range(&massip->ipv4, range.begin, range.end); break; case Ipv6_Address: range6list_add_range(&massip->ipv6, range6.begin, range6.end); break; default: offset = max_offset; /* An error means skipping the rest of the string */ return 1; } while (offset < max_offset && (isspace(ranges[offset]&0xFF) || ranges[offset] == ',')) offset++; } return 0; } int massip_add_port_string(struct MassIP *targets, const char *string, unsigned defaultrange) { unsigned is_error = 0; rangelist_parse_ports(&targets->ports, string, &is_error, defaultrange); if (is_error) return 1; else return 0; } int massip_selftest(void) { struct MassIP targets; struct MassIP excludes; int err; int line; massint128_t count; memset(&targets, 0, sizeof(targets)); memset(&excludes, 0, sizeof(targets)); rangelist_parse_ports(&targets.ports, "80", 0, 0); /* First, create a list of targets */ line = __LINE__; err = massip_add_target_string(&targets, "2607:f8b0:4002:801::2004/124,1111::1"); if (err) goto fail; /* Second, create an exclude list */ line = __LINE__; err = massip_add_target_string(&excludes, "2607:f8b0:4002:801::2004/126,1111::/16"); if (err) goto fail; /* Third, apply the excludes, causing ranges to be removed * from the target list */ massip_apply_excludes(&targets, &excludes); /* Now make sure the count equals the expected count */ line = __LINE__; count = massip_range(&targets); if (count.hi != 0 || count.lo != 12) goto fail; return 0; fail: fprintf(stderr, "[-] massip: test fail, line=%d\n", line); return 1; } ================================================ FILE: src/massip.h ================================================ #ifndef MASSIP_H #define MASSIP_H #include #include "massip-rangesv4.h" #include "massip-rangesv6.h" struct MassIP { struct RangeList ipv4; struct Range6List ipv6; /** * The ports we are scanning for. The user can specify repeated ports * and overlapping ranges, but we'll deduplicate them, scanning ports * only once. * NOTE: TCP ports are stored 0-64k, but UDP ports are stored in the * range 64k-128k, thus, allowing us to scan both at the same time. */ struct RangeList ports; /** * Used internally to differentiate between indexes selecting an * IPv4 address and higher ones selecting an IPv6 address. */ uint64_t ipv4_index_threshold; uint64_t count_ports; uint64_t count_ipv4s; uint64_t count_ipv6s; }; /** * Count the total number of targets in a scan. This is calculated * the (IPv6 addresses * IPv4 addresses * ports). This can produce * a 128-bit number (larger, actually). */ massint128_t massip_range(struct MassIP *massip); /** * Remove everything in "targets" that's listed in the "exclude" * list. The reason for this is that we'll have a single policy * file of those address ranges which we are forbidden to scan. * Then, each time we run a scan with different targets, we * apply this policy file. */ void massip_apply_excludes(struct MassIP *targets, struct MassIP *exclude); /** * The last step after processing the configuration, setting up the * state to be used for scanning. This sorts the address, removes * duplicates, and creates an optimized 'picker' system to easily * find an address given an index, or find an index given an address. */ void massip_optimize(struct MassIP *targets); /** * This selects an IP+port combination given an index whose value * is [0..range], where 'range' is the value returned by the function * `massip_range()`. Since the optimization step (`massip_optimized()`) * sorted all addresses/ports, a monotonically increasing index will * list everything in sorted order. The intent, however, is to use the * "blackrock" algorithm to randomize the index before calling this function. * * It is this function, plus the 'blackrock' randomization algorithm, that * is at the heart of Masscan. */ int massip_pick(const struct MassIP *massip, uint64_t index, ipaddress *addr, unsigned *port); int massip_has_ip(const struct MassIP *massip, ipaddress ip); int massip_has_port(const struct MassIP *massip, unsigned port); int massip_add_target_string(struct MassIP *massip, const char *string); /** * Parse the string contain port specifier. */ int massip_add_port_string(struct MassIP *massip, const char *string, unsigned proto); /** * Indicates whether there are IPv4 targets. If so, we'll have to * initialize the IPv4 portion of the stack. * @return true if there are IPv4 targets to be scanned, false * otherwise */ int massip_has_ipv4_targets(const struct MassIP *massip); int massip_has_target_ports(const struct MassIP *massip); /** * Indicates whether there are IPv6 targets. If so, we'll have to * initialize the IPv6 portion of the stack. * @return true if there are IPv6 targets to be scanned, false * otherwise */ int massip_has_ipv6_targets(const struct MassIP *massip); int massip_selftest(void); #endif ================================================ FILE: src/misc-rstfilter.c ================================================ #include "misc-rstfilter.h" #include "util-malloc.h" #include "crypto-siphash24.h" #include struct ResetFilter { unsigned long long seed; size_t bucket_count; size_t bucket_mask; unsigned counter; unsigned char *buckets; }; static size_t next_pow2(size_t n) { size_t bit_count = 0; /* Always have at least one bit */ if (n == 0) return 1; /* If already a power-of-two, then return that */ if ((n & (n - 1)) == 0) return n; /* Count the number of bits */ while (n != 0) { n >>= 1; bit_count += 1; } return (size_t)1 << (size_t)bit_count; } struct ResetFilter * rstfilter_create(unsigned long long seed, size_t bucket_count) { struct ResetFilter *rf; rf = CALLOC(1, sizeof(*rf)); rf->seed = seed; rf->bucket_count = next_pow2(bucket_count); rf->bucket_mask = rf->bucket_count - 1; rf->buckets = CALLOC(rf->bucket_count/2, sizeof(*rf->buckets)); return rf; } void rstfilter_destroy(struct ResetFilter *rf) { if (rf == NULL) return; free(rf->buckets); free(rf); } int rstfilter_is_filter(struct ResetFilter *rf, ipaddress src_ip, unsigned src_port, ipaddress dst_ip, unsigned dst_port) { uint64_t hash; uint64_t input[5]; uint64_t key[2]; size_t index; unsigned char *p; int result = 0; /* * Setup the input */ switch (src_ip.version) { case 4: input[0] = src_ip.ipv4; input[1] = src_port; input[2] = dst_ip.ipv4; input[3] = dst_port; break; case 6: input[0] = src_ip.ipv6.hi; input[1] = src_ip.ipv6.lo; input[2] = dst_ip.ipv6.hi; input[3] = dst_ip.ipv6.lo; input[4] = src_port<<16 | dst_port; break; } key[0] = rf->seed; key[1] = rf->seed; /* * Grab the bucket */ hash = siphash24(input, sizeof(input), key); index = hash & rf->bucket_mask; /* * Find the result (1=filterout, 0=sendrst) */ p = &rf->buckets[index/2]; if (index & 1) { if ((*p & 0x0F) == 0x0F) result = 1; /* filter out */ else *p = (*p) + 0x01; } else { if ((*p & 0xF0) == 0xF0) result = 1; /* filter out */ else *p = (*p) + 0x10; } /* * Empty a random bucket */ input[0] = (unsigned)hash; input[1] = rf->counter++; hash = siphash24(input, sizeof(input), key); index = hash & rf->bucket_mask; p = &rf->buckets[index/2]; if (index & 1) { if ((*p & 0x0F)) *p = (*p) - 0x01; } else { if ((*p & 0xF0)) *p = (*p) - 0x10; } return result; } int rstfilter_selftest(void) { struct ResetFilter *rf; size_t i; unsigned count_filtered = 0; unsigned count_passed = 0; ipaddress src; ipaddress dst; src.version = 4; src.ipv4 = 1; dst.version = 4; dst.ipv4 = 3; rf = rstfilter_create(time(0), 64); /* Verify the first 15 packets pass the filter */ for (i=0; i<15; i++) { int x; x = rstfilter_is_filter(rf, src, 2, dst, 4); if (x) { fprintf(stderr, "[-] rstfilter failed, line=%u\n", __LINE__); return 1; } } /* Now run 10000 more times */ for (i=0; i<1000; i++) { int x; x = rstfilter_is_filter(rf, src, 2, dst, 4); count_filtered += x; count_passed += !x; } /* SOME must have passed, due to us emptying random buckets */ if (count_passed == 0) { fprintf(stderr, "[-] rstfilter failed, line=%u\n", __LINE__); return 1; } /* However, while some pass, the vast majority should be filtered */ if (count_passed > count_filtered/10) { fprintf(stderr, "[-] rstfilter failed, line=%u\n", __LINE__); return 1; } //printf("filtered=%u passed=%u\n", count_filtered, count_passed); return 0; } ================================================ FILE: src/misc-rstfilter.h ================================================ /* RST filter In theory, we should transmit a RST packet every time we receive an invalid TCP packet. In practice, this can lead to endless transmits when the other size continues to transmit bad packets. This may happen accidentally, or this may happen on purpose from the other side trying to attack the scanner intentionally. In May 2019 I see this from somebody who I suspect is trying to do that, replying back as fast as the scanner transmits (when running at 10,000 packets per-second). This halts the scan, as it's throttle limit is filled sending RSTs and not doing something useful. The design is a simple non-deterministic algorithm. It hashes the IP/prot combo, then updates a counter at that bucket. When it reaches its limit, it stops transmitting resets. However, it'll also slowly empty buckets, so can occasionally transmit a RST now and then. */ #ifndef MISC_RSTFILTER_H #define MISC_RSTFILTER_H #include #include "massip-addr.h" struct ResetFilter; /** * Create a structure for this. * @param seed * A random seed chosen via entropy at startup, so that adversaries * can't predict where the buckets will be. * @param bucket_count * The number of buckets. This'll be rounded up to the nearest * power-of-two. 16384 is probably a good number. * @return an instance of this object that should be eventually * cleaned up with 'rstfilter_destroy()'. */ struct ResetFilter * rstfilter_create(unsigned long long seed, size_t bucket_count); /** * Cleans up the object that was created with 'rstfilter_create()'. */ void rstfilter_destroy(struct ResetFilter *rf); /** * Tests to see if we should ignore the given RST packet. This will * also slowly empty a random bucket * @return 1 if we should filter out the offending packet and ignore it, * or else 0 if we shouldn't ignore it. */ int rstfilter_is_filter(struct ResetFilter *rf, ipaddress src_ip, unsigned src_port, ipaddress dst_ip, unsigned dst_port); int rstfilter_selftest(void); #endif ================================================ FILE: src/out-binary.c ================================================ #include "output.h" #include "masscan-app.h" #include "masscan-status.h" #include "out-record.h" #include "util-safefunc.h" #include /**************************************************************************** ****************************************************************************/ static void binary_out_open(struct Output *out, FILE *fp) { char firstrecord[2+'a']; size_t bytes_written; UNUSEDPARM(out); memset(firstrecord, 0, 2+'a'); snprintf(firstrecord, 2+'a', "masscan/1.1\ns:%u\n", (unsigned)out->when_scan_started); bytes_written = fwrite(firstrecord, 1, 2+'a', fp); if (bytes_written != 2+'a') { perror("output"); exit(1); } out->rotate.bytes_written += bytes_written; } /**************************************************************************** ****************************************************************************/ static void binary_out_close(struct Output *out, FILE *fp) { char firstrecord[2+'a']; size_t bytes_written; UNUSEDPARM(out); memset(firstrecord, 0, 2+'a'); snprintf(firstrecord, 2+'a', "masscan/1.1"); bytes_written = fwrite(firstrecord, 1, 2+'a', fp); if (bytes_written != 2+'a') { perror("output"); exit(1); } out->rotate.bytes_written += bytes_written; } static void _put_byte(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num) { size_t offset = *r_offset; (*r_offset) += 1; if (*r_offset <= length) { buf[offset++] = (unsigned char)(num>>0); } } static void _put_short(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num) { size_t offset = *r_offset; (*r_offset) += 2; if (*r_offset <= length) { buf[offset++] = (unsigned char)(num>>8); buf[offset++] = (unsigned char)(num>>0); } } static void _put_integer(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num) { size_t offset = *r_offset; (*r_offset) += 4; if (*r_offset <= length) { buf[offset++] = (unsigned char)(num>>24); buf[offset++] = (unsigned char)(num>>16); buf[offset++] = (unsigned char)(num>>8); buf[offset++] = (unsigned char)(num>>0); } } static void _put_long(unsigned char *buf, size_t length, size_t *r_offset, unsigned long long num) { size_t offset = *r_offset; (*r_offset) += 8; if (*r_offset <= length) { buf[offset++] = (unsigned char)(num>>56); buf[offset++] = (unsigned char)(num>>48); buf[offset++] = (unsigned char)(num>>40); buf[offset++] = (unsigned char)(num>>32); buf[offset++] = (unsigned char)(num>>24); buf[offset++] = (unsigned char)(num>>16); buf[offset++] = (unsigned char)(num>>8); buf[offset++] = (unsigned char)(num>>0); } } /**************************************************************************** ****************************************************************************/ static void binary_out_status_ipv6(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { unsigned char buf[256+1]; size_t max = sizeof(buf)-1; size_t offset = 0; size_t bytes_written; /* [TYPE] field */ switch (status) { case PortStatus_Open: _put_byte(buf, max, &offset, Out_Open6); break; case PortStatus_Closed: _put_byte(buf, max, &offset, Out_Closed6); break; case PortStatus_Arp: _put_byte(buf, max, &offset, Out_Arp6); break; default: return; } /* [LENGTH] field * see assert() below */ _put_byte(buf, max, &offset, 26); _put_integer(buf, max, &offset, timestamp); _put_byte(buf, max, &offset, ip_proto); _put_short(buf, max, &offset, port); _put_byte(buf, max, &offset, reason); _put_byte(buf, max, &offset, ttl); _put_byte(buf, max, &offset, ip.version); _put_long(buf, max, &offset, ip.ipv6.hi); _put_long(buf, max, &offset, ip.ipv6.lo); assert(offset == 2 + 26); bytes_written = fwrite(buf, 1, offset, fp); if (bytes_written != offset) { perror("output"); exit(1); } out->rotate.bytes_written += bytes_written; } /**************************************************************************** ****************************************************************************/ static void binary_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { unsigned char foo[256]; size_t bytes_written; /* This function is for IPv6, call a different function for IPv6 */ if (ip.version == 6) { binary_out_status_ipv6(out, fp, timestamp, status, ip, ip_proto, port, reason, ttl); return; } /* [TYPE] field */ switch (status) { case PortStatus_Open: foo[0] = Out_Open2; break; case PortStatus_Closed: foo[0] = Out_Closed2; break; case PortStatus_Arp: foo[0] = Out_Arp2; break; default: return; } /* [LENGTH] field */ foo[1] = 13; /* [TIMESTAMP] field */ foo[2] = (unsigned char)(timestamp>>24); foo[3] = (unsigned char)(timestamp>>16); foo[4] = (unsigned char)(timestamp>> 8); foo[5] = (unsigned char)(timestamp>> 0); foo[6] = (unsigned char)(ip.ipv4 >>24); foo[7] = (unsigned char)(ip.ipv4 >>16); foo[8] = (unsigned char)(ip.ipv4 >> 8); foo[9] = (unsigned char)(ip.ipv4 >> 0); foo[10] = (unsigned char)(ip_proto); foo[11] = (unsigned char)(port>>8); foo[12] = (unsigned char)(port>>0); foo[13] = (unsigned char)reason; foo[14] = (unsigned char)ttl; bytes_written = fwrite(&foo, 1, 15, fp); if (bytes_written != 15) { perror("output"); exit(1); } out->rotate.bytes_written += bytes_written; } /**************************************************************************** ****************************************************************************/ static void binary_out_banner_ipv6(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { unsigned char foo[32768]; unsigned i; size_t bytes_written; static const unsigned HeaderLength = 14 + 13; /* [TYPE] field */ foo[0] = Out_Banner6; /*banner*/ /* [LENGTH] field*/ if (length >= 128 * 128 - HeaderLength) return; if (length < 128 - HeaderLength) { foo[1] = (unsigned char)(length + HeaderLength); i = 2; } else { foo[1] = (unsigned char)((length + HeaderLength)>>7) | 0x80; foo[2] = (unsigned char)((length + HeaderLength) & 0x7F); i = 3; } /* [TIMESTAMP] field */ foo[i+0] = (unsigned char)(timestamp>>24); foo[i+1] = (unsigned char)(timestamp>>16); foo[i+2] = (unsigned char)(timestamp>> 8); foo[i+3] = (unsigned char)(timestamp>> 0); foo[i+ 4] = (unsigned char)(ip_proto); foo[i+ 5] = (unsigned char)(port>>8); foo[i+ 6] = (unsigned char)(port>>0); foo[i+ 7] = (unsigned char)(proto>>8); foo[i+ 8] = (unsigned char)(proto>>0); foo[i+ 9] = (unsigned char)(ttl); foo[i+10] = (unsigned char)(ip.version); foo[i+11] = (unsigned char)(ip.ipv6.hi >> 56ULL); foo[i+12] = (unsigned char)(ip.ipv6.hi >> 48ULL); foo[i+13] = (unsigned char)(ip.ipv6.hi >> 40ULL); foo[i+14] = (unsigned char)(ip.ipv6.hi >> 32ULL); foo[i+15] = (unsigned char)(ip.ipv6.hi >> 24ULL); foo[i+16] = (unsigned char)(ip.ipv6.hi >> 16ULL); foo[i+17] = (unsigned char)(ip.ipv6.hi >> 8ULL); foo[i+18] = (unsigned char)(ip.ipv6.hi >> 0ULL); foo[i+19] = (unsigned char)(ip.ipv6.lo >> 56ULL); foo[i+20] = (unsigned char)(ip.ipv6.lo >> 48ULL); foo[i+21] = (unsigned char)(ip.ipv6.lo >> 40ULL); foo[i+22] = (unsigned char)(ip.ipv6.lo >> 32ULL); foo[i+23] = (unsigned char)(ip.ipv6.lo >> 24ULL); foo[i+24] = (unsigned char)(ip.ipv6.lo >> 16ULL); foo[i+25] = (unsigned char)(ip.ipv6.lo >> 8ULL); foo[i+26] = (unsigned char)(ip.ipv6.lo >> 0ULL); /* Banner */ memcpy(foo+i+14+13, px, length); bytes_written = fwrite(&foo, 1, length+i+HeaderLength, fp); if (bytes_written != length+i+HeaderLength) { perror("output"); exit(1); } out->rotate.bytes_written += bytes_written; } /**************************************************************************** ****************************************************************************/ static void binary_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { unsigned char foo[32768]; unsigned i; size_t bytes_written; static const unsigned HeaderLength = 14; if (ip.version == 6) { binary_out_banner_ipv6(out, fp, timestamp, ip, ip_proto, port, proto, ttl, px, length); return; } /* [TYPE] field */ foo[0] = Out_Banner9; /*banner*/ /* [LENGTH] field*/ if (length >= 128 * 128 - HeaderLength) return; if (length < 128 - HeaderLength) { foo[1] = (unsigned char)(length + HeaderLength); i = 2; } else { foo[1] = (unsigned char)((length + HeaderLength)>>7) | 0x80; foo[2] = (unsigned char)((length + HeaderLength) & 0x7F); i = 3; } /* [TIMESTAMP] field */ foo[i+0] = (unsigned char)(timestamp>>24); foo[i+1] = (unsigned char)(timestamp>>16); foo[i+2] = (unsigned char)(timestamp>> 8); foo[i+3] = (unsigned char)(timestamp>> 0); foo[i+4] = (unsigned char)(ip.ipv4 >> 24); foo[i+5] = (unsigned char)(ip.ipv4 >> 16); foo[i+6] = (unsigned char)(ip.ipv4 >> 8); foo[i+7] = (unsigned char)(ip.ipv4 >> 0); foo[i+8] = (unsigned char)(ip_proto); foo[i+ 9] = (unsigned char)(port>>8); foo[i+10] = (unsigned char)(port>>0); foo[i+11] = (unsigned char)(proto>>8); foo[i+12] = (unsigned char)(proto>>0); foo[i+13] = (unsigned char)(ttl); /* Banner */ memcpy(foo+i+14, px, length); bytes_written = fwrite(&foo, 1, length+i+HeaderLength, fp); if (bytes_written != length+i+HeaderLength) { perror("output"); exit(1); } out->rotate.bytes_written += bytes_written; } /**************************************************************************** ****************************************************************************/ const struct OutputType binary_output = { "scan", 0, binary_out_open, binary_out_close, binary_out_status, binary_out_banner, }; ================================================ FILE: src/out-certs.c ================================================ #include "output.h" #include "masscan-app.h" #include "masscan-status.h" #include "util-safefunc.h" #include /**************************************************************************** ****************************************************************************/ static void cert_out_open(struct Output *out, FILE *fp) { UNUSEDPARM(out); UNUSEDPARM(fp); } /**************************************************************************** ****************************************************************************/ static void cert_out_close(struct Output *out, FILE *fp) { UNUSEDPARM(out); fprintf(fp, "{finished: 1}\n"); } /****************************************************************************** ******************************************************************************/ static void cert_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { /* certificates only come with banner info, so there is no port info * to report */ UNUSEDPARM(out); UNUSEDPARM(fp); UNUSEDPARM(timestamp); UNUSEDPARM(status); UNUSEDPARM(ip); UNUSEDPARM(ip_proto); UNUSEDPARM(port); UNUSEDPARM(reason); UNUSEDPARM(ttl); } /****************************************************************************** ******************************************************************************/ static void cert_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { unsigned i; UNUSEDPARM(ip_proto); UNUSEDPARM(ip); UNUSEDPARM(timestamp); UNUSEDPARM(fp); UNUSEDPARM(out); UNUSEDPARM(ttl); UNUSEDPARM(proto); UNUSEDPARM(port); if (length > 5 && memcmp(px, "cert:", 5) == 0) { px += 5; length -= 5; } printf("-----BEGIN CERTIFICATE-----\n"); for (i=0; i 72) len = 72; printf("%.*s\n", len, px+i); } printf("-----END CERTIFICATE-----\n"); } /**************************************************************************** ****************************************************************************/ const struct OutputType certs_output = { "cert", 0, cert_out_open, cert_out_close, cert_out_status, cert_out_banner }; ================================================ FILE: src/out-grepable.c ================================================ #include "output.h" #include "masscan.h" #include "masscan-version.h" #include "masscan-status.h" #include "out-tcp-services.h" #include "massip-port.h" #include "util-safefunc.h" /**************************************************************************** ****************************************************************************/ static unsigned count_type(const struct RangeList *ports, int start_type, int end_type) { unsigned min_port = start_type; unsigned max_port = end_type; unsigned i; unsigned result = 0; for (i=0; icount; i++) { struct Range r = ports->list[i]; if (r.begin > max_port) continue; if (r.end < min_port) continue; if (r.begin < min_port) r.begin = min_port; if (r.end > max_port) r.end = max_port; result += r.end - r.begin + 1; } return result; } /**************************************************************************** ****************************************************************************/ static void print_port_list(const struct RangeList *ports, int type, FILE *fp) { unsigned min_port = type; unsigned max_port = type + 65535; unsigned i; for (i=0; icount; i++) { struct Range r = ports->list[i]; if (r.begin > max_port) continue; if (r.end < min_port) continue; if (r.begin < min_port) r.begin = min_port; if (r.end > max_port) r.end = max_port; fprintf(fp, "%u-%u%s", r.begin, r.end, (i+1count)?",":""); } } extern const char *debug_recv_status; /**************************************************************************** * This function doesn't really "open" the file. Instead, the purpose of * this function is to initialize the file by printing header information. ****************************************************************************/ static void grepable_out_open(struct Output *out, FILE *fp) { char timestamp[64]; struct tm tm; unsigned count; safe_gmtime(&tm, &out->when_scan_started); //Tue Jan 21 20:23:22 2014 //%a %b %d %H:%M:%S %Y strftime(timestamp, sizeof(timestamp), "%c", &tm); fprintf(fp, "# Masscan " MASSCAN_VERSION " scan initiated %s\n", timestamp); count = count_type(&out->masscan->targets.ports, Templ_TCP, Templ_TCP_last); fprintf(fp, "# Ports scanned: TCP(%u;", count); if (count) print_port_list(&out->masscan->targets.ports, Templ_TCP, fp); count = count_type(&out->masscan->targets.ports, Templ_UDP, Templ_UDP_last); fprintf(fp, ") UDP(%u;", count); if (count) print_port_list(&out->masscan->targets.ports, Templ_UDP, fp); count = count_type(&out->masscan->targets.ports, Templ_SCTP, Templ_SCTP_last); fprintf(fp, ") SCTP(%u;", count); if (count) print_port_list(&out->masscan->targets.ports, Templ_SCTP, fp); count = count_type(&out->masscan->targets.ports, Templ_Oproto_first, Templ_Oproto_last); fprintf(fp, ") PROTOCOLS(%u;", count); if (count) print_port_list(&out->masscan->targets.ports, Templ_Oproto_first, fp); fprintf(fp, ")\n"); } /**************************************************************************** * This function doesn't really "close" the file. Instead, it's purpose * is to print trailing information to the file. This is pretty much only * a concern for XML files that need stuff appended to the end. ****************************************************************************/ static void grepable_out_close(struct Output *out, FILE *fp) { time_t now = time(0); char timestamp[64]; struct tm tm; UNUSEDPARM(out); safe_gmtime(&tm, &now); //Tue Jan 21 20:23:22 2014 //%a %b %d %H:%M:%S %Y strftime(timestamp, sizeof(timestamp), "%c", &tm); fprintf(fp, "# Masscan done at %s\n", timestamp); } /**************************************************************************** * Prints out the status of a port, which is almost always just "open" * or "closed". ****************************************************************************/ static void grepable_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { const char *service; ipaddress_formatted_t fmt; UNUSEDPARM(timestamp); UNUSEDPARM(out); UNUSEDPARM(reason); UNUSEDPARM(ttl); if (ip_proto == 6) service = tcp_service_name(port); else if (ip_proto == 17) service = udp_service_name(port); else service = oproto_service_name(ip_proto); fprintf(fp, "Timestamp: %llu", (unsigned long long)timestamp); fmt = ipaddress_fmt(ip); fprintf(fp, "\tHost: %s ()", fmt.string); fprintf(fp, "\tPorts: %u/%s/%s/%s/%s/%s/%s\n", port, status_string(status), //"open", "closed" name_from_ip_proto(ip_proto), //"tcp", "udp", "sctp" "", //owner service, //service "", //SunRPC info "" //Version info ); } /**************************************************************************** * Prints out "banner" information for a port. This is done when there is * a protocol defined for a port, and we do some interaction to find out * more information about which protocol is running on a port, it's version, * and other useful information. ****************************************************************************/ static void grepable_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { char banner_buffer[MAX_BANNER_LENGTH]; ipaddress_formatted_t fmt; UNUSEDPARM(ttl); UNUSEDPARM(timestamp); UNUSEDPARM(out); UNUSEDPARM(ip_proto); fmt = ipaddress_fmt(ip); fprintf(fp, "Host: %s ()", fmt.string); fprintf(fp, "\tPort: %u", port); fprintf(fp, "\tService: %s", masscan_app_to_string(proto)); normalize_string(px, length, banner_buffer, sizeof(banner_buffer)); fprintf(fp, "\tBanner: %s\n", banner_buffer); } /**************************************************************************** * This is the only structure exposed to the rest of the system. Everything * else in the file is defined 'static' or 'private'. ****************************************************************************/ const struct OutputType grepable_output = { "grepable", 0, grepable_out_open, grepable_out_close, grepable_out_status, grepable_out_banner }; ================================================ FILE: src/out-hostonly.c ================================================ #include "output.h" #include "masscan.h" #include "masscan-app.h" #include "masscan-status.h" #include "unusedparm.h" #include "out-tcp-services.h" static void hostonly_out_open(struct Output *out, FILE *fp) { UNUSEDPARM(fp); UNUSEDPARM(out); } static void hostonly_out_close(struct Output *out, FILE *fp) { UNUSEDPARM(fp); UNUSEDPARM(out); } static void hostonly_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { ipaddress_formatted_t fmt = ipaddress_fmt(ip); UNUSEDPARM(reason); UNUSEDPARM(out); UNUSEDPARM(timestamp); UNUSEDPARM(ttl); UNUSEDPARM(port); UNUSEDPARM(ip_proto); UNUSEDPARM(status); fprintf(fp, "%s\n", fmt.string); } /*************************************** ************************************* ****************************************************************************/ static void hostonly_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { /* SYN only - no banner */ ipaddress_formatted_t fmt = ipaddress_fmt(ip); UNUSEDPARM(out); UNUSEDPARM(ttl); UNUSEDPARM(port); UNUSEDPARM(fp); UNUSEDPARM(timestamp); UNUSEDPARM(ip); UNUSEDPARM(ip_proto); UNUSEDPARM(proto); UNUSEDPARM(px); UNUSEDPARM(length); fprintf(fp, "%s\n", fmt.string); return; } /**************************************************************************** ****************************************************************************/ const struct OutputType hostonly_output = { "hostonly", 0, hostonly_out_open, hostonly_out_close, hostonly_out_status, hostonly_out_banner }; ================================================ FILE: src/out-json.c ================================================ #include "output.h" #include "masscan-app.h" #include "masscan-status.h" #include "util-safefunc.h" #include /**************************************************************************** ****************************************************************************/ static void json_out_open(struct Output *out, FILE *fp) { UNUSEDPARM(out); fprintf(fp, "[\n"); // enclose the atomic {}'s into an [] } /**************************************************************************** ****************************************************************************/ static void json_out_close(struct Output *out, FILE *fp) { UNUSEDPARM(out); fprintf(fp, "]\n"); // enclose the atomic {}'s into an [] } //{ ip: "124.53.139.201", ports: [ {port: 443, proto: "tcp", status: "open", reason: "syn-ack", ttl: 48} ] } /**************************************************************************** ****************************************************************************/ static void json_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { char reason_buffer[128]; ipaddress_formatted_t fmt; UNUSEDPARM(out); //UNUSEDPARM(timestamp); /* Trailing comma breaks some JSON parsers. We don't know precisely when * we'll end, but we do know when we begin, so instead of appending * a command to the record, we prepend it -- but not before first record */ if (out->is_first_record_seen) fprintf(fp, ",\n"); else out->is_first_record_seen = 1; fprintf(fp, "{ "); fmt = ipaddress_fmt(ip); fprintf(fp, " \"ip\": \"%s\", ", fmt.string); fprintf(fp, " \"timestamp\": \"%d\", \"ports\": [ {\"port\": %u, \"proto\": \"%s\", \"status\": \"%s\"," " \"reason\": \"%s\", \"ttl\": %u} ] ", (int) timestamp, port, name_from_ip_proto(ip_proto), status_string(status), reason_string(reason, reason_buffer, sizeof(reason_buffer)), ttl ); fprintf(fp, "}\n"); } /***************************************************************************** * Remove bad characters from the banner, especially new lines and HTML * control codes. *****************************************************************************/ static const char * normalize_json_string(const unsigned char *px, size_t length, char *buf, size_t buf_len) { size_t i=0; size_t offset = 0; for (i=0; i>4]; buf[offset++] = "0123456789abcdef"[px[i]&0xF]; } } } buf[offset] = '\0'; return buf; } /****************************************************************************** ******************************************************************************/ static void json_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { char banner_buffer[65536]; ipaddress_formatted_t fmt; UNUSEDPARM(ttl); /* Trailing comma breaks some JSON parsers. We don't know precisely when * we'll end, but we do know when we begin, so instead of appending * a command to the record, we prepend it -- but not before first record */ if (out->is_first_record_seen) fprintf(fp, ",\n"); else out->is_first_record_seen = 1; fprintf(fp, "{ "); fmt = ipaddress_fmt(ip); fprintf(fp, " \"ip\": \"%s\", ", fmt.string); fprintf(fp, " \"timestamp\": \"%d\", \"ports\": [ {\"port\": %u, \"proto\": \"%s\", \"service\": {\"name\": \"%s\", \"banner\": \"%s\"} } ] ", (int) timestamp, port, name_from_ip_proto(ip_proto), masscan_app_to_string(proto), normalize_json_string(px, length, banner_buffer, sizeof(banner_buffer)) ); fprintf(fp, "}\n"); UNUSEDPARM(out); } /**************************************************************************** ****************************************************************************/ const struct OutputType json_output = { "json", 0, json_out_open, json_out_close, json_out_status, json_out_banner }; ================================================ FILE: src/out-ndjson.c ================================================ #include "output.h" #include "masscan-app.h" #include "masscan-status.h" #include "util-safefunc.h" #include /**************************************************************************** ****************************************************************************/ static void ndjson_out_open(struct Output *out, FILE *fp) { UNUSEDPARM(out); UNUSEDPARM(fp); } /**************************************************************************** ****************************************************************************/ static void ndjson_out_close(struct Output *out, FILE *fp) { UNUSEDPARM(out); UNUSEDPARM(fp); } //{ ip: "124.53.139.201", ports: [ {port: 443, proto: "tcp", status: "open", reason: "syn-ack", ttl: 48} ] } /**************************************************************************** ****************************************************************************/ static void ndjson_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { char reason_buffer[128]; ipaddress_formatted_t fmt; UNUSEDPARM(out); fprintf(fp, "{"); fmt = ipaddress_fmt(ip); fprintf(fp, "\"ip\":\"%s\",", fmt.string); fprintf(fp, "\"timestamp\":\"%d\",\"port\":%u,\"proto\":\"%s\",\"rec_type\":\"status\",\"data\":{\"status\":\"%s\"," "\"reason\":\"%s\",\"ttl\":%u}", (int) timestamp, port, name_from_ip_proto(ip_proto), status_string(status), reason_string(reason, reason_buffer, sizeof(reason_buffer)), ttl ); fprintf(fp, "}\n"); } /***************************************************************************** * Remove bad characters from the banner, especially new lines and HTML * control codes. * * Keeping this here since we may need to change the behavior from what * is done in the sister `normalize_json_string` function. It's unlikely * but it's a small function and will save time later if needed. Could also * set it up to base64 encode the banner payload. *****************************************************************************/ static const char * normalize_ndjson_string(const unsigned char *px, size_t length, char *buf, size_t buf_len) { size_t i=0; size_t offset = 0; for (i=0; i>4]; buf[offset++] = "0123456789abcdef"[px[i]&0xF]; } } } buf[offset] = '\0'; return buf; } /****************************************************************************** ******************************************************************************/ static void ndjson_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { char banner_buffer[65536]; ipaddress_formatted_t fmt; UNUSEDPARM(ttl); //UNUSEDPARM(timestamp); fprintf(fp, "{"); fmt = ipaddress_fmt(ip); fprintf(fp, "\"ip\":\"%s\",", fmt.string); fprintf(fp, "\"timestamp\":\"%d\",\"port\":%u,\"proto\":\"%s\",\"rec_type\":\"banner\",\"data\":{\"service_name\":\"%s\",\"banner\":\"%s\"}", (int) timestamp, port, name_from_ip_proto(ip_proto), masscan_app_to_string(proto), normalize_ndjson_string(px, length, banner_buffer, sizeof(banner_buffer)) ); // fprintf(fp, "\"timestamp\":\"%d\",\"ports\":[{\"port\":%u,\"proto\":\"%s\",\"service\":{\"name\":\"%s\",\"banner\":\"%s\"}}]", // (int) timestamp, // port, // name_from_ip_proto(ip_proto), // masscan_app_to_string(proto), // normalize_ndjson_string(px, length, banner_buffer, sizeof(banner_buffer)) // ); fprintf(fp, "}\n"); UNUSEDPARM(out); /* fprintf(fp, "" "
" "" "" "" "" "" "" "" "\r\n", (unsigned)timestamp, (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>> 8)&0xFF, (ip>> 0)&0xFF, name_from_ip_proto(ip_proto), port, reason, ttl, masscan_app_to_string(proto), normalize_string(px, length, banner_buffer, sizeof(banner_buffer)) );*/ } /**************************************************************************** ****************************************************************************/ const struct OutputType ndjson_output = { "ndjson", 0, ndjson_out_open, ndjson_out_close, ndjson_out_status, ndjson_out_banner }; ================================================ FILE: src/out-null.c ================================================ #include "output.h" #include "masscan.h" /**************************************************************************** * This function doesn't really "open" the file. Instead, the purpose of * this function is to initialize the file by printing header information. ****************************************************************************/ static void null_out_open(struct Output *out, FILE *fp) { UNUSEDPARM(out); UNUSEDPARM(fp); } /**************************************************************************** * This function doesn't really "close" the file. Instead, it's purpose * is to print trailing information to the file. This is pretty much only * a concern for XML files that need stuff appended to the end. ****************************************************************************/ static void null_out_close(struct Output *out, FILE *fp) { UNUSEDPARM(out); UNUSEDPARM(fp); } /**************************************************************************** * Prints out the status of a port, which is almost always just "open" * or "closed". ****************************************************************************/ static void null_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { UNUSEDPARM(timestamp); UNUSEDPARM(out); UNUSEDPARM(fp); UNUSEDPARM(status); UNUSEDPARM(ip_proto); UNUSEDPARM(ip); UNUSEDPARM(port); UNUSEDPARM(reason); UNUSEDPARM(ttl); } /**************************************************************************** * Prints out "banner" information for a port. This is done when there is * a protocol defined for a port, and we do some interaction to find out * more information about which protocol is running on a port, it's version, * and other useful information. ****************************************************************************/ static void null_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { UNUSEDPARM(ttl); UNUSEDPARM(timestamp); UNUSEDPARM(out); UNUSEDPARM(fp); UNUSEDPARM(ip); UNUSEDPARM(ip_proto); UNUSEDPARM(port); UNUSEDPARM(proto); UNUSEDPARM(px); UNUSEDPARM(length); } /**************************************************************************** * This is the only structure exposed to the rest of the system. Everything * else in the file is defined 'static' or 'private'. ****************************************************************************/ const struct OutputType null_output = { "null", 0, null_out_open, null_out_close, null_out_status, null_out_banner }; ================================================ FILE: src/out-record.h ================================================ #ifndef OUT_RECORD_H #define OUT_RECORD_H enum OutputRecordType { Out_Open = 1, Out_Closed = 2, Out_Banner1 = 5, Out_Open2 = 6, Out_Closed2 = 7, Out_Arp2 = 8, Out_Banner9 = 9, Out_Open6 = 10, Out_Closed6 = 11, Out_Arp6 = 12, Out_Banner6 = 13, }; #endif ================================================ FILE: src/out-redis.c ================================================ #include "output.h" #include "masscan.h" #include "pixie-sockets.h" #include "util-logger.h" #include /**************************************************************************** * Receive a full line from the socket ****************************************************************************/ static size_t recv_line(SOCKET fd, void *buf, size_t buf_size) { size_t count = 0; while (count < buf_size) { size_t bytes_received; bytes_received = recv(fd, (char*)buf+count, 1, 0); if (bytes_received == 0) { LOG(0, "redis: recv_line() failed\n"); exit(1); } count++; if (((unsigned char*)buf)[count-1] == '\n') break; } return count; } /**************************************************************************** ****************************************************************************/ static int parse_state_machine(struct Output *out, const unsigned char *px, size_t length) { unsigned state = out->redis.state; unsigned i; enum { START, NUMBER, P, PO, PON, PONG, PONG_CR, PONG_CR_LF }; for (i=0; iredis.outstanding == 0) { LOG(0, "redis: out of sync\n"); exit(1); } out->redis.outstanding--; } else { LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i); exit(1); } break; case P: case PO: case PON: case PONG_CR: case PONG_CR_LF: if ("PONG+\r\n"[i-P] == px[i]) { state++; if (px[i] == '\n') { out->redis.state = 0; return 1; } } else { LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i); exit(1); } default: LOG(0, "redis: unexpected state: %u\n", state); exit(1); } out->redis.state = state; return 0; } /**************************************************************************** ****************************************************************************/ static int clean_response_queue(struct Output *out, SOCKET fd) { fd_set readfds; struct timeval tv = {0,0}; int x; int nfds; unsigned char buf[1024]; size_t bytes_read; FD_ZERO(&readfds); #ifdef _MSC_VER #pragma warning(disable:4127) #endif FD_SET(fd, &readfds); nfds = (int)fd; x = select(nfds, &readfds, 0, 0, &tv); if (x == 0) return 1; if (x < 0) { LOG(0, "redis:select() failed\n"); exit(1); } if (x != 1) { LOG(0, "redis:select() failed\n"); exit(1); } /* * Data exists, so parse it */ bytes_read = recv(fd, (char*)buf, sizeof(buf), 0); if (bytes_read == 0) { LOG(0, "redis:recv() failed\n"); exit(1); } return parse_state_machine(out, buf, bytes_read); } /**************************************************************************** ****************************************************************************/ static void redis_out_open(struct Output *out, FILE *fp) { /*FIXME: why did I write this code using ptrdiff_t? */ ptrdiff_t fd = (ptrdiff_t)fp; size_t count; char line[1024]; UNUSEDPARM(out); if (out->redis.password != NULL) { snprintf(line, sizeof(line), "*2\r\n" "$4\r\nAUTH\r\n" "$%u\r\n%s\r\n", (unsigned)strlen(out->redis.password), out->redis.password); count = send((SOCKET)fd, line, (int)strlen(line), 0); if (count != strlen(line)) { LOG(0, "redis: error auth\n"); exit(1); } count = recv_line((SOCKET)fd, line, sizeof(line)); if (count != 5 && memcmp(line, "+OK\r\n", 5) != 0) { LOG(0, "redis: unexpected response from redis server: %s\n", line); exit(1); } } count = send((SOCKET)fd, "PING\r\n", 6, 0); if (count != 6) { LOG(0, "redis: send(ping) failed\n"); exit(1); } count = recv_line((SOCKET)fd, line, sizeof(line)); if (count != 7 && memcmp(line, "+PONG\r\n", 7) != 0) { LOG(0, "redis: unexpected response from redis server: %s\n", line); exit(1); } } /**************************************************************************** ****************************************************************************/ static void redis_out_close(struct Output *out, FILE *fp) { ptrdiff_t fd = (ptrdiff_t)fp; size_t count; unsigned char line[1024]; UNUSEDPARM(out); count = send((SOCKET)fd, "QUIT\r\n", 6, 0); if (count != 6) { LOG(0, "redis: send(quit) failed\n"); exit(1); } count = recv_line((SOCKET)fd, line, sizeof(line)); if ((count != 5 && memcmp(line, "+OK\r\n", 5) != 0) && (count != 4 && memcmp(line, ":0\r\n", 4) != 0)){ LOG(0, "redis: unexpected response from redis server: %s\n", line); exit(1); } } /**************************************************************************** ****************************************************************************/ static void redis_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { ptrdiff_t fd = (ptrdiff_t)fp; char line[1024]; int line_length; char ip_string[64]; char port_string[10]; int ip_string_length; int port_string_length; size_t count; char values[64]; int values_length; ipaddress_formatted_t fmt = ipaddress_fmt(ip); ip_string_length = snprintf(ip_string, sizeof(ip_string), "%s", fmt.string); port_string_length = snprintf(port_string, sizeof(port_string), "%u/%s", port, name_from_ip_proto(ip_proto)); /**3 $3 SET $5 mykey $7 myvalue */ /* * KEY: "host" * VALUE: ip */ snprintf(line, sizeof(line), "*3\r\n" "$4\r\nSADD\r\n" "$%d\r\n%s\r\n" "$%d\r\n%s\r\n" , 4, "host", ip_string_length, ip_string ); count = send((SOCKET)fd, line, (int)strlen(line), 0); if (count != strlen(line)) { LOG(0, "redis: error sending data\n"); exit(1); } out->redis.outstanding++; /* * KEY: ip * VALUE: port */ snprintf(line, sizeof(line), "*3\r\n" "$4\r\nSADD\r\n" "$%d\r\n%s\r\n" "$%d\r\n%s\r\n" , ip_string_length, ip_string, port_string_length, port_string); count = send((SOCKET)fd, line, (int)strlen(line), 0); if (count != strlen(line)) { LOG(0, "redis: error sending data\n"); exit(1); } out->redis.outstanding++; /* * KEY: ip:port * VALUE: timestamp:status:reason:ttl */ values_length = snprintf(values, sizeof(values), "%u:%u:%u:%u", (unsigned)timestamp, status, reason, ttl); line_length = snprintf(line, sizeof(line), "*3\r\n" "$4\r\nSADD\r\n" "$%d\r\n%s:%s\r\n" "$%d\r\n%s\r\n" , ip_string_length + 1 + port_string_length, ip_string, port_string, values_length, values ); count = send((SOCKET)fd, line, (int)line_length, 0); if (count != (size_t)line_length) { LOG(0, "redis: error sending data\n"); exit(1); } out->redis.outstanding++; clean_response_queue(out, (SOCKET)fd); } /**************************************************************************** ****************************************************************************/ static void redis_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { UNUSEDPARM(ttl); UNUSEDPARM(timestamp); UNUSEDPARM(out); UNUSEDPARM(fp); UNUSEDPARM(ip); UNUSEDPARM(ip_proto); UNUSEDPARM(port); UNUSEDPARM(proto); UNUSEDPARM(px); UNUSEDPARM(length); } /**************************************************************************** ****************************************************************************/ const struct OutputType redis_output = { "redis", 0, redis_out_open, redis_out_close, redis_out_status, redis_out_banner }; ================================================ FILE: src/out-tcp-services.c ================================================ #include "out-tcp-services.h" #include #include #ifndef WIN32 #include #else #include #endif #include /** * This is a stupid hack to avoid dependencies. I want to minimize the dependence * on network libraries. For example, I get a warning message on FreeBSD about * a missing `htons()`. I could just add a system header, but then this increases * dependencies on other things. Alternatively, I could just implement the * function myself. So I chose that route. */ static unsigned short my_htons(unsigned port) { static const char test[3] = "\x11\x22"; if (*(unsigned short*)test == 0x1122) return (unsigned short)(0xFFFF & port); else return (unsigned short)((port>>8)&0xFF) | ((port&0xFF)<<8); } #if _MSC_VER #define strdup _strdup #endif static char *tcp_services[65536]; static char *udp_services[65536]; static char *oproto_services[256]; const char * tcp_service_name(int port) { if (tcp_services[port]) return tcp_services[port]; #if defined(__linux__) && !defined(__TERMUX__) int r; struct servent result_buf; struct servent *result; char buf[2048]; r = getservbyport_r(my_htons(port), "tcp", &result_buf,buf, sizeof(buf), &result); /* ignore ERANGE - if the result can't fit in 2k, just return unknown */ if (r != 0 || result == NULL) return "unknown"; return tcp_services[port] = strdup(result_buf.s_name); #else { struct servent *result; result = getservbyport(my_htons((unsigned short)port), "tcp"); if (result == 0) return "unknown"; return tcp_services[port] = strdup(result->s_name); } #endif } const char * udp_service_name(int port) { if (udp_services[port]) return udp_services[port]; #if defined(__linux__) && !defined(__TERMUX__) int r; struct servent result_buf; struct servent *result; char buf[2048]; r = getservbyport_r(my_htons(port), "udp", &result_buf,buf, sizeof(buf), &result); /* ignore ERANGE - if the result can't fit in 2k, just return unknown */ if (r != 0 || result == NULL) return "unknown"; return udp_services[port] = strdup(result_buf.s_name); #else { struct servent *result; result = getservbyport(my_htons((unsigned short)port), "udp"); if (result == 0) return "unknown"; return udp_services[port] = strdup(result->s_name); } #endif } const char * oproto_service_name(int port) { if (oproto_services[port]) return oproto_services[port]; { struct protoent *result; result = getprotobynumber(port); if (result == 0) return "unknown"; return oproto_services[port] = strdup(result->p_name); } } ================================================ FILE: src/out-tcp-services.h ================================================ #ifndef OUT_TCP_SERVICES_H #define OUT_TCP_SERVICES_H const char *tcp_service_name(int port); const char *udp_service_name(int port); const char *oproto_service_name(int protocol_number); #endif ================================================ FILE: src/out-text.c ================================================ #include "output.h" #include "masscan.h" #include "masscan-app.h" #include "masscan-status.h" #include "unusedparm.h" #include /**************************************************************************** ****************************************************************************/ static void text_out_open(struct Output *out, FILE *fp) { UNUSEDPARM(out); fprintf(fp, "#masscan\n"); } /**************************************************************************** ****************************************************************************/ static void text_out_close(struct Output *out, FILE *fp) { UNUSEDPARM(out); fprintf(fp, "# end\n"); } /**************************************************************************** ****************************************************************************/ static void text_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { ipaddress_formatted_t fmt = ipaddress_fmt(ip); UNUSEDPARM(ttl); UNUSEDPARM(reason); UNUSEDPARM(out); fprintf(fp, "%s %s %u %s %u\n", status_string(status), name_from_ip_proto(ip_proto), port, fmt.string, (unsigned)timestamp ); } /*************************************** ************************************* ****************************************************************************/ static void text_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { char banner_buffer[MAX_BANNER_LENGTH]; ipaddress_formatted_t fmt = ipaddress_fmt(ip); UNUSEDPARM(out); UNUSEDPARM(ttl); fprintf(fp, "%s %s %u %s %u %s %s\n", "banner", name_from_ip_proto(ip_proto), port, fmt.string, (unsigned)timestamp, masscan_app_to_string(proto), normalize_string(px, length, banner_buffer, sizeof(banner_buffer)) ); } /**************************************************************************** ****************************************************************************/ const struct OutputType text_output = { "txt", 0, text_out_open, text_out_close, text_out_status, text_out_banner }; ================================================ FILE: src/out-unicornscan.c ================================================ #include "output.h" #include "masscan.h" #include "masscan-app.h" #include "masscan-status.h" #include "unusedparm.h" #include "out-tcp-services.h" static void unicornscan_out_open(struct Output *out, FILE *fp) { UNUSEDPARM(out); fprintf(fp, "#masscan\n"); } static void unicornscan_out_close(struct Output *out, FILE *fp) { UNUSEDPARM(out); fprintf(fp, "# end\n"); } static void unicornscan_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { ipaddress_formatted_t fmt = ipaddress_fmt(ip); UNUSEDPARM(reason); UNUSEDPARM(out); UNUSEDPARM(timestamp); if (ip_proto == 6) { fprintf(fp,"TCP %s\t%16s[%5d]\t\tfrom %s ttl %-3d\n", status_string(status), tcp_service_name(port), port, fmt.string, ttl); } else { /* unicornscan is TCP only, so just use grepable format for other protocols */ fprintf(fp, "Host: %s ()", fmt.string); fprintf(fp, "\tPorts: %u/%s/%s/%s/%s/%s/%s\n", port, status_string(status), //"open", "closed" name_from_ip_proto(ip_proto), //"tcp", "udp", "sctp" "", //owner "", //service "", //SunRPC info "" //Version info ); } } /*************************************** ************************************* ****************************************************************************/ static void unicornscan_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { /* SYN only - no banner */ UNUSEDPARM(out); UNUSEDPARM(ttl); UNUSEDPARM(port); UNUSEDPARM(fp); UNUSEDPARM(timestamp); UNUSEDPARM(ip); UNUSEDPARM(ip_proto); UNUSEDPARM(proto); UNUSEDPARM(px); UNUSEDPARM(length); return; } /**************************************************************************** ****************************************************************************/ const struct OutputType unicornscan_output = { "uni", 0, unicornscan_out_open, unicornscan_out_close, unicornscan_out_status, unicornscan_out_banner }; ================================================ FILE: src/out-xml.c ================================================ #include "output.h" #include "masscan-app.h" #include "masscan-status.h" #include "util-safefunc.h" /**************************************************************************** ****************************************************************************/ static void xml_out_open(struct Output *out, FILE *fp) { //const struct Masscan *masscan = out->masscan; fprintf(fp, "\r\n"); fprintf(fp, "\r\n"); if (out->xml.stylesheet && out->xml.stylesheet[0]) { fprintf(fp, "\r\n", out->xml.stylesheet); } fprintf(fp, "\r\n", "masscan", (unsigned)time(0), "1.0-BETA", "1.03" /* xml output version I copied from their site */ ); fprintf(fp, "\r\n", "syn", "tcp" ); } /**************************************************************************** ****************************************************************************/ static void xml_out_close(struct Output *out, FILE *fp) { char buffer[256]; time_t now = time(0); struct tm tm; if (out->is_gmt) safe_gmtime(&tm, &now); else safe_localtime(&tm, &now); strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); fprintf(fp, "\r\n" "\r\n" "\r\n" "\r\n" "\r\n", (unsigned)now, /* time */ buffer, /* timestr */ (unsigned)(now - out->rotate.last), /* elapsed */ out->counts.tcp.open, out->counts.tcp.closed, out->counts.tcp.open + out->counts.tcp.closed ); } /**************************************************************************** ****************************************************************************/ static void xml_out_status(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl) { char reason_buffer[128]; ipaddress_formatted_t fmt = ipaddress_fmt(ip); UNUSEDPARM(out); fprintf(fp, "" "
" "" "" "" "" "" "" "\r\n", (unsigned)timestamp, fmt.string, name_from_ip_proto(ip_proto), port, status_string(status), reason_string(reason, reason_buffer, sizeof(reason_buffer)), ttl ); } /**************************************************************************** ****************************************************************************/ static void xml_out_banner(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length) { char banner_buffer[MAX_BANNER_LENGTH]; const char *reason; ipaddress_formatted_t fmt = ipaddress_fmt(ip); switch (proto) { case 6: reason = "syn-ack"; break; default: reason = "response"; break; } UNUSEDPARM(out); fprintf(fp, "" "
" "" "" "" "" "" "" "" "\r\n", (unsigned)timestamp, fmt.string, name_from_ip_proto(ip_proto), port, reason, ttl, masscan_app_to_string(proto), normalize_string(px, length, banner_buffer, sizeof(banner_buffer)) ); } /**************************************************************************** ****************************************************************************/ const struct OutputType xml_output = { "xml", 0, xml_out_open, xml_out_close, xml_out_status, xml_out_banner }; ================================================ FILE: src/output.c ================================================ /* output logging/reporting This is the file that formats the output files -- that is to say, where we report everything we find. PLUGINS The various types of output (XML, binary, Redis, etc.) are written vaguely as "plugins", which means as a structure with function pointers. In the future, it should be possible to write plugins as DDLs/shared-objects and load them at runtime, but right now, they are just hard coded. ROTATE Files can be "rotated". This is done by prefixing the file with the date/time when the file was created. A key feature of this design is to prevent files being lost during rotation. Therefore, the files are renamed while they are still open. If the rename function fails, then the file is left in-place and still open for writing, with continued appending to the file. Thus, you could start the program logging to "--rotate-dir ../foobar" and then notice the error messages saying that rotating isn't working, then go create the "foobar" directory, at which point rotating will now work -- it's just that the first rotated file will contain several periods of data. */ /* Needed for Linux to make offsets 64 bits */ #define _FILE_OFFSET_BITS 64 #include "output.h" #include "masscan.h" #include "masscan-status.h" #include "proto-banner1.h" #include "masscan-app.h" #include "main-globals.h" #include "pixie-file.h" #include "pixie-sockets.h" #include "util-malloc.h" #include "util-errormsg.h" #include "util-logger.h" #include #include #include /* Put this at the bottom of the include lists because of warnings */ #include "util-safefunc.h" /***************************************************************************** *****************************************************************************/ static int64_t ftell_x(FILE *fp) { #if defined(WIN32) && defined(__GNUC__) return ftello64(fp); #elif defined(WIN32) && defined(_MSC_VER) return _ftelli64(fp); #else return ftello(fp); #endif } /***************************************************************************** * The 'status' variable contains both the open/closed info as well as the * protocol info. This splits it back out into two values. *****************************************************************************/ const char * name_from_ip_proto(unsigned ip_proto) { switch (ip_proto) { case 0: return "arp"; case 1: return "icmp"; case 6: return "tcp"; case 17: return "udp"; case 132: return "sctp"; default: return "err"; } } /***************************************************************************** * The actual 'status' variable is narrowly defined depending on the * underlying protocol. This function creates a gross "open" v. "closed" * string based on the narrow variable. *****************************************************************************/ const char * status_string(enum PortStatus status) { switch (status) { case PortStatus_Open: return "open"; case PortStatus_Closed: return "closed"; case PortStatus_Arp: return "up"; default: return "unknown"; } } /***************************************************************************** * Convert TCP flags into an nmap-style "reason" string *****************************************************************************/ const char * reason_string(int x, char *buffer, size_t sizeof_buffer) { snprintf(buffer, sizeof_buffer, "%s%s%s%s%s%s%s%s", (x&0x01)?"fin-":"", (x&0x02)?"syn-":"", (x&0x04)?"rst-":"", (x&0x08)?"psh-":"", (x&0x10)?"ack-":"", (x&0x20)?"urg-":"", (x&0x40)?"ece-":"", (x&0x80)?"cwr-":"" ); if (buffer[0] == '\0') return "none"; else buffer[strlen(buffer)-1] = '\0'; return buffer; } /***************************************************************************** * Remove bad characters from the banner, especially new lines and HTML * control codes. *****************************************************************************/ const char * normalize_string(const unsigned char *px, size_t length, char *buf, size_t buf_len) { size_t i=0; size_t offset = 0; for (i=0; i>4]; buf[offset++] = "0123456789abcdef"[px[i]&0xF]; } } } buf[offset] = '\0'; return buf; } /***************************************************************************** * PORTABILITY: WINDOWS * * Windows POSIX functions open the file without the "share-delete" flag, * meaning they can't be renamed while open. Therefore, we need to * construct our own open flag. *****************************************************************************/ static FILE * open_rotate(struct Output *out, const char *filename) { FILE *fp = 0; unsigned is_append = out->is_append; int x; /* * KLUDGE: do something special for redis */ if (out->format == Output_Redis) { ptrdiff_t fd = out->redis.fd; if (fd < 1) { struct sockaddr_in sin = {0}; fd = (ptrdiff_t)socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { LOG(0, "redis: socket() failed to create socket\n"); exit(1); } sin.sin_addr.s_addr = htonl(out->redis.ip.ipv4); /* TODO: IPv6 */ sin.sin_port = htons((unsigned short)out->redis.port); sin.sin_family = AF_INET; x = connect((SOCKET)fd, (struct sockaddr*)&sin, sizeof(sin)); if (x != 0) { LOG(0, "redis: connect() failed\n"); perror("connect"); } out->redis.fd = fd; } out->funcs->open(out, (FILE*)fd); return (FILE*)fd; } /* Do something special for the "-" filename */ if (filename[0] == '-' && filename[1] == '\0') fp = stdout; /* open a "shareable" file. On Windows, by default files can't be renamed * while they are open, so we need a special function that takes care * of this. */ if (fp == 0) { x = pixie_fopen_shareable(&fp, filename, is_append); if (x != 0 || fp == NULL) { fprintf(stderr, "out: could not open file for %s\n", is_append?"appending":"writing"); perror(filename); is_tx_done = 1; return NULL; } } /* * Mark the file as newly opened. That way, before writing any data * to it, we'll first have to write headers */ out->is_virgin_file = 1; return fp; } /***************************************************************************** * Write the remaining data the file and close it. This function is * called "rotate", but it doesn't actually rotate, this name just reflects * how it's used in the rotate process. *****************************************************************************/ static void close_rotate(struct Output *out, FILE *fp) { if (out == NULL) return; if (fp == NULL) return; /* * Write the format-specific trailers, like */ if (!out->is_virgin_file) out->funcs->close(out, fp); memset(&out->counts, 0, sizeof(out->counts)); /* Redis Kludge*/ if (out->format == Output_Redis) return; fflush(fp); fclose(fp); } /***************************************************************************** * Returns the time when the next rotate should occur. Rotations are * aligned to the period, which means that if you rotate hourly, it's done * on the hour every hour, like at 9:00:00 o'clock exactly. In other words, * a period of "hourly" doesn't really mean "every 60 minutes", but * on the hour". Since the program will be launched midway in a period, * that means the first rotation will happen in less than a full period. *****************************************************************************/ static time_t next_rotate_time(time_t last_rotate, unsigned period, unsigned offset) { time_t next; next = last_rotate - (last_rotate % period) + period + offset; return next; } #if 0 /***************************************************************************** *****************************************************************************/ static int ends_with(const char *filename, const char *extension) { if (filename == NULL || extension == NULL) return 0; if (strlen(filename) + 1 < strlen(extension)) return 0; if (memcmp(filename + strlen(filename) - strlen(extension), extension, strlen(extension)) != 0) return 0; if (filename[strlen(filename) - strlen(extension) - 1] != '.') return 0; return 1; } #endif /***************************************************************************** * strdup(): compilers don't like strdup(), so I just write my own here. I * should probably find a better solution. *****************************************************************************/ static char * duplicate_string(const char *str) { size_t length; char *result; /* Find the length of the string. We allow NULL strings, in which case * the length is zero */ if (str == NULL) length = 0; else length = strlen(str); /* Allocate memory for the string */ result = MALLOC(length + 1); /* Copy the string */ if (str) memcpy(result, str, length+1); result[length] = '\0'; return result; } /***************************************************************************** * Adds the index variable to just before the file extension. For example, * if the original filename is "foo.bar", and the index is 1, then the * new filename becomes "foo.01.bar". By putting the index before the * extension, it preserves the file type. By prepending a zero on the index, * it allows up to 100 files while still being able to easily sort the files. *****************************************************************************/ static char * indexed_filename(const char *filename, unsigned index) { size_t len = strlen(filename); size_t ext; char *new_filename; size_t new_length = strlen(filename) + 32; /* find the extension */ ext = len; while (ext) { ext--; if (filename[ext] == '.') break; if (filename[ext] == '/' || filename[ext] == '\\') { /* no dot found, so ext is end of file */ ext = len; break; } } if (ext == 0 && len > 0 && filename[0] != '.') ext = len; /* allocate memory */ new_filename = MALLOC(new_length); /* format the new name */ snprintf(new_filename, new_length, "%.*s.%02u%s", (unsigned)ext, filename, index, filename+ext); return new_filename; } /***************************************************************************** * Create an "output" structure. If we are writing a file, we create the * file now, so that any errors creating the file are caught immediately, * rather than later in the scan when it might fail. *****************************************************************************/ struct Output * output_create(const struct Masscan *masscan, unsigned thread_index) { struct Output *out; unsigned i; /* allocate/initialize memory */ out = CALLOC(1, sizeof(*out)); out->masscan = masscan; out->when_scan_started = time(0); out->is_virgin_file = 1; /* * Copy the configuration information from the 'masscan' structure. */ out->rotate.period = masscan->output.rotate.timeout; out->rotate.offset = masscan->output.rotate.offset; out->rotate.filesize = masscan->output.rotate.filesize; out->redis.port = masscan->redis.port; out->redis.ip = masscan->redis.ip; out->redis.password = masscan ->redis.password; out->is_banner = masscan->is_banners; /* --banners */ out->is_banner_rawudp = masscan->is_banners_rawudp; /* --rawudp */ out->is_gmt = masscan->is_gmt; out->is_interactive = masscan->output.is_interactive; out->is_show_open = masscan->output.is_show_open; out->is_show_closed = masscan->output.is_show_closed; out->is_show_host = masscan->output.is_show_host; out->is_append = masscan->output.is_append; out->xml.stylesheet = duplicate_string(masscan->output.stylesheet); out->rotate.directory = duplicate_string(masscan->output.rotate.directory); if (masscan->nic_count <= 1) out->filename = duplicate_string(masscan->output.filename); else out->filename = indexed_filename(masscan->output.filename, thread_index); for (i=0; i<8; i++) { out->src[i] = masscan->nic[i].src; } /* * Link the appropriate output module. * TODO: support multiple output modules */ out->format = masscan->output.format; switch (out->format) { case Output_List: out->funcs = &text_output; break; case Output_Unicornscan: out->funcs = &unicornscan_output; break; case Output_XML: out->funcs = &xml_output; break; case Output_JSON: out->funcs = &json_output; break; case Output_NDJSON: out->funcs = &ndjson_output; break; case Output_Certs: out->funcs = &certs_output; break; case Output_Binary: out->funcs = &binary_output; break; case Output_Grepable: out->funcs = &grepable_output; break; case Output_Redis: out->funcs = &redis_output; break; case Output_Hostonly: out->funcs = &hostonly_output; break; case Output_None: out->funcs = &null_output; break; default: out->funcs = &null_output; break; } /* * Open the desired output file. We do this now at the start of the scan * so that we can immediately notify the user of an error, rather than * waiting midway through a long scan and have it fail. */ if (masscan->output.filename[0] && out->funcs != &null_output) { FILE *fp; fp = open_rotate(out, masscan->output.filename); if (fp == NULL) { perror(masscan->output.filename); exit(1); } out->fp = fp; out->rotate.last = time(0); } /* * Set the time of the next rotation. If we aren't rotating files, then * this time will be set at "infinity" in the future. * TODO: this code isn't Y2036 compliant. */ if (masscan->output.rotate.timeout == 0) { /* TODO: how does one find the max time_t value??*/ out->rotate.next = (time_t)LONG_MAX; } else { if (out->rotate.offset > 1) { out->rotate.next = next_rotate_time( out->rotate.last-out->rotate.period, out->rotate.period, out->rotate.offset); } else { out->rotate.next = next_rotate_time( out->rotate.last, out->rotate.period, out->rotate.offset); } } return out; } /***************************************************************************** * Rotate the file, moving it from the local directory to a remote directory * and changing the name to include the timestamp. This is done while the file * is still open: we move the file and rename it first, then close it. *****************************************************************************/ static FILE * output_do_rotate(struct Output *out, int is_closing) { const char *dir; const char *filename; char *new_filename; size_t new_filename_size; struct tm tm; int err; /* Don't do anything if there is no file */ if (out == NULL || out->fp == NULL) return NULL; dir = out->rotate.directory; filename = out->filename; /* Make sure that all output has been flushed to the file */ fflush(out->fp); /* Remove directory prefix from filename, we just want the root filename * to start with */ while (strchr(filename, '/')) { filename = strchr(filename, '/'); if (*filename == '/') filename++; } while (strchr(filename, '\\')) { filename = strchr(filename, '\\'); if (*filename == '\\') filename++; } /* Allocate memory for the new filename */ new_filename_size = strlen(dir) + strlen("/") + strlen(filename) + strlen("1308201101-") + strlen(filename) + 1 /* - */ + 1; /* null */ new_filename = MALLOC(new_filename_size); /* Get the proper timestamp for the file */ if (out->is_gmt) { err = safe_gmtime(&tm, &out->rotate.last); } else { err = safe_localtime(&tm, &out->rotate.last); } if (err != 0) { free(new_filename); perror("gmtime(): file rotation ended"); return out->fp; } /* Look for a name that doesn't collide with an exist name. If the desired * file already exists, then increment the filename. This should never * happen. */ err = 0; again: if (out->rotate.filesize) { size_t x_off=0, x_len=0; if (strrchr(filename, '.')) { x_off = strrchr(filename, '.') - filename; x_len = strlen(filename + x_off); } else { x_off = strlen(filename); x_len = 0; } snprintf(new_filename, new_filename_size, "%s/%.*s-%05u%.*s", dir, (unsigned)x_off, filename, out->rotate.filecount++, (unsigned)x_len, filename + x_off ); } else { snprintf(new_filename, new_filename_size, "%s/%02u%02u%02u-%02u%02u%02u" "-%s", dir, tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, filename); if (access(new_filename, 0) == 0) { tm.tm_sec++; if (err++ == 0) goto again; } } filename = out->filename; /* * Move the file */ err = rename(filename, new_filename); if (err) { LOG(0, "rename(\"%s\", \"%s\"): failed\n", filename, new_filename); perror("rename()"); free(new_filename); return out->fp; } /* * Set the next rotate time, which is the current time plus the period * length */ out->rotate.bytes_written = 0; if (out->rotate.period) { out->rotate.next = next_rotate_time(time(0), out->rotate.period, out->rotate.offset); } LOG(1, "rotated: %s\n", new_filename); free(new_filename); /* * Now create a new file */ if (is_closing) out->fp = NULL; /* program shutting down, so don't create new file */ else { FILE *fp; fp = open_rotate(out, filename); if (fp == NULL) { LOG(0, "rotate: %s: failed: %s\n", filename, strerror(errno)); } else { close_rotate(out, out->fp); out->fp = fp; out->rotate.last = time(0); LOG(1, "rotate: started new file: %s\n", filename); } } return out->fp; } /*************************************************************************** ***************************************************************************/ static int is_rotate_time(const struct Output *out, time_t now, FILE *fp) { if (out->is_virgin_file) return 0; if (now >= out->rotate.next) return 1; if (out->rotate.filesize != 0 && ftell_x(fp) >= (int64_t)out->rotate.filesize) return 1; return 0; } /*************************************************************************** * Return the vendor/OUI string matching the first three bytes of a * MAC address. * TODO: this should be read in from a file ***************************************************************************/ static const char * oui_from_mac(const unsigned char mac[6]) { unsigned oui = mac[0]<<16 | mac[1]<<8 | mac[2]; switch (oui) { case 0x0001c0: return "Compulab"; case 0x000732: return "Aaeon"; case 0x00089B: return "ICPelec"; case 0x000c29: return "VMware"; case 0x001075: return "Seagate"; case 0x001132: return "Synology"; case 0x022618: return "Asus"; case 0x0022b0: return "D-Link"; case 0x00236c: return "Apple"; case 0x0016CB: return "Apple"; case 0x001e06: return "Odroid"; case 0x001ff3: return "Apple"; case 0x002590: return "Supermicro"; case 0x04421A: return "Asus"; case 0x08cc68: return "Cisco"; case 0x0C9D92: return "Asus"; case 0x244BFE: return "Asus"; case 0x244CE3: return "Amazon"; case 0x28CDC1: return "RPi 22-02"; case 0x2c27d7: return "HP"; case 0x3497f6: return "Asus"; case 0x38f73d: return "Amazon"; case 0x3A3541: return "RPi 19-12"; case 0x3C22FB: return "Apple"; case 0x404a03: return "Zyxel"; case 0x4C9EFF: return "Zyxel"; case 0x5855CA: return "Apple"; case 0x60a44c: return "Asus"; case 0x640BD7: return "Apple"; case 0x6c72e7: return "Apple"; case 0x8C3BAD: return "Netgear"; case 0x9003b7: return "Parrot"; case 0x9009d0: return "Synology"; case 0x94A408: return "Trolink"; case 0x94dbc9: return "Azurewave"; case 0xA0CEC8: return "CeLink"; case 0xacbc32: return "Apple"; case 0xb827eb: return "RPi 12-03"; case 0xc05627: return "Belkin"; case 0xc0c1c0: return "Cisco-Linksys"; case 0xD83ADD: return "RPi 22-11"; case 0xDCA4CA: return "Apple"; case 0xdca632: return "RPi 19-03"; case 0xE45F01: return "RPi 20-07"; case 0xe4956e: return "[random]"; case 0xFCECDA: return "Ubiquiti"; default: return ""; } } /*************************************************************************** * Report simply "open" or "closed", with little additional information. * This is called directly from the receive thread when responses come * back. ***************************************************************************/ void output_report_status(struct Output *out, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl, const unsigned char mac[6]) { FILE *fp = out->fp; time_t now = time(0); ipaddress_formatted_t fmt = ipaddress_fmt(ip); global_now = now; /* if "--open"/"--open-only" parameter specified on command-line, then * don't report the status of closed-ports */ if (!out->is_show_closed && status == PortStatus_Closed) return; if (!out->is_show_open && status == PortStatus_Open) return; /* If in "--interactive" mode, then print the banner to the command * line screen */ if (out->is_interactive || out->format == 0 || out->format == Output_Interactive) { unsigned count; switch (ip_proto) { case 0: /* ARP */ count = fprintf(stdout, "Discovered %s port %u/%s on %s (%02x:%02x:%02x:%02x:%02x:%02x) %s", status_string(status), port, name_from_ip_proto(ip_proto), fmt.string, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], oui_from_mac(mac) ); break; default: count = fprintf(stdout, "Discovered %s port %u/%s on %s", status_string(status), port, name_from_ip_proto(ip_proto), fmt.string ); } /* Because this line may overwrite the "%done" status line, print * some spaces afterward to completely cover up the line */ if (count < 80) fprintf(stdout, "%.*s", (int)(79-count), " " " "); fprintf(stdout, "\n"); fflush(stdout); } else if (fp == NULL) { ERRMSG("no output file, use `--output-filename ` to set one\n"); ERRMSG("for `stdout`, use `--output-filename -`\n"); return; } /* Rotate, if we've pass the time limit. Rotating the log files happens * inline while writing output, whenever there's output to write to the * file, rather than in a separate thread right at the time interval. * Thus, if results are coming in slowly, the rotation won't happen * on precise boundaries */ if (is_rotate_time(out, now, fp)) { fp = output_do_rotate(out, 0); if (fp == NULL) return; } /* Keep some statistics so that the user can monitor how much stuff is * being found. */ switch (status) { case PortStatus_Open: switch (ip_proto) { case 1: out->counts.icmp.echo++; break; case 6: out->counts.tcp.open++; break; case 17: out->counts.udp.open++; break; case 132: out->counts.sctp.open++; break; default: out->counts.oproto.open++; break; } if (!out->is_show_open) return; break; case PortStatus_Closed: switch (ip_proto) { case 6: out->counts.tcp.closed++; break; case 17: out->counts.udp.closed++; break; case 132: out->counts.sctp.closed++; break; } if (!out->is_show_closed) return; break; case PortStatus_Arp: out->counts.arp.open++; break; default: LOG(0, "unknown status type: %u\n", status); return; } /* * If this is a newly opened file, then write file headers */ if (out->is_virgin_file) { out->funcs->open(out, fp); out->is_virgin_file = 0; } /* * Now do the actual output, whether it be XML, binary, JSON, ndjson, Redis, * and so on. */ out->funcs->status(out, fp, timestamp, status, ip, ip_proto, port, reason, ttl); } /*************************************************************************** ***************************************************************************/ void output_report_banner(struct Output *out, time_t now, ipaddress ip, unsigned ip_proto, unsigned port, unsigned proto, unsigned ttl, const unsigned char *px, unsigned length) { FILE *fp = out->fp; ipaddress_formatted_t fmt = ipaddress_fmt(ip); /* If we aren't doing banners, then don't do anything. That's because * when doing UDP scans, we'll still get banner information from * decoding the response packets, even if the user isn't interested */ if (!out->is_banner) return; /* If in "--interactive" mode, then print the banner to the command * line screen */ if (out->is_interactive || out->format == 0 || out->format == Output_Interactive) { unsigned count; char banner_buffer[MAX_BANNER_LENGTH]; count = fprintf(stdout, "Banner on port %u/%s on %s: [%s] %s", port, name_from_ip_proto(ip_proto), fmt.string, masscan_app_to_string(proto), normalize_string(px, length, banner_buffer, sizeof(banner_buffer)) ); /* Because this line may overwrite the "%done" status line, print * some spaces afterward to completely cover up the line */ if (count < 80) fprintf(stdout, "%.*s", (int)(79-count), " " " "); fprintf(stdout, "\n"); } /* If not outputting to a file, then don't do anything */ if (fp == NULL) return; /* Rotate, if we've pass the time limit. Rotating the log files happens * inline while writing output, whenever there's output to write to the * file, rather than in a separate thread right at the time interval. * Thus, if results are coming in slowly, the rotation won't happen * on precise boundaries */ if (is_rotate_time(out, now, fp)) { fp = output_do_rotate(out, 0); if (fp == NULL) return; } /* * If this is a newly opened file, then write file headers */ if (out->is_virgin_file) { out->funcs->open(out, fp); out->is_virgin_file = 0; } /* * Now do the actual output, whether it be XML, binary, JSON, ndjson, Redis, * and so on. */ out->funcs->banner(out, fp, now, ip, ip_proto, port, proto, ttl, px, length); } /*************************************************************************** * Called on exit of the program to close/free everything ***************************************************************************/ void output_destroy(struct Output *out) { if (out == NULL) return; /* If rotating files, then do one last rotate of this file to the * destination directory */ if (out->rotate.period || out->rotate.filesize) { LOG(1, "doing finale rotate\n"); output_do_rotate(out, 1); } /* If not rotating files, then simply close this file. Remember * that some files will write closing information before closing * the file */ if (out->fp) close_rotate(out, out->fp); free(out->xml.stylesheet); free(out->rotate.directory); free(out->filename); free(out); } /***************************************************************************** * Regression tests for this unit. *****************************************************************************/ int output_selftest(void) { char *f; f = indexed_filename("foo.bar", 1); if (strcmp(f, "foo.01.bar") != 0) { fprintf(stderr, "output: failed selftest\n"); return 1; } free(f); f = indexed_filename("foo.b/ar", 2); if (strcmp(f, "foo.b/ar.02") != 0) { fprintf(stderr, "output: failed selftest\n"); return 1; } free(f); f = indexed_filename(".foobar", 3); if (strcmp(f, ".03.foobar") != 0) { fprintf(stderr, "output: failed selftest\n"); return 1; } free(f); return 0; } ================================================ FILE: src/output.h ================================================ #ifndef OUTPUT_H #define OUTPUT_H #include #include #include #include #include "massip-addr.h" #include "stack-src.h" #include "unusedparm.h" #include "masscan-app.h" #define MAX_BANNER_LENGTH 8192 struct Masscan; struct Output; enum ApplicationProtocol; enum PortStatus; /** * Output plugins * * The various means for writing output are essentially plugins. As new methods * are created, we just fill in a structure of function pointers. * TODO: this needs to be a loadable DLL, but in the meantime, it's just * internal structures. */ struct OutputType { const char *file_extension; void *(*create)(struct Output *out); void (*open)(struct Output *out, FILE *fp); void (*close)(struct Output *out, FILE *fp); void (*status)(struct Output *out, FILE *fp, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl); void (*banner)(struct Output *out, FILE *fp, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, enum ApplicationProtocol proto, unsigned ttl, const unsigned char *px, unsigned length); }; /** * Masscan creates one "output" structure per thread. */ struct Output { const struct Masscan *masscan; char *filename; struct stack_src_t src[8]; FILE *fp; const struct OutputType *funcs; unsigned format; /** * The timestamp when this scan started. This is preserved in output files * because that's what nmap does, and a lot of tools parse this. */ time_t when_scan_started; /** * Whether we've started writing to a file yet. We are lazy writing * the file header until we've actually go something to write */ unsigned is_virgin_file:1; /** * used by json output to test if the first record has been seen, in order * to determine if it needs a , comma before the record */ unsigned is_first_record_seen:1; struct { time_t next; time_t last; unsigned period; unsigned offset; uint64_t filesize; uint64_t bytes_written; unsigned filecount; /* filesize rotates */ char *directory; } rotate; unsigned is_banner:1; /* --banners */ unsigned is_banner_rawudp:1; /* --rawudp */ unsigned is_gmt:1; /* --gmt */ unsigned is_interactive:1; /* echo to command line */ unsigned is_show_open:1; /* show open ports (default) */ unsigned is_show_closed:1; /* show closed ports */ unsigned is_show_host:1; /* show host status info, like up/down */ unsigned is_append:1; /* append to file */ struct { struct { uint64_t open; uint64_t closed; uint64_t banner; } tcp; struct { uint64_t open; uint64_t closed; } udp; struct { uint64_t open; uint64_t closed; } sctp; struct { uint64_t echo; uint64_t timestamp; } icmp; struct { uint64_t open; } arp; struct { uint64_t open; uint64_t closed; } oproto; } counts; struct { ipaddress ip; unsigned port; char *password; ptrdiff_t fd; uint64_t outstanding; unsigned state; } redis; struct { char *stylesheet; } xml; }; const char *name_from_ip_proto(unsigned ip_proto); const char *status_string(enum PortStatus x); const char *reason_string(int x, char *buffer, size_t sizeof_buffer); const char *normalize_string(const unsigned char *px, size_t length, char *buf, size_t buf_len); extern const struct OutputType text_output; extern const struct OutputType unicornscan_output; extern const struct OutputType xml_output; extern const struct OutputType json_output; extern const struct OutputType ndjson_output; extern const struct OutputType certs_output; extern const struct OutputType binary_output; extern const struct OutputType null_output; extern const struct OutputType redis_output; extern const struct OutputType hostonly_output; extern const struct OutputType grepable_output; /** * Creates an "output" object. This is called by the receive thread in order * to send "status" information (open/closed ports) and "banners" to either * the command-line or to files in specific formats, such as XML or Redis * @param masscan * The master configuration. * @param thread_index * When there are more than one receive threads, they are differentiated * by this index number. * @return * an output object that must eventually be destroyed by output_destroy(). */ struct Output * output_create(const struct Masscan *masscan, unsigned thread_index); void output_destroy(struct Output *output); void output_report_status(struct Output *output, time_t timestamp, int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl, const unsigned char mac[6]); typedef void (*OUTPUT_REPORT_BANNER)( struct Output *output, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, unsigned proto, unsigned ttl, const unsigned char *px, unsigned length); void output_report_banner( struct Output *output, time_t timestamp, ipaddress ip, unsigned ip_proto, unsigned port, unsigned proto, unsigned ttl, const unsigned char *px, unsigned length); /** * Regression tests this unit. * @return * 0 on success, or positive integer on failure */ int output_selftest(void); #endif ================================================ FILE: src/pixie-backtrace.c ================================================ /* When program crashes, print backtrace with line numbers */ #include "pixie-backtrace.h" #include "unusedparm.h" #include #include #include #include char global_self[512] = ""; #if defined(__GLIBC__) && !defined(WIN32) #include #include #include #define BACKTRACE_SIZE 256 static void handle_segfault(int sig) { void *func[BACKTRACE_SIZE]; char **symb = NULL; int size; printf("======================================================================\n"); printf(" Segmentation fault: please post this backtrace to:\n"); printf(" https://github.com/robertdavidgraham/masscan/issues\n"); printf("======================================================================\n"); size = backtrace(func, BACKTRACE_SIZE); symb = backtrace_symbols(func, size); while (size > 0) { const char *symbol = symb[size - 1]; char foo[1024]; printf("%d: [%s]\n", size, symbol); if (strstr(symbol, "[0x")) { char *p = strstr(symbol, "[0x") + 1; char *pp = strchr(p, ']'); snprintf(foo, sizeof(foo), "addr2line -p -i -f -e %s %.*s", global_self, (unsigned)(pp-p), p); if (system(foo) == -1) printf("(addr2line missing)\n"); } size --; } exit(1); return; } /*************************************************************************** ***************************************************************************/ void pixie_backtrace_finish(void) { } /*************************************************************************** ***************************************************************************/ void pixie_backtrace_init(const char *self) { ssize_t x; /* Need to get a handle to the currently executing program. On Linux, * we'll get this with /proc/self/exe, but on other platforms, we may * need to do other things */ /* TODO: should we use readlink() to get the actual filename? */ #if defined(__linux__) x = readlink("/proc/self/exe", global_self, sizeof(global_self)); #elif defined(__FreeBSD__) x = readlink("/proc/curproc/file", global_self, sizeof(global_self)); #elif defined(__Solaris__) x = readlink("/proc/self/path/a.out", global_self, sizeof(global_self)); #else x = -1; #endif if (x == -1) snprintf(global_self, sizeof(global_self), "%s", self); signal(SIGSEGV, handle_segfault); } #elif defined(__MINGW32__) void pixie_backtrace_init(const char *self) { } #elif defined(WIN32) #include typedef struct _SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; // Type Index of symbol ULONG64 Reserved[2]; ULONG Index; ULONG Size; ULONG64 ModBase; // Base Address of module containing this symbol ULONG Flags; ULONG64 Value; // Value of symbol, ValuePresent should be 1 ULONG64 Address; // Address of symbol including base address of module ULONG Register; // register holding value or pointer to value ULONG Scope; // scope of the symbol ULONG Tag; // pdb classification ULONG NameLen; // Actual length of name ULONG MaxNameLen; CHAR Name[1]; // Name of symbol } SYMBOL_INFO, *PSYMBOL_INFO; typedef BOOL (__stdcall *FUNC_SymInitialize)( HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess ); typedef BOOL (__stdcall * FUNC_SymFromAddr)( HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol ); struct _Dbg { FUNC_SymInitialize SymInitialize; FUNC_SymFromAddr SymFromAddr; } Dbg; void printStack( void ); void printStack( void ) { unsigned int i; void * stack[ 100 ]; unsigned short frames; SYMBOL_INFO * symbol; HANDLE process; process = GetCurrentProcess(); if (Dbg.SymInitialize == NULL) return; if (Dbg.SymFromAddr == NULL) return; if (RtlCaptureStackBackTrace == NULL) return; Dbg.SymInitialize( process, NULL, TRUE ); frames = CaptureStackBackTrace( 0, 100, stack, NULL ); symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 ); symbol->MaxNameLen = 255; symbol->SizeOfStruct = sizeof( SYMBOL_INFO ); for( i = 0; i < frames; i++ ) { Dbg.SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol ); printf( "%u: %s - 0x%0X\n", frames - i - 1, symbol->Name, symbol->Address ); } free( symbol ); } static void handle_segfault(int sig) { UNUSEDPARM(sig); printf("======================================================================"); printf(" Segmentation fault: please post this backtrace to:\n"); printf(" https://github.com/robertdavidgraham/masscan/issues\n"); printf("======================================================================"); exit(1); } void pixie_backtrace_init(const char *self) { self; GetModuleFileNameA(NULL, global_self, sizeof(global_self)); { HMODULE h; h = LoadLibraryA("DbgHelp.dll"); if (h != NULL) { //printf("found DbgHelp.dll\n"); Dbg.SymFromAddr = (FUNC_SymFromAddr)GetProcAddress(h, "SymFromAddr"); //if (Dbg.SymFromAddr) printf("found Dbg.SymFromAddr\n"); Dbg.SymInitialize = (FUNC_SymInitialize)GetProcAddress(h, "SymInitialize"); //if (Dbg.SymInitialize) printf("found Dbg.SymInitialize\n"); h = LoadLibraryA("Kernel32.dll"); if (GetProcAddress(NULL, "RtlCaptureStackBackTrace") != NULL) ; //printf("found Dbg.SymInitialize\n"); } } //signal(SIGSEGV, handle_segfault); } #else void pixie_backtrace_init(const char *self) { } #endif ================================================ FILE: src/pixie-backtrace.h ================================================ #ifndef PIXIE_BACKTRACE_H #define PIXIE_BACKTRACE_H /** * Call this function at program startup in order to insert a signal handler * that will be caught when the program crashes. This signal handler will * print debug information to the console, such as the line numbers where * the program crashes. */ void pixie_backtrace_init(const char *self); #endif ================================================ FILE: src/pixie-file.c ================================================ #include "pixie-file.h" #if defined(WIN32) #include #include #include #define access _access #else #include #include #endif int pixie_fopen_shareable(FILE **in_fp, const char *filename, unsigned is_append) { FILE *fp; *in_fp = NULL; #if defined(WIN32) /* PORTABILITY: WINDOWS * This bit of code deals with the fact that on Windows, fopen() opens * a file so that it can't be moved. This code opens it a different * way so that we can move it. * * NOTE: this is probably overkill, it appears that there is a better * API _fsopen() that does what I want without all this nonsense. */ { HANDLE hFile; int fd; /* The normal POSIX C functions lock the file */ /* int fd = open(filename, O_RDWR | O_CREAT, _S_IREAD | _S_IWRITE); */ /* Fails */ /* int fd = _sopen(filename, O_RDWR | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); */ /* Also fails */ /* We need to use WINAPI + _open_osfhandle to be able to use file descriptors (instead of WINAPI handles) */ hFile = CreateFileA( filename, GENERIC_WRITE | (is_append?FILE_APPEND_DATA:0), FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); if (hFile == INVALID_HANDLE_VALUE) { return -1; } fd = _open_osfhandle((intptr_t)hFile, _O_CREAT | _O_RDONLY | _O_TEMPORARY); if (fd == -1) { perror("_open_osfhandle"); return -1; } fp = _fdopen(fd, "w"); } #else fp = fopen(filename, is_append?"a":"w"); if (fp == NULL) return errno; #endif *in_fp = fp; return 0; } ================================================ FILE: src/pixie-file.h ================================================ #ifndef PIXIE_FILE_H #define PIXIE_FILE_H #include #if defined(WIN32) #include #define access _access #else #include #endif /** * On Windows, files aren't shareable, so we need to have a portable function * to open files that can be shared and renamed while they are still open. */ int pixie_fopen_shareable(FILE **in_fp, const char *filename, unsigned is_append); #endif ================================================ FILE: src/pixie-sockets.h ================================================ #ifndef PIXIE_SOCKETS_H #define PIXIE_SOCKETS_H #include #if defined(WIN32) #include #else #include #include #include typedef int SOCKET; #endif #endif ================================================ FILE: src/pixie-threads.c ================================================ #define _GNU_SOURCE #include "pixie-threads.h" #if defined(WIN32) #include #include #endif #if defined(__GNUC__) && !defined(WIN32) #include #include #include #include #endif #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include #include #endif #ifndef UNUSEDPARM #ifdef _MSC_VER #define UNUSEDPARM(x) x #else #define UNUSEDPARM(x) #endif #endif /**************************************************************************** ****************************************************************************/ void pixie_cpu_raise_priority(void) { #if defined WIN32 DWORD_PTR result; result = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); if (result == 0) { fprintf(stderr, "set_priority: returned error win32:%u\n", (unsigned)GetLastError()); } #elif defined(__linux__) && defined(__GNUC__) pthread_t thread = pthread_self(); pthread_attr_t thAttr; int policy = 0; int max_prio_for_policy = 0; pthread_attr_init(&thAttr); pthread_attr_getschedpolicy(&thAttr, &policy); max_prio_for_policy = sched_get_priority_max(policy); pthread_setschedprio(thread, max_prio_for_policy); pthread_attr_destroy(&thAttr); return; #endif } /**************************************************************************** * Set the current thread (implicit) to run exclusively on the explicit * process. * http://en.wikipedia.org/wiki/Processor_affinity ****************************************************************************/ void pixie_cpu_set_affinity(unsigned processor) { #if defined WIN32 DWORD_PTR mask; DWORD_PTR result; if (processor > 0) processor--; mask = ((size_t)1)<>= 1; } if (count == 0) return 1; else return count; #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) /* BSD - use sysctl() function */ int x; int mib[2]; size_t ncpu_length; int ncpu = 1; mib[0] = CTL_HW; mib[1] = HW_NCPU; ncpu_length = sizeof(ncpu); x = sysctl(mib, 2, &ncpu, &ncpu_length, NULL, 0); if (x == -1) { perror("sysctl(HW_NCPU) failed"); return 1; } else return (unsigned)ncpu; #elif defined linux /* http://linux.die.net/man/2/sched_getaffinity */ { pid_t pid; cpu_set_t mask; int err; /* Gegret our process ID */ pid = getpid(); /* Get list of available CPUs for our system */ err = sched_getaffinity(pid, sizeof(mask), &mask); if (err) { perror("sched_getaffinity"); return 1; } else { #ifndef CPU_COUNT return 1; #else return CPU_COUNT(&mask); #endif } } #elif defined(_SC_NPROCESSORS_ONLN) /* Linux, Solaris, Mac OS>=10.4 */ return sysconf(_SC_NPROCESSORS_ONLN); #elif defined(_SC_NPROC_ONLN) /* Irix */ return sysconf(_SC_NPROC_ONLN); #elif defined(MPC_GETNUMSPUS) return mpctl(MPC_GETNUMSPUS, 0, 0); #else #error need to find CPU count /* UNKNOWN - Well, we don't know the type of system which means we won't * be able to start multiple threads anyway, so just return '1' */ return 1; #endif } /**************************************************************************** ****************************************************************************/ size_t pixie_begin_thread( void (*worker_thread)(void*), unsigned flags, void *worker_data) { #if defined(WIN32) UNUSEDPARM(flags); return _beginthread(worker_thread, 0, worker_data); #else typedef void *(*PTHREADFUNC)(void*); pthread_t thread_id = 0; pthread_create( &thread_id, NULL, (PTHREADFUNC)worker_thread, worker_data); return (size_t)thread_id; #endif } /**************************************************************************** ****************************************************************************/ void pixie_thread_join(size_t thread_handle) { #if defined(WIN32) WaitForSingleObject((HANDLE)thread_handle, INFINITE); #else void *p; pthread_join((pthread_t)thread_handle, &p); #endif } ================================================ FILE: src/pixie-threads.h ================================================ #ifndef PORT_THREADS_H #define PORT_THREADS_H #include #include #if defined(_MSC_VER) #include #endif /** * Returns the number of CPUs in the system, including virtual CPUs. * On a single processor system, the number returned will be '1'. * On a dual socket, dual-core per socket, hyperthreaded system, the * count will be '8'. */ unsigned pixie_cpu_get_count(void); /** * Launch a thread */ size_t pixie_begin_thread(void (*worker_thread)(void*), unsigned flags, void *worker_data); void pixie_thread_join(size_t thread_handle); void pixie_cpu_set_affinity(unsigned processor); void pixie_cpu_raise_priority(void); void pixie_locked_subtract_u32(unsigned *lhs, unsigned rhs); #if defined(_MSC_VER) #define pixie_locked_add_u32(dst, src) _InterlockedExchangeAdd((volatile long*)(dst), (src)) #define pixie_locked_CAS32(dst, src, expected) (_InterlockedCompareExchange((volatile long*)dst, src, expected) == (expected)) #define pixie_locked_CAS64(dst, src, expected) (_InterlockedCompareExchange64((volatile long long*)dst, src, expected) == (expected)) #define rte_atomic32_cmpset(dst, exp, src) (_InterlockedCompareExchange((volatile long *)dst, (long)src, (long)exp)==(long)(exp)) #elif defined(__GNUC__) #define pixie_locked_add_u32(dst, src) __sync_add_and_fetch((volatile int*)(dst), (int)(src)); #define rte_atomic32_cmpset(dst, expected, src) __sync_bool_compare_and_swap((volatile int*)(dst),(int)expected,(int)src) #define pixie_locked_CAS32(dst, src, expected) __sync_bool_compare_and_swap((volatile int*)(dst),(int)expected,(int)src); #define pixie_locked_CAS64(dst, src, expected) __sync_bool_compare_and_swap((volatile long long int*)(dst),(long long int)expected,(long long int)src); #if !defined(__x86_64__) && !defined(__i386__) #define rte_wmb() __sync_synchronize() #define rte_rmb() __sync_synchronize() #define rte_pause() #else #define rte_wmb() asm volatile("sfence;" : : : "memory") #define rte_rmb() asm volatile("lfence;" : : : "memory") #define rte_pause() asm volatile ("pause") #endif #else unsigned pixie_locked_add_u32(volatile unsigned *lhs, unsigned rhs); int pixie_locked_CAS32(volatile unsigned *dst, unsigned src, unsigned expected); int pixie_locked_CAS64(volatile uint64_t *dst, uint64_t src, uint64_t expected); #endif #endif ================================================ FILE: src/pixie-timer.c ================================================ /* portability: time Since this program runs on both Linux and Windows, I need a portable way to get a high-resolution timer. NOTE: The time I'm looking for is "elapsed time" not "wall clock" time. In other words, if you put the system to sleep and wake it up a day later, this function should see no change, since time wasn't elapsing while the system was asleep. Reference: http://www.python.org/dev/peps/pep-0418/#monotonic-clocks http://www.brain-dump.org/blog/entry/107 */ #include "pixie-timer.h" #include #include #include #include #ifndef UNUSEDPARM #ifdef __GNUC__ #define UNUSEDPARM(x) #else #define UNUSEDPARM(x) x=(x) #endif #endif #if defined(WIN32) #include LARGE_INTEGER getFILETIMEoffset(void) { SYSTEMTIME s; FILETIME f; LARGE_INTEGER t; s.wYear = 1970; s.wMonth = 1; s.wDay = 1; s.wHour = 0; s.wMinute = 0; s.wSecond = 0; s.wMilliseconds = 0; SystemTimeToFileTime(&s, &f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; return (t); } int win_clock_gettime(int X, struct timeval *tv) { LARGE_INTEGER t; FILETIME f; double microseconds; static LARGE_INTEGER offset; static double frequencyToMicroseconds; static int initialized = 0; static BOOL usePerformanceCounter = 0; UNUSEDPARM(X); if (!initialized) { LARGE_INTEGER performanceFrequency; initialized = 1; usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); if (usePerformanceCounter) { QueryPerformanceCounter(&offset); frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; } else { offset = getFILETIMEoffset(); frequencyToMicroseconds = 10.; } } if (usePerformanceCounter) QueryPerformanceCounter(&t); else { GetSystemTimeAsFileTime(&f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; } t.QuadPart -= offset.QuadPart; microseconds = (double)t.QuadPart / frequencyToMicroseconds; t.QuadPart = (LONGLONG)microseconds; tv->tv_sec = (long)(t.QuadPart / 1000000); tv->tv_usec = t.QuadPart % 1000000; return (0); } uint64_t pixie_gettime(void) { //struct timeval tv; //clock_gettime(0, &tv); uint64_t time1 = 0, freq = 0; double seconds; QueryPerformanceCounter((LARGE_INTEGER *) &time1); QueryPerformanceFrequency((LARGE_INTEGER *)&freq); seconds = (double)time1/(double)freq; return (uint64_t)(seconds * 1000000.0); //return (uint64_t)tv.tv_sec * 1000000UL + tv.tv_usec; } uint64_t pixie_nanotime(void) { uint64_t time1 = 0, freq = 0; double seconds; QueryPerformanceCounter((LARGE_INTEGER *) &time1); QueryPerformanceFrequency((LARGE_INTEGER *)&freq); seconds = (double)time1/(double)freq; return (uint64_t)(seconds * 1000000000.0); } void pixie_mssleep(unsigned waitTime) { Sleep(waitTime); } void pixie_usleep(uint64_t waitTime) { /* uint64_t time1 = 0, time2 = 0, freq = 0; QueryPerformanceCounter((LARGE_INTEGER *) &time1); QueryPerformanceFrequency((LARGE_INTEGER *)&freq); do { QueryPerformanceCounter((LARGE_INTEGER *) &time2); } while((time2-time1) < waitTime); */ uint64_t start; start = pixie_gettime(); if (waitTime > 1000) Sleep((DWORD)(waitTime/1000)); while (pixie_gettime() - start < waitTime) ; } #elif defined(CLOCK_MONOTONIC) #include void pixie_mssleep(unsigned milliseconds) { pixie_usleep(milliseconds * 1000ULL); } void pixie_usleep(uint64_t microseconds) { struct timespec ts; struct timespec remaining; int err; ts.tv_sec = microseconds/1000000; ts.tv_nsec = (microseconds%1000000) * 1000; again: err = nanosleep(&ts, &remaining); if (err == -1 && errno == EINTR) { memcpy(&ts, &remaining, sizeof(ts)); goto again; } //usleep(microseconds); } uint64_t pixie_gettime(void) { int x; struct timespec tv; #if defined(CLOCK_UPTIME_RAW) /* macOS: ignores time when suspended/sleep */ x = clock_gettime(CLOCK_UPTIME_RAW, &tv); //#elif defined(CLOCK_MONOTONIC_RAW) // x = clock_gettime(CLOCK_MONOTONIC_RAW, &tv); #else x = clock_gettime(CLOCK_MONOTONIC, &tv); #endif if (x != 0) { printf("clock_gettime() err %d\n", errno); } return tv.tv_sec * 1000000 + tv.tv_nsec/1000; } uint64_t pixie_nanotime(void) { int x; struct timespec tv; #ifdef CLOCK_MONOTONIC_RAW x = clock_gettime(CLOCK_MONOTONIC_RAW, &tv); #else x = clock_gettime(CLOCK_MONOTONIC, &tv); #endif if (x != 0) { printf("clock_gettime() err %d\n", errno); } return tv.tv_sec * 1000000000 + tv.tv_nsec; } #elif defined(__MACH__) || defined(__FreeBSD__) /* works for Apple */ #include #include void pixie_usleep(uint64_t microseconds) { struct timespec t; t.tv_nsec = microseconds * 1000; if (microseconds > 1000000) t.tv_sec = microseconds/1000000; else { t.tv_sec = 0; } nanosleep(&t, 0); //usleep(microseconds); } void pixie_mssleep(unsigned milliseconds) { pixie_usleep(milliseconds * 1000ULL); } uint64_t pixie_gettime(void) { return mach_absolute_time()/1000; } uint64_t pixie_nanotime(void) { return mach_absolute_time(); } #endif /* * Timing is incredibly importatn to masscan because we need to throttle * how fast we spew packets. Every platofrm has slightly different timing * even given standard APIs. We need to make sure we have an accurate * timing function. * * This function tests betwe [0.9, 1.9] the expected results. I want something * tight, like [0.99,1.01] (plus/minus 1%), but unfortunately automated * testing platforms, like GitHub Actions, are overloaded, so when I wait * for half a second, they might actually wait for 0.7 seconds, causing * this test to fail. Thus, I have to greatly expand the range that passes * this test. */ int pixie_time_selftest(void) { static const uint64_t duration = 456789; uint64_t start, stop, elapsed; start = pixie_gettime(); pixie_usleep(duration); stop = pixie_gettime(); elapsed = stop - start; if (elapsed < 0.9 * duration) { fprintf(stderr, "timing error, long delay\n"); return 1; } if (1.9 * duration < elapsed) { fprintf(stderr, "timing error, long delay %5.0f%%\n", elapsed*100.0/duration); return 1; } return 0; } ================================================ FILE: src/pixie-timer.h ================================================ #ifndef PIXIE_TIMER_H #define PIXIE_TIMER_H #include /** * The current time, in microseconds */ uint64_t pixie_gettime(void); /** * The current time, in nanoseconds */ uint64_t pixie_nanotime(void); /** * Wait the specified number of microseconds */ void pixie_usleep(uint64_t usec); /** * Wait the specified number of milliseconds */ void pixie_mssleep(unsigned milliseconds); /** * Do a self-test. Note that in some cases, this may * actually fail when there is no problem. So far it hasn't, but I should * probably add some code to fix this. */ int pixie_time_selftest(void); #endif ================================================ FILE: src/proto-arp.c ================================================ #include "proto-arp.h" #include "proto-preprocess.h" #include "util-logger.h" #include "output.h" #include "masscan-status.h" #include "unusedparm.h" /*************************************************************************** * Process an ARP packet received in response to an ARP-scan. ***************************************************************************/ void arp_recv_response(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed) { ipaddress ip_them = parsed->src_ip; ipaddress_formatted_t fmt = ipaddress_fmt(ip_them); UNUSEDPARM(length); UNUSEDPARM(px); LOG(3, "ARP %s = [%02X:%02X:%02X:%02X:%02X:%02X]\n", fmt.string, parsed->mac_src[0], parsed->mac_src[1], parsed->mac_src[2], parsed->mac_src[3], parsed->mac_src[4], parsed->mac_src[5]); output_report_status( out, timestamp, PortStatus_Arp, ip_them, 0, /* ip proto */ 0, 0, 0, parsed->mac_src); } ================================================ FILE: src/proto-arp.h ================================================ #ifndef PROTO_ARP_H #define PROTO_ARP_H #include struct Output; struct PreprocessedInfo; void arp_recv_response(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed); #endif ================================================ FILE: src/proto-banner1.c ================================================ /* state machine for receiving banners */ #include "smack.h" #include "rawsock-pcapfile.h" #include "proto-preprocess.h" #include "stack-tcp-api.h" #include "proto-banner1.h" #include "proto-http.h" #include "proto-ssl.h" #include "proto-smb.h" #include "proto-ssh.h" #include "proto-ftp.h" #include "proto-smtp.h" #include "proto-tcp-telnet.h" #include "proto-tcp-rdp.h" #include "proto-imap4.h" #include "proto-pop3.h" #include "proto-vnc.h" #include "proto-memcached.h" #include "proto-mc.h" #include "proto-versioning.h" #include "masscan-app.h" #include "scripting.h" #include "util-malloc.h" #include "util-logger.h" #include #include #include #include struct Patterns patterns[] = { {"\x00\x00" "**" "\xff" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS, 0}, {"\x00\x00" "**" "\xfe" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS, 0}, {"\x82\x00\x00\x00", 4, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Positive Session Response */ {"\x83\x00\x00\x01\x80", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Not listening on called name */ {"\x83\x00\x00\x01\x81", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Not listening for calling name */ {"\x83\x00\x00\x01\x82", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Called name not present */ {"\x83\x00\x00\x01\x83", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Called name present, but insufficient resources */ {"\x83\x00\x00\x01\x8f", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN, 0}, /* Unspecified error */ /* ...the remainder can be in any order */ {"{\x22", 2, PROTO_MC, 0, 0}, {"SSH-1.", 6, PROTO_SSH1, SMACK_ANCHOR_BEGIN, 0}, {"SSH-2.", 6, PROTO_SSH2, SMACK_ANCHOR_BEGIN, 0}, {"HTTP/1.", 7, PROTO_HTTP, SMACK_ANCHOR_BEGIN, 0}, {"220-", 4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 0}, {"220 ", 4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 1}, {"+OK ", 4, PROTO_POP3, SMACK_ANCHOR_BEGIN, 0}, {"* OK ", 5, PROTO_IMAP4, SMACK_ANCHOR_BEGIN, 0}, {"521 ", 4, PROTO_SMTP, SMACK_ANCHOR_BEGIN, 0}, {"\x16\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"\x16\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"\x16\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"\x16\x03\x03",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"\x15\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"\x15\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"\x15\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"\x15\x03\x03",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN, 0}, {"RFB 000.000\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 1}, /* UltraVNC repeater mode */ {"RFB 003.003\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 3}, /* default version for everything */ {"RFB 003.005\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 3}, /* broken, same as 003.003 */ {"RFB 003.006\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 3}, /* broken, same as 003.003 */ {"RFB 003.007\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 7}, {"RFB 003.008\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, {"RFB 003.889\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* Apple's remote desktop, 003.007 */ {"RFB 003.009\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, {"RFB 004.000\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* Intel AMT KVM */ {"RFB 004.001\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, /* RealVNC 4.6 */ {"RFB 004.002\n", 12, PROTO_VNC_RFB, SMACK_ANCHOR_BEGIN, 8}, {"STAT pid ", 9, PROTO_MEMCACHED,SMACK_ANCHOR_BEGIN, 0}, /* memcached stat response */ {"\xff\xfb\x01\xff\xf0", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\xff\xfb", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\xff\xfc", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\xff\xfd", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\xff\xfe", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\x0a\x0d", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\x0d\x0a", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\x0d\x0d", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\x0a\x0a", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb%\x25xff\xfb", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x26\xff\xfd", 5, PROTO_TELNET, 0, 0}, {"\xff\xfd\x18\xff\xfd", 5, PROTO_TELNET, 0, 0}, {"\xff\xfd\x20\xff\xfd", 5, PROTO_TELNET, 0, 0}, {"\xff\xfd\x23\xff\xfd", 5, PROTO_TELNET, 0, 0}, {"\xff\xfd\x27\xff\xfd", 5, PROTO_TELNET, 0, 0}, {"\xff\xfb\x01\x1b[", 5, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0}, {"\xff\xfb\x01Input", 8, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0}, {"\xff\xfb\x01 ", 6, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0}, {"\xff\xfb\x01login", 8, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0}, {"login:", 6, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0}, {"password:", 9, PROTO_TELNET, SMACK_ANCHOR_BEGIN, 0}, {"\x03\x00\x00\x13\x0e\xd0\xbe\xef\x12\x34\x00\x02\x0f\x08\x00\x00\x00\x00\x00", 12, PROTO_RDP, SMACK_ANCHOR_BEGIN, 0}, {"\x03\x00\x00\x13\x0e\xd0\x00\x00\x12\x34\x00\x02\x0f\x08\x00\x00\x00\x00\x00", 12, PROTO_RDP, SMACK_ANCHOR_BEGIN, 0}, {0,0,0,0,0} }; /*************************************************************************** ***************************************************************************/ unsigned banner1_parse( const struct Banner1 *banner1, struct StreamState *tcb_state, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { size_t x; unsigned offset = 0; unsigned proto; switch (tcb_state->app_proto) { case PROTO_NONE: case PROTO_HEUR: x = smack_search_next( banner1->smack, &tcb_state->state, px, &offset, (unsigned)length); if (x == SMACK_NOT_FOUND) proto = 0xFFFFFFFF; else proto = patterns[x].id; if (proto != 0xFFFFFFFF && !(proto == PROTO_SSL3 && !tcb_state->is_sent_sslhello)) { unsigned i; /* re-read the stuff that we missed */ for (i=0; patterns[i].id && patterns[i].id != tcb_state->app_proto; i++) ; /* Kludge: patterns look confusing, so add port info to the * pattern */ switch (proto) { case PROTO_FTP: if (patterns[x].extra == 1) { if (tcb_state->port == 25 || tcb_state->port == 587) proto = PROTO_SMTP; } break; case PROTO_VNC_RFB: tcb_state->sub.vnc.version = (unsigned char)patterns[x].extra; break; } tcb_state->app_proto = (unsigned short)proto; /* reset the state back again */ tcb_state->state = 0; /* If there is any data from a previous packet, re-parse that */ { const unsigned char *s = banout_string(banout, PROTO_HEUR); unsigned s_len = banout_string_length(banout, PROTO_HEUR); if (s && s_len) banner1_parse( banner1, tcb_state, s, s_len, banout, socket); } banner1_parse( banner1, tcb_state, px, length, banout, socket); } else { banout_append(banout, PROTO_HEUR, px, length); } break; case PROTO_FTP: banner_ftp.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_SMTP: banner_smtp.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_TELNET: banner_telnet.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_RDP: banner_rdp.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_POP3: banner_pop3.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_IMAP4: banner_imap4.parse(banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_SSH1: case PROTO_SSH2: /* generic text-based parser * TODO: in future, need to split these into separate protocols, * especially when binary parsing is added to SSH */ banner_ssh.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_HTTP: banner_http.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_SSL3: banner_ssl.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_SMB: banner_smb1.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_VNC_RFB: banner_vnc.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_MEMCACHED: banner_memcached.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_SCRIPTING: banner_scripting.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_VERSIONING: banner_versioning.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; case PROTO_MC: banner_mc.parse( banner1, banner1->http_fields, tcb_state, px, length, banout, socket); break; default: fprintf(stderr, "banner1: internal error\n"); break; } return tcb_state->app_proto; } /* * Simple banners with hello probes from nmap-service-probes */ static const char genericlines_hello[] = "\r\n\r\n"; struct ProtocolParserStream banner_genericlines = { "banner-GenericLines", 1098, genericlines_hello, sizeof(genericlines_hello) - 1, 0, NULL, NULL, NULL, }; static const char x11_hello[] = "\x6C\0\x0B\0\0\0\0\0\0\0\0\0"; struct ProtocolParserStream banner_x11 = { "banner-X11Probe", 6000, x11_hello, sizeof(x11_hello) - 1, 0, NULL, NULL, NULL, }; static const char javarmi_hello[] = "\x4a\x52\x4d\x49\0\x02\x4b"; struct ProtocolParserStream banner_javarmi = { "banner-JavaRMI", 1098, javarmi_hello, sizeof(javarmi_hello) - 1, 0, NULL, NULL, NULL, }; static const char mongodb_hello[] = "\x41\0\0\0\x3a\x30\0\0\xff\xff\xff\xff\xd4\x07\0\0\0\0\0\0test.$cmd\0\0\0\0\0\xff\xff\xff\xff\x1b\0\0\0\x01serverStatus\0\0\0\0\0\0\0\xf0\x3f\0"; struct ProtocolParserStream banner_mongodb = { "banner-mongodb", 27017, mongodb_hello, sizeof(mongodb_hello) - 1, 0, NULL, NULL, NULL, }; static const char kerberos_hello[] = "\0\0\0\x71\x6a\x81\x6e\x30\x81\x6b\xa1\x03\x02\x01\x05\xa2\x03\x02\x01\x0a\xa4\x81\x5e\x30\x5c\xa0\x07\x03\x05\0\x50\x80\0\x10\xa2\x04\x1b\x02NM\xa3\x17\x30\x15\xa0\x03\x02\x01\0\xa1\x0e\x30\x0c\x1b\x06krbtgt\x1b\x02NM\xa5\x11\x18\x0f""19700101000000Z\xa7\x06\x02\x04\x1f\x1e\xb9\xd9\xa8\x17\x30\x15\x02\x01\x12\x02\x01\x11\x02\x01\x10\x02\x01\x17\x02\x01\x01\x02\x01\x03\x02\x01\x02"; struct ProtocolParserStream banner_kerberos = { "banner-Kerberos", 88, kerberos_hello, sizeof(kerberos_hello) - 1, 0, NULL, NULL, NULL, }; static const char dicom_hello[] = "\x01\x00\x00\x00\x00\xcd\x00\x01\x00\x00""ANY-SCP ECHOSCU 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x15""1.2.840.10008.3.1.1.1 \x00\x00.\x01\x00\x00\x00""0\x00\x00\x11""1.2.840.10008.1.1@\x00\x00\x11""1.2.840.10008.1.2P\x00\x00:Q\x00\x00\x04\x00\x00@\x00R\x00\x00\x1b""1.2.276.0.7230010.3.0.3.6.2U\x00\x00\x0fOFFIS_DCMTK_362"; struct ProtocolParserStream banner_dicom = { "banner-dicom", 104, dicom_hello, sizeof(dicom_hello) - 1, 0, NULL, NULL, NULL, }; static const char ldap_hello[] = "\x30\x84\x00\x00\x00\x2d\x02\x01\x07\x63\x84\x00\x00\x00\x24\x04\x00\x0a\x01\x00\x0a\x01\x00\x02\x01\x00\x02\x01\x64\x01\x01\x00\x87\x0b\x6f\x62\x6a\x65\x63\x74\x43\x6c\x61\x73\x73\x30\x84\x00\x00\x00\x00"; struct ProtocolParserStream banner_ldap = { "banner-LDAPSearchReq", 389, ldap_hello, sizeof(ldap_hello) - 1, 0, NULL, NULL, NULL, }; static const char sip_hello[] = "OPTIONS sip:nm SIP/2.0\r\nVia: SIP/2.0/TCP nm;branch=foo\r\nFrom: ;tag=root\r\nTo: \r\nCall-ID: 50000\r\nCSeq: 42 OPTIONS\r\nMax-Forwards: 70\r\nContent-Length: 0\r\nContact: \r\nAccept: application/sdp\r\n\r\n"; struct ProtocolParserStream banner_sip = { "banner-SIPOptions", 5060, sip_hello, sizeof(sip_hello) - 1, 0, NULL, NULL, NULL, }; static const char rtsp_hello[] = "OPTIONS / RTSP/1.0\r\n\r\n"; struct ProtocolParserStream banner_rtsp = { "banner-RTSPRequest", 554, rtsp_hello, sizeof(rtsp_hello) - 1, 0, NULL, NULL, NULL, }; static const char rpc_hello[] = "\x80\0\0\x28\x72\xFE\x1D\x13\0\0\0\0\0\0\0\x02\0\x01\x86\xA0\0\x01\x97\x7C\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; struct ProtocolParserStream banner_rpc = { "banner-RPCCheck", 111, rpc_hello, sizeof(rpc_hello) - 1, 0, NULL, NULL, NULL, }; static const char dns_hello[] = "\0\x1E\0\x06\x01\0\0\x01\0\0\0\0\0\0\x07version\x04""bind\0\0\x10\0\x03"; struct ProtocolParserStream banner_dns = { "banner-DNSVersionBindReqTCP", 53, dns_hello, sizeof(dns_hello) - 1, 0, NULL, NULL, NULL, }; static const char docker_hello[] = "GET /version HTTP/1.1\r\n\r\n"; struct ProtocolParserStream banner_docker = { "banner-docker", 2375, docker_hello, sizeof(docker_hello) - 1, 0, NULL, NULL, NULL, }; static const char redis_hello[] = "*1\r\n$4\r\ninfo\r\n"; struct ProtocolParserStream banner_redis = { "banner-redis-server", 6379, redis_hello, sizeof(redis_hello) - 1, 0, NULL, NULL, NULL, }; static const char notes_rpc_hello[] = "\x3A\x00\x00\x00\x2F\x00\x00\x00\x02\x00\x00\x40\x02\x0F\x00\x01\x00\x3D\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x1F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; struct ProtocolParserStream banner_notes_rpc = { "banner-NotesRPC", 6379, notes_rpc_hello, sizeof(notes_rpc_hello) - 1, 0, NULL, NULL, NULL, }; static const char ms_sql_s_hello[] = "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x0c\x03\x00\x28\x00\x04\xff\x08\x00\x01\x55\x00\x00\x00\x4d\x53\x53\x51\x4c\x53\x65\x72\x76\x65\x72\x00\x48\x0f\x00\x00"; struct ProtocolParserStream banner_ms_sql_s = { "banner-ms-sql-s", 6379, ms_sql_s_hello, sizeof(ms_sql_s_hello) - 1, 0, NULL, NULL, NULL, }; static const char afp_hello[] = "\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x0f\x00"; struct ProtocolParserStream banner_afp = { "banner-afp", 548, afp_hello, sizeof(afp_hello) - 1, 0, NULL, NULL, NULL, }; /*************************************************************************** * Create the --banners systems ***************************************************************************/ struct Banner1 * banner1_create(void) { struct Banner1 *b; unsigned i; b = CALLOC(1, sizeof(*b)); /* * This creates a pattern-matching blob for heuristically determining * a protocol that runs on wrong ports, such as how FTP servers * often respond with "220 " or VNC servers respond with "RFB". */ b->smack = smack_create("banner1", SMACK_CASE_INSENSITIVE); for (i=0; patterns[i].pattern; i++) smack_add_pattern( b->smack, patterns[i].pattern, patterns[i].pattern_length, i, patterns[i].is_anchored); smack_compile(b->smack); /* * [TODO] These need to be moved into the 'init' functions */ b->payloads.tcp[80] = &banner_http; b->payloads.tcp[8080] = &banner_http; b->payloads.tcp[139] = (void*)&banner_smb0; b->payloads.tcp[445] = (void*)&banner_smb1; b->payloads.tcp[8530] = (void*)&banner_http; /* WSUS */ b->payloads.tcp[8531] = (void*)&banner_ssl; /* WSUS/s */ /* https://www.nomotion.net/blog/sharknatto/ */ b->payloads.tcp[49955] = (void*)&banner_ssl; /* AT&T box */ b->payloads.tcp[443] = (void*)&banner_ssl; /* HTTP/s */ b->payloads.tcp[465] = (void*)&banner_ssl; /* SMTP/s */ b->payloads.tcp[990] = (void*)&banner_ssl; /* FTP/s */ b->payloads.tcp[991] = (void*)&banner_ssl; b->payloads.tcp[992] = (void*)&banner_ssl; /* Telnet/s */ b->payloads.tcp[993] = (void*)&banner_ssl; /* IMAP4/s */ b->payloads.tcp[994] = (void*)&banner_ssl; b->payloads.tcp[995] = (void*)&banner_ssl; /* POP3/s */ b->payloads.tcp[2083] = (void*)&banner_ssl; /* cPanel - SSL */ b->payloads.tcp[2087] = (void*)&banner_ssl; /* WHM - SSL */ b->payloads.tcp[2096] = (void*)&banner_ssl; /* cPanel webmail - SSL */ b->payloads.tcp[8443] = (void*)&banner_ssl; /* Plesk Control Panel - SSL */ b->payloads.tcp[9050] = (void*)&banner_ssl; /* Tor */ b->payloads.tcp[8140] = (void*)&banner_ssl; /* puppet */ b->payloads.tcp[11211] = (void*)&banner_memcached; b->payloads.tcp[23] = (void*)&banner_telnet; b->payloads.tcp[3389] = (void*)&banner_rdp; b->payloads.tcp[1098] = (void*)&banner_javarmi; b->payloads.tcp[1099] = (void*)&banner_javarmi; for (i=0; i < 20; i++) { b->payloads.tcp[6000 + i] = (void*)&banner_x11; } b->payloads.tcp[88] = (void*)&banner_kerberos; b->payloads.tcp[9001] = (void*)&banner_mongodb; b->payloads.tcp[27017] = (void*)&banner_mongodb; b->payloads.tcp[49153] = (void*)&banner_mongodb; b->payloads.tcp[104] = (void*)&banner_dicom; b->payloads.tcp[2345] = (void*)&banner_dicom; b->payloads.tcp[2761] = (void*)&banner_dicom; b->payloads.tcp[2762] = (void*)&banner_dicom; b->payloads.tcp[4242] = (void*)&banner_dicom; b->payloads.tcp[11112] = (void*)&banner_dicom; b->payloads.tcp[256] = (void*)&banner_ldap; b->payloads.tcp[257] = (void*)&banner_ldap; b->payloads.tcp[389] = (void*)&banner_ldap; b->payloads.tcp[390] = (void*)&banner_ldap; b->payloads.tcp[1702] = (void*)&banner_ldap; b->payloads.tcp[3268] = (void*)&banner_ldap; b->payloads.tcp[3892] = (void*)&banner_ldap; b->payloads.tcp[11711] = (void*)&banner_ldap; /* LDAP/s */ b->payloads.tcp[636] = (void*)&banner_ssl; b->payloads.tcp[637] = (void*)&banner_ssl; b->payloads.tcp[3269] = (void*)&banner_ssl; b->payloads.tcp[11712] = (void*)&banner_ssl; b->payloads.tcp[406] = (void*)&banner_sip; b->payloads.tcp[5060] = (void*)&banner_sip; b->payloads.tcp[8081] = (void*)&banner_sip; b->payloads.tcp[31337] = (void*)&banner_sip; /* SIP/s */ b->payloads.tcp[5061] = (void*)&banner_ssl; b->payloads.tcp[554] = (void*)&banner_rtsp; b->payloads.tcp[8554] = (void*)&banner_rtsp; /* RTSP/s */ b->payloads.tcp[322] = (void*)&banner_ssl; b->payloads.tcp[111] = (void*)&banner_rpc; b->payloads.tcp[2049] = (void*)&banner_rpc; b->payloads.tcp[53] = (void*)&banner_dns; b->payloads.tcp[135] = (void*)&banner_dns; b->payloads.tcp[50000] = (void*)&banner_dns; b->payloads.tcp[50001] = (void*)&banner_dns; b->payloads.tcp[50002] = (void*)&banner_dns; b->payloads.tcp[2375] = (void*)&banner_docker; /* Docker/s */ b->payloads.tcp[2376] = (void*)&banner_ssl; b->payloads.tcp[2379] = (void*)&banner_docker; b->payloads.tcp[2380] = (void*)&banner_docker; b->payloads.tcp[6379] = (void*)&banner_redis; b->payloads.tcp[130] = (void*)&banner_notes_rpc; b->payloads.tcp[427] = (void*)&banner_notes_rpc; b->payloads.tcp[1352] = (void*)&banner_notes_rpc; b->payloads.tcp[1972] = (void*)&banner_notes_rpc; b->payloads.tcp[7171] = (void*)&banner_notes_rpc; b->payloads.tcp[8728] = (void*)&banner_notes_rpc; b->payloads.tcp[22001] = (void*)&banner_notes_rpc; b->payloads.tcp[1433] = (void*)&banner_ms_sql_s; /* AFP */ b->payloads.tcp[548] = (void*)&banner_afp; /* * This goes down the list of all the TCP protocol handlers and initializes * them. */ banner_ftp.init(b); banner_http.init(b); banner_imap4.init(b); banner_memcached.init(b); banner_pop3.init(b); banner_smtp.init(b); banner_ssh.init(b); banner_ssl.init(b); banner_ssl_12.init(b); banner_smb0.init(b); banner_smb1.init(b); banner_telnet.init(b); banner_rdp.init(b); banner_vnc.init(b); banner_mc.init(b); /* scripting/versioning come after the rest */ //banner_scripting.init(b); //banner_versioning.init(b); return b; } /*************************************************************************** ***************************************************************************/ void banner1_destroy(struct Banner1 *b) { if (b == NULL) return; if (b->smack) smack_destroy(b->smack); if (b->http_fields) smack_destroy(b->http_fields); free(b); } /*************************************************************************** * Test the banner1 detection system by throwing random frames at it ***************************************************************************/ void banner1_test(const char *filename) { struct PcapFile *cap; unsigned link_type; cap = pcapfile_openread(filename); if (cap == NULL) { fprintf(stderr, "%s: can't open capture file\n", filename); return; } link_type = pcapfile_datalink(cap); for (;;) { int packets_read; unsigned secs; unsigned usecs; unsigned origlength; unsigned length; unsigned char px[65536]; struct PreprocessedInfo parsed; unsigned x; packets_read = pcapfile_readframe( cap, /* capture dump file */ &secs, &usecs, &origlength, &length, px, sizeof(px)); if (packets_read == 0) break; x = preprocess_frame(px, length, link_type, &parsed); if (x == 0) continue; } pcapfile_close(cap); } /*************************************************************************** ***************************************************************************/ int banner1_selftest() { unsigned i; struct Banner1 *b; struct StreamState tcb_state[1]; const unsigned char *px; unsigned length; struct BannerOutput banout[1]; static const char *http_header = "HTTP/1.0 302 Redirect\r\n" "Date: Tue, 03 Sep 2013 06:50:01 GMT\r\n" "Connection: close\r\n" "Via: HTTP/1.1 ir14.fp.bf1.yahoo.com (YahooTrafficServer/1.2.0.13 [c s f ])\r\n" "Server: YTS/1.20.13\r\n" "Cache-Control: no-store\r\n" "Content-Type: text/html\r\n" "Content-Language: en\r\n" "Location: http://failsafe.fp.yahoo.com/404.html\r\n" "Content-Length: 227\r\n" "\r\nhello\n"; px = (const unsigned char *)http_header; length = (unsigned)strlen(http_header); LOG(1, "[ ] banners: selftesting\n"); /* * First, test the "banout" subsystem */ if (banout_selftest() != 0) { fprintf(stderr, "banout: failed\n"); return 1; } /* * Test one character at a time */ b = banner1_create(); banout_init(banout); memset(tcb_state, 0, sizeof(tcb_state[0])); for (i=0; i #include #include "masscan-app.h" #include "proto-banout.h" #include "proto-x509.h" #include "proto-spnego.h" struct stack_handle_t; struct Banner1; struct StreamState; typedef void (*BannerParser)( const struct Banner1 *banner1, void *banner1_private, struct StreamState *stream_state, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket); struct Banner1 { struct lua_State *L; struct SMACK *smack; struct SMACK *http_fields; struct SMACK *html_fields; struct SMACK *memcached_responses; struct SMACK *memcached_stats; unsigned is_capture_html:1; unsigned is_capture_cert:1; unsigned is_capture_servername:1; unsigned is_capture_heartbleed:1; unsigned is_capture_ticketbleed:1; unsigned is_heartbleed:1; unsigned is_ticketbleed:1; unsigned is_poodle_sslv3:1; struct { const struct ProtocolParserStream *tcp[65536]; } payloads; BannerParser parser[PROTO_end_of_list]; }; struct BannerBase64 { unsigned state:2; unsigned temp:24; }; struct SSL_SERVER_HELLO { unsigned state; unsigned remaining; unsigned timestamp; unsigned short cipher_suite; unsigned short ext_tag; unsigned short ext_remaining; unsigned char compression_method; unsigned char version_major; unsigned char version_minor; }; struct SSL_SERVER_CERT { unsigned state; unsigned remaining; struct { unsigned remaining; } sub; struct CertDecode x509; }; struct SSL_SERVER_ALERT { unsigned char level; unsigned char description; }; struct SSLRECORD { unsigned char type; unsigned char version_major; unsigned char version_minor; struct { unsigned state; unsigned char type; unsigned remaining; } handshake; union { struct { /* all these structs should start with state */ unsigned state; } all; struct SSL_SERVER_HELLO server_hello; struct SSL_SERVER_CERT server_cert; struct SSL_SERVER_ALERT server_alert; } x; }; struct PIXEL_FORMAT { unsigned short red_max; unsigned short green_max; unsigned short blue_max; unsigned char red_shift; unsigned char green_shift; unsigned char blue_shift; unsigned char bits_per_pixel; unsigned char depth; unsigned big_endian_flag:1; unsigned true_colour_flag:1; }; struct VNCSTUFF { unsigned sectype; unsigned char version; unsigned char len; unsigned short width; unsigned short height; struct PIXEL_FORMAT pixel; }; struct FTPSTUFF { unsigned code; unsigned is_last:1; }; struct MCSTUFF { char * banmem; size_t totalLen; size_t imgstart; size_t imgend; int brackcount; }; struct SMTPSTUFF { unsigned code; unsigned is_last:1; }; struct POP3STUFF { unsigned code; unsigned is_last:1; }; struct MEMCACHEDSTUFF { unsigned match; }; struct Smb72_Negotiate { uint16_t DialectIndex; uint16_t SecurityMode; uint64_t SystemTime; uint32_t SessionKey; uint32_t Capabilities; uint16_t ServerTimeZone; uint8_t ChallengeLength; uint8_t ChallengeOffset; }; struct Smb73_Setup { uint16_t BlobLength; uint16_t BlobOffset; }; struct SMBSTUFF { unsigned nbt_state; unsigned char nbt_type; unsigned char nbt_flags; unsigned is_printed_ver:1; unsigned is_printed_guid:1; unsigned is_printed_time:1; unsigned is_printed_boottime:1; unsigned nbt_length; unsigned nbt_err; union { struct { unsigned char command; unsigned status; unsigned char flags1; unsigned short flags2; unsigned pid; unsigned char signature[8]; unsigned short tid; unsigned short uid; unsigned short mid; unsigned short param_length; unsigned short param_offset; unsigned short byte_count; unsigned short byte_offset; unsigned short byte_state; unsigned short unicode_char; } smb1; struct { unsigned seqno; unsigned short header_length; unsigned short offset; unsigned short state; unsigned short opcode; unsigned short struct_length; unsigned is_dynamic:1; unsigned char flags; unsigned ntstatus; unsigned number; unsigned short blob_offset; unsigned short blob_length; } smb2; } hdr; union { struct Smb72_Negotiate negotiate; struct Smb73_Setup setup; struct { uint64_t current_time; uint64_t boot_time; } negotiate2; } parms; struct SpnegoDecode spnego; }; struct RDPSTUFF { unsigned short tpkt_length; struct { unsigned state; unsigned short dstref; unsigned short srcref; unsigned char len; unsigned char type; unsigned char flags; } cotp; struct { unsigned state; unsigned result; unsigned char type; unsigned char flags; unsigned char len; } cc; }; struct SSHSTUFF{ size_t packet_length; }; struct StreamState { unsigned state; unsigned remaining; unsigned short port; unsigned short app_proto; unsigned is_sent_sslhello:1; struct BannerBase64 base64; union { struct SSLRECORD ssl; struct VNCSTUFF vnc; struct FTPSTUFF ftp; struct SMTPSTUFF smtp; struct POP3STUFF pop3; struct MEMCACHEDSTUFF memcached; struct SMBSTUFF smb; struct RDPSTUFF rdp; struct MCSTUFF mc; struct SSHSTUFF ssh; } sub; }; enum StreamFlags { SF__none = 0, SF__close = 0x01, /* send FIN after the static Hello is sent*/ SF__nowait_hello = 0x02, /* send our hello immediately, don't wait for their hello */ }; /** * A registration structure for various TCP stream protocols * like HTTP, SSL, and SSH */ struct ProtocolParserStream { const char *name; unsigned port; const void *hello; size_t hello_length; enum StreamFlags flags; int (*selftest)(void); void *(*init)(struct Banner1 *b); void (*parse)( const struct Banner1 *banner1, void *banner1_private, struct StreamState *stream_state, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket); void (*cleanup)(struct StreamState *stream_state); void (*transmit_hello)(const struct Banner1 *banner1, struct stack_handle_t *socket); /* When multiple items are registered for a port. When one * connection is closed, the next will be opened.*/ struct ProtocolParserStream *next; /*NOTE: the 'next' parameter should be the last one in this structure, * because we statically initialize the rest of the members at compile * time, and then use this last parameter to link up structures * at runtime */ }; /** * Patterns that match the data from the start of a TCP connection. * This will hint at what protocol that connection might be. */ struct Patterns { /** A string like "SSH-" or "220 " that matches a banner */ const char *pattern; /** The length of that string, since it may be binary containing * nul characters */ unsigned pattern_length; /** An integer arbitrarily assigned to this pattern, which should * probably match the protocol ID that we are looking for */ unsigned id; /** * Whether this string matches only at the beginning ('anchored') * or anywhere in the input. Virtually all the patterns are anchored. */ unsigned is_anchored; /** * Some extra flags for the pattern matcher for a few of the patterns. */ unsigned extra; }; struct Banner1 * banner1_create(void); void banner1_destroy(struct Banner1 *b); unsigned banner1_parse( const struct Banner1 *banner1, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket); /** * Test the banner protocol-parsing system by reading * in a capture file */ void banner1_test(const char *filename); int banner1_selftest(void); #endif ================================================ FILE: src/proto-banout.c ================================================ /* Banner Output This module remembers "banners" from a connection. These are often simple strings, like the FTP hello string. The can also be more complex strings, parsed from binary protocols. They also may contain bulk data, such as BASE64 encoded X.509 certificates from SSL. One complication is that since we can extract multiple types of information from the same connection, we can have more than one banner for the same connection. */ #include "proto-banner1.h" #include "util-malloc.h" #include #include #include #include /*************************************************************************** ***************************************************************************/ void banout_init(struct BannerOutput *banout) { banout->length = 0; banout->protocol = 0; banout->next = 0; banout->max_length = sizeof(banout->banner); } /*************************************************************************** ***************************************************************************/ void banout_release(struct BannerOutput *banout) { while (banout->next) { struct BannerOutput *next = banout->next->next; free(banout->next); banout->next = next; } banout_init(banout); } /*************************************************************************** ***************************************************************************/ static struct BannerOutput * banout_find_proto(struct BannerOutput *banout, unsigned proto) { while (banout && banout->protocol != proto) banout = banout->next; return banout; } /*************************************************************************** ***************************************************************************/ const unsigned char * banout_string(const struct BannerOutput *banout, unsigned proto) { while (banout && (banout->protocol&0xFFFF) != proto) banout = banout->next; if (banout) return banout->banner; else return NULL; } /*************************************************************************** ***************************************************************************/ unsigned banout_is_equal(const struct BannerOutput *banout, unsigned proto, const char *string) { const unsigned char *string2; size_t string_length; size_t string2_length; /* * Grab the string */ string2 = banout_string(banout, proto); if (string2 == NULL) return string == NULL; if (string == NULL) return 0; string_length = strlen(string); string2_length = banout_string_length(banout, proto); if (string_length != string2_length) return 0; return memcmp(string, string2, string2_length) == 0; } /*************************************************************************** ***************************************************************************/ unsigned banout_is_contains(const struct BannerOutput *banout, unsigned proto, const char *string) { const unsigned char *string2; size_t string_length; size_t string2_length; size_t i; /* * Grab the string */ string2 = banout_string(banout, proto); if (string2 == NULL) return string == NULL; if (string == NULL) return 0; string_length = strlen(string); string2_length = banout_string_length(banout, proto); if (string_length > string2_length) return 0; for (i=0; iprotocol != proto) banout = banout->next; if (banout) return banout->length; else return 0; } /*************************************************************************** ***************************************************************************/ void banout_newline(struct BannerOutput *banout, unsigned proto) { struct BannerOutput *p; p = banout_find_proto(banout, proto); if (p && p->length) { banout_append_char(banout, proto, '\n'); } } /*************************************************************************** ***************************************************************************/ void banout_end(struct BannerOutput *banout, unsigned proto) { struct BannerOutput *p; p = banout_find_proto(banout, proto); if (p && p->length) { p->protocol |= 0x80000000; } } /*************************************************************************** ***************************************************************************/ void banout_append_char(struct BannerOutput *banout, unsigned proto, int c) { char cc = (char)c; banout_append(banout, proto, &cc, 1); } /*************************************************************************** ***************************************************************************/ void banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits) { if (digits == 0) { for (digits=16; digits>0; digits--) if (number>>((digits-1)*4) & 0xF) break; } for (;digits>0; digits--) { char c = "0123456789abcdef"[(number>>(unsigned long long)((digits-1)*4)) & 0xF]; banout_append_char(banout, proto, c); } } /*************************************************************************** * Output either a normal character, or the hex form of a UTF-8 string ***************************************************************************/ void banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c) { if (c & ~0xFFFF) { unsigned c2; c2 = 0xF0 | ((c>>18)&0x03); banout_append_char(banout, proto, c2); c2 = 0x80 | ((c>>12)&0x3F); banout_append_char(banout, proto, c2); c2 = 0x80 | ((c>> 6)&0x3F); banout_append_char(banout, proto, c2); c2 = 0x80 | ((c>> 0)&0x3F); banout_append_char(banout, proto, c2); } else if (c & ~0x7FF) { unsigned c2; c2 = 0xE0 | ((c>>12)&0x0F); banout_append_char(banout, proto, c2); c2 = 0x80 | ((c>> 6)&0x3F); banout_append_char(banout, proto, c2); c2 = 0x80 | ((c>> 0)&0x3F); banout_append_char(banout, proto, c2); } else if (c & ~0x7f) { unsigned c2; c2 = 0xc0 | ((c>> 6)&0x1F); banout_append_char(banout, proto, c2); c2 = 0x80 | ((c>> 0)&0x3F); banout_append_char(banout, proto, c2); } else banout_append_char(banout, proto, c); } /*************************************************************************** ***************************************************************************/ static struct BannerOutput * banout_new_proto(struct BannerOutput *banout, unsigned proto) { struct BannerOutput *p; if (banout->protocol == 0 && banout->length == 0) { banout->protocol = proto; return banout; } p = CALLOC(1, sizeof(*p)); p->protocol = proto; p->max_length = sizeof(p->banner); p->next = banout->next; banout->next = p; return p; } /*************************************************************************** ***************************************************************************/ static struct BannerOutput * banout_expand(struct BannerOutput *banout, struct BannerOutput *p) { struct BannerOutput *n; /* Double the space */ n = MALLOC( offsetof(struct BannerOutput, banner) + 2 * p->max_length); /* Copy the old structure */ memcpy(n, p, offsetof(struct BannerOutput, banner) + p->max_length); n->max_length *= 2; if (p == banout) { /* 'p' is the head of the linked list, so we can't free it */ banout->next = n; p->protocol = 0; p->length = 0; } else { /* 'p' is not the head, so replace it in the list with 'n', * then free it. */ while (banout->next != p) banout = banout->next; banout->next = n; free(p); } return n; } /*************************************************************************** ***************************************************************************/ static void banout_vprintf(struct BannerOutput *banout, unsigned proto, const char *fmt, va_list marker) { char str[10]; int len; va_list marker_cpy; // a va_list is consumed when passed to vsnprintf. va_copy(marker_cpy, marker); len = vsnprintf(str, sizeof(str), fmt, marker_cpy); va_end(marker_cpy); if (len > sizeof(str)-1) { char *tmp = malloc(len+1); vsnprintf(tmp, len+1, fmt, marker); banout_append(banout, proto, tmp, len); free(tmp); } else { banout_append(banout, proto, str, len); } } /*************************************************************************** ***************************************************************************/ void banout_printf(struct BannerOutput *banout, unsigned proto, const char *fmt, ...) { va_list marker; va_start(marker, fmt); banout_vprintf(banout, proto, fmt, marker); va_end(marker); } /*************************************************************************** ***************************************************************************/ void banout_append(struct BannerOutput *banout, unsigned proto, const void *px, size_t length) { struct BannerOutput *p; if (length == AUTO_LEN) length = strlen((const char*)px); /* * Get the matching record for the protocol (e.g. HTML, SSL, etc.). * If it doesn't already exist, add the protocol object to the linked * list. */ p = banout_find_proto(banout, proto); if (p == NULL) { p = banout_new_proto(banout, proto); } /* * If the current object isn't big enough, expand it */ while (p->length + length >= p->max_length) { p = banout_expand(banout, p); } /* * Now that we are assured there is enough space, do the copy */ memcpy(p->banner + p->length, px, length); p->length = (unsigned)(p->length + length); } /***************************************************************************** *****************************************************************************/ static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "+/"; /***************************************************************************** *****************************************************************************/ void banout_init_base64(struct BannerBase64 *base64) { base64->state = 0; base64->temp = 0; } /***************************************************************************** *****************************************************************************/ void banout_append_base64(struct BannerOutput *banout, unsigned proto, const void *vpx, size_t length, struct BannerBase64 *base64) { const unsigned char *px = (const unsigned char *)vpx; size_t i; unsigned x = base64->temp; unsigned state = base64->state; for (i=0; i>18)&0x3F]); banout_append_char(banout, proto, b64[(x>>12)&0x3F]); banout_append_char(banout, proto, b64[(x>> 6)&0x3F]); banout_append_char(banout, proto, b64[(x>> 0)&0x3F]); } } base64->temp = x; base64->state = state; } /***************************************************************************** *****************************************************************************/ void banout_finalize_base64(struct BannerOutput *banout, unsigned proto, struct BannerBase64 *base64) { unsigned x = base64->temp; switch (base64->state) { case 0: break; case 1: banout_append_char(banout, proto, b64[(x>>18)&0x3F]); banout_append_char(banout, proto, b64[(x>>12)&0x3F]); banout_append_char(banout, proto, '='); banout_append_char(banout, proto, '='); break; case 2: banout_append_char(banout, proto, b64[(x>>18)&0x3F]); banout_append_char(banout, proto, b64[(x>>12)&0x3F]); banout_append_char(banout, proto, b64[(x>>6)&0x3F]); banout_append_char(banout, proto, '='); break; } } /***************************************************************************** *****************************************************************************/ static int banout_string_equals(struct BannerOutput *banout, unsigned proto, const char *rhs) { const unsigned char *lhs = banout_string(banout, proto); size_t lhs_length = banout_string_length(banout, proto); size_t rhs_length = strlen(rhs); if (lhs_length != rhs_length) return 0; return memcmp(lhs, rhs, rhs_length) == 0; } /***************************************************************************** *****************************************************************************/ int banout_selftest(void) { /* * Basic test */ { struct BannerOutput banout[1]; unsigned i; banout_init(banout); for (i=0; i<10; i++) { banout_append(banout, 1, "xxxx", 4); banout_append(banout, 2, "yyyyy", 5); } if (banout->next == 0) return 1; if (banout_string_length(banout, 1) != 40) return 1; if (banout_string_length(banout, 2) != 50) return 1; banout_release(banout); if (banout->next != 0) return 1; } /* * Test BASE64 encoding. We are going to do strings of various lengths * in order to test the boundary condition of finalizing various strings * properly */ { struct BannerOutput banout[1]; struct BannerBase64 base64[1]; banout_init(banout); banout_init_base64(base64); banout_append_base64(banout, 1, "x", 1, base64); banout_finalize_base64(banout, 1, base64); banout_init_base64(base64); banout_append_base64(banout, 2, "bc", 2, base64); banout_finalize_base64(banout, 2, base64); banout_init_base64(base64); banout_append_base64(banout, 3, "mno", 3, base64); banout_finalize_base64(banout, 3, base64); banout_init_base64(base64); banout_append_base64(banout, 4, "stuv", 4, base64); banout_finalize_base64(banout, 4, base64); banout_init_base64(base64); banout_append_base64(banout, 5, "fghij", 5, base64); banout_finalize_base64(banout, 5, base64); if (!banout_string_equals(banout, 1, "eA==")) return 1; if (!banout_string_equals(banout, 2, "YmM=")) return 1; if (!banout_string_equals(banout, 3, "bW5v")) return 1; if (!banout_string_equals(banout, 4, "c3R1dg==")) return 1; if (!banout_string_equals(banout, 5, "ZmdoaWo=")) return 1; banout_release(banout); } return 0; } ================================================ FILE: src/proto-banout.h ================================================ #ifndef PROTO_BANOUT_H #define PROTO_BANOUT_H struct BannerBase64; /** * A structure for tracking one or more banners from a target. * There can be multiple banner information from a target, such * as SSL certificates, or HTTP headers separate from HTML * content, and so on. This will be exploited more in the future * for extracting multiple bits of information from the same * port, but giving them different labels. This will also be * used for doing optional stuff, such as grabbing the entire * default webpage when connecting to port 80. */ struct BannerOutput { struct BannerOutput *next; unsigned protocol; unsigned length; unsigned max_length; unsigned char banner[200]; }; /** * Initialize the list of banners. This doesn't allocate any * memory, such sets it to zero. */ void banout_init(struct BannerOutput *banout); /** * Release any memory. If the list contains only one short * banner, then no memory was allocated, so nothing gets * freed. */ void banout_release(struct BannerOutput *banout); /** * Just appends a newline '\n' character. In the future, this may do something * more interesting, which is why it's a separate function. */ void banout_newline(struct BannerOutput *banout, unsigned proto); /** * End the banner of the current. This is called when the protocol parser * knows it's at the end. The major reason for this is processing the * SSL certificates, so that each certificate comes back as a separate * banner. */ void banout_end(struct BannerOutput *banout, unsigned proto); /** * Append text onto the banner. If this exceeds the buffer, then the * buffer will be expanded. */ void banout_append(struct BannerOutput *banout, unsigned proto, const void *px, size_t length); #define AUTO_LEN ((size_t)~0) void banout_printf(struct BannerOutput *banout, unsigned proto, const char *fmt, ...); /** * Append a single character to the banner. */ void banout_append_char(struct BannerOutput *banout, unsigned proto, int c); /** * Append an integer, with hex digits, with the specified number of * digits */ void banout_append_hexint(struct BannerOutput *banout, unsigned proto, unsigned long long number, int digits); void banout_append_unicode(struct BannerOutput *banout, unsigned proto, unsigned c); /** * Select a specific string (of the specified protocol). * The "banner output" can have multiple protocol objects associated * with it, such as an SSL protocol object and an X.509 certificate. * Thus, instead of just grabbing the string, we need to grab the * specific protocol instead. */ const unsigned char * banout_string(const struct BannerOutput *banout, unsigned proto); /** * Get the length of a specific string of the specified protocol. * This is the matching function to banout_string. */ unsigned banout_string_length(const struct BannerOutput *banout, unsigned proto); /** * Prepare to start calling banout_append_base64() */ void banout_init_base64(struct BannerBase64 *base64); /** * Converts the string to BASE64 and appends it to the banner. * Since this can be called iteratively as new input arrives, * a call to banout_init_base64() must be called before the first fragment, * and a call to banout_finalize_base64() must be called after the last * fragment */ void banout_append_base64(struct BannerOutput *banout, unsigned proto, const void *px, size_t length, struct BannerBase64 *base64); /** * Finish encoding the BASE64 string, appending the '==' things on the * end if necessary */ void banout_finalize_base64(struct BannerOutput *banout, unsigned proto, struct BannerBase64 *base64); /** * Compares a banner string to a fixed string. This is primarily used * in the "self-test" feature in order to compare parsed banners from * expected banners. */ unsigned banout_is_equal(const struct BannerOutput *banout, unsigned proto, const char *string); unsigned banout_is_contains(const struct BannerOutput *banout, unsigned proto, const char *string); /** * Do the typical unit/regression test, for this module. */ int banout_selftest(void); #endif ================================================ FILE: src/proto-coap.c ================================================ /* CoAP - Constrained Application Protocol https://en.wikipedia.org/wiki/Constrained_Application_Protocol This is a very simple protocol for interacting with IoT devices that have a minimal amount of resources, such as less than a megabyte of RAM. From a scanner point of view, we want to execute the equivalent of: GET /.well-known/core This will return the list of additional items that we can access on the target device. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Ver| T | TKL | Code | Message ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Token (if any, TKL bytes) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1| Payload (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #include "proto-coap.h" #include "proto-banner1.h" #include "smack.h" #include "unusedparm.h" #include "util-logger.h" #include "masscan-app.h" #include "output.h" #include "stack-tcp-api.h" #include "proto-preprocess.h" #include "proto-ssl.h" #include "proto-udp.h" #include "syn-cookie.h" #include "massip-port.h" #include "util-malloc.h" #include "util-safefunc.h" #include "util-bool.h" #include #include #include struct CoapLink { unsigned link_offset; unsigned link_length; unsigned parms_offset; unsigned parms_length; }; /**************************************************************************** ****************************************************************************/ static const char * response_code(unsigned code) { #define CODE(x,y) (((x)<<5) | (y)) switch (code) { case CODE(2,0): return "Okay"; case CODE(2,1): return "Created"; case CODE(2,2): return "Deleted"; case CODE(2,3): return "Valid"; case CODE(2,4): return "Changed"; case CODE(2,5): return "Content"; case CODE(4,0): return "Bad Request"; case CODE(4,1): return "Unauthorized"; case CODE(4,2): return "Bad Option"; case CODE(4,3): return "Forbidden"; case CODE(4,4): return "Not Found"; case CODE(4,5): return "Method Not Allowed"; case CODE(4,6): return "Not Acceptable"; case CODE(4,12): return "Precondition Failed"; case CODE(4,13): return "Request Too Large"; case CODE(4,15): return "Unsupported Content-Format"; case CODE(5,0): return "Internal Server Error"; case CODE(5,1): return "Not Implemented"; case CODE(5,2): return "Bad Gateway"; case CODE(5,3): return "Service Unavailable"; case CODE(5,4): return "Gateway Timeout"; case CODE(5,5): return "Proxying Not Supported"; } switch (code>>5) { case 2: return "Okay"; case 4: return "Error"; default: return "PARSE_ERR"; } } /**************************************************************************** * RFC5987 * attr-char = ALPHA / DIGIT * / "!" / "#" / "$" / "&" / "+" / "-" / "." * / "^" / "_" / "`" / "|" / "~" * ; token except ( "*" / "'" / "%" ) * We need this in parsing the links, which may have parameters afterwards * whose names are in this format. ****************************************************************************/ static bool is_attr_char(unsigned c) { switch (c) { case '!': case '#': case '$': case '&': case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~': return true; default: return isalnum(c) != 0; } } /**************************************************************************** ****************************************************************************/ static struct CoapLink * parse_links(const unsigned char *px, unsigned offset, unsigned length, size_t *r_count) { struct CoapLink *l; struct CoapLink *links; unsigned count = 0; enum { LINK_BEGIN=0, LINK_VALUE, LINK_END, PARM_BEGIN, PARM_NAME_BEGIN, PARM_VALUE_BEGIN, PARM_QUOTED, PARM_QUOTED_ESCAPE, PARM_NAME, PARM_VALUE, INVALID } state = LINK_BEGIN; /* For selftesting purposes, we pass in nul-terminated strings, * indicated by a length of (~0) */ if (length == ~0) length = (unsigned)strlen((const char *)px); /* Allocate space for at least one result */ links = CALLOC(1, sizeof(*links)); l = &links[0]; l->parms_offset = offset; l->link_offset = offset; for (; offset < length; offset++) switch (state) { case INVALID: offset = length; break; case LINK_BEGIN: /* Ignore leading whitespace */ if (isspace(px[offset])) continue; /* Links must start with "<" character */ if (px[offset] != '<') { state = INVALID; break; } /* Reserve space for next link */ links = REALLOCARRAY(links, ++count+1, sizeof(*links)); links[count].link_offset = length; /* indicate end-of-list by pointing to end-of-input */ links[count].link_length = 0; links[count].parms_offset = length; links[count].parms_length = 0; /* Grab a pointer to this */ l = &links[count-1]; l->link_offset = offset+1; l->parms_offset = l->link_offset; state = LINK_VALUE; break; case LINK_VALUE: if (px[offset] == '>') { /* End of the link, it may be followed by parameters */ state = LINK_END; } else { l->link_length++; } break; case LINK_END: l->parms_offset = offset+1; l->parms_length = 0; if (isspace(px[offset])) { continue; } else if (px[offset] == ',') { /* next link */ state = LINK_BEGIN; } else if (px[offset] == ';') { state = PARM_NAME_BEGIN; } else { state = INVALID; } break; case PARM_BEGIN: if (isspace(px[offset])) { continue; } else if (px[offset] == ',') { /* next link */ l->parms_length = offset - l->parms_offset; state = LINK_BEGIN; } else if (px[offset] == ';') { state = PARM_NAME_BEGIN; } else { state = INVALID; } break; case PARM_NAME_BEGIN: if (isspace(px[offset])) continue; if (!is_attr_char(px[offset])) state = INVALID; else state = PARM_NAME; break; case PARM_NAME: if (isspace(px[offset])) { continue; } else if (px[offset] == '=') { state = PARM_VALUE_BEGIN; } else if (!is_attr_char(px[offset])) { state = INVALID; } break; case PARM_VALUE_BEGIN: if (isspace(px[offset])) continue; else if (px[offset] == '\"') { state = PARM_QUOTED; } else if (offset == ';') { state = PARM_NAME_BEGIN; } else if (px[offset] == ',') { l->parms_length = offset - l->parms_offset; state = LINK_BEGIN; } else state = PARM_VALUE; break; case PARM_VALUE: if (isspace(px[offset])) continue; else if (px[offset] == ';') state = PARM_NAME_BEGIN; else if (px[offset] == ',') { l->parms_length = offset - l->parms_offset; state = LINK_BEGIN; } else { ; /* do nothing */ } break; case PARM_QUOTED: /* RFC2616: quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) qdtext = > quoted-pair = "\" CHAR */ if (px[offset] == '\\') { state = PARM_QUOTED_ESCAPE; } else if (px[offset] == '\"') { state = PARM_VALUE; } break; case PARM_QUOTED_ESCAPE: state = PARM_QUOTED; break; default: fprintf(stderr, "invalid state\n"); state = INVALID; break; } /* Return an array of links and a count of the number of links */ *r_count = count; return links; } /**************************************************************************** ****************************************************************************/ static bool coap_parse(const unsigned char *px, size_t length, struct BannerOutput *banout, unsigned *request_id) { unsigned version; unsigned type; unsigned code = 0; unsigned token_length = 0; unsigned long long token = 0; unsigned offset; unsigned optnum; unsigned content_format; size_t i; /* All coap responses will be at least 8 bytes */ if (length < 4) { LOG(3, "[-] CoAP: short length\n"); goto not_this_protocol; } /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Ver| T | TKL | Code | Message ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Token (if any, TKL bytes) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1| Payload (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ version = (px[0]>>6) & 3; type = (px[0]>>4) & 3; token_length = px[0] & 0x0F; code = px[1]; *request_id = px[2]<<8 | px[3]; /* Only version supported is v1 */ if (version != 1) { LOG(3, "[-] CoAP: version=%u\n", version); goto not_this_protocol; } /* Only ACKs suported */ if (type != 2) { LOG(3, "[-] CoAP: type=%u\n", type); goto not_this_protocol; } /* Only token lengths up to 8 bytes are supported. * Token length must fit within the packet */ if (token_length > 8 || 4 + token_length > length) { LOG(3, "[-] CoAP: token-length=%u\n", token_length); goto not_this_protocol; } token = 0; for (i=0; i>5, code&0x1F, response_code(code)); banout_append(banout, PROTO_COAP, buf, AUTO_LEN); //code >>= 5; } /* If there was a token, the print it. */ if (token) { char buf[64]; snprintf(buf, sizeof(buf), " token=0x%llu", token); banout_append(banout, PROTO_COAP, buf, AUTO_LEN); } /* * Now process the options fields 0 1 2 3 4 5 6 7 +---------------+---------------+ | | | | Option Delta | Option Length | 1 byte | | | +---------------+---------------+ \ \ / Option Delta / 0-2 bytes \ (extended) \ +-------------------------------+ \ \ / Option Length / 0-2 bytes \ (extended) \ +-------------------------------+ \ \ / / \ \ / Option Value / 0 or more bytes \ \ / / \ \ +-------------------------------+ */ offset = 4 + token_length; optnum = 0; content_format = 0; while (offset < length) { unsigned delta; unsigned opt; unsigned optlen; /* Get the 'opt' byte */ opt = px[offset++]; if (opt == 0xFF) break; optlen = (opt>>0) & 0x0F; delta = (opt>>4) & 0x0F; /* Decode the delta field */ switch (delta) { default: optnum += delta; break; case 13: if (offset >= length) { banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN); optnum = 0xFFFFFFFF; } else { delta = px[offset++] + 13; optnum += delta; } break; case 14: if (offset + 1 >= length) { banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN); optnum = 0xFFFFFFFF; } else { delta = px[offset+0]<<8 | px[offset+1]; delta += 269; offset += 2; optnum += delta; } break; case 15: if (optlen != 15) banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN); optnum = 0xFFFFFFFF; } /* Decode the optlen field */ switch (optlen) { default: break; case 13: if (offset >= length) { banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN); optnum = 0xFFFFFFFF; } else { optlen = px[offset++] + 13; } break; case 14: if (offset + 1 >= length) { banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN); optnum = 0xFFFFFFFF; } else { optlen = px[offset+0]<<8 | px[offset+1]; optlen += 269; offset += 2; } break; } if (offset + optlen > length) { banout_append(banout, PROTO_COAP, " PARSE_ERR", AUTO_LEN); optnum = 0xFFFFFFFF; } /* Process the option contents */ switch (optnum) { case 0xFFFFFFFF: break; case 1: banout_append(banout, PROTO_COAP, " /If-Match/", AUTO_LEN); break; case 3: banout_append(banout, PROTO_COAP, " /Uri-Host/", AUTO_LEN); break; case 4: banout_append(banout, PROTO_COAP, " /Etag", AUTO_LEN); break; case 5: banout_append(banout, PROTO_COAP, " /If-None-Match/", AUTO_LEN); break; case 7: banout_append(banout, PROTO_COAP, " /Uri-Port/", AUTO_LEN); break; case 8: banout_append(banout, PROTO_COAP, " /Location-Path/", AUTO_LEN); break; case 11: banout_append(banout, PROTO_COAP, " /Uri-Path/", AUTO_LEN); break; case 12: banout_append(banout, PROTO_COAP, " /Content-Format/", AUTO_LEN); content_format = 0; for (i=0; isrc_ip; ipaddress ip_me = parsed->dst_ip; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; unsigned message_id = 0; unsigned cookie; struct BannerOutput banout[1]; bool is_valid; LOG(1, "[+] COAP\n"); /* Initialize the "banner output" module that we'll use to print * pretty text in place of the raw packet */ banout_init(banout); /* * Do the protocol parsing */ is_valid = coap_parse(px, length, banout, &message_id); /* Validate the "syn-cookie" style information, which should match the "Message ID field*/ cookie = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy); /*if ((seqno&0xffff) != message_id) goto not_this_protocol;*/ /* See if cookies match. So far, we are allowing responses with the * wrong cookie */ if ((cookie&0xffff) != message_id) banout_append(banout, PROTO_COAP, " IP-MISMATCH", AUTO_LEN); /* Print the banner information, or save to a file, depending */ if (is_valid) { output_report_banner( out, timestamp, ip_them, 17 /*udp*/, parsed->port_src, PROTO_COAP, parsed->ip_ttl, banout_string(banout, PROTO_COAP), banout_string_length(banout, PROTO_COAP)); banout_release(banout); return 0; } else { banout_release(banout); return default_udp_parse(out, timestamp, px, length, parsed, entropy); } } /**************************************************************************** ****************************************************************************/ unsigned coap_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) { /* The frame header is 4 bytes long, with bytes 2 and 3 being the Message ID. We can also put up to 8 bytes of a "token" here instead of just using the message ID. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Ver| T | TKL | Code | Message ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Token (if any, TKL bytes) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ if (length < 4) return 0; px[2] = (unsigned char)(seqno >> 8); px[3] = (unsigned char)(seqno >> 0); return 0; } /**************************************************************************** * For the selftest code, tests whether the indicated link is within the * given list. ****************************************************************************/ static int test_is_link(const char *name, const unsigned char *vinput, struct CoapLink *links, size_t count, int line_number) { size_t i; size_t name_length = strlen(name); const char *input = (const char *)vinput; for (i=0; i;if=\"se\\\"\\;\\,\\<\\>\\\\nsor\","; links = parse_links(input, 0, (unsigned)(~0), &count); if (!test_is_link("/success", input, links, count, __LINE__)) return 1; } /* test a simple link */ { static const unsigned char *input = (const unsigned char *) ";if=\"sensor\""; links = parse_links(input, 0, (unsigned)(~0), &count); if (!test_is_link("/sensors/temp", input, links, count, __LINE__)) return 1; } /* Test a complex dump */ { static const unsigned char *input = (const unsigned char *) ";if=\"sensor\"," ";if=\"sensor\"," ";ct=40," ";rt=\"temperature-c\";if=\"sensor\"," ";rt=\"light-lux\";if=\"sensor\"," ";rt=\"light-lux\";if=\"sensor\"," ";rt=\"light-lux core.sen-light\";if=\"sensor\"," ";ct=40;title=\"Sensor Index\"," ";rt=\"temperature-c\";if=\"sensor\"," ";rt=\"light-lux\";if=\"sensor\"," ";anchor=\"/sensors/temp\";rel=\"describedby\"," ";anchor=\"/sensors/temp\";rel=\"alternate\"," ";rt=\"firmware\";sz=262144" ; links = parse_links(input, 0, (unsigned)(~0), &count); if (!test_is_link("/firmware/v2.1", input, links, count, __LINE__)) return 1; } /* Now test an entire packet */ { const char input[] = "\x60\x45\x01\xce\xc1\x28\xff\x3c\x2f\x72\x65\x67\x69\x73\x74\x65" "\x72\x3e\x2c\x3c\x2f\x6e\x64\x6d\x2f\x64\x69\x73\x3e\x2c\x3c\x2f" "\x6e\x64\x6d\x2f\x63\x69\x3e\x2c\x3c\x2f\x6d\x69\x72\x72\x6f\x72" "\x3e\x2c\x3c\x2f\x75\x68\x70\x3e\x2c\x3c\x2f\x6e\x64\x6d\x2f\x6c" "\x6f\x67\x6f\x75\x74\x3e\x2c\x3c\x2f\x6e\x64\x6d\x2f\x6c\x6f\x67" "\x69\x6e\x3e\x2c\x3c\x2f\x69\x6e\x66\x6f\x3e"; unsigned request_id = 0; struct BannerOutput banout[1]; bool is_valid; banout_init(banout); /* parse a test packet */ is_valid = coap_parse( (const unsigned char*)input, sizeof(input)-1, banout, &request_id ); //fprintf(stderr, "[+] %.*s\n", (int)banout_string_length(banout, PROTO_COAP), banout_string(banout, PROTO_COAP)); if (!is_valid) return 1; if (request_id != 462) return 1; { const unsigned char *str = banout_string(banout, PROTO_COAP); size_t str_length = banout_string_length(banout, PROTO_COAP); if (str_length <= 16 && memcmp(str, "rsp=2.5(Content)", 16) != 0) return 1; } banout_release(banout); } return 0; } ================================================ FILE: src/proto-coap.h ================================================ #ifndef PROTO_COAP_H #define PROTO_COAP_H #include "proto-banner1.h" struct Output; struct PreprocessedInfo; /* * For sending TCP requests and parsing TCP responses. */ extern const struct ProtocolParserStream banner_coap; /* * For parsing UDP responses */ unsigned coap_handle_response(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ); /* * For creating UDP request */ unsigned coap_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno); int proto_coap_selftest(void); #endif ================================================ FILE: src/proto-dns-parse.h ================================================ #ifndef PROTO_DNS_PARSE_H #define PROTO_DNS_PARSE_H struct DomainPointer { const unsigned char *name; unsigned length; }; struct DNS_Incoming { unsigned id; /* transaction id */ unsigned is_valid:1; unsigned is_formerr:1; unsigned is_edns0:1;/* edns0 features found */ unsigned qr:1; /* 'query' or 'response' */ unsigned aa:1; /* 'authoritative answer' */ unsigned tc:1; /* 'truncation' */ unsigned rd:1; /* 'recursion desired' */ unsigned ra:1; /* 'recursion available' */ unsigned z:3; /* reserved */ unsigned opcode; unsigned rcode; /* response error code */ unsigned qdcount; /* query count */ unsigned ancount; /* answer count */ unsigned nscount; /* name-server/authority count */ unsigned arcount; /* additional record count */ struct { unsigned payload_size; unsigned version; unsigned z; } edns0; const unsigned char *req; unsigned req_length; /* the query name */ struct DomainPointer query_name; unsigned query_type; unsigned char query_name_buffer[256]; unsigned rr_count; unsigned short rr_offset[1024]; unsigned edns0_offset; }; void proto_dns_parse(struct DNS_Incoming *dns, const unsigned char px[], unsigned offset, unsigned max); unsigned dns_name_skip(const unsigned char px[], unsigned offset, unsigned max); #endif ================================================ FILE: src/proto-dns.c ================================================ /* Parses DNS response information The scanner sends a CHAOS TXT query for "version.bind". This module parses DNS in order to find the response string. */ #include "proto-udp.h" #include "proto-dns.h" #include "proto-dns-parse.h" #include "proto-preprocess.h" #include "syn-cookie.h" #include "util-logger.h" #include "output.h" #include "masscan-app.h" #include "proto-banner1.h" #include "massip-port.h" #include "masscan.h" #include "unusedparm.h" #define VERIFY_REMAINING(n) if (offset+(n) > length) return; /**************************************************************************** * This skips over a name field while parsing the packet. If the name * is just a two-byte compression field like 0xc0 0x1a, then it'll skip * those two bytes. However, when it does the skip, it does validate * the name. Thus, if it's a compressed name, it'll follow the compression * links to validate things like long names and infinite recursion. ****************************************************************************/ static unsigned dns_name_skip_validate(const unsigned char *px, unsigned offset, unsigned length, unsigned name_length) { unsigned ERROR = length + 1; unsigned result = offset + 2; unsigned recursion = 0; /* 'for all labels' */ for (;;) { unsigned len; /* validate: the eventual uncompressed name will be less than 255 */ if (name_length >= 255) return ERROR; /* validate: haven't gone off end of packet */ if (offset >= length) return ERROR; /* grab length of next label */ len = px[offset]; /* Do two types of processing, either a compression code or a * original label. Note that we can alternate back and forth * between these two states. */ if (len & 0xC0) { /* validate: top 2 bits are 11*/ if ((len & 0xC0) != 0xC0) return ERROR; /* validate: enough bytes left for 2 byte compression field */ if (offset + 1 >= length) return ERROR; /* follow the compression pointer to the next location */ offset = (px[offset]&0x3F)<<8 | px[offset+1]; /* validate: follow a max of 4 links */ if (++recursion > 4) return ERROR; } else { /* we have a normal label */ recursion = 0; /* If the label-length is zero, then that means we've reached * the end of the name */ if (len == 0) { return result; /* end of domain name */ } /* There are more labels to come, therefore skip this and go * to the next one */ name_length += len + 1; offset += len + 1; } } } /**************************************************************************** * Just skip the name, without validating whether it's valid or not. This * is for re-parsing the packet usually, after we've validated that all * the names are OK. ****************************************************************************/ unsigned dns_name_skip(const unsigned char px[], unsigned offset, unsigned max) { unsigned name_length = 0; /* Loop through all labels * NOTE: the only way this loops around is in the case of a normal * label. All other conditions cause a 'return' from this function */ for (;;) { if (name_length >= 255) return max + 1; if (offset >= max) return max + 1; switch (px[offset]>>6) { case 0: /* uncompressed label */ if (px[offset] == 0) { return offset+1; /* end of domain name */ } else { name_length += px[offset] + 1; offset += px[offset] + 1; continue; } break; case 3: /* 0xc0 = compressed name */ return dns_name_skip_validate(px, offset, max, name_length); case 2: /* 0x40 - ENDS0 extended label type * rfc2671 section 3.1 * I have no idea how to parse this */ return max + 1; /* totally clueless how to parse it */ case 1: return max + 1; } } } /**************************************************************************** ****************************************************************************/ static void dns_extract_name(const unsigned char px[], unsigned offset, unsigned max, struct DomainPointer *name) { name->length = 0; for (;;) { unsigned len; if (offset >= max) return; len = px[offset]; if (len & 0xC0) { if ((len & 0xC0) != 0xC0) return; else if (offset + 1 >= max) return; else { offset = (px[offset]&0x3F)<<8 | px[offset+1]; } } else { if (len == 0) { return; /* end of domain name */ } else { memcpy((unsigned char*)name->name+name->length, px+offset, len+1); name->length = (unsigned char)(name->length + len + 1); offset += len + 1; } } } } /**************************************************************************** ****************************************************************************/ void proto_dns_parse(struct DNS_Incoming *dns, const unsigned char px[], unsigned offset, unsigned max) { static const unsigned MAX_RRs = sizeof(dns->rr_offset)/sizeof(dns->rr_offset[0]); unsigned i; dns->is_valid = 0; /* not valid yet until we've successfully parsed*/ dns->req = px; dns->req_length = max-offset; dns->edns0.payload_size = 512; /* RFC 1035 4.2.1 */ /* 1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ if (max - offset < 12) return; dns->id = px[offset+0]<<8 | px[offset+1]; dns->qr = (px[offset+2]>>7)&1; dns->aa = (px[offset+2]>>2)&1; dns->tc = (px[offset+2]>>1)&1; dns->rd = (px[offset+2]>>0)&1; dns->ra = (px[offset+3]>>7)&1; dns->z = (px[offset+3]>>4)&7; dns->opcode = (px[offset+2]>>3)&0xf; dns->rcode = (px[offset+3]>>0)&0xf; dns->qdcount = px[offset+4]<<8 | px[offset+5]; dns->ancount = px[offset+6]<<8 | px[offset+7]; dns->nscount = px[offset+8]<<8 | px[offset+9]; dns->arcount = px[offset+10]<<8 | px[offset+11]; dns->rr_count = 0; /* so far */ offset += 12; dns->is_valid = 1; dns->is_formerr = 1; /* is "formate-error" until we've finished parsing */ /* 1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / QNAME / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ for (i=0; iqdcount; i++) { unsigned xclass; unsigned xtype; if (dns->rr_count >= MAX_RRs) return; dns->rr_offset[dns->rr_count++] = (unsigned short)offset; offset = dns_name_skip(px, offset, max); offset += 4; /* length of type and class */ if (offset > max) return; xclass = px[offset-2]<<8 | px[offset-1]; if (xclass != 1 && xclass != 255 && xclass != 3) return; xtype = px[offset-4]<<8 | px[offset-3]; dns->query_type = xtype; } /* 1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / / / NAME / | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| / RDATA / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ for (i=0; iancount + dns->nscount; i++) { unsigned rdlength; if (dns->rr_count >= sizeof(dns->rr_offset)/sizeof(dns->rr_offset[0])) return; dns->rr_offset[dns->rr_count++] = (unsigned short)offset; offset = dns_name_skip(px, offset, max); offset += 10; if (offset > max) return; rdlength = px[offset-2]<<8 | px[offset-1]; offset += rdlength; if (offset > max) return; } for (i=0; iarcount; i++) { unsigned rdlength; if (dns->rr_count >= sizeof(dns->rr_offset)/sizeof(dns->rr_offset[0])) return; dns->rr_offset[dns->rr_count++] = (unsigned short)offset; /* ENDS0 OPT parsing */ if (offset + 11 <= max && px[offset] == 0 && px[offset+1] == 0 && px[offset+2] == 0x29) { dns->edns0.payload_size = px[offset+3]<<8 | px[offset+4]; if (dns->edns0.payload_size < 512) return; dns->rcode |= px[offset+5]<<4; dns->edns0.version = px[offset+6]; dns->is_edns0 = 1; } offset = dns_name_skip(px, offset, max); offset += 10; if (offset > max) return; rdlength = px[offset-2]<<8 | px[offset-1]; offset += rdlength; if (offset > max) return; } dns->query_name.name = dns->query_name_buffer; dns_extract_name(px, dns->rr_offset[0], max, &dns->query_name); dns->is_formerr = 0; return; } /*************************************************************************** * Set the "syn-cookie" style information so that we can validate replies * match a valid request. We don't hold "state" on the requests, so this * becomes a hash of the port/IP information. * DNS has a two-byte "transaction id" field, so we can't use the full * cookie, just the lower two bytes of it. * Below in "handle_dns", we validate that the cookie is correct. ***************************************************************************/ unsigned dns_set_cookie(unsigned char *px, size_t length, uint64_t cookie) { if (length > 2) { px[0] = (unsigned char)(cookie >> 8); px[1] = (unsigned char)(cookie >> 0); return cookie & 0xFFFF; } else return 0; } /*************************************************************************** * Process a DNS packet received in response to UDP probes to port 53. * This function has three main tasks: * - parse the DNS protocol, and make sure it's valid DNS. * - make sure that the DNS response matches a valid request using * the "syn-cookie" approach. * - parse the "version.bind" response and report it as the version * string for the banner. ***************************************************************************/ unsigned handle_dns(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy) { ipaddress ip_them = parsed->src_ip; ipaddress ip_me = parsed->dst_ip; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; struct DNS_Incoming dns[1]; unsigned offset; uint64_t seqno; const char *reason = 0; seqno = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy); proto_dns_parse(dns, px, parsed->app_offset, parsed->app_offset + parsed->app_length); if ((seqno & 0xFFFF) != dns->id) return 1; /* * In practice, DNS queries always have the query count set to 1, * though in theory servers could support multiple queries in a * single request, almost none of them do */ if (dns->qr != 1) return 0; /* * If we get back NOERROR, we drop through and extract the strings in * the packet. Otherwise, we report the error here. */ switch (dns->rcode) { case 0: reason = 0; break; /* NOERROR */ case 1: reason = "1:FORMERR"; break; case 2: reason = "2:SERVFAIL"; break; case 3: reason = "3:NXDOMAIN"; break; case 4: reason = "4:NOTIMP"; break; case 5: reason = "5:REFUSED"; break; case 6: reason = "6:YXDOMAIN"; break; case 7: reason = "7:XRRSET"; break; case 8: reason = "8:NOTAUTH"; break; case 9: reason = "9:NOTZONE"; break; } if (reason != 0) { output_report_banner( out, timestamp, ip_them, 17, port_them, PROTO_DNS_VERSIONBIND, parsed->ip_ttl, (const unsigned char*)reason, (unsigned)strlen(reason)); return 0; } /*if (dns->qdcount != 1) return 0; if (dns->ancount < 1) return 0; if (dns->rr_count < 2) return 0;*/ offset = dns->rr_offset[1]; offset = dns_name_skip(px, offset, length); if (offset + 10 >= length) return 0; { unsigned type = px[offset+0]<<8 | px[offset+1]; unsigned xclass = px[offset+2]<<8 | px[offset+3]; unsigned rrlen = px[offset+8]<<8 | px[offset+9]; unsigned txtlen = px[offset+10]; offset += 11; /* Make sure can't exceed bounds of RR */ if (txtlen > length - offset) txtlen = length - offset; if (rrlen == 0 || txtlen > rrlen-1) return 0; if (type != 0x10 || xclass != 3) return 0; output_report_banner( out, timestamp, ip_them, 17, port_them, PROTO_DNS_VERSIONBIND, parsed->ip_ttl, px + offset, txtlen); return 1; } } ================================================ FILE: src/proto-dns.h ================================================ #ifndef PROTO_DNS_H #define PROTO_DNS_H #include #include struct PreprocessedInfo; struct Output; unsigned handle_dns(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); unsigned dns_set_cookie(unsigned char *px, size_t length, uint64_t seqno); #endif ================================================ FILE: src/proto-ftp.c ================================================ #include "proto-ftp.h" #include "proto-banner1.h" #include "unusedparm.h" #include "masscan-app.h" #include "stack-tcp-api.h" #include "proto-ssl.h" #include #include /*************************************************************************** ***************************************************************************/ static void ftp_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned i; struct FTPSTUFF *ftp = &pstate->sub.ftp; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; icode = 0; state++; /* fall through */ case 1: case 2: case 3: case 101: case 102: case 103: if (!isdigit(px[i]&0xFF)) { state = 0xffffffff; tcpapi_close(socket); } else { ftp->code *= 10; ftp->code += (px[i] - '0'); state++; banout_append_char(banout, PROTO_FTP, px[i]); } break; case 4: case 104: if (px[i] == ' ') { ftp->is_last = 1; state++; banout_append_char(banout, PROTO_FTP, px[i]); } else if (px[i] == '-') { ftp->is_last = 0; state++; banout_append_char(banout, PROTO_FTP, px[i]); } else { state = 0xffffffff; tcpapi_close(socket); } break; case 5: if (px[i] == '\r') continue; else if (px[i] == '\n') { if (ftp->is_last) { tcpapi_send(socket, "AUTH TLS\r\n", 10, 0); state = 100; banout_append_char(banout, PROTO_FTP, px[i]); } else { banout_append_char(banout, PROTO_FTP, px[i]); state = 0; } } else if (px[i] == '\0' || !isprint(px[i])) { state = 0xffffffff; tcpapi_close(socket); continue; } else { banout_append_char(banout, PROTO_FTP, px[i]); } break; case 105: if (px[i] == '\r') continue; else if (px[i] == '\n') { if (ftp->code == 234) { /* change the state here to SSL */ unsigned port = pstate->port; memset(pstate, 0, sizeof(*pstate)); pstate->app_proto = PROTO_SSL3; pstate->is_sent_sslhello = 1; pstate->port = (unsigned short)port; state = 0; tcpapi_send(socket, banner_ssl.hello, banner_ssl.hello_length, 0); } else { state = 0xffffffff; tcpapi_close(socket); } } else if (px[i] == '\0' || !isprint(px[i])) { state = 0xffffffff; tcpapi_close(socket); continue; } else { banout_append_char(banout, PROTO_FTP, px[i]); } break; default: i = (unsigned)length; break; } } pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * ftp_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); //banner1->payloads.tcp[21] = &banner_ftp; return 0; } /*************************************************************************** ***************************************************************************/ static int ftp_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_ftp = { "ftp", 21, 0, 0, 0, ftp_selftest, ftp_init, ftp_parse, }; ================================================ FILE: src/proto-ftp.h ================================================ #ifndef PROTO_FTP_H #define PROTO_FTP_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_ftp; #endif ================================================ FILE: src/proto-http.c ================================================ #include "proto-http.h" #include "proto-banner1.h" #include "stack-tcp-api.h" #include "smack.h" #include "unusedparm.h" #include "util-safefunc.h" #include "masscan-app.h" #include "util-malloc.h" #include "util-bool.h" #include "stack-tcp-core.h" #include #include #include #include enum { HTTPFIELD_INCOMPLETE, HTTPFIELD_SERVER, HTTPFIELD_CONTENT_LENGTH, HTTPFIELD_CONTENT_TYPE, HTTPFIELD_VIA, HTTPFIELD_LOCATION, HTTPFIELD_UNKNOWN, HTTPFIELD_NEWLINE, }; static struct Patterns http_fields[] = { {"Server:", 7, HTTPFIELD_SERVER, SMACK_ANCHOR_BEGIN}, //{"Content-Length:", 15, HTTPFIELD_CONTENT_LENGTH, SMACK_ANCHOR_BEGIN}, //{"Content-Type:", 13, HTTPFIELD_CONTENT_TYPE, SMACK_ANCHOR_BEGIN}, {"Via:", 4, HTTPFIELD_VIA, SMACK_ANCHOR_BEGIN}, {"Location:", 9, HTTPFIELD_LOCATION, SMACK_ANCHOR_BEGIN}, {":", 1, HTTPFIELD_UNKNOWN, 0}, {"\n", 1, HTTPFIELD_NEWLINE, 0}, {0,0,0,0} }; enum { HTML_INCOMPLETE, HTML_TITLE, HTML_UNKNOWN, }; static struct Patterns html_fields[] = { {" 0 && hdr[offset-1] == '\r') offset--; } break; case end_of_field: while (offset < header_length && hdr[offset] != '\n') offset++; if (offset < header_length && hdr[offset] == '\n') { while (offset > 0 && hdr[offset-1] == '\r') offset--; } break; case end_of_line: while (offset < header_length && hdr[offset] != '\n') offset++; if (offset < header_length && hdr[offset] == '\n') offset++; break; } return offset; } /** * Used when editing our HTTP prototype request, it replaces the existing * field (start..end) with the new field. The header is resized and data moved * to accommodate this insertion. */ static size_t _http_insert(unsigned char **r_hdr, size_t start, size_t end, size_t header_length, size_t field_length, const void *field) { size_t old_field_length = (end-start); size_t new_header_length = header_length + field_length - old_field_length; unsigned char *hdr; *r_hdr = REALLOC(*r_hdr, new_header_length + 1); hdr = *r_hdr; /* Shrink/expand the field */ memmove(&hdr[start + field_length], &hdr[end], header_length - end + 1); /* Insert the new header at this location */ memcpy(&hdr[start], field, field_length); return new_header_length; } /*************************************************************************** ***************************************************************************/ size_t http_change_requestline(unsigned char **hdr, size_t header_length, const void *field, size_t field_length, int item) { size_t offset; size_t start; /* If no length given, calculate length */ if (field_length == ~(size_t)0) field_length = strlen((const char *)field); /* GET /example.html HTTP/1.0 * 0111233333333333334 * #0 skip leading whitespace * #1 skip past method * #2 skip past space after method * #3 skip past URL field * #4 skip past space after URL * #5 skip past version */ /* #0 Skip leading whitespace */ offset = 0; offset = _skip(spaces, *hdr, offset, header_length); /* #1 Method */ start = offset; if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, "GET"); offset = _skip(notspaces, *hdr, offset, header_length); if (item == 0) { return _http_insert(hdr, start, offset, header_length, field_length, field); } /* #2 Method space */ if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, " "); offset = _skip(spaces, *hdr, offset, header_length); /* #3 URL */ start = offset; if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, "/"); offset = _skip(notspaces, *hdr, offset, header_length); if (item == 1) { return _http_insert(hdr, start, offset, header_length, field_length, field); } /* #4 Space after URL */ if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, " "); offset = _skip(spaces, *hdr, offset, header_length); /* #5 version */ start = offset; if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, "HTTP/1.0"); offset = _skip(notspaces, *hdr, offset, header_length); if (item == 2) { return _http_insert(hdr, start, offset, header_length, field_length, field); } /* ending line */ if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, "\r\n"); offset = _skip(spaces, *hdr, offset, header_length); offset = _skip(end_of_line, *hdr, offset, header_length); /* now find a blank line */ for (;;) { /* make sure there's at least one line left */ if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, "\r\n"); if (offset + 1 == header_length && (*hdr)[offset] == '\r') header_length += _http_append(hdr, header_length, field_length, "\n"); start = offset; offset = _skip(end_of_field, *hdr, offset, header_length); if (start == offset) { /* We've reached the end of the header*/ offset = _skip(end_of_line, *hdr, offset, header_length); break; } if (offset == header_length) header_length += _http_append(hdr, header_length, field_length, "\r\n"); if (offset + 1 == header_length && (*hdr)[offset] == '\r') header_length += _http_append(hdr, header_length, field_length, "\n"); offset = _skip(end_of_line, *hdr, offset, header_length); } start = offset; offset = header_length; if (item == 3) { return _http_insert(hdr, start, offset, header_length, field_length, field); } return header_length; } static size_t _field_length(const unsigned char *hdr, size_t offset, size_t hdr_length) { size_t original_offset = offset; /* Find newline */ while (offset < hdr_length && hdr[offset] != '\n') offset++; /* Trim trailing whitespace */ while (offset > original_offset && isspace(hdr[offset-1]&0xFF)) offset--; return offset - original_offset; } static size_t _next_field(const unsigned char *hdr, size_t offset, size_t hdr_length) { size_t original_offset = offset; /* Find newline */ while (offset < hdr_length && hdr[offset] != '\n') offset++; /* Remove newline too*/ if (offset > original_offset && isspace(hdr[offset-1]&0xFF)) offset++; return offset; } static bool _has_field_name(const char *name, size_t name_length, const unsigned char *hdr, size_t offset, size_t hdr_length) { size_t x; bool found_colon = false; /* Trim leading whitespace */ while (offset < hdr_length && isspace(hdr[offset]&0xFF) && hdr[offset] != '\n') offset++; /* Make sure there's enough space left */ if (hdr_length - offset < name_length) return false; /* Make sure there's colon after */ for (x = offset + name_length; xhttp_fields = smack_create("http", SMACK_CASE_INSENSITIVE); for (i=0; http_fields[i].pattern; i++) smack_add_pattern( b->http_fields, http_fields[i].pattern, http_fields[i].pattern_length, http_fields[i].id, http_fields[i].is_anchored); smack_compile(b->http_fields); /* * These match HTML html_fields = smack_create("html", SMACK_CASE_INSENSITIVE); for (i=0; html_fields[i].pattern; i++) smack_add_pattern( b->html_fields, html_fields[i].pattern, html_fields[i].pattern_length, html_fields[i].id, html_fields[i].is_anchored); smack_compile(b->html_fields); banner_http.hello = MALLOC(banner_http.hello_length); memcpy((char*)banner_http.hello, http_hello, banner_http.hello_length); return b->http_fields; } /*************************************************************************** * BIZARRE CODE ALERT! * * This uses a "byte-by-byte state-machine" to parse the response HTTP * header. This is standard practice for high-performance network * devices, but is probably unfamiliar to the average network engineer. * * The way this works is that each byte of input causes a transition to * the next state. That means we can parse the response from a server * without having to buffer packets. The server can send the response * one byte at a time (one packet for each byte) or in one entire packet. * Either way, we don't. We don't need to buffer the entire response * header waiting for the final packet to arrive, but handle each packet * individually. * * This is especially useful with our custom TCP stack, which simply * rejects out-of-order packets. ***************************************************************************/ static void http_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned i; unsigned state2; unsigned log_begin = 0; unsigned log_end = 0; size_t id; enum { FIELD_START = 9, FIELD_NAME, FIELD_COLON, FIELD_VALUE, CONTENT, CONTENT_TAG, CONTENT_FIELD, DONE_PARSING }; UNUSEDPARM(banner1_private); UNUSEDPARM(socket); state2 = (state>>16) & 0xFFFF; id = (state>>8) & 0xFF; state = (state>>0) & 0xFF; for (i=0; ihttp_fields, &state2, px, &i, (unsigned)length); i--; if (id == HTTPFIELD_NEWLINE) { state2 = 0; state = FIELD_START; } else if (id == SMACK_NOT_FOUND) ; /* continue here */ else if (id == HTTPFIELD_UNKNOWN) { /* Oops, at this point, both ":" and "Server:" will match. * Therefore, we need to make sure ":" was found, and not * a known field like "Server:" */ size_t id2; id2 = smack_next_match(banner1->http_fields, &state2); if (id2 != SMACK_NOT_FOUND) id = id2; state = FIELD_COLON; } else state = FIELD_COLON; break; case FIELD_COLON: if (px[i] == '\n') { state = FIELD_START; break; } else if (isspace(px[i])) { break; } else { //field_name(banout, id, http_fields); state = FIELD_VALUE; /* drop down */ } case FIELD_VALUE: if (px[i] == '\r') break; else if (px[i] == '\n') { state = FIELD_START; break; } switch (id) { case HTTPFIELD_SERVER: banout_append(banout, PROTO_HTTP_SERVER, &px[i], 1); break; case HTTPFIELD_LOCATION: case HTTPFIELD_VIA: //banner_append(&px[i], 1, banout); break; case HTTPFIELD_CONTENT_LENGTH: if (isdigit(px[i]&0xFF)) { ; /* TODO: add content length parsing */ } else { id = 0; } break; } break; case CONTENT: { unsigned next = i; id = smack_search_next( banner1->html_fields, &state2, px, &next, (unsigned)length); if (banner1->is_capture_html) { banout_append(banout, PROTO_HTML_FULL, &px[i], next-i); } if (id != SMACK_NOT_FOUND) { state = CONTENT_TAG; } i = next - 1; } break; case CONTENT_TAG: for (; iis_capture_html) { banout_append_char(banout, PROTO_HTML_FULL, px[i]); } if (px[i] == '>') { state = CONTENT_FIELD; break; } } break; case CONTENT_FIELD: if (banner1->is_capture_html) { banout_append_char(banout, PROTO_HTML_FULL, px[i]); } if (px[i] == '<') state = CONTENT; else { banout_append_char(banout, PROTO_HTML_TITLE, px[i]); } break; case DONE_PARSING: default: i = (unsigned)length; break; } if (log_end == 0 && state < CONTENT) log_end = i; if (log_begin < log_end) banout_append(banout, PROTO_HTTP, px + log_begin, log_end-log_begin); if (state == DONE_PARSING) pstate->state = state; else pstate->state = (state2 & 0xFFFF) << 16 | ((unsigned)id & 0xFF) << 8 | (state & 0xFF); } static const char *test_response = "HTTP/1.0 200 OK\r\n" "Date: Wed, 13 Jan 2021 18:18:25 GMT\r\n" "Expires: -1\r\n" "Cache-Control: private, max-age=0\r\n" "Content-Type: text/html; charset=ISO-8859-1\r\n" "P3P: CP=\x22This is not a P3P policy! See g.co/p3phelp for more info.\x22\r\n" "Server: gws\r\n" "X-XSS-Protection: 0\r\n" "X-Frame-Options: SAMEORIGIN\r\n" "Set-Cookie: 1P_JAR=2021-01-13-18; expires=Fri, 12-Feb-2021 18:18:25 GMT; path=/; domain=.google.com; Secure\r\n" "Set-Cookie: NID=207=QioO2ZqRsR6k1wtvXjuuhLrXYtl6ki8SQhf56doo_wcADvldNoHfnKvFk1YXdxSVTWnmqHQVPC6ZudGneMs7vDftJ6vB36B0OCDy_KetZ3sOT_ZAHcmi1pAGeO0VekZ0SYt_UXMjcDhuvNVW7hbuHEeXQFSgBywyzB6mF2EVN00; expires=Thu, 15-Jul-2021 18:18:25 GMT; path=/; domain=.google.com; HttpOnly\r\n" "Accept-Ranges: none\r\n" "Vary: Accept-Encoding\r\n" "\r\n"; /*************************************************************************** ***************************************************************************/ static int http_selftest_parser(void) { struct Banner1 *banner1 = NULL; struct StreamState pstate[1]; struct BannerOutput banout[1]; memset(pstate, 0, sizeof(pstate[0])); memset(banout, 0, sizeof(banout[0])); /* * Test start */ banner1 = banner1_create(); banner1->is_capture_servername = 1; memset(pstate, 0, sizeof(pstate[0])); banout_init(banout); /* * Run Test */ http_parse(banner1, 0, pstate, (const unsigned char *)test_response, strlen(test_response), banout, 0); /* * Verify results */ { const unsigned char *string; size_t length; string = banout_string(banout, PROTO_HTTP_SERVER); length = banout_string_length(banout, PROTO_HTTP_SERVER); if (length != 3 || memcmp(string, "gws", 3) != 0) { fprintf(stderr, "[-] HTTP parser failed: %s %u\n", __FILE__, __LINE__); return 1; /* failure */ } } /* * Test end */ banner1_destroy(banner1); banout_release(banout); return 0; /* success */ } /*************************************************************************** ***************************************************************************/ static int http_selftest_config(void) { size_t i; static const struct {const char *from; const char *to;} urlsamples[] = { {"", "GET /foo.html"}, {"GET / HTTP/1.0\r\n\r\n", "GET /foo.html HTTP/1.0\r\n\r\n"}, {"GET /longerthan HTTP/1.0\r\n\r\n", "GET /foo.html HTTP/1.0\r\n\r\n"}, {0,0} }; static const struct {const char *from; const char *to;} methodsamples[] = { {"", "POST"}, {"GET / HTTP/1.0\r\n\r\n", "POST / HTTP/1.0\r\n\r\n"}, {"O / HTTP/1.0\r\n\r\n", "POST / HTTP/1.0\r\n\r\n"}, {0,0} }; static const struct {const char *from; const char *to;} versionsamples[] = { {"", "GET / HTTP/1.1"}, {"GET / FOO\r\n\r\n", "GET / HTTP/1.1\r\n\r\n"}, {"GET / XXXXXXXXXXXX\r\n\r\n", "GET / HTTP/1.1\r\n\r\n"}, {0,0} }; static const struct {const char *from; const char *to;} fieldsamples[] = { {"GET / HTTP/1.0\r\nfoobar: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoobar: a\r\nHost: xyz\r\nfoo: bar\r\n\r\n"}, {"GET / HTTP/1.0\r\nfoo:abc\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoo: bar\r\nHost: xyz\r\n\r\n"}, {"GET / HTTP/1.0\r\nfoo: abcdef\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoo: bar\r\nHost: xyz\r\n\r\n"}, {"GET / HTTP/1.0\r\nfoo: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfoo: bar\r\nHost: xyz\r\n\r\n"}, {"GET / HTTP/1.0\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nHost: xyz\r\nfoo: bar\r\n\r\n"}, {0,0} }; static const struct {const char *from; const char *to;} removesamples[] = { {"GET / HTTP/1.0\r\nfoo: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nHost: xyz\r\n\r\n"}, {"GET / HTTP/1.0\r\nfooa: a\r\nHost: xyz\r\n\r\n", "GET / HTTP/1.0\r\nfooa: a\r\nHost: xyz\r\n\r\n"}, {0,0} }; static const struct {const char *from; const char *to;} payloadsamples[] = { {"", "GET / HTTP/1.0\r\n\r\nfoo"}, {"GET / HTTP/1.0\r\nHost: xyz\r\n\r\nbar", "GET / HTTP/1.0\r\nHost: xyz\r\n\r\nfoo"}, {0,0} }; /* Test replacing URL */ for (i=0; urlsamples[i].from; i++) { unsigned char *x = (unsigned char*)STRDUP(urlsamples[i].from); size_t len1 = strlen((const char *)x); size_t len2; size_t len3 = strlen(urlsamples[i].to); /* Replace whatever URL is in the header with this new one */ len2 = http_change_requestline(&x, len1, "/foo.html", ~(size_t)0, 1); if (len2 != len3 && memcmp(urlsamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config URL sample #%u\n", (unsigned)i); return 1; } } /* Test replacing method */ for (i=0; methodsamples[i].from; i++) { unsigned char *x = (unsigned char*)STRDUP(methodsamples[i].from); size_t len1 = strlen((const char *)x); size_t len2; size_t len3 = strlen(methodsamples[i].to); len2 = http_change_requestline(&x, len1, "POST", ~(size_t)0, 0); if (len2 != len3 && memcmp(methodsamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config method sample #%u\n", (unsigned)i); return 1; } } /* Test replacing version */ for (i=0; versionsamples[i].from; i++) { unsigned char *x = (unsigned char*)STRDUP(versionsamples[i].from); size_t len1 = strlen((const char *)x); size_t len2; size_t len3 = strlen(versionsamples[i].to); len2 = http_change_requestline(&x, len1, "HTTP/1.1", ~(size_t)0, 2); if (len2 != len3 && memcmp(versionsamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config version sample #%u\n", (unsigned)i); return 1; } } /* Test payload */ for (i=0; payloadsamples[i].from; i++) { unsigned char *x = (unsigned char*)STRDUP(payloadsamples[i].from); size_t len1 = strlen((const char *)x); size_t len2; size_t len3 = strlen(payloadsamples[i].to); len2 = http_change_requestline(&x, len1, "foo", ~(size_t)0, 3); if (len2 != len3 && memcmp(payloadsamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config payload sample #%u\n", (unsigned)i); return 1; } } /* Test adding fields */ for (i=0; fieldsamples[i].from; i++) { unsigned char *x; size_t len1 = strlen((const char *)fieldsamples[i].from); size_t len2; size_t len3 = strlen(fieldsamples[i].to); /* Replace whatever URL is in the header with this new one */ x = (unsigned char*)STRDUP(fieldsamples[i].from); len2 = http_change_field(&x, len1, "foo", (const unsigned char *)"bar", ~(size_t)0, http_field_replace); if (len2 != len3 || memcmp(fieldsamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config header field sample #%u\n", (unsigned)i); return 1; } free(x); /* Same test as above, but when name specified with a colon */ x = (unsigned char*)STRDUP(fieldsamples[i].from); len2 = http_change_field(&x, len1, "foo:", (const unsigned char *)"bar", ~(size_t)0, http_field_replace); if (len2 != len3 || memcmp(fieldsamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config header field sample #%u\n", (unsigned)i); return 1; } free(x); /* Same test as above, but with name having additional space */ x = (unsigned char*)STRDUP(fieldsamples[i].from); len2 = http_change_field(&x, len1, "foo : : ", (const unsigned char *)"bar", ~(size_t)0, http_field_replace); if (len2 != len3 || memcmp(fieldsamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config header field sample #%u\n", (unsigned)i); return 1; } free(x); } /* Removing fields */ for (i=0; removesamples[i].from; i++) { unsigned char *x = (unsigned char*)STRDUP(removesamples[i].from); size_t len1 = strlen((const char *)x); size_t len2; size_t len3 = strlen(removesamples[i].to); /* Replace whatever URL is in the header with this new one */ len2 = http_change_field(&x, len1, "foo", (const unsigned char *)"bar", ~(size_t)0, http_field_remove); if (len2 != len3 || memcmp(removesamples[i].to, x, len3) != 0) { fprintf(stderr, "[-] HTTP.selftest: config remove field sample #%u\n", (unsigned)i); return 1; } free(x); } return 0; } /*************************************************************************** * Called when `--selftest` command-line parameter in order to do some * basic unit testing of this module. ***************************************************************************/ static int http_selftest(void) { int err; /* Test parsing HTTP responses */ err = http_selftest_parser(); if (err) return 1; /* failure */ /* Test configuring HTTP requests */ err = http_selftest_config(); if (err) return 1; /* failure */ return 0; /* success */ } /*************************************************************************** ***************************************************************************/ struct ProtocolParserStream banner_http = { "http", 80, http_hello, sizeof(http_hello)-1, 0, http_selftest, http_init, http_parse, }; ================================================ FILE: src/proto-http.h ================================================ #ifndef PROTO_HTTP_H #define PROTO_HTTP_H #include "proto-banner1.h" #include "util-bool.h" extern struct ProtocolParserStream banner_http; /** * Called during configuration when processing a command-line option * like "--http-field " to add/change a field in the HTTP * header. */ size_t http_change_field(unsigned char **inout_header, size_t header_length, const char *field_name, const unsigned char *field_value, size_t field_value_len, int what); /** * Called during configuration when processing a command-line option * like "--http-url /foo.html". This replaces whatever the existing * URL is into the new one. * @param item * 0=method, 1=url, 2=version * @return * the new length of the header (expanded or shrunk) */ size_t http_change_requestline(unsigned char **inout_header, size_t header_length, const void *url, size_t url_length, int item); #endif ================================================ FILE: src/proto-icmp.c ================================================ #include "proto-icmp.h" #include "proto-preprocess.h" #include "syn-cookie.h" #include "util-logger.h" #include "output.h" #include "masscan-status.h" #include "massip-port.h" #include "main-dedup.h" /*************************************************************************** ***************************************************************************/ static int matches_me(struct Output *out, ipaddress ip, unsigned port) { unsigned i; for (i=0; i<8; i++) { if (is_myself(&out->src[i], ip, port)) return 1; } return 0; } /*************************************************************************** ***************************************************************************/ static int parse_port_unreachable(const unsigned char *px, unsigned length, unsigned *r_ip_me, unsigned *r_ip_them, unsigned *r_port_me, unsigned *r_port_them, unsigned *r_ip_proto) { if (length < 24) return -1; *r_ip_me = px[12]<<24 | px[13]<<16 | px[14]<<8 | px[15]; *r_ip_them = px[16]<<24 | px[17]<<16 | px[18]<<8 | px[19]; *r_ip_proto = px[9]; /* TCP=6, UDP=17 */ px += (px[0]&0xF)<<2; length -= (px[0]&0xF)<<2; if (length < 4) return -1; *r_port_me = px[0]<<8 | px[1]; *r_port_them = px[2]<<8 | px[3]; return 0; } /*************************************************************************** * This is where we handle all incoming ICMP packets. Some of these packets * will be due to scans we are doing, like pings (echoes). Some will * be inadvertent, such as "destination unreachable" messages. ***************************************************************************/ void handle_icmp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy) { unsigned type = parsed->port_src; unsigned code = parsed->port_dst; unsigned seqno_me; ipaddress ip_me = parsed->dst_ip; ipaddress ip_them = parsed->src_ip; unsigned cookie; /* dedup ICMP echo replies as well as SYN/ACK replies */ static struct DedupTable *echo_reply_dedup = NULL; if (!echo_reply_dedup) echo_reply_dedup = dedup_create(); seqno_me = px[parsed->transport_offset+4]<<24 | px[parsed->transport_offset+5]<<16 | px[parsed->transport_offset+6]<<8 | px[parsed->transport_offset+7]<<0; switch (type) { case 0: /* ICMP echo reply */ case 129: cookie = (unsigned)syn_cookie(ip_them, Templ_ICMP_echo, ip_me, 0, entropy); if ((cookie & 0xFFFFFFFF) != seqno_me) return; /* not my response */ if (dedup_is_duplicate(echo_reply_dedup, ip_them, 0, ip_me, 0)) break; //if (syn_hash(ip_them, Templ_ICMP_echo) != seqno_me) // return; /* not my response */ /* * Report "open" or "existence" of host */ output_report_status( out, timestamp, PortStatus_Open, ip_them, 1, /* ip proto */ 0, 0, parsed->ip_ttl, parsed->mac_src); break; case 3: /* destination unreachable */ switch (code) { case 0: /* net unreachable */ /* We get these a lot while port scanning, often a flood coming * back from broken/misconfigured networks */ break; case 1: /* host unreachable */ /* This means the router doesn't exist */ break; case 2: /* protocol unreachable */ /* The host exists, but it doesn't support SCTP */ break; case 3: /* port unreachable */ if (length - parsed->transport_offset > 8) { ipaddress ip_me2; ipaddress ip_them2; unsigned port_me2, port_them2; unsigned ip_proto; int err; ip_me2.version = 4; ip_them2.version = 4; err = parse_port_unreachable( px + parsed->transport_offset + 8, length - parsed->transport_offset + 8, &ip_me2.ipv4, &ip_them2.ipv4, &port_me2, &port_them2, &ip_proto); if (err) return; if (!matches_me(out, ip_me2, port_me2)) return; switch (ip_proto) { case 6: output_report_status( out, timestamp, PortStatus_Closed, ip_them2, ip_proto, port_them2, 0, parsed->ip_ttl, parsed->mac_src); break; case 17: output_report_status( out, timestamp, PortStatus_Closed, ip_them2, ip_proto, port_them2, 0, parsed->ip_ttl, parsed->mac_src); break; case 132: output_report_status( out, timestamp, PortStatus_Closed, ip_them2, ip_proto, port_them2, 0, parsed->ip_ttl, parsed->mac_src); break; } } } break; default: ; } } ================================================ FILE: src/proto-icmp.h ================================================ #ifndef PROTO_ICMP_H #define PROTO_ICMP_H #include #include struct PreprocessedInfo; struct Output; void handle_icmp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); #endif ================================================ FILE: src/proto-imap4.c ================================================ /* imap4 banner checker */ #include "proto-imap4.h" #include "proto-banner1.h" #include "unusedparm.h" #include "masscan-app.h" #include "stack-tcp-api.h" #include "proto-ssl.h" #include #include /*************************************************************************** ***************************************************************************/ static void imap4_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned i; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; iport; memset(pstate, 0, sizeof(*pstate)); pstate->app_proto = PROTO_SSL3; pstate->is_sent_sslhello = 1; pstate->port = (unsigned short)port; state = 0; tcpapi_send(socket, banner_ssl.hello, banner_ssl.hello_length, 0); break; } break; case 0xffffffff: default: i = (unsigned)length; break; } } pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * imap4_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /*************************************************************************** ***************************************************************************/ static int imap4_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_imap4 = { "imap4", 21, 0, 0, 0, imap4_selftest, imap4_init, imap4_parse, }; ================================================ FILE: src/proto-imap4.h ================================================ #ifndef PROTO_IMAP4_H #define PROTO_IMAP4_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_imap4; #endif ================================================ FILE: src/proto-isakmp.c ================================================ /* ISAKMP protocol support 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Initiator ! ! Cookie ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Responder ! ! Cookie ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Message ID ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Length ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #include "proto-isakmp.h" #include "proto-banout.h" #include "proto-preprocess.h" #include "syn-cookie.h" #include "massip-port.h" #include "output.h" #include "util-extract.h" #include "util-logger.h" #include #include typedef struct payload_t { unsigned char next; unsigned char reserved; size_t length; struct ebuf_t ebuf; } payload_t; static payload_t _get_payload(const struct ebuf_t *ebuf) { payload_t result = {0}; result.ebuf = *ebuf; result.next = e_next_byte(&result.ebuf); result.reserved = e_next_byte(&result.ebuf); result.length = e_next_short16(&result.ebuf, EBUF_BE); if (result.length >= 4) { result.ebuf.max = result.ebuf.offset + result.length - 4; } return result; } static unsigned _parse_transform(struct BannerOutput *banout, unsigned proto, struct ebuf_t in_ebuf) { struct ebuf_t *ebuf = &in_ebuf; unsigned transform_id; e_next_byte(ebuf); /* transform number */ transform_id = e_next_byte(ebuf); switch (transform_id) { case 1: { banout_printf(banout, proto, "trans=IKE "); e_next_short16(ebuf, EBUF_BE); /* reserved */ while (ebuf->offset < ebuf->max) { unsigned x = e_next_short16(ebuf, EBUF_BE); unsigned val = e_next_short16(ebuf, EBUF_BE); if ((x & 0x8000) == 0) return 1; switch (x&0x7fff) { case 1: /* encryption algorithm */ switch (val) { case 5: banout_printf(banout, proto, "%s ", "3DES-CBC"); break; case 7: banout_printf(banout, proto, "%s ", "AES-CBC"); break; default: banout_printf(banout, proto, "encrypt=0x%x ", val); break; } break; case 2: /* hash algorithm */ switch (val) { case 2: banout_printf(banout, proto, "%s ", "SHA"); break; default: banout_printf(banout, proto, "hash=0x%x ", val); break; } break; case 3: /* auth */ switch (val) { case 1: banout_printf(banout, proto, "%s ", "PSK"); break; case 5: banout_printf(banout, proto, "%s ", "PSK"); break; default: banout_printf(banout, proto, "auth=0x%x ", val); break; } break; case 4: /* group */ break; case 11: /* life type */ break; case 12: /* life duration*/ break; case 14: /* key length */ banout_printf(banout, proto, "key=%ubits ", val); break; default: banout_printf(banout, proto, "val=0x%04x%04x ", x&0x7fff, val); break; } } } break; default: banout_printf(banout, proto, "trans=%u ", transform_id); break; } return 1; } static unsigned _parse_transforms(struct BannerOutput *banout, unsigned proto, struct ebuf_t ebuf, unsigned next_payload ) { while (ebuf.offset + 4 <= ebuf.max) { payload_t payload = _get_payload(&ebuf); _parse_transform(banout, proto, payload.ebuf); /* loop around */ ebuf.offset += payload.length; next_payload = payload.next; if (next_payload == 0) break; } return 0; } static unsigned _parse_proposal(struct BannerOutput *banout, unsigned proto, struct ebuf_t ebuf) { unsigned proto_id; banout_printf(banout, proto, "%u ", e_next_byte(&ebuf)); proto_id = e_next_byte(&ebuf); switch (proto_id) { case 1: banout_printf(banout, proto, "id=ISAKMP "); break; default: banout_printf(banout, proto, "id=%u ", proto_id); break; } e_next_byte(&ebuf); /* spi size */ e_next_byte(&ebuf); /* proposal transforms */ _parse_transforms(banout, proto, ebuf, 0); return 1; } static unsigned _parse_proposals(struct BannerOutput *banout, unsigned proto, struct ebuf_t ebuf, unsigned next_payload ) { while (ebuf.offset + 4 <= ebuf.max) { payload_t payload = _get_payload(&ebuf); _parse_proposal(banout, proto, payload.ebuf); /* loop around */ ebuf.offset += payload.length; next_payload = payload.next; if (next_payload == 0) break; } return 0; } static unsigned _payload_security_association(struct BannerOutput *banout, unsigned proto, struct ebuf_t ebuf) { unsigned doi; unsigned bitmap; doi = e_next_int32(&ebuf, EBUF_BE); bitmap = e_next_int32(&ebuf, EBUF_BE); switch (doi) { case 0: /* generic */ banout_printf(banout, proto, "DOI=generic "); break; case 1: /* IPsec */ banout_printf(banout, proto, "DOI=ipsec "); if (bitmap & 0x00000001) banout_printf(banout, proto, "IDENTITY "); if (bitmap & 0x00000002) banout_printf(banout, proto, "SECRECY "); if (bitmap & 0x00000004) banout_printf(banout, proto, "INTEGRITY "); _parse_proposals(banout, proto, ebuf, 0); break; default: banout_printf(banout, proto, "DOI=%u ", doi); break; } return 1; } static unsigned _payload_vendor_id(struct BannerOutput *banout, unsigned proto, struct ebuf_t ebuf) { size_t i; size_t length = ebuf.max - ebuf.offset; struct { unsigned length; const char *vendor; const char *name; } vendors[] = { {16, "\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f", "RFC-39947-NAT"}, {16, "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00", "CISCO-UNITY"}, {16, "\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00", "RFC3706-DPD"}, {8, "\x09\x00" "&\x89\xdf\xd6\xb7\x12", "XAUTH"}, {0,0} }; for (i=0; vendors[i].length; i++) { if (length != vendors[i].length) continue; if (memcmp(vendors[i].vendor, ebuf.buf + ebuf.offset, length) == 0) { banout_printf(banout, proto, "{%s} ", vendors[i].name); break; } } return 1; } static unsigned _parse_response(struct BannerOutput *banout, unsigned proto, const unsigned char *px, size_t length ) { struct ebuf_t ebuf[1] = {{px, 0, length}}; unsigned next_payload; unsigned version; unsigned flags; unsigned exchange_type; unsigned my_length; static const char *payload_names[] = { "[0]", "[SEC-ASSOC]", "[2]", "[3]", "[KEY-XCHG]", "[5]", "[6]", "[7]", "[8]", "[9]", "[NONCE]", "[11]", "[12]", ""/*vendor-id*/, "[14]", "[15]", "[16]", "[17]", "[18]", "[19]", "[NAT-D]", "[21]", "[22]", "[23]", "[24]", "[25]", "[26]", "[27]", "[28]", "[29]", "[30]", "[31]", }; /* 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Initiator ! ! Cookie ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Responder ! ! Cookie ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Message ID ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Length ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /* Skip the cookies */ e_next_long64(ebuf, EBUF_BE); e_next_long64(ebuf, EBUF_BE); /* Parse the header */ next_payload = e_next_byte(ebuf); version = e_next_byte(ebuf); exchange_type = e_next_byte(ebuf); flags = e_next_byte(ebuf); /* flags */ e_next_int32(ebuf, 0); my_length = e_next_int32(ebuf, EBUF_BE); if (ebuf->max >= my_length) ebuf->max = my_length; banout_printf(banout, proto, "v%u.%u ", (version>>4)&0xF, version&0xF); switch (exchange_type) { case 2: banout_printf(banout, proto, "xchg=id-prot "); break; default: banout_printf(banout, proto, "xchg=%u ", exchange_type); break; } if (flags & 1) { banout_printf(banout, proto, "ENCRYPTED ", exchange_type); return 1; } /* * Enumerate all the payloads */ while (next_payload && ebuf->offset + 4 <= ebuf->max) { /* * Parse this payload-header */ payload_t payload = _get_payload(ebuf); /* * Print the payload name if it's in our list of known names, * or print a number if it isn't */ if (next_payload < sizeof(payload_names)/sizeof(payload_names[0])) banout_printf(banout, proto, "%s ", payload_names[next_payload]); else banout_printf(banout, proto, "[%u] ", next_payload); /* * Handle the individual payload if it's a known type */ switch (next_payload) { case 1: _payload_security_association(banout, proto, payload.ebuf); break; case 4: /* key exchange */ //banout_printf(banout, proto, "KEY-EXCH "); break; case 10: /* nonce */ //banout_printf(banout, proto, "NONCE "); break; case 13: /* vendir id */ _payload_vendor_id(banout, proto, payload.ebuf); break; case 20: //banout_printf(banout, proto, "NAT-D "); break; default: break; } /* * Loop around to the next payload */ ebuf->offset += payload.length; next_payload = payload.next; } return 1; } unsigned isakmp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ) { ipaddress ip_them; ipaddress ip_me; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; uint64_t cookie; uint64_t resp_cookie; /* All responses will be at least 8 bytes */ if (length < 16) return 0; /* Grab IP addresses */ ip_them = parsed->src_ip; ip_me = parsed->dst_ip; /* Calculate the expected SYN-cookie */ cookie = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy); /* Extract the SYN-cookie from the response. We just do this byte-by-byte */ resp_cookie = (uint64_t)px[0] << 56ull; resp_cookie |= (uint64_t)px[1] << 48ull; resp_cookie |= (uint64_t)px[2] << 40ull; resp_cookie |= (uint64_t)px[3] << 32ull; resp_cookie |= (uint64_t)px[4] << 24ull; resp_cookie |= (uint64_t)px[5] << 16ull; resp_cookie |= (uint64_t)px[6] << 8ull; resp_cookie |= (uint64_t)px[7] << 0ull; if (resp_cookie != cookie) { /* If they aren't equal, then this is some other protocol. * TODO: we should use a heuristic on these bytes to * discover what the protocol probably is */ /*output_report_banner(out, timestamp, ip_them, 17, port_them, PROTO_ERROR, parsed->ip_ttl, (unsigned char *) "IP-MISSMATCH", 12);*/ return 0; } else { /* We've found our protocol, so report the banner * TODO: we should parse this better. */ struct BannerOutput banout[1]; banout_init(banout); /* Parse the packet and generate strings */ _parse_response(banout, PROTO_ISAKMP, px, length); /* Print the banner to the output */ output_report_banner( out, timestamp, ip_them, 17, port_them, PROTO_ISAKMP, parsed->ip_ttl, banout_string(banout, PROTO_ISAKMP), banout_string_length(banout, PROTO_ISAKMP)); banout_release(banout); return 1; } } unsigned isakmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) { /* The frame header starts with an 8 bytes init cookie, which is just fine for us */ unsigned char i; if (length < 8) return 0; for(i = 0; i < 8; i++) px[i] = (unsigned char)(seqno >> (56 - 8 * i)); return 0; } static const unsigned char sample1[] = "\x00\x00\x00\x00\xc1\x18" "\x84\xda\xbe\x3d\xc6\x8e\xea\xf2\xda\xac\x01\x10\x02\x00\x00\x00" "\x00\x00\x00\x00\x00\x50\x00\x00\x00\x34\x00\x00\x00\x01\x00\x00" "\x00\x01\x00\x00\x00\x28\x01\x01\x00\x01\x00\x00\x00\x20\x01\x01" "\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02\x80\x04\x00\x02\x80\x03" "\x00\x01\x80\x0b\x00\x01\x80\x0c\x00\x01"; static const unsigned char sample2[] = "\xe4\x7a\x59\x1f\xd0\x57" "\x58\x7f\xa0\x0b\x8e\xf0\x90\x2b\xb8\xec\x01\x10\x02\x00\x00\x00" "\x00\x00\x00\x00\x00\x6c\x0d\x00\x00\x3c\x00\x00\x00\x01\x00\x00" "\x00\x01\x00\x00\x00\x30\x01\x01\x00\x01\x00\x00\x00\x28\x01\x01" "\x00\x00\x80\x01\x00\x07\x80\x0e\x00\x80\x80\x02\x00\x02\x80\x04" "\x00\x02\x80\x03\x00\x01\x80\x0b\x00\x01\x00\x0c\x00\x04\x00\x01" "\x51\x80\x00\x00\x00\x14\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57" "\x28\xf2\x0e\x95\x45\x2f"; static const unsigned char sample3[] = "\xe4\x7a\x59\x1f\xd0\x57\x58\x7f\xa0\x0b\x8e\xf0\x90\x2b\xb8\xec" "\x04\x10\x02\x00\x00\x00\x00\x00\x00\x00\x01\x30\x0a\x00\x00\x84" "\x6d\x02\x6d\x56\x16\xc4\x5b\xe0\x5e\x5b\x89\x84\x11\xe9\xf9\x5d" "\x19\x5c\xea\x00\x9a\xd2\x2c\x62\xbe\xf0\x6c\x57\x1b\x7c\xfb\xc4" "\x79\x2f\x45\x56\x4e\xc7\x10\xac\x58\x4a\xa1\x8d\x20\xcb\xc8\xf5" "\xf8\x91\x06\x66\xb8\x9e\x4e\xe2\xf9\x5a\xbc\x02\x30\xe2\xcb\xa1" "\xb8\x8a\xc4\xbb\xa7\xfc\xc8\x18\xa9\x86\xc0\x1a\x4c\xa8\x65\xa5" "\xeb\x82\x88\x4d\xbe\xc8\x5b\xfd\x7d\x1a\x30\x3b\x09\x89\x4d\xcf" "\x2e\x37\x85\xfd\x79\xdb\xa2\x25\x37\x7c\xf8\xcc\xa0\x09\xce\xff" "\xbb\x6a\xa3\x8b\x64\x8c\x4b\x05\x40\x4f\x1c\xfa\xac\x36\x1a\xff" "\x0d\x00\x00\x18\x15\xb6\x88\x42\x1e\xd5\xc3\xdd\x92\xd3\xb8\x6e" "\x47\xa7\x6f\x0d\x39\xcc\x09\xe0\x0d\x00\x00\x14\x12\xf5\xf2\x8c" "\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00\x0d\x00\x00\x14" "\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00" "\x0d\x00\x00\x14\x55\xcc\x29\xed\x90\x2a\xb8\xec\x53\xb1\xdf\x86" "\x7c\x61\x09\x29\x14\x00\x00\x0c\x09\x00\x26\x89\xdf\xd6\xb7\x12" "\x14\x00\x00\x18\xfe\xbf\x46\x2f\x1c\xd7\x58\x05\xa7\xba\xa2\x87" "\x47\xe7\x69\xd6\x74\xf8\x56\x00\x00\x00\x00\x18\x15\x74\xd6\x4c" "\x01\x65\xba\xd1\x6a\x02\x3f\x03\x8d\x45\xa0\x74\x98\xd8\xd0\x51"; const char sample4[] = "\xe4\x7a\x59\x1f\xd0\x57\x58\x7f\xa0\x0b\x8e\xf0\x90\x2b\xb8\xec" "\x05\x10\x02\x01\x00\x00\x00\x00\x00\x00\x00\x4c\xb0\x32\xaa\xa6" "\x2a\x70\x71\x8e\xf2\xf0\x99\xcd\xd8\xbf\x6e\xb9\x04\x42\xed\x9d" "\x72\x6d\xaa\x6b\x6d\xad\x62\x40\x26\xf5\xfb\xb1\x73\xd9\xf7\x75" "\x71\xc2\x32\xa5\x6a\xcf\xe1\x2c\x74\x03\xe9\x53"; static int _test_sample(const void *sample, size_t sizeof_sample, const char *expected) { int is_valid; struct BannerOutput banout[1]; /* Initialize printing banners */ banout_init(banout); /* Parse the sample */ is_valid = _parse_response(banout, PROTO_ISAKMP, (const unsigned char*)sample, sizeof_sample); /* If there was a parse error, then*/ if (!is_valid) goto fail; { const unsigned char *str = banout_string(banout, PROTO_ISAKMP); size_t str_length = banout_string_length(banout, PROTO_ISAKMP); //printf("%.*s\n", (unsigned)str_length, str); if (str_length < strlen(expected) || memcmp(str, expected, strlen(expected)) != 0) goto fail; } banout_release(banout); return 0; fail: banout_release(banout); return 0; } /**************************************************************************** ****************************************************************************/ int proto_isakmp_selftest(void) { unsigned fail_count = 0; LOG(1, "[ ] ISAKMP: selftesting...\n"); fail_count += _test_sample( sample1, sizeof(sample1)-1, "v1.0 xchg=id-prot [SEC-ASSOC] DOI=ipsec IDENTITY 1 id=ISAKMP trans=IKE 3DES-CBC SHA PSK"); fail_count += _test_sample( sample2, sizeof(sample2)-1, "v1.0 xchg=id-prot [SEC-ASSOC] DOI=ipsec IDENTITY 1 id=ISAKMP trans=IKE AES-CBC key=128bits SHA PSK {RFC-39947-NAT}"); fail_count += _test_sample( sample3, sizeof(sample3)-1, "v1.0 xchg=id-prot [KEY-XCHG] [NONCE] {CISCO-UNITY} {RFC3706-DPD} {XAUTH} [NAT-D] [NAT-D]"); fail_count += _test_sample( sample4, sizeof(sample4)-1, "v1.0 xchg=id-prot ENCRYPTED"); if (fail_count) goto fail; LOG(1, "[-] ISAKMP: success\n"); return 0; fail: LOG(1, "[-] ISAKMP: fail\n"); return 1; } ================================================ FILE: src/proto-isakmp.h ================================================ #ifndef PROTO_ISAKMP_H #define PROTO_ISAKMP_H #include #include #include struct Output; struct PreprocessedInfo; unsigned isakmp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); unsigned isakmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno); int proto_isakmp_selftest(void); #endif ================================================ FILE: src/proto-mc.c ================================================ #include "proto-mc.h" #include "proto-banner1.h" #include "unusedparm.h" #include "masscan-app.h" #include "stack-tcp-api.h" #include "output.h" #include #include #include static unsigned char hand_shake_ptr[128]; static unsigned char * hand_shake(uint16_t port, const char* ip, size_t ip_len) { size_t tlen = 10+ip_len; unsigned char * ret = (unsigned char *)calloc(1,tlen); ret[0] = (unsigned char)(7+ip_len); ret[2] = 0xf7; ret[3] = 5; ret[4] = (unsigned char)ip_len; memcpy(ret+5,ip,ip_len); ret[tlen-5] = (unsigned char)(port>>8); ret[tlen-4] = (unsigned char)(port&0xff); ret[tlen-3] = 1; ret[tlen-2] = 1; ret[tlen-1] = 0; return ret; } static void * memstr(void * mem, size_t len, char * str) { size_t i; size_t stlen = strlen(str); if(len < stlen) return 0; for(i = 0; i < len-stlen; i++) { if(!memcmp((char*)mem+i,str,stlen)) return (char*)mem+i; } return 0; } /*************************************************************************** ***************************************************************************/ static void mc_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { size_t i; struct MCSTUFF *mc = &pstate->sub.mc; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for(i = 0; i < length; i++) { if(px[i] == '{') mc->brackcount++; if(px[i] == '}') mc->brackcount--; } if(mc->brackcount <= 0) tcpapi_close(socket); if((mc->imgstart&&mc->imgend) || mc->brackcount <= 0) { // we already found and removed image data banout_append(banout, PROTO_MC,px,length); } else { mc->banmem = realloc(mc->banmem,mc->totalLen+length+1); // expand to add new memory for added paket memcpy(mc->banmem+mc->totalLen,px,length); // copy in new packet mc->banmem[mc->totalLen] = 0; // add ending 0 for str mc->totalLen+=length; if(!mc->imgstart) { // dont search again if we found start mc->imgstart = (size_t)memstr(mc->banmem,mc->totalLen,"data:image/png;base64"); if(mc->imgstart) mc->imgstart-=(size_t)mc->banmem; } else { // we found start but not the end mc->imgend = (size_t)memchr(mc->banmem+mc->imgstart,'\"',mc->totalLen-mc->imgstart); if(mc->imgend){ // we found the end mc->imgend-=(size_t)mc->banmem; memcpy(mc->banmem+mc->imgstart,mc->banmem+mc->imgend,(mc->totalLen-mc->imgend)+1); // copy data after B64 mc->totalLen=mc->imgstart+(mc->totalLen-mc->imgend); // shrink length to subtract B64 image banout_append(banout, PROTO_MC,mc->banmem,mc->totalLen); // print out banner minus image data free(mc->banmem); // we dont need to keep track of this any more. } } } } /*************************************************************************** ***************************************************************************/ static void * mc_init(struct Banner1 *banner1) { unsigned char * tmp = hand_shake(25565,"localhost",9); memcpy(hand_shake_ptr,tmp,tmp[0]+3); free(tmp); banner_mc.hello = hand_shake_ptr; banner_mc.hello_length = hand_shake_ptr[0]+3; banner1->payloads.tcp[25565] = (void*)&banner_mc; return 0; } /*************************************************************************** ***************************************************************************/ static int mc_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ struct ProtocolParserStream banner_mc = { "mc", 25565, 0, 0, 0, mc_selftest, mc_init, mc_parse, }; ================================================ FILE: src/proto-mc.h ================================================ #ifndef PROTO_MC_H #define PROTO_MC_H #include "proto-banner1.h" #include "util-bool.h" extern struct ProtocolParserStream banner_mc; #endif ================================================ FILE: src/proto-memcached.c ================================================ /* memcached banner check */ #include "proto-memcached.h" #include "proto-banner1.h" #include "smack.h" #include "unusedparm.h" #include "masscan-app.h" #include "output.h" #include "stack-tcp-api.h" #include "proto-preprocess.h" #include "proto-ssl.h" #include "proto-udp.h" #include "syn-cookie.h" #include "massip-port.h" #include "util-malloc.h" #include #include #include struct SMACK *sm_memcached_responses; struct SMACK *sm_memcached_stats; enum { MC_ERROR, MC_CLIENT_ERROR, MC_SERVER_ERROR, MC_STORED, MC_NOT_STORED, MC_EXISTS, MC_NOT_FOUND, MC_END, MC_VALUE, MC_DELETED, MC_TOUCHED, MC_OK, MC_BUSY, MC_BADCLASS, MC_NOSPARE, MC_NOTFULL, MC_UNSAFE, MC_SAME, MC_STAT, MC_empty, }; static struct Patterns memcached_responses[] = { {"ERROR", 0, MC_ERROR, SMACK_ANCHOR_BEGIN}, {"CLIENT_ERROR", 0, MC_CLIENT_ERROR, SMACK_ANCHOR_BEGIN}, {"SERVER_ERROR", 0, MC_SERVER_ERROR, SMACK_ANCHOR_BEGIN}, {"STORED", 0, MC_STORED, SMACK_ANCHOR_BEGIN}, {"NOT_STORED", 0, MC_NOT_STORED, SMACK_ANCHOR_BEGIN}, {"EXISTS", 0, MC_EXISTS, SMACK_ANCHOR_BEGIN}, {"NOT_FOUND", 0, MC_NOT_FOUND, SMACK_ANCHOR_BEGIN}, {"END", 0, MC_END, SMACK_ANCHOR_BEGIN}, {"VALUE", 0, MC_VALUE, SMACK_ANCHOR_BEGIN}, {"DELETED", 0, MC_DELETED, SMACK_ANCHOR_BEGIN}, {"TOUCHED", 0, MC_TOUCHED, SMACK_ANCHOR_BEGIN}, {"OK", 0, MC_OK, SMACK_ANCHOR_BEGIN}, {"BUSY", 0, MC_BUSY, SMACK_ANCHOR_BEGIN}, {"BADCLASS", 0, MC_BADCLASS, SMACK_ANCHOR_BEGIN}, {"NOSPARE", 0, MC_NOSPARE, SMACK_ANCHOR_BEGIN}, {"NOTFULL", 0, MC_NOTFULL, SMACK_ANCHOR_BEGIN}, {"UNSAFE", 0, MC_UNSAFE, SMACK_ANCHOR_BEGIN}, {"SAME", 0, MC_SAME, SMACK_ANCHOR_BEGIN}, {"STAT", 0, MC_STAT, SMACK_ANCHOR_BEGIN}, {"", 0, MC_empty, SMACK_ANCHOR_BEGIN}, {0,0,0,0} }; enum { MS_PID, MS_UPTIME, MS_TIME, MS_VERSION, MS_POINTER_SIZE, MS_RUSAGE_USER, MS_RUSAGE_SYSTEM, MS_CURR_TIMES, MS_TOTAL_ITEMS, MS_BYTES, MS_MAX_CONNECTIONS, MS_CURR_CONNECTIONS, MS_TOTAL_CONNECTIONS, }; static struct Patterns memcached_stats[] = { {"pid", 0, MS_PID, SMACK_ANCHOR_BEGIN}, {"uptime", 0, MS_UPTIME, SMACK_ANCHOR_BEGIN}, {"time", 0, MS_TIME, SMACK_ANCHOR_BEGIN}, {"version", 0, MS_VERSION, SMACK_ANCHOR_BEGIN}, {"pointer_size", 0, MS_POINTER_SIZE, SMACK_ANCHOR_BEGIN}, {"rusage_user", 0, MS_RUSAGE_USER, SMACK_ANCHOR_BEGIN}, {"rusage_system", 0, MS_RUSAGE_SYSTEM, SMACK_ANCHOR_BEGIN}, {"curr_items", 0, MS_CURR_TIMES, SMACK_ANCHOR_BEGIN}, {"total_items", 0, MS_TOTAL_ITEMS, SMACK_ANCHOR_BEGIN}, {"bytes", 0, MS_BYTES, SMACK_ANCHOR_BEGIN}, {"max_connections", 0, MS_MAX_CONNECTIONS, SMACK_ANCHOR_BEGIN}, {"curr_connections", 0, MS_CURR_CONNECTIONS, SMACK_ANCHOR_BEGIN}, {"total_connections", 0, MS_TOTAL_CONNECTIONS, SMACK_ANCHOR_BEGIN}, {0,0,0,0} }; /*************************************************************************** ***************************************************************************/ static void memcached_tcp_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned i; struct MEMCACHEDSTUFF *memcached = &pstate->sub.memcached; size_t id; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); UNUSEDPARM(socket); if (sm_memcached_responses == 0) return; for (i=0; imatch = 0; /* drop through */ case 1: id = smack_search_next( sm_memcached_responses, &memcached->match, px, &i, (unsigned)length); i--; switch (id) { case SMACK_NOT_FOUND: /* continue processing */ break; case MC_STAT: if (px[i] == '\n') state = 2; /* premature end of line */ else state = 100; break; case MC_END: state = 3; break; default: state = 2; } break; /* We've reached the end of input */ case 3: i = (unsigned)length; break; /* Ignore until end of line */ case 2: while (i < length && px[i] != '\n') i++; if (px[i] == '\n') state = 0; break; /* process stat */ case 100: case 200: if (px[i] == '\n') state = 0; else if (isspace(px[i])) continue; /* stay in this space until end of whitespace */ else { state++; memcached->match = 0; i--; } break; case 101: id = smack_search_next( sm_memcached_stats, &memcached->match, px, &i, (unsigned)length); i--; switch (id) { case SMACK_NOT_FOUND: /* continue processing */ break; case MS_UPTIME: case MS_TIME: case MS_VERSION: banout_append(banout, PROTO_MEMCACHED, memcached_stats[id].pattern, AUTO_LEN); if (px[i] == '\n') state = 0; else state = 200; banout_append_char(banout, PROTO_MEMCACHED, '='); break; default: if (px[i] == '\n') state = 0; else state = 2; } break; case 201: if (px[i] == '\r') continue; else if (px[i] == '\n') { banout_append_char(banout, PROTO_MEMCACHED, ' '); state = 0; break; } else banout_append_char(banout, PROTO_MEMCACHED, px[i]); break; } } pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * memcached_init(struct Banner1 *b) { unsigned i; /* * These match response codes */ b->memcached_responses = smack_create("memcached-responses", SMACK_CASE_INSENSITIVE); for (i=0; memcached_responses[i].pattern; i++) { char *tmp; unsigned j; size_t len; len = strlen(memcached_responses[i].pattern); tmp = MALLOC(len + 2); memcpy(tmp, memcached_responses[i].pattern, len); tmp[len+1] = '\0'; /* Add all patterns 4 times, once each for the possible whitespace */ for (j=0; j<4; j++) { tmp[len] = " \t\r\n"[j]; smack_add_pattern( b->memcached_responses, tmp, (unsigned)len+1, memcached_responses[i].id, memcached_responses[i].is_anchored); } free(tmp); } smack_compile(b->memcached_responses); sm_memcached_responses = b->memcached_responses; /* * These match stats we might be interested in */ b->memcached_stats = smack_create("memcached-stats", SMACK_CASE_INSENSITIVE); for (i=0; memcached_stats[i].pattern; i++) { char *tmp; unsigned j; size_t len; len = strlen(memcached_stats[i].pattern); tmp = MALLOC(len + 2); memcpy(tmp, memcached_stats[i].pattern, len); tmp[len+1] = '\0'; /* Add all patterns 4 times, once each for the possible whitespace */ for (j=0; j<4; j++) { tmp[len] = " \t\r\n"[j]; smack_add_pattern( b->memcached_stats, tmp, (unsigned)len+1, memcached_stats[i].id, memcached_stats[i].is_anchored); } free(tmp); } smack_compile(b->memcached_stats); sm_memcached_stats = b->memcached_stats; return b->http_fields; } /*************************************************************************** ***************************************************************************/ unsigned memcached_udp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ) { ipaddress ip_them; ipaddress ip_me; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; unsigned request_id = 0; unsigned sequence_num = 0; unsigned total_dgrams = 0; unsigned reserved = 0; unsigned cookie = 0; struct BannerOutput banout[1]; /* All memcached responses will be at least 8 bytes */ if (length < 8) return 0; /* The frame header is 8 bytes long, as follows (all values are 16-bit integers in network byte order, high byte first): 0-1 Request ID 2-3 Sequence number 4-5 Total number of datagrams in this message 6-7 Reserved for future use; must be 0 */ request_id = px[0]<<8 | px[1]; sequence_num = px[2]<<8 | px[3]; total_dgrams = px[4]<<8 | px[5]; reserved = px[6]<<8 | px[7]; /* Ignore high sequence numbers. This should be zero normally */ if (sequence_num > 100) goto not_memcached; /* Ignore too many dgrams, should be one normally */ if (total_dgrams > 100) goto not_memcached; /* Make sure reserved field is zero */ if (reserved != 0) goto not_memcached; /* Grab IP addresses */ ip_them = parsed->src_ip; ip_me = parsed->dst_ip; /* Validate the "syn-cookie" style information. In the case of SNMP, * this will be held in the "request-id" field. If the cookie isn't * a good one, then we'll ignore the response */ cookie = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy); /*if ((seqno&0xffff) != request_id) return 1;*/ /* Initialize the "banner output" module that we'll use to print * pretty text in place of the raw packet */ banout_init(banout); /* Parse the remainder of the packet as if this were TCP */ { struct StreamState stuff[1]; memset(stuff, 0, sizeof(stuff[0])); memcached_tcp_parse( 0, 0, stuff, px+8, length-8, banout, 0); } if ((cookie&0xffff) != request_id) banout_append(banout, PROTO_MEMCACHED, " IP-MISMATCH", AUTO_LEN); /* Print the banner information, or save to a file, depending */ output_report_banner( out, timestamp, ip_them, 17 /*UDP*/, parsed->port_src, PROTO_MEMCACHED, parsed->ip_ttl, banout_string(banout, PROTO_MEMCACHED), banout_string_length(banout, PROTO_MEMCACHED)); /* Free memory for the banner, if there was any allocated */ banout_release(banout); return 0; not_memcached: return default_udp_parse(out, timestamp, px, length, parsed, entropy); } /**************************************************************************** ****************************************************************************/ unsigned memcached_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) { /* The frame header is 8 bytes long, as follows (all values are 16-bit integers in network byte order, high byte first): 0-1 Request ID 2-3 Sequence number 4-5 Total number of datagrams in this message 6-7 Reserved for future use; must be 0 */ if (length < 2) return 0; px[0] = (unsigned char)(seqno >> 8); px[1] = (unsigned char)(seqno >> 0); return 0; } /*************************************************************************** ***************************************************************************/ static int memcached_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_memcached = { "memcached", 11211, "stats\r\n", 7, 0, memcached_selftest, memcached_init, memcached_tcp_parse, }; ================================================ FILE: src/proto-memcached.h ================================================ #ifndef PROTO_MEMCACHED_H #define PROTO_MEMCACHED_H #include "proto-banner1.h" struct Output; struct PreprocessedInfo; /* * For sending TCP requests and parsing TCP responses. */ extern const struct ProtocolParserStream banner_memcached; /* * For parsing UDP responses */ unsigned memcached_udp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ); /* * For creating UDP request */ unsigned memcached_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno); #endif ================================================ FILE: src/proto-netbios.c ================================================ #include "proto-netbios.h" #include "proto-udp.h" #include "proto-dns-parse.h" #include "proto-preprocess.h" #include "syn-cookie.h" #include "util-logger.h" #include "output.h" #include "masscan-app.h" #include "proto-banner1.h" #include "massip-port.h" #include "masscan.h" #include "unusedparm.h" #include /*************************************************************************** ***************************************************************************/ static void append_char(unsigned char *banner, size_t banner_max, unsigned *banner_length, char c) { if (*banner_length < banner_max) banner[(*banner_length)++] = c; } /*************************************************************************** ***************************************************************************/ static void append_name(unsigned char *banner, size_t banner_max, unsigned *banner_length, const unsigned char *name) { unsigned i; unsigned char c; for (i=0; i<15; i++) { c = name[i]; if (c == 0x20 || c == '\0') append_char(banner, banner_max, banner_length, ' '); else if (isalnum(c) || ispunct(c)) append_char(banner, banner_max, banner_length, c); else { append_char(banner, banner_max, banner_length, '<'); append_char(banner, banner_max, banner_length, "0123456789ABCDEF"[c>>4]); append_char(banner, banner_max, banner_length, "0123456789ABCDEF"[c&0xF]); append_char(banner, banner_max, banner_length, '>'); } } c = name[i]; append_char(banner, banner_max, banner_length, '<'); append_char(banner, banner_max, banner_length, "0123456789ABCDEF"[c>>4]); append_char(banner, banner_max, banner_length, "0123456789ABCDEF"[c&0xF]); append_char(banner, banner_max, banner_length, '>'); append_char(banner, banner_max, banner_length, '\n'); } /***************************************************************************** * Process one of them many "resource-records" within the NBTSTAT response *****************************************************************************/ static unsigned handle_nbtstat_rr(struct Output *out, time_t timestamp, unsigned ttl, const unsigned char *px, unsigned length, ipaddress ip_them, unsigned port_them) { unsigned char banner[65536]; unsigned banner_length = 0; unsigned offset = 0; unsigned name_count; if (offset >= length) return 0; name_count = px[offset++]; /* Report all the names */ while (offset + 18 <= length && name_count) { append_name(banner, sizeof(banner), &banner_length, &px[offset]); offset += 18; name_count--; } /* Report the MAC address at the end */ { unsigned i; for (i=0; i<6; i++) { if (offset + i < length) { unsigned char c = px[offset]; append_char(banner, sizeof(banner), &banner_length, "0123456789ABCDEF"[c>>4]); append_char(banner, sizeof(banner), &banner_length, "0123456789ABCDEF"[c&0xF]); if (i < 5) append_char(banner, sizeof(banner), &banner_length, '-'); } } } output_report_banner( out, timestamp, ip_them, 17, port_them, PROTO_NBTSTAT, ttl, banner, banner_length); return 0; } /*************************************************************************** ***************************************************************************/ unsigned handle_nbtstat(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy) { ipaddress ip_them = parsed->src_ip; ipaddress ip_me = parsed->dst_ip; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; struct DNS_Incoming dns[1]; unsigned offset; uint64_t seqno; seqno = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy); proto_dns_parse(dns, px, parsed->app_offset, parsed->app_offset + parsed->app_length); if ((seqno & 0xFFFF) != dns->id) return 1; if (dns->qr != 1) return 0; if (dns->rcode != 0) return 0; if (dns->qdcount > 1) return 0; if (dns->ancount < 1) return 0; if (dns->rr_count < 1) return 0; offset = dns->rr_offset[dns->qdcount]; offset = dns_name_skip(px, offset, length); if (offset + 10 >= length) return 0; { unsigned type = px[offset+0]<<8 | px[offset+1]; unsigned xclass = px[offset+2]<<8 | px[offset+3]; unsigned rrlen = px[offset+8]<<8 | px[offset+9]; unsigned txtlen = px[offset+10]; if (rrlen == 0 || txtlen > rrlen-1) return 0; if (type != 0x21 || xclass != 1) return 0; offset += 10; return handle_nbtstat_rr(out, timestamp, parsed->ip_ttl, px + offset, length - offset, ip_them, port_them); } } ================================================ FILE: src/proto-netbios.h ================================================ #ifndef PROTO_NETBIOS_H #define PROTO_NETBIOS_H #include #include struct PreprocessedInfo; struct Output; unsigned handle_nbtstat(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); #endif ================================================ FILE: src/proto-ntlmssp.c ================================================ #include "proto-ntlmssp.h" #include "masscan-app.h" #include "proto-banout.h" #include "util-safefunc.h" #include "util-malloc.h" #include #include /* +--------+--------+--------+--------+ | 'N' | 'T' | 'L' | 'M' | +- -+- -+- -+- -+ | 'S' | 'S' | 'P' | '\0' | +--------+--------+--------+--------+ | MessageType | +--------+--------+--------+--------+ | TargetNameLen | TargetNameMaxLen| TagetName fields set to zero if +--------+--------+--------+--------+ NTLMSSP_REQUEST_TARGET flag not set | TargetNameOffset | +--------+--------+--------+--------+ | NegotiateFlags | +--------+--------+--------+--------+ | | +- ServerChallenge -+ | | +--------+--------+--------+--------+ | | +- Reserved -+ | | +--------+--------+--------+--------+ | TargetInfoLen | TargetInfoMaxLen| TagetInfo fields set to zero if +--------+--------+--------+--------+ NTLMSSP_NEGOTIATE_TARGET_INFO flag not set | TargetInfoOffset | +--------+--------+--------+--------+ |MajorVer|MinorVer| ProductBuild | +--------+--------+--------+--------+ | Reserved |NTLMver | +--------+--------+--------+--------+ | | +- -+- -+- -+- -+ . . . . . . . . . . . . . . . . . . . +- -+- -+- -+- -+ | | | | | +--------+--------+--------+--------+ Signature (8 bytes): "An 8-byte character array that MUST contain the ASCII string ('N', 'T', 'L', 'M', 'S', 'S', 'P', '\0')." MessageType (4 bytes): "A 32-bit unsigned integer that indicates the message type. This field MUST be set to 0x00000002." TargetNameLen (2 bytes): "A 16-bit unsigned integer that defines the size, in bytes, of TargetName in Payload." Zero if NTLMSSP_REQUEST_TARGET not set. TargetNameMaxLen (2 bytes): "A 16-bit unsigned integer that SHOULD be set to the value of TargetNameLen and MUST be ignored on receipt." Zero if NTLMSSP_REQUEST_TARGET not set. TargetNameBufferOffset (4 bytes): "A 32-bit unsigned integer that defines the offset, in bytes, from the beginning of the CHALLENGE_MESSAGE to TargetName in Payload. If TargetName is a Unicode string, the values of TargetNameBufferOffset and TargetNameLen MUST be multiples of 2." VERSION FIELDS: These fields are valid only if "NTLMSSP_NEGOTIATE_VERSION" flag is set. MajorVer [ProductMajorVersion] (1 byte): "An 8-bit unsigned integer that SHOULD contain the major version number of the operating system in use." MinorVer [ProductMinorVersion] (1 byte): "An 8-bit unsigned integer that SHOULD<34> contain the minor version number of the operating system in use." ProductBuild (2 bytes): "A 16-bit unsigned integer that contains the build number of the operating system in use. This field SHOULD be set to a 16-bit quantity that identifies the operating system build number." NTLMRevisionCurrent (1 byte): "An 8-bit unsigned integer that contains a value indicating the current revision of the NTLMSSP in use. This field SHOULD contain the following value:" "NTLMSSP_REVISION_W2K3 (0x0F): Version 15 of the NTLMSSP is in use." */ static void append_unicode_string(struct BannerOutput *banout, unsigned proto, const char *name, const unsigned char *value, size_t value_length) { unsigned j; banout_append_char(banout, proto, ' '); banout_append(banout, PROTO_SMB, name, AUTO_LEN); banout_append_char(banout, proto, '='); for (j=0; j x->length - x->offset) length = x->length - x->offset; /* See if we have a fragment, in which case we need to allocate a buffer * to contain it */ if (x->offset == 0 && x->length > length) { x->buf = MALLOC(x->length); memcpy(x->buf, px, length); x->offset = (unsigned)length; return; } else if (x->offset) { memcpy(x->buf + x->offset, px, length); x->offset += (unsigned)length; if (x->offset < x->length) return; /* now reset the input to point to our buffer instead */ px = x->buf; length = x->length; } if (length < 56) goto end; /* Verify the signature. There are other protocols that we could possibly * detect at this point and do something else useful with, but for right now, * we are just doing NTLM */ if (memcmp("NTLMSSP", px, 8) != 0) goto end; /* Verify this is a "challenge" packet, which has all the interesting * fields. */ message_type = px[8] | px[9]<<8 | px[10]<<16 | px[11]<<24; if (message_type != 2) goto end; /* Grab the Domain field. This is a pointer in these 8 bytes here * that points into the payload section of the chunk */ name_length = px[12] | px[13]<<8; name_offset = px[16] | px[17]<<8 | px[18]<<16 | px[19]<<24; if (name_length && name_length + name_offset < length) { append_unicode_string(banout, PROTO_SMB, "domain", px+name_offset, name_length); } /* Grab flags */ //flags = px[20] | px[21]<<8 | px[22]<<16 | px[23]<<24; /* Info field */ info_length = px[40] | px[41]<<8; info_offset = px[44] | px[45]<<8 | px[46]<<16 | px[47]<<24; /* Version field */ { char buf[64]; snprintf(buf, sizeof(buf), " version=%u.%u.%u ntlm-ver=%u", px[48], px[49], px[50] | px[51]<<8, px[55] ); banout_append(banout, PROTO_SMB, buf, AUTO_LEN); } /* Parse all the fields */ for (i=info_offset; i+4 info_offset + info_length - i) len = info_offset + info_length - i; if (len > length - i) len = length - i; switch (type) { case 0x00: /* MsvAvEOL */ i = info_offset + info_length; continue; case 1: /* MsvAvNbComputerName */ append_unicode_string(banout, PROTO_SMB, "name", px+i, len); break; case 2: /* MsvAvNbDomainName */ append_unicode_string(banout, PROTO_SMB, "domain", px+i, len); break; case 3: /* MsvAvDnsComputerName */ append_unicode_string(banout, PROTO_SMB, "name-dns", px+i, len); break; case 4: /* MsvAvDnsDomainName */ append_unicode_string(banout, PROTO_SMB, "domain-dns", px+i, len); break; case 5: /* MsvAvDnsTreeName */ append_unicode_string(banout, PROTO_SMB, "forest", px+i, len); break; case 6: /* MsvAvFlags */ break; case 7: /* MsvAvTimestamp */ break; case 8: /* MsvAvSingleHost */ break; case 9: /* MsvAvTargetName */ append_unicode_string(banout, PROTO_SMB, "target", px+i, len); break; case 10: /* MsvChannelBindings */ break; default: break; } i += (unsigned)len; } /* Grab the other fields. This*/ end: /* * Free the buffer if needed */ if (x->buf) { free(x->buf); x->buf = 0; } } void ntlmssp_cleanup(struct NtlmsspDecode *x) { if (x->buf) { free(x->buf); x->buf = 0; } } void ntlmssp_decode_init(struct NtlmsspDecode *x, size_t length) { memset(x, 0, sizeof(*x)); /* [security] Double-check this input, since it's ultimately driven by user-input. * The code that leads to here should already have double-checked this, but I'm * doing it again just in case. This is larger than any input that should be * seen in the real world that a hacker isn't messing with. */ if (length > 65536) length = 65536; x->length = (unsigned)length; x->offset = 0; x->buf = NULL; } ================================================ FILE: src/proto-ntlmssp.h ================================================ #ifndef PROTO_NTLMSSP_H #define PROTO_NTLMSSP_H #include struct BannerOutput; struct NtlmsspDecode { unsigned length; unsigned offset; unsigned char *buf; }; void ntlmssp_decode_init(struct NtlmsspDecode *x, size_t length); void ntlmssp_cleanup(struct NtlmsspDecode *x); void ntlmssp_decode(struct NtlmsspDecode *x, const unsigned char *px, size_t length, struct BannerOutput *banout); #endif ================================================ FILE: src/proto-ntp.c ================================================ /* NTP protocol handler */ #include "proto-ntp.h" #include #include #include "smack.h" #include "util-safefunc.h" #include "output.h" #include "masscan-app.h" #include "proto-preprocess.h" #include "proto-banner1.h" #include "syn-cookie.h" #include "massip-port.h" #include "unusedparm.h" /**************************************************************************** ****************************************************************************/ unsigned ntp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) { UNUSEDPARM(px); UNUSEDPARM(length); UNUSEDPARM(seqno); return 0; } struct Val2String { unsigned value; const char *string; }; static const struct Val2String request_codes[] = { { 0, "PEER_LIST" }, { 1, "PEER_LIST_SUM" }, { 2, "PEER_INFO" }, { 3, "PEER_STATS" }, { 4, "SYS_INFO" }, { 5, "SYS_STATS" }, { 6, "IO_STATS" }, { 7, "MEM_STATS" }, { 8, "LOOP_INFO" }, { 9, "TIMER_STATS" }, { 10, "CONFIG" }, { 11, "UNCONFIG" }, { 12, "SET_SYS_FLAG" }, { 13, "CLR_SYS_FLAG" }, { 16, "GET_RESTRICT" }, { 17, "RESADDFLAGS" }, { 18, "RESSUBFLAGS" }, { 19, "UNRESTRICT" }, { 20, "MON_GETLIST" }, { 21, "RESET_STATS" }, { 22, "RESET_PEER" }, { 23, "REREAD_KEYS" }, { 26, "TRUSTKEY" }, { 27, "UNTRUSTKEY" }, { 28, "AUTHINFO" }, { 29, "TRAPS" }, { 30, "ADD_TRAP" }, { 31, "CLR_TRAP" }, { 32, "REQUEST_KEY" }, { 33, "CONTROL_KEY" }, { 34, "GET_CTLSTATS" }, { 36, "GET_CLOCKINFO" }, { 37, "SET_CLKFUDGE" }, { 38, "GET_KERNEL" }, { 39, "GET_CLKBUGINFO" }, { 42, "MON_GETLIST_1" }, { 43, "HOSTNAME_ASSOCID" }, { 0, 0} }; struct Val2String error_codes[] = { {0, "No Error"}, {1, "Incompatible Implementation Number"}, {2, "Unimplemented Request Code"}, {3, "Format Error"}, {4, "No Data Available"}, {7, "Authentication Failure"}, {0,0} }; /***************************************************************************** *****************************************************************************/ static const char * val2string_lookup(const struct Val2String *list, unsigned val) { unsigned i; for (i=0; list[i].string; i++) { if (list[i].value == val) return list[i].string; } return 0; } /***************************************************************************** *****************************************************************************/ static void ntp_modlist_parse(const unsigned char *px, unsigned length, struct BannerOutput *banout, unsigned *request_id) { unsigned offset = 4; unsigned errcode; unsigned record_count; unsigned record_size; UNUSEDPARM(request_id); if (offset + 4 >= length) return; errcode = (px[offset]>>4)&0xF; record_count = (px[offset+0]&0xF) << 8 | px[offset+1]; record_size = (px[offset+2]&0xF) << 8 | px[offset+3]; if (errcode) { char foo[12]; const char *errmsg = val2string_lookup(error_codes, errcode); if (errmsg == 0) errmsg = "Bogus Error Code"; snprintf(foo, sizeof(foo), "%u", errcode); banout_append(banout, PROTO_NTP, "Response was NTP Error Code ", AUTO_LEN); banout_append(banout, PROTO_NTP, foo, AUTO_LEN); banout_append(banout, PROTO_NTP, " - \"", AUTO_LEN); banout_append(banout, PROTO_NTP, errmsg, AUTO_LEN); banout_append(banout, PROTO_NTP, "\"", AUTO_LEN); return; } if (4 + record_count * record_size > length) { banout_append(banout, PROTO_NTP, "response-too-big", AUTO_LEN); return; } if (record_count * record_size > 500) { banout_append(banout, PROTO_NTP, "response-too-big", AUTO_LEN); return; } //offset += 4; { char msg[128]; snprintf(msg, sizeof(msg), " response-size=%u-bytes more=%s", record_count * record_size, ((px[0]>>6)&1)?"true":"false"); banout_append(banout, PROTO_NTP, msg, AUTO_LEN); } } /***************************************************************************** *****************************************************************************/ static void ntp_priv(const unsigned char *px, unsigned length, struct BannerOutput *banout, unsigned *request_id) { unsigned implementation = px[2]; unsigned request_code = px[3]; const char *request_string; switch (implementation) { case 0: banout_append(banout, PROTO_NTP, "UNIV", 4); return; case 2: banout_append(banout, PROTO_NTP, "XNTPD-OLD", 9); return; case 3: banout_append(banout, PROTO_NTP, "XNTPD", 5); break; default: return; } request_string = val2string_lookup(request_codes, request_code); if (request_string) { banout_append(banout, PROTO_NTP, " ", 1); banout_append(banout, PROTO_NTP, request_string, strlen(request_string)); } switch (request_code) { case 42: ntp_modlist_parse(px, length, banout, request_id); break; } } /***************************************************************************** *****************************************************************************/ static void ntp_v2_parse(const unsigned char *px, unsigned length, struct BannerOutput *banout, unsigned *request_id) { unsigned mode; if (length < 4) return; /* Validate: response bit is set */ if ((px[0]>>7) != 1) return; /* Validate: this is version 2 */ if (((px[0]>>3)&7) != 2) return; /* Extract: mode */ mode = px[0] & 7; switch (mode) { case 6: /* control */ break; case 7: ntp_priv(px, length, banout, request_id); break; } } /***************************************************************************** * Handles an NTP response. *****************************************************************************/ unsigned ntp_handle_response(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ) { ipaddress ip_them = parsed->src_ip; unsigned request_id = 0; struct BannerOutput banout[1]; unsigned offset = parsed->app_offset; UNUSEDPARM(length); UNUSEDPARM(entropy); if (parsed->app_length < 4) return 0; /* Initialize the "banner output" module that we'll use to print * pretty text in place of the raw packet */ banout_init(banout); /* Parse the packet */ switch ((px[offset]>>3)&7) { case 2: ntp_v2_parse( px + parsed->app_offset, /* incoming response */ parsed->app_length, /* length of response */ banout, /* banner printing */ &request_id); /* syn-cookie info */ break; default: banout_release(banout); return 0; } /* Validate the "syn-cookie" style information. */ //seqno = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me); //if ((seqno&0x7FFFffff) != request_id) // return 1; /* Print the banner information, or save to a file, depending */ output_report_banner( out, timestamp, ip_them, 17, parsed->port_src, PROTO_NTP, parsed->ip_ttl, banout_string(banout, PROTO_NTP), banout_string_length(banout, PROTO_NTP)); /* Free memory for the banner, if there was any allocated */ banout_release(banout); return 0; } /**************************************************************************** ****************************************************************************/ int ntp_selftest(void) { return 0; } ================================================ FILE: src/proto-ntp.h ================================================ #ifndef PROTO_NTP_H #define PROTO_NTP_H #include #include struct Output; struct PreprocessedInfo; /** * Does a regression test. * @return * 0 if success, 1 if failure */ int ntp_selftest(void); /** * Sets a cookie on the packet, if possible. */ unsigned ntp_set_cookie(unsigned char *px, size_t length, uint64_t seqno); /** * Parse NTP responses looking for any "banner" information */ unsigned ntp_handle_response(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); #endif ================================================ FILE: src/proto-oproto.c ================================================ #include "proto-oproto.h" #include "unusedparm.h" void handle_oproto(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy) { UNUSEDPARM(entropy); UNUSEDPARM(parsed); UNUSEDPARM(length); UNUSEDPARM(px); UNUSEDPARM(timestamp); UNUSEDPARM(out); } ================================================ FILE: src/proto-oproto.h ================================================ /* Other IP protocol (not TCP, UDP, TCP, ICMP Specifically for scanning things like GRE. */ #ifndef PROTO_OPROTO_H #define PROTO_OPROTO_H #include #include struct Output; struct PreprocessedInfo; /** * Parse an incoming response. * @param entropy * The random seed, used in calculating syn-cookies. */ void handle_oproto(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); #endif ================================================ FILE: src/proto-pop3.c ================================================ /* POP3 banner checker */ #include "proto-pop3.h" #include "proto-banner1.h" #include "unusedparm.h" #include "masscan-app.h" #include "stack-tcp-api.h" #include "proto-ssl.h" #include #include /*************************************************************************** ***************************************************************************/ static void pop3_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned i; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; iport; memset(pstate, 0, sizeof(*pstate)); pstate->app_proto = PROTO_SSL3; pstate->is_sent_sslhello = 1; pstate->port = (unsigned short)port; state = 0; tcpapi_send(socket, banner_ssl.hello, banner_ssl.hello_length, 0); break; } break; case 100: if (px[i] == '\r') continue; banout_append_char(banout, PROTO_POP3, px[i]); if (px[i] == '\n') { state = 0xffffffff; tcpapi_close(socket); } break; default: i = (unsigned)length; break; } } pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * pop3_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /*************************************************************************** ***************************************************************************/ static int pop3_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_pop3 = { "pop3", 110, 0, 0, 0, pop3_selftest, pop3_init, pop3_parse, }; ================================================ FILE: src/proto-pop3.h ================================================ #ifndef PROTO_POP3_H #define PROTO_POP3_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_pop3; #endif ================================================ FILE: src/proto-preprocess.c ================================================ /* Copyright: (c) 2009-2010 by Robert David Graham */ /**************************************************************************** PREPROCESS PACKETS This function parses the entire TCP/IP stack looking for IP addresses and ports. The intent is that this is the minimal parsing necessary to find address/port information. While it does basic checking (to confirm length information, for example), it does not do more extensive checking (like whether the checksum is correct). ****************************************************************************/ #include "proto-preprocess.h" #include #include #include #define ex32be(px) ( *((unsigned char*)(px)+0)<<24 \ | *((unsigned char*)(px)+1)<<16 \ | *((unsigned char*)(px)+2)<< 8 \ | *((unsigned char*)(px)+3)<< 0 ) #define ex32le(px) ( *((unsigned char*)(px)+0)<< 0 \ | *((unsigned char*)(px)+1)<< 8 \ | *((unsigned char*)(px)+2)<<16 \ | *((unsigned char*)(px)+3)<<24 ) #define ex16be(px) ( *((unsigned char*)(px)+0)<< 8 \ | *((unsigned char*)(px)+1)<< 0 ) #define ex16le(px) ( *((unsigned char*)(px)+0)<< 0 \ | *((unsigned char*)(px)+1)<< 8 ) #define ex24be(px) ( *((unsigned char*)(px)+0)<<16 \ | *((unsigned char*)(px)+1)<< 8 \ | *((unsigned char*)(px)+2)<< 0 ) #define ex24le(px) ( *((unsigned char*)(px)+0)<< 0 \ | *((unsigned char*)(px)+1)<< 8 \ | *((unsigned char*)(px)+2)<<16 ) #define ex64be(px) ( (((uint64_t)ex32be(px))<<32L) + ((uint64_t)ex32be((px)+4)) ) #define ex64le(px) ( ((uint64_t)ex32be(px)) + (((uint64_t)ex32be((px)+4))<<32L) ) /** * Call this frequently while parsing through the headers to make sure that * we don't go past the end of a packet. Remember that 1 byte past the * end can cause a crash. **/ #define VERIFY_REMAINING(n,f) if (offset+(n) > length) return 0; else {info->found_offset=offset; info->found=f;} /**************************************************************************** ****************************************************************************/ unsigned preprocess_frame(const unsigned char *px, unsigned length, unsigned link_type, struct PreprocessedInfo *info) { unsigned offset = 0; unsigned ethertype = 0; info->transport_offset = 0; info->found = FOUND_NOTHING; info->found_offset = 0; /* If not standard Ethernet, go do something else */ if (link_type != 1) goto parse_linktype; parse_ethernet: VERIFY_REMAINING(14, FOUND_ETHERNET); info->mac_dst = px+offset+0; info->mac_src = px+offset+6; ethertype = ex16be(px+offset+12); offset += 14; if (ethertype < 2000) goto parse_llc; if (ethertype != 0x0800) goto parse_ethertype; parse_ipv4: { unsigned header_length; unsigned flags; unsigned fragment_offset; unsigned total_length; info->ip_offset = offset; VERIFY_REMAINING(20, FOUND_IPV4); /* Check version */ if ((px[offset]>>4) != 4) return 0; /* not IPv4 or corrupt */ /* Check header length */ header_length = (px[offset] & 0x0F) * 4; VERIFY_REMAINING(header_length, FOUND_IPV4); /*TODO: verify checksum */ /* Check for fragmentation */ flags = px[offset+6]&0xE0; fragment_offset = (ex16be(px+offset+6) & 0x3FFF) << 3; if (fragment_offset != 0 || (flags & 0x20)) return 0; /* fragmented */ /* Check for total-length */ total_length = ex16be(px+offset+2); VERIFY_REMAINING(total_length, FOUND_IPV4); if (total_length < header_length) return 0; /* weird corruption */ length = offset + total_length; /* reduce the max length */ /* Save off pseudo header for checksum calculation */ info->ip_version = (px[offset]>>4)&0xF; info->_ip_src = px+offset+12; info->_ip_dst = px+offset+16; info->src_ip.ipv4 = px[offset+12] << 24 | px[offset+13] << 16 | px[offset+14] << 8 | px[offset+15] << 0; info->src_ip.version = 4; info->dst_ip.ipv4 = px[offset+16] << 24 | px[offset+17] << 16 | px[offset+18] << 8 | px[offset+19] << 0; info->dst_ip.version = 4; info->ip_ttl = px[offset+8]; info->ip_protocol = px[offset+9]; info->ip_length = total_length; if (info->ip_version != 4) return 0; /* next protocol */ offset += header_length; info->transport_offset = offset; info->transport_length = length - info->transport_offset; switch (info->ip_protocol) { case 1: goto parse_icmp; case 2: goto parse_igmp; case 6: goto parse_tcp; case 17: goto parse_udp; case 132: goto parse_sctp; default: VERIFY_REMAINING(0, FOUND_OPROTO); return 0; /* TODO: should add more protocols, like ICMP */ } } parse_tcp: { unsigned tcp_length; VERIFY_REMAINING(20, FOUND_TCP); tcp_length = px[offset + 12]>>2; VERIFY_REMAINING(tcp_length, FOUND_TCP); info->port_src = ex16be(px+offset+0); info->port_dst = ex16be(px+offset+2); info->app_offset = offset + tcp_length; info->app_length = length - info->app_offset; //assert(info->app_length < 2000); return 1; } parse_udp: { VERIFY_REMAINING(8, FOUND_UDP); info->port_src = ex16be(px+offset+0); info->port_dst = ex16be(px+offset+2); offset += 8; info->app_offset = offset; info->app_length = length - info->app_offset; assert(info->app_length < 2000); if (info->port_dst == 53 || info->port_src == 53) { goto parse_dns; } return 1; } parse_icmp: { VERIFY_REMAINING(4, FOUND_ICMP); info->port_src = px[offset+0]; info->port_dst = px[offset+1]; return 1; } parse_igmp: { VERIFY_REMAINING(4, FOUND_IGMP); info->port_src = 0; info->port_dst = px[offset+0]; return 1; } parse_sctp: { VERIFY_REMAINING(12, FOUND_SCTP); info->port_src = ex16be(px+offset+0); info->port_dst = ex16be(px+offset+2); info->app_offset = offset + 12; info->app_length = length - info->app_offset; assert(info->app_length < 2000); return 1; } parse_dns: { VERIFY_REMAINING(8, FOUND_DNS); return 1; } parse_ipv6: { unsigned payload_length; info->ip_offset = offset; VERIFY_REMAINING(40, FOUND_IPV6); /* Check version */ if ((px[offset]>>4) != 6) return 0; /* not IPv4 or corrupt */ /* Payload length */ payload_length = ex16be(px+offset+4); VERIFY_REMAINING(40+payload_length, FOUND_IPV6); if (length > offset + 40 + payload_length) length = offset + 40 + payload_length; /* Save off pseudo header for checksum calculation */ info->ip_version = (px[offset]>>4)&0xF; info->_ip_src = px+offset+8; info->_ip_dst = px+offset+8+16; info->ip_protocol = px[offset+6]; info->src_ip.version = 6; info->src_ip.ipv6.hi = 0ULL | (uint64_t)px[offset + 8] << 56ULL | (uint64_t)px[offset + 9] << 48ULL | (uint64_t)px[offset + 10] << 40ULL | (uint64_t)px[offset + 11] << 32ULL | (uint64_t)px[offset + 12] << 24ULL | (uint64_t)px[offset + 13] << 16ULL | (uint64_t)px[offset + 14] << 8ULL | (uint64_t)px[offset + 15] << 0ULL; info->src_ip.ipv6.lo = 0ULL | (uint64_t)px[offset + 16] << 56ULL | (uint64_t)px[offset + 17] << 48ULL | (uint64_t)px[offset + 18] << 40ULL | (uint64_t)px[offset + 19] << 32ULL | (uint64_t)px[offset + 20] << 24ULL | (uint64_t)px[offset + 21] << 16ULL | (uint64_t)px[offset + 22] << 8ULL | (uint64_t)px[offset + 23] << 0ULL; info->dst_ip.version = 6; info->dst_ip.ipv6.hi = 0ULL | (uint64_t)px[offset + 24] << 56ULL | (uint64_t)px[offset + 25] << 48ULL | (uint64_t)px[offset + 26] << 40ULL | (uint64_t)px[offset + 27] << 32ULL | (uint64_t)px[offset + 28] << 24ULL | (uint64_t)px[offset + 29] << 16ULL | (uint64_t)px[offset + 30] << 8ULL | (uint64_t)px[offset + 31] << 0ULL; info->dst_ip.ipv6.lo = 0ULL | (uint64_t)px[offset + 32] << 56ULL | (uint64_t)px[offset + 33] << 48ULL | (uint64_t)px[offset + 34] << 40ULL | (uint64_t)px[offset + 35] << 32ULL | (uint64_t)px[offset + 36] << 24ULL | (uint64_t)px[offset + 37] << 16ULL | (uint64_t)px[offset + 38] << 8ULL | (uint64_t)px[offset + 39] << 0ULL; /* next protocol */ offset += 40; info->transport_offset = offset; info->transport_length = length - info->transport_offset; parse_ipv6_next: switch (info->ip_protocol) { case 0: goto parse_ipv6_hop_by_hop; case 6: goto parse_tcp; case 17: goto parse_udp; case 58: goto parse_icmpv6; case 132: goto parse_sctp; case 0x2c: /* IPv6 fragment */ return 0; default: //printf("***** test me ******\n"); return 0; /* TODO: should add more protocols, like ICMP */ } } parse_ipv6_hop_by_hop: { unsigned len; VERIFY_REMAINING(8, FOUND_IPV6_HOP); info->ip_protocol = px[offset]; len = px[offset+1] + 8; VERIFY_REMAINING(len, FOUND_IPV6_HOP); offset += len; info->transport_offset = offset; info->transport_length = length - info->transport_offset; } goto parse_ipv6_next; parse_icmpv6: { unsigned icmp_type; unsigned icmp_code; VERIFY_REMAINING(4, FOUND_ICMP); icmp_type = px[offset+0]; icmp_code = px[offset+1]; info->port_src = icmp_type; info->port_dst = icmp_code; if (133 <= icmp_type && icmp_type <= 136) { info->found = FOUND_NDPv6; } } return 1; parse_vlan8021q: VERIFY_REMAINING(4, FOUND_8021Q); ethertype = ex16be(px+offset+2); offset += 4; goto parse_ethertype; parse_vlanmpls: /* MULTILEVEL: * Regress: wireshark/mpls-twolevel.cap(9) * There can be multiple layers of MPLS tags. This is marked by a * flag in the header whether the current header is the "final" * header in the stack*/ while (offset + 4 < length && !(px[offset+2] & 1)) offset += 4; VERIFY_REMAINING(4, FOUND_MPLS); offset += 4; if (px[offset-4+2]&1) { goto parse_ipv4; } else return 0; wifi_data: { unsigned flag; VERIFY_REMAINING(24, FOUND_WIFI_DATA); flag = px[offset]; switch (px[offset+1]&0x03) { case 0: case 2: info->mac_dst = px+offset+4; info->mac_bss = px+offset+10; info->mac_src = px+offset+16; break; case 1: info->mac_bss = px+offset+4; info->mac_src = px+offset+10; info->mac_dst = px+offset+16; break; case 3: info->mac_bss = (const unsigned char*)"\0\0\0\0\0\0"; info->mac_dst = px+offset+16; info->mac_src = px+offset+24; offset += 6; break; } if ((px[offset+1]&0x04) != 0 || (px[offset+22]&0xF) != 0) return 0; offset += 24; if (flag == 0x88) { offset += 2; } goto parse_llc; } parse_wifi: VERIFY_REMAINING(2, FOUND_WIFI); switch (px[offset]) { case 0x08: case 0x88: /* QoS data */ if (px[1] & 0x40) return 0; goto wifi_data; break; default: return 0; } parse_radiotap_header: /* Radiotap headers for WiFi. http://www.radiotap.org/ * * struct ieee80211_radiotap_header { * u_int8_t it_version; // set to 0 * u_int8_t it_pad; * u_int16_t it_len; // entire length * u_int32_t it_present; // fields present * }; */ { unsigned header_length; unsigned features; VERIFY_REMAINING(8, FOUND_RADIOTAP); if (px[offset] != 0) return 0; header_length = ex16le(px+offset+2); features = ex32le(px+offset+4); VERIFY_REMAINING(header_length, FOUND_RADIOTAP); /* If FCS is present at the end of the packet, then change * the length to remove it */ if (features & 0x4000) { unsigned fcs_header = ex32le(px+offset+header_length-4); unsigned fcs_frame = ex32le(px+length-4); if (fcs_header == fcs_frame) length -= 4; VERIFY_REMAINING(header_length, FOUND_RADIOTAP); } offset += header_length; goto parse_wifi; } parse_prism_header: /* DLT_PRISM_HEADER */ /* This was original created to handle Prism II cards, but now we see this * from other cards as well, such as the 'madwifi' drivers using Atheros * chipsets. * * This starts with a "TLV" format, a 4-byte little-endian tag, followed by * a 4-byte little-endian length. This TLV should contain the entire Prism * header, after which we'll find the real header. Therefore, we should just * be able to parse the 'length', and skip that many bytes. I'm told it's more * complicated than that, but it seems to work right now, so I'm keeping it * this way. */ { unsigned header_length; VERIFY_REMAINING(8, FOUND_PRISM); if (ex32le(px+offset+0) != 0x00000044) return 0; header_length = ex32le(px+offset+4); if (header_length > 0xFFFFF) return 0; VERIFY_REMAINING(header_length, FOUND_PRISM); offset += header_length; goto parse_wifi; } parse_llc: { unsigned oui; VERIFY_REMAINING(3, FOUND_LLC); switch (ex24be(px+offset)) { case 0x0000aa: offset += 2; goto parse_llc; default: return 0; case 0xaaaa03: break; } offset +=3 ; VERIFY_REMAINING(5, FOUND_LLC); oui = ex24be(px+offset); ethertype = ex16be(px+offset+3); offset += 5; switch (oui){ case 0x000000: goto parse_ethertype; default: return 0; } } parse_ethertype: switch (ethertype) { case 0x0800: goto parse_ipv4; case 0x0806: goto parse_arp; case 0x86dd: goto parse_ipv6; case 0x8100: goto parse_vlan8021q; case 0x8847: goto parse_vlanmpls; default: return 0; } parse_linktype: /* * The "link-type" is the same as specified in "libpcap" headers */ switch (link_type) { case 0: offset += 4; switch (ex32be(px)) { case 0x02000000: case 0x00000002: goto parse_ipv4; /* Depending on operating system, these can have different values: 24, 28, or 30 */ case 0x18000000: case 0x00000018: case 0x1c000000: case 0x0000001c: case 0x1e000000: case 0x0000001e: goto parse_ipv6; } return 0; case 1: goto parse_ethernet; case 12: switch (px[offset]>>4) { case 4: goto parse_ipv4; case 6: goto parse_ipv6; } return 0; case 0x69: goto parse_wifi; case 113: goto parse_linux_sll; /* LINKTYPE_LINUX_SLL DLT_LINUX_SLL */ case 119: goto parse_prism_header; case 127: goto parse_radiotap_header; default: return 0; } parse_linux_sll: /* +--------+--------+ | packet type | +--------+--------+ | ARPHRD_ type | +--------+--------+ | addr length | +--------+--------+ | | + first 8 bytes + | of the | + hardware/MAC + | address | + + | | +--------+--------+ | ethertype | +--------+--------+ */ { struct { unsigned packet_type; unsigned arp_type; unsigned addr_length; unsigned char mac_address[8]; unsigned ethertype; } sll; VERIFY_REMAINING(16, FOUND_SLL); sll.packet_type = ex16be(px+offset+0); sll.arp_type = ex16be(px+offset+2); sll.addr_length = ex16be(px+offset+4); memcpy(sll.mac_address, px+offset+6, 8); sll.ethertype = ex16be(px+offset+14); offset += 16; goto parse_ethertype; } parse_arp: info->ip_version = 256; info->ip_offset = offset; { //unsigned hardware_type; //unsigned protocol_type; unsigned hardware_length; unsigned protocol_length; unsigned opcode; VERIFY_REMAINING(8, FOUND_ARP); //hardware_type = px[offset]<<8 | px[offset+1]; //protocol_type = px[offset+2]<<8 | px[offset+3]; hardware_length = px[offset+4]; protocol_length = px[offset+5]; opcode = px[offset+6]<<8 | px[offset+7]; info->opcode = opcode; info->ip_protocol = opcode; offset += 8; VERIFY_REMAINING(2*hardware_length + 2*protocol_length, FOUND_ARP); info->_ip_src = px + offset + hardware_length; info->_ip_dst = px + offset + 2*hardware_length + protocol_length; info->src_ip.version = 4; info->src_ip.ipv4 = px[offset + hardware_length + 0] << 24 | px[offset + hardware_length + 1] << 16 | px[offset + hardware_length + 2] << 8 | px[offset + hardware_length + 3] << 0; info->dst_ip.version = 4; info->dst_ip.ipv4 = px[offset + 2*hardware_length + protocol_length + 0] << 24 | px[offset + 2*hardware_length + protocol_length + 1] << 16 | px[offset + 2*hardware_length + protocol_length + 2] << 8 | px[offset + 2*hardware_length + protocol_length + 3] << 0; info->found_offset = info->ip_offset; return 1; } } ================================================ FILE: src/proto-preprocess.h ================================================ /* Copyright: (c) 2009-2010 by Robert David Graham */ #ifndef PREPROCESS_H #define PREPROCESS_H #include "massip-addr.h" enum { FOUND_NOTHING=0, FOUND_ETHERNET, FOUND_IPV4, FOUND_IPV6, FOUND_ICMP, FOUND_TCP, FOUND_UDP, FOUND_SCTP, FOUND_DNS, FOUND_IPV6_HOP, FOUND_8021Q, FOUND_MPLS, FOUND_WIFI_DATA, FOUND_WIFI, FOUND_RADIOTAP, FOUND_PRISM, FOUND_LLC, FOUND_ARP, FOUND_SLL, /* Linux SLL */ FOUND_OPROTO, /* some other IP protocol */ FOUND_IGMP, FOUND_NDPv6, }; struct PreprocessedInfo { const unsigned char *mac_src; const unsigned char *mac_dst; const unsigned char *mac_bss; unsigned ip_offset; /* 14 for normal Ethernet */ unsigned ip_version; /* 4 or 6 */ unsigned ip_protocol; /* 6 for TCP, 11 for UDP */ unsigned ip_length; /* length of total packet */ unsigned ip_ttl; const unsigned char *_ip_src; const unsigned char *_ip_dst; ipaddress src_ip; ipaddress dst_ip; unsigned transport_offset; /* 34 for normal Ethernet */ unsigned transport_length; union { unsigned port_src; unsigned opcode; }; unsigned port_dst; unsigned app_offset; /* start of TCP payload */ unsigned app_length; /* length of TCP payload */ int found; int found_offset; }; /** * @return 1 if useful stuff found, 0 otherwise */ unsigned preprocess_frame(const unsigned char *px, unsigned length, unsigned link_type, struct PreprocessedInfo *info); #endif ================================================ FILE: src/proto-sctp.c ================================================ #include "proto-sctp.h" #include "proto-preprocess.h" #include "masscan-status.h" #include "output.h" #include #include #define CRC32C_POLY 0x1EDC6F41 #define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) static unsigned crc_c[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, }; unsigned sctp_checksum(const void *vbuffer, size_t length) { const unsigned char *buffer = (const unsigned char *)vbuffer; unsigned i; unsigned crc32 = (unsigned)~0; unsigned result; unsigned char byte0,byte1,byte2,byte3; for (i = 0; i < 8; i++) { CRC32C(crc32, buffer[i]); } CRC32C(crc32, 0); CRC32C(crc32, 0); CRC32C(crc32, 0); CRC32C(crc32, 0); for (i = 12; i < length; i++) { CRC32C(crc32, buffer[i]); } result = ~crc32; /* result now holds the negated polynomial remainder; * since the table and algorithm is "reflected" [williams95]. * That is, result has the same value as if we mapped the message * to a polynomial, computed the host-bit-order polynomial * remainder, performed final negation, then did an end-for-end * bit-reversal. * Note that a 32-bit bit-reversal is identical to four in-place * 8-bit reversals followed by an end-for-end byte swap. * In other words, the bytes of each bit are in the right order, * but the bytes have been byte swapped. So we now do an explicit * byte swap. On a little-endian machine, this byte swap and * the final ntohl cancel out and could be elided. */ byte0 = result & 0xff; byte1 = (result>>8) & 0xff; byte2 = (result>>16) & 0xff; byte3 = (result>>24) & 0xff; crc32 = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); return ( crc32 ); } /***************************************************************************** *****************************************************************************/ void handle_sctp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, unsigned cookie, struct PreprocessedInfo *parsed, uint64_t entropy) { ipaddress ip_them = parsed->src_ip; unsigned port_them = parsed->port_src; unsigned verification_tag; unsigned offset = parsed->transport_offset; UNUSEDPARM(entropy); verification_tag = px[offset + 4] << 24 | px[offset + 5] << 16 | px[offset + 6] << 8 | px[offset + 7]; if (cookie != verification_tag) return; if (offset + 16 > length) return; switch (px[offset + 12]) { case 2: /* init ACK */ output_report_status( out, timestamp, PortStatus_Open, ip_them, 132, /* ip proto = sctp */ port_them, 0, parsed->ip_ttl, parsed->mac_src); break; case 6: /* abort */ output_report_status( out, timestamp, PortStatus_Closed, ip_them, 132, /* ip proto = sctp */ port_them, 0, parsed->ip_ttl, parsed->mac_src); break; default: ; } } /***************************************************************************** *****************************************************************************/ int sctp_selftest(void) { const char testcase[] = "\xd1\x60\x00\x50\x00\x00\x00\x00\x58\xe4\x5d\x36\x01\x00\x00\x14" "\x9e\x8d\x52\x25\x00\x00\x80\x00\x00\x0a\x08\x00\x46\x1a\xdf\x3d"; unsigned xsum; xsum = sctp_checksum(testcase, 32); if (xsum != 0x58e45d36) return 1; return 0; } ================================================ FILE: src/proto-sctp.h ================================================ #ifndef PROTO_SCTP_H #define PROTO_SCTP_H #include #include struct PreprocessedInfo; struct Output; /** * Calculate the "CRC32c" checksum used in SCTP. This is a non-destructive * checksum that skips the checksum field itself. */ unsigned sctp_checksum(const void *vbuffer, size_t length); /** * Handle incoming SCTP response */ void handle_sctp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, unsigned cookie, struct PreprocessedInfo *parsed, uint64_t entropy); int sctp_selftest(void); #endif ================================================ FILE: src/proto-smb.c ================================================ /* SMB parser */ #include "proto-smb.h" #include "stack-tcp-api.h" #include "unusedparm.h" #include "masscan-app.h" #include "crypto-siphash24.h" #include "util-safefunc.h" #include "unusedparm.h" #include #include #include #include /* "NT LM 0.12" - Win2k "SMB 2.002" 0x0202 Vista "SMB 2.???" 0x02FF Win7, Windows 2008 "PC NETWORK PROGRAM 1.0" MS-DOS "MICROSOFT NETWORKS 1.03" MS-DOS "MICROSOFT NETWORKS 3.0" MS-DOS "LANMAN 1.0" OS/2 "LM1.2X002" OS/2 */ /* PC NETWORK PROGRAM 1.0 LANMAN1.0 Windows for Workgroups 3.1a LM1.2X002 LANMAN2.1 NT LM 0.12 SMB 2.002 SMB 2.??? Samba XENIX CORE */ /* References: http://pubs.opengroup.org/onlinepubs/9697999099/toc.pdf */ struct SmbParams { unsigned short command; unsigned short external_offset; unsigned char external_length; unsigned char internal_type; unsigned short internal_offset; }; enum InternalType { IT_uint0, IT_uint8, IT_uint16, IT_uint32, IT_uint64, }; struct SmbParams params[] = { /* USHORT DialectIndex; UCHAR SecurityMode; USHORT MaxMpxCount; USHORT MaxNumberVcs; ULONG MaxBufferSize; ULONG MaxRawSize; ULONG SessionKey; ULONG Capabilities; FILETIME SystemTime; SHORT ServerTimeZone; UCHAR ChallengeLength; */ {0x72, 0, 2, IT_uint16, offsetof(struct Smb72_Negotiate, DialectIndex)}, {0x72, 2, 1, IT_uint8, offsetof(struct Smb72_Negotiate, SecurityMode)}, //{0x72, 3, 2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxMpxCount)}, //{0x72, 5, 2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxNumberVcs)}, //{0x72, 7, 4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxBufferSize)}, //{0x72, 11, 4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxRawSize)}, {0x72, 15, 4, IT_uint32, offsetof(struct Smb72_Negotiate, SessionKey)}, {0x72, 19, 4, IT_uint32, offsetof(struct Smb72_Negotiate, Capabilities)}, {0x72, 23, 8, IT_uint64, offsetof(struct Smb72_Negotiate, SystemTime)}, {0x72, 31, 2, IT_uint16, offsetof(struct Smb72_Negotiate, ServerTimeZone)}, {0x72, 33, 1, IT_uint8, offsetof(struct Smb72_Negotiate, ChallengeLength)}, {0x73, 6, 2, IT_uint16, offsetof(struct Smb73_Setup, BlobLength)}, {0xFF, 0, 0xFF, IT_uint0, 0}, }; #define memberat(t, s, offset) (t*)((char*)(s)+(offset)) static const char smb1_hello_template[] = { 0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, 0x00 }; static const char smb1_hello_template_v1[] = { 0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, 0x00 }; void smb_set_hello_v1(struct ProtocolParserStream *smb) { smb->hello = smb1_hello_template_v1; smb->hello_length = sizeof(smb1_hello_template_v1); } static unsigned char smb1_null_session_setup[] = { 0x00, 0x00, 0x00, 0x7e, 0xff, 0x53, 0x4d, 0x42, 0x73, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0x0d, 0xff, 0x00, 0x00, 0x00, 0x04, 0x41, 0x32, 0x00, 0xef, 0x00, 0x53, 0x45, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xc0, 0x80, 0x00, 0x41, 0x00, 0x00, 0x47, 0x00, 0x55, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x61, 0x00, 0x63, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x20, 0x00, 0x58, 0x00, 0x20, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x00, 0x00, 0x53, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x46, 0x00, 0x53, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x00, 0x00 }; static char smb1_null_session_setup_ex[] = { 0x00, 0x00, 0x00, 0xb8, 0xff, 0x53, 0x4d, 0x42, 0x73, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x04, 0x41, 0x32, 0x00, 0xf1, 0x00, 0xa5, 0x12, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xc0, 0x80, 0x80, 0x7d, 0x00, 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x2a, 0x04, 0x28, 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x02, 0x88, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb0, 0x1d, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x61, 0x00, 0x63, 0x00, 0x20, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x20, 0x00, 0x58, 0x00, 0x20, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x00, 0x00, 0x53, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x46, 0x00, 0x53, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x00, 0x00 }; char smb2_negotiate_request[] = { 0x00, 0x00, 0x00, 0x6c, 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x17, 0x97, 0x90, 0x40, 0xcd, 0xf0, 0x5e, 0x31, 0x8d, 0xea, 0xef, 0x98, 0xcd, 0xa5, 0x08, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x02, 0x00, 0x03, 0x02, 0x03 }; char smb2_null_session_setup[] = { 0x00, 0x00, 0x00, 0xa2, 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x2a, 0x04, 0x28, 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x82, 0x88, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb0, 0x1d, 0x0f, 0x00, 0x00, 0x00 }; /***************************************************************************** * * ****** WARNING: UGLY HACK !!!! ****** * * This code is an ugly hack so I can express SMB parameters (word_count) * headers as a structure instead of writing individual parsers for them. * This code makes no sense. If you find a bug in it, it's probably worth * rewriting rather than figure out its convoluted logic. No really, I mean * this. * *****************************************************************************/ static size_t smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max) { size_t original_offset = offset; size_t c; if (max > offset + (smb->hdr.smb1.param_length - smb->hdr.smb1.param_offset)) max = offset + (smb->hdr.smb1.param_length - smb->hdr.smb1.param_offset); /* Find the correct header */ for (c=0; params[c].command != smb->hdr.smb1.command && params[c].command != 0xFF; c++) ; for (; offset < max; offset++, smb->hdr.smb1.param_offset++) { again: //printf("\n%u/%u %u\n", (unsigned)smb->hdr.smb1.param_offset, (unsigned)smb->hdr.smb1.param_length, (unsigned)c); /* If we've gone past our header, just continue consuming bytes */ if (params[c].command != smb->hdr.smb1.command) continue; /* If we've gone past the end of this field, goto next field */ if (params[c].external_offset + params[c].external_length <= smb->hdr.smb1.param_offset) { c++; goto again; } /* Haven't reached the next field yet */ if (params[c].external_offset > smb->hdr.smb1.param_offset) continue; //printf("\n%u/%u %u [%02x]\n", (unsigned)smb->hdr.smb1.param_offset, (unsigned)smb->hdr.smb1.param_length, (unsigned)c, px[offset]); /* Shift the type, because all fields little-endian */ switch (params[c].internal_type) { case IT_uint0: default: break; case IT_uint8: { uint8_t *x = memberat(uint8_t, &smb->parms, params[c].internal_offset); *x = px[offset]; } break; case IT_uint16: { uint16_t *x = memberat(uint16_t, &smb->parms, params[c].internal_offset); //*x <<= 8; *x |= px[offset] << ((smb->hdr.smb1.param_offset - params[c].external_offset)*8); } break; case IT_uint32: { uint32_t *x = memberat(uint32_t, &smb->parms, params[c].internal_offset); //*x <<= 8; *x |= px[offset] << ((smb->hdr.smb1.param_offset - params[c].external_offset)*8); } break; case IT_uint64: { uint64_t *x = memberat(uint64_t, &smb->parms, params[c].internal_offset); //*x <<= 8; *x |= (uint64_t)px[offset] << (uint64_t)((smb->hdr.smb1.param_offset - params[c].external_offset)*8); } break; } } /* Return the number of bytes processed */ return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static const unsigned long long TICKS_PER_SECOND = 10000000LL; static const unsigned long long EPOCH_DIFFERENCE = 11644473600LL; static time_t convert_windows_time(long long int filetime) { unsigned long long seconds = filetime / TICKS_PER_SECOND; seconds -= EPOCH_DIFFERENCE; return (time_t)seconds; } /***************************************************************************** *****************************************************************************/ /***************************************************************************** *****************************************************************************/ static size_t smb1_parse_negotiate1(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb1.byte_state; enum { D_NEGOT_CHALLENGE, D_NEGOT_DOMAINA_PRE, D_NEGOT_NAMEA_PRE, D_NEGOT_DOMAINA, D_NEGOT_NAMEA, D_NEGOT_ENDA, D_NEGOT_DOMAINU1, D_NEGOT_DOMAINU2, D_NEGOT_DOMAIN1, D_NEGOT_DOMAIN2, D_NEGOT_NAMEU1, D_NEGOT_NAMEU2, D_NEGOT_NAME1, D_NEGOT_NAME2, D_NEGOT_END, D_UNKNOWN, }; if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); for (;offsetparms.negotiate.ChallengeLength == 0) { if (smb->hdr.smb1.flags2 & 0x8000) { state = D_NEGOT_DOMAINU1; } else { state = D_NEGOT_DOMAINA_PRE; } offset--; } else smb->parms.negotiate.ChallengeLength--; break; case D_NEGOT_DOMAINU1: case D_NEGOT_NAMEU1: smb->hdr.smb1.unicode_char = px[offset]; state++; break; case D_NEGOT_DOMAINU2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) { state = D_NEGOT_NAMEU1; } else { banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state++; } break; case D_NEGOT_NAMEU2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) { state = D_NEGOT_END; } else { banout_append(banout, PROTO_SMB, " name=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state++; } break; case D_NEGOT_DOMAIN1: case D_NEGOT_NAME1: smb->hdr.smb1.unicode_char = px[offset]; state++; break; case D_NEGOT_DOMAIN2: case D_NEGOT_NAME2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) { state++; } else { banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; case D_NEGOT_DOMAINA_PRE: if (px[offset] == 0) { state = D_NEGOT_NAMEA_PRE; } else { banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_NEGOT_DOMAINA; } break; case D_NEGOT_NAMEA_PRE: if (px[offset] == 0) { state = D_NEGOT_END; } else { banout_append(banout, PROTO_SMB, " name=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_NEGOT_NAMEA; } break; case D_NEGOT_DOMAINA: case D_NEGOT_NAMEA: if (px[offset] == 0) { state++; } else { banout_append_char(banout, PROTO_SMB, px[offset]); } break; default: break; } smb->hdr.smb1.byte_state = (unsigned short)state; smb->hdr.smb1.byte_offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static size_t smb1_parse_setup1(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb1.byte_state; enum { D_PADDING, D_OSA1, D_OSA2, D_VERSIONA1, D_VERSIONA2, D_DOMAINA1, D_DOMAINA2, D_ENDA, D_OSU1, D_OSU2, D_OSU3, D_OSU4, D_VERSION1, D_VERSION2, D_VERSION3, D_VERSION4, D_DOMAIN1, D_DOMAIN2, D_DOMAIN3, D_DOMAIN4, D_UNKNOWN, }; if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); for (;offsethdr.smb1.flags2 & 0x8000) { state = D_OSU1; } else { state = D_OSA1; } break; case D_OSA1: if (px[offset] == 0) state = D_VERSIONA1; else { banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_OSA2; } break; case D_OSA2: if (px[offset] == 0) state = D_VERSIONA1; else banout_append_char(banout, PROTO_SMB, px[offset]); break; case D_VERSIONA1: if (px[offset] == 0) state = D_DOMAINA1; else { banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_VERSIONA2; } break; case D_VERSIONA2: if (px[offset] == 0) state = D_DOMAINA1; else banout_append_char(banout, PROTO_SMB, px[offset]); break; case D_DOMAINA1: if (px[offset] == 0) state = D_UNKNOWN; else { banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_DOMAINA2; } break; case D_DOMAINA2: if (px[offset] == 0) state = D_UNKNOWN; else banout_append_char(banout, PROTO_SMB, px[offset]); break; case D_OSU1: case D_OSU3: case D_VERSION1: case D_VERSION3: case D_DOMAIN1: case D_DOMAIN3: smb->hdr.smb1.unicode_char = px[offset]; state++; break; case D_OSU2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_VERSION1; else { banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state = D_OSU3; } break; case D_OSU4: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_VERSION1; else { banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; case D_VERSION2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_DOMAIN1; else { banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state = D_VERSION3; } break; case D_VERSION4: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_DOMAIN1; else { banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; case D_DOMAIN2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_UNKNOWN; else { banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state = D_DOMAIN3; } break; case D_DOMAIN4: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_UNKNOWN; else { banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; default: break; } } smb->hdr.smb1.byte_state = (unsigned short)state; smb->hdr.smb1.byte_offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static size_t smb1_parse_setup2(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb1.byte_state; enum { D_BLOB, D_PADDING, D_PADDING2, D_OSA1, D_OSA2, D_VERSIONA1, D_VERSIONA2, D_DOMAINA1, D_DOMAINA2, D_ENDA, D_OSU1, D_OSU2, D_OSU3, D_OSU4, D_VERSION1, D_VERSION2, D_VERSION3, D_VERSION4, D_DOMAIN1, D_DOMAIN2, D_DOMAIN3, D_DOMAIN4, D_UNKNOWN, }; if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); for (;offsetparms.setup.BlobOffset == 0) { spnego_decode_init(&smb->spnego, smb->parms.setup.BlobLength); } { size_t new_max = max; if (new_max > offset + smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset) new_max = offset + smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset; spnego_decode(&smb->spnego, px+offset, new_max-offset, banout); smb->parms.setup.BlobOffset += (uint16_t)(new_max-offset); offset = new_max; if (smb->parms.setup.BlobLength - smb->parms.setup.BlobOffset == 0) { offset--; state = D_PADDING; } } break; case D_PADDING: /* If the blog length is odd, then there is no padding. Otherwise, * there is one byte of padding */ //if (smb->parms.setup.BlobLength & 1) offset--; state = D_PADDING2; break; case D_PADDING2: if (smb->hdr.smb1.flags2 & 0x8000) { state = D_OSU1; } else { state = D_OSA1; } offset--; break; case D_OSA1: if (px[offset] == 0) state = D_VERSIONA1; else { banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_OSA2; } break; case D_OSA2: if (px[offset] == 0) state = D_VERSIONA1; else banout_append_char(banout, PROTO_SMB, px[offset]); break; case D_VERSIONA1: if (px[offset] == 0) state = D_DOMAINA1; else { banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_VERSIONA2; } break; case D_VERSIONA2: if (px[offset] == 0) state = D_DOMAINA1; else banout_append_char(banout, PROTO_SMB, px[offset]); break; case D_DOMAINA1: if (px[offset] == 0) state = D_UNKNOWN; else { banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); banout_append_char(banout, PROTO_SMB, px[offset]); state = D_DOMAINA2; } break; case D_DOMAINA2: if (px[offset] == 0) state = D_UNKNOWN; else banout_append_char(banout, PROTO_SMB, px[offset]); break; case D_OSU1: case D_OSU3: case D_VERSION1: case D_VERSION3: case D_DOMAIN1: case D_DOMAIN3: smb->hdr.smb1.unicode_char = px[offset]; state++; break; case D_OSU2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_VERSION1; else { banout_append(banout, PROTO_SMB, " os=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state = D_OSU3; } break; case D_OSU4: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_VERSION1; else { banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; case D_VERSION2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_DOMAIN1; else { banout_append(banout, PROTO_SMB, " ver=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state = D_VERSION3; } break; case D_VERSION4: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_DOMAIN1; else { banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; case D_DOMAIN2: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_UNKNOWN; else { banout_append(banout, PROTO_SMB, " domain=", AUTO_LEN); banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state = D_DOMAIN3; } break; case D_DOMAIN4: smb->hdr.smb1.unicode_char |= px[offset]<<8; if (smb->hdr.smb1.unicode_char == 0) state = D_UNKNOWN; else { banout_append_unicode(banout, PROTO_SMB, smb->hdr.smb1.unicode_char); state--; } break; default: break; } } smb->hdr.smb1.byte_state = (unsigned short)state; smb->hdr.smb1.byte_offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static size_t smb1_parse_negotiate2(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb1.byte_state; UNUSEDPARM(banout); UNUSEDPARM(px); if (max > offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset)) max = offset + (smb->hdr.smb1.byte_count - smb->hdr.smb1.byte_offset); for (;offsethdr.smb1.byte_state = (unsigned short)state; smb->hdr.smb1.byte_offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** * A default parser for SMBv2 structs. The simplest implementation would be * to simply skip the "struct_length" bytes. However, we have all this * extra code to serve as a template for creating additional functions. *****************************************************************************/ static size_t smb2_parse_response(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb2.state; UNUSEDPARM(banout); UNUSEDPARM(px); if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset)) max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset); for (;offsethdr.smb2.state = (unsigned short)state; smb->hdr.smb2.offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static size_t smb2_parse_negotiate(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb2.state; enum { N_SECMOD1, N_SECMOD2, N_DIALECT1, N_DIALECT2, N_CONTEXTS1, N_CONTEXTS2, N_GUID01, N_GUID02, N_GUID03, N_GUID04, N_GUID05, N_GUID06, N_GUID07, N_GUID08, N_GUID09, N_GUID10, N_GUID11, N_GUID12, N_GUID13, N_GUID14, N_GUID15, N_GUID16, N_CAP1, N_CAP2, N_CAP3, N_CAP4, N_TRANSACTSIZE1, N_TRANSACTSIZE2, N_TRANSACTSIZE3, N_TRANSACTSIZE4, N_READSIZE1, N_READSIZE2, N_READSIZE3, N_READSIZE4, N_WRITESIZE1, N_WRITESIZE2, N_WRITESIZE3, N_WRITESIZE4, N_TIME1, N_TIME2, N_TIME3, N_TIME4, N_TIME5, N_TIME6, N_TIME7, N_TIME8, N_BOOT1, N_BOOT2, N_BOOT3, N_BOOT4, N_BOOT5, N_BOOT6, N_BOOT7, N_BOOT8, N_BLOB_OFFSET1, N_BLOB_OFFSET2, N_BLOB_LENGTH1, N_BLOB_LENGTH2, }; /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Buffer Code | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+ +-+-+-+-+ | Server | +-+-+-+-+ GUID +-+-+-+-+ | | +-+-+-+-+ +-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+ Current Time +-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+ Boot Time +-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sec Blob Offset | Sec Blob Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sec Blob ANS.1/DER encoded blob containing supported authentication mechanisms +-+-+-+-+... */ if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset)) max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset); for (;offsetis_printed_guid) banout_append(banout, PROTO_SMB, " guid=", AUTO_LEN); state++; break; case N_GUID01: case N_GUID05: case N_GUID07: smb->hdr.smb2.number = px[offset]; state++; break; case N_GUID02: case N_GUID03: case N_GUID04: smb->hdr.smb2.number |= px[offset] << (8*(state-N_GUID01)); if (state == N_GUID04 && !smb->is_printed_guid) { banout_append_hexint(banout, PROTO_SMB, smb->hdr.smb2.number, 8); banout_append_char(banout, PROTO_SMB, '-'); } state++; break; case N_GUID06: case N_GUID08: smb->hdr.smb2.number |= px[offset] << 8; if (!smb->is_printed_guid) { banout_append_hexint(banout, PROTO_SMB, smb->hdr.smb2.number, 4); banout_append_char(banout, PROTO_SMB, '-'); } state++; break; case N_GUID10: if (!smb->is_printed_guid) { banout_append_hexint(banout, PROTO_SMB, px[offset], 2); banout_append_char(banout, PROTO_SMB, '-'); } state++; break; case N_GUID09: case N_GUID11: case N_GUID12: case N_GUID13: case N_GUID14: case N_GUID15: case N_GUID16: if (!smb->is_printed_guid) banout_append_hexint(banout, PROTO_SMB, px[offset], 2); if (state == N_GUID16) smb->is_printed_guid = 1; state++; break; case N_CAP1: case N_CAP2: case N_CAP3: case N_CAP4: case N_TRANSACTSIZE1: case N_TRANSACTSIZE2: case N_TRANSACTSIZE3: case N_TRANSACTSIZE4: case N_READSIZE1: case N_READSIZE2: case N_READSIZE3: case N_READSIZE4: case N_WRITESIZE1: case N_WRITESIZE2: case N_WRITESIZE3: case N_WRITESIZE4: state++; break; case N_TIME1: case N_TIME2: case N_TIME3: case N_TIME4: case N_TIME5: case N_TIME6: case N_TIME7: case N_TIME8: smb->parms.negotiate2.current_time |= ((uint64_t)px[offset]<<(uint64_t)((state-N_TIME1)*8)); if (state == N_TIME8 && !smb->is_printed_time) { char str[64] = "(err)"; time_t timestamp = convert_windows_time(smb->parms.negotiate2.current_time); struct tm tm = {0}; size_t len; safe_gmtime(&tm, ×tamp); len = strftime(str, sizeof(str), " time=%Y-%m-%d %H:%M:%S ", &tm); banout_append(banout, PROTO_SMB, str, len); smb->is_printed_time = 1; } state++; break; case N_BOOT1: case N_BOOT2: case N_BOOT3: case N_BOOT4: case N_BOOT5: case N_BOOT6: case N_BOOT7: case N_BOOT8: smb->parms.negotiate2.boot_time |= ((uint64_t)px[offset]<<(uint64_t)((state-N_BOOT1)*8)); if (state == N_BOOT8 && !smb->is_printed_boottime) { char str[64] = "(err)"; time_t timestamp = convert_windows_time(smb->parms.negotiate2.boot_time); struct tm tm = {0}; size_t len; safe_gmtime(&tm, ×tamp); len = strftime(str, sizeof(str), " boottime=%Y-%m-%d %H:%M:%S ", &tm); banout_append(banout, PROTO_SMB, str, len); smb->is_printed_boottime = 1; } state++; break; case N_BLOB_OFFSET1: smb->hdr.smb2.blob_offset = px[offset]; state++; break; case N_BLOB_OFFSET2: smb->hdr.smb2.blob_offset |= (px[offset]<<8); state++; break; case N_BLOB_LENGTH1: smb->hdr.smb2.blob_length = px[offset]; state++; break; case N_BLOB_LENGTH2: smb->hdr.smb2.blob_length |= (px[offset]<<8); state++; break; default: break; } smb->hdr.smb2.state = (unsigned short)state; smb->hdr.smb2.offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static size_t smb2_parse_setup(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb2.state; /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Buffer Code | Flags | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sec Blob Offset | Sec Blob Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sec Blob +-+-+-+-+... */ enum { N_FLAGS1, N_FLAGS2, N_BLOB_OFFSET1, N_BLOB_OFFSET2, N_BLOB_LENGTH1, N_BLOB_LENGTH2, }; UNUSEDPARM(banout); if (max > offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset)) max = offset + (smb->hdr.smb2.struct_length - smb->hdr.smb2.offset); for (;offsethdr.smb2.blob_offset = px[offset]; state++; break; case N_BLOB_OFFSET2: smb->hdr.smb2.blob_offset |= (px[offset]<<8); state++; break; case N_BLOB_LENGTH1: smb->hdr.smb2.blob_length = px[offset]; state++; break; case N_BLOB_LENGTH2: smb->hdr.smb2.blob_length |= (px[offset]<<8); state++; break; default: break; } smb->hdr.smb2.state = (unsigned short)state; smb->hdr.smb2.offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static size_t smb2_parse_header(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) { size_t original_offset = offset; unsigned state = smb->hdr.smb2.state; enum { SMB2_CRED_CHARGE1, SMB2_CRED_CHARG2, SMB2_STATUS1, SMB2_STATUS2, SMB2_STATUS3, SMB2_STATUS4, SMB2_OPCODE1, SMB2_OPCODE2, SMB2_CRED_GRANT1, SMB2_CRED_GRANT2, SMB2_FLAGS1, SMB2_FLAGS2, SMB2_FLAGS3, SMB2_FLAGS4, SMB2_CHAIN_OFFSET1, SMB2_CHAIN_OFFSET2, SMB2_CHAIN_OFFSET3, SMB2_CHAIN_OFFSET4, SMB2_MSGID1, SMB2_MSGID2, SMB2_MSGID3, SMB2_MSGID4, SMB2_MSGID5, SMB2_MSGID6, SMB2_MSGID7, SMB2_MSGID8, SMB2_PID1, SMB2_PID2, SMB2_PID3, SMB2_PID4, SMB2_TID1, SMB2_TID2, SMB2_TID3, SMB2_TID4, SMB2_SESSID1, SMB2_SESSID2, SMB2_SESSID3, SMB2_SESSID4, SMB2_SIG01, SMB2_SIG02, SMB2_SIG03, SMB2_SIG04, SMB2_SIG05, SMB2_SIG06, SMB2_SIG07, SMB2_SIG08, SMB2_SIG09, SMB2_SIG10, SMB2_SIG11, SMB2_SIG12, SMB2_SIG13, SMB2_SIG14, SMB2_SIG15, SMB2_SIG16, SMB2_ERROR }; /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0xFE | 'S' | 'M' | 'B' | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Header Length | (padding) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NT_Status | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | (padding) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | :S:C:P:R| | | | Flags +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Chain Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Command Sequence- | +-+-+-+-+-+-+ +-+-+-+-+-+-+-+ | Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Process ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Tree ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+ User ID +-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+ +-+-+-+-+ | | +-+-+-+-+ Signature +-+-+-+-+ | | +-+-+-+-+ +-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ if (max > offset + (smb->hdr.smb2.header_length - smb->hdr.smb2.offset)) max = offset + (smb->hdr.smb2.header_length - smb->hdr.smb2.offset); for (;offsethdr.smb2.ntstatus |= (px[offset] << ((state - SMB2_STATUS1)*8)); state++; break; case SMB2_OPCODE1: case SMB2_OPCODE2: smb->hdr.smb2.opcode |= (px[offset] << ((state - SMB2_OPCODE1)*8)); state++; break; case SMB2_CRED_GRANT1: case SMB2_CRED_GRANT2: state++; break; case SMB2_FLAGS1: smb->hdr.smb2.flags = px[offset]; if ((smb->hdr.smb2.flags & 1) == 0) { banout_append(banout, PROTO_SMB, " PARSERROR[flags] ", AUTO_LEN); state = SMB2_ERROR; } else state++; break; case SMB2_FLAGS2: case SMB2_FLAGS3: case SMB2_FLAGS4: case SMB2_CHAIN_OFFSET1: case SMB2_CHAIN_OFFSET2: case SMB2_CHAIN_OFFSET3: case SMB2_CHAIN_OFFSET4: state++; break; case SMB2_MSGID1: case SMB2_MSGID2: case SMB2_MSGID3: case SMB2_MSGID4: case SMB2_MSGID5: case SMB2_MSGID6: case SMB2_MSGID7: case SMB2_MSGID8: smb->hdr.smb2.seqno |= (px[offset] << ((state - SMB2_MSGID1)*8)); state++; break; case SMB2_PID1: case SMB2_PID2: case SMB2_PID3: case SMB2_PID4: case SMB2_TID1: case SMB2_TID2: case SMB2_TID3: case SMB2_TID4: case SMB2_SESSID1: case SMB2_SESSID2: case SMB2_SESSID3: case SMB2_SESSID4: case SMB2_SIG01: case SMB2_SIG02: case SMB2_SIG03: case SMB2_SIG04: case SMB2_SIG05: case SMB2_SIG06: case SMB2_SIG07: case SMB2_SIG08: case SMB2_SIG09: case SMB2_SIG10: case SMB2_SIG11: case SMB2_SIG12: case SMB2_SIG13: case SMB2_SIG14: case SMB2_SIG15: case SMB2_SIG16: state++; break; default: break; } smb->hdr.smb2.state = (unsigned short)state; smb->hdr.smb2.offset += (unsigned short)(offset - original_offset); return offset - original_offset; } /***************************************************************************** *****************************************************************************/ static size_t smb_parse_smb(struct SMBSTUFF *smb, const unsigned char *px, size_t max, struct BannerOutput *banout, struct stack_handle_t *socket) { size_t len; /*scratch variables used in a couple places */ unsigned state = smb->nbt_state; size_t i = 0; enum { SMB_VER, SMB1_VER_S, SMB1_VER_M, SMB1_VER_B, SMB1_CMD, SMB1_STATUS1, SMB1_STATUS2, SMB1_STATUS3, SMB1_STATUS4, SMB1_FLAGS1, SMB1_FLAGS2, SMB1_FLAGS3, SMB1_PID1, SMB1_PID2, SMB1_SIG1, SMB1_SIG2, SMB1_SIG3, SMB1_SIG4, SMB1_SIG5, SMB1_SIG6, SMB1_SIG7, SMB1_SIG8, SMB1_RSVD1,SMB1_RSVD2, SMB1_TID1, SMB1_TID2, SMB1_PID3, SMB1_PID4, SMB1_UID1, SMB1_UID2, SMB1_MID1, SMB1_MID2, SMB1_WORD_COUNT, SMB1_PARAMETERS, SMB1_BYTE_COUNT1, SMB1_BYTE_COUNT2, SMB1_DATA, SMB1_DATA_AFTER, /* UCHAR Protocol[4]; UCHAR Command; SMB_ERROR Status; UCHAR Flags; USHORT Flags2; USHORT PIDHigh; UCHAR SecurityFeatures[8]; USHORT Reserved; USHORT TID; USHORT PIDLow; USHORT UID; USHORT MID; */ SMB2_VER_S, SMB2_VER_M, SMB2_VER_B, SMB2_HDR_LEN1, SMB2_HDR_LEN2, SMB2_PARSE_HEADER, SMB2_STRUCT_LEN1, SMB2_STRUCT_LEN2, SMB2_PARSE_STRUCT, SMB2_UNTIL_BLOB, SMB2_PARSE_BLOB, SMB2_PARSE_REMAINDER, SMB_ERROR, }; if (max > i + smb->nbt_length) max = i + smb->nbt_length; /* * `for all bytes in the segment` * `do a state transition for that byte ` */ for (; iis_printed_ver) banout_append(banout, PROTO_SMB, "SMBv1 ", AUTO_LEN); smb->is_printed_ver = 1; state = SMB1_VER_S; break; case 0xFE: if (!smb->is_printed_ver) banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN); smb->is_printed_ver = 1; state = SMB2_VER_S; break; default: if (!smb->is_printed_ver) banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN); smb->is_printed_ver = 1; state = SMB_ERROR; } break; case SMB1_VER_S: case SMB2_VER_S: if (px[i] != 'S') state = SMB_ERROR; else state++; break; case SMB1_VER_M: case SMB2_VER_M: if (px[i] != 'M') state = SMB_ERROR; else state++; break; case SMB1_VER_B: case SMB2_VER_B: if (px[i] != 'B') state = SMB_ERROR; else state++; break; case SMB1_CMD: memset(&smb->hdr, 0, sizeof(smb->hdr)); smb->hdr.smb1.command = px[i]; state++; break; case SMB1_STATUS1: case SMB1_STATUS2: case SMB1_STATUS3: case SMB1_STATUS4: smb->hdr.smb1.status <<= 8; smb->hdr.smb1.status |= px[i]; state++; break; case SMB1_FLAGS1: smb->hdr.smb1.flags1 = px[i]; state++; break; case SMB1_FLAGS2: smb->hdr.smb1.flags2 = px[i]; state++; break; case SMB1_FLAGS3: smb->hdr.smb1.flags2 |= px[i]<<8; state++; break; case SMB1_PID1: case SMB1_PID2: smb->hdr.smb1.pid <<= 8; smb->hdr.smb1.pid |= px[i]; state++; break; case SMB1_SIG1: case SMB1_SIG2: case SMB1_SIG3: case SMB1_SIG4: case SMB1_SIG5: case SMB1_SIG6: case SMB1_SIG7: case SMB1_SIG8: state++; break; case SMB1_RSVD1:case SMB1_RSVD2: state++; break; case SMB1_TID1: case SMB1_TID2: smb->hdr.smb1.tid <<= 8; smb->hdr.smb1.tid |= px[i]; state++; break; case SMB1_PID3: case SMB1_PID4: smb->hdr.smb1.pid <<= 8; smb->hdr.smb1.pid |= px[i]; state++; break; case SMB1_UID1: case SMB1_UID2: smb->hdr.smb1.uid <<= 8; smb->hdr.smb1.uid |= px[i]; state++; break; case SMB1_MID1: case SMB1_MID2: smb->hdr.smb1.mid <<= 8; smb->hdr.smb1.mid |= px[i]; state++; break; case SMB1_WORD_COUNT: smb->hdr.smb1.param_length = px[i]*2; memset(&smb->parms, 0, sizeof(smb->parms)); state++; break; case SMB1_PARAMETERS: /* Transfer control to a sub-parser, which may consume zero * or greater bytes, up to the end of the parameters field * (meaning, up to word_count*2 bytes) */ len = smb_params_parse(smb, px, i, max); i += len; if (smb->hdr.smb1.param_offset < smb->hdr.smb1.param_length) break; /* We've reached the end of the parameters field, so go onto * read the byte-count/data field */ state = SMB1_BYTE_COUNT1; /* Unconsume the next byte. The "word-count" field may have been * zero when we get to this state, so therefore the logic needs * to be written to handle this. That means when we loop around * again, we need to counter-act the fact that we will automatically * increment the index, so we subtract one from it here. */ i--; /* Process the parameter/word-count field according to what it * actually contained * TODO: I should make this a function, but I'm lazy */ switch (smb->hdr.smb1.command) { case 0x72: if (!smb->is_printed_time) { char str[64] = "(err)"; time_t timestamp = convert_windows_time(smb->parms.negotiate.SystemTime); struct tm tm = {0}; safe_gmtime(&tm, ×tamp); len = strftime(str, sizeof(str), " time=%Y-%m-%d %H:%M:%S", &tm); banout_append(banout, PROTO_SMB, str, len); snprintf(str, sizeof(str), " TZ=%+d ", (short)smb->parms.negotiate.ServerTimeZone); banout_append(banout, PROTO_SMB, str, AUTO_LEN); smb->is_printed_time = 1; } smb->hdr.smb1.byte_state = 0; if (smb->hdr.smb1.flags2 & 0x0800) { tcpapi_send(socket, smb1_null_session_setup_ex, sizeof(smb1_null_session_setup_ex), 0); } else { if (smb->parms.negotiate.SessionKey) { unsigned char *buf; buf = malloc(sizeof(smb1_null_session_setup)); memcpy(buf, smb1_null_session_setup, sizeof(smb1_null_session_setup)); buf[0x2f] = (unsigned char)(smb->parms.negotiate.SessionKey>> 0) & 0xFF; buf[0x30] = (unsigned char)(smb->parms.negotiate.SessionKey>> 8) & 0xFF; buf[0x31] = (unsigned char)(smb->parms.negotiate.SessionKey>>16) & 0xFF; buf[0x32] = (unsigned char)(smb->parms.negotiate.SessionKey>>24) & 0xFF; tcpapi_send(socket, buf, sizeof(smb1_null_session_setup), TCP__copy); free(buf); } else { tcpapi_send(socket, smb1_null_session_setup, sizeof(smb1_null_session_setup), TCP__static); } } break; case 0x73: /* session setup and x */ break; default: banout_append(banout, PROTO_SMB, " PARSERR(unknown-resonse) ", AUTO_LEN); smb->hdr.smb1.byte_state = 0; } break; case SMB1_BYTE_COUNT1: smb->hdr.smb1.byte_count = px[i]; state++; break; case SMB1_BYTE_COUNT2: smb->hdr.smb1.byte_count |= px[i]<<8; state++; break; case SMB1_DATA: switch (smb->hdr.smb1.command) { case 0x72: if ((smb->hdr.smb1.flags2 & 0x0800) > 0 && smb->parms.negotiate.ChallengeLength == 0) i += smb1_parse_negotiate2(smb, px, i, max, banout); else i += smb1_parse_negotiate1(smb, px, i, max, banout); break; case 0x73: /* session setup and x */ if ((smb->hdr.smb1.flags2 & 0x0800) > 0 || smb->parms.setup.BlobLength) i += smb1_parse_setup2(smb, px, i, max, banout); else i += smb1_parse_setup1(smb, px, i, max, banout); break; default: ; } if (smb->hdr.smb1.byte_offset >= smb->hdr.smb1.byte_count) { state = SMB1_DATA_AFTER; i--; /* unconsume byte because of auto-increment */ /* close the connection, we've found all we can */ if (smb->hdr.smb1.command == 0x73) tcpapi_close(socket); } break; case SMB1_DATA_AFTER: if (i < max) { ; } else { state = 0; i--; } break; case SMB2_HDR_LEN1: memset(&smb->hdr, 0, sizeof(smb->hdr)); smb->hdr.smb2.header_length = px[i]; state++; break; case SMB2_HDR_LEN2: smb->hdr.smb2.header_length |= (px[i]<<8); if (smb->hdr.smb2.header_length < 12) { banout_append(banout, PROTO_SMB, " PARSERROR[hdrlen] ", AUTO_LEN); state = SMB_ERROR; } else { smb->hdr.smb2.offset = 6; state++; } break; case SMB2_PARSE_HEADER: i += smb2_parse_header(smb, px, i, max, banout); if (smb->hdr.smb2.offset >= smb->hdr.smb2.header_length) { state++; i--; } break; case SMB2_STRUCT_LEN1: smb->hdr.smb2.struct_length = px[i]; state++; break; case SMB2_STRUCT_LEN2: smb->hdr.smb2.struct_length |= (px[i]<<8); smb->hdr.smb2.is_dynamic = (smb->hdr.smb2.struct_length&1); smb->hdr.smb2.struct_length &= 0xFFFe; smb->hdr.smb2.state = 0; smb->hdr.smb2.offset = 2; memset(&smb->parms, 0, sizeof(smb->parms)); if (smb->hdr.smb2.struct_length < 2) { banout_append(banout, PROTO_SMB, " PARSERROR[structlen] ", AUTO_LEN); state = SMB_ERROR; } else state++; break; case SMB2_PARSE_STRUCT: /* * Parse the data portion */ switch (smb->hdr.smb2.opcode) { case 0x00: /* Negotiate Response */ i += smb2_parse_negotiate(smb, px, i, max, banout); break; case 0x01: /* Session Setup */ i += smb2_parse_setup(smb, px, i, max, banout); break; default: i += smb2_parse_response(smb, px, i, max, banout); break; } /* * Respond if necessary */ if (smb->hdr.smb2.offset >= smb->hdr.smb2.struct_length) { switch (smb->hdr.smb2.opcode) { case 0x00: /* negotiate response */ if (smb->hdr.smb2.seqno == 0) { tcpapi_send(socket, smb2_negotiate_request, sizeof(smb2_negotiate_request), 0); } else if (smb->hdr.smb2.seqno == 1) { tcpapi_send(socket, smb2_null_session_setup, sizeof(smb2_null_session_setup), 0); } break; default: ; } i--; /* * Process security blob */ if (smb->hdr.smb2.blob_length == 0) state = SMB2_PARSE_REMAINDER; else if (smb->hdr.smb2.blob_offset < smb->hdr.smb2.header_length + smb->hdr.smb2.struct_length) { printf("\n***** parse error *****\n"); state = SMB2_PARSE_REMAINDER; } else { smb->hdr.smb2.blob_offset -= smb->hdr.smb2.header_length; smb->hdr.smb2.blob_offset -= smb->hdr.smb2.struct_length; state = SMB2_UNTIL_BLOB; } } break; case SMB2_UNTIL_BLOB: if (smb->hdr.smb2.blob_offset == 0) { spnego_decode_init(&smb->spnego, smb->hdr.smb2.blob_length); i--; state = SMB2_PARSE_BLOB; } else smb->hdr.smb2.blob_offset--; break; case SMB2_PARSE_BLOB: { size_t new_max = max; if (new_max > i + smb->hdr.smb2.blob_length) new_max = i + smb->hdr.smb2.blob_length; spnego_decode(&smb->spnego, px+i, new_max-i, banout); smb->hdr.smb2.blob_length -= (unsigned short)(new_max-i); i = new_max; if (smb->hdr.smb2.blob_length == 0) { i--; state = SMB2_PARSE_REMAINDER; /* Close the connection when we get a SessionSetup response */ if (smb->hdr.smb2.opcode == 1) tcpapi_close(socket); } } break; case SMB2_PARSE_REMAINDER: case SMB_ERROR: default: break; } smb->nbt_length -= (unsigned)i; smb->nbt_state = state; return i; } /***************************************************************************** 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TYPE | FLAGS | LENGTH | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | / TRAILER (Packet Type Dependent) / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *****************************************************************************/ static void smb_parse_record( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t max, struct BannerOutput *banout, struct stack_handle_t *socket) { size_t i; unsigned state = pstate->state; struct SMBSTUFF *smb = &pstate->sub.smb; enum { NBT_TYPE, NBT_FLAGS, NBT_LEN1, NBT_LEN2, NBT_ERR, NBT_SMB, NBT_DRAIN, NBT_UNKNOWN, }; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; ispnego.ntlmssp.buf) ntlmssp_cleanup(&smb->spnego.ntlmssp); smb->nbt_type = px[i]; state++; break; case NBT_FLAGS: smb->nbt_flags = px[i] & 0xFE; smb->nbt_length = px[i] & 0x01; state++; break; case NBT_LEN1: smb->nbt_length <<= 8; smb->nbt_length |= px[i]; state++; break; case NBT_LEN2: smb->nbt_length <<= 8; smb->nbt_length |= px[i]; state++; /* 00 - SESSION MESSAGE 81 - SESSION REQUEST 82 - POSITIVE SESSION RESPONSE 83 - NEGATIVE SESSION RESPONSE 84 - RETARGET SESSION RESPONSE 85 - SESSION KEEP ALIVE */ switch (smb->nbt_type) { case 0x00: state = NBT_SMB; smb->nbt_state = 0; break; case 0x81: banout_append(banout, PROTO_SMB, " PARSERR(nbt-sess) ", AUTO_LEN); state = NBT_UNKNOWN; break; case 0x82: tcpapi_send(socket, smb1_hello_template, sizeof(smb1_hello_template), 0); state = NBT_DRAIN; break; case 0x85: state = NBT_DRAIN; break; case 0x83: state = NBT_ERR; break; case 0x84: banout_append(banout, PROTO_SMB, " PARSERR(nbt-retarget) ", AUTO_LEN); state = NBT_UNKNOWN; break; default: banout_append(banout, PROTO_SMB, "ERR unknown response", AUTO_LEN); break; } break; case NBT_ERR: smb->nbt_err = px[i]; /* 80 - Not listening on called name 81 - Not listening for calling name 82 - Called name not present 83 - Called name present, but insufficient resources 8F - Unspecified error */ switch (smb->nbt_err) { case 0x80: banout_append(banout, PROTO_SMB, "ERROR(Not listening on called name)", AUTO_LEN); break; case 0x81: banout_append(banout, PROTO_SMB, "ERROR(Not listening for calling name)", AUTO_LEN); break; case 0x82: banout_append(banout, PROTO_SMB, "ERROR(Called name not present)", AUTO_LEN); break; case 0x83: banout_append(banout, PROTO_SMB, "ERROR(Called name present, but insufficient resources)", AUTO_LEN); break; case 0x8F: banout_append(banout, PROTO_SMB, "ERROR(Unspecified error)", AUTO_LEN); break; default: banout_append(banout, PROTO_SMB, "ERROR(UNKNOWN)", AUTO_LEN); break; } state = NBT_DRAIN; break; case NBT_SMB: i += smb_parse_smb(smb, px+i, max-i, banout, socket); if (smb->nbt_length == 0) { state = 0; i--; } break; case NBT_DRAIN: if (smb->nbt_length == 0) { state = 0; i--; } else smb->nbt_length--; break; case NBT_UNKNOWN: default: break; } pstate->state = state; } /***************************************************************************** *****************************************************************************/ #if 0 static int negot_add_dialect(unsigned char *buf, size_t sizeof_buf, const char *dialect) { size_t nbt_length; size_t dialect_length = strlen(dialect) + 1; size_t word_count; //size_t byte_count; /* Parse NetBIOS header */ if (sizeof_buf < 4 || sizeof_buf + 4 < dialect_length) return -1; if (buf[0] != 0) return -1; nbt_length = buf[2]<<8 | buf[3]; if (nbt_length <= 4 || nbt_length >= sizeof_buf - dialect_length) return -1; /* Parse SMB header */ if (memcmp(buf+4, "\xFF" "SMB" "\x72", 5) != 0) return -1; if (nbt_length < 39) return -1; word_count = buf[36]; if (word_count != 0) return -1; //byte_count = buf[37] | buf[38]<<8; return 0; } #endif /***************************************************************************** *****************************************************************************/ static void * smb_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /***************************************************************************** *****************************************************************************/ static const char smb0_hello_template[] = { 0x81, 0x00, 0x00, 0x44, 0x20, 0x43, 0x4b, 0x46, 0x44, 0x45, 0x4e, 0x45, 0x43, 0x46, 0x44, 0x45, 0x46, 0x46, 0x43, 0x46, 0x47, 0x45, 0x46, 0x46, 0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x45, 0x44, 0x46, 0x43, 0x45, 0x46, 0x45, 0x46, 0x45, 0x44, 0x45, 0x49, 0x45, 0x46, 0x46, 0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00, /*0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, 0x00*/ }; /***************************************************************************** * Do a single test of response packets *****************************************************************************/ static int smb_do_test(const char *substring, const unsigned char *packet_bytes, size_t length) { struct Banner1 *banner1; struct StreamState state[1]; struct BannerOutput banout1[1]; struct stack_handle_t socket = {0}; int x; banner1 = banner1_create(); banout_init(banout1); memset(&state[0], 0, sizeof(state[0])); smb_parse_record(banner1, 0, state, packet_bytes, length, banout1, &socket); x = banout_is_contains(banout1, PROTO_SMB, substring); if (x == 0) printf("smb parser failure: %s\n", substring); banner1_destroy(banner1); banout_release(banout1); return x?0:1; } /***************************************************************************** *****************************************************************************/ static int smb_selftest(void) { int x = 0; /***************************************************************************** *****************************************************************************/ { static const unsigned char packet_bytes[] = { 0x00, 0x00, 0x00, 0x9f, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x11, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xad, 0xa0, 0x03, 0x0a, 0x7c, 0xe0, 0x00, 0x80, 0x00, 0x1d, 0xbd, 0xd5, 0xe2, 0x0f, 0xcf, 0x01, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x00, 0x00, 0x01, 0x2a, 0xff, 0x53, 0x4d, 0x42, 0x73, 0x16, 0x00, 0x00, 0xc0, 0x88, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0xff, 0x00, 0x2a, 0x01, 0x00, 0x00, 0xb3, 0x00, 0xff, 0x00, 0xa1, 0x81, 0xb0, 0x30, 0x81, 0xad, 0xa0, 0x03, 0x0a, 0x01, 0x01, 0xa1, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x81, 0x97, 0x04, 0x81, 0x94, 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x12, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x02, 0x89, 0xe0, 0x31, 0x6a, 0x74, 0x8f, 0xb5, 0xf1, 0xe1, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x52, 0x00, 0x42, 0x00, 0x00, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x50, 0x00, 0x02, 0x00, 0x12, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x50, 0x00, 0x01, 0x00, 0x16, 0x00, 0x45, 0x00, 0x50, 0x00, 0x53, 0x00, 0x4f, 0x00, 0x4e, 0x00, 0x38, 0x00, 0x38, 0x00, 0x33, 0x00, 0x31, 0x00, 0x46, 0x00, 0x45, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0x00, 0x45, 0x00, 0x50, 0x00, 0x53, 0x00, 0x4f, 0x00, 0x4e, 0x00, 0x38, 0x00, 0x38, 0x00, 0x33, 0x00, 0x31, 0x00, 0x46, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x50, 0x00, 0x53, 0x00, 0x4f, 0x00, 0x4e, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x51, 0x00, 0x20, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x00, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x50, 0x00, 0x00, 0x00 }; x += smb_do_test("os=EPSON", packet_bytes, sizeof(packet_bytes)); } /***************************************************************************** *****************************************************************************/ { static const unsigned char packet_bytes[] = { 0x00, 0x00, 0x00, 0x56, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x98, 0x45, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x03, 0x05, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x1d, 0xc2, 0x00, 0x00, 0x00, 0x83, 0xa9, 0xe2, 0x31, 0x02, 0xd4, 0x01, 0x00, 0x00, 0x08, 0x11, 0x00, 0x77, 0x6d, 0x78, 0x8f, 0x06, 0x52, 0x8f, 0xb8, 0x53, 0x36, 0x35, 0x39, 0x43, 0x32, 0x37, 0x44, 0x00 }; x += smb_do_test("domain=S659C27D", packet_bytes, sizeof(packet_bytes)); } if (x) { printf("smb parser failure: google.com\n"); return 1; } return 0; #if 0 { struct Banner1 *banner1; struct ProtocolState state[1]; struct BannerOutput banout1[1]; struct InteractiveData socket; size_t i; /* * LET'S FUZZ THIS CRAP!!! * * We are going to re-parse the response packet as many times as needed, * each time flipping one bit in the packet. This should crash the * parser if it has such a bug that will crash it. */ for (i=2; i< 5 && isub.smb; if (smb->spnego.ntlmssp.buf) ntlmssp_cleanup(&smb->spnego.ntlmssp); } /***************************************************************************** * This is the 'plugin' structure that registers callbacks for this parser in * the main system. *****************************************************************************/ struct ProtocolParserStream banner_smb0 = { "smb", 139, smb0_hello_template, sizeof(smb0_hello_template), 0, smb_selftest, smb_init, smb_parse_record, smb_cleanup }; struct ProtocolParserStream banner_smb1 = { "smb", 445, smb1_hello_template, sizeof(smb1_hello_template), 0, smb_selftest, smb_init, smb_parse_record, smb_cleanup }; ================================================ FILE: src/proto-smb.h ================================================ #ifndef PROTO_SMB_H #define PROTO_SMB_H #include "proto-banner1.h" extern struct ProtocolParserStream banner_smb0; extern struct ProtocolParserStream banner_smb1; /** * Called when command line parameter: * --hello smbv1 * is set, in order to force negotiation down to SMBv1. This is because some machines * have faulty SMBv2 implementations. SMBv2, though, is the default negotiation * because Win10 disables SMBv1 by default. */ void smb_set_hello_v1(struct ProtocolParserStream *smb); #endif ================================================ FILE: src/proto-smtp.c ================================================ /* SMTP banner checker This file interacts with an SMTP server when it finds a connection on an SMTP port like 25 with a "220 " as the banner. Firstly, SMTP requires that the client send a "EHLO" command in order to announce its presence. This command will tell us about some optional features of the server, which we'll record as part of the [smtp] banner. Secondly, we'll attempt to do a STARTTLS command, regardless whether the server advertised the capability. This should either get back an "OK" message or an error, which we also record as part of the banner. If we get an OK, then we switch the parser to SSL, and continue as if this were an SSL connection. Any SSL data will show up as an [ssl] protocol rather than an SMTP protocol. */ #include "proto-smtp.h" #include "proto-banner1.h" #include "unusedparm.h" #include "masscan-app.h" #include "stack-tcp-api.h" #include "proto-ssl.h" #include #include /*************************************************************************** ***************************************************************************/ static void smtp_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned i; struct SMTPSTUFF *smtp = &pstate->sub.smtp; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; icode = 0; state++; /* fall through */ case 1: case 2: case 3: case 101: case 102: case 103: case 201: case 202: case 203: if (!isdigit(px[i]&0xFF)) { state = 0xffffffff; tcpapi_close(socket); } else { smtp->code *= 10; smtp->code += (px[i] - '0'); state++; banout_append_char(banout, PROTO_SMTP, px[i]); } break; case 4: case 104: case 204: if (px[i] == ' ') { smtp->is_last = 1; state++; banout_append_char(banout, PROTO_SMTP, px[i]); } else if (px[i] == '-') { smtp->is_last = 0; state++; banout_append_char(banout, PROTO_SMTP, px[i]); } else { state = 0xffffffff; tcpapi_close(socket); } break; case 5: if (px[i] == '\r') continue; else if (px[i] == '\n') { if (smtp->is_last) { tcpapi_send(socket, "EHLO masscan\r\n", 14, 0); state = 100; banout_append_char(banout, PROTO_SMTP, px[i]); } else { banout_append_char(banout, PROTO_SMTP, px[i]); state = 0; } } else if (px[i] == '\0' || !isprint(px[i])) { state = 0xffffffff; tcpapi_close(socket); continue; } else { banout_append_char(banout, PROTO_SMTP, px[i]); } break; case 105: if (px[i] == '\r') continue; else if (px[i] == '\n') { if (smtp->is_last) { tcpapi_send(socket, "STARTTLS\r\n", 10, 0); state = 200; banout_append_char(banout, PROTO_SMTP, px[i]); } else { banout_append_char(banout, PROTO_SMTP, px[i]); state = 100; } } else if (px[i] == '\0' || !isprint(px[i])) { state = 0xffffffff; tcpapi_close(socket); continue; } else { banout_append_char(banout, PROTO_SMTP, px[i]); } break; case 205: if (px[i] == '\r') continue; else if (px[i] == '\n') { if (smtp->code == 220) { /* change the state here to SSL */ unsigned port = pstate->port; memset(pstate, 0, sizeof(*pstate)); pstate->app_proto = PROTO_SSL3; pstate->is_sent_sslhello = 1; pstate->port = (unsigned short)port; state = 0; tcpapi_send(socket, banner_ssl.hello, banner_ssl.hello_length, 0); } else { state = 0xffffffff; tcpapi_close(socket); } } else if (px[i] == '\0' || !isprint(px[i])) { state = 0xffffffff; tcpapi_close(socket); continue; } else { banout_append_char(banout, PROTO_SMTP, px[i]); } break; default: i = (unsigned)length; break; } } pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * smtp_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /*************************************************************************** ***************************************************************************/ static int smtp_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_smtp = { "smtp", 25, 0, 0, 0, smtp_selftest, smtp_init, smtp_parse, }; ================================================ FILE: src/proto-smtp.h ================================================ #ifndef PROTO_SMTP_H #define PROTO_SMTP_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_smtp; #endif ================================================ FILE: src/proto-snmp.c ================================================ /* SNMP protocol handler This module does two primary things: #1 track sequence number Like TCP, we can use seqno-cookies to match requrests with replies. All SNMP packets have a "request-id" field to match requests with replies, so we can fill this in with our cookies This requires any SNMP template to reserve 4 bytes for this field. #2 parse the response This code will report any OIDs in the response along with their values. However, you should probably hard-code a "MIB" so that we can translate OIDs into shorter names as well as format their values correctly. You can look at sysName and sysDescr for an example of how this works. NOTES for IDS LULZ: The default template packet is designed to evade IDS that looks for OIDS, but inserting "nul" bytes into the OID. This is the difference between "pattern-matching" IDS and "protocol-analysis" IDS: those using protocol-analysis decodes the entire protocol, whereas pattern-matchers don't. How protocol-analysis works is demonstrated in this module, as it has to analyze the SNMP protocol itself in order to decode responses. It can handle responses that that have the deliberate "nul" insertion technique, as shown in the self-test. One of the useful tricks is to exploit DFA pattern matches. In this case, the DFA pattern-matcher that ultimately matches on the OIDs has been tweaked to handle the nuls as part of the pattern-matching process. */ #include "proto-snmp.h" #include #include #include "smack.h" #include "util-safefunc.h" #include "output.h" #include "masscan-app.h" #include "proto-preprocess.h" #include "proto-banner1.h" #include "syn-cookie.h" #include "massip-port.h" static struct SMACK *global_mib; /**************************************************************************** * We parse an SNMP packet into this structure ****************************************************************************/ struct SNMP { uint64_t version; uint64_t pdu_tag; const unsigned char *community; uint64_t community_length; uint64_t request_id; uint64_t error_index; uint64_t error_status; }; /**************************************************************************** * This is the "compiled MIB" essentially. At program startup, we compile * this into an OID tree. We use this to replace OIDs with names. ****************************************************************************/ static struct SnmpOid { const char *oid; const char *name; } mib[] = { {"43.1006.51.341332", "selftest"}, /* for regression test */ {"43", "iso.org"}, {"43.6", "dod"}, {"43.6.1", "inet"}, {"43.6.1.2", "mgmt"}, {"43.6.1.2.1", "mib2"}, {"43.6.1.2.1.", "sys"}, {"43.6.1.2.1.1.1", "sysDescr"}, {"43.6.1.2.1.1.2", "sysObjectID"}, {"43.6.1.2.1.1.3", "sysUpTime"}, {"43.6.1.2.1.1.4", "sysContact"}, {"43.6.1.2.1.1.5", "sysName"}, {"43.6.1.2.1.1.6", "sysLocation"}, {"43.6.1.2.1.1.7", "sysServices"}, {"43.6.1.4", "priv"}, {"43.6.1.4.1", "enterprise"}, {"43.6.1.4.1.2001", "okidata"}, {0,0}, }; /**************************************************************************** * An ASN.1 length field has two formats. * - if the high-order bit of the length byte is clear, then it * encodes a length between 0 and 127. * - if the high-order bit is set, then the length byte is a * length-of-length, where the low order bits dictate the number of * remaining bytes to be used in the length. ****************************************************************************/ static uint64_t asn1_length(const unsigned char *px, uint64_t length, uint64_t *r_offset) { uint64_t result; /* check for errors */ if ( (*r_offset >= length) || ((px[*r_offset] & 0x80) && ((*r_offset) + (px[*r_offset]&0x7F) >= length))) { *r_offset = length; return 0xFFFFffff; } /* grab the byte's value */ result = px[(*r_offset)++]; if (result & 0x80) { unsigned length_of_length = result & 0x7F; if (length_of_length == 0) { *r_offset = length; return 0xFFFFffff; } result = 0; while (length_of_length) { result = result * 256 + px[(*r_offset)++]; if (result > 0x10000) { *r_offset = length; return 0xFFFFffff; } length_of_length--; } } return result; } /**************************************************************************** * Extract an integer. Note ****************************************************************************/ static uint64_t asn1_integer(const unsigned char *px, uint64_t length, uint64_t *r_offset) { uint64_t int_length; uint64_t result; if (px[(*r_offset)++] != 0x02) { *r_offset = length; return 0xFFFFffff; } int_length = asn1_length(px, length, r_offset); if (int_length == 0xFFFFffff) { *r_offset = length; return 0xFFFFffff; } if (*r_offset + int_length > length) { *r_offset = length; return 0xFFFFffff; } if (int_length > 20) { *r_offset = length; return 0xFFFFffff; } result = 0; while (int_length--) result = result * 256 + px[(*r_offset)++]; return result; } /**************************************************************************** ****************************************************************************/ static unsigned asn1_tag(const unsigned char *px, uint64_t length, uint64_t *r_offset) { if (*r_offset >= length) return 0; return px[(*r_offset)++]; } /**************************************************************************** ****************************************************************************/ static uint64_t next_id(const unsigned char *oid, unsigned *offset, uint64_t oid_length) { uint64_t result = 0; while (*offset < oid_length && (oid[*offset] & 0x80)) { result <<= 7; result |= oid[(*offset)++]&0x7F; } if (*offset < oid_length) { result <<= 7; result |= oid[(*offset)++]&0x7F; } return result; } /**************************************************************************** ****************************************************************************/ static void snmp_banner_oid(const unsigned char *oid, size_t oid_length, struct BannerOutput *banout) { unsigned i; size_t id; unsigned offset; unsigned state; size_t found_id = SMACK_NOT_FOUND; size_t found_offset = 0; /* * Find the var name */ state = 0; for (offset=0; offset= oid_length) break; snprintf(foo, sizeof(foo), ".%" PRIu64 "", x); banout_append(banout, PROTO_SNMP, foo, strlen(foo)); } } /**************************************************************************** ****************************************************************************/ static void snmp_banner(const unsigned char *oid, size_t oid_length, uint64_t var_tag, const unsigned char *var, size_t var_length, struct BannerOutput *banout) { size_t i; banout_newline(banout, PROTO_SNMP); /* print the OID */ snmp_banner_oid(oid, oid_length, banout); banout_append_char(banout, PROTO_SNMP, ':'); switch (var_tag) { case 2: { char foo[32]; uint64_t result = 0; for (i=0; i outer_length + offset) length = outer_length + offset; /* Version */ snmp->version = asn1_integer(px, length, &offset); if (snmp->version != 0) return; /* Community */ if (asn1_tag(px, length, &offset) != 0x04) return; snmp->community_length = asn1_length(px, length, &offset); snmp->community = px+offset; offset += snmp->community_length; /* PDU */ snmp->pdu_tag = asn1_tag(px, length, &offset); if (snmp->pdu_tag < 0xA0 || 0xA5 < snmp->pdu_tag) return; outer_length = asn1_length(px, length, &offset); if (length > outer_length + offset) length = outer_length + offset; /* Request ID */ snmp->request_id = asn1_integer(px, length, &offset); *request_id = (unsigned)snmp->request_id; snmp->error_status = asn1_integer(px, length, &offset); snmp->error_index = asn1_integer(px, length, &offset); /* Varbind List */ if (asn1_tag(px, length, &offset) != 0x30) return; outer_length = asn1_length(px, length, &offset); if (length > outer_length + offset) length = outer_length + offset; /* Var-bind list */ while (offset < length) { uint64_t varbind_length; uint64_t varbind_end; if (px[offset++] != 0x30) { break; } varbind_length = asn1_length(px, length, &offset); if (varbind_length == 0xFFFFffff) break; varbind_end = offset + varbind_length; if (varbind_end > length) { return; } /* OID */ if (asn1_tag(px,length,&offset) != 6) return; else { uint64_t oid_length = asn1_length(px, length, &offset); const unsigned char *oid = px+offset; uint64_t var_tag; uint64_t var_length; const unsigned char *var; offset += oid_length; if (offset > length) return; var_tag = asn1_tag(px,length,&offset); var_length = asn1_length(px, length, &offset); var = px+offset; offset += var_length; if (offset > length) return; if (var_tag == 5) continue; /* null */ snmp_banner(oid, (size_t)oid_length, var_tag, var, (size_t)var_length, banout); } } } /**************************************************************************** ****************************************************************************/ unsigned snmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) { uint64_t offset=0; uint64_t outer_length; uint64_t version; uint64_t tag; uint64_t len; /* tag */ if (asn1_tag(px, length, &offset) != 0x30) return 0; /* length */ outer_length = asn1_length(px, length, &offset); if (length > outer_length + offset) length = (size_t)(outer_length + offset); /* Version */ version = asn1_integer(px, length, &offset); if (version != 0) return 0; /* Community */ if (asn1_tag(px, length, &offset) != 0x04) return 0; offset += asn1_length(px, length, &offset); /* PDU */ tag = asn1_tag(px, length, &offset); if (tag < 0xA0 || 0xA5 < tag) return 0; outer_length = asn1_length(px, length, &offset); if (length > outer_length + offset) length = (size_t)(outer_length + offset); /* Request ID */ asn1_tag(px, length, &offset); len = asn1_length(px, length, &offset); switch (len) { case 0: return 0; case 1: px[offset+0] = (unsigned char)(seqno>>0)&0x7F; return seqno & 0x7F; case 2: px[offset+0] = (unsigned char)(seqno>>8)&0x7F; px[offset+1] = (unsigned char)(seqno>>0); return seqno & 0x7fff; case 3: px[offset+0] = (unsigned char)(seqno>>16)&0x7F; px[offset+1] = (unsigned char)(seqno>>8); px[offset+2] = (unsigned char)(seqno>>0); return seqno & 0x7fffFF; case 4: px[offset+0] = (unsigned char)(seqno>>24)&0x7F; px[offset+1] = (unsigned char)(seqno>>16); px[offset+2] = (unsigned char)(seqno>>8); px[offset+3] = (unsigned char)(seqno>>0); return seqno & 0x7fffFFFF; case 5: px[offset+0] = 0; px[offset+1] = (unsigned char)(seqno>>24); px[offset+2] = (unsigned char)(seqno>>16); px[offset+3] = (unsigned char)(seqno>>8); px[offset+4] = (unsigned char)(seqno>>0); return seqno & 0xffffFFFF; } return 0; } #define TWO_BYTE ((unsigned long long)(~0)<<7) #define THREE_BYTE ((unsigned long long)(~0)<<14) #define FOUR_BYTE ((unsigned long long)(~0)<<21) #define FIVE_BYTE ((unsigned long long)(~0)<<28) /**************************************************************************** ****************************************************************************/ static unsigned id_prefix_count(unsigned id) { if (id & FIVE_BYTE) return 4; if (id & FOUR_BYTE) return 3; if (id & THREE_BYTE) return 2; if (id & TWO_BYTE) return 1; return 0; } /**************************************************************************** * Convert text OID to binary ****************************************************************************/ static unsigned convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src) { size_t offset = 0; while (*src) { const char *next_src; unsigned id; unsigned count; unsigned i; while (*src == '.') src++; id = (unsigned)strtoul(src, (char**)&next_src, 0); if (src == next_src) break; else src = next_src; count = id_prefix_count(id); for (i=count; i>0; i--) { if (offset < sizeof_dst) dst[offset++] = ((id>>(7*i)) & 0x7F) | 0x80; } if (offset < sizeof_dst) dst[offset++] = (id & 0x7F); } return (unsigned)offset; } /**************************************************************************** * Handles an SNMP response. ****************************************************************************/ unsigned handle_snmp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ) { ipaddress ip_them = parsed->src_ip; ipaddress ip_me = parsed->dst_ip; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; unsigned seqno; unsigned request_id = 0; struct BannerOutput banout[1]; UNUSEDPARM(length); /* Initialize the "banner output" module that we'll use to print * pretty text in place of the raw packet */ banout_init(banout); /* Parse the SNMP packet */ snmp_parse( px + parsed->app_offset, /* incoming SNMP response */ parsed->app_length, /* length of SNMP response */ banout, /* banner printing */ &request_id); /* syn-cookie info */ /* Validate the "syn-cookie" style information. In the case of SNMP, * this will be held in the "request-id" field. If the cookie isn't * a good one, then we'll ignore the response */ seqno = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy); if ((seqno&0x7FFFffff) != request_id) return 1; /* Print the banner information, or save to a file, depending */ output_report_banner( out, timestamp, ip_them, 17, parsed->port_src, PROTO_SNMP, parsed->ip_ttl, banout_string(banout, PROTO_SNMP), banout_string_length(banout, PROTO_SNMP)); /* Free memory for the banner, if there was any allocated */ banout_release(banout); return 0; } /**************************************************************************** * We need to initialize the OID/MIB parser * This should be called on program startup. * This is so that we can show short names, like "sysName", rather than * the entire OID. ****************************************************************************/ void snmp_init(void) { unsigned i; /* We use an Aho-Corasick pattern matcher for this. Not necessarily * the most efficient, but also not bad */ global_mib = smack_create("snmp-mib", 0); /* We just go through the table of OIDs and add them all one by * one */ for (i=0; mib[i].name; i++) { unsigned char pattern[256]; unsigned len; len = convert_oid(pattern, sizeof(pattern), mib[i].oid); smack_add_pattern( global_mib, pattern, len, i, SMACK_ANCHOR_BEGIN | SMACK_SNMP_HACK ); } /* Now that we've added all the OIDs, we need to compile this into * an efficient data structure. Later, when we get packets, we'll * use this for searching */ smack_compile(global_mib); } /**************************************************************************** ****************************************************************************/ static int snmp_selftest_banner() { static const unsigned char snmp_response[] = { 0x30, 0x39, 0x02, 0x01, 0x00, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0xA2, 0x2C, 0x02, 0x01, 0x26, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x21, 0x30, 0x1F, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x80, 0x02, 0x01, 0x01, 0x02, 0x00, 0x06, 0x12, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x8F, 0x51, 0x01, 0x01, 0x01, 0x82, 0x29, 0x5D, 0x01, 0x1B, 0x02, 0x02, 0x01, }; unsigned request_id = 0; struct BannerOutput banout[1]; banout_init(banout); /* parse a test packet */ snmp_parse( snmp_response, sizeof(snmp_response), banout, &request_id ); if (request_id != 0x26) return 1; { const unsigned char *str = banout_string(banout, PROTO_SNMP); size_t str_length = banout_string_length(banout, PROTO_SNMP); if (memcmp(str, "sysObjectID:okidata.1.1.1.297.93.1.27.2.2.1", str_length) != 0) return 1; } banout_release(banout); return 0; } /**************************************************************************** ****************************************************************************/ int snmp_selftest(void) { static const unsigned char xx[] = { 43, 0x80|7, 110, 51, 0x80|20, 0x80|106, 84, }; size_t i; unsigned state; unsigned offset; size_t found_id = SMACK_NOT_FOUND; if (snmp_selftest_banner()) return 1; /* * test of searching OIDs */ state = 0; offset = 0; while (offset < sizeof(xx)) { i = smack_search_next( global_mib, &state, xx, &offset, (unsigned)sizeof(xx) ); if (i != SMACK_NOT_FOUND) found_id = i; } if (found_id == SMACK_NOT_FOUND) { fprintf(stderr, "snmp: oid parser failed\n"); return 1; } if (strcmp(mib[found_id].name, "selftest") != 0) { fprintf(stderr, "snmp: oid parser failed\n"); return 1; } return 0; } ================================================ FILE: src/proto-snmp.h ================================================ #ifndef PROTO_SNMP_H #define PROTO_SNMP_H #include #include struct Output; struct PreprocessedInfo; /** * Need to call this on startup to compile the internal MIB. */ void snmp_init(void); /** * Does a regression test. * @return * 0 if success, 1 if failure */ int snmp_selftest(void); unsigned snmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno); unsigned handle_snmp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); #endif ================================================ FILE: src/proto-spnego.h ================================================ #ifndef PROTO_SPNEGO_H #define PROTO_SPNEGO_H #include "proto-x509.h" #include "proto-ntlmssp.h" struct SpnegoDecode { /* * ====== KLUDGE ALERT: there's no generic ASN.1 encoding, it's specific to * ====== x.509 parsing, so therefore we are just going to overload that * ====== a bit until we move the code out into it's own ASN.1 module */ struct CertDecode x509[1]; struct NtlmsspDecode ntlmssp; }; void spnego_decode_init(struct SpnegoDecode *x, size_t length); void spnego_decode(struct SpnegoDecode *x, const unsigned char *px, size_t length, struct BannerOutput *banout); #endif ================================================ FILE: src/proto-ssh.c ================================================ #include "proto-ssh.h" #include "proto-banner1.h" #include "unusedparm.h" #include "masscan-app.h" #include "stack-tcp-api.h" #include #define PAYLOAD_BANNER "SSH-2.0-OPENSSH_7.9\r\n" #define SIZE_BANNER 21 #define PAYLOAD_KEY_EXHANGE_INIT "\x00\x00\x04\x4c" /* packet length: 1100 */ \ "\x04" /* padding_length (this value is include in the packet length) */ \ "\x14" /* message_code = 20 */ \ "\xf3\xca\xd2\x90\xec\xf4\x7c\x47\x55\x4c\x88\xcf\x3a\x72\x2b\xb2" /*cookie */ \ "\x00\x00\x00\xd8" /* kex_algorithms_length */ \ "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256" /* kex_algorithms_string */ \ "\x00\x00\x00\x21" /* server_host_key_algorithms_length */ \ "ssh-rsa,rsa-sha2-512,rsa-sha2-256" /* server_host_key_algorithms_string */ \ "\x00\x00\x00\xaf" /* encryption_algorithms_client_to_server_length */ \ "chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc" /* encryption_algorithms_client_to_server_string */ \ "\x00\x00\x00\xaf" /* encryption_algorithms_server_to_client_length */ \ "chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc" /* encryption_algorithms_server_to_client_string */ \ "\x00\x00\x00\xd5" /* mac_algorithms_client_to_server_length */ \ "umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1" /* mac_algorithms_client_to_server_string */ \ "\x00\x00\x00\xd5" /* mac_algorithms_server_to_client_length */ \ "umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1" /* mac_algorithms_server_to_client_string */ \ "\x00\x00\x00\x04" /* compression_algorithms_client_to_server_length */ \ "none" /* compression_algorithms_client_to_server_string */ \ "\x00\x00\x00\x04" /* compression_algorithms_server_to_client_length */ \ "none" /* compression_algorithms_server_to_client_string */ \ "\x00\x00\x00\x00" /* languages_client_to_server_length */ \ "\x00\x00\x00\x00" /* languages_server_to_client_length */ \ "\x00" /* first_KEX_Packet_Follows */ \ "\x00\x00\x00\x00" /* reserved */ \ "\x00\x00\x00\x00" /* Padding_String */ \ "\x00\x00\x00\x8c" /* DH_packet_length */ \ "\x05" /* DH_padding_length */ \ "\x1e" /* DH_message_code */ \ "\x00\x00\x00\x81" /* DH_multiprecision_integer_length */ \ "\x00\xd4\x6e\xe0\x12\xa6\x56\x95\x37\xa0\x14\x2e\x4e\x4d\x57\x48\x1d\x4b\x80\x90\x1e\x61\x6f\x5c\xc4\xd7\xbc\x17\x25\xb7\x41\x8c\x6c\x8b\xed\x74\x2d\xc0\x54\xeb\x08\x3a\x79\x5e\x0c\xad\x04\xe8\xb7\xfb\xa1\x68\x62\x66\xd3\x9a\x26\x39\xaa\x6c\x89\x2f\x5c\x99\xab\xd2\x43\xda\xa7\xef\x1c\x19\xdc\xa6\x03\xc9\x8a\x56\x19\x74\xd1\xb8\x08\xdc\x76\x14\xe7\x86\x50\x74\x01\xed\xd4\xfb\x1a\x1a\x25\x5d\x1a\xc7\x5f\x0c\xb3\xcc\x58\x5a\x40\xd5\x04\xa5\xc1\x30\x14\x86\xf0\xb8\x33\x17\xb4\x23\x9d\x43\x6d\x38\x87\xec\xa9\xbc\x3b" /* DH_padding_string */ \ "\x00\x00\x00\x00\x00" /* DH_padding_string */ #define SIZE_KEY_EXCHANGE_INIT (1100+4+140+4) //length_of_the_first_packet(packet_length + length of the packet_length_field(4)) + DH_packet_length (including the DH_padding_length) + the length of the DH_length_field (4). #define PAYLOAD_NEWKEYS "\x00\x00\x00\x0c" /* packet length */ \ "\x0a" /* padding length */ \ "\x15" /* message code */ \ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" /* padding string */ #define SIZE_NEWKEYS (12+4) #define DEADSTORE(x) x=x /*************************************************************************** ***************************************************************************/ static void ssh_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; size_t packet_length = pstate -> sub.ssh.packet_length; unsigned i; enum{ BANNER = 0, MSG_KEY_EXCHANGE_INIT = 1, MSG_NEW_KEYS = 2, MSG_UNKNOWN = 9, PADDING_LENGTH = 10, MESSAGE_CODE = 11, CHECK_LENGTH = 20, LENGTH_1 = 21, LENGTH_2 = 22, LENGTH_3 = 23, LENGTH_4 = 24, BEFORE_END = 29, END = 30, ERROR = 31, }; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; istate = state; pstate->sub.ssh.packet_length = packet_length; } /*************************************************************************** ***************************************************************************/ static void * ssh_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /*************************************************************************** ***************************************************************************/ static int ssh_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_ssh = { "ssh", 22, 0, 0, 0, ssh_selftest, ssh_init, ssh_parse, }; ================================================ FILE: src/proto-ssh.h ================================================ #ifndef PROTO_SSH_H #define PROTO_SSH_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_ssh; #endif ================================================ FILE: src/proto-ssl-test.c ================================================ #include const char ssl_test_case_1[] = "\x16\x03\x03\x00\x50\x02\x00\x00\x4c\x03\x03\x52\x78\x1e\x3f\x6d" "\x3a\xd1\xa3\xe3\xa8\x1c\xc6\x08\xc8\x0a\xe2\x4b\x53\x68\x79\xa6" "\xe8\xeb\x25\x4c\xfd\x8a\x3a\x87\x40\xee\xa1\x00\xc0\x07\x00\x00" "\x24\x00\x00\x00\x00\xff\x01\x00\x01\x00\x00\x0b\x00\x04\x03\x00" "\x01\x02\x00\x23\x00\x00\x00\x10\x00\x0b\x00\x09\x08\x73\x70\x64" "\x79\x2f\x33\x2e\x31\x16\x03\x03\x0d\xff\x0b\x00\x0d\xfb\x00\x0d" "\xf8\x00\x06\x66\x30\x82\x06\x62\x30\x82\x05\x4a\xa0\x03\x02\x01" "\x02\x02\x08\x59\x09\xaa\x30\xa2\xb3\xfc\xf6\x30\x0d\x06\x09\x2a" "\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x49\x31\x0b\x30\x09" "\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x13\x30\x11\x06\x03\x55" "\x04\x0a\x13\x0a\x47\x6f\x6f\x67\x6c\x65\x20\x49\x6e\x63\x31\x25" "\x30\x23\x06\x03\x55\x04\x03\x13\x1c\x47\x6f\x6f\x67\x6c\x65\x20" "\x49\x6e\x74\x65\x72\x6e\x65\x74\x20\x41\x75\x74\x68\x6f\x72\x69" "\x74\x79\x20\x47\x32\x30\x1e\x17\x0d\x31\x33\x31\x30\x30\x39\x31" "\x30\x33\x38\x35\x38\x5a\x17\x0d\x31\x34\x31\x30\x30\x39\x31\x30" "\x33\x38\x35\x38\x5a\x30\x66\x31\x0b\x30\x09\x06\x03\x55\x04\x06" "\x13\x02\x55\x53\x31\x13\x30\x11\x06\x03\x55\x04\x08\x0c\x0a\x43" "\x61\x6c\x69\x66\x6f\x72\x6e\x69\x61\x31\x16\x30\x14\x06\x03\x55" "\x04\x07\x0c\x0d\x4d\x6f\x75\x6e\x74\x61\x69\x6e\x20\x56\x69\x65" "\x77\x31\x13\x30\x11\x06\x03\x55\x04\x0a\x0c\x0a\x47\x6f\x6f\x67" "\x6c\x65\x20\x49\x6e\x63\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c" "\x0c\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x30\x59\x30" "\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce" "\x3d\x03\x01\x07\x03\x42\x00\x04\xde\x41\xd4\x06\xbe\x15\x57\x70" "\x57\xa0\xe8\x14\xac\x8c\xd0\x8c\xf7\xb9\x23\x33\xae\xb2\x11\xa4" "\xa8\x79\x52\x86\xba\x99\xdc\x85\x88\x03\x0c\xc1\xa0\xc3\x3c\xeb" "\xe1\xe3\xaa\x8d\xde\x6d\x7c\x3b\x4c\xcb\x51\x75\xa7\xbc\x51\xce" "\x44\xc2\x83\xee\x88\x64\x94\xea\xa3\x82\x03\xfa\x30\x82\x03\xf6" "\x30\x1d\x06\x03\x55\x1d\x25\x04\x16\x30\x14\x06\x08\x2b\x06\x01" "\x05\x05\x07\x03\x01\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x02\x30" "\x82\x02\xc3\x06\x03\x55\x1d\x11\x04\x82\x02\xba\x30\x82\x02\xb6" "\x82\x0c\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x82\x0d" "\x2a\x2e\x61\x6e\x64\x72\x6f\x69\x64\x2e\x63\x6f\x6d\x82\x16\x2a" "\x2e\x61\x70\x70\x65\x6e\x67\x69\x6e\x65\x2e\x67\x6f\x6f\x67\x6c" "\x65\x2e\x63\x6f\x6d\x82\x12\x2a\x2e\x63\x6c\x6f\x75\x64\x2e\x67" "\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x82\x16\x2a\x2e\x67\x6f\x6f" "\x67\x6c\x65\x2d\x61\x6e\x61\x6c\x79\x74\x69\x63\x73\x2e\x63\x6f" "\x6d\x82\x0b\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x61\x82\x0b" "\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6c\x82\x0e\x2a\x2e\x67" "\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x2e\x69\x6e\x82\x0e\x2a\x2e\x67" "\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x2e\x6a\x70\x82\x0e\x2a\x2e\x67" "\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x2e\x75\x6b\x82\x0f\x2a\x2e\x67" "\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2e\x61\x72\x82\x0f\x2a\x2e" "\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2e\x61\x75\x82\x0f\x2a" "\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2e\x62\x72\x82\x0f" "\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2e\x63\x6f\x82" "\x0f\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2e\x6d\x78" "\x82\x0f\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2e\x74" "\x72\x82\x0f\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2e" "\x76\x6e\x82\x0b\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x64\x65\x82" "\x0b\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x65\x73\x82\x0b\x2a\x2e" "\x67\x6f\x6f\x67\x6c\x65\x2e\x66\x72\x82\x0b\x2a\x2e\x67\x6f\x6f" "\x67\x6c\x65\x2e\x68\x75\x82\x0b\x2a\x2e\x67\x6f\x6f\x67\x6c\x65" "\x2e\x69\x74\x82\x0b\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x6e\x6c" "\x82\x0b\x2a\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x70\x6c\x82\x0b\x2a" "\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x70\x74\x82\x0f\x2a\x2e\x67\x6f" "\x6f\x67\x6c\x65\x61\x70\x69\x73\x2e\x63\x6e\x82\x14\x2a\x2e\x67" "\x6f\x6f\x67\x6c\x65\x63\x6f\x6d\x6d\x65\x72\x63\x65\x2e\x63\x6f" "\x6d\x82\x0d\x2a\x2e\x67\x73\x74\x61\x74\x69\x63\x2e\x63\x6f\x6d" "\x82\x0c\x2a\x2e\x75\x72\x63\x68\x69\x6e\x2e\x63\x6f\x6d\x82\x10" "\x2a\x2e\x75\x72\x6c\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d" "\x82\x16\x2a\x2e\x79\x6f\x75\x74\x75\x62\x65\x2d\x6e\x6f\x63\x6f" "\x6f\x6b\x69\x65\x2e\x63\x6f\x6d\x82\x0d\x2a\x2e\x79\x6f\x75\x74" "\x75\x62\x65\x2e\x63\x6f\x6d\x82\x16\x2a\x2e\x79\x6f\x75\x74\x75" "\x62\x65\x65\x64\x75\x63\x61\x74\x69\x6f\x6e\x2e\x63\x6f\x6d\x82" "\x0b\x2a\x2e\x79\x74\x69\x6d\x67\x2e\x63\x6f\x6d\x82\x0b\x61\x6e" "\x64\x72\x6f\x69\x64\x2e\x63\x6f\x6d\x82\x04\x67\x2e\x63\x6f\x82" "\x06\x67\x6f\x6f\x2e\x67\x6c\x82\x14\x67\x6f\x6f\x67\x6c\x65\x2d" "\x61\x6e\x61\x6c\x79\x74\x69\x63\x73\x2e\x63\x6f\x6d\x82\x0a\x67" "\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x82\x12\x67\x6f\x6f\x67\x6c" "\x65\x63\x6f\x6d\x6d\x65\x72\x63\x65\x2e\x63\x6f\x6d\x82\x0a\x75" "\x72\x63\x68\x69\x6e\x2e\x63\x6f\x6d\x82\x08\x79\x6f\x75\x74\x75" "\x2e\x62\x65\x82\x0b\x79\x6f\x75\x74\x75\x62\x65\x2e\x63\x6f\x6d" "\x82\x14\x79\x6f\x75\x74\x75\x62\x65\x65\x64\x75\x63\x61\x74\x69" "\x6f\x6e\x2e\x63\x6f\x6d\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03" "\x02\x07\x80\x30\x68\x06\x08\x2b\x06\x01\x05\x05\x07\x01\x01\x04" "\x5c\x30\x5a\x30\x2b\x06\x08\x2b\x06\x01\x05\x05\x07\x30\x02\x86" "\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x70\x6b\x69\x2e\x67\x6f\x6f\x67" "\x6c\x65\x2e\x63\x6f\x6d\x2f\x47\x49\x41\x47\x32\x2e\x63\x72\x74" "\x30\x2b\x06\x08\x2b\x06\x01\x05\x05\x07\x30\x01\x86\x1f\x68\x74" "\x74\x70\x3a\x2f\x2f\x63\x6c\x69\x65\x6e\x74\x73\x31\x2e\x67\x6f" "\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x6f\x63\x73\x70\x30\x1d\x06" "\x03\x55\x1d\x0e\x04\x16\x04\x14\x28\xfd\x66\x74\xc2\xd5\x2f\xcb" "\xeb\x62\x19\x57\xcc\xc4\xe5\xed\xf7\x8a\x50\x7a\x30\x0c\x06\x03" "\x55\x1d\x13\x01\x01\xff\x04\x02\x30\x00\x30\x1f\x06\x03\x55\x1d" "\x23\x04\x18\x30\x16\x80\x14\x4a\xdd\x06\x16\x1b\xbc\xf6\x68\xb5" "\x76\xf5\x81\xb6\xbb\x62\x1a\xba\x5a\x81\x2f\x30\x17\x06\x03\x55" "\x1d\x20\x04\x10\x30\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\xd6" "\x79\x02\x05\x01\x30\x30\x06\x03\x55\x1d\x1f\x04\x29\x30\x27\x30" "\x25\xa0\x23\xa0\x21\x86\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x70\x6b" "\x69\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x47\x49\x41" "\x47\x32\x2e\x63\x72\x6c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d" "\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x7f\x5d\x95\x04\xe5\xdc" "\xbb\x74\xae\x2e\x62\x52\xe8\x30\x58\xed\x57\x6e\xc3\x7b\xff\x6c" "\x73\xf2\xe1\xab\x80\xd0\xc4\x02\x28\xee\x41\xe5\x01\x46\xec\x64" "\x6d\xd3\x4f\x62\x95\xa2\xbb\xd3\x4e\x02\x8e\x7d\xd3\xfe\x37\xbb" "\xab\x9f\x03\x45\x58\x98\xd8\x4d\x11\x99\xab\x7c\x6d\x45\xa2\xa6" "\xa4\xf1\xca\x1c\x1f\xd5\xf4\x56\xdc\x99\x86\xf5\xbf\x33\xc6\xb3" "\x77\x9d\x0b\x4d\x33\x89\x57\x1d\x9c\x3a\x4d\xb0\x5f\xe2\x5d\x77" "\xab\x7b\xd5\x5b\x88\xe8\x32\x61\x92\x44\xfa\xe1\xc7\x94\x29\x28" "\xcd\xc8\xee\xc1\xd7\x89\xe6\xda\x49\x28\x98\xfd\x80\x73\x2b\xf1" "\xba\x54\x0c\x3d\xe0\x36\xc9\xb6\xe2\xe9\x2f\x24\x32\x96\x63\x1c" "\xc1\x19\xfb\xad\xc9\xab\x2f\x10\xb6\x18\x22\xdf\x7d\xef\x0f\x10" "\x9a\x3f\x63\x60\x1a\xb6\xa5\xe6\x8f\xe2\x3e\x0c\x5a\x70\x0f\x4a" "\xac\xe4\x6b\xd8\x68\xcd\x47\xe5\x41\x06\x5c\x1a\xb9\x28\x4d\x67" "\x91\x9b\x28\x67\xfb\x3a\x77\x34\x07\x0e\xd7\x84\x5b\xe3\xbb\x16" "\x6c\xd9\x61\x1c\x60\xfa\xd4\x2f\xab\x97\xcc\x46\x34\x5b\xc5\x35" "\x30\xca\xde\x01\xa2\x27\x61\x97\x5c\x8a\x4f\xbd\x85\xce\x1a\x25" "\xa2\x45\xdd\xe7\x4a\x15\x48\x1c\x8f\xd0\x00\x04\x08\x30\x82\x04" "\x04\x30\x82\x02\xec\xa0\x03\x02\x01\x02\x02\x03\x02\x3a\x69\x30" "\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x42" "\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x16\x30" "\x14\x06\x03\x55\x04\x0a\x13\x0d\x47\x65\x6f\x54\x72\x75\x73\x74" "\x20\x49\x6e\x63\x2e\x31\x1b\x30\x19\x06\x03\x55\x04\x03\x13\x12" "\x47\x65\x6f\x54\x72\x75\x73\x74\x20\x47\x6c\x6f\x62\x61\x6c\x20" "\x43\x41\x30\x1e\x17\x0d\x31\x33\x30\x34\x30\x35\x31\x35\x31\x35" "\x35\x35\x5a\x17\x0d\x31\x35\x30\x34\x30\x34\x31\x35\x31\x35\x35" "\x35\x5a\x30\x49\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55" "\x53\x31\x13\x30\x11\x06\x03\x55\x04\x0a\x13\x0a\x47\x6f\x6f\x67" "\x6c\x65\x20\x49\x6e\x63\x31\x25\x30\x23\x06\x03\x55\x04\x03\x13" "\x1c\x47\x6f\x6f\x67\x6c\x65\x20\x49\x6e\x74\x65\x72\x6e\x65\x74" "\x20\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x20\x47\x32\x30\x82\x01" "\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" "\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\x9c\x2a" "\x04\x77\x5c\xd8\x50\x91\x3a\x06\xa3\x82\xe0\xd8\x50\x48\xbc\x89" "\x3f\xf1\x19\x70\x1a\x88\x46\x7e\xe0\x8f\xc5\xf1\x89\xce\x21\xee" "\x5a\xfe\x61\x0d\xb7\x32\x44\x89\xa0\x74\x0b\x53\x4f\x55\xa4\xce" "\x82\x62\x95\xee\xeb\x59\x5f\xc6\xe1\x05\x80\x12\xc4\x5e\x94\x3f" "\xbc\x5b\x48\x38\xf4\x53\xf7\x24\xe6\xfb\x91\xe9\x15\xc4\xcf\xf4" "\x53\x0d\xf4\x4a\xfc\x9f\x54\xde\x7d\xbe\xa0\x6b\x6f\x87\xc0\xd0" "\x50\x1f\x28\x30\x03\x40\xda\x08\x73\x51\x6c\x7f\xff\x3a\x3c\xa7" "\x37\x06\x8e\xbd\x4b\x11\x04\xeb\x7d\x24\xde\xe6\xf9\xfc\x31\x71" "\xfb\x94\xd5\x60\xf3\x2e\x4a\xaf\x42\xd2\xcb\xea\xc4\x6a\x1a\xb2" "\xcc\x53\xdd\x15\x4b\x8b\x1f\xc8\x19\x61\x1f\xcd\x9d\xa8\x3e\x63" "\x2b\x84\x35\x69\x65\x84\xc8\x19\xc5\x46\x22\xf8\x53\x95\xbe\xe3" "\x80\x4a\x10\xc6\x2a\xec\xba\x97\x20\x11\xc7\x39\x99\x10\x04\xa0" "\xf0\x61\x7a\x95\x25\x8c\x4e\x52\x75\xe2\xb6\xed\x08\xca\x14\xfc" "\xce\x22\x6a\xb3\x4e\xcf\x46\x03\x97\x97\x03\x7e\xc0\xb1\xde\x7b" "\xaf\x45\x33\xcf\xba\x3e\x71\xb7\xde\xf4\x25\x25\xc2\x0d\x35\x89" "\x9d\x9d\xfb\x0e\x11\x79\x89\x1e\x37\xc5\xaf\x8e\x72\x69\x02\x03" "\x01\x00\x01\xa3\x81\xfb\x30\x81\xf8\x30\x1f\x06\x03\x55\x1d\x23" "\x04\x18\x30\x16\x80\x14\xc0\x7a\x98\x68\x8d\x89\xfb\xab\x05\x64" "\x0c\x11\x7d\xaa\x7d\x65\xb8\xca\xcc\x4e\x30\x1d\x06\x03\x55\x1d" "\x0e\x04\x16\x04\x14\x4a\xdd\x06\x16\x1b\xbc\xf6\x68\xb5\x76\xf5" "\x81\xb6\xbb\x62\x1a\xba\x5a\x81\x2f\x30\x12\x06\x03\x55\x1d\x13" "\x01\x01\xff\x04\x08\x30\x06\x01\x01\xff\x02\x01\x00\x30\x0e\x06" "\x03\x55\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x06\x30\x3a\x06" "\x03\x55\x1d\x1f\x04\x33\x30\x31\x30\x2f\xa0\x2d\xa0\x2b\x86\x29" "\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x2e\x67\x65\x6f\x74\x72" "\x75\x73\x74\x2e\x63\x6f\x6d\x2f\x63\x72\x6c\x73\x2f\x67\x74\x67" "\x6c\x6f\x62\x61\x6c\x2e\x63\x72\x6c\x30\x3d\x06\x08\x2b\x06\x01" "\x05\x05\x07\x01\x01\x04\x31\x30\x2f\x30\x2d\x06\x08\x2b\x06\x01" "\x05\x05\x07\x30\x01\x86\x21\x68\x74\x74\x70\x3a\x2f\x2f\x67\x74" "\x67\x6c\x6f\x62\x61\x6c\x2d\x6f\x63\x73\x70\x2e\x67\x65\x6f\x74" "\x72\x75\x73\x74\x2e\x63\x6f\x6d\x30\x17\x06\x03\x55\x1d\x20\x04" "\x10\x30\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\xd6\x79\x02\x05" "\x01\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00" "\x03\x82\x01\x01\x00\x36\xd7\x06\x80\x11\x27\xad\x2a\x14\x9b\x38" "\x77\xb3\x23\xa0\x75\x58\xbb\xb1\x7e\x83\x42\xba\x72\xda\x1e\xd8" "\x8e\x36\x06\x97\xe0\xf0\x95\x3b\x37\xfd\x1b\x42\x58\xfe\x22\xc8" "\x6b\xbd\x38\x5e\xd1\x3b\x25\x6e\x12\xeb\x5e\x67\x76\x46\x40\x90" "\xda\x14\xc8\x78\x0d\xed\x95\x66\xda\x8e\x86\x6f\x80\xa1\xba\x56" "\x32\x95\x86\xdc\xdc\x6a\xca\x04\x8c\x5b\x7f\xf6\xbf\xcc\x6f\x85" "\x03\x58\xc3\x68\x51\x13\xcd\xfd\xc8\xf7\x79\x3d\x99\x35\xf0\x56" "\xa3\xbd\xe0\x59\xed\x4f\x44\x09\xa3\x9e\x38\x7a\xf6\x46\xd1\x1d" "\x12\x9d\x4f\xbe\xd0\x40\xfc\x55\xfe\x06\x5e\x3c\xda\x1c\x56\xbd" "\x96\x51\x7b\x6f\x57\x2a\xdb\xa2\xaa\x96\xdc\x8c\x74\xc2\x95\xbe" "\xf0\x6e\x95\x13\xff\x17\xf0\x3c\xac\xb2\x10\x8d\xcc\x73\xfb\xe8" "\x8f\x02\xc6\xf0\xfb\x33\xb3\x95\x3b\xe3\xc2\xcb\x68\x58\x73\xdb" "\xa8\x24\x62\x3b\x06\x35\x9d\x0d\xa9\x33\xbd\x78\x03\x90\x2e\x4c" "\x78\x5d\x50\x3a\x81\xd4\xee\xa0\xc8\x70\x38\xdc\xb2\xf9\x67\xfa" "\x87\x40\x5d\x61\xc0\x51\x8f\x6b\x83\x6b\xcd\x05\x3a\xca\xe1\xa7" "\x05\x78\xfc\xca\xda\x94\xd0\x2c\x08\x3d\x7e\x16\x79\xc8\xa0\x50" "\x20\x24\x54\x33\x71\x00\x03\x81\x30\x82\x03\x7d\x30\x82\x02\xe6" "\xa0\x03\x02\x01\x02\x02\x03\x12\xbb\xe6\x30\x0d\x06\x09\x2a\x86" "\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x4e\x31\x0b\x30\x09\x06" "\x03\x55\x04\x06\x13\x02\x55\x53\x31\x10\x30\x0e\x06\x03\x55\x04" "\x0a\x13\x07\x45\x71\x75\x69\x66\x61\x78\x31\x2d\x30\x2b\x06\x03" "\x55\x04\x0b\x13\x24\x45\x71\x75\x69\x66\x61\x78\x20\x53\x65\x63" "\x75\x72\x65\x20\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x65\x20" "\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x30\x1e\x17\x0d\x30\x32\x30" "\x35\x32\x31\x30\x34\x30\x30\x30\x30\x5a\x17\x0d\x31\x38\x30\x38" "\x32\x31\x30\x34\x30\x30\x30\x30\x5a\x30\x42\x31\x0b\x30\x09\x06" "\x03\x55\x04\x06\x13\x02\x55\x53\x31\x16\x30\x14\x06\x03\x55\x04" "\x0a\x13\x0d\x47\x65\x6f\x54\x72\x75\x73\x74\x20\x49\x6e\x63\x2e" "\x31\x1b\x30\x19\x06\x03\x55\x04\x03\x13\x12\x47\x65\x6f\x54\x72" "\x75\x73\x74\x20\x47\x6c\x6f\x62\x61\x6c\x20\x43\x41\x30\x82\x01" "\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" "\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xda\xcc" "\x18\x63\x30\xfd\xf4\x17\x23\x1a\x56\x7e\x5b\xdf\x3c\x6c\x38\xe4" "\x71\xb7\x78\x91\xd4\xbc\xa1\xd8\x4c\xf8\xa8\x43\xb6\x03\xe9\x4d" "\x21\x07\x08\x88\xda\x58\x2f\x66\x39\x29\xbd\x05\x78\x8b\x9d\x38" "\xe8\x05\xb7\x6a\x7e\x71\xa4\xe6\xc4\x60\xa6\xb0\xef\x80\xe4\x89" "\x28\x0f\x9e\x25\xd6\xed\x83\xf3\xad\xa6\x91\xc7\x98\xc9\x42\x18" "\x35\x14\x9d\xad\x98\x46\x92\x2e\x4f\xca\xf1\x87\x43\xc1\x16\x95" "\x57\x2d\x50\xef\x89\x2d\x80\x7a\x57\xad\xf2\xee\x5f\x6b\xd2\x00" "\x8d\xb9\x14\xf8\x14\x15\x35\xd9\xc0\x46\xa3\x7b\x72\xc8\x91\xbf" "\xc9\x55\x2b\xcd\xd0\x97\x3e\x9c\x26\x64\xcc\xdf\xce\x83\x19\x71" "\xca\x4e\xe6\xd4\xd5\x7b\xa9\x19\xcd\x55\xde\xc8\xec\xd2\x5e\x38" "\x53\xe5\x5c\x4f\x8c\x2d\xfe\x50\x23\x36\xfc\x66\xe6\xcb\x8e\xa4" "\x39\x19\x00\xb7\x95\x02\x39\x91\x0b\x0e\xfe\x38\x2e\xd1\x1d\x05" "\x9a\xf6\x4d\x3e\x6f\x0f\x07\x1d\xaf\x2c\x1e\x8f\x60\x39\xe2\xfa" "\x36\x53\x13\x39\xd4\x5e\x26\x2b\xdb\x3d\xa8\x14\xbd\x32\xeb\x18" "\x03\x28\x52\x04\x71\xe5\xab\x33\x3d\xe1\x38\xbb\x07\x36\x84\x62" "\x9c\x79\xea\x16\x30\xf4\x5f\xc0\x2b\xe8\x71\x6b\xe4\xf9\x02\x03" "\x01\x00\x01\xa3\x81\xf0\x30\x81\xed\x30\x1f\x06\x03\x55\x1d\x23" "\x04\x18\x30\x16\x80\x14\x48\xe6\x68\xf9\x2b\xd2\xb2\x95\xd7\x47" "\xd8\x23\x20\x10\x4f\x33\x98\x90\x9f\xd4\x30\x1d\x06\x03\x55\x1d" "\x0e\x04\x16\x04\x14\xc0\x7a\x98\x68\x8d\x89\xfb\xab\x05\x64\x0c" "\x11\x7d\xaa\x7d\x65\xb8\xca\xcc\x4e\x30\x0f\x06\x03\x55\x1d\x13" "\x01\x01\xff\x04\x05\x30\x03\x01\x01\xff\x30\x0e\x06\x03\x55\x1d" "\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x06\x30\x3a\x06\x03\x55\x1d" "\x1f\x04\x33\x30\x31\x30\x2f\xa0\x2d\xa0\x2b\x86\x29\x68\x74\x74" "\x70\x3a\x2f\x2f\x63\x72\x6c\x2e\x67\x65\x6f\x74\x72\x75\x73\x74" "\x2e\x63\x6f\x6d\x2f\x63\x72\x6c\x73\x2f\x73\x65\x63\x75\x72\x65" "\x63\x61\x2e\x63\x72\x6c\x30\x4e\x06\x03\x55\x1d\x20\x04\x47\x30" "\x45\x30\x43\x06\x04\x55\x1d\x20\x00\x30\x3b\x30\x39\x06\x08\x2b" "\x06\x01\x05\x05\x07\x02\x01\x16\x2d\x68\x74\x74\x70\x73\x3a\x2f" "\x2f\x77\x77\x77\x2e\x67\x65\x6f\x74\x72\x75\x73\x74\x2e\x63\x6f" "\x6d\x2f\x72\x65\x73\x6f\x75\x72\x63\x65\x73\x2f\x72\x65\x70\x6f" "\x73\x69\x74\x6f\x72\x79\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d" "\x01\x01\x05\x05\x00\x03\x81\x81\x00\x76\xe1\x12\x6e\x4e\x4b\x16" "\x12\x86\x30\x06\xb2\x81\x08\xcf\xf0\x08\xc7\xc7\x71\x7e\x66\xee" "\xc2\xed\xd4\x3b\x1f\xff\xf0\xf0\xc8\x4e\xd6\x43\x38\xb0\xb9\x30" "\x7d\x18\xd0\x55\x83\xa2\x6a\xcb\x36\x11\x9c\xe8\x48\x66\xa3\x6d" "\x7f\xb8\x13\xd4\x47\xfe\x8b\x5a\x5c\x73\xfc\xae\xd9\x1b\x32\x19" "\x38\xab\x97\x34\x14\xaa\x96\xd2\xeb\xa3\x1c\x14\x08\x49\xb6\xbb" "\xe5\x91\xef\x83\x36\xeb\x1d\x56\x6f\xca\xda\xbc\x73\x63\x90\xe4" "\x7f\x7b\x3e\x22\xcb\x3d\x07\xed\x5f\x38\x74\x9c\xe3\x03\x50\x4e" "\xa1\xaf\x98\xee\x61\xf2\x84\x3f\x12\x16\x03\x03\x00\x95\x0c\x00" "\x00\x91\x03\x00\x17\x41\x04\x4c\x17\x75\x15\xc7\xeb\x2a\x7f\x4b" "\xf4\xe8\xd9\xee\x6c\x06\x58\x2c\xb3\x37\xe6\xad\xe4\x46\x24\xfe" "\xfe\x91\xdd\x2e\x87\x2a\xde\xad\x4d\xd3\x85\x19\xd1\xa8\x21\xf5" "\xb8\x42\xa7\xd8\x98\x28\xbb\x94\xeb\x60\xe0\xb9\x97\x6c\x99\xbb" "\x9f\xd7\x31\xac\x5a\x1a\xc0\x04\x03\x00\x48\x30\x46\x02\x21\x00" "\xa7\x25\x1d\x54\x2e\x5a\xf9\x47\x3f\x4e\xb2\x31\xd2\x97\x4b\xe9" "\xf5\xe8\xc1\xc7\x51\xf8\x39\x88\x4b\x16\x59\xbb\xe5\x06\x54\xe4" "\x02\x21\x00\xb4\xa3\x31\x5b\x4e\xc5\x6b\xf9\x35\x52\xda\x26\x22" "\x8c\x2c\xeb\xf2\x8e\x41\xec\xc9\xeb\x65\x03\xba\x08\x91\x64\x0f" "\x33\x9e\x70\x16\x03\x03\x00\x04\x0e\x00\x00\x00\x16\x03\x03\x00" "\xbe\x04\x00\x00\xba\x00\x01\x89\xc0\x00\xb4\xdd\x76\xa2\xde\xd9" "\x62\xc3\xc9\x6a\x16\x37\x12\xd2\x2a\x86\xed\xc8\xb3\x47\xbb\xf8" "\xf6\x95\x1a\x19\x99\x07\xd3\xfe\x60\x0c\x67\xc5\x44\x8d\xe7\x92" "\xa5\xa5\x8e\x29\x58\x27\x2e\x25\x69\x43\x60\xe7\x9f\x6b\xdc\xd7" "\x43\x4a\xbc\xf3\x09\xe5\x5c\x3a\xfc\xe1\xd9\xd8\xf4\x6a\x93\x1c" "\x47\x90\xfc\xfc\x35\x1b\xfe\xef\xd7\x4f\xb5\x03\x6d\xab\xde\x93" "\x12\x9b\x34\xb6\x8b\xb8\xbc\x45\x51\x80\x38\xfb\x66\x2c\xca\xbf" "\xd5\x65\xe9\x5d\x0d\xa8\x8c\x0b\xe6\xb2\x43\x2e\xc7\x86\x81\xf4" "\x9d\x2e\xef\x17\x0a\x5b\x11\x9b\xfa\x32\xf5\xfb\x0b\x1e\x7f\x1a" "\xf0\xcd\xe6\xd4\x5e\x97\xd8\x11\x6b\x05\x58\x52\xd2\xb4\x17\x3a" "\x36\x85\xa0\xd9\x8b\x7f\x16\x47\x73\x6a\x5b\xbd\x15\x24\xaf\xe6" "\x02\xed\x7d\x98\xea\x29\xde\x4b\x12\x55\xa4\x8e\xd6\x09\xe2\x14" "\x03\x03\x00\x01\x01\x16\x03\x03\x00\x24\xee\x01\xaa\x34\x2e\xdf" "\x36\x7a\x6a\xb5\x8f\x73\xf1\x58\xbe\xd4\xc1\x7f\x7f\xc7\x16\x4f" "\xb7\x29\x5b\xad\x80\xe1\x50\x34\xd0\x75\x28\x49\x65\xb5\x17\x03" "\x03\x00\x30\xa8\x43\x75\x45\xc6\x7f\x3b\xdd\xc4\x86\xa2\x49\x65" "\xed\x10\x5f\x7d\xac\xf0\x93\x78\x09\xc1\xdb\x8f\xad\x99\x5b\xfd" "\xa5\x40\x14\xfd\x36\x1f\x46\x12\x77\xa7\x4b\x75\x22\xea\x05\xb5" "\xe1\xb6\xa9\x17\x03\x03\x01\x19\x9a\xc2\x10\x16\xcb\x08\xca\x7b" "\x5e\xde\x7c\xb5\x14\x85\x9f\xf5\xdc\xb7\x5c\xe2\xe2\x4a\xff\x7e" "\x28\x71\xd3\xc2\x0d\xb0\x65\xca\x43\x7d\xcf\xda\xd5\x05\xfd\xb7" "\xa5\x13\x5c\x82\x40\x21\x9b\xa6\x22\x56\xb9\x1d\xe5\x24\x73\x12" "\x2d\x99\x8b\x68\xa5\x9c\xee\x63\x24\x07\xae\xb7\x32\x95\x6a\xe6" "\x9b\x59\x35\x26\x74\xc9\xde\x95\x7f\x6b\x00\xe0\x62\xfe\x66\x49" "\xaa\xc4\x8e\xbb\xc5\xef\x6b\x21\x09\x90\xb7\x7f\x4e\x06\x61\x05" "\x7c\x7c\x6d\x8a\x2c\x49\xb3\xcf\x08\xcb\x59\xca\x2e\x71\x7d\x83" "\x58\x30\x42\x23\x0b\xf7\xec\x4b\x9c\x9d\xa9\x11\x11\x34\x1d\x2e" "\xb6\xb3\xee\x4e\x35\x2c\x87\xcf\x37\xab\xe2\xd4\x33\x24\xfc\x94" "\x65\x62\x55\xc0\x6e\x5a\xe9\xd1\x96\xf1\x6f\x64\x2e\x7f\x65\x92" "\x86\x6c\xe7\x93\x11\x2c\xe8\xb0\x75\x03\x18\x9d\xe6\xb2\x42\xab" "\x45\x54\x9a\xdb\xee\xd9\x84\x7e\x1a\xe6\x8e\x00\xc1\x02\x16\x83" "\xc7\x19\x17\x03\x30\x2e\x20\x6d\xbe\x23\xb1\x95\x3c\x24\xbe\x55" "\x01\x9e\xbb\xc0\x2b\xaf\xfc\x71\xaa\x35\x0e\xd0\xbd\x19\x0c\xcd" "\xfe\x97\xf2\x10\xe7\x95\x16\x7c\x43\x92\xeb\x92\xde\x4c\x01\xed" "\x1d\x4f\x76\xc0\xb7\xb6\xe9\x95\x40\xb6\x26\x55\x5a\x74\x9e\xc5" "\x8a\x1d\xf4\x56\xcc\x58\x27\x62\xbb\xe1\x43\xce\xb0\x4a\x8a\xb6" "\xd3\x17\x03\x03\x05\x4b\xfb\x08\xd6\x6a\xbf\xff\x99\x61\x4d\xb0" "\x24\x0d\x40\xd7\xef\x0c\x38\x00\x35\x12\xf4\x3d\x4a\x0b\xf9\x20" "\x4d\x1f\x72\x43\x34\x17\x74\xd3\x30\xcc\x2c\x95\xa8\xde\x41\x20" "\xb2\xe0\x38\xce\xd0\x5f\xa1\x92\xcd\x99\x94\x04\xf6\xf5\x06\x97" "\x9a\xb5\x8c\xa1\xa6\xef\xdd\x2f\x7d\x30\xba\xe0\xc0\x23\x07\x54" "\x6f\x91\x96\xd1\x7c\xfe\x7e\xb3\xfd\xf7\x9a\xe7\xfa\x67\x1e\x5c" "\x16\x5a\xde\x43\x49\x25\x39\x52\x13\x66\x29\xbd\xb6\xda\x59\x5a" "\x13\xfb\x76\xac\xf3\x6c\x1b\x00\xf4\x1e\x60\x45\xf4\x23\xca\x9d" "\x02\x8b\x88\x64\xf2\xf2\x86\x1a\xa2\x9c\xe9\x6e\xaa\x39\xe5\x4f" "\xc5\xb6\x68\xfd\x75\x10\xec\xa5\xd6\x66\x24\xc3\x85\xa1\x17\xfd" "\x46\x69\x69\xaf\x5f\x7c\xa4\x5f\x0a\x8a\xb1\xdb\xeb\x4c\x67\x9b" "\xc6\x33\x36\x4b\xf1\xdc\x86\x95\x63\x55\x13\x37\x86\xf6\xba\xf2" "\xf2\xc2\x89\x98\xe9\x82\xf6\x89\x2c\xbc\x91\x12\xfb\x3f\x42\x6f" "\xfa\xa4\xd6\x9b\x66\x67\x6a\xb6\x99\xb1\xac\xe0\x60\xc8\x37\x71" "\xd6\x4a\x08\x71\xf9\x26\x4e\xa9\x06\x76\x89\xb3\xa3\x91\xa6\x5a" "\xdf\xfe\xc0\xb9\xc5\xf2\xfa\x23\x46\xe5\x89\xd4\x19\x76\xf7\xef" "\xc5\x2b\x5a\x07\xe4\x9a\x81\xf4\x39\x80\xbd\xa7\xc5\x3d\x11\xcd" "\xea\xb9\x5f\xe5\x69\xe5\x35\x4e\x24\x6b\x97\x8a\xba\x7b\xaf\x10" "\xd0\x7a\xc7\xdf\x44\x23\x0e\x1a\x5f\x6a\xa4\xed\x14\x79\xb9\xd9" "\x93\x38\xd7\x50\xd6\x6a\x7b\x5b\x99\xa5\x49\xef\xd1\x64\xef\x35" "\xdc\xef\x0b\xd7\x6c\xe4\xe6\x7b\x55\x2c\x46\xf8\x03\xd2\xfa\x67" "\x2a\x64\xc7\x7f\xd4\xaf\x32\x43\xc0\x4d\xf5\x55\xc9\xf6\x1b\xc8" "\xe9\x9f\x7f\xed\x7a\x8f\xa5\x59\x9d\x60\x65\xa4\x7b\x6b\xc6\x32" "\xa4\x30\xea\x2e\x29\x9c\x9b\x83\x05\x80\x22\x3a\xdd\x04\x53\x33" "\xef\xeb\xd7\x6d\x17\x34\x4b\x0f\x5a\x6d\xb7\xea\x7c\x15\x69\xdf" "\xec\x65\xc7\xa9\x63\xc8\x25\x19\xc3\x13\xca\xdf\x23\x20\x27\xb7" "\x29\xf3\xa3\xe5\x2d\x6f\xea\x91\x12\xfa\xd5\x71\x48\xc7\x2a\x5a" "\xc8\x75\x23\xcc\x8f\xa0\x4d\x63\xb2\x60\xe0\x52\xd1\x1e\x4f\x44" "\x69\xf7\xf1\x63\x59\x83\x79\x8e\x53\x27\x61\xa7\x92\xe1\xed\x4f" "\x75\x8e\xd9\x3c\x88\x74\xc7\x7b\xb0\x09\x0e\x3b\x0a\xad\xc1\xbc" "\x5d\x25\xa6\xce\xea\x98\xaf\x27\xb2\xc7\xb1\x2e\x26\xef\x35\x81" "\x34\x49\xfd\x74\x45\x16\x5f\x20\xd6\x6d\x54\xf8\x97\x4a\x7e\xfe" "\x8b\x24\x8c\xa1\x46\xa9\x7d\x0b\x32\xe9\x0d\xa9\x69\x56\xad\x1f" "\x95\xe0\x7a\xed\x6f\x95\x5a\xd7\xf4\x25\xbf\x79\xa7\xa3\x69\x43" "\xd2\x41\x41\x65\xeb\x43\x13\xe1\x42\xbd\xde\x05\x43\x01\x1d\x20" "\x7d\x54\xf1\x92\xef\x94\xc8\x45\xba\xd0\x83\x25\x26\x47\x57\x32" "\x63\x61\x38\xa1\xb9\x9d\xa2\x88\xb7\x8e\x87\xbb\x13\xfc\xd8\x3f" "\xf9\x8d\x68\xcc\xa1\xa3\xa7\x35\xfd\x5c\xa1\x8b\x6a\xce\x36\x2d" "\xca\x0f\x05\x52\x86\x86\x87\xbe\x90\x9c\x06\xcb\x08\xbe\xcc\x42" "\x4e\xda\xf6\x8a\x4e\x02\x0f\x56\xce\xad\x19\xfd\x8d\xb7\xf1\xcc" "\xb9\x6c\x56\x18\x48\x44\xbe\x24\x14\x00\x08\xbe\x63\x32\x15\x71" "\x60\xd3\x15\xa8\xeb\xcd\x69\xbe\x6f\xe9\x26\x43\x89\x10\x73\x67" "\xb4\x1d\xeb\x24\x9b\x27\x73\x20\xfb\xab\xd1\xe1\x28\x06\xbd\x36" "\x3a\x8f\x87\x05\x34\x96\x4e\x86\xaa\xcb\xbf\xd4\x5d\x92\xf8\xb9" "\x08\x7d\xcd\xee\xc2\x17\xac\xab\xdd\xf8\xc0\xdc\xd4\x24\x6a\xa1" "\x7b\xaa\x60\x31\x19\x5f\xf5\xd0\xc4\x9a\xa7\x8a\x12\xd2\xd9\x3d" "\xae\x44\x7a\x1b\x07\x4b\xf3\xa5\x69\x4e\xa8\x51\x51\xbe\xfb\xfc" "\xbc\xdc\x27\x65\x9e\x2c\xa6\xb1\xca\x15\x25\x7f\xc6\x5f\x84\x64" "\x79\x01\xde\x7a\x87\x00\x6b\x83\x0b\x8f\x1c\xe9\x6c\x1a\xa2\xb7" "\xad\x33\x9e\xe8\x48\xbb\x06\x60\xb3\x26\x81\x7e\x2a\x58\xff\xe5" "\x35\xc8\x28\xd0\x39\x6a\x24\x75\xba\xe9\xe1\x05\xb4\x37\x36\xd3" "\x61\x07\x0b\x85\xe9\x8d\xd4\xdc\x5b\x96\x64\x5f\x30\xe7\x94\x95" "\x72\x73\x3f\x40\x2c\x78\x8b\xab\x86\x0a\xf9\x6a\x2c\x27\xdc\xaa" "\xfb\x95\x02\x7e\x3b\x28\x7d\x99\xbc\x07\xa0\xb5\x67\xa4\x04\xaf" "\x3d\x33\x34\x09\x67\xa3\x1a\x28\x0b\x21\xf0\x45\xb3\x93\x3a\x77" "\x90\x6f\xed\x1f\xb0\xf7\x27\x5a\x0b\xd3\xc8\x36\x6a\xda\x6c\x38" "\xe8\xd7\xea\x6e\x0d\xad\x64\x5b\xf5\x53\x2d\x9b\x36\x95\x5c\x6b" "\xaf\xa5\x12\x42\x1b\x24\x05\x2c\x64\x45\x07\xff\xb9\xc3\xf8\x62" "\x57\x67\x53\x7d\x34\xb9\x47\x5b\xaa\x46\x8d\x4b\x25\x99\xfe\x5c" "\xba\x7e\xfe\xc8\x0c\x47\x7a\x47\xd2\xd7\x3b\xbe\x8c\xaa\xb4\x1a" "\x56\xe4\x08\xb1\xac\x16\xbe\x52\xd5\xb2\xd4\xc5\xa1\xe7\x18\x12" "\x00\xa9\x7a\x7b\x86\x6c\x1e\x32\x26\xc4\xf2\x5b\xb2\x01\x65\x5f" "\x33\x5e\x7c\x86\xae\x7b\xf1\x52\x2a\x07\x56\x08\x9b\xda\x77\x9d" "\x59\x4b\xd6\xb9\x8f\xec\x43\xa7\xc7\x34\x42\x2f\xa5\xab\x4f\xbf" "\x1c\x58\x32\x04\x53\x26\x00\x38\xd1\xfe\x7b\xac\x8e\xe3\x96\x79" "\x68\x7c\x0a\x06\xb4\x74\xd4\xed\xd6\xc2\xe7\x8d\xfd\x62\xd2\x9e" "\x95\x64\xa7\x13\x74\x77\xff\x27\x55\x88\x91\xac\xc5\xc7\xa0\x70" "\x95\x46\x12\x91\x1b\xce\x80\x6a\xdc\xff\x62\xb3\x04\x0f\xd0\x63" "\xbc\xe0\xec\xe9\x0f\xc4\xff\x5d\xc9\xad\x1e\x71\xa8\x16\x64\xd3" "\x53\x54\xd7\x49\xc2\xc9\x06\xfc\xa3\x5a\x30\xc0\xdd\xee\xb2\x10" "\x29\x3b\x19\x6a\xf0\xab\xff\x7c\x40\xc3\x26\x19\x17\x26\x5c\xb5" "\x16\xd9\xd7\xa0\x9b\xea\xd5\x16\xaa\xe1\x3f\x17\x0c\xb2\xee\xf1" "\xd9\x34\x93\xe8\x60\x7e\x59\x1c\xaf\x29\x7f\xf3\xbd\x7a\x62\xe3" "\x55\x73\xa4\x29\x9b\x62\x77\x6e\xdc\x6c\xce\xe6\xad\x1f\x7b\x6d" "\xa9\x68\x28\x26\x81\x61\x10\x42\x6e\x5d\x1e\x24\xdf\xf6\x75\x75" "\x31\x6f\x96\xe2\x41\xc1\x84\xae\x3e\x0b\xc5\xea\x71\x45\xba\x59" "\xe5\xf3\xfd\xa4\x53\x98\x91\x38\x5b\xe5\x3f\x2c\x76\xb5\x12\x77" "\x45\x7e\x9d\xa3\xb2\x7d\x5b\xad\x23\xdc\x7e\x80\x02\x3d\x19\x46" "\x7a\xd4\x3f\xb5\x7a\x50\xfb\x24\x83\xab\xa4\x5a\xe8\x0d\x9f\xd1" "\xf4\x66\xdf\x85\x42\xef\xcc\x13\x59\xed\x5a\xa2\x3a\x51\xdc\x0a" "\x88\x62\x9a\x12\xfe\x2f\x71\x24\x6c\xd4\x11\x18\x82\x01\x3f\x06" "\xc0\x1b\x0a\xae\x2f\xf1\xc2\x7a\xad\x99\x2d\xce\xf9\x4d\x7b\x1c" "\xfb\xce\x7c\x2d\xa7\x9f\x86\xdd\x6b\xcc\xd1\x6e\xc4\xfd\x2c\xfc" "\x77\xd6\xf5\xfa\xf3\x3c\x06\x9a\x40\x05\xd3\x54\x80\xb0\xe5\x91" "\x17\xc5\xa1\x07\xc0\x0d\x0d\x31\x57\x44\x50\xfb\x46\x1f\xf9\x87" "\x8f\x17\x03\x03\x05\x4b\x6e\x79\x18\xa9\x24\xf5\x4b\x1e\x7f\xba" "\x08\x11\xca\x4d\x8a\x87\x7d\x44\xec\x5d\xa2\xff\x79\xbc\xc5\x83" "\x1a\x71\xde\x28\x53\x3f\x3f\x53\xa4\x4e\x22\x1f\xb2\x86\xcd\x9c" "\xe7\xff\xbc\x33\x45\x22\x52\xac\x81\xe8\x63\x0f\x7c\x15\xed\x1d" "\xdb\xf3\x95\xed\x42\xdb\x2e\x3f\x24\xee\x7c\xd6\xc3\xa3\xa4\xe6" "\xf4\xdb\x37\x2a\xbe\x1e\x5e\x2f\xe4\xe5\x67\xde\x19\x95\x1a\x6c" "\x20\xe6\x0a\xc2\x26\x5c\x90\xd2\x3b\x3f\x87\xd1\x78\x35\x16\x40" "\x5a\x21\xe7\x42\x3f\x4b\x79\x21\xe0\x03\x3d\x61\x75\xfe\x94\xa5" "\x4d\x85\xff\x8d\xfc\xc7\x92\x88\x5c\x5f\xe2\xb7\x9c\x5e\x9b\xa8" "\xf9\xee\xa7\xa5\x57\x88\x7e\xf9\x9d\xdc\xa8\x10\xc3\x17\x26\x7d" "\x56\xd2\x76\x3b\x7a\xe5\xad\x4e\x2a\x83\xf5\x45\x50\xea\xb7\xad" "\x28\x10\x80\x64\x77\x6e\xc7\x69\xdf\x04\x53\xc1\x0f\x5e\x7a\xd3" "\x1d\x7a\x40\x44\x3f\x04\x14\x16\x38\x1a\x03\x2d\x42\x10\xf6\x67" "\x3e\x81\x46\x6b\x57\xaa\x41\x42\x12\x73\x20\x81\x57\x40\x9c\xc4" "\x03\xbd\x0b\x51\xfd\x87\x6e\xa4\x78\x14\x67\x11\xed\x79\x92\x39" "\xa0\xee\xd1\x18\xf6\x49\x66\xc8\x1b\x11\xfd\x6d\x84\x78\xd6\x28" "\xc3\xdf\x35\x64\x0a\x75\x1f\x30\x1e\x9a\x58\xb5\x6e\x29\x75\xed" "\xfe\x86\x08\xd2\x29\x4d\xf2\x94\xa0\x9a\xa3\x30\x9f\xb2\x35\x5b" "\x6e\x13\x0c\x40\xdc\x00\x89\x78\xea\xad\x6b\x59\x6e\xee\x8d\x77" "\x43\xd4\xaa\x69\x9f\x23\xfd\x92\xe9\xbb\x3f\x1e\xbd\xdc\x94\xdc" "\x52\x35\xba\xae\x8c\x03\xf3\x4f\x4e\x1a\xb4\xb2\x70\x58\xf8\xd8" "\x01\x6a\x0a\xc6\xc5\xc7\x86\x6e\x9a\x3b\xd2\x4a\xdc\x68\xc2\x7e" "\xac\x23\x79\x4c\x03\xf9\x2a\x04\xbe\x57\x7b\x2a\xbf\xa4\x1c\x2e" "\x9f\x0f\x87\x78\x0c\x06\x7e\x35\x43\x78\x94\xe6\x79\x15\xdc\x94" "\x38\x80\x75\xed\x25\xe0\x9c\x0e\xec\xa5\x07\x73\x19\xa5\x08\x33" "\x34\xbb\x88\xc8\x13\xad\x09\xa8\x04\x7f\x0e\xf9\xf2\x01\x76\xf5" "\x3d\xd8\x25\xa3\x8a\x6b\xce\xa6\xb2\x91\x53\xb6\x11\x1d\xd9\xd1" "\x4d\x63\x20\xd8\x81\x0a\x26\x50\xfc\xd4\x08\x8c\x97\x8e\x6a\xf4" "\x8f\xd2\x81\xea\xcc\xfd\x14\x07\x76\xcb\xcb\xdc\x30\x13\x6d\xdd" "\x46\x56\x90\x26\x1f\xa5\x72\x89\x0f\xfa\x63\x2e\xd5\xab\x7d\x94" "\xe7\x39\x1c\x92\xd5\xea\xcb\x5e\x33\x86\x7d\xf6\x5d\x1f\xb9\x01" "\x27\xb2\xb3\xff\x70\xd0\x49\x07\x3c\x3d\xcf\xf9\xb2\x85\x2c\x8b" "\x13\xa4\x38\xef\xfb\xcb\x1a\x2a\x82\x3a\x05\x4a\x58\x3c\x70\x9e" "\x09\x74\xbf\xd2\x53\x69\xec\x87\x18\xf5\xd1\xcc\x0c\x6c\xe7\x2b" "\x10\x04\x55\x85\xe1\x68\x4e\x65\x49\x88\x11\x1b\x00\xea\xfa\xb5" "\x9f\xca\x6b\x95\x6e\x20\x59\xea\x88\x9a\xd3\xbc\x6e\xdd\x92\xb9" "\x15\x58\xf1\x94\x52\x23\xb5\x7d\xd5\x70\x83\x26\xb5\x16\x60\x61" "\x3e\xe7\xd7\x40\xbc\x81\x15\x65\xc3\xe8\xdb\xd0\x1e\x4f\xd7\xe1" "\xa6\x35\x53\xf0\x34\xbc\xa6\xa7\xf5\x9c\x0b\x5d\x41\x49\xdd\x74" "\xf6\x35\xb9\x20\x76\x0a\x91\x98\xf6\x23\xc6\x37\x22\xb5\x47\xc9" "\x78\x75\x86\x6d\x7b\xc0\xbf\x66\x5a\x5e\x65\x58\x79\xf2\xf4\x1e" "\x79\x46\x83\xec\xfc\xbd\xae\x02\x27\xe6\xca\x66\xec\x0c\x46\x76" "\xb9\x8b\xc1\xb5\xfd\x0d\x92\x8d\x1e\xdc\x22\x6a\xa5\x4b\x82\x54" "\xca\xcc\xfb\xe6\x71\xba\x6e\xce\x77\xc7\x3b\xab\xe0\x3b\x6f\x39" "\xf0\xef\xd7\x90\x51\xb4\xa7\xcd\x36\xb1\x54\x9f\x39\x19\x3d\x87" "\x0f\xc0\xf0\x34\x49\xc3\xf6\x04\x0e\x9e\x05\xb7\xb7\x69\xf5\x44" "\x97\xab\x6b\x38\x6d\x57\x82\x60\x5c\x70\xa5\x5e\xc7\x4e\xba\x3f" "\xdf\x0b\xd5\x60\x74\x76\x3f\x44\xf9\x23\x08\x68\x02\x04\x1a\x42" "\x7a\xe0\x68\xc8\xb3\x0c\x71\x13\x29\xf1\xf6\x92\x35\xe8\xd8\xcd" "\xb3\x0b\xee\xaa\xf7\x05\xe7\xb2\xd1\x27\x5c\x08\x10\x9d\x51\x23" "\x83\x7a\xe6\xda\xf5\xac\x25\x03\xef\x2e\x8c\x6d\x34\x04\x33\xf2" "\x28\x6f\x46\x2e\xed\x23\x12\x8f\x50\xe1\x14\xe6\x1a\x49\xc5\x46" "\x50\x09\xab\xb0\x6e\x43\x48\xaa\x9e\xb3\x91\x06\x16\xc5\xca\x52" "\x75\x4a\x7a\x79\x53\x91\x3f\x6e\x01\xfd\xab\x0e\xd6\x84\x48\x94" "\xa5\x95\x11\x94\xdd\x59\x36\x75\x68\x07\xbd\x4e\x2a\xa5\x88\x7a" "\xbc\x78\x05\xec\x08\x08\x71\xe7\x93\x9e\x2e\x45\xbd\xf7\x5f\x86" "\x83\xb2\x6f\xfd\x65\x1a\x6c\x6e\xab\x15\x25\xa8\xf7\x9d\x6e\x7b" "\x75\xde\x2f\xcb\xe9\x15\x4b\x3a\x61\x97\x36\xf3\xbb\xa2\x2d\x96" "\x09\x99\x3c\x6b\x10\x9e\x49\xdb\x19\x4b\xfb\xe7\x71\x04\xf0\xbd" "\xb4\xcd\x68\x7e\xf7\xa0\xc5\x1f\x09\x11\x57\xc9\x91\x19\x1d\x91" "\xaa\x28\xa5\x6e\x93\x04\x89\x65\x59\xaf\x75\x62\x81\x53\x07\xb4" "\xc9\x42\x60\x9f\x00\x60\xb0\x95\xfc\xb1\xbf\xf6\x95\xc5\x3a\xa9" "\xf4\x03\x4d\x22\xbb\xb8\x83\x3c\x11\xdb\xb9\x58\x36\x6f\xe0\xa7" "\xe3\x98\xd6\xa3\x0e\x35\x82\xcb\x4c\xc9\x5c\xd3\x41\x3b\xb6\x9b" "\x48\x2b\x34\xa9\x0e\xfd\xc4\x5e\xc8\x11\xa6\xc0\xdf\xd7\x01\xb9" "\xd2\xab\x7c\xd4\xee\x09\x8a\x5f\x79\xb4\xd2\x4e\x59\x08\xd4\xca" "\xfc\x4b\x8d\x32\x14\x10\xad\x1a\x41\xb0\xd0\x3e\xce\xf6\x5c\x5d" "\xf4\x90\x5c\x5b\x72\x52\x98\xe0\xb2\x1a\x30\x2e\xf6\x95\x48\x36" "\x0c\x9f\x01\x20\x11\xe2\x39\xde\x5d\xfd\x8c\xfa\xeb\xb7\xcf\xc1" "\x96\xea\x70\x42\x82\x1f\xac\xa1\x43\x85\x36\xba\x5b\x90\xc4\xd5" "\xc4\x98\xf5\x63\x3a\x39\xf9\x15\xee\x34\x26\x76\x62\xf3\xfe\xce" "\x7c\x46\x33\x8d\x6a\x58\x23\x11\xf3\x55\x37\x0d\x58\x52\xef\x6b" "\x25\x37\x3d\xfb\xe2\xf6\x7f\x0d\xb8\xc4\xcd\x0e\x14\x8b\x66\x02" "\xf2\x9f\x51\xce\x8d\x8c\x39\xf7\x9d\xe6\x38\x55\x1a\x24\xce\x9d" "\x96\x8a\x46\x8f\x72\x80\x0d\x3f\x90\xf5\x4f\x2a\xef\x66\x98\xd0" "\x4c\x1b\x77\x73\xe5\xa2\x3d\x89\xe5\x2d\x14\x08\x85\xe5\xc5\x1f" "\xc0\x4d\x60\xad\x5f\x9d\x92\x84\x45\x18\xec\x0b\x17\xc1\x50\x22" "\x43\xc4\x47\x32\x7d\x18\x2e\xf0\x6b\x53\xdd\x02\x24\x96\xc6\xfa" "\x5b\x3c\xd8\x70\x06\x71\xb7\x4b\xd2\xe0\x45\xad\x04\x80\x14\x30" "\x7b\x38\x9f\x7c\x7a\xf9\xe8\x3c\xac\x46\x58\xb9\x9f\xfa\x3a\x38" "\xdf\x98\xc4\x13\x28\x46\x74\xa5\x66\xf0\x25\x3c\xb2\xfd\xcd\x20" "\x5a\x4e\x43\x6c\x35\x75\x1c\xb2\x2c\x69\x03\x9f\x13\x38\x04\xf6" "\x87\x32\xb9\x67\xf6\x1f\x86\x26\xd4\xdc\x0e\x66\x4a\x52\xa0\xac" "\xb0\x62\x7d\xd1\xa2\x48\x6c\xfe\x27\xe3\xf6\x8c\x9b\xe7\x99\x3d" "\x0f\x0e\x16\x04\xb5\xa0\x17\xe4\x23\xe1\x3c\xaa\x25\x50\x44\x45" "\x72\x17\x03\x03\x05\x4b\xdf\xa8\x14\xb8\x38\xdd\x78\x9c\x0f\x8e" "\x92\x94\xb5\x28\x7b\x8e\xdd\x08\xc4\xb9\xdb\x9c\x2b\x94\x2b\x80" "\xd6\xbe\x63\xf2\x43\x04\x55\xee\x6f\x4c\x6b\x49\x32\x20\xf3\x95" "\x65\xee\x63\x30\x0b\x34\x3b\xfe\x70\x3a\x81\x6e\x6a\x61\x2e\x46" "\x08\x44\xdc\x34\xf0\x8f\xa7\x6d\x2e\xb4\x23\xa8\x6a\x77\xd0\x2a" "\x5a\xbc\x6b\x46\x33\xf5\x5a\xb3\x16\x20\xa4\xdd\x39\xcc\x1c\x77" "\x54\x5e\x86\x38\x79\x7d\x7b\xae\xed\x1d\x77\xc4\x2c\x49\x2f\xb6" "\xc0\x58\x62\x9d\x2b\x02\xf5\x57\x10\xe9\x39\x65\x43\x99\x66\x38" "\x5f\xf7\xc0\xa7\xd0\x3d\xdd\x42\x88\xa3\xd8\x86\x01\xcf\x07\x17" "\x86\xa8\xe5\xed\xc7\xd9\xdd\xc4\xfe\x43\x88\x51\x56\x48\x6b\x40" "\xd0\x98\xc2\xf7\xb5\xa5\x6b\x3f\xdd\xb6\x56\x28\xac\x45\x4b\xd8" "\xc5\x16\x42\x2c\x4e\x33\xa8\x54\xf3\xb2\xac\xc6\x24\xd1\xeb\xb0" "\x2a\x06\x24\x4b\x0c\x1f\x5a\xc3\xa1\xa5\x18\x69\xad\x32\xc2\xcd" "\x29\x37\xe8\x33\x53\x04\x12\xd7\xe5\x26\x7c\x8d\xb0\x2a\x40\x44" "\x36\x0b\x20\x60\x58\xab\xb2\x06\x8d\xd3\x42\x5d\x5f\x85\x57\xf6" "\x1f\x88\x34\xb6\xad\x01\xd7\xc8\x33\xea\x96\x3c\x09\x16\xb5\x59" "\x35\x13\xfd\x5e\x59\x2a\xd9\x5d\x89\x45\x23\x63\x06\x4e\x79\xe9" "\x94\xa0\x0f\xe7\x12\x74\xf3\x38\x97\x51\xb4\xed\x48\xc9\xcc\x7a" "\xe9\xd7\x8d\x6d\x5a\xa3\x1d\x36\xba\xd8\xa9\x58\x0b\x05\x32\x51" "\xc4\x14\x04\x3b\xbe\x50\xf5\x15\xcf\xa6\xea\x18\x06\xfd\xde\x06" "\x8a\xcc\xed\x67\xe3\x58\x48\xd8\x5c\xf8\x60\x28\x17\xcd\xbf\x49" "\xc8\xaf\x7f\x9e\x81\xe7\x04\x3e\x31\xc6\xfe\x32\x35\xcb\xd7\x39" "\x9a\xc9\x34\x9c\x1f\x83\x21\x66\x39\x39\xc4\xd2\x40\x25\x06\x84" "\xdc\xca\x1a\x99\xee\x88\xdd\x81\x5d\xea\x1a\xda\xa7\xa0\x43\xa6" "\x46\xa4\x08\xbc\x1c\x2d\x99\x5c\x71\xfd\x31\xb1\xd1\xbb\xcd\xba" "\x7d\x7d\x94\x8a\x07\x38\x7e\x69\x98\x5e\xdc\xb5\x68\xe1\x67\x24" "\x24\x53\xe7\x08\xf5\x04\x09\xe4\x75\xf7\x4d\x8a\x43\x5f\x2f\xe7" "\xf1\x62\xd5\x7a\x67\xdc\xf6\xbc\x04\x03\x75\xc6\xf9\xd4\xa3\x49" "\x16\x2c\x82\x01\x27\x30\xf7\x7c\x1d\xb5\xcd\x72\xd5\xc2\xfe\x8e" "\x3e\xf2\xf3\x1a\xda\xc2\x0a\xa2\x30\xf3\x3f\x4b\xa4\xf1\x91\x27" "\x9f\xe4\xac\x1e\xe6\xaf\xaa\x3f\x47\x9d\x84\x83\x79\xd2\xe4\x3f" "\xd4\xf4\x66\x1d\x44\xea\xa0\xd4\x96\x2a\x3a\xf3\x5d\xeb\x88\xd3" "\x42\x10\x79\xf1\xb5\xdc\xfc\x48\xbf\x18\x25\x19\x40\x1e\x9a\x30" "\x40\xf8\xc9\xd4\xe0\x4b\x6a\x39\x70\xa4\xfe\x57\x87\x69\x39\xf3" "\x36\xd9\xec\x22\xfe\x5d\x2d\xdd\x20\xe7\x5b\x2d\x6f\x7f\x78\xf4" "\x34\xfb\xcc\x32\x13\x92\x6a\x7f\xa5\xe0\xdb\x8b\xf1\x2d\xe8\xbe" "\xfd\x66\x44\xad\x8b\xc3\x0d\x1b\x25\x89\xbe\x98\x4b\x50\xca\x56" "\x40\x9d\xc4\x96\x46\x4f\x6b\x65\x3b\xae\x9a\x8f\xa9\x7d\xde\xbb" "\x71\xd7\x84\x14\x75\x39\xb1\xa1\x4d\x74\x1f\xaa\x25\x5c\x04\xeb" "\x35\x1c\x5a\x31\x21\x52\xd4\x4c\xc8\x50\x9b\x0e\x54\x04\x71\xeb" "\xe0\xc2\x99\xca\x87\xf0\x9a\x89\x68\x20\x13\x71\x93\xf0\xce\xd1" "\x9c\xd6\x8d\x39\xad\x4c\x32\xad\xab\x30\xb2\x12\xbd\xa7\xe2\xe4" "\xcd\x25\xf1\x9b\x5b\x31\xe1\x5e\x2f\xd9\xad\x08\x1d\x10\x86\x2f" "\x4d\xc5\xd1\xea\xf9\xa2\x91\x15\x98\xa8\x30\x05\x45\x4d\xab\x1e" "\x20\x31\xef\xe2\x3e\x56\x7f\xbb\xf8\x02\xe3\x84\x53\x40\xaf\x48" "\x8e\x7c\xe0\x91\x80\x29\x9a\x4c\x8b\x8b\x29\xbf\x61\xa3\x41\x01" "\x0f\x95\x09\x1c\x97\x32\xd9\x59\x13\x3d\xd0\xe7\x9c\xbb\x72\x9b" "\x56\x10\x5a\xcd\x8d\x97\xc3\xe2\x52\xc9\x23\xc6\x65\x90\xee\x4a" "\xc3\xe9\xea\x6b\x17\x89\xd8\xca\xbb\x8a\x4d\xa5\x81\x3b\x5e\x6e" "\x69\xf2\xa0\x0a\x5e\x37\x90\xb7\x89\xf0\x1c\x58\xbb\x4e\xbf\xaa" "\x35\x86\xc6\xf5\x8d\x63\x62\x5f\xe9\xd6\x62\x5c\xba\xf3\x0a\xa1" "\x55\x02\x33\x83\x8e\x0c\x7b\x1b\xd9\x66\xa3\xed\x90\x08\x6f\x02" "\x05\x99\xbf\xa5\x96\x8d\xe6\x3f\x42\xbd\x75\x50\xc8\x88\x0d\x24" "\x9a\xee\xbf\x9e\x91\x0f\x9b\x89\x29\xeb\x9c\x82\x3b\xf8\x9a\x93" "\xba\x91\xa3\xaf\x19\x25\xb6\xcd\x64\x89\x76\x03\x3c\xa0\xc3\xeb" "\xa2\x2b\xc1\x0a\xe6\x3d\xf4\x5a\x47\xda\xc7\x3f\x88\x13\xb6\x61" "\x0f\x2c\xed\x94\x64\xde\x4b\xab\xfc\x48\x20\x55\x27\x2b\x08\x7f" "\x72\x66\x92\xa5\xc5\x55\x12\x72\x5f\xb0\x4f\x1d\x08\x7e\x8b\xe0" "\x30\xdd\x25\x60\x13\x3a\x45\x69\x07\x42\xe2\x5b\xac\x7a\x78\x39" "\x53\xc2\xe5\xd7\xbb\x8f\x7b\xd4\x8e\x09\xc0\x54\xca\x6b\xaf\x0f" "\x95\x67\xce\x78\xc1\xe7\x0f\x8c\x15\x20\xfd\x61\x19\xd7\xda\x49" "\x0b\xaf\x24\x44\x7e\x88\xb9\x7d\x77\x4a\x7d\x64\xac\xd5\x67\xd5" "\xe7\x05\xb3\x90\xa3\x68\xfd\xf9\x35\x6c\xfc\x60\x9e\x47\xa6\xb3" "\x2f\xab\xe2\xe7\x68\x2a\xb7\x0b\xc0\x30\xdf\x63\xf3\x63\x4b\xfd" "\x8e\xd4\x84\x36\x84\x5e\x9d\x2a\xa4\x56\x9e\xb0\xe4\x75\xaa\xca" "\xde\x1e\x0c\x46\xd7\xf4\xf6\xaa\xa9\xd8\x41\x9e\x59\x36\xfe\xac" "\xef\x6f\x9c\x46\x29\x21\xc2\x60\xf4\xc0\x2d\x67\x26\xce\x19\x9e" "\xa5\xb9\xf7\x35\x0b\x33\x42\x5b\xd3\xb7\x46\x25\x93\x8d\x95\x30" "\x0a\x38\x5c\x07\x7b\x7a\x21\xfa\xe0\x3c\xcd\x24\x8a\x22\xb0\x87" "\xf5\x52\x99\xcc\x0a\x25\x07\xef\xc1\x18\xbd\x55\xf3\x66\x0d\x20" "\x08\x40\x7e\xf1\xb3\x90\x24\xef\xf0\xb0\xa2\xc5\x4f\xf1\x91\x91" "\xde\x69\xd3\x27\x15\x1c\x18\x22\x06\x84\x7f\x32\xac\x07\xc0\x4e" "\xd2\xbf\x6e\x19\x01\xed\xfc\x44\x77\x61\x52\x85\x83\x07\x68\x25" "\x1d\xde\xd1\xea\xb2\xb4\x57\xa3\x11\x0f\x60\xa7\x64\x64\x2b\x12" "\x34\xaf\xb4\x51\x1d\xeb\xf4\x87\xd9\x41\x7b\xc1\x20\xaf\x41\xdc" "\xe0\x84\xc1\x1c\xeb\xd9\x8d\x97\xae\x37\x51\x83\xcd\x8c\xaf\xef" "\xaf\xfc\x32\x6e\xea\x9a\xc4\xd2\x1d\x6d\x91\x48\x6a\x42\x9b\x0d" "\xd7\xe0\x12\x79\xbd\x48\xdd\xde\xb1\x02\xd2\xbb\xdc\x49\xd9\x6b" "\xd7\x5b\x61\x87\xeb\xbd\x82\xec\x28\x83\x86\x43\x0f\x1b\x0a\x7f" "\x31\x73\x85\x89\x8b\xd6\x48\x1d\xfa\xd5\xdd\x6d\x3e\x39\x8b\x9b" "\xd6\x13\xbf\xc8\x54\x97\x96\x47\x34\x84\xb9\xe5\x78\x3c\xed\xd8" "\xd8\xb4\x8e\x43\x6b\xb2\x9f\xf2\xe2\x72\x79\x45\xc6\xfb\xcc\xe4" "\xf7\x78\x81\x72\x46\x81\x89\xf3\xf7\xca\x9f\xed\xd5\x1c\xc1\xf8" "\x6d\x86\x08\xdc\x63\x5d\xc5\x1e\xd1\x79\xec\x22\xbc\xde\xce\x67" "\x49\x44\xf2\xe4\xc6\xdc\x36\x64\x92\x4e\x2e\xf0\xc0\x04\x88\xc5" "\x3d\x17\x03\x03\x05\x4b\x17\xdc\x13\x16\x8a\x2f\x4d\x2c\xde\x49" "\x69\xdd\x5d\xd0\x30\x98\x31\x8a\x37\x12\x38\xeb\xb3\x8a\x48\x8e" "\x46\x1a\x4d\xab\xb1\xba\xd3\x89\x61\xa2\x58\xc3\xd0\x78\x80\xf1" "\x79\x6c\xfc\x58\x08\x59\xeb\x78\x3e\x46\x3d\x44\x66\xb5\xaf\xaf" "\x0f\x07\xba\x2c\x6e\x3f\xb9\x40\xd8\xb6\x4e\x87\xc5\x50\x35\x09" "\x55\x3f\x95\xfd\x15\xa8\xa5\x94\xe0\xf9\x71\xd8\x6a\x00\xa1\xfd" "\x11\x3a\xeb\x1b\xd1\xac\x3b\xe8\x35\x61\xbe\xe7\xcf\x0e\x93\x89" "\xe0\xc6\xa1\xf7\xab\xe7\x67\xc8\x72\x07\x19\x57\x54\x21\x80\x38" "\x3f\x20\x3d\x23\x3d\xa1\xe3\x21\x8b\x77\x61\x5f\x25\x15\xe1\xc2" "\x46\xb9\xca\x0c\x1d\x12\x54\x8e\xcb\xec\xb8\x14\xf4\xd1\xac\x5e" "\x08\xb6\xc2\x0e\xed\xc4\x1f\x43\xbe\x2f\x98\xa7\x6e\x1f\x02\xe4" "\xfe\xd2\x42\x0b\x9e\xd2\x25\x8f\xc2\x1b\x1c\xd6\x91\x80\xe1\x76" "\xc6\x38\xc6\x5a\x02\x7a\x25\xe2\x39\x6f\x64\xfc\xaf\x58\xd4\x1a" "\xb1\x40\xc4\x82\x9f\x47\x31\x2b\x14\x08\xf0\xc0\x6d\x37\xd5\x33" "\x52\x1b\x42\x69\x61\x16\x81\x4d\xa7\x56\xe2\x89\x46\x11\x63\x03" "\x04\x6a\x9b\x2f\xc5\xa1\x2d\xca\x6e\x07\xf5\xe8\x38\xb9\xb3\xfa" "\xe4\x09\xec\x01\x17\x84\x7c\x6b\x4f\xce\x15\xd4\x3c\x57\x95\x48" "\x2b\x6f\x55\xec\xb9\x38\x8e\xb1\xce\xab\xde\x41\x65\x38\xe8\x5a" "\x1f\xb8\x91\x7e\x64\x25\x1c\x3a\x29\xf1\x38\x75\x78\xd1\xc3\x2a" "\x07\x58\xa4\xe0\x19\x41\x21\xf6\x11\x33\x82\x8a\x60\xf4\x3f\x75" "\xaf\xae\x33\x4b\xb6\xcd\x1f\xf7\xee\x14\x15\xef\x49\x4b\xc2\x72" "\x3b\xab\x40\x1e\x20\xf0\xb8\x8d\x10\xc7\x79\x28\x35\xea\xad\x6d" "\x2e\xc0\xf5\xec\x9d\x8b\xe6\x4b\xd2\xb9\xb3\x29\xc1\xc0\x74\x15" "\xb7\x24\x11\x41\x03\x52\x38\xc7\x6b\xad\x2f\x2c\xac\x5e\x1f\xad" "\xd6\x21\x5e\x80\x13\x3a\x90\x3d\xcc\x42\xf2\xd3\x4b\x4b\xe0\xf1" "\x03\x3c\xef\xfb\xa4\x6e\xdf\xe3\xdc\x59\xc8\x57\xe6\x7c\x08\x6b" "\x98\x92\xf0\xde\xa7\x87\x2a\x0e\xb9\x63\xa7\x81\x9c\xad\x52\xc5" "\x8f\x6a\x7f\xc2\x09\xec\xcd\x23\x36\x19\x57\x37\xbe\x3e\xa8\x64" "\xd8\xa3\x8d\xbd\x63\x2d\x1d\x6f\xd0\x61\x1f\x16\xd3\xab\x76\x08" "\x53\xa4\x4e\x74\x14\x62\x8c\xc2\xc8\xba\x63\xd4\x55\xf1\x4a\xa7" "\x48\x2c\xc7\x7b\x8c\xa5\x5d\x60\x30\x8e\xbe\x67\xc3\xac\x0c\x74" "\x4c\x39\xe8\x15\xb9\xb0\x71\xf7\x86\x1a\x46\xff\xbe\x33\x8f\x9a" "\xa8\x2a\x72\x4a\x6e\x01\x5b\xe6\x4b\x3f\x52\x92\x45\x33\x10\x92" "\x64\xff\xa0\xa9\xc9\x84\x7b\x12\x51\x2f\x09\xb2\xbd\x23\xda\x40" "\xe1\x50\x65\x3e\x35\x69\x5d\xc9\xce\x97\x8a\xc7\xc8\xdd\x28\x9d" "\x38\x80\x07\x37\x8d\x5f\x96\xf9\x0c\xb2\xb2\xaa\x38\x33\x29\x89" "\x9f\xab\x27\xba\x51\x0c\xb5\x5a\x75\x7d\xfd\x4f\x8b\x6a\xde\x6f" "\x28\x6b\x12\xab\x7a\x06\x4e\x5a\xbd\x2a\xb8\x82\xe0\x58\xd6\x71" "\x1c\xfb\x2a\x33\xf9\x21\xd0\x3a\x1a\x7b\xcd\x0d\xcd\x3b\x13\x29" "\x93\x43\x38\x7a\x96\x59\x0b\x44\x03\x91\xe3\x5e\xb8\x4e\xf8\x0a" "\xe7\xe4\x6e\x91\x03\x22\x25\xa6\x22\x05\x52\x11\xc0\x43\xfc\xe3" "\xae\x92\x5f\x9c\x80\xe7\x33\xf7\x2d\xb5\x6c\x23\x36\x65\xab\x74" "\xcc\x37\xde\x7a\x74\x54\x3f\x2e\x2e\x38\x45\x74\xcc\xda\xa9\x61" "\xf8\xe8\x7e\x62\x5c\x8a\x5c\xf6\x0b\xd1\xbd\x1f\x45\xce\x76\xd9" "\xf2\xa0\x86\x72\xea\x33\x62\xa0\x01\x31\xa5\x16\xdd\xb2\x54\x76" "\x58\x59\x26\x41\x43\x16\x1e\x69\x21\x79\xeb\xe8\xac\x66\xba\x4b" "\xd7\xd4\xb6\x41\x66\x1f\x89\x89\x0f\xf7\xbc\x65\xda\x8c\xa1\x67" "\x0f\x0a\x37\xe8\xa5\x0c\xb7\x2b\x58\xb1\x9e\xca\xfe\x9d\x73\xd1" "\x83\xa7\x5e\xaf\x33\xc4\xfb\xff\x03\x89\x2a\x60\x55\x07\x0b\x25" "\x81\x5a\x55\xba\x6b\xcd\xff\xf0\xd1\x81\x61\x0d\x3e\x00\x4c\x51" "\xba\x29\xc0\xaa\x98\x33\x6b\xff\x68\x2c\xcc\x0c\xd3\x40\x7c\x9e" "\xe1\x71\x0b\x08\x03\xb7\x29\x56\x5e\x92\x90\xc7\x8f\xe8\xd8\xf4" "\xb6\xe6\x40\xf6\x49\x7a\xf9\x62\x8d\xd0\x83\xb7\x76\x95\x6e\x9b" "\xc9\xf4\x56\x03\xe0\x80\xca\x12\x0f\xda\x9e\x35\x5a\xfd\x9e\x6e" "\x8b\xd5\xf6\x9e\xab\xb6\xd3\xd5\xff\x8a\xdb\x7c\x85\x4a\xa0\xb5" "\xd0\x76\xb2\x8e\x4c\x73\x9f\x53\x0a\x9d\x65\xd8\x2e\xf3\x95\xb6" "\x9e\x1a\xd0\xb1\xfb\xa0\xcc\xa6\xed\xbc\xd1\x59\xdb\x43\x73\x10" "\x80\xe9\xd7\xd4\x7d\xa3\x0c\x94\xcc\x3a\x19\xd8\x25\xdb\x9f\xcc" "\x88\xe1\x61\x97\x81\x87\x78\x83\x46\x51\xbf\xa1\x9f\x8c\xcb\x63" "\x77\xfc\x52\x67\x55\xd3\x24\x1d\xfc\x4b\x87\x74\x50\xc0\xc8\x34" "\x34\x65\x04\x6c\xda\xcd\x8b\xd6\x1b\x01\x5b\x09\x65\x6b\x63\xda" "\xac\x3b\xad\x8d\x02\xab\xd3\x2c\xd9\x85\xdb\x05\xb4\x40\xf6\xde" "\xc3\x78\xf8\xff\x23\x60\x65\xd2\x92\x28\xf6\x1c\x1b\x1f\x4a\x06" "\x1b\xd6\x51\x17\xb9\xd7\x4d\x74\xd5\x7a\xb3\x42\xb3\xf6\xbf\xd9" "\xaa\x80\x1d\xd0\x85\xeb\x79\x24\x06\xa3\xad\x5a\x62\xb9\x18\x46" "\x5e\xe3\x77\x5c\x39\x45\x69\x64\x16\xd5\xba\x3c\x6c\xb7\x02\x47" "\xb3\xcb\x95\xa6\x47\x5d\x5c\xcd\xdc\xd5\x6e\xfd\x9d\xe2\x76\xef" "\x1f\xc7\x66\xc3\xac\xc9\x20\x37\x14\xa9\x13\x0c\x6c\xcf\x81\x13" "\x95\xff\x68\xf1\x6d\x97\xe4\x4e\xc1\x7f\x7a\x73\x25\xc3\xcd\x72" "\x0e\x59\x8a\xb8\xb3\xe4\xd4\x1b\x21\x7a\x53\x5a\xee\xc9\x5a\xdb" "\xf1\x79\x03\xd8\xb8\xba\x6e\x58\xf9\x9a\x72\xb4\x62\xf1\x40\xc2" "\x28\x2c\x10\xad\x09\x86\xa3\xda\xc1\x7f\x6b\xd6\x94\x3c\x8a\x45" "\xbe\x5a\xea\x80\x79\x79\x87\xd5\xe7\xc7\x67\x9c\x85\x91\x65\xf1" "\xde\xcc\x0e\x2a\x6d\x42\x62\x07\x51\x3a\x2c\xb9\xc9\x3f\x62\xe5" "\x05\x6c\xb6\x6c\x52\xf1\x7d\xe8\xf4\x7b\xa6\xf2\x24\x3f\x06\x6e" "\x91\x38\x91\xdf\x58\xef\x04\x54\xc6\x22\x02\x70\x07\x86\xe7\x3a" "\x23\x0d\xab\x7e\x6d\x20\x07\xf1\x6f\x15\x4c\x82\xc3\x9d\x65\xb0" "\xb0\x32\x1f\x82\x64\x10\xb1\x75\x96\x2d\xd6\x32\x23\x38\x39\xc5" "\x60\x2b\x7e\x1c\xb3\xd4\x51\xf0\xad\xfd\x75\x76\xff\x93\x18\xac" "\x67\x12\xef\x0b\xd8\x1a\x1e\xa2\xfc\xa2\x78\x21\x56\x5e\x91\x7c" "\x66\x75\xbc\xfe\x99\xd4\x47\x01\x9d\x67\x84\x6f\x49\x66\x4f\x1d" "\x3d\xe9\x66\x22\xf3\xac\xc0\xf5\x69\xb2\xe6\x59\x21\xe4\xf9\xc9" "\x2e\xc2\x63\x5a\x70\xf7\x28\x74\xc5\x2b\xf9\xb7\xce\x0b\x7f\xb7" "\x51\x60\xe8\xbc\xe2\x7b\x0d\x4f\x2c\x61\xe7\x49\x04\x41\xe7\xa2" "\x04\xdf\x40\x4c\x7f\x39\x4f\x20\xc9\x63\x9a\xb7\x1f\xbf\x1a\xe6" "\xc8\x17\x03\x03\x05\x4b\xce\xec\x40\xa0\x91\xad\x95\x8a\x8b\x25" "\xfb\x3f\xef\xfd\x84\x5e\xca\xe4\x11\xa3\x65\x27\xf3\x28\x48\x60" "\x71\xad\x92\xe7\x6a\x9c\xfc\xe1\x0c\x53\x1e\xaa\xf8\xe0\xd7\x67" "\xbd\x0f\xaa\x00\x73\x3e\x67\x80\x0f\x70\xee\xec\x6f\xe9\xb6\xff" "\x85\x6d\x64\xc7\xcb\xf2\xb7\xf3\x1a\x29\x17\xb3\xeb\x8d\x73\xba" "\xaf\xaa\x8b\x02\x04\x23\xbf\x89\x2d\x7c\xd6\x67\x50\xa1\x22\x5f" "\x0e\xeb\x87\xfd\xd2\xd7\x55\x1e\xc0\xb8\xad\x99\xaf\xb7\xf6\x74" "\x41\x7c\xf6\xb3\x51\xbc\x90\xc7\xd6\x00\xca\x7a\x72\x7e\x2d\x76" "\x1e\x81\x9a\xad\x8d\x69\x4c\xda\x2f\x40\xa3\x35\x29\x79\xa8\x15" "\x38\x6f\x68\xcb\x7d\xd1\xe6\xb3\x9c\x0f\x95\x4e\x37\x8f\xf2\xca" "\xef\xf8\x1c\xe9\xda\x51\x18\x60\x66\x26\x3d\xe1\x6a\xa5\x7f\x8f" "\xce\x8a\x6a\x59\xef\x8a\x06\xb9\x67\xa0\x2a\x27\x9c\xdd\x6a\x6b" "\x51\x31\xc9\x24\x77\x7c\x83\x98\x82\x99\xa2\x10\xa7\xa2\xcb\x22" "\x00\xef\xf3\x25\x38\xa7\x6c\x88\x49\x22\xb1\xd3\x1b\xaa\x50\xee" "\x3b\xfe\xfe\xaa\x6f\x03\xf1\x10\x13\x4e\xa8\x43\x6f\x9a\x52\x8d" "\x75\xba\x0e\x2a\x13\xbe\x77\x68\x52\x73\x14\x03\x86\xea\x29\xc0" "\xb7\x2b\x0a\x17\x93\x01\x23\xe4\xe5\x5a\x19\xcd\xd1\x08\x02\x6c" "\x76\x20\xe6\x62\x9a\x37\x1a\xb1\xfc\xe6\xc9\xf1\xe6\xab\xc8\xcd" "\x16\x89\x77\x8d\xd5\xb2\x3e\xa5\xfe\x71\xb4\xaf\xc4\x4b\xd0\x34" "\xbd\x10\x40\xe5\x17\x19\x25\x9d\x67\x72\x0c\x65\x9e\x5f\xb1\x9e" "\xf0\x0a\x0a\xfc\x75\x4b\x19\x0f\xb3\xc4\xf1\x1a\x48\xa4\xe6\x6d" "\x10\xbb\x32\x9d\xc9\x67\xb8\xad\x32\xc9\x15\x7f\xea\x2e\xad\x01" "\x7d\x2a\x7f\x73\xbd\x15\x40\xc2\x70\x08\x57\x50\xf8\xce\x98\x2b" "\xa1\xb4\x26\xe9\xc4\x4c\x3e\x54\x90\x9e\xc0\xb8\xf0\xbc\x77\x1e" "\x0f\xd9\x35\xd0\x86\x05\x89\x03\xba\x9a\xab\x0b\xeb\x52\x13\x95" "\xcd\xcc\x6d\x84\xd4\x6e\xbe\x23\xf2\x97\x45\xa5\xfb\xfc\xff\x6d" "\x84\xba\xc4\x9d\x22\x0e\x0d\x5a\x5a\x33\x8a\x95\x53\xdf\x2d\x71" "\x1e\x4b\xea\xfc\xb4\x80\xd7\xcb\xe7\x35\x15\x0d\x63\x56\x99\xc9" "\x4a\x15\x0d\xaa\xdf\x5f\xc0\xae\x51\xab\x88\x24\xa7\xae\xd4\x32" "\xba\xd5\x0b\xa9\x48\x00\x79\x0f\xb1\x68\x5f\xa6\x08\x24\xdf\x7c" "\xc9\xd9\xa9\x7b\xe8\xbd\xa8\xe6\xc1\x02\xf3\x15\x9b\xe0\x79\x27" "\xaa\xe0\xbe\x0d\x6c\x41\x7d\x5a\xd4\x03\x0b\x2e\x80\x35\xfb\xd7" "\xba\x22\x53\xf2\xad\x53\x79\xd1\xc1\x8a\x37\xaa\xfb\x1a\x9f\x77" "\xa1\x69\xdc\xdf\x7b\xa0\x4f\x58\x2e\x26\x52\x59\xa7\xe7\x24\xe7" "\xa2\xa7\xf9\xe9\x37\xb1\x8c\x4f\x64\x8a\x62\x7b\x89\xa3\x0e\x1c" "\x55\xa9\xa8\x04\xd0\x73\x70\xbf\xa7\x34\x51\xe9\xcf\xe1\xaf\x47" "\x85\xe8\x4d\xc1\x33\xc0\xbb\x5b\x9d\xc6\xd2\x29\x00\xbb\x61\xd2" "\x04\x5b\x5c\x07\x73\x07\xf3\x17\xd8\x52\x16\xd7\x3c\x8e\xda\xfb" "\xd4\xd2\xee\x7b\x1a\x47\xcb\xca\x8a\xa0\xf7\x9f\x3e\xa2\x4d\xcb" "\xaa\xa4\x94\x51\x3b\xcf\xfb\x45\x0b\xb6\x2a\xf7\xfc\xbe\x87\x22" "\x6e\x42\xd3\x9d\xba\x90\x72\xd5\xd5\xcc\x0d\x04\x64\x8f\xf2\x7f" "\x02\x2a\xd6\xb4\x07\x6f\xe6\x33\x8e\xbf\x2c\xfd\x67\x12\xd2\x5a" "\xaf\x39\x5e\x51\x8e\x43\x61\x66\x12\x3f\x35\x61\xba\x38\x54\x07" "\x7c\xd1\x83\x4d\xe5\x7a\xde\xfb\x7f\x4c\xaf\x21\x26\xdb\x0f\xfc" "\xde\xea\x92\x51\xb0\x83\x41\xef\x42\x8e\x68\x56\xbc\x28\x66\x53" "\x59\xd3\xde\x95\x99\x46\x84\xf6\xf1\x28\x5e\x67\xad\x44\x8b\x2e" "\xd5\x36\x59\x08\x4d\x59\xfb\xf7\x88\xd2\x0d\xc2\x41\x53\x5f\x01" "\x6c\x86\x24\x8e\x2e\x0a\xfc\x0f\xfd\xf5\x28\x3e\xe4\x70\x4b\xbe" "\xc3\x8b\xe1\xb5\x6b\xdf\x8d\x19\x52\x9e\xab\x65\xfe\xb8\x5e\x3e" "\xc1\x33\xf3\x41\x20\xf1\xf7\x5b\x4d\xd0\x8b\x4c\x1b\xf0\xd3\xe6" "\xc0\xbf\xc0\x7a\x67\x49\x43\x2b\x64\xbf\xe8\xb5\x2e\xa1\x9d\x1e" "\x35\xc2\x3a\xb5\x60\x1f\x03\x28\x8b\xcd\x07\xdd\x10\x94\x68\x06" "\x1b\xd9\x8b\xaf\xa1\x6f\x8d\x79\xef\xc8\xea\xeb\xe3\x42\xc9\xb5" "\x3a\xff\xf2\x45\xa0\x07\x4a\x76\x26\xdc\x46\x9b\xe6\x24\xbe\xe6" "\x1b\x34\xf5\xff\x69\xf2\x7a\x39\x25\xd3\xd7\xe1\x12\x7c\x35\x1a" "\xa1\x98\x2a\x3a\x99\x18\x51\x7e\xaa\xe8\xfc\xce\xe7\x9f\x7b\x08" "\x4c\x47\xaf\x71\xe9\xd2\x8e\x7b\x5f\x84\x7d\x04\x70\x3b\xda\xba" "\xad\x9f\x0e\xb4\x7d\x97\x77\x9a\xaa\xcc\xc1\x70\xea\x72\xe5\x51" "\x16\x9d\x56\x3f\x42\x9a\xaf\x2e\x1e\xe2\x4e\x9f\x92\x40\x88\xaf" "\x7a\x95\x3a\xa9\xc5\x83\x77\xe3\x97\x82\x3d\x18\x23\xc0\x6f\x80" "\x7c\xe2\x4a\x44\xcc\x15\xc2\xb8\x4d\x7a\x0e\x49\x47\x73\x39\x23" "\x0d\x95\xed\x53\xbf\x9a\x0a\x4e\xf6\x54\x4a\x2f\x16\xa5\x9c\xeb" "\x60\xf1\x65\x00\x8f\x3e\xcf\xef\x6f\x07\x76\x41\xab\x07\x25\xe4" "\x53\x49\x83\x77\x15\xd7\x00\x64\x6d\xc0\x99\x63\xc5\xbf\x46\x27" "\xb6\x24\x12\xd1\x34\xbf\x1a\xd6\xed\xfe\x09\x69\x2b\x06\x6c\x32" "\x4f\xc8\x30\x51\xb0\xd9\x33\x8e\xbe\x3b\xc6\x60\xf2\x23\x7e\x6f" "\x71\x03\x4c\x25\xc8\x68\x0a\xab\xec\x4a\xdb\x4c\x7e\xa9\x23\x74" "\x1f\x34\xca\xe9\x21\xb1\x4f\x97\x94\x61\x20\x11\x7e\x99\xcf\xf7" "\x76\x2b\x05\xaf\x6a\x39\x33\x64\xcd\x38\x51\xfe\xca\x5b\xfb\x61" "\x79\x65\x02\x39\x05\x92\x72\x2d\x91\x91\x0b\xd3\x45\xa1\xb6\x4a" "\x2f\xd9\x77\x86\x91\x0b\xa4\x85\xb8\x3e\x82\xad\x7e\xce\x48\x18" "\x52\x85\xcd\xb2\x9c\x31\x1d\x7b\x25\x40\x04\x75\x3b\x2c\xc8\xf3" "\xa6\x3c\x8e\x21\x36\xdc\xe3\xf4\x3f\x8f\x9d\x35\x77\x40\xe0\x76" "\x57\x3a\x5e\xd3\x6b\x97\x76\xf5\x33\x2e\x26\x6b\x45\xed\xf3\x2c" "\xe9\x34\x24\x54\x3f\x86\x82\x29\x32\xf9\x97\xae\x1c\x33\x87\x95" "\x6d\xa8\xfc\x3f\x66\x6a\x07\xd5\x1f\x0a\xb1\x46\xbd\x53\xa5\x92" "\x0e\x2a\x55\x03\x2b\xa9\xfc\x0d\xcc\x90\xa2\x47\x14\x54\x63\xb2" "\x6b\xce\xff\x43\xe1\x95\x37\xdb\x67\xc8\xc4\x04\xc7\x86\xb2\xb0" "\xaf\x49\xd0\x34\x4a\x56\x3e\xeb\xd3\x32\xab\x5e\x60\xe5\x30\x65" "\x87\x81\x48\xae\x15\xfc\xdb\xbd\xa9\x37\x3a\x28\xcb\x9f\xf2\xff" "\xe0\x6c\x1a\x9a\xbb\x23\x59\x5e\xb7\x26\x68\x8a\xca\x74\xcf\xfb" "\x12\xf3\x17\x37\x50\xcf\x33\xda\xbf\x3a\x87\xda\x44\xc9\xdc\xcc" "\xbc\xd9\x53\xe3\x3e\xa0\x83\x3c\xe9\xe2\xd6\x62\x58\x95\x6c\x4d" "\xcb\x1d\x87\xf7\x5e\x03\xcf\xbf\xf8\x92\xb1\xce\xe8\x21\xe8\x37" "\xf2\x20\x95\x4d\x05\x84\x51\x51\x58\x23\xb2\x84\x0b\x2a\x22\xc9" "\x39\x17\x03\x03\x05\x4b\x4f\xe0\x92\xa8\xe8\xe5\x5f\x20\xd4\xb3" "\x34\xe7\x1a\x87\xbf\x9d\x57\x83\x44\x50\x53\xcc\x5f\x0b\xc3\x7b" "\x1c\x56\xd6\x32\xa1\x22\x90\xeb\x34\xa6\xb6\xb3\xb2\x2e\x7e\x8c" "\xbb\x5f\x98\x80\x32\x2c\x0e\x5c\x9a\x2a\xed\x13\x1b\x57\x51\xa8" "\xdd\x87\xbd\x94\x38\x96\x92\x5e\xd5\x0e\x58\x61\x12\x5e\xc6\xe2" "\xe1\x78\x62\x5c\x7f\x03\x12\x37\x31\x0a\x80\xca\xcf\x60\x33\x53" "\x4d\x9c\x3f\xbe\xaf\x0f\x45\x52\x2c\x34\x43\x7b\xc3\xc7\xa3\x85" "\xb5\x21\x9c\xb7\xd1\x2a\x43\x5b\x68\x63\xc1\xd6\x4f\xe2\xc0\x72" "\x3a\xa1\xa8\xe7\xec\x81\x91\xb0\x14\x77\xb4\xa4\x3f\x5f\x1b\x2e" "\x44\xb1\x4d\x55\xef\x25\x76\xd0\x45\xa3\xcb\x10\xe7\x47\x33\xd6" "\xeb\xd9\x58\x07\xc4\xc7\x78\x24\xc7\x4f\xc9\xbc\xd1\xa2\x44\x97" "\x77\x2b\xb0\x19\x37\x4d\x39\x41\x9f\x55\x27\x16\xa0\x74\xc9\x9d" "\xba\xd1\x71\x53\x22\x1a\x5c\xd6\x6d\xe8\x24\x19\xd6\x30\xee\x30" "\x0a\x8f\x3d\x06\xa6\xbe\xd1\xda\x8a\x2c\x06\x6a\xf8\x51\x18\x70" "\x36\x6f\x98\xd2\x8b\x7a\xa1\x18\xe9\x5b\x06\x6c\x6b\x8e\x70\xf5" "\xf8\xff\x9d\xf3\x58\x6e\x7e\x1e\x45\xfd\xf4\xe1\x25\xf7\x1e\xe1" "\xa4\x6c\xab\xf7\x1b\x86\xc9\xe9\x49\xeb\xf4\x67\x06\x6e\x7e\xea" "\xf4\x97\xfc\x20\xc7\x9c\xbf\xd2\x3d\xe6\xd5\x1d\x6d\xed\xae\x08" "\xb6\x99\x7a\x5e\xd3\x3c\xaa\xc6\x00\x76\xae\xe1\x47\xf1\x81\xe9" "\x09\x20\x93\x92\xfc\xb9\xed\x09\x83\x74\x76\xeb\x32\x38\x36\xcf" "\xf3\x0c\xbb\x2c\xcf\x8e\x0f\x15\x30\x32\xae\x0d\x75\xd1\xfe\x29" "\x28\x18\x00\x93\x9b\xfb\xdb\x6d\xb3\xc1\xfb\xe2\x2c\xe1\x2f\xf4" "\xc0\xdb\x12\xc6\x14\xb8\xca\xeb\x3e\x49\xc0\xe1\xcd\x42\xa6\x04" "\xc2\x0f\x48\x45\x6b\xac\x45\x48\x3d\x82\x02\xe9\xad\x6b\x2e\x38" "\xad\xd6\x90\x93\x50\xb9\x66\xc9\x17\x40\x6e\x6f\x0d\x4f\x16\xe7" "\xad\xae\x97\x0c\x5d\x08\x0f\x6a\x99\x14\x98\x88\xc0\xf2\xe4\xaa" "\x87\x02\x6f\x49\x55\xf9\x4e\x43\x29\xb4\x53\xbf\xb8\x29\xf7\x45" "\x37\xd1\x12\x84\xd1\x35\xaf\x91\x39\x20\xfc\x4e\x3e\xa1\xc6\xe7" "\x0f\xc1\x75\x15\x42\xee\xa7\x60\x5e\x6f\x94\x65\xc3\x7f\x12\xb0" "\xf9\x43\xfc\x58\x9a\x79\xea\x0b\xb0\xfd\x05\xd7\xf4\xd5\x87\x7b" "\x39\x03\xe4\x2d\x80\xb1\xc3\x00\xdc\x6d\x3a\x0b\x2d\xc0\xf7\xc2" "\x65\x4e\xeb\x7f\x3a\xa1\xe5\x7a\x64\x56\x3e\xe9\xc7\x90\xf2\x6e" "\x2c\x8d\xd5\xd3\xe8\x6e\x9c\x7c\x6e\xda\x52\x29\xe4\xfa\xfe\x7c" "\x98\xcb\xc2\xb1\x26\x9c\xd6\xd9\x8a\x81\x0a\xf9\x1e\x79\xae\x18" "\x25\x72\x81\xcf\xef\x42\xe1\x6e\x7f\xdf\x0c\xf0\xa6\x1d\x5f\xcd" "\x99\xed\x3e\x6f\x85\xb9\xb7\xa2\x1f\x43\x22\x2a\x4f\x10\x1e\x7c" "\xe3\x6c\x56\x3c\x7b\x1a\xc5\xfc\x3a\xe6\x1d\xac\x4b\xe7\x55\x27" "\x3e\x6c\x95\xda\xc4\x6f\xa3\x1f\xf9\xec\xe2\xdc\xd2\x02\x7d\x5d" "\xe8\xa8\x9d\x17\xc8\x0b\xaf\xd8\x45\xf2\x36\xe2\xd4\x05\x87\x90" "\x4a\x70\xe9\xf9\xc6\x7d\xf3\x80\xc7\x83\x27\xc2\xdb\xc1\x35\xec" "\xb9\xa9\x1a\x1b\xb4\x41\xe9\xb7\xd5\xb9\x17\x9b\x50\xd7\x98\xbc" "\x38\xcb\xd1\x4c\x42\x5a\x2a\x61\xfd\xb3\x8b\xab\xd0\x39\xe1\x41" "\x7d\xec\x8a\xbf\x0b\x89\x24\xa7\x2e\xc7\x26\x04\xa6\x6c\xcb\xa2" "\x53\xed\x8d\x5a\x96\xf9\x90\x0e\xda\xbd\xb3\x65\x6e\xde\x9e\xdc" "\x4c\x53\xed\x9f\x3f\x57\x5b\x1a\x1b\x9a\x01\x4c\xaa\x07\xe0\x57" "\x3c\xb3\x96\xfd\xf7\x36\xfd\x15\x71\xaf\xc2\xff\x2d\x9b\x27\x9e" "\xfc\x70\xf9\xb6\x79\xf9\xcb\x10\x14\x41\xc4\xe1\x62\x97\x90\xac" "\x47\x9b\x41\x34\x7a\x07\x63\xd2\xb0\xa7\xaa\xc4\x2b\xba\x74\x1e" "\x42\x6d\x29\x57\xe1\x90\xab\x23\x82\x6c\x82\xcd\x3b\xbb\x1e\xce" "\x7b\x98\x4e\x86\xb9\xf6\x61\x9c\x7f\xf6\x7d\x4f\xf2\xb5\x87\xaa" "\x1b\x96\x79\x5b\x71\x47\x11\x9f\x33\x06\x2e\x76\xb9\x88\x99\xc6" "\x2a\x43\xc2\x5d\x19\xe6\x31\x29\xac\x32\x21\x96\x13\x10\xb1\xb1" "\x94\xc5\x0a\xcd\x36\xac\x7b\x93\xdd\x93\x45\x18\x5e\x7d\x20\xca" "\x14\xfe\x68\x5e\xf4\x14\xa6\xe9\xfa\x5a\x3d\xda\x61\x46\x34\x79" "\x6b\x05\xf3\x5a\x19\x17\x33\x61\x55\x87\xb0\x25\x71\x20\x51\x91" "\x81\x1d\x40\x54\x88\xe1\x7a\x28\x66\xbf\x29\x1a\x63\xe3\xd6\x1a" "\x7e\xed\x84\xa0\x7a\xda\xb5\xc6\x0b\x9c\x65\xf5\xd8\x61\x8f\x79" "\xe3\x65\x41\x9b\xa0\x50\x1f\x3f\x93\xa2\x17\x17\x9d\xa0\x53\xd9" "\x16\xaf\x03\x51\x19\xd9\x60\x79\xeb\x52\x52\x26\x2e\x72\xb8\x25" "\x98\x58\xbe\xcf\xe2\xf3\x41\xe0\xcc\x2c\xa2\x17\x8d\x73\xb2\x56" "\x42\x9a\x3b\x4e\x99\xa8\xeb\xe6\x27\x99\x5d\x07\x5d\x24\xfe\xbb" "\x48\x10\xea\xd7\xcd\xa1\xda\x7a\xf2\x2c\xed\x69\xea\x54\x62\x79" "\xb1\x33\x4f\x60\x5e\x24\x2b\x3b\x46\xee\xf8\x01\xbe\x32\x9f\x1f" "\x89\xf3\x20\xd9\xdf\x7c\x28\x8c\x63\x8b\x5a\x39\x53\x1b\x36\xf1" "\xff\xa1\xf6\xed\x88\x81\x9a\x96\xf0\x48\xfc\x6d\x66\x6d\xaa\xce" "\x80\x35\xb2\xf0\xb0\x7e\xaf\x59\xb0\x9a\x47\x0d\x99\xf9\xc0\xce" "\x52\xa9\x3d\xdc\x4d\xac\xcb\xbb\x05\xbb\x8d\x01\x90\x07\x2b\x7b" "\x68\x62\xd7\xe4\x5b\x2f\x5d\x11\x22\x6d\x4b\x81\xbf\x48\xe0\x1d" "\x04\x03\x0b\xad\x77\x0e\x62\xa1\xbe\x1d\x30\x9a\xff\x40\x82\xd7" "\xc3\x10\xba\x5f\x89\x57\x75\x84\x19\xcf\xa5\x25\x50\x96\x49\xd7" "\x4f\xad\xfe\x5a\xf2\xe5\x55\xb1\x54\x21\x9e\xb4\x15\x17\x8f\x67" "\x9d\x69\xda\x9b\x27\x0a\xba\xff\xda\x38\x82\xf2\x3a\x62\x6f\x79" "\x15\x37\x4c\xea\x05\x2b\x33\xd2\x6d\x58\x96\x8b\xa9\x2f\x7b\x08" "\x08\x7a\x0d\x1f\x59\xc9\x32\xa2\xf7\x40\xe1\x3d\xb1\xaa\x8b\x8a" "\xdb\x7c\x87\xfe\xd2\xa1\x19\xf7\x65\x0a\xd2\xe5\xfa\xcf\x44\xde" "\xbb\x42\x7b\x36\xc3\x8e\xbc\x60\xf3\xc8\x44\xbb\x75\xab\x78\x1f" "\xa5\x5e\x01\x2c\x5d\x9c\xb6\xf8\xcc\x52\xcf\x63\x0c\xfe\xe3\x8a" "\x36\x84\x07\x64\x4d\x9a\x4f\xea\x70\xa9\x7d\x7a\xaa\x65\x6f\x34" "\xe8\x7e\x89\x00\x41\x06\x47\xd6\x7f\x57\x3f\x60\x6d\x09\x5a\x5f" "\x7d\x87\x14\xce\x79\x88\xab\x74\x6c\x98\x92\xfd\x20\x46\xd3\x44" "\xfc\xe6\x44\xb0\x03\xda\x57\x61\x3f\x7e\xa9\x91\x60\x9e\x11\x55" "\xff\x76\x0b\x14\x29\x72\x96\xd9\x00\xff\xe9\x53\x19\x5f\x68\xec" "\x86\x01\xdc\x15\x4c\x59\x7e\x8a\xb1\x73\x66\x32\x9e\xf8\x46\xbc" "\xa1\x5b\xf5\x29\x44\x29\xd4\xd1\x15\x32\x82\xe9\x84\xee\x70\xa5" "\xc9\xcd\xe9\xea\x5c\x56\x91\xca\x73\xf8\x4a\x0f\x5c\x0b\x9c\x6d" "\x3b\x17\x03\x03\x05\x4b\xb1\x6b\x56\xf7\x0a\x4a\xf4\x4a\x40\x46" "\x30\xb6\xaf\x59\xa7\x9f\xe3\x36\x17\x9c\xd5\xd7\xa8\x10\x20\xb2" "\x71\x50\xc3\x6b\xe4\x70\xc7\x2e\x27\xd6\x3b\x8e\xca\x24\x28\x01" "\x97\x9b\x70\x02\xae\x87\x29\x75\xe8\x6b\xa2\xb1\xf1\x73\x3d\x3f" "\x24\x04\x60\x2d\xe2\xfb\x95\x5b\x93\x22\xb3\x67\x93\xdd\x4c\xc8" "\x29\xdf\x60\xde\xe4\x9b\x6c\x46\x38\xd3\x82\x59\x75\xbb\x23\x37" "\xd7\xcf\xf1\xc1\x77\xe8\xd1\x8b\xe5\xe0\xe5\x26\x63\x2d\xce\x06" "\x0d\xe9\x0b\x52\xf6\xfd\x2e\x67\x7f\xc1\x41\x8f\x51\x73\x39\xda" "\x4c\x65\xd2\x14\xab\x7d\x18\x03\xd2\x94\x80\x11\x6f\x66\x8a\x4b" "\x6b\xe5\xac\x6d\x17\xbb\x79\x8a\x8b\xd2\x7d\xf5\xc2\x41\x01\x32" "\x2d\x9f\xde\x41\x26\x28\xc0\x0b\xa9\x07\x92\x42\x12\x07\x90\x77" "\x9f\xa5\x99\x1e\xe6\x7c\x39\xef\x6f\xf8\x9a\x18\xb8\x23\x6e\xdd" "\x8b\x81\xf0\x07\xb3\x70\xea\x32\x19\xb0\xd4\x7c\x78\xc1\xd2\x52" "\xb1\xa4\x41\xf1\x79\xf2\x97\x29\x0d\xa6\x07\xc2\xee\x63\xc0\x55" "\xcd\x14\x5c\x34\x14\xb2\xac\x90\x49\x78\xe6\x3d\x7c\xc2\x79\x16" "\x16\x3a\xd6\x16\x1c\xf5\x6b\xe8\x8b\xfa\xb9\xde\x6f\xa6\x63\x27" "\xc6\xf8\x08\xb2\xcb\xf2\xbf\xa0\x29\x7b\xaa\xba\x03\x9c\x3c\x83" "\x51\xb5\xef\x66\x97\x5e\x95\x90\xd8\x80\x20\xc3\xa0\x23\xa9\x04" "\x5d\x15\x03\x3e\x4f\xef\x3f\x09\xec\x9f\xe4\xdf\x5c\x3d\x5c\x9e" "\x35\x69\xe2\xc9\x2d\xc8\xcb\xfe\xa7\x54\x36\x6d\xe3\x44\xe3\xb0" "\xff\x74\x55\xb3\x41\x27\xc3\x5c\xe6\x7f\xcb\xfd\xc3\x2b\x9b\xdb" "\xe6\xcc\x87\x6b\xf0\x9e\x2d\xbe\xb6\x8f\xd4\x7c\x7a\x72\x74\x3d" "\x9b\xb0\x74\x33\x22\x71\x2b\x92\x63\x55\x1f\x0f\x5f\xf2\x65\x7c" "\xa3\x9e\xe9\x42\xf4\x05\x16\xca\x5e\xa8\x61\xbc\x1e\xbd\xca\xdf" "\x3c\x7d\x10\x87\xe4\xbb\x41\xf0\x34\x4e\xc6\x00\xf6\xdf\xe7\xf1" "\xeb\xda\x86\x0a\x8c\x9c\xb1\x97\xc4\xa7\x2c\x4b\x96\xa6\xbf\xc0" "\x85\xaf\x69\x2d\x28\x63\xbd\x5f\x85\x1c\x00\x8a\x8a\xd6\x9f\x60" "\xd6\xd7\x8f\x75\x62\x74\xb7\x32\x5d\x0b\x4f\x29\xb8\x72\xfa\xf3" "\x80\x47\x3a\xbb\x08\x27\x79\xa7\x2c\x6a\x63\x66\xaa\xe2\xc5\x17" "\x8d\x1b\xe5\x6b\xb7\xd1\xed\x18\x9b\xde\xfc\x92\xc8\x3d\x30\xff" "\xf3\x33\xba\x01\x0c\x6a\x19\xc2\x93\xed\x5c\x19\xc7\xb7\x45\x99" "\xf4\x46\x42\x6a\xe0\xd9\x5e\x99\x64\x24\xa2\xd2\x34\xb6\x46\x88" "\x2c\xdd\xc0\xbc\x7e\xd1\x57\x72\x2c\xb5\xa2\x72\xbd\x8c\xe0\x2a" "\x03\xe7\xe9\x84\xe4\x90\x1c\x3a\xa8\x79\x48\xe1\xc1\x72\x6a\x83" "\xf8\x1f\x39\xb2\x08\x70\xba\x96\xdd\x9e\xd0\x4d\x9f\x2d\x9a\xdc" "\x11\xe2\x4d\x27\x41\xce\xec\x6e\x4f\xff\x9a\xd5\x09\xcd\x38\x9b" "\xe6\x4f\xc5\xfc\xac\xeb\xb8\x76\xaa\x42\xc4\xeb\x9a\xff\xf8\xd0" "\x07\x25\xe8\x08\xa5\xd1\xa0\x59\x8c\xca\x85\x5f\x33\xde\x97\x3b" "\xa5\xe9\x81\xd5\x90\x7c\xeb\xd9\x50\x84\x0f\x51\xc2\xdc\x2a\x21" "\xe9\x2e\xb0\xcd\xc8\x26\x6e\x81\xbc\x5a\x63\x9f\x0b\x4c\x67\x97" "\x0e\xfd\xb8\x03\xd4\xd5\x1a\x53\x2c\x59\x7b\xcd\x14\xd7\x8b\x09" "\xee\xbb\xad\xd4\x02\x47\x4a\xb0\x23\x4a\xd2\xdb\x40\x7d\x03\xf7" "\x46\xb8\xf2\x18\x34\x42\x52\xf4\x77\xc2\xc8\x91\xb0\x6b\xb9\xb6" "\x87\xda\xbb\xb8\x1b\x4c\xf0\x2b\x1d\x4e\xff\x87\xd7\xb8\x10\xa7" "\xaf\x08\x20\xa5\x14\xb6\xe7\xe8\xc9\x26\xa6\x29\x6e\x5f\xbf\x1f" "\x4e\xcd\xd7\x6f\x7a\x89\x28\x5a\x1c\x9c\x82\x88\x05\x50\x75\x8e" "\xa3\xec\x50\xe7\xa8\x69\x4a\x17\xd4\xab\x92\xca\x35\x42\x5f\xa4" "\x40\x07\xe6\xbb\xf6\x7b\x57\xe9\xa8\x29\xae\x98\xf3\x0a\x8e\x78" "\x69\xaf\xc7\x63\xe7\x15\xd5\xfc\x4f\x51\x2f\x5f\xee\x6d\xec\xe8" "\x58\xc3\x7c\x6c\xbf\x9d\xb8\xc0\x40\xb4\xe5\x17\xc5\x6e\x63\x32" "\x78\x34\x94\xfe\x0c\x6c\xc3\x1d\x38\x3e\xe7\x8b\xd9\x48\x7e\x39" "\x39\x25\xcd\x06\xa2\x94\x37\xb9\x49\xf9\x51\x84\x95\x1f\x80\xcd" "\x6c\x9e\x4f\xc0\xc1\x2a\xbe\xbd\x6e\xc0\xb0\xb8\x1c\x5e\xbb\xf5" "\xdb\xe1\x74\xe0\x86\x15\xa9\x77\x5c\x85\x50\xe1\xa2\x6b\xe4\xdf" "\x5f\x81\xe6\xd6\xdb\xae\x3b\xed\x51\xed\xd9\x3b\xa3\xf4\x1c\x79" "\x1d\x90\x0d\x14\x3d\x6b\x94\xc2\xf3\xdc\xe0\x5a\xb7\x9b\x2d\xdb" "\xa2\xea\x66\x53\x13\x3f\xc7\x3d\xeb\xb7\x85\x00\xf3\x85\xc6\xa2" "\x5d\x0d\x5f\x79\xab\x78\x88\x7c\x5b\x43\xf1\x4f\xaf\xdd\xfd\x80" "\x89\x15\x00\x71\xc1\xaf\x86\xf7\x8a\x9c\xd0\x71\x32\x8d\x82\x43" "\xfe\xa9\x66\xaf\xed\x17\x31\xf7\x07\xd1\x55\x60\xbb\xd5\x31\x7e" "\x1c\x3a\x37\x2d\xb1\x59\x85\x50\xe9\xac\x0f\xef\xcf\xbd\x0a\xee" "\x7b\xee\xd3\xe9\x27\xd1\xbb\xfc\x76\xe0\x7d\xc2\xf5\xc8\xf0\xc6" "\x15\x2c\x06\x0a\xdd\x86\x59\x69\xd0\x9e\x00\x68\x7a\x7e\xdf\x57" "\xb6\x49\xc2\xba\xcc\xf1\x1f\x69\x89\x44\xdf\x35\xce\x98\xf1\xbe" "\x85\x11\x28\x8a\xdb\x6c\x38\xf7\x6a\x27\x72\x78\xfc\x4f\x7c\x8b" "\x41\x23\x2c\x90\x7d\x37\x70\x48\x4f\xb0\x4c\x33\xcd\x73\x6b\x85" "\xfd\x61\xe4\x62\xdd\x63\x72\x20\x73\x53\x01\x0f\x4f\xe4\xb4\x8b" "\x33\x5f\x64\x03\x03\xe7\x15\xc9\x50\x71\xd6\x9e\x37\x49\xc4\x3e" "\xf1\xef\x57\x68\xbd\x9a\xd5\xe8\x3d\xaf\xe5\xc8\x42\xc6\xed\xf0" "\x6a\xd8\x4e\x90\xa1\xe2\xb7\x81\xbf\xa5\x39\x46\xee\x45\x9f\xd4" "\x91\xcb\x74\x10\x81\x88\x9c\xe8\x94\x17\x05\x33\x39\x96\x4f\x21" "\x9f\xd2\x84\x62\x9b\x1e\x29\x04\x85\xdd\xbe\x47\xdd\xc0\x6c\xfa" "\x1e\x00\x41\xf7\x76\xdc\x10\xc9\xbb\x1c\xbd\xc5\x7b\x59\x0c\x5b" "\xc9\x7e\xca\x9e\xf8\xcf\x00\xc1\xf6\x73\x44\x38\x44\x65\xaa\x57" "\x72\x6c\x3a\x22\x70\xbe\xe7\xc4\x23\xca\xbb\x14\xcc\x5a\x3e\xc3" "\xf4\xf7\x11\x92\x05\x77\x5f\x64\x7b\x5c\x41\x92\xa2\xd2\x42\x96" "\x85\x17\x26\xd0\x49\x93\xa6\x16\x80\x0d\x44\x75\x52\xd8\xa3\xf0" "\x7f\xbb\x8d\x96\x60\x61\xf9\xe3\x11\xfd\x32\xf9\x8e\xc5\x6f\x60" "\x17\x98\x14\xcf\x8e\x0e\xd6\x40\xbd\x05\x67\x1a\x3a\x6c\x5d\x29" "\x00\x72\xc8\x79\xde\x06\xe1\x61\x8d\x54\x0b\x0a\xe8\x07\x52\x97" "\xaf\x3e\x7d\xf7\x08\x86\x3a\x6b\x10\xde\xc3\x9c\xad\xe4\x9b\x12" "\x8a\x63\x85\x8a\x38\x59\x12\xd0\x8b\x1a\x98\xe1\x65\x14\x5c\x1d" "\x8e\xdf\x77\x84\xde\x66\x37\x4e\x0a\x25\xea\xd0\x74\x17\xe0\x34" "\x17\xeb\xe2\x4d\x3d\x21\xe4\xe1\x75\x40\x36\xe1\x6c\x32\x8b\x29" "\xb6\x87\xd5\x49\xc6\x6a\xfe\xa3\x58\x85\x7e\x67\xae\x74\xe3\xc2" "\x3d\x17\x03\x03\x05\x4b\x7a\xa0\xc3\x74\x01\x15\xc9\xa6\xee\xb1" "\x6e\x6a\xa8\xbf\x2b\x35\x00\xa9\xf9\x2c\xaf\x29\x4d\xad\x6a\xc7" "\xba\xda\x74\xaf\x29\x24\x49\x15\xb2\x9b\x25\x3c\xae\xa4\x64\xe2" "\x27\x32\x8b\xf7\x79\x05\x47\x69\x5a\xae\xaf\xdb\x77\x93\x07\xd3" "\x9e\x8e\x66\x97\x39\x0e\x9a\x33\x12\x4e\xed\x26\xe3\x29\x12\xb6" "\xe3\x67\xf4\x82\x6b\x4c\xc9\xcb\x90\xf1\xac\xd3\xff\x33\x07\xa7" "\xe2\x21\xe4\x0d\x40\x5a\x73\x3a\x3c\x34\x16\xf0\xbd\xfe\xe9\x4a" "\x69\xa0\xb8\xc0\x7c\x94\x5e\x0d\x76\xb7\xcf\x6d\xc6\xca\x56\x07" "\xf9\xeb\xe2\x5a\x83\x0d\x45\xf8\x02\x21\x2d\xf1\xf1\x3f\x34\x16" "\x3d\xbf\x2c\xba\x26\xe8\x31\x9f\xa2\x58\xfe\x84\x97\x19\x39\xbc" "\xca\xf2\x18\x80\x73\xda\xbe\xb8\x01\xfd\xce\xdb\x5d\x40\x48\x62" "\x3c\xa8\xfa\x2a\xdf\xc9\x9c\xa4\x50\x02\x9a\xa0\x0d\x7c\xba\x6b" "\xa3\xb7\x4a\x9e\xd7\x71\x90\x5c\xfd\xc1\x98\x1a\xaa\xcb\x47\x6a" "\x5a\x8b\xa3\x65\xfc\x68\x8b\x46\x9e\x9a\x51\x5f\x74\xcd\x24\x9b" "\xb5\x22\x44\x5b\x8b\x9f\xc3\x8b\x44\x25\x86\xc1\x1e\xee\xa4\x47" "\x1d\x71\x5c\x4a\xa5\xd9\x1c\x4c\xa9\x22\xfb\xb0\x84\xc1\xf9\xd3" "\x1b\xf4\xfc\xcd\x1c\xaf\x5e\xa5\xa7\x93\xcb\x2b\x2f\x6d\xd1\x4d" "\xdb\x7b\x1c\x70\xba\x42\x32\xfe\x55\x0f\xa5\xab\xbc\x27\x88\x5f" "\x63\x1a\x46\x99\x64\xc7\x7b\xc4\x64\x9c\x10\x72\x1c\x48\x2c\xf2" "\x0f\xba\xa9\xc0\xcd\x5a\xea\x18\x74\x7a\x41\x49\x5a\xc9\x99\x39" "\x55\x07\xb9\xc6\x22\xe6\xed\x04\x70\xdc\xe0\x56\x57\x38\x86\xf4" "\x27\x1d\x01\x06\xf5\xc8\x73\x50\x1d\xeb\x67\xbd\x47\x67\x9e\x1f" "\xa8\x86\x5c\xdf\x0a\xf9\xd5\x39\x93\x02\x3e\x39\xde\x73\xd9\xbc" "\x6f\x55\xce\x61\x08\x9c\xd8\x01\x7f\x48\x89\x9a\x8e\x94\xe3\xce" "\x12\xf8\xe4\x69\xa3\x5a\xb9\xa4\x5d\xcf\x4e\x5a\x6a\x7e\x97\x6c" "\xd6\x3d\xf4\x15\xf0\x37\xe0\x6a\x16\x7e\xec\x90\xe1\xea\xdd\x6d" "\x29\x27\x06\x4e\x08\x02\xb8\x87\x7b\x0b\x18\x71\x7c\xe9\xf6\x65" "\x98\x4d\xbe\x1f\x1d\x9e\x5e\xc5\xb3\xdd\xa8\xfa\x10\x5e\x6b\x89" "\xba\xf0\xd3\xfb\x7c\xb6\x5a\x74\xbd\x80\xa4\x2d\x87\x62\x08\xee" "\x86\xb3\xdf\x5e\x7d\xeb\xe4\xc4\x1f\x3a\x4c\x03\x21\xbc\x1c\x99" "\x96\xca\x7d\xed\x46\x4f\x58\x8f\x3b\xf0\xfb\x43\xc1\x14\xfa\x89" "\x97\x8c\x8a\xa6\x77\xdc\x3b\xe2\xe1\x3f\x01\xda\xbf\xfa\x54\xcd" "\x45\xbc\xf5\x6d\x75\x9f\x17\x05\x64\xad\xb4\x25\x3b\x79\xcd\xb2" "\x61\x9e\xb8\xd1\x21\xda\x83\x96\xfa\xe9\x3b\xa4\xa9\x24\x80\x91" "\x8b\x2d\x6d\xd6\x94\x4e\x18\x99\x88\xaf\x09\xe0\x50\x4a\x83\x7c" "\xdb\xc0\x2b\x86\x02\xf0\xf7\x22\xd3\x6b\x2d\x07\x43\xf2\xd7\x78" "\x0a\x0c\x88\x7a\x05\x2d\x55\xf3\x0e\x63\x94\x14\x40\x72\xee\xa4" "\x9d\x68\x1e\x75\x1c\x14\x7e\x07\x4c\x7c\x36\x0f\x0b\x8c\xcc\x26" "\xa5\xfc\xe2\x86\xbf\x0f\x93\x77\xab\xf7\x2f\x26\x6e\x5e\x39\x90" "\xe2\x10\x1c\xaf\x27\x8e\x76\x3d\x63\xff\x4c\xbe\x39\x10\xfa\x97" "\x55\xe0\xe3\x6e\x76\xcc\x06\xbd\xb4\x05\x18\xdb\x0b\x97\x47\x19" "\x88\x1d\x0f\x57\xe0\x46\x07\x87\x4c\x09\x2d\x46\x2a\xb5\xda\x22" "\xd0\x11\xae\xa1\x9e\xad\x03\xba\xaa\x8c\x98\x5f\x80\xd9\x11\xff" "\x49\xbb\x87\xe2\x46\x9d\xe1\xe6\xab\xf1\xbc\x14\x82\x9b\xc4\x77" "\xcd\xf8\x07\x14\xf9\xaa\xc4\xa0\x7b\x0a\x46\xe0\xb5\xf2\x6b\x7c" "\xcf\xb8\x3a\x84\x1a\xa3\x28\x36\x7e\xdd\xe7\xea\xd1\x5d\xaf\x38" "\x8e\xa0\x4c\x72\xb4\xbb\x05\x91\x19\x81\x86\x97\x22\xe4\xc4\xeb" "\x8a\xe3\x20\x62\x45\x24\xf0\x1f\xc3\x41\xc2\xe7\x5e\xbc\xdb\x98" "\xb4\x6b\xec\x0b\x9c\xcf\xa1\xe3\x37\x96\xc7\x92\xfe\x74\x0e\x38" "\xbd\xcb\xdd\x73\x93\x3c\x4b\x8f\xe6\xcc\x86\x17\x45\xbf\x48\x09" "\xcf\x33\x5f\xc1\x81\xe1\xa8\xb5\x79\xef\x3c\x9d\x1d\xf6\xbf\xec" "\xc1\xbb\x42\x82\xea\x4d\x7d\x9d\x2a\x52\x45\xaf\x04\xfa\x3b\x46" "\x5d\x23\xbd\x62\x07\xf9\xbe\x67\x8e\xb0\xb3\xe7\x60\x62\xb6\x1c" "\xd8\x99\x2b\x2a\xff\x36\xb4\xd2\xa8\xdc\x8b\xb8\xd4\x08\xdb\x81" "\xd2\x4d\xda\xc1\x33\xa6\xe5\xc8\xfe\x55\xb7\x20\x07\x73\xcd\x41" "\x79\x81\xff\x19\xbf\x84\xa4\x69\x5c\x65\xe2\xc9\xae\x3d\x0f\x68" "\xab\x65\x74\xdb\xee\xed\x75\x1e\xc3\xaa\x6c\xc5\xef\x25\x26\x9b" "\xd8\x36\xd0\x82\xc7\xd8\x05\xc9\x47\x0f\xf1\xf9\x18\xb6\x64\xfb" "\xd2\xcc\x83\xe0\x2e\x01\x2d\x58\x59\x8b\xba\x91\xc1\x99\x42\x99" "\x93\xc1\x26\xd5\x52\x03\x7e\x49\x39\xd9\x13\xf2\x2c\x18\xec\x89" "\x2f\x0a\xe1\x3f\xb6\x79\x73\xa6\x82\x2a\x21\x9f\xf4\x33\x49\x13" "\xb6\x09\x58\xca\x93\x39\xa6\x3a\x62\xd6\x90\xd7\x06\x2c\x1e\xf5" "\xcc\xc1\xac\x6e\x27\x78\xc2\x11\x1a\xf4\xbf\xd7\x5a\xd6\x88\xbc" "\xfb\x81\xb1\xa8\xc1\x61\xbf\x9d\x02\x89\xce\xf3\x85\x62\x04\x2e" "\x90\x9d\x92\x11\x98\xe6\x05\x21\x7a\x83\x94\x22\x0c\x95\xcd\xe2" "\xa7\xc3\x66\x0a\x1c\x28\xcc\x05\xd2\xde\x96\x9f\xfb\xd3\x32\xec" "\x51\x3e\x67\x96\x2d\x24\xad\x9c\xe4\xee\xa7\xc1\x55\xf0\x86\x1b" "\x5d\x5d\xef\x95\x5a\xab\x39\xbf\xb1\xe9\x8e\x82\xef\xb2\xb0\x8b" "\x2e\x88\xdf\x45\xe9\x09\xe0\x4c\x50\xec\xb5\xa1\xa8\x68\x48\xa9" "\xf3\x77\xf5\xaa\xd0\x8f\xba\xbf\x89\x97\x9f\xd2\xff\x0a\x73\x95" "\xe3\xde\x2a\x47\x76\x2f\x56\x37\xef\x44\x7d\xc4\x61\x23\xac\xd6" "\x50\x5e\xd6\x82\x71\x90\xfe\xd8\x85\xd2\xf6\xaf\x8d\x5e\xfc\x09" "\xee\x61\x34\xa2\x8c\xe6\xad\x49\x8e\x49\xc1\xe6\xad\xf0\x93\x28" "\xb7\xa9\xe6\x4e\x4d\xd9\xb2\x20\xce\x94\x78\x8b\x72\x8e\x39\x17" "\xa5\x6a\x6e\x85\x83\x4e\x72\x0f\x9e\x55\xaa\x25\x6e\x38\xf6\x9f" "\xc4\x2b\xdf\xe4\x5d\x06\xdb\x12\x33\xbd\x3e\xf3\x49\xbb\x1c\x0c" "\xe1\x58\x49\x07\xe7\xcb\xdb\x68\xd5\x03\xe7\xcb\xfc\xe6\x21\xaf" "\xeb\xb9\x7c\x80\x81\x8a\xe9\x92\x4b\x19\x84\x02\x59\xdf\x47\x47" "\xc2\x63\xb8\xfa\x31\xc3\x56\xf3\x6f\x4d\x88\x97\x77\x9d\xfb\x3a" "\xdd\x30\xdd\xd9\x13\x11\xdf\xa6\xe8\xe9\x6a\x75\xde\xdd\xd8\x1e" "\xf1\xc1\x02\xce\x0e\x89\x68\xc6\xd5\x66\x29\xee\x58\xe3\x19\xa0" "\xc3\xe1\xfc\x0f\xaa\xb3\x68\x43\xd8\x06\x20\x5f\x0e\x13\xe6\xd3" "\x7e\xcc\x0d\x84\x68\x16\x9e\x22\xf9\xc6\xae\x2b\xe1\xfd\xe5\xa7" "\xba\x65\x12\xf2\x64\xbc\x78\x02\x4a\x9d\x76\x7e\xaa\x83\xcf\x0f" "\xa5\xb6\x1a\x47\x7a\x16\x50\xd7\x40\x41\x86\x98\x75\xca\x7b\x79" "\xc2\x17\x03\x03\x00\x58\x01\x13\xe8\x5c\xca\xef\xcf\x13\xe7\x31" "\x1a\x59\x81\x57\x8d\x8f\x15\x2a\xaf\x2f\xe3\x37\xf8\x1e\xf5\xe6" "\x91\x5d\x6a\xde\x22\xd3\x57\x7e\x90\x5f\x17\x63\xc2\x98\x0e\x50" "\x46\xf7\x04\x7d\x34\xc5\xd7\xb8\x6c\xcf\xda\x59\x43\x66\x6a\x49" "\x07\xe4\x30\x1d\xb9\x18\xb7\xf0\xe0\xbb\xca\xc4\xd9\x3d\xa6\x87" "\xd8\xd2\x0e\x11\xf6\xe4\x23\x55\xf0\x54\xd1\xeb\x93\xaa\x17\x03" "\x03\x00\x3e\xa6\xec\xb5\x00\x2d\xca\x7a\x32\xbf\xf0\x82\x38\xe7" "\xd1\x97\x8d\x71\x61\x5b\x01\x4b\xa6\x03\xc1\xf5\xcb\xa4\x3d\x6a" "\xd9\xa4\x77\x75\xe0\x36\xd1\x49\x2a\xae\x29\x5a\x54\xa1\x1b\x00" "\x43\xfd\x8e\x60\xd1\xe7\xe4\xf7\x94\xb3\x5a\x1f\x62\x78\x5a\xe9" "\x0b\x17\x03\x03\x02\xfa\xf8\xa2\x71\x92\xf6\x0b\x21\x4d\x79\xa1" "\xb8\x27\x14\xe7\xd3\x21\xc4\x09\xcd\x7e\x2e\x65\xf1\xaf\x09\x93" "\x1a\xfb\x0c\x97\xd7\x87\x7a\x88\xfe\xd4\xfc\xd5\xa0\x81\xd4\x11" "\x40\x83\x17\x42\x58\x07\x0f\xf1\xa8\x6e\xdb\xa4\x8c\x51\x50\xc3" "\x74\xa6\xf0\xb1\x87\x89\x75\xad\xa8\xf2\x9d\x0f\x4e\x16\x54\xc7" "\xaa\xa9\xb7\x02\x1c\xa7\xe7\xe7\x1e\x70\x88\x1b\xb0\x79\x3e\xe2" "\xde\x7d\x42\xd3\xc6\x4e\x92\x1d\x3a\xe3\xc2\x8e\x0c\x50\xbd\xfd" "\xbe\xe6\xdc\x63\x93\x77\x79\x7d\x51\x63\x59\xad\x5c\xc8\x0d\xd0" "\x0f\x83\x57\xbe\xd8\x80\x95\xbb\xde\x40\xb1\x11\xc9\x54\x7e\x73" "\x23\xfc\xd3\x74\x97\x0f\xdd\xd1\xaa\x95\xc6\xf5\x29\xa3\x85\xd4" "\x92\x94\xc3\xf1\x0c\x92\x9c\x9a\x3e\x1c\x9e\x4f\x1e\x5a\x05\x5c" "\xe2\xed\x03\xb0\x1b\xb2\xbe\x26\x25\xd1\x18\x63\x3f\x89\xf2\x0e" "\xed\x63\x01\xdf\x7f\x78\xe2\x65\xd1\xa5\xa0\xe5\x1d\xf1\x16\x50" "\xe5\x48\x9e\x55\xeb\xb4\x9a\xa9\xb2\xa4\xdf\x72\x77\x04\xfa\x28" "\x58\x98\xcf\x17\xae\x47\xef\xdb\x1f\x36\x28\xd3\x58\x69\x58\x7e" "\x51\xb8\xc2\xd1\xc8\x6d\x99\x56\x39\x39\x45\xac\xfd\xde\xf0\x97" "\x96\x68\x12\x37\xe0\x3b\xa0\x70\x41\x96\xe5\xbd\x28\x7c\x9b\x7a" "\x7d\xe4\xfe\x5e\x36\x1a\x2f\x85\xc5\x65\xa2\xb8\xb6\x20\xad\xff" "\x19\x55\xfe\xa7\x73\x0e\x6f\x90\x25\x4c\x85\xda\xbf\x45\x73\x21" "\x29\xc7\x7a\x6f\xa7\xa9\x71\x13\xc5\x12\x5d\xe9\xf6\xec\x38\x86" "\x0c\xba\x33\x39\x92\x1b\x32\x46\xd9\xd8\xe5\x37\xa2\x27\x64\x9b" "\x06\x3d\x38\xff\xd5\x41\x84\x63\x6a\x0f\xfd\xec\x2b\x16\x85\x7f" "\xa5\xa5\x0b\xd9\xc1\x96\x00\xb9\x30\x91\xb1\xba\x00\xb1\x07\xa0" "\x03\xe9\xc5\x60\x07\x71\x53\x88\xcf\xd5\x4f\xdd\x92\xe8\xd1\xb3" "\xac\xb8\xaa\x7a\xd5\x1f\x49\x69\xd8\x8b\x39\x48\xc9\x82\x05\x5c" "\x25\x1a\x84\xc8\x43\xf3\x7a\x03\xfa\xf3\x6e\xff\x56\xc2\x14\xca" "\x31\x4b\xb7\xf6\x19\x3f\x59\x5a\x96\xb0\xb2\xdf\x3f\xfe\x10\x1a" "\x01\xcb\xe9\x6c\xea\xa4\x92\x02\xfe\xed\x0e\xed\x3d\x7a\xa1\xba" "\xf5\x53\xe5\x49\x73\xa1\x25\x8d\x47\x83\x44\xfe\x22\x06\x7b\x0c" "\x47\xba\x79\x4a\x2e\x6d\x61\x3c\x8e\xff\x6f\x5a\xc7\x5a\x55\x9f" "\x48\xf3\xa4\x5b\x4f\x39\xad\x8f\x07\x53\xea\x4c\x0d\x16\xf4\x62" "\xcb\xd0\x87\xc2\xc2\x18\x92\x27\xfa\xe4\xed\x76\xe2\x31\xce\xab" "\x08\xcc\x15\xb3\xc3\x57\xe5\xcf\xcc\xa4\x6d\x4b\x73\xb6\xea\x82" "\x56\x79\x31\x6f\xdf\x6c\xa4\x0b\x43\x21\xbe\x27\x72\x06\x1b\x31" "\xbc\x95\xb6\xb7\x6a\xdf\xee\x87\x05\x28\x88\x31\x6a\x6f\x5e\xdd" "\xdb\xe2\x34\x7d\xdb\x35\x5b\x75\xf7\x12\x20\x8e\x9b\xc2\x8a\x0a" "\xad\x12\x27\x5d\x20\x2d\x1b\x31\x3f\x53\x81\x39\xd2\x40\x16\x29" "\x1a\x39\x6a\x46\x87\x5e\xbf\xd0\x22\xa3\x22\x7e\xf6\xbe\xc7\xfb" "\x28\xd5\x20\x72\xe5\x05\x49\xef\x59\xdc\x7b\x5f\x5d\x5c\x2e\x63" "\x08\x7a\xe0\x48\x13\xe1\x77\xbc\xbe\x7a\xea\xd7\x53\xc6\xd6\xae" "\x3a\x03\x3d\x19\xae\x09\x8b\xed\xb0\xbf\x04\xa0\x99\xd9\x7d\xe2" "\x87\xcd\xb7\x13\xe3\x0f\x51\x5a\x27\x29\xfe\x40\x58\x0e\x64\x2d" "\xe6\x57\xc8\xc5\x6e\x56\xaa\x06\x8b\xf5\xf5\xb4\x41\xb7\x6a\x10" "\x8b\x02\x44\xfa\x66\x77\xeb\xc2\x0c\xc9\xe3\x72\x16\x0c\x98\x3f" "\xb3\xd1\x0a\x94\x2e\xc9\xd5\x8c\xff\x08\x31\x4f\xd7\x10\x20\x9c" "\xa1\xcf\x52\x74\xf3\xa5\x04\x29\x31\xe3\xf3\xce\x36\xf5\x0f\x9e" "\x8c\xb8\x88\x71\x61\x84\xb8\xec\xd9\xa1\x45\x9e\x20\xa6\xd5\x94" "\x3c\x06\x0c\xd1\x5b\x95\x0d\x38\x2f\xcc\x25\x45\x4f\x78\x41\xa2" "\x17\x03\x03\x00\x3f\x98\x62\x5c\x36\x38\xab\xdd\x46\xb1\x5c\xbc" "\x34\xcc\xa1\x29\xb3\x58\xf0\x40\xf6\x1d\x6e\x8f\x6f\xee\xb6\xb8" "\x34\x8f\x89\xe7\x4e\x63\x52\x3c\x03\x63\xd9\x93\x78\xb7\x5b\x74" "\xb0\xa1\xd4\x15\xf7\xeb\x77\xbc\xec\x3c\x8f\x01\x18\x71\x18\x42" "\x21\xf6\x0c\x40\x17\x03\x03\x05\x4b\xf5\xe2\xcb\x37\x60\xc4\x70" "\x30\x62\xf6\x9b\x22\x1a\x6a\xc6\x31\xb1\xd4\xc7\x6f\x41\x99\x67" "\x2b\x08\xcf\xe3\xe8\x77\x1c\x5c\x37\x37\x4c\xd1\x00\x20\xa5\xc1" "\x60\xc2\x34\xb0\xdd\x9d\x16\xef\x03\x60\x4b\x24\xce\xbf\x71\x99" "\x42\x02\xb1\x14\x38\xe6\x04\xa1\xa6\x74\x11\x27\x18\xe0\xe5\x36" "\xeb\x94\x1d\xe0\x5a\x50\x2b\x53\x85\xc6\x56\xe2\xf7\x7d\x44\xa5" "\xc6\x88\xb7\x4d\xad\x79\xdb\x57\xb5\x02\x78\x79\x9e\x43\x6d\xc7" "\xcd\x25\x8c\x11\x50\x9d\x46\xa7\x23\x76\xc9\x5d\x02\x47\xad\x15" "\x94\x18\xa2\x82\x82\x06\x70\xe2\x57\xc3\x08\xf3\x4f\xc3\xe2\x17" "\x26\x8f\x6b\x88\x95\x89\xe8\x6b\x97\x7f\x10\xf7\x4f\xe3\xeb\xbb" "\x68\x1a\x83\x40\x12\x5e\x14\xa9\x28\x25\x56\xc3\x14\x78\x93\xe1" "\xae\x09\xf0\xf7\x38\x03\x2a\x63\x50\x1f\x5e\x5b\x53\x85\x59\x43" "\x69\xa2\x6f\xaf\x55\x5a\xf5\xd6\x82\x3d\x78\xd8\x55\x22\x53\xd2" "\x3c\x90\xe3\x68\xd7\x31\xb3\xcc\x3d\x18\x2e\x70\x49\x82\x86\x58" "\x89\x0c\x50\xf8\xec\x28\x48\x62\x0d\xde\x9a\x2a\x94\x25\x41\x11" "\xa8\xc6\x34\xfa\x65\x38\x97\x24\x5d\x61\x6d\x37\x01\x2b\x89\xb9" "\xd9\x8c\x34\xbf\x3c\x59\xbc\x75\xc4\x31\x6f\xa0\xf6\x5e\x23\x22" "\xfc\x74\xc5\x56\x60\x0a\xef\x39\x68\x20\xeb\x8d\x3f\xef\x12\xb2" "\x4c\x0d\xfb\xbb\x16\xda\xb1\xd8\xe9\x5b\x1b\xb4\x92\x8a\x8f\xc7" "\x7c\x39\x57\x75\x95\x12\x78\xdc\xec\xc6\x2f\x2b\x7a\x46\x5d\x9c" "\x9b\xd7\x9e\xdf\xfc\xdc\xc0\x01\x26\x8e\xbf\x46\x9b\x4c\x3c\x0b" "\xd6\x4e\xdf\xf7\x08\xe0\x45\x27\xcd\x06\xda\xe6\xae\x1a\x09\xc4" "\x78\x83\x3e\xee\x06\x75\x11\x96\xd9\x8a\xc7\x3b\xbb\x3a\x7e\xb6" "\x84\xbe\xaf\x9e\xf6\x50\x8f\x6e\x6d\xa5\x45\xe7\x99\x0f\xba\x78" "\x0b\x1e\xe9\xf7\x84\x1a\x45\xbe\xdc\x9e\x7e\xa8\xd2\x95\x45\x8c" "\x00\x59\xe4\x14\xef\x78\xd2\x04\x83\x99\xc5\x8f\x75\x67\xd0\x5c" "\x89\xd9\x6b\x89\x07\xe1\x27\x2e\x4c\x6d\x52\xa0\xc1\xb7\xa9\xd9" "\x42\xb6\xf0\xbd\x04\x90\xea\x4c\x2b\x1c\x38\x17\xaf\x58\xb3\x8f" "\x5f\x01\xad\x15\xb8\x4f\x01\xc2\x7d\xa3\x2c\x5f\x8c\x4f\xd8\x81" "\xba\x1c\x7e\xd7\xd4\xcd\xc6\x33\x0d\x1d\x22\xd3\xe5\x31\x2d\x44" "\x43\xfb\xca\x57\x46\xbd\x11\x45\xa0\x4b\xc4\x25\x3d\x69\x26\x75" "\x3c\xc1\xac\xdf\x17\xa7\xcc\x2a\x23\xeb\x83\xae\x40\x90\x27\x0d" "\xbb\x96\x44\xe1\x07\xc8\xd4\x50\x61\x9c\xd3\xd4\xe0\xbd\x40\xc5" "\x2e\xe4\x5b\xff\xe3\x52\xa4\xae\x0c\xad\x6a\xdd\xf7\xaf\x82\x0d" "\x7a\x0d\x36\x7d\x52\x5b\x80\x55\x13\x6e\x4f\x96\x41\x0f\xdb\x70" "\x62\x1b\xfa\x81\xc4\x42\x8e\x17\xe4\x2e\xb2\x65\x94\x09\x49\xb2" "\xe5\x53\x59\xcb\x6f\x02\xe7\xee\x44\x71\x09\x85\x83\xf8\xb0\xd0" "\x77\xc6\xc0\x84\xc0\x6d\x1a\xfc\x82\x07\xcc\x3e\x1e\x76\x93\x9a" "\xbd\xdc\xb4\xc4\xaf\x75\xac\x25\x0b\x1e\xfb\xf3\x17\xf1\x9c\x56" "\xeb\x24\x6d\xf9\xab\xd8\xf1\xf4\x88\x14\x9a\xd0\xc4\x14\x89\x4f" "\xe2\xe9\x0d\x15\xb8\x76\x84\x90\x87\xa8\xa4\x3a\x8f\xed\xb3\x0f" "\x46\x5b\x9f\x6d\xe1\x21\xd1\x11\x09\x94\x7a\xdd\xf7\x0b\xf0\x11" "\xe7\xf2\x4b\x8e\xdb\x98\x47\xe7\xec\x84\x6c\xee\x67\x4b\xb9\x53" "\x5a\xa3\x08\x84\xcc\xcb\xfe\x11\xcc\x5a\x71\x1f\xad\x8d\x9c\x92" "\x9a\x93\x08\xd3\x3d\x32\x1a\xb9\x62\xd5\x0d\xab\x4e\xa3\xa8\xd7" "\xa8\x48\x9c\x8c\xe0\xf0\x3e\x92\x21\xf5\x91\xc8\x0e\xbe\x19\xbf" "\x30\x07\xf7\x78\xb1\xbd\x09\xd7\xdc\x82\xcb\x10\xa3\xee\x0d\x77" "\xba\x21\x73\x27\x89\x60\x2f\xd9\x27\xdd\x81\xde\x00\x95\xbe\xcd" "\x4d\x00\xbc\xfd\x89\x19\x99\xde\xd1\x16\x91\xcd\x47\xb2\x55\xf0" "\x98\xf7\xdb\x72\xdb\x05\x21\x2b\xd3\xc4\x5d\x43\x6b\x5b\xf8\x13" "\x76\x9e\x42\x2f\x50\xbc\x39\x1a\xcf\xff\x9f\x21\xb1\xdd\x32\x01" "\xe1\xc3\x61\xb5\x76\xee\xcb\xb8\x16\x3f\xe6\x7c\xbf\xff\x0b\xf3" "\xb7\x96\x20\x23\xa9\xbe\x28\x79\x55\x3d\x04\x54\x0b\xd4\xbb\x16" "\xef\x75\xbf\xc8\xdf\x87\x7b\xde\x74\xb9\x88\x92\xf1\x3c\x99\xbf" "\x56\xa7\xe1\xa6\x14\x46\x9a\xe9\x55\x4e\x8f\xbd\xfe\xa7\x27\xd4" "\x4a\x2f\x5d\xcd\x84\x71\x20\x8b\x90\xa5\x93\x1e\xaf\x40\x4a\xbd" "\xe9\xd9\x40\xb2\x6e\x8e\x1b\x84\x02\x79\x0e\xd7\xaa\x8e\xc7\x92" "\x54\x8a\x6e\xc9\x44\x9f\x2d\x39\x35\x0d\x6d\x6b\x93\x51\x8a\x02" "\xba\x80\x7f\x39\xc8\xb7\x3a\x82\xcc\x90\x49\xf7\x77\xa7\x11\xe6" "\x01\x75\x2b\xd5\x6e\x5a\x2f\xb0\x0a\x17\x74\xd0\xbd\x89\xb7\x1f" "\x43\xcd\xbe\xef\xc8\xc9\x20\x3e\xf8\xe0\xb9\xb7\x94\x09\x43\xcd" "\x9c\x7a\xe0\x8f\xe6\xf2\x59\x1f\xed\x88\xb4\xb8\x90\xe8\x0a\xe6" "\x16\x8c\xb5\xdc\x13\x0b\xcb\x36\xa5\xe5\x97\x24\x71\x2b\x2b\xee" "\x81\xd3\xa6\x83\x98\x70\xca\x99\x02\xe4\x05\x7c\x89\xb2\x99\xd1" "\x35\xf1\x8e\x77\xa9\x30\x7f\xb6\x7b\x81\x90\x7c\xb2\x5d\xaf\x17" "\x97\x74\xc6\x0a\x21\x45\x47\x03\x94\xf4\x2e\x7a\x15\x7d\xfd\xe3" "\xf3\xbc\xae\x7c\xce\x23\xfc\xe7\xe7\x0d\x81\x74\x1f\x8f\xef\x20" "\x70\x25\x4c\x01\x30\x55\xe1\xd2\xb9\xc3\x26\x5d\x69\x45\x6e\x8e" "\x74\x53\xc0\x4d\x07\xa3\x9f\xf3\x46\xbe\xb2\x0b\x15\x1a\x17\xae" "\x3f\x26\x23\xe1\x03\x20\xee\xfb\xfe\x50\x34\x70\x57\xd9\xeb\xa6" "\xb0\xd3\x6d\xc1\x1d\x89\xd7\x94\xa6\x4a\x1e\x47\xf7\xcb\x97\x20" "\x20\xe2\x3f\xea\x74\xc6\x37\x7c\x16\xda\xa9\x83\x65\xbf\x64\x20" "\x1c\xc4\x3f\xc7\x83\x07\x1e\x96\xbd\x77\xd7\x2c\xf6\x10\x64\x0c" "\xef\x61\xd5\xb0\xa1\x31\xb6\x8a\x45\xc9\x85\xcf\xaa\xf4\x77\xc6" "\x2f\xac\xa5\xde\x69\x62\xf8\x08\x1c\x74\x80\x4f\x9e\x08\xc4\xf4" "\x8e\x71\x5b\xa5\x53\x46\xd6\x9e\x47\x40\xef\x86\x9f\xa6\x00\xa1" "\xc0\xf1\x36\xa0\xc3\xda\x92\xf2\x06\x08\x93\x3d\x04\xc3\x20\x24" "\xd9\x7b\x7a\x80\xdd\xac\xd4\x01\x7d\xb8\x5d\xa8\xce\xbd\x70\x28" "\x87\x07\x53\x0c\x0a\x5a\x6a\xa3\x6c\x41\x0c\x05\xf0\xd1\xaa\x12" "\x60\xbc\xd1\xfa\xb5\xb9\xec\x52\xa4\x7b\x32\x28\xd0\xf9\x5f\xf0" "\x14\x39\xdc\xc9\xff\x53\xc7\x51\xe7\x60\x9f\x7e\x6a\x49\x25\xc1" "\xf4\x57\x4a\x95\x93\xf3\x6d\x7c\xa1\x67\x1f\xfd\x2c\xf2\x36\x12" "\x02\x29\x2f\x97\x90\xb7\x5b\xb8\x80\xb5\x99\xbe\xaa\xf1\xd5\xa1" "\x4a\x60\x8f\x5c\xf8\x0b\x6e\xa6\x50\x64\x45\xf4\x3d\x16\x11\x50" "\x5c\x7e\x52\x92\x1c\xd7\x80\x6a\x56\xc0\xd8\x81\x7c\x44\x8b\x75" "\x97\x67\xcf\xe6\x17\x03\x03\x05\x4b\xe6\x0e\xb7\x81\x02\x6d\x40" "\xef\x8e\xb3\xba\xd0\x06\xf5\xa0\xc9\x67\xe1\x47\x93\xab\x8e\xc6" "\xd6\x43\xd8\x78\x03\x6f\xca\xbf\xb8\x37\xe9\x99\x16\x57\xb0\x92" "\x4c\x53\x30\x49\xb4\xad\x38\x1f\x3d\x27\x81\x6e\xf3\x25\x2e\xa1" "\xd6\xbc\xe5\x1b\xbc\x20\x2f\x8c\x48\x87\x9d\xc3\x16\x6b\x17\x88" "\x81\x82\x89\x54\x76\x78\xf2\xfd\x77\x5c\x76\xb4\x4a\x23\x57\xb8" "\xbe\xc4\x25\xf3\xc4\x9a\x6a\xd1\x44\xd7\xe6\xe3\x22\x44\x3a\x24" "\x07\x78\x36\xcb\x68\xd6\xe0\x8f\x88\x0d\x4c\xd6\x92\x6b\x67\xd2" "\x78\xe1\x07\x9c\x0d\x62\x18\xbd\x87\x72\xb5\x05\xf9\x6f\xfb\x2f" "\xb7\x2a\xe3\x28\x90\x53\xd9\xd5\xc3\xc2\xca\xc8\x5b\x4d\x99\x2b" "\xb7\x94\xec\x26\x40\x2a\x51\xa0\x2a\xaa\xf5\x49\x4c\xce\x3c\x5d" "\xcf\x1f\xd4\x11\xa6\x03\x56\xe8\x7f\x6b\xc8\xd9\xb6\x9f\x8e\xce" "\x95\xe4\x12\xcc\xda\x02\x8d\xd2\x5f\x73\x04\x69\xf5\x5e\x39\xf1" "\xbd\x32\x6f\x53\x91\xd5\x8f\xe3\x88\x1d\xb8\x1e\x33\xf2\xc2\x17" "\x0c\x65\xfe\x5a\x7c\x3f\x44\x43\x25\x01\xe5\xb0\xcd\xd0\xcd\x57" "\x68\x2e\x0d\xcb\xab\xb2\xca\x1f\x5e\x29\x37\xce\x8e\x77\xa9\x28" "\xa9\xaf\x36\x63\xab\x66\x31\xa7\x23\x2b\xb6\xa1\x3e\x53\xee\x88" "\x4d\xa7\x6f\xda\xa0\x74\xf2\xeb\x63\x35\x8f\x24\xa7\x89\xf5\x0c" "\x17\xf0\x91\x67\x3c\x04\xfb\xcf\xc2\xd5\x48\x32\xb4\x2d\xd6\xb8" "\x33\x4c\x72\xf9\x8d\x29\xc1\xb5\xde\x3f\x0a\x06\xb0\x3b\x60\x4d" "\x4e\x14\x4e\x35\x52\x8b\x5c\x24\xdd\xfc\x21\xf5\xac\xf7\x01\x9e" "\x72\x71\x0c\x8e\xaf\x1a\x9a\xe5\x27\xbc\xe0\x57\xf4\xca\xe3\xe4" "\x96\xc1\xf0\xe0\x2c\xbf\xe8\x07\xd2\x75\x95\x93\x75\x97\x97\x91" "\x20\x34\xe0\xf4\xf7\x65\xc2\x55\xe4\x77\x75\x4b\x23\x70\x7b\x5b" "\x51\xe4\x5a\xab\x87\x05\xbb\x64\x14\xba\x4a\x4e\x32\x9f\x38\x67" "\xbf\x0c\x11\x0d\x12\xb6\x5f\xfe\xf4\xcc\x71\x12\x01\x11\x57\x5f" "\x6f\xe5\x3d\xbd\xb3\x62\x9f\x3e\x7d\x62\xff\x10\xcb\x8d\x85\x59" "\xc5\x8c\xca\x73\x54\x56\xef\x66\x46\x3c\x92\x54\x69\x35\x4b\x8e" "\x37\x77\x60\x57\xc2\x0b\xfc\x19\xa3\xba\x2e\x20\xde\x06\x0b\xc0" "\xd0\xf7\x64\xe7\x3a\x80\xa7\xb5\x81\x98\x15\xd6\x47\xe7\x92\x01" "\x79\xff\xae\x7a\x3c\x84\x75\x08\x04\x6e\xc6\xf6\xdf\x7b\x19\x8b" "\x8c\xa5\x42\x44\xa3\xf2\x93\x07\xf7\x3f\x0a\x60\x69\xa5\x50\xaf" "\xc3\xed\xaf\x11\x21\x0c\xde\xc9\x76\x51\x3d\x9d\xc9\x0b\x14\x2f" "\xef\x05\x25\x25\x11\xcf\xff\x00\xf8\xdd\x5b\x20\x10\xc1\xe8\x84" "\xce\xb6\xd9\xb8\x13\x6d\x49\xac\xdf\x2a\xf4\xc5\x60\x3d\xa1\xd2" "\x7c\x5d\x5e\x4e\x9f\xa0\xc6\xfa\xf6\xcc\x6b\x16\x12\xf1\x42\x06" "\x6a\x42\x8e\xf4\xde\x18\x81\x2f\xcf\xc2\x59\xe6\x9c\x1c\x5a\x8b" "\x31\x25\x40\x72\x57\xb4\xde\xb2\x19\xeb\xe6\x79\x99\x14\x8f\xa9" "\xf3\x35\x8b\x45\x64\x7b\x8d\x8c\xbe\x3b\x06\x5a\x7a\xc8\x5e\x0a" "\x69\x24\x88\x9f\xd0\x8a\x56\x0d\xf2\xad\x25\x37\x9b\x67\x7a\x4d" "\xea\x89\xf6\xa6\x05\x04\xab\xeb\x49\xff\x03\xfd\x9d\xf7\xc4\xcd" "\x1a\xd4\x0c\x68\x8b\x04\x35\xfc\x9b\xdf\x48\x6d\x4c\xe8\x09\xd5" "\x43\xcf\xd7\x1b\x0e\xd9\x0c\xd5\x4a\x6f\x68\x4a\x3c\xf1\x70\x93" "\xb5\x22\xa7\x9b\x3d\x21\x3b\x77\x75\x43\x25\x03\xcb\x34\xff\x73" "\x27\x1c\x36\x95\xac\xde\x43\xb2\x89\x2d\x87\x16\x78\xba\xf7\xb0" "\xcf\x2b\xb2\x1e\x84\x69\x29\x57\x9e\xb6\xcc\x67\xef\x52\x9b\x02" "\x0d\xbe\xee\xc7\x52\x00\x74\xec\x14\x2e\xda\x8d\x1f\xee\xe9\x55" "\x5d\x58\x88\xe8\x0f\x00\x97\xe2\x2e\xfe\xa8\x4a\x24\x24\xec\x49" "\x45\x0e\x61\xea\xb2\x1b\xc7\x70\x09\x46\xfa\x74\xa1\xd6\x1c\xe1" "\x19\x3d\x94\x16\x74\xb7\xb0\xaa\x0f\x0f\x4b\x9a\x13\xfa\x21\x80" "\xb5\x61\xc7\x45\xc7\x5f\xd2\x97\x8a\xd8\x0f\x6d\xac\xab\xe7\x35" "\x63\xd5\x6d\x2e\x07\x3c\x9e\x5d\x99\x46\x8d\x23\xa7\xaa\xdb\xdc" "\xad\xca\xed\x6f\x77\x85\x59\xf3\x28\xdf\x19\x5f\x46\x62\x08\x1e" "\x5e\x41\xdd\x3b\xc2\xf7\xad\xf6\xbb\xa8\x0f\xa8\x5d\xbc\x18\x37" "\xbc\x69\xfd\xb8\x51\x03\x13\x3d\x6c\x5c\xce\x01\xa8\xd8\x81\xbc" "\xcf\xc7\x31\xb5\x36\x46\x14\x5d\x99\x79\x60\x46\xaf\xe9\xf7\xa2" "\x5b\xb1\x78\xb2\x69\x9d\x4e\x81\x19\x13\x67\x6b\x7a\x89\xa4\x22" "\x01\xe8\x76\x1b\xe0\xbb\xc1\xe6\x87\x8d\x67\x7b\x0d\x35\x35\x71" "\x5c\xb9\x81\xc1\x4c\x93\xfa\xff\xb2\xa9\xf3\x62\x64\x3a\x0a\x3c" "\x47\x10\x35\xd8\x2b\xc5\xb5\xf0\xa4\xcd\xe9\xc6\x4b\x30\x60\x0d" "\x3f\xcd\x8e\x63\xf3\x04\x00\xa7\xd4\xb5\x3f\x0a\x22\x3d\x05\x7e" "\x14\x4e\xf6\xc4\xbf\x0a\x87\x32\x45\x7d\x9e\xd8\x2e\x07\xdc\x5b" "\x3f\xd5\x6c\x38\x4b\x8c\xe0\xa0\x35\xb0\xb6\x08\x1b\x76\xaa\x13" "\x71\xdc\x15\x27\x64\x55\xb5\x2a\xb4\xe9\xf6\xde\x99\x37\x31\x81" "\xbe\x38\xe2\xf1\x70\xab\xbe\xf8\xae\xe0\x96\x92\xaf\x33\x68\xe5" "\x67\xeb\x01\x7e\x21\xab\x1e\x29\x06\x1d\x4a\x8c\x9d\xea\x0d\x77" "\xc1\x78\x9c\x3f\x34\xd0\xc7\x35\x6f\x9f\x17\x5b\x61\x98\xff\x4e" "\x16\xc5\x13\x9b\x62\x24\x26\x47\x40\x8a\xae\xd7\xa6\x1a\x78\xbd" "\x86\x52\xa1\xc8\x68\x91\x26\x98\x3c\xe2\xaa\x21\x0c\x9a\xc9\x35" "\x5f\xbc\x93\x36\xbc\x46\x0e\x64\x1f\xae\x64\x4b\x7a\x3f\x60\x2e" "\x5f\x87\x94\x19\x1b\x73\x8e\x33\xed\x27\xf7\xcd\x36\xec\x22\xb7" "\x28\x3c\x11\x39\x45\xe3\xf6\x0b\x25\xe0\x44\xa4\xc0\xf5\xca\x64" "\xed\xe0\x47\x04\xd1\xe1\x8b\x1f\x87\xc6\x61\x38\xfa\x20\xfe\xb0" "\x16\x34\xad\xe7\x3b\xb9\x59\x48\xd5\x8d\x54\xa3\x79\x89\xeb\xaa" "\x69\xf2\xb1\x19\x3f\x38\xe1\x2d\xab\xeb\xe7\x3b\xf4\xd1\x1e\x13" "\x1b\x13\xa2\xfe\xff\xe1\x15\x3b\xaa\x56\x37\x94\x6f\x45\xda\x8f" "\x89\x35\x05\x5e\x7e\x60\x9d\x3a\xdf\x72\xdc\xf0\x59\x83\x8b\xb5" "\xe4\x58\xa4\xea\x5a\x81\xbf\xfd\xc3\xf6\x97\x20\xb0\xf4\xdf\xf1" "\x21\x65\x61\xc2\x98\x32\x52\xb6\x3a\x4b\xd8\x5b\x48\xb3\xf7\xd6" "\x1a\x3a\x98\x91\x73\x51\xff\xcb\x50\x09\xf4\x0d\x64\x57\x87\x3b" "\xb7\xaf\x9f\x81\x37\xf3\x94\x9b\x97\xde\x10\x50\x7c\xec\x47\x84" "\x8b\x53\x56\xac\x46\x6f\xab\xf1\x0b\x9f\xc3\xd9\x5b\x45\x5b\x7a" "\x28\xcc\xec\xb5\x92\x04\x66\xae\x75\x48\xc7\x56\x36\xa8\x34\x73" "\xcd\x23\xa7\x5e\x8b\x0a\x92\xbf\x70\x24\x90\x1b\x75\x27\x88\x80" "\x82\x10\xfc\x5b\x50\xc4\xa3\x7e\x13\x4f\x76\x24\xd0\x84\x9c\x85" "\x06\xb5\x09\xf0\x17\x03\x03\x05\x4b\xdb\x98\xcb\xf1\x36\xa5\xc5" "\x48\x6a\x6f\x5f\xaf\x36\x17\x90\xd2\xc3\x67\x69\x98\x50\x0e\x19" "\x80\x81\xe7\x61\x09\x1a\x27\x15\x44\xdc\x08\x59\xaa\xc1\xb6\xf7" "\x22\xa5\xe2\x83\x25\x9b\x7d\x02\xd0\xc2\x85\xfe\x76\xc5\x47\xfa" "\x79\xc1\x92\xfa\xe0\xbe\xab\x21\xc2\x8f\x5a\xce\x42\x3c\xbf\xb4" "\xf2\xcf\x74\x1b\x1e\x40\x62\x4d\xa4\xb4\x25\x5d\xfb\x1d\x9f\xed" "\x4f\x41\xbb\x24\xa9\xd0\x24\x0c\x3a\x30\x99\xb6\xf9\x84\x20\x31" "\xec\xc5\xac\x27\x36\x0d\xbb\x8f\x0f\x64\xb4\x28\x61\x20\x79\x46" "\x49\xc3\xdb\x26\xf1\x1d\x25\xf6\x5b\xaf\x89\xf0\x71\xc5\x68\x18" "\xb1\xdd\xf5\x94\xf5\x6a\x10\x5f\x40\x1e\x45\xaa\x90\x91\xce\x96" "\xd6\xe3\xff\x17\x16\xce\xa6\xb6\xce\x60\x0d\xe4\x2d\xc0\x91\xa0" "\x06\x59\x34\x03\x43\x0f\x39\x52\x63\x1f\x7a\xa3\x25\x06\xed\xf5" "\x23\xaf\x13\x26\xd1\xaa\xea\x33\x7e\xf1\x9a\x5b\x9b\x87\xbf\xd8" "\x14\xe5\x00\x4c\x55\x90\x8c\x24\x5b\x1d\x66\xdd\x49\x36\x49\xc0" "\x1a\xd7\x18\xed\x97\x4a\x34\xca\x57\xbd\xf7\x7d\xa8\xee\x01\x28" "\xf2\x10\xa6\x77\x3b\x6e\xa6\x62\x37\x9f\x1e\x6c\xa6\x9c\xbc\xd8" "\xa7\xf4\xb7\xf9\x72\xc2\x59\xff\x76\x5a\x60\xf4\x6d\xfb\x4e\xb1" "\xe0\x5c\x0b\xf7\xec\x8e\x8e\xf3\x59\x6a\x0f\x26\x2a\xc9\x86\x8b" "\x55\xf4\x7f\x98\xc8\x2a\x2d\x63\x78\x58\x95\xe9\x9e\xb1\x22\xa0" "\x2a\x43\x56\xf7\xbe\xef\x80\xb7\x64\x9d\x43\x5c\x84\x3b\x54\x56" "\x23\x49\x5b\x64\xa3\x2e\xdd\x48\xb7\x45\x6f\x8f\xf6\xcb\x53\xd0" "\x9e\x6f\x8e\xa0\x1d\x21\x11\xe8\x86\x8d\x1d\x89\xe2\x76\x33\x9e" "\x39\x67\x7e\xfb\x37\x89\xce\x43\x66\xa1\x33\xc3\xb9\x77\xe9\x11" "\x2f\xb5\x4a\xd4\xb3\x97\x8f\xfe\x77\x29\xd1\xcd\x5e\xb4\x91\x10" "\xd2\xaa\xcc\xa3\xd6\x95\xa1\x1b\x1e\xe0\xa8\x18\x63\x48\xc0\xe2" "\x11\xb0\x00\x1d\x38\x8c\xdd\xb6\x0b\xd5\x4a\xa5\x8e\x2a\x6a\x3a" "\x37\x02\x68\x4d\xf6\x89\x6d\x35\xab\x03\x20\x36\x55\xbb\x78\xca" "\xf9\x75\x17\x7e\x25\x42\x07\x37\xdd\x23\x7d\xb1\x60\xec\x2f\x9d" "\xf3\xc6\x93\xa3\x52\xdb\xe6\x74\xed\xfa\xed\x5e\x5b\xf7\xc6\xba" "\xad\x02\x05\xe6\x66\x81\x68\x7b\x12\x6f\x3a\x64\xca\x51\x3d\xd9" "\x74\x13\xca\x89\x32\xa5\xcf\xd9\xf0\x09\xbe\x1a\xd1\xc1\x3d\x60" "\x5c\x21\x8f\xed\x73\xc9\x03\x85\x69\x28\xe9\x26\x5b\x75\xe3\xc3" "\xa2\xee\xa1\xab\x94\x02\x9c\x52\xf2\x14\x2d\x43\xfb\xed\xdf\x79" "\x97\x88\x6e\x5f\xff\x08\xb6\x02\xb6\x1a\x09\xb0\x0e\xf3\xf4\xe9" "\x3f\x7c\x23\x05\x63\xba\x3e\x9c\xe5\x63\xe6\xb8\xd3\xef\x8a\x1f" "\x76\x3f\xd1\xdc\x7e\x80\x3a\x21\xe5\xb4\x8c\x94\x90\x46\x65\x08" "\xcb\x02\x8d\xad\x80\xb6\x86\x8d\x3c\xdc\x6e\xfa\x07\x78\xb2\x1e" "\x6d\xe8\xf4\xc4\x48\x82\x77\x72\x03\x6f\x88\xf8\x64\xc7\x60\x70" "\x9c\xe2\x11\xcf\x79\x8a\x70\xc9\x9f\x1a\x7b\x46\x29\x4f\x2a\x48" "\xe0\x54\x51\x19\xe6\x57\x96\x7b\x40\x92\xb6\x4f\xa8\x90\x73\x62" "\x9f\x4f\xc6\x55\x2f\xc9\xc7\xcc\xe9\xb7\x74\x0a\xdb\x51\x3f\x90" "\xae\x55\x58\xcd\xd6\xc1\x6a\x3e\x7b\xdf\xd8\xec\xef\x19\xae\xbe" "\x9f\x82\x4d\x44\x29\x4f\xe9\xa1\xc0\xba\xcb\x03\x06\xab\xf4\xf6" "\x6e\x0d\xdd\xaa\xa9\x35\xcd\x98\xee\xe1\x94\x97\xd4\x9c\x2c\xbc" "\x5e\xfb\xce\x42\x13\x6c\xc3\x61\x52\x43\xc2\x09\x1b\x58\x06\xba" "\xa3\xa4\x38\xb0\xf3\x16\x02\xdc\xf5\x24\x02\xe6\xab\xaf\xc7\x48" "\x95\xbc\xa1\xde\x08\xca\x83\xdd\x83\x5b\xbc\xfd\x73\x95\x82\x94" "\xfc\x11\xc3\xd5\x29\x92\xd8\xe2\x16\xbd\xff\x82\x77\xc6\x1f\xc7" "\xd6\xf2\x1a\x65\xe9\x74\x76\x9b\x08\x67\x98\x4f\x7c\xb9\x32\xda" "\xed\xa1\x9b\x1b\x45\x95\x5f\xa5\x94\xfc\x1c\x15\x44\xa9\x46\x0b" "\x0f\x20\x4a\xd4\x4e\x03\x75\x01\x60\x51\x1c\x92\x22\xaf\x6a\x2b" "\x0e\x55\x3b\xc4\x12\x3a\xc4\x5d\x9f\x3b\xa0\x2f\x7f\x66\x6f\x46" "\xff\xb1\x4a\x9d\x2f\x62\x81\x95\x88\xad\xa5\x25\x21\xb3\x67\x41" "\xc5\xf9\x33\xe8\xbe\x41\xf3\xf3\xd7\x2f\x45\x22\xa9\x97\xb0\xa1" "\x9b\xb1\xe3\x9e\xa7\x1b\x64\x0a\x30\xc8\xfb\x4f\x3b\x2d\xc0\x63" "\xfa\x54\x05\x24\xb6\xca\x5f\x0f\x84\x46\xd8\x4f\x1a\xcd\xda\x9e" "\xaa\xc0\x68\x92\x64\x4b\x7f\x50\x14\xc6\xc6\xec\x19\xf6\xdb\xe6" "\xf1\x44\xf8\x07\x99\xac\xa7\x73\x0f\x1d\x70\xdc\x9c\x62\xc1\x44" "\xbf\x79\xa9\xc5\xee\x4a\x22\xa2\x24\xb1\xcd\xaa\x21\x54\xc7\xbc" "\x4e\xf8\xf7\xfe\x61\x18\xe1\x44\x14\x53\xc9\x59\x6c\xa8\xe6\x14" "\x74\x12\x64\x50\xa2\x86\x74\x13\x1b\xfb\xd0\x11\xc0\xe4\x95\x8e" "\x10\xf6\x01\x27\xb7\x2a\xc3\x9c\x69\x80\x01\x5d\x50\x60\x56\xfa" "\x6a\x1d\xdf\xa4\x8d\xf6\x81\xb4\x5d\xaf\x99\x8b\x4c\xa4\x51\x34" "\x18\xc5\x10\x26\xb1\xbc\x91\x3f\x75\x0e\x4d\x8d\x00\xda\xf4\xd8" "\x6f\xe3\x65\x94\x47\x96\x3a\x68\xab\x31\x25\x8a\xef\x63\xda\x96" "\xc3\x4c\x61\x60\xf9\x6f\xf7\xa9\x0d\x29\x40\x56\x34\x3f\x98\x08" "\xc7\x82\x27\xfd\xe5\xb1\x13\xaa\x3b\x5f\x38\xc8\x12\x4f\x8e\xda" "\x03\x90\x63\x7b\xc0\xc5\xc3\x3c\x59\x78\x9a\x75\xee\x4b\x57\xf3" "\xbe\x4e\x73\x28\x3c\x24\x5a\x99\xa7\xfa\x4e\x16\xe6\x26\xe7\x86" "\xf8\x38\x0d\xc1\xd8\x44\xc9\xd4\xb1\x2c\x6d\x0d\xbb\x7c\x46\xcb" "\xb9\x6a\x95\xc6\x71\xe0\xaf\x98\x47\x9f\xb9\xc8\x47\x65\x96\xc3" "\x8e\xc6\xa4\xce\x09\x20\x47\x15\xc1\x3b\x28\xb4\x2c\xa3\xc6\x7b" "\x7a\xa0\x32\xfd\x19\xba\x05\x1b\xd6\xd7\x91\xf2\x7f\x91\xef\xc8" "\x95\x5c\xf8\xfe\xaf\xa0\xa6\x9a\xf9\x4a\xe1\x4e\x5e\xa7\xcb\x3f" "\x18\xfc\x13\x4d\xa1\xb5\xea\x43\x10\x5a\x1c\x84\x52\x5d\x4c\x93" "\xf3\xa4\xda\xf6\xf6\x4f\x77\xda\x51\x62\x1d\x4a\xe2\xe3\x4b\x1e" "\x41\x6a\x84\x79\xc8\x9f\xb7\x5d\x6e\x64\x3e\xe0\x9a\x25\x51\x89" "\x3d\x88\x4f\xc9\x4d\x0b\x95\x48\x4d\xb7\x5a\xea\x9e\x9b\x0d\x35" "\xa6\x86\x0d\x98\xc9\x41\x7b\xfc\xf7\x90\x60\x29\x7c\x0c\xd1\x82" "\x82\xdf\xaf\xb9\xaa\xf8\x60\x1d\xeb\xdd\x9b\xa6\x0f\x1f\x55\xe0" "\x6a\x50\x73\x6a\xf5\x47\x81\xfa\xfe\x60\xab\x27\xf4\x59\xbf\xda" "\xf8\xed\x1a\x64\x53\xba\xcb\x93\xdb\x01\x11\xb2\x57\x36\x11\x35" "\x57\xe4\x84\x19\xab\xba\xf1\xa6\x5a\x6b\x68\x95\x7f\xf4\x8a\x8c" "\x08\x64\x2a\x03\xc0\x8d\xfd\x7b\x90\x4d\xf0\xb2\x55\x9d\x69\x20" "\x74\x0d\x8f\x3a\x20\xb4\x51\x66\x42\x26\x86\xb5\x63\x4d\x5e\x86" "\x58\x89\x9a\xbd\x17\x03\x03\x05\x4b\xd4\xb0\xda\x60\xf7\x7f\x04" "\xca\x95\x77\xf1\x5f\xfd\xd4\x5d\x09\x7d\x1d\x16\xa3\x88\x96\xf8" "\x8b\x18\xfd\x4e\x1c\xe7\xf1\xa2\x4f\x84\x84\x11\x64\x63\x65\x5a" "\xe1\xc1\x84\x89\xa5\xd6\x51\xbd\xc7\x5c\xc7\xb0\x71\x37\x84\x22" "\x46\xea\x61\xeb\xf5\x32\xfb\x66\xec\x7b\xe5\x82\xf2\x76\x71\x2d" "\x63\xb1\xdf\x2a\xe1\x4d\x67\x84\x9f\xc4\x68\xa3\x58\x18\x8c\x57" "\x2a\xfb\xe3\x6d\xf8\x2e\x25\x19\xc4\x73\x19\x96\x23\x27\xd7\x6a" "\xa2\xd8\xe6\x2e\x9e\x01\x83\x53\x28\x17\x73\x54\x96\x6f\x74\xc5" "\x36\xff\x27\xa1\x32\xb4\x33\x10\x73\x5a\x2d\x90\xe3\xc9\xb1\x67" "\x54\x5d\xc1\x68\x49\xff\xec\x06\x52\x61\x5f\xe2\x01\x9f\x74\x25" "\xb3\xd5\xb1\xdc\xc8\x23\xfa\x55\xab\x46\x88\x76\x13\x2f\x4f\x06" "\xea\x39\x2c\xb1\xd2\xd1\xd9\xcd\x05\xde\x2d\x38\x1d\xf7\xe6\x62" "\x05\x5b\x93\x72\xe9\xc9\x01\x8a\x31\xe1\x73\x12\x42\x32\x8e\x9e" "\x98\xe3\x45\x0b\x7f\x7a\xb5\x21\xd4\xef\x5e\xbd\xf5\x9f\x60\xd9" "\xe9\x5e\x14\xb1\x5a\xf5\x33\x5f\x37\xe1\x63\xd7\xd1\x11\x62\xb7" "\x10\xc6\x62\x38\x23\xa7\x60\xd3\x34\x09\xa0\x55\x70\xce\xf3\xf4" "\x4e\x76\xbf\x5f\x91\x48\xe5\x73\xd5\x74\x77\x69\x59\x11\x1f\x33" "\xa2\xbc\x28\xa0\xc7\x5e\x2c\xee\xb1\x8e\x0b\xc8\x2d\xab\x1c\x86" "\x93\xa3\x51\x8f\xcb\x8c\x98\x68\xa8\x4a\xa2\xd0\xae\xe3\x3c\xb9" "\x07\x0f\x0d\xf6\x5d\x2a\x32\xe3\x7e\x41\x87\x7f\xbb\xab\x2a\x41" "\x20\x74\x6f\x32\x56\x03\xd8\x62\x52\x9d\x41\xb9\xf4\xc9\xbd\x2b" "\x67\x8d\xeb\xdd\xc0\x31\x80\xb5\x06\x4f\x34\xfa\xad\xd9\x46\x34" "\x47\xb7\x10\x7d\x89\xed\xf7\xd6\x13\x72\x13\x05\x62\x25\x50\x5f" "\xb1\x60\x3c\xc8\x8f\xe7\x2c\xfe\x03\xc1\x1b\xb2\x72\x46\xd6\x5d" "\x90\x05\x1e\x25\x1f\xdc\x3a\x8f\x11\x1e\x53\xc4\xab\x55\x63\x79" "\x31\xc3\x0c\x1b\x09\xab\xba\x6c\xa9\xc3\xd3\xf7\x21\x57\x98\xfb" "\x7c\xe6\x0f\xe1\x85\xe1\xa7\x7a\x1c\x4f\xc5\xac\xb4\x63\x18\x0a" "\x20\x86\xa1\xa3\x68\x59\xce\x29\xa8\x17\x48\x87\x49\x71\x9d\x6b" "\x47\xb3\xb2\x47\xa4\xa4\x4f\xe4\x39\x28\x85\x4d\x74\xe4\x1b\xc9" "\x8c\x9a\xe6\xe9\x88\xb2\x3b\xfe\x1e\x6e\x0e\x79\xd1\x22\x8c\x72" "\x92\x59\x0b\x2b\x12\x94\xda\xad\xac\xc3\xcd\x28\xfd\x4b\x16\xf0" "\x59\xf7\x8c\xb8\x6d\x8d\xc1\x44\x6b\xdd\x16\x2f\x5d\xe3\x78\xa8" "\x28\x39\x20\x07\x97\xbb\xf5\xe8\xa5\xd0\x13\x81\x7d\x7d\x6e\x9c" "\xfb\x48\xb9\xbc\x86\x43\x3c\x79\xd7\x09\x17\xa3\x08\x2e\xe1\x07" "\x5c\x10\x30\x61\x85\xa6\x73\x46\x09\x25\x16\xad\xa7\x37\x5d\x24" "\x61\x05\x56\x7e\x71\x38\x0e\x1a\x6b\xb8\xfc\xa0\x29\x9c\x4f\xae" "\x07\xe3\x97\xc5\x1b\xfb\x36\xef\x80\x36\x72\x69\x99\x2d\x1b\x8c" "\xf6\xff\x60\x3a\xd3\x80\x03\x7a\x03\x4d\xeb\x94\xd9\x87\xf7\xd1" "\xc8\x7a\xb9\x3a\xc9\x64\x20\xb1\x19\x14\x5e\x4c\x5a\x28\x4a\x38" "\x06\xd5\x9b\x70\xdb\x2c\x8c\x00\xa7\xa7\x0e\x3b\x26\xa9\x88\xe5" "\xfc\x3c\x0a\xf3\xbc\xfc\x84\xa1\x0f\x3e\x27\x59\xcd\xb3\xcc\x23" "\xb5\x80\x59\x07\x9d\xb4\x76\x7d\x0a\x54\xa9\xc5\x76\x7d\x1c\x89" "\x2d\xcd\x2f\xee\x8c\xf1\x97\x4d\xa2\x7c\xb9\xa8\x54\xff\x8e\xbd" "\x8f\x01\xdc\x77\xcf\x8a\x1a\xd2\xe3\xdb\xc4\x90\x63\x4a\x5c\x07" "\x4d\xd0\xa0\x61\xec\xd8\x79\x17\x1a\xbd\x0f\xa4\x8b\xfe\x01\x88" "\x4d\xa6\x94\x7e\xd7\x1c\x5f\xfc\x89\x9b\x8a\x74\xe2\x09\x58\x7f" "\x1a\x0a\x58\xbd\x86\xa8\xda\x68\x69\xea\x1d\x41\x5e\xe5\x03\x8d" "\xe2\x4f\x0c\x3c\x72\xc5\xd9\x11\x96\x65\x65\x28\x20\x3a\xbe\xbd" "\x9f\x92\xca\x1d\xde\xcb\x09\xb4\x2c\x5b\x86\x7c\x41\x7c\xc8\x75" "\xc9\x45\x44\x28\x2d\x66\xe0\x17\x3e\x7a\x2d\xb7\xe5\x2e\x9a\x84" "\x68\x99\x15\x21\x0a\x42\x30\xc3\xe6\x18\x36\x98\x0a\x0f\xc4\x5d" "\xa1\x36\x55\x71\x52\xad\xc9\x3e\xf3\xb9\xa7\x22\x25\xa3\xb6\x81" "\x4c\x47\xd8\x45\x57\x43\x1a\xa2\x58\x6e\x51\xd3\xee\xac\x0c\x87" "\x8c\xa8\x3a\xa7\xca\x9f\x04\xab\x21\x56\x12\xba\xc5\xe1\x8d\x42" "\x18\x06\x9f\x08\x0f\xa1\xf2\x89\x58\x33\x2c\xe5\xf4\x7e\x78\x9a" "\xa7\x34\xdd\x28\xae\x65\xe8\xf1\xcc\xe7\xc4\xdf\x94\x60\x85\xa7" "\x6e\x6f\x47\x7b\xab\x7d\x7b\x45\xe6\xe7\x66\xe0\x02\xfa\x87\xfe" "\x26\xb9\x83\x5f\xea\x4b\xe6\x12\x1b\xb1\x66\x9a\x23\x17\xb7\xf0" "\xc7\xe6\x23\xd3\xfc\x71\xe0\x62\x35\xbe\xc6\xc3\xcd\xf8\xfd\xfe" "\x97\x85\xcc\xb6\x13\xc2\x1f\x7e\x4f\x4d\x32\x5c\x4c\xa6\xba\xb8" "\x2a\xab\x63\x5c\xd6\x5c\x4f\xef\xb2\x42\xad\x61\xa0\xaf\xa9\xd5" "\x7a\xb1\x8a\x49\x62\x9a\x4f\x7e\x6e\x7e\x07\x5f\x86\x1e\x8e\xd9" "\x1e\x0d\x85\xe1\x00\x14\x0a\xb3\x61\xd3\xeb\xda\xc2\x9d\xe7\xbf" "\x4f\xed\x5f\x79\x43\xd0\x6d\x48\x70\xc4\xc4\x32\x77\x1c\xf7\x77" "\xae\xde\xb3\x68\x98\xe7\x31\x6c\xf2\x0e\xf8\xf9\xab\x45\xe8\x49" "\xe0\x6d\x73\xee\xa6\x50\x25\x7f\x56\xb2\x60\x3d\xcf\x89\x2f\xc0" "\x11\xc3\xc4\xd1\x1d\x0c\xbc\x1b\x9e\xb9\xc6\x4c\x70\xe4\xd7\x0a" "\x30\xb7\x71\x77\x07\x15\x18\xd0\x2e\x0e\xb1\x1b\xe0\x9a\x94\xd8" "\xb6\x17\x14\xba\xba\x6b\x3e\xc2\xe2\xc5\xb8\x7c\x2c\x16\x99\xec" "\xf3\xfe\x68\x6b\xc7\x49\xbc\x68\x17\x74\xca\xd3\x18\x21\xdd\x4e" "\xa1\x8c\x1a\x87\x48\xbe\xfd\x86\x86\x9a\xcf\xb5\x4d\xb9\x2f\x8a" "\xef\x4d\x2a\xec\x89\x9f\x04\x0f\xb6\x7f\x43\x81\x94\x13\x05\xda" "\x46\x0a\x07\x7a\x56\x83\x02\x8a\xd0\xcd\x53\x82\xc5\xc6\x2c\x67" "\x49\xcf\x4f\xff\x34\x49\x82\x48\xab\xe3\xfe\x7c\x86\xbd\xc5\x8c" "\xa3\x2e\x9c\x34\x97\xe8\x57\xde\x4f\x74\x65\x43\x8a\x5f\x27\x4d" "\xc9\x8e\x51\x99\x22\xc6\x51\x41\xf5\xb5\x43\x73\x5a\x98\xe6\xf6" "\x2a\xac\xf3\x59\x9b\xeb\xce\xe1\x85\xee\xe5\x7b\x73\x25\xc4\xde" "\x2b\x7b\x05\x31\x48\x6e\x02\xf7\xbf\x09\xd6\x27\xe8\xe6\x2e\xad" "\x85\x90\x6f\xc6\x1f\xa8\x40\x67\x6e\x45\xf1\x77\x9d\x5d\x12\x68" "\x11\x13\x6e\xc4\x5c\xfe\xfe\xdd\xaf\x54\xc4\x63\xfe\x42\x02\xd6" "\x2d\xa6\x7b\x83\x3c\x3d\x9a\x74\xe0\x3a\xa2\x37\xe1\xbe\xc3\xc2" "\x7a\x42\xfd\x33\xa3\x5f\x5e\xba\xcb\xd3\x1a\x6b\x01\x95\x98\x5a" "\xf2\x8d\x78\x23\xca\x2c\x7e\x40\xd5\xde\x23\x7f\xac\x7c\x41\xb6" "\x38\x1b\x1d\xbe\x10\x67\xcd\x48\x98\x76\xf1\x81\x50\x80\x49\x11" "\xb9\x73\x55\x2e\x57\xab\x2e\x87\xed\xc1\x71\xd2\xe5\x23\xdf\x62" "\x2c\xdc\xd7\xd6\x17\x03\x03\x05\x4b\x90\xae\x72\xb7\x18\xf8\x53" "\x3a\xb3\x11\x1d\x3c\x5b\x70\x31\x04\xa8\x36\x0b\xb3\xdb\x5e\x73" "\x72\x43\x96\x21\x52\x90\xb2\x70\x99\x59\xe5\x42\x36\x0b\x22\x0e" "\xb8\x48\x4b\x04\xdd\x99\x41\xdf\x0d\x84\x76\x25\x7f\xf7\xd6\x30" "\x15\x9c\x1b\xd8\x25\x37\x86\x04\xe5\x1b\xdb\xec\x28\x70\xb9\x9f" "\x4f\x74\xd0\x3f\x78\x1b\x8f\xb5\xf8\xe7\x1c\xcc\xbf\xc0\x5a\xee" "\x3e\x99\xbd\x54\x91\x29\x63\x79\xc4\x5b\x35\xa9\xd1\x25\x2f\xea" "\x34\x69\x94\x22\x6f\x02\xeb\xc0\x91\xdc\x07\x18\xef\x96\x7e\xc4" "\x97\xaf\x74\xd3\x05\xed\x41\xfb\xb8\x54\xf4\x14\xed\x60\x44\xc3" "\xb0\x54\xb2\x2f\x60\xd3\x6d\x8b\x1b\xdb\x3c\x84\xe9\xa1\xb4\x9d" "\x29\x52\xea\x1a\x75\x5c\x9a\xf3\x26\xf5\xea\x4e\x53\x83\x16\x48" "\x35\x94\x68\x8c\xbb\x9a\x92\x09\xb6\x11\xa0\x24\xc8\x34\x2d\x84" "\xc5\xee\xad\xb7\xdb\x6f\xb7\x60\x28\x30\x84\xa4\x6e\x5e\xf9\x9c" "\x72\xa3\x27\xde\x2b\xfe\xf0\x0a\x52\xc5\xa0\x4b\x98\x27\x69\xa8" "\xb1\x4f\x17\x4d\x05\x10\x74\xf1\x38\x60\xf8\xa4\xff\x7d\xf8\x78" "\x16\x84\x41\xc8\xa1\x48\xc1\x4b\x3a\xbb\xff\x04\xdc\x3c\x0c\xe2" "\x43\x1e\x91\x0e\xbd\x14\xab\xf2\xdb\x72\x60\x73\x5c\xaf\x5f\xab" "\x66\x27\x4f\xf6\x34\xf5\x54\x89\xb9\xf2\xf7\x69\x02\x87\x8d\x25" "\xac\x1e\xda\x03\xa4\xcd\x07\x58\x53\x98\xd6\x8d\x0a\x74\x06\xe5" "\x1c\x1c\x91\x22\x9a\xbb\x10\xfc\xbe\x86\xcb\x85\x0d\xe5\x6b\x17" "\x54\xa0\x1a\x4d\x4a\xc2\xcc\x24\xe8\x03\xef\x4b\x9d\x7b\x21\x50" "\xe2\xf4\xe5\x4f\x73\x8c\xef\x95\xe1\x88\x6d\x2e\x1e\x88\x1d\xa4" "\x29\x4d\x35\xf0\x6d\x7d\xff\x43\xfd\x1b\x0e\x8a\xe3\xf2\xdd\xfc" "\x09\xf1\xd9\x34\x13\xc2\x23\xc8\x89\xb5\xee\xca\x53\xe2\x91\x7e" "\x1f\x94\xf0\xe0\xd8\x24\xdc\x33\x95\xd5\xbb\x29\x1a\x5b\xfe\xe6" "\x24\xf9\xe9\xcd\xe6\xfc\x94\x49\x8f\xb7\x06\x6c\x71\x81\xae\xad" "\x66\x67\x51\x2a\xc5\xc0\x39\x41\x5b\x41\xf5\x66\xff\x8c\xcc\x47" "\xbe\x3e\x02\x06\x02\x6c\xa4\x1c\x2a\x94\xca\x0f\x35\xe0\x23\x81" "\x68\x37\x9d\x96\xf5\x7b\xa3\x2d\xcc\x9e\xf4\xbd\x06\x4a\xcb\x07" "\xbf\x22\xf0\xbc\x01\x3e\xca\x76\x9f\x1f\xf0\x63\xc0\xba\x68\x4f" "\x62\x14\x93\x94\x47\xa0\x2a\x31\x65\x03\xdb\xe5\xa1\x84\xc6\x82" "\xe6\x05\xb7\xa3\x5c\x00\xcd\xdc\x82\x27\xde\xfe\x39\xcf\x4d\xe0" "\xb5\xfe\x83\x83\x80\xb2\x23\x81\xde\xe1\x6b\x37\xd7\x61\x5a\xb0" "\x64\xb0\xaa\x95\x9b\x94\xd6\xcc\x7d\x5c\x11\xaa\x5b\x4d\x49\x0c" "\x1c\x55\x2a\xd5\x7c\x44\x0c\x2f\x57\xc0\xf6\x3a\xcb\xf7\xc2\x1b" "\xe8\x5c\x2c\xe9\x91\x3e\x97\x82\x4a\xe1\xc0\xa4\xd7\xe2\x14\xd8" "\x9c\xf3\xf3\x8c\x35\xfd\xb1\x2a\xed\x52\x7c\x8e\xa9\xfe\x81\x56" "\x96\xa4\xb9\x87\x70\xef\x93\x5a\xdb\xd1\xca\xc6\x51\x9a\x94\x30" "\xb7\x8b\x64\x0c\x94\x82\x24\x63\x76\x95\x1a\x62\x11\x1f\x64\xc2" "\xda\xda\xbc\x1f\x2b\x84\x5b\xda\x97\xf1\x74\x9c\x83\x83\xb1\xfb" "\x34\x05\xce\xb6\x2e\x88\x5d\x70\x90\xb5\x1b\x63\x2d\x0e\x8c\x2e" "\x8b\x88\x9b\xfc\x98\x16\xe6\x7d\x72\x05\xd3\x21\x93\xdd\xdf\xed" "\xb0\x4e\xe9\xb1\x99\xd2\x24\x00\x0f\x46\xed\x7b\xf6\xbb\x2d\x37" "\xce\xc5\xad\xf5\x36\xff\x63\x58\x90\x71\x80\x1f\xb6\xe5\x15\x58" "\x27\xf4\x4b\x33\xa9\xc9\xde\x92\xea\x1d\x06\x9b\xb5\x83\x85\xf4" "\x8c\xe1\x0d\x51\x42\xd8\x93\x1b\xdd\x9a\x61\xa9\xe7\xf3\xa9\x5f" "\x77\xdd\x0d\x10\xff\xe0\x8d\xb7\x89\x6b\xb7\x6f\xb7\x5c\x62\xd3" "\xc2\x8f\xe0\x63\xe8\xcf\x21\x47\x74\xd0\xaa\xd5\xb3\xe9\x17\xb2" "\xc2\xad\xab\x32\x92\xd3\xa0\x76\xb5\x77\x1b\x9d\xb8\x85\x15\x66" "\x1e\x59\x4d\xb0\x5a\xfb\x7d\x6d\x29\x46\x5d\xe4\x86\x0e\x37\x75" "\x19\xde\xc8\xd5\xfd\x11\x38\x8c\xcb\x37\x16\xc5\x54\x57\x17\xb5" "\x5f\x29\x39\x9c\x0a\xfc\x6e\xe0\x36\x5b\xd7\x32\xf4\xcb\x80\xae" "\x10\xb9\x36\x58\xad\x33\x1b\x79\x0c\xdd\x86\x79\xef\xd8\xd3\xff" "\x20\x4f\x24\x50\xf1\xc8\x66\x90\xa2\x9c\xf3\x61\x88\x61\x77\xa9" "\xdf\x8d\x93\x9a\x15\xd1\x38\x05\xa4\x9d\xc3\x02\x62\x22\x78\xe8" "\x2a\x02\x5f\xb9\xa2\xd7\x0a\xe5\x15\xe6\x72\x04\xb6\x11\xd5\xd6" "\x1e\x45\x3f\x80\xb3\x5a\x94\x99\x22\x4a\x67\xbe\x33\x49\xe0\x5e" "\x27\xa7\x31\xe1\x8b\x88\x47\x58\xb8\x9e\xbf\x6d\x04\x7b\xe0\x0f" "\x02\xbe\x34\xe0\x14\x80\xbe\x17\x17\xa6\x21\x02\xa2\x6b\xa8\xa6" "\xd0\x18\xa9\x47\xb9\x71\xd1\x7e\x17\xfd\xd0\xa7\xc4\x2f\x51\x0f" "\x1d\x4a\x97\xda\x80\xea\x64\x0c\xef\x38\x7e\x7f\x5d\x09\xd4\x4e" "\xcd\xcb\x9c\x64\xe1\x63\x09\x80\xc7\x6a\x20\x26\x98\x95\xec\x2b" "\xd2\xff\x19\x24\x25\xd9\xd1\x0d\x0c\x18\x3d\x55\x56\x15\x4b\xe3" "\xf1\x13\x71\x39\x99\xb4\x3c\xdc\x90\xef\x44\x50\x6d\xeb\xb8\x4d" "\x37\xab\x66\xf2\x4c\x8a\x86\x4d\x45\x35\x73\x5f\xc5\x64\x2c\xa5" "\x44\xcf\xc3\x5f\x91\xa7\xbb\x1f\xa6\xfd\x79\x47\xde\x58\x79\x18" "\x0c\x31\x1d\x69\x98\xc0\xef\x94\x54\x2e\xc1\xc4\x25\xf9\x8f\x20" "\xc0\x66\xe2\x00\x14\xee\x02\x7c\x50\xc9\x40\xd0\x57\x28\xdb\x1f" "\x61\xb6\x90\x19\x4d\x12\x0f\x15\xbe\x41\x51\xe5\x76\xf6\xf2\xda" "\x54\xb4\x1b\xab\x93\xbc\xdb\x4c\x4b\x4b\x46\x88\x09\x23\x79\x80" "\xb3\xc4\xf5\x9f\xab\xb0\x00\xb4\x46\xe9\x5e\x11\xa8\x3d\xed\x92" "\x41\x03\x3a\xc4\xa6\x96\xbd\x00\x1e\x6f\xf7\xa4\xdc\x92\x4f\x6c" "\x5c\x16\xfa\x44\x4a\x8e\x1a\xb5\x1f\xd1\x7e\xb2\x4a\xcd\x4a\x5a" "\xd5\xd3\xcf\x14\x71\x0b\x17\x9f\xee\x6a\xa4\x90\xc0\xa2\x8a\x89" "\x8a\x62\xd1\x68\x1c\xa3\xdd\x68\xed\xe3\xdd\x99\x2c\x94\xb6\x45" "\xce\xd1\x74\x39\x00\xf4\x35\x96\xe1\x9a\x90\x35\x9a\x97\x30\x78" "\xf1\x16\x21\x94\xce\x9f\x36\x46\x25\xea\x48\x1a\xf6\x2d\x77\x5e" "\xfb\x63\x37\xe3\xbe\xdb\x8d\xb4\x13\xd7\x68\xfd\x2e\x56\x96\xf7" "\xb8\x9e\x91\xe0\x7c\xe3\x44\x6e\xbd\x52\x46\xdd\xbc\xec\xe6\x76" "\x57\x7a\xb9\x24\x66\xcd\xd3\x5d\xee\x33\x69\x8e\xd9\x68\xbf\xda" "\x63\xe9\x5c\x89\x67\xcd\xa2\x54\xa0\x26\xdc\xbc\x88\x55\x33\x3c" "\xad\x26\xca\x62\x39\x9b\xfc\xb8\x5e\xa8\x4e\xb2\xff\xfd\xaf\x54" "\x13\x32\x4a\x8b\xba\x6d\x72\x6f\xf7\xdb\x73\x43\xef\x1f\x4f\x02" "\x30\x17\x04\xfe\x88\x25\x07\x95\x41\x07\x4b\x85\x74\x74\x88\x03" "\x52\x2a\x08\x21\xa0\xfe\xa1\xe7\x51\x08\x43\x41\x00\x8e\xfe\x10" "\x1c\xb4\xe9\x02\x17\x03\x03\x05\x4b\x8f\xd5\xbb\x32\x19\x69\xdc" "\xf4\xbe\x44\xfd\x99\xca\xe5\x5c\xbf\xbe\x05\xb7\x93\x0f\x62\x1f" "\x04\xfc\xe4\xce\x5a\xa5\x9b\xdc\x23\xcc\x69\x47\x5b\xb5\xaa\x5e" "\x98\x5d\x4b\x37\x28\x3c\xad\x6d\x63\x23\x90\x91\x83\xc6\x10\x5e" "\xf7\xc8\xbf\x11\xfc\xf4\x71\x14\xbb\x25\x8d\x9b\xcd\x2e\xfd\x72" "\xd7\x47\x6f\x1e\x73\x3c\x75\x41\x4c\x7a\x96\x64\xf0\x18\xea\x58" "\x54\x5d\x9f\x3f\x88\x0d\x2f\xc0\xe1\x50\xd5\xbc\x6a\x09\x44\x8a" "\xf4\x45\x0b\x0e\x8b\x47\x25\x93\x18\x61\x2d\x75\x96\x2b\xe7\x5a" "\x37\xf1\x1e\xc1\x71\xcd\x1b\xee\x83\x13\x90\x03\x29\xbf\x0b\x5a" "\x4f\x38\x0c\x7c\xca\x17\x13\x2f\xde\x06\x27\x35\x70\xec\xca\xe2" "\x5b\x59\xbb\x16\x41\x12\x6c\x9c\xb4\xe0\x93\xc4\x44\x5a\x5b\xac" "\x99\xc9\xa4\x24\x29\xe4\x8f\x4f\x79\x60\xce\xee\x0a\xcf\x99\xda" "\xa5\x78\x7e\x6a\xea\x9f\xd6\x2a\xed\x41\x44\x78\xe8\x80\x07\xfc" "\xd8\x11\x87\x5e\x36\x0d\x0d\x44\x79\xf8\x2d\xb2\x17\xd9\x2a\xf5" "\xb4\x46\xb7\x17\x34\xe0\x7e\xd7\xb3\x5c\x16\x71\x66\xca\xda\x23" "\x3c\xcd\x52\x33\x66\x0e\x07\x54\x3e\x0f\xc5\x10\xc4\xb6\xa0\xae" "\xae\x1d\x80\x2a\x63\xf1\x4f\xb4\x1f\xad\xb3\x49\x58\x41\x6a\x2a" "\xd1\x7c\x13\x8d\x06\xaf\x60\xcf\xa9\xb4\x50\xc1\x32\x94\x5a\x15" "\x90\xc5\x51\x2f\x90\x68\x40\x7b\x55\x91\x50\x70\x64\x9d\x8a\x7f" "\xcd\x55\xc9\x60\x24\x47\x0c\xfc\x7c\x61\xcd\x8d\x8b\x2f\x36\x27" "\xb2\xf3\x41\x9d\x92\x14\x9a\xdd\x9b\x78\x22\x6c\x78\x82\x37\x15" "\x66\xdd\x3b\xbd\x31\xc4\x80\xa8\x8c\xd8\x95\x41\xb3\xcb\xdf\xb8" "\xd7\x70\xe9\xa4\x01\x68\xa7\x8c\xcb\x94\xd7\x10\xce\x0d\x2c\x9a" "\x13\x86\x16\xc6\x93\x8f\x08\x64\xb8\xe6\x49\x6e\x1d\xd3\x90\x44" "\x13\x76\x5e\x14\x3b\xbd\x06\xbf\x9c\x29\xff\x76\x25\x96\x2a\x56" "\x88\x2d\xaf\xfe\x5f\x4e\xd2\x67\xb6\x8b\x36\xa7\xf6\x8a\x9f\x7e" "\x0a\x58\xed\xaa\xe3\x68\x9d\x0a\x65\x7b\xc8\xb3\x5e\x2b\x47\xae" "\x9a\x4d\x19\xae\x3d\xdc\xb6\xf6\x97\xe3\x89\x3c\x3d\x91\xa4\xe8" "\x42\xb0\x45\xcb\x89\x3d\xc2\x8e\xa1\x04\xac\x44\xf8\x51\xab\xf4" "\x60\x54\xfe\x04\xa4\x42\x85\xd9\x4b\xdf\x54\xd0\x05\xce\x00\x15" "\x4c\xbb\xa6\x87\x4c\xe4\xcf\xfd\xbc\xd1\x5d\x9e\xdc\xb0\x20\x18" "\xaa\x2a\x5c\x7a\xb5\xf7\x8a\xe3\xb9\xdf\x9f\x9f\x5b\x74\xad\xc2" "\x5f\x60\x83\xf2\x98\x8b\xa7\xb7\x90\xc7\xf3\x9b\xe3\x98\x1b\x0d" "\x4b\x5e\xcd\x9f\x22\x82\x9e\xe8\x48\xad\x00\xe3\xe7\x80\x40\x01" "\x5c\xaa\x55\xa0\xd4\x56\x6e\x78\xee\xe2\xa7\x95\x6b\x27\xc8\xa8" "\xf9\xc6\xa1\x90\x45\x43\xc6\xcd\xff\x88\xac\xfa\x69\x08\x23\x69" "\xc8\x5d\x17\x5d\x38\xc7\x87\x60\xc7\xf7\x24\xfd\x33\x99\x1a\x3f" "\x0f\xff\x52\x50\xc9\x8c\xc1\x21\x4c\xd8\xf5\xd5\xd1\xd4\x23\x2e" "\x6a\xba\xbb\xed\xb2\xe5\x51\x99\xdd\x73\xd5\x39\xec\x80\x7d\x30" "\x85\x54\xb8\x57\xb4\x5b\x5d\x02\x23\x24\xba\x1c\x6e\x5d\xc5\xbe" "\xed\x8f\x1b\xf9\xa1\x6a\x83\x2c\xbe\xe8\xd4\x6a\x09\x7d\x09\x55" "\x92\x49\xc0\x9a\xea\x2e\xd8\x6b\xb9\xbe\x6f\x1b\xc1\x2d\xbc\xc4" "\x79\x27\x24\xa1\xda\x44\x18\x3e\xd2\xff\x92\x56\xf2\xef\x45\x0d" "\xc6\xb2\x34\x9b\x3c\xe1\x40\xb9\xb1\x14\xc5\xb6\xe0\x02\xab\x12" "\xd5\xb4\x6f\xdd\x71\xc1\x20\x92\xd6\x9a\xef\x30\x60\x36\x00\xaf" "\x09\x06\xeb\x0c\xbe\x29\xd2\xc3\x25\x46\x48\xc3\xd4\x8a\xd9\xdd" "\xd6\x44\x97\xed\xa4\xbf\x5a\x8d\xfd\x3d\x2d\x82\xa3\x3d\x5c\x0d" "\xde\xfe\x11\xd5\x3f\x7e\xc3\x69\xc2\xac\x9f\x7d\x08\x2e\xef\x8f" "\x2d\x30\xac\x7b\x76\xc1\x43\x29\x20\x50\x4a\xca\x87\x21\xd8\x61" "\x7c\x1c\xfe\x57\xb0\x66\x82\x91\xe7\x62\xa7\x95\x35\xdd\xda\x92" "\xff\xc5\x6d\x79\x84\x4b\xac\x9c\xef\x76\x40\x43\xa6\x86\x10\xa4" "\x5a\xa8\x16\x78\x40\xc3\x4a\x6f\xc3\xb8\xbd\x5f\xa1\x3a\x4f\x22" "\xca\xd0\x81\x0f\x20\xea\xde\xfc\x8e\x8d\x52\x9f\x6d\x3f\x12\x0f" "\xf0\xbb\x10\x17\x39\xa8\x7a\xec\xf4\xbc\x31\x95\xb3\xe4\x3e\x71" "\xbd\xc2\xbb\x2b\x71\x2f\x96\xc2\x50\xa0\xe5\xa9\xaa\x9a\xc1\x02" "\x06\x3b\x26\xc8\x6f\xf7\x0c\xea\x39\x2a\x30\x69\x6a\x43\x46\x94" "\xc6\x9e\xf9\x45\xc6\x15\x57\xc6\x9d\xc9\xd2\xdf\xee\xb1\xa0\xe1" "\x7e\x83\xd7\x5f\x47\xe0\x33\x3a\x3e\x54\x60\xd1\x9a\x9a\x1f\x9a" "\x91\xc4\x33\xd6\x2d\x39\xa1\x94\x26\x75\x8e\x1f\x03\x8f\x62\xb1" "\x48\x53\xb0\x3e\xe4\x83\x07\xd9\xb8\x02\x87\x5e\x34\x29\x05\xb9" "\xfd\x2a\xff\x57\xae\x68\x88\x7c\x9a\x5a\xe6\xa4\x3e\x4f\x95\x9c" "\x21\xe6\x62\x54\x30\xce\x21\x87\x7e\x64\x91\xf6\x08\x6f\x11\x66" "\xad\x62\x82\xe3\x9d\x15\xd2\xd0\xbb\xf1\x59\x38\x4d\xfa\x78\xa8" "\xf9\x40\x37\x2b\x88\xe2\xd2\xcf\xb9\x21\x24\xe5\xeb\xb2\x09\x0c" "\x09\xb8\xd4\x9c\x19\x05\x4f\x73\x42\x4a\x5a\x36\xeb\x75\xac\x96" "\xf8\xad\xc6\xdf\xe4\x88\x8b\xb4\xca\x59\x5a\x5e\x80\x56\x70\x7d" "\x20\x23\xcf\xf7\x50\x2d\xc8\x13\xb1\x2b\xac\x30\xe9\x18\xe2\xc8" "\xbf\x5d\xe9\x97\xff\x9a\x1a\x9f\x94\x31\x68\xb0\x23\xdc\xf2\x5c" "\xe8\xc7\xbc\xb2\xea\x40\x95\x1f\xcb\xc8\xf3\xc8\x44\x4a\xdd\x2e" "\xbd\x84\x29\x2d\x77\xef\x28\x78\x54\x68\x8a\x9d\x00\x2a\xcf\x1d" "\xbe\x04\x95\xf2\x1b\x35\x8a\x1f\xae\x62\x28\x6d\x03\xce\x8a\x26" "\xe3\xb9\x2d\xf7\x50\xb8\xbb\xd8\x42\x34\x49\x5a\xb1\x9c\x6e\xad" "\x03\xf9\x23\x60\x75\x8e\xc4\xbd\x33\x3c\xdc\x13\x9b\xfd\xa7\x1d" "\x28\x54\x48\x7a\x30\x08\xa6\xe6\xb2\xc6\xa1\x41\x27\xf5\x07\xf8" "\x70\xd6\x6c\x66\x19\xee\xe3\x5c\x61\xcc\x37\x82\xab\x36\x72\x4a" "\xcc\xe0\xd7\x93\x62\x94\xf1\x7e\x46\x5f\x05\xb7\x9e\x0e\x4d\xea" "\xc0\x55\xb9\x49\xcc\xc5\x06\xa5\xb4\x8d\xf6\x3f\x5d\x7c\x07\x8c" "\x3b\x90\xa1\x80\xa3\x2f\x9a\xe3\xf1\xc2\xcb\x1b\x74\x2b\x92\xae" "\xce\x19\xa2\xfc\xb4\x9f\xf4\xc7\x92\x88\xb2\x1e\x37\x2b\x8d\xc3" "\x24\x9e\x68\x44\x9d\xa0\x76\x8a\x7f\x3e\x03\xc1\x4d\x6a\xfa\xa1" "\x01\x4c\xd7\x09\xbc\x59\xc9\xa4\x72\x4b\xb9\x6d\x96\xde\x75\xb3" "\xe0\xde\x8f\x51\xf2\x9e\xe9\x19\x56\x4c\x36\x3f\xbe\x52\xcf\x9e" "\x7c\xab\x6a\xc7\x27\x2a\x71\x47\xe3\x9b\x96\x6b\xf5\xd9\x21\x06" "\x09\xbd\x9e\xbd\x00\xc5\xb2\x9d\x42\x81\x05\x48\x97\x0f\x9d\x46" "\x20\x82\xd3\x6c\x9d\xe7\x23\x82\x7e\x35\x0c\xe3\x15\xb2\x35\x88" "\x07\xbb\x69\x76\x17\x03\x03\x00\xeb\x91\x48\xe1\xda\x41\x9c\xbb" "\x7a\x0e\xa0\xed\x32\x03\x2e\xf2\x1d\x55\x29\xb3\x29\x09\x27\x30" "\xe6\x88\xb9\x3f\x1e\x21\x5e\xe6\x4a\xdb\xb1\xf8\xec\x18\xe3\x47" "\xc2\x95\x42\x39\x31\xb1\x66\x18\xb9\xaa\x04\x30\xde\x5a\xb1\xe3" "\x9d\xee\xa4\x7f\x0e\x0c\x93\x99\x08\x4d\x6d\x2e\x17\xd0\xd6\xa0" "\x0c\xf3\xf9\x49\x8f\x16\xc5\xd0\x78\x31\x40\xf3\xf7\xf9\xe0\x12" "\x0a\xff\xff\x4d\x81\x90\x06\xe5\x6c\x2d\x00\xaf\xa5\x70\x09\xf7" "\xa7\x9b\x02\xf2\x33\x67\x72\x10\xd6\x22\xf6\x87\x66\x03\x17\x73" "\x8e\x7d\xfe\x3d\x0e\x09\xf5\x32\x2a\x11\x0e\x1e\x46\x55\xf2\x6d" "\x23\xa0\x02\x56\x69\x4f\x92\x89\x44\xfd\x7a\x09\x5c\xdf\xd3\x58" "\xfa\x70\xc2\x86\x1c\x57\x8f\xfb\x93\x98\x83\xf4\x34\x43\x44\x3a" "\x5c\x89\xf1\x0c\xd4\x37\x72\x13\xd5\x53\x89\x6f\xfb\x30\x7b\x04" "\xe3\x3c\x61\xf6\x99\x01\xb1\x76\x8a\x3d\xe7\xf9\xf7\x1e\xe9\x37" "\xf1\xe0\x85\x5b\xa0\x9f\x5b\x56\xa9\x26\xb2\xff\x9b\xca\x29\x20" "\x64\x57\x95\x3f\xf5\x8b\x16\x86\x9a\x9f\xca\x1a\xb3\x43\x25\x1f" "\xd2\xf3\x60\x56\x17\x03\x03\x00\x35\x32\xda\xe9\xf2\x03\xc0\x8b" "\x19\x08\xbc\x02\xe3\x60\x6f\xee\xf9\x15\x50\xc1\x46\xeb\x78\x1f" "\x97\x96\x87\x7b\x4f\xc9\x27\x92\xb2\x5c\x0d\xbd\xb5\x0e\x92\x34" "\x10\xe2\x80\x98\x46\x34\x28\xd6\x83\xbd\xe8\xd8\xf0\x5d\x17\x03" "\x03\x00\xec\x8d\x37\x66\xe8\x26\xd6\x41\x25\xa7\xb0\x7f\x29\x7a" "\x42\x1a\xef\x99\x67\x0e\x86\x0f\x8a\xa1\x66\xb1\x1d\x78\x4e\xf0" "\x75\xd9\x77\xcb\x70\x64\x93\x1a\x47\x00\xee\x85\xc5\x65\x44\x8f" "\x9a\x74\x5f\xa1\x5c\x9d\xea\x72\xfa\x6a\x0d\x43\x66\xba\x9d\xee" "\x5c\x81\xf4\xbb\xd0\x69\x36\x1f\xf4\xee\x70\xb4\x74\xbd\x55\x08" "\xc0\x9a\x3c\x45\x05\x80\xf8\xa8\xf4\x13\x90\x54\x45\xf0\x61\x4a" "\x70\x00\x2d\xc3\xf2\x2e\x2c\x03\xc6\xb7\xe3\xc8\x1f\x6f\x8d\xb0" "\x97\xa3\xfa\xf6\x6a\xe3\x96\x9a\xba\x51\x21\x0a\xe0\xc6\x3a\xd0" "\x4b\x15\x81\xad\xed\xdf\x23\xf2\x01\x2b\xac\xe6\xff\x32\x43\x69" "\x15\x06\xc4\x32\xdc\x68\x2c\x60\x37\x8b\xa4\x94\x76\x12\x21\x69" "\xa0\x1f\x0d\x2e\x89\x1b\xe2\x4d\x32\x3f\x35\x3e\x97\x3e\x85\x1e" "\x59\xf4\x89\x3b\x96\x60\x81\x4d\x69\x8a\x32\x14\x4d\xcf\x76\x06" "\x40\x7e\xa0\x96\x2d\x1c\x34\x25\x27\x6e\x01\x04\x72\xb0\x0a\xb7" "\x18\x64\xd3\x5e\x20\x6b\xb9\x03\x54\x1d\x2f\x4c\x08\x72\x1d\xa1" "\xe0\xc5\xa2\xd9\x43\xf4\xdd\x27\x95\xab\xcf\xe5\x49\x45\x5c"; size_t ssl_test_case_1_size = sizeof(ssl_test_case_1); unsigned char ssl_test_case_2[] = { 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x46, 0x03, 0x01, 0x52, 0x8c, 0x30, 0x62, 0x00, 0x61, 0x09, 0xc1, 0xaf, 0xb1, 0x9e, 0x62, 0x99, 0x65, 0x39, 0x82, 0x77, 0x21, 0x59, 0x65, 0x26, 0x2a, 0xeb, 0x64, 0xd9, 0xe3, 0x5d, 0xe6, 0xf7, 0x55, 0x3b, 0xf1, 0x20, 0xb0, 0x14, 0x78, 0x6c, 0xb9, 0x39, 0x8e, 0x23, 0xf0, 0xe4, 0xdc, 0x43, 0xc2, 0x98, 0x51, 0x58, 0xe5, 0x24, 0x43, 0x28, 0xbc, 0xd8, 0xb9, 0x5e, 0xcb, 0x3a, 0x11, 0x04, 0x8f, 0xd7, 0xa9, 0xc7, 0x00, 0x39, 0x00, 0x16, 0x03, 0x01, 0x02, 0xf0, 0x0b, 0x00, 0x02, 0xec, 0x00, 0x02, 0xe9, 0x00, 0x02, 0xe6, 0x30, 0x82, 0x02, 0xe2, 0x30, 0x82, 0x01, 0xca, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x83, 0x7c, 0x42, 0x54, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x33, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x46, 0x47, 0x54, 0x38, 0x30, 0x43, 0x33, 0x39, 0x31, 0x31, 0x36, 0x30, 0x32, 0x31, 0x31, 0x32, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x46, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x30, 0x35, 0x31, 0x39, 0x32, 0x34, 0x35, 0x33, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x34, 0x30, 0x35, 0x31, 0x39, 0x32, 0x34, 0x35, 0x33, 0x5a, 0x30, 0x33, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x46, 0x47, 0x54, 0x38, 0x30, 0x43, 0x33, 0x39, 0x31, 0x31, 0x36, 0x30, 0x32, 0x31, 0x31, 0x32, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x46, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9f, 0xdb, 0xe1, 0x8d, 0x6e, 0x0b, 0xe1, 0xad, 0xbc, 0x3e, 0x76, 0xa9, 0x14, 0x3f, 0xb1, 0xfd, 0x1e, 0x95, 0xc5, 0xc8, 0x87, 0x2b, 0x72, 0xf5, 0x48, 0x32, 0xf0, 0x6b, 0x81, 0x9b, 0x56, 0x41, 0x92, 0x6e, 0x78, 0x1d, 0x26, 0x10, 0x1e, 0xcc, 0xc8, 0x2a, 0x1d, 0x74, 0x18, 0xad, 0x38, 0x1f, 0xb5, 0x36, 0xf3, 0xe2, 0x64, 0x45, 0x6b, 0x10, 0xde, 0x23, 0x4a, 0xfa, 0xfa, 0x44, 0x5c, 0xc6, 0xf7, 0x4f, 0xe2, 0xd2, 0x41, 0x76, 0xbc, 0x59, 0xbc, 0x8b, 0x13, 0x1b, 0xc5, 0x66, 0x0f, 0xbf, 0x96, 0x11, 0xe5, 0xc4, 0x35, 0x14, 0x7f, 0x16, 0x2f, 0x40, 0x9d, 0xe2, 0x88, 0xe6, 0xef, 0x29, 0xa1, 0x34, 0x10, 0xc7, 0x56, 0xe2, 0xec, 0xfd, 0x5b, 0x52, 0x88, 0x7c, 0x5b, 0xb1, 0x27, 0x9c, 0x85, 0xaa, 0x4b, 0x62, 0x34, 0xb8, 0xf5, 0xa2, 0x32, 0x1f, 0x4d, 0xfc, 0xe0, 0x81, 0xab, 0x08, 0xc3, 0xf7, 0x37, 0xf9, 0x44, 0x13, 0x9e, 0xfb, 0x48, 0x1c, 0xf5, 0xce, 0xcf, 0xbc, 0xbf, 0x56, 0xaf, 0xbf, 0x05, 0x1b, 0xf8, 0x60, 0x27, 0xdf, 0x22, 0x33, 0xea, 0xf3, 0xcf, 0x29, 0x19, 0x4b, 0x9b, 0xb9, 0xe6, 0x94, 0x60, 0xea, 0xc0, 0x8b, 0xfd, 0x12, 0xcc, 0xfc, 0x5e, 0x45, 0xd5, 0x6e, 0x1b, 0x47, 0x25, 0x36, 0x54, 0x2a, 0x97, 0x25, 0x53, 0x0e, 0xff, 0xa6, 0x0b, 0x0f, 0xfd, 0x47, 0xf8, 0xa7, 0x43, 0xfc, 0x2e, 0x32, 0xcc, 0x2a, 0xd0, 0x3d, 0xab, 0x1d, 0x9d, 0x12, 0x2e, 0x56, 0x82, 0x18, 0x08, 0xba, 0x03, 0xf3, 0x87, 0xbc, 0x89, 0x0e, 0x44, 0x82, 0x9a, 0xfd, 0x01, 0xfb, 0x19, 0xa6, 0xa0, 0x1e, 0x9c, 0xb4, 0x30, 0x6c, 0x20, 0xb3, 0xa8, 0x5a, 0x39, 0x9a, 0xa6, 0xee, 0x13, 0xb0, 0xed, 0xd0, 0x87, 0x00, 0x7d, 0xfd, 0xa8, 0x92, 0xc6, 0x06, 0xa2, 0x38, 0x31, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x2a, 0xaf, 0x06, 0x84, 0x68, 0xe4, 0x06, 0xe7, 0x2a, 0x5c, 0x37, 0x1f, 0x42, 0xbf, 0xfe, 0x76, 0x80, 0x89, 0xee, 0xf0, 0x04, 0xb9, 0x66, 0xec, 0x4e, 0x5e, 0x0f, 0xf4, 0x7f, 0x2c, 0x2a, 0x3b, 0xd6, 0x9c, 0x78, 0xc0, 0xb6, 0x52, 0x55, 0xb6, 0x8f, 0xf7, 0xca, 0x1a, 0xe7, 0x5f, 0xb6, 0xd5, 0xf4, 0x2d, 0x2b, 0xad, 0x28, 0x9e, 0x02, 0x6f, 0xf9, 0x48, 0xef, 0x99, 0x9a, 0xee, 0x9d, 0x94, 0x99, 0x2f, 0x8b, 0xa4, 0x18, 0xf0, 0xdf, 0x21, 0x32, 0x0f, 0x7a, 0x80, 0x5f, 0x63, 0x19, 0xd8, 0x84, 0x0f, 0x73, 0x9d, 0xa3, 0x95, 0x2b, 0xe8, 0x13, 0x45, 0xd3, 0x11, 0xb2, 0x2a, 0xcf, 0xb3, 0x06, 0x10, 0x01, 0x02, 0x58, 0x53, 0xb2, 0x75, 0x95, 0x6f, 0x79, 0xc7, 0x55, 0x9e, 0xe2, 0xb6, 0x1b, 0x79, 0xf1, 0x76, 0x27, 0x48, 0xb2, 0xc9, 0xea, 0x05, 0xad, 0x69, 0x5b, 0x06, 0xdb, 0x16, 0x5d, 0x9c, 0x00, 0x52, 0x09, 0xe0, 0x82, 0xe7, 0x07, 0x58, 0xc0, 0x01, 0x67, 0x94, 0xe1, 0x3e, 0xfc, 0xe1, 0x7d, 0x6e, 0xd3, 0x93, 0xf2, 0xfe, 0x53, 0xe8, 0x71, 0x89, 0x09, 0x1b, 0x13, 0x31, 0x47, 0xad, 0x26, 0xc3, 0xbe, 0x72, 0xb0, 0x58, 0x08, 0x66, 0x51, 0x4d, 0x5a, 0x9e, 0xf5, 0x8c, 0x29, 0x9b, 0xef, 0x97, 0xf5, 0xc1, 0xb4, 0x1e, 0xc0, 0x37, 0xe7, 0x79, 0x7d, 0x54, 0x5d, 0x36, 0xb3, 0xb6, 0xf9, 0x03, 0xb5, 0xb9, 0xa4, 0xf0, 0xc9, 0x93, 0x7d, 0x34, 0x63, 0x95, 0xc7, 0xe0, 0x77, 0xec, 0x18, 0x5f, 0xcb, 0x87, 0x2e, 0x7b, 0x87, 0xee, 0x6b, 0xbf, 0x38, 0xaa, 0x0b, 0xcf, 0xb0, 0x14, 0x84, 0x47, 0xd2, 0xd3, 0x25, 0x4b, 0x72, 0x86, 0xb5, 0xef, 0x60, 0x39, 0x94, 0x75, 0xe4, 0xc5, 0x58, 0xf3, 0x29, 0x9e, 0xe6, 0xe7, 0xbd, 0xeb, 0x36, 0x8b, 0x21, 0x63, 0xd1, 0xb3, 0x16, 0x03, 0x01, 0x02, 0x0d, 0x0c, 0x00, 0x02, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x00, 0x80, 0xbb, 0x86, 0xf1, 0x2c, 0xf4, 0x05, 0x26, 0xcb, 0x05, 0x6e, 0x35, 0x04, 0x34, 0xd5, 0x44, 0x00, 0xf4, 0xc5, 0x63, 0x89, 0xa8, 0x4e, 0x42, 0xd8, 0x12, 0x18, 0xfc, 0xfc, 0x15, 0x35, 0xe2, 0x9e, 0x30, 0xc5, 0x18, 0xc5, 0x2a, 0x96, 0x67, 0x0a, 0x63, 0xd1, 0xd5, 0x8b, /* }; char peer1_1[] = {*/ 0xe1, 0x32, 0x51, 0x78, 0xdf, 0x9e, 0xc8, 0xa0, 0x30, 0xa5, 0x42, 0x66, 0xac, 0x11, 0xc9, 0x86, 0xc1, 0xba, 0x93, 0xe3, 0x76, 0xab, 0x2b, 0x68, 0xd6, 0x78, 0xf0, 0xd5, 0xee, 0x0e, 0x61, 0x04, 0xb8, 0x60, 0x5b, 0x91, 0x30, 0x8f, 0x4e, 0x79, 0xf8, 0xa2, 0x51, 0xe9, 0xcb, 0x28, 0x29, 0xf1, 0x21, 0xef, 0xa3, 0x00, 0x35, 0x0d, 0xec, 0xb1, 0x6d, 0xf4, 0xef, 0xe9, 0xea, 0x5a, 0xce, 0x2c, 0x9a, 0xcb, 0x41, 0x14, 0xde, 0xfe, 0x3a, 0x2a, 0x6f, 0x7d, 0x0b, 0x69, 0x0a, 0x52, 0x61, 0x20, 0x2e, 0x9f, 0xa3, 0x2a, 0x01, 0x00, 0x5a, 0x65, 0x10, 0xde, 0x4a, 0x73, 0x2a, 0x04, 0x93, 0x73, 0x39, 0x33, 0xd3, 0x2d, 0x27, 0x48, 0xe9, 0x4a, 0x3e, 0xd4, 0x0c, 0x98, 0xbb, 0xd3, 0x20, 0x24, 0x3c, 0x31, 0x44, 0xae, 0xa4, 0xb8, 0xd1, 0x62, 0xb1, 0xd8, 0x52, 0xe8, 0x06, 0xc2, 0x95, 0xf0, 0x3f, 0x44, 0xd7, 0x76, 0x35, 0x24, 0xa9, 0x35, 0x35, 0x11, 0xda, 0x95, 0x10, 0x9b, 0xb9, 0xb6, 0xd2, 0xb8, 0x9d, 0x13, 0x35, 0xbc, 0x43, 0xd9, 0x5d, 0x44, 0xda, 0x88, 0x3a, 0xa4, 0xef, 0x23, 0xc6, 0x9e, 0x30, 0x00, 0xfc, 0x27, 0xf4, 0xb3, 0xb9, 0x0b, 0x00, 0x4d, 0x72, 0x56, 0xc5, 0xa7, 0xa5, 0x3c, 0x5b, 0x74, 0x55, 0x07, 0x8f, 0x12, 0x08, 0xf2, 0x74, 0x5d, 0x86, 0x38, 0xb3, 0xc9, 0x1c, 0xe9, 0xd2, 0xdb, 0xd3, 0x74, 0xe0, 0xe4, 0x70, 0x5a, 0x36, 0xc5, 0xa2, 0x2a, 0x79, 0xac, 0x59, 0xd9, 0x2b, 0xb1, 0xf0, 0xb3, 0xa4, 0xfd, 0x22, 0x8e, 0x43, 0x91, 0x60, 0x3d, 0x91, 0xd7, 0x02, 0xdf, 0x18, 0xb9, 0x8a, 0xc2, 0xf8, 0x3d, 0x8c, 0x4e, 0x4b, 0xec, 0x5f, 0x4d, 0x76, 0x3b, 0x9c, 0xda, 0x77, 0xa9, 0x86, 0x5a, 0x58, 0x4c, 0xb7, 0x3b, 0x15, 0x65, 0xf9, 0xfb, 0x46, 0xda, 0xa5, 0x71, 0x06, 0x92, 0xf0, 0x4f, 0x7f, 0x16, 0xb4, 0xe4, 0x7f, 0x28, 0x61, 0x7a, 0x04, 0x6a, 0xef, 0xf1, 0x95, 0x14, 0x8d, 0xe9, 0xd0, 0x11, 0xc9, 0x63, 0xa1, 0x6c, 0xc6, 0x86, 0xc5, 0x95, 0x03, 0x66, 0xf9, 0xd0, 0x8f, 0x88, 0xc9, 0x9c, 0xbb, 0x98, 0x7e, 0xa5, 0x42, 0x43, 0x48, 0xae, 0x8f, 0xab, 0x01, 0x45, 0x1d, 0x83, 0x10, 0x1f, 0x45, 0x46, 0x01, 0xe5, 0x92, 0x27, 0x62, 0xb4, 0x0e, 0xc5, 0xdb, 0xf9, 0xf6, 0xf1, 0x9b, 0xb4, 0xdc, 0x81, 0xa2, 0x6b, 0xa6, 0xff, 0xce, 0x7f, 0x71, 0x5c, 0x51, 0x93, 0x10, 0x6a, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, /* }; char peer1_2[] = {*/ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xa7, 0xa2, 0xb7, 0xa1, 0x1c, 0x86, 0x9c, 0x8a, 0x52, 0x0c, 0x66, 0x80, 0xc5, 0x22, 0xf9, 0x1e, 0x24, 0xa4, 0xb3, 0x0a, 0x8b, 0xcb, 0x8b, 0x72, 0x8d, 0x76, 0xb0, 0x16, 0xd6, 0x1d, 0x56, 0x96, 0x0f, 0xda, 0xc0, 0x44, 0xde, 0xa2, 0x81, 0xc6, 0x83, 0x5c, 0xd4, 0x41, 0xea, 0x31, 0xa3, 0x62, /* }; char peer1_3[] = {*/ 0x15, 0x03, 0x01, 0x00, 0x20, 0xf0, 0xf1, 0xdb, 0x2b, 0x08, 0xae, 0xc4, 0x76, 0xdf, 0x0c, 0x1c, 0xc1, 0xdd, 0x70, 0x42, 0xd2, 0x6f, 0x91, 0x8a, 0xb5, 0xd2, 0x9f, 0x26, 0x75, 0xa3, 0x45, 0xd0, 0x6e, 0x17, 0xb1, 0xb2, 0x7b }; size_t ssl_test_case_2_size = sizeof(ssl_test_case_2); unsigned char ssl_test_case_3[] = { 0x16, 0x03, 0x01, 0x00, 0x35, 0x02, 0x00, 0x00, 0x31, 0x03, 0x01, 0x52, 0x8c, 0x2f, 0xb6, 0x9d, 0x0c, 0x4c, 0x3e, 0xc2, 0xa5, 0x55, 0x2f, 0x0a, 0xa3, 0x03, 0x2d, 0x64, 0x64, 0x7b, 0xed, 0x92, 0x87, 0xa7, 0x94, 0x62, 0x41, 0x5b, 0x23, 0xef, 0x0d, 0x8f, 0xe0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x09, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, 0x0a, 0xf2, 0x0b, 0x00, 0x0a, 0xee, 0x00, 0x0a, 0xeb, 0x00, 0x05, 0x9b, 0x30, 0x82, 0x05, 0x97, 0x30, 0x82, 0x03, 0x7f, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x28, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1d, 0x50, 0x75, 0x70, 0x70, 0x65, 0x74, 0x20, 0x43, 0x41, 0x3a, 0x20, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x31, 0x31, 0x39, 0x30, 0x33, 0x33, 0x34, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x31, 0x31, 0x39, 0x30, 0x33, 0x33, 0x34, 0x30, 0x30, 0x5a, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x12, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xc7, 0xf7, 0xa6, 0xaf, 0x45, 0xe4, 0x20, 0xff, 0x02, 0x4d, 0xe2, 0xb2, 0xd6, 0x67, 0x97, 0x37, 0x4e, 0x7a, 0x58, 0x56, 0x81, 0xba, 0x07, 0x64, 0x1c, 0xf5, 0xa0, 0xb2, 0x86, 0xbd, 0xc4, 0x3d, 0x66, 0x93, 0x72, 0xd1, 0x3d, 0xf7, 0xa4, 0x43, 0xfb, 0x9e, 0xbe, 0x22, 0xd9, 0x27, 0x35, 0x6a, 0xc9, 0xe9, 0x17, 0x34, 0x69, 0x92, 0x60, 0xd9, 0x4b, 0xe0, 0xaa, 0x53, 0x46, 0xb3, 0x4b, 0x40, 0x12, 0xa4, 0xfa, 0xcc, 0xff, 0xfa, 0x5d, 0x72, 0x0e, 0xb9, 0xec, 0x32, 0xb8, 0xf7, 0x74, 0x4c, 0xda, 0xc2, 0xc4, 0x35, 0xae, 0xd1, 0xb8, 0x9f, 0x2e, 0xa4, 0x19, 0xe2, 0xa1, 0x94, 0xe2, 0xc4, 0x12, 0x49, 0x82, 0x97, 0x5b, 0xb6, 0x08, 0x79, 0xcf, 0xf8, 0x9c, 0x8a, 0x8d, 0x04, 0xfa, 0xb7, 0xe1, 0xec, 0xf1, 0x9e, 0xc3, 0x2d, 0x6c, 0x83, 0xfe, 0x4a, 0x9d, 0x72, 0x98, 0x99, 0xaa, 0xc0, 0xd7, 0x13, 0xb9, 0xb9, 0x34, 0x65, 0x47, 0x36, 0x44, 0xdd, 0x8d, 0x30, 0x49, 0x10, 0x04, 0x19, 0x64, 0x01, 0x79, 0x6d, 0xb4, 0xb0, 0x2a, 0x98, 0x95, 0xc6, 0xfb, 0x97, 0xa9, 0x4e, 0xd3, 0x30, 0x24, 0x37, 0x92, 0x87, 0x91, 0x6c, 0x0d, 0x04, 0x34, 0x40, 0x03, 0x20, 0x25, 0xd1, 0xde, 0x00, 0x0f, 0xad, 0xc1, 0xc9, 0xc3, 0xd4, 0x50, 0x0c, 0x90, 0xbd, 0x07, 0xd7, 0x9d, 0xd1, 0xf1, 0x88, 0xe0, 0xa1, 0x75, 0x5a, 0xe0, 0xdb, 0x36, 0x3c, 0x88, 0xa4, 0x9a, 0x84, 0x8b, 0x27, 0xfd, 0xe3, 0xd3, 0x92, 0x4b, 0xa4, 0x29, 0x81, 0xa7, 0x4a, 0x61, 0x64, 0xe3, 0xa8, 0x92, 0x97, 0xa1, 0xa2, 0xa9, 0xde, 0xdd, 0x27, 0x42, 0x77, 0xe9, 0x87, 0xb1, 0x17, 0x21, 0x70, 0xbb, 0x22, 0xab, 0xf4, 0x4c, 0x23, 0xc4, 0x8d, 0x0c, 0x7c, 0x3a, 0x9a, 0x44, 0x8d, 0xd3, 0x97, 0xf5, 0x30, 0x70, 0xd5, 0x05, 0x04, 0x0c, 0xaa, 0xb3, 0x19, 0x73, 0x90, 0x12, 0x9b, 0xf9, 0x9d, 0x0f, 0x37, 0x76, 0xe0, 0xfc, 0xa0, 0x1a, 0x32, 0xca, 0x2a, 0x14, 0x23, 0x2f, 0xaf, 0xed, 0x7c, 0x42, 0x36, 0xc0, 0xbb, 0x98, 0x27, 0xe3, 0xed, 0xaa, 0x80, 0xff, 0x4d, 0xea, 0x1d, 0x9f, 0x01, 0xc1, 0xb0, 0x51, 0x61, 0x96, 0x2b, 0x1c, 0x15, 0xcb, 0x38, 0x10, 0x8f, 0xbf, 0xd9, 0xa2, 0xf1, 0x01, 0x36, 0x8d, 0xec, 0x10, 0xb0, 0xea, 0x07, 0x43, 0xf5, 0xce, 0xb3, 0x80, 0xba, 0x6c, 0xb5, 0xdd, 0xc4, 0xaf, 0x17, 0x69, 0x18, 0x72, 0xd9, 0x34, 0x22, 0x00, 0xcc, 0x4f, 0x16, 0xb2, 0x75, 0x8c, 0x31, 0x7c, 0xeb, 0xd0, 0x90, 0x45, 0xba, 0x78, 0x02, 0xc1, 0xbb, 0x2c, 0xbe, 0x7c, 0x8a, 0xc4, 0xa2, 0x7b, 0x07, 0x8f, 0x54, 0x69, 0xc7, 0x54, 0x7a, 0x4a, 0xea, 0x5d, 0xc7, 0x02, 0x88, 0xeb, 0x8d, 0xdd, 0x59, 0xf5, 0x0d, 0xd8, 0xd5, 0x58, 0x45, 0xa9, 0xaf, 0xaa, 0xec, 0x38, 0x55, 0x40, 0xe0, 0x1a, 0xcf, 0x72, 0x1b, 0x67, 0x7c, 0xf7, 0x0c, 0x72, 0x5b, 0x55, 0xfd, 0xc0, 0x0d, 0x2e, 0xf3, 0x24, 0xb4, 0x63, 0xb1, 0x3d, 0xdd, 0xca, 0xeb, 0xac, 0x0c, 0x8a, 0xcb, 0x09, 0xb7, 0xaf, 0x05, 0xb5, 0x7d, 0x79, 0xb7, 0xa2, 0x75, 0xbc, 0xa7, 0x2d, 0x5a, 0x1e, 0x30, 0x68, 0x69, 0x98, 0x62, 0x7d, 0xaa, 0x42, 0xcd, 0x7f, 0xb9, 0x00, 0xda, 0xef, 0xac, 0xcc, 0x41, 0xe4, 0x61, 0x3e, 0x90, 0x9b, 0xeb, 0xdb, 0xe0, 0x01, 0x24, 0xf5, 0x3c, 0xc6, 0x9c, 0x46, 0x17, 0xa1, 0x9f, 0xfc, 0xc2, 0x47, 0x6f, 0xa6, 0xf5, 0x1b, 0x21, 0xd7, 0x00, 0x5d, 0x26, 0x1b, 0x67, 0x54, 0x36, 0x76, 0x71, 0x3e, 0xfa, 0xbf, 0x1d, 0x7d, 0xaf, 0x88, 0x8b, 0xe3, 0xe7, 0x2b, 0x8d, 0x53, 0x75, 0x73, 0x8c, 0xbb, 0x77, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xd6, 0x30, 0x81, 0xd3, 0x30, 0x39, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x32, 0x30, 0x30, 0x82, 0x06, 0x70, 0x75, 0x70, 0x70, 0x65, 0x74, 0x82, 0x12, 0x70, 0x75, 0x70, 0x70, 0x65, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x82, 0x12, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x20, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x37, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x2a, 0x16, 0x28, 0x50, 0x75, 0x70, 0x70, 0x65, 0x74, 0x20, 0x52, 0x75, 0x62, 0x79, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x53, 0x4c, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xde, 0x9c, 0x3e, 0x14, 0x35, 0x6f, 0x93, 0xe3, 0xbc, 0x84, 0x8b, 0x0c, 0xfb, 0x64, 0x30, 0x22, 0xf2, 0x9d, 0x31, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0xb7, 0x22, 0x97, 0x64, 0x5c, 0xde, 0x12, 0x6c, 0x37, 0x9d, 0x56, 0x41, 0x92, 0x12, 0xf0, 0x9f, 0xc4, 0xcd, 0x99, 0x38, 0x8e, 0x06, 0x78, 0x0b, 0x76, 0xdd, 0x87, 0x31, /* }; char peer1_1[] = {*/ 0x62, 0xb2, 0x92, 0xb5, 0xd6, 0x1c, 0xd0, 0xb2, 0x92, 0xb5, 0xd6, 0x63, 0x47, 0x58, 0xe3, 0x99, 0x55, 0x0b, 0xee, 0xa1, 0xe7, 0x9a, 0x0d, 0xac, 0x57, 0x97, 0xbc, 0x13, 0xb7, 0x24, 0xb2, 0x5a, 0x9a, 0x49, 0x28, 0xfa, 0xf6, 0x68, 0xdb, 0xe1, 0x1a, 0xe8, 0x58, 0x7a, 0x00, 0x35, 0xa7, 0x4b, 0xe2, 0xcd, 0x28, 0x89, 0x50, 0xe3, 0x20, 0xb0, 0x93, 0xbb, 0x1c, 0xae, 0x04, 0x3e, 0x80, 0xbd, 0x54, 0xeb, 0xeb, 0x41, 0x7b, 0xd5, 0x92, 0x5b, 0xc3, 0x89, 0xed, 0x5f, 0xdd, 0xed, 0x10, 0xb9, 0x41, 0xef, 0xeb, 0x30, 0x7a, 0x18, 0xba, 0x88, 0x4a, 0x49, 0x25, 0x06, 0xab, 0x1d, 0xfa, 0xe9, 0xc2, 0x0e, 0x31, 0x26, 0x52, 0xfd, 0x09, 0xac, 0xff, 0x1d, 0xb7, 0xfa, 0x2e, 0xb5, 0x6f, 0x3e, 0x04, 0x30, 0xa9, 0xf8, 0x66, 0x3c, 0xc0, 0xaf, 0x16, 0x7b, 0xaf, 0x88, 0xdb, 0xd4, 0x1a, 0xbf, 0x1f, 0xef, 0x4f, 0xbb, 0xc2, 0xf4, 0x6f, 0x98, 0x84, 0x77, 0x64, 0xd6, 0xd2, 0x28, 0x8f, 0xa4, 0xf7, 0x21, 0x64, 0xae, 0x9d, 0xc2, 0x50, 0xd3, 0xd9, 0xd2, 0xe0, 0x19, 0x31, 0xbf, 0xcd, 0xf8, 0x5a, 0x7e, 0x51, 0x07, 0x85, 0x6b, 0x67, 0x31, 0x87, 0xcc, 0x28, 0x6e, 0xa5, 0xc5, 0x11, 0x86, 0x62, 0xb8, 0x4f, 0xe4, 0x4b, 0x19, 0xdc, 0x79, 0xd2, 0xd3, 0x6e, 0x25, 0x8c, 0x88, 0x14, 0x0d, 0xf4, 0x04, 0xcf, 0x52, 0xc6, 0xff, 0x2a, 0xdb, 0x04, 0xbf, 0xc1, 0x1b, 0x4e, 0x10, 0x7a, 0x37, 0x56, 0x89, 0xb3, 0x83, 0xb4, 0x4a, 0x07, 0xb3, 0x23, 0x5f, 0xbe, 0x41, 0xc4, 0x3c, 0xc7, 0xc0, 0xec, 0xe0, 0xe8, 0x5c, 0x1a, 0xde, 0xff, 0x1f, 0xf4, 0x20, 0x8e, 0x57, 0x0e, 0x6c, 0x57, 0xa3, 0xdf, 0xab, 0x63, 0x13, 0x15, 0xd8, 0x63, 0xcb, 0x44, 0x89, 0x58, 0xb7, 0xfd, 0x81, 0x4c, 0x2c, 0xdb, 0x98, 0x6a, 0xe0, 0xe8, 0x6f, 0x20, 0xe8, 0xd3, 0xaf, 0x1c, 0x5d, 0x7a, 0x5f, 0x2c, 0xb5, 0xd9, 0x2f, 0xf9, 0x3b, 0xf8, 0x86, 0xda, 0xbf, 0x1d, 0xd1, 0x3e, 0xaa, 0xf1, 0xa2, 0x7d, 0x9c, 0xa3, 0xbf, 0xfb, 0x79, 0x54, 0xed, 0x4d, 0xd5, 0xc9, 0x22, 0xe8, 0x48, 0x95, 0x05, 0xf0, 0xe2, 0x8c, 0xc5, 0x3e, 0xea, 0x8e, 0xeb, 0x50, 0xaf, 0x9d, 0xe1, 0x8a, 0x0b, 0x63, 0x12, 0x89, 0x7b, 0x2f, 0x1f, 0x0e, 0x78, 0x1a, 0x86, 0xde, 0x0d, 0xd4, 0x70, 0x15, 0xa5, 0xa2, 0xd2, 0xb3, 0x98, 0x19, 0xd8, 0x7f, 0xef, 0x43, 0x11, 0x12, 0x76, 0x36, 0xdb, 0xde, 0x06, 0x1e, 0xe1, 0x56, 0x9a, 0x44, 0xce, 0xc8, 0xcc, 0x3b, 0x52, 0x3c, 0x3c, 0x74, 0xe5, 0x99, 0x40, 0xe1, 0xc5, 0xdf, 0xc0, 0x17, 0xc0, 0x96, 0xcf, 0xcb, 0x93, 0x60, 0xc3, 0xf6, 0x36, 0x25, 0x1e, 0x27, 0x09, 0x11, 0xae, 0x63, 0xf2, 0x88, 0x04, 0xbe, 0x0f, 0xda, 0x3f, 0x61, 0xe4, 0x5a, 0xa5, 0x89, 0xde, 0x3e, 0x3c, 0x93, 0x1f, 0xad, 0xf9, 0x67, 0x25, 0xf3, 0x35, 0x3d, 0x61, 0x4e, 0x67, 0x25, 0x4b, 0x27, 0xad, 0x6f, 0xcf, 0x78, 0xeb, 0x8e, 0x77, 0xf0, 0xe4, 0x2c, 0xc2, 0xc4, 0xcd, 0xbd, 0xaa, 0x72, 0x66, 0x4b, 0x46, 0x14, 0x45, 0xe2, 0xa8, 0x66, 0xce, 0xa2, 0xb3, 0xac, 0x24, 0x2d, 0x97, 0x9b, 0x5e, 0x18, 0xe2, 0xf7, 0x97, 0x3b, 0xce, 0x9d, 0xe4, 0x1a, 0xa0, 0x89, 0x49, 0xb2, 0xa2, 0xdf, 0x0d, 0x1d, 0xc2, 0x3d, 0x6d, 0xc4, 0x04, 0xec, 0xb2, 0x5e, 0x91, 0xc2, 0x8c, 0xcf, 0x40, 0xeb, 0x52, 0x7a, 0x1b, 0x46, 0x0c, 0x00, 0x05, 0x4a, 0x30, 0x82, 0x05, 0x46, 0x30, 0x82, 0x03, 0x2e, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x28, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1d, 0x50, 0x75, 0x70, 0x70, 0x65, 0x74, 0x20, 0x43, 0x41, 0x3a, 0x20, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x31, 0x31, 0x39, 0x30, 0x33, 0x33, 0x33, 0x35, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x31, 0x31, 0x39, 0x30, 0x33, 0x33, 0x33, 0x35, 0x39, 0x5a, 0x30, 0x28, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1d, 0x50, 0x75, 0x70, 0x70, 0x65, 0x74, 0x20, 0x43, 0x41, 0x3a, 0x20, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xbf, 0x81, 0x70, 0x18, 0x7b, 0x07, 0x5f, 0x6d, 0x33, 0x27, 0x2c, 0x32, 0x4d, 0x20, 0x0d, 0x2e, 0xed, 0x05, 0xaa, 0xd0, 0xfa, 0x2f, 0xcb, 0x97, 0x00, 0x17, 0xe8, 0xcf, 0xcb, 0x68, 0x4b, 0x93, 0x9a, 0x0d, 0x64, 0x71, 0x15, 0x2c, 0x5b, 0x43, 0x98, 0x87, 0xa4, 0x2b, 0xa9, 0x45, 0xcb, 0x55, 0x3d, 0x17, 0xe4, 0xed, 0x43, 0xae, 0x2b, 0x08, 0x56, 0xae, 0xd4, 0xcd, 0x4b, 0x12, 0xd9, 0x54, 0xbe, 0x1f, 0x45, 0xf5, 0x53, 0x92, 0x0b, 0x58, 0xc9, 0x4b, 0xec, 0x15, 0x14, 0xb9, 0xb9, 0x92, 0xf4, 0x0d, 0xbd, 0xf7, 0xee, 0xb5, 0x36, 0xab, 0xc7, 0x54, 0xc3, 0x78, 0x32, 0xed, 0xec, 0x91, 0x1c, 0x8c, 0x6b, 0x72, 0xfc, 0x69, 0xfb, 0x2a, 0xbe, 0xd7, 0x18, 0x6a, 0xa1, 0xc5, 0xa9, 0x28, 0x91, 0xbf, 0xc1, 0x12, 0x0e, 0xff, 0x75, 0xa2, 0x61, 0x9b, 0xc1, 0x01, 0x49, 0x69, 0xee, 0xde, 0x00, 0x18, 0x89, 0x7b, 0xfc, 0x9c, 0x4f, 0x10, 0xe5, 0x40, 0x21, 0x3b, 0x0e, 0x15, 0x37, 0x64, 0x25, 0x0b, 0x27, 0xa4, 0xd1, 0x8b, 0xd0, 0x60, 0x54, 0xb4, 0xe2, 0x8b, 0x1a, 0xe2, 0x97, 0x63, 0x51, 0x71, 0xdc, 0xb1, 0x6f, 0xe2, 0x74, 0xc9, 0x37, 0xd5, 0x51, 0x26, 0x1a, 0xdc, 0x4a, 0x2d, 0x69, 0xd6, 0xa3, 0x16, 0x5d, 0x25, 0x21, 0x58, 0xbf, 0x30, 0xb9, 0xc1, 0x25, 0x70, 0x5b, 0xf5, 0x77, 0x9d, 0x01, 0x4a, 0x26, 0x7e, 0xca, 0x84, 0x60, 0xa5, 0x1d, 0x64, 0xfa, 0x27, 0x22, 0x89, 0x27, 0xa6, 0xb9, 0xf5, 0x76, 0xc1, 0x2e, 0xba, 0x91, 0xb6, 0xd3, 0x83, 0x59, 0x07, 0xeb, 0xa4, 0x30, 0x49, 0xb1, 0x35, 0x33, 0x46, 0xab, 0x0d, 0xf9, 0xf9, 0xb7, 0xb8, 0x13, 0x6a, 0xc3, 0xa7, 0x66, 0x19, 0x2f, 0x26, 0x87, 0xdd, 0x91, 0x9d, 0xc5, 0xf1, 0xc1, 0xc7, 0x82, 0xaf, 0x9e, 0x73, 0x28, 0x52, 0xbd, 0xb2, 0xa5, 0xa4, 0xae, 0xba, 0xdb, 0xbe, 0x29, 0xd4, 0x00, 0x65, 0x69, 0xe6, 0xc0, 0x01, 0x38, 0xe8, 0xb1, 0x74, 0xf7, 0xa4, 0x84, 0x61, 0x83, 0x12, 0xec, 0x16, 0x8d, 0x1a, 0xb7, 0xaa, 0x9f, 0x36, 0xae, 0x8e, 0x50, 0x06, 0x9c, 0x0f, 0xe9, 0x74, 0xff, 0xa2, 0x9b, 0x55, 0x5b, 0xc4, 0xb1, 0x25, 0xd0, 0x49, 0x03, 0xdb, 0xe4, 0xd5, 0x11, 0x03, 0xa0, 0x10, 0x82, 0x0c, 0x35, 0xae, 0xcb, 0x62, 0x6b, 0x3e, 0x93, 0xc9, 0x69, 0xa9, 0xfd, 0x66, 0x74, 0x51, 0x44, 0x46, 0xe9, 0xd7, 0x1a, 0xa3, 0x9e, 0x4d, 0xbe, 0xca, 0x84, 0xf7, 0x57, 0x18, 0x8f, 0x8f, 0xce, 0x78, 0xf5, 0xe7, 0xb6, 0x7b, 0xed, /* }; char peer1_2[] = {*/ 0x60, 0x7b, 0xae, 0x43, 0x62, 0x0f, 0x83, 0x0d, 0x0c, 0xce, 0xaf, 0xf3, 0x1f, 0xc7, 0x69, 0xc4, 0x55, 0x4a, 0xe1, 0x35, 0xe8, 0xc1, 0x9b, 0x66, 0xfa, 0x04, 0xe4, 0x99, 0x31, 0x4e, 0x4d, 0x97, 0xca, 0x2d, 0xe6, 0xf4, 0x0a, 0x31, 0x7e, 0x51, 0x47, 0xdf, 0x2f, 0xce, 0x3f, 0xdd, 0x50, 0x3d, 0xd5, 0xe6, 0x81, 0x0e, 0xb5, 0x8c, 0x40, 0x8d, 0x58, 0xd2, 0x4d, 0x1b, 0x8f, 0xd0, 0xd5, 0x16, 0xcd, 0x2e, 0x29, 0x8f, 0x40, 0xa4, 0x23, 0x3b, 0xc3, 0x1f, 0x56, 0x5e, 0x42, 0x03, 0x12, 0x79, 0x9a, 0x66, 0x25, 0xec, 0x55, 0x4b, 0xd7, 0x2d, 0xb5, 0xe8, 0x62, 0xb1, 0xf4, 0x7e, 0x93, 0x0b, 0xfc, 0x9a, 0x71, 0x52, 0x09, 0x2d, 0x61, 0xfb, 0x65, 0x22, 0x07, 0x68, 0x7e, 0x9c, 0xc7, 0x74, 0xf9, 0xe7, 0xbe, 0x16, 0x99, 0x92, 0x14, 0x71, 0x2e, 0x91, 0xc2, 0x36, 0xf9, 0x2d, 0xb9, 0x95, 0x9d, 0x3b, 0x3a, 0x3e, 0x16, 0xfb, 0x09, 0x8c, 0x84, 0x52, 0x50, 0x2f, 0x78, 0x62, 0x37, 0x54, 0xef, 0x69, 0xdf, 0xd4, 0x3f, 0xa5, 0xc1, 0x4e, 0x67, 0xd5, 0xd5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x7b, 0x30, 0x79, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x37, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x2a, 0x16, 0x28, 0x50, 0x75, 0x70, 0x70, 0x65, 0x74, 0x20, 0x52, 0x75, 0x62, 0x79, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x53, 0x4c, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xde, 0x9c, 0x3e, 0x14, 0x35, 0x6f, 0x93, 0xe3, 0xbc, 0x84, 0x8b, 0x0c, 0xfb, 0x64, 0x30, 0x22, 0xf2, 0x9d, 0x31, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0xa9, 0x16, 0x08, 0xe6, 0xb8, 0x3c, 0x04, 0x33, 0xcd, 0xcc, 0x87, 0x9a, 0x7b, 0x41, 0xdd, 0xe7, 0x6b, 0xe0, 0xa9, 0xfd, 0x2a, 0x35, 0x3a, 0xd8, 0xb1, 0xf1, 0xf8, 0x5c, 0x85, 0xd4, 0x3b, 0xab, 0x61, 0xc1, 0xd2, 0x2e, 0x21, 0x00, 0xfe, 0x1b, 0xd6, 0xeb, 0x0c, 0x75, 0xfb, 0x80, 0x66, 0x3d, 0x01, 0xc9, 0xb1, 0x3e, 0x87, 0xd7, 0x11, 0x8c, 0x93, 0x2f, 0x91, 0x44, 0x35, 0xf8, 0xe0, 0x7d, 0xaf, 0xe5, 0xd9, 0x24, 0xd8, 0xc1, 0x0d, 0xc4, 0x48, 0x72, 0x45, 0xdc, 0x3b, 0x4f, 0x23, 0xed, 0x1a, 0x57, 0xe0, 0xeb, 0x2c, 0x91, 0x09, 0x1c, 0xa0, 0x9f, 0x10, 0x77, 0xb6, 0x71, 0x81, 0x0a, 0x21, 0xca, 0x09, 0x3c, 0xea, 0xa0, 0x5d, 0x03, 0x88, 0xdb, 0x00, 0x48, 0xf0, 0x2b, 0x49, 0xee, 0x49, 0xc8, 0x83, 0xd1, 0x14, 0xe7, 0xcd, 0x92, 0x98, 0x23, 0xc3, 0xbd, 0xdb, 0x19, 0x30, 0x7b, 0xe8, 0xb2, 0xc0, 0x7f, 0xff, 0x0c, 0xa2, 0x52, 0x45, 0x16, 0xc3, 0x74, 0x3a, 0x9b, 0xfa, 0xc2, 0x71, 0xad, 0x6e, 0xc5, 0x80, 0xe6, 0x9a, 0x55, 0x09, 0x2b, 0xc4, 0xf0, 0xe8, 0xdb, 0x75, 0x15, 0xdc, 0x58, 0xdb, 0x7d, 0xa6, 0xf7, 0x48, 0xfd, 0x68, 0x86, 0x10, 0xdc, 0xaf, 0x49, 0x5d, 0xe7, 0x05, 0x7f, 0xd0, 0x4d, 0xf6, 0xfe, 0xf8, 0xf6, 0x2f, 0x88, 0x2c, 0xed, 0xeb, 0xee, 0x89, 0x55, 0xcf, 0x69, 0xb2, 0x3c, 0x39, 0x61, 0xb8, 0x4b, 0xba, 0xbb, 0x2b, 0x6d, 0x83, 0xc9, 0x04, 0xe7, 0x03, 0xe7, 0xd8, 0xd7, 0xb5, 0x43, 0x4b, 0x9b, 0x3f, 0xd9, 0xf8, 0xa4, 0xb2, 0xdb, 0x83, 0x54, 0x45, 0xe0, 0x18, 0xde, 0x56, 0x0f, 0x72, 0x21, 0x26, 0xda, 0xcd, 0xc8, 0x80, 0x3b, 0x88, 0x0f, 0xff, 0xf3, 0xe0, 0x3d, 0x8c, 0xba, 0x87, 0x83, 0x78, 0x55, 0x10, 0xed, 0x0b, 0x6d, 0x73, 0x69, 0x2d, 0x47, 0xa4, 0xc6, 0xf4, 0xef, 0xb2, 0xb6, 0xbf, 0xb7, 0xda, 0x33, 0x76, 0xa4, 0x06, 0x1b, 0x39, 0x1a, 0x98, 0xa5, 0x5a, 0xde, 0x81, 0x73, 0xfd, 0xe2, 0x0e, 0xb7, 0xe0, 0x3d, 0x80, 0x91, 0x78, 0xa2, 0x67, 0x0a, 0x8b, 0x8b, 0x90, 0xe0, 0x8e, 0x6b, 0xda, 0x58, 0x3b, 0xfc, 0x77, 0x74, 0xdb, 0x77, 0xcf, 0xf2, 0xf1, 0xed, 0x37, 0x16, 0xae, 0x87, 0xd9, 0x39, 0x39, 0x82, 0x4e, 0x3b, 0x0f, 0xf7, 0xed, 0x8d, 0x19, 0x86, 0xfd, 0x7d, 0x6c, 0x21, 0x03, 0x2d, 0x89, 0x66, 0xaa, 0x3e, 0x2f, 0xf9, 0x07, 0x60, 0xd7, 0x89, 0x33, 0xb3, 0x77, 0xe8, 0x72, 0x77, 0x63, 0xf0, 0x5f, 0xe7, 0x2a, 0xae, 0xf1, 0xe1, 0xe2, 0xa1, 0x85, 0x3b, 0x8c, 0x85, 0x8f, 0x30, 0x37, 0x0e, 0x66, 0x83, 0x48, 0x0a, 0xd9, 0x35, 0xc1, 0xcf, 0x1e, 0x72, 0x62, 0x99, 0x7a, 0xa1, 0xf0, 0x8b, 0xc4, 0xf0, 0x0d, 0x89, 0x58, 0x3f, 0x20, 0x72, 0x9e, 0x96, 0x80, 0x70, 0x5f, 0x71, 0x25, 0x92, 0x22, 0xf5, 0x27, 0x38, 0x2a, 0x4e, 0x03, 0x3e, 0xdc, 0x4a, 0x2a, 0x1a, 0xe2, 0x4a, 0x20, 0x1a, 0x3e, 0xb1, 0x8e, 0x59, 0x7b, 0xe8, 0x15, 0x2e, 0xf9, 0x37, 0x2b, 0x96, 0xeb, 0x34, 0xef, 0x73, 0x47, 0x62, 0xc8, 0x5e, 0x58, 0x96, 0x8d, 0xb0, 0xfb, 0xe9, 0xbf, 0xd8, 0xe6, 0x6a, 0x8e, 0x22, 0x71, 0x07, 0x63, 0xe5, 0xbe, 0xf0, 0x62, 0x93, 0xba, 0x8c, 0x8d, 0x4b, 0x3d, 0x88, 0x36, 0x8f, 0x01, 0x29, 0xc8, 0xb4, 0x35, 0x28, 0xc7, 0x73, 0x78, 0xd9, 0x1e, 0xcd, 0x35, 0xa9, 0xca, 0xec, 0xea, 0xd0, 0x3d, 0x87, 0x1d, 0xf8, 0x4c, 0x06, 0x6d, 0xd4, 0x9e, 0x30, 0xcb, 0x45, 0x79, 0xff, 0x68, 0x7e, 0x2d, 0x29, 0x14, 0x1e, 0x69, 0x53, 0xb5, 0x83, 0x54, 0x80, 0xa6, 0xa5, 0x33, 0x93, 0xd0, 0xc8, 0x16, 0x03, 0x01, 0x03, 0x0d, 0x0c, 0x00, 0x03, 0x09, 0x00, 0x80, 0x9d, 0x25, 0x39, 0x5c, 0xb4, 0x54, 0x8a, 0xff, 0x25, 0xe6, 0xd6, 0x9f, 0x4c, 0xc3, 0xc1, 0x8d, 0xa1, 0xfa, 0xba, 0x88, /* }; char peer1_3[] = {*/ 0x4c, 0x53, 0xa9, 0x74, 0xda, 0xfa, 0xba, 0x0b, 0x20, 0xbe, 0x40, 0xd7, 0xba, 0xe7, 0x1d, 0x70, 0x28, 0x61, 0x60, 0x4c, 0x49, 0x01, 0x5f, 0xd9, 0x0f, 0x60, 0x16, 0x3d, 0xba, 0xd3, 0xa9, 0x5e, 0xfa, 0x98, 0x64, 0x60, 0x26, 0x0e, 0x04, 0x75, 0xd8, 0x13, 0xd7, 0x31, 0xb4, 0x8e, 0xad, 0xeb, 0x9c, 0x57, 0x4c, 0x8f, 0x65, 0xf3, 0x90, 0x16, 0x31, 0xdc, 0x15, 0x6f, 0x7d, 0x1d, 0x00, 0xae, 0x76, 0xf2, 0xd1, 0x11, 0xd1, 0x4f, 0x88, 0x7b, 0x29, 0x9f, 0xf6, 0xce, 0x68, 0xef, 0x57, 0xe7, 0x85, 0xf2, 0x40, 0x54, 0x1c, 0x12, 0x40, 0xa2, 0x35, 0x25, 0xcf, 0x12, 0xa3, 0xe1, 0x07, 0x8e, 0xdb, 0x1d, 0xb4, 0x14, 0xff, 0x57, 0xe7, 0x19, 0x8d, 0x51, 0x77, 0x83, 0x00, 0x01, 0x02, 0x00, 0x80, 0x96, 0x22, 0x80, 0xe9, 0x86, 0xcc, 0xfc, 0x63, 0x58, 0x45, 0x78, 0xe0, 0x9b, 0xbb, 0x2e, 0x89, 0x1e, 0x5a, 0x7a, 0x11, 0xbf, 0xb8, 0xed, 0x6d, 0x69, 0x49, 0xf5, 0xdb, 0xa8, 0x58, 0x6d, 0x39, 0x03, 0x66, 0x8b, 0x1a, 0x54, 0xab, 0xe3, 0x31, 0xc8, 0xc9, 0x07, 0x10, 0x0d, 0x28, 0x0b, 0x81, 0x15, 0xf1, 0x28, 0xb7, 0xed, 0xc2, 0x80, 0x0a, 0xef, 0xef, 0xa3, 0x23, 0x2d, 0x0c, 0x20, 0x76, 0xd1, 0x5b, 0x33, 0x07, 0x6b, 0x99, 0xc2, 0xb1, 0xcb, 0x90, 0x87, 0x5e, 0xf3, 0xef, 0x1d, 0xa6, 0x57, 0x3b, 0x0d, 0x32, 0xad, 0xd0, 0x96, 0xc6, 0xe2, 0xd1, 0x38, 0x65, 0x7e, 0x7c, 0xe1, 0xe4, 0xb1, 0x2e, 0x53, 0xfd, 0xf6, 0x01, 0xc6, 0x0d, 0x5b, 0x0d, 0xe3, 0x12, 0x21, 0x78, 0xc4, 0x2b, 0x4f, 0xa9, 0x47, 0x71, 0x1b, 0x05, 0x68, 0x9b, 0x52, 0x90, 0xc8, 0x35, 0xd8, 0x1b, 0x98, 0x00, 0x02, 0x00, 0x9b, 0x91, 0x80, 0xfb, 0xa2, 0x15, 0xb4, 0xab, 0xf7, 0xee, 0x57, 0xb0, 0xe4, 0xe8, 0xb7, 0x46, 0x3a, 0x90, 0x3b, 0x83, 0x18, 0xfb, 0x41, 0x2c, 0x58, 0xb4, 0xa5, 0x19, 0x01, 0xb5, 0x4c, 0x44, 0x79, 0x11, 0x4b, 0x41, 0x00, 0xf6, 0x9e, 0x2b, 0x71, 0x43, 0xea, 0xb6, 0xb4, 0xc5, 0xc7, 0x8f, 0x39, 0xf8, 0x32, 0xeb, 0x69, 0xf5, 0xde, 0xbc, 0xc9, 0x45, 0x70, 0x38, 0x03, 0xcf, 0x72, 0xf9, 0x14, 0x75, 0x00, 0xf0, 0xfd, 0x2e, 0x96, 0xd9, 0x3a, 0xad, 0xae, 0x8f, 0x96, 0x50, 0xc1, 0x5d, 0xe4, 0xd9, 0x13, 0x60, 0x44, 0x25, 0x90, 0xaa, 0xd1, 0x50, 0x5f, 0x01, 0xd4, 0x5d, 0x51, 0x2d, 0x6b, 0xaf, 0x6a, 0xb9, 0x13, 0x27, 0xdc, 0x5a, 0x79, 0xde, 0xb5, 0x66, 0xe7, 0xc9, 0x52, 0x57, 0x8f, 0x6f, 0x72, 0x39, 0x00, 0x5b, 0x8f, 0xdb, 0x8e, 0x19, 0x98, 0x9b, 0x12, 0x3b, 0xb4, 0xb3, 0x32, 0x74, 0x4c, 0x8d, 0x8b, 0xd3, 0xfe, 0xb9, 0x7d, 0xb0, 0xad, 0x9d, 0x81, 0x47, 0x69, 0x8b, 0x7b, 0xeb, 0x79, 0x39, 0x6c, 0xf6, 0x40, 0xe5, 0xb0, 0x95, 0x56, 0x7f, 0xbc, 0x8f, 0x4e, 0xed, 0x5d, 0xff, 0x32, 0x53, 0x5d, 0x72, 0x65, 0x5e, 0xdf, 0xe3, 0xa6, 0xfe, 0xce, 0x6b, 0x1d, 0xbf, 0x54, 0xe0, 0xc7, 0x93, 0xca, 0xb0, 0xd2, 0x7a, 0x03, 0x2d, 0xe4, 0x0d, 0x94, 0x3a, 0xbf, 0xe5, 0x3a, 0xbe, 0xa9, 0x6a, 0xa9, 0x1b, 0xe9, 0x6b, 0x51, 0x87, 0x18, 0x3f, 0xe7, 0xd6, 0x8e, 0x46, 0xb4, 0x91, 0xda, 0x66, 0xd7, 0xd3, 0x5f, 0xdd, 0x9b, 0xd9, 0xb3, 0xd0, 0xc8, 0x0d, 0xbd, 0xba, 0xb3, 0x20, 0xeb, 0x27, 0xd0, 0x4b, 0xa9, 0xa6, 0x42, 0x09, 0xef, 0x33, 0xb9, 0x25, 0xc7, 0xa6, 0xa4, 0x09, 0xce, 0xef, 0xe4, 0xe1, 0xb3, 0x6c, 0x4c, 0x98, 0xc9, 0x7a, 0xb1, 0x32, 0xb3, 0x0f, 0x31, 0xa0, 0x56, 0xc8, 0xc9, 0x8c, 0x1e, 0xc8, 0xed, 0x55, 0xd1, 0xdf, 0xd4, 0x13, 0xe8, 0x95, 0x4f, 0x6e, 0x4f, 0xb2, 0xd9, 0x1c, 0x19, 0xb5, 0x1a, 0x43, 0x6f, 0x69, 0x8f, 0x9c, 0x1c, 0xaa, 0xff, 0xbf, 0xe9, 0xc1, 0x5d, 0x6e, 0x85, 0x9f, 0x65, 0x7d, 0xc5, 0xdb, 0x76, 0x59, 0x58, 0xfb, 0xe3, 0xd6, 0x24, 0xd8, 0xae, 0xc4, 0xc7, 0x84, 0x74, 0xab, 0x40, 0xa0, 0xc4, 0xeb, 0xd9, 0x13, 0x05, 0x0c, 0xd6, 0x5a, 0x9d, 0x98, 0xb7, 0x88, 0x39, 0x1d, 0xb7, 0x33, 0xff, 0x1d, 0xc6, 0xd4, 0x49, 0xec, 0x5d, 0x93, 0x2f, 0x5c, 0xf1, 0xc9, 0x0e, 0x1f, 0x89, 0xf0, 0x93, 0x20, 0x53, 0x5e, 0xde, 0x85, 0x78, 0x49, 0x69, 0x51, 0x32, 0x6c, 0x53, 0x1b, 0x50, 0xfb, 0xef, 0x34, 0x57, 0xec, 0xb0, 0x33, 0x95, 0x54, 0xf3, 0x39, 0x1d, 0x89, 0x92, 0x02, 0x46, 0x26, 0x4e, 0xce, 0x8e, 0x95, 0xa3, 0x9a, 0x51, 0xec, 0xcb, 0xb3, 0x91, 0x5e, 0x00, 0x03, 0x64, 0x38, 0xbf, 0x41, 0xb8, 0x0d, 0x61, 0xb8, 0x99, 0x10, 0xfd, 0xb6, 0xf4, 0x98, 0x01, 0xca, 0x7a, 0x1b, 0x8c, 0xd6, 0x62, 0x28, 0xf1, 0x67, 0x87, 0x4f, 0x02, 0xe4, 0x59, 0x73, 0x39, 0x5b, 0xe2, 0xa6, 0x39, 0xb0, 0xb2, 0x0a, 0xc0, 0x86, 0xe4, 0xec, 0x77, 0x86, 0x8b, 0x72, 0xb5, 0x1a, 0x6e, 0xaa, 0x52, 0xf3, 0x6a, 0x13, 0x69, 0xb9, 0x27, 0x75, 0x5d, 0xf5, 0x85, 0xe5, 0xbf, 0x81, 0x50, 0x1a, 0xcc, 0x43, 0x7a, 0x76, 0x68, 0x81, 0x95, 0x9b, 0x02, 0x3b, 0x1a, 0x5d, 0x76, 0x2b, 0xd0, 0xc1, 0x89, 0x9b, 0xe1, 0xb3, 0xea, 0x5c, 0x08, 0x4f, 0x7a, 0x0c, 0x77, 0xf4, 0xd8, 0xcc, 0x78, 0xd2, 0x96, 0x07, 0xe1, 0x61, 0x3c, 0x9e, 0x0f, 0x80, 0x22, 0x51, 0x22, 0x65, 0xfc, 0x6e, 0xa4, 0x67, 0x0c, 0x85, 0xbc, 0xb9, 0x16, 0x03, 0x01, 0x00, 0x10, 0x0d, 0x00, 0x00, 0x08, 0x05, 0x03, 0x04, 0x01, 0x02, 0x40, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00 }; size_t ssl_test_case_3_size = sizeof(ssl_test_case_3); const char google_cert[] = "\x30\x82\x04\x76\x30\x82\x03\x5e\xa0\x03\x02\x01\x02\x02\x08\x6b" "\xa4\xae\xf1\xc9\xe3\x08\x5e\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7" "\x0d\x01\x01\x05\x05\x00\x30\x49\x31\x0b\x30\x09\x06\x03\x55\x04" "\x06\x13\x02\x55\x53\x31\x13\x30\x11\x06\x03\x55\x04\x0a\x13\x0a" "\x47\x6f\x6f\x67\x6c\x65\x20\x49\x6e\x63\x31\x25\x30\x23\x06\x03" "\x55\x04\x03\x13\x1c\x47\x6f\x6f\x67\x6c\x65\x20\x49\x6e\x74\x65" "\x72\x6e\x65\x74\x20\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x20\x47" "\x32\x30\x1e\x17\x0d\x31\x34\x30\x34\x30\x39\x31\x31\x34\x30\x31" "\x31\x5a\x17\x0d\x31\x34\x30\x37\x30\x38\x30\x30\x30\x30\x30\x30" "\x5a\x30\x68\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53" "\x31\x13\x30\x11\x06\x03\x55\x04\x08\x0c\x0a\x43\x61\x6c\x69\x66" "\x6f\x72\x6e\x69\x61\x31\x16\x30\x14\x06\x03\x55\x04\x07\x0c\x0d" "\x4d\x6f\x75\x6e\x74\x61\x69\x6e\x20\x56\x69\x65\x77\x31\x13\x30" "\x11\x06\x03\x55\x04\x0a\x0c\x0a\x47\x6f\x6f\x67\x6c\x65\x20\x49" "\x6e\x63\x31\x17\x30\x15\x06\x03\x55\x04\x03\x0c\x0e\x77\x77\x77" "\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x30\x82\x01\x22\x30" "\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82" "\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\x9f\x3c\x67\x60" "\x62\xa7\x30\xbb\xb7\x63\xd0\x00\xe0\x98\xab\x24\x3e\x15\xdf\x1a" "\x85\x54\x8f\xf0\xd9\x0d\xc6\x77\x32\x79\xe0\x48\x1c\x76\x46\x22" "\x61\x64\x93\xa3\x12\xcf\xd8\xd1\x86\x5d\x23\x9f\xca\xc7\x3b\xd9" "\x4b\xad\xf0\xf0\xe4\x83\x3d\x64\x93\xb3\x21\x3f\xef\x03\x9f\xde" "\x5f\x01\x9e\xbc\x73\xf2\xf4\xeb\xc3\x71\x39\x4c\x42\x6b\x97\x23" "\x3c\x0a\x4b\x55\x46\x07\x6f\x55\x17\x90\xfa\x57\xb0\xbe\x54\x77" "\x3d\x77\x2e\x20\x74\x53\xad\xd3\x59\x28\x4d\xd3\x8d\x38\x93\xf3" "\x04\xa0\x0d\xec\xa6\x3e\x62\x2e\x90\x9f\x18\x3e\x22\x71\xcc\xd9" "\x81\xf6\x49\xab\x50\x86\xfd\xde\xed\x5a\x7c\xc8\x00\xa6\x9d\x87" "\xa1\xee\x4b\x91\xe8\xd0\x91\x8e\xdc\x4b\xbc\x5b\xf4\xd2\xae\xa5" "\x62\x37\x7e\x7a\xd2\x5d\x37\x6b\x5d\x18\xee\xde\x6d\x8a\xb3\x8e" "\xc0\x52\x28\xbe\x52\x5a\xdd\xd8\x95\x5d\xd3\xdf\xb4\x71\x64\x6a" "\x8a\x03\x25\xe4\xc1\xd2\xbf\xfe\x9d\xa3\xb0\xaf\xd0\x01\x4a\x90" "\x60\x8e\x89\xfa\x0a\x0e\xbf\x08\x66\x89\x42\x0a\x88\x5d\x86\xd5" "\xc5\x2f\xa8\xcd\x8f\x8a\xfc\xab\xef\xbd\xc4\x9a\x44\x01\xd4\x0a" "\x89\x9b\x7b\xa6\x0b\xe6\xee\x6f\xc1\x5d\x55\x9f\x02\x03\x01\x00" "\x01\xa3\x82\x01\x41\x30\x82\x01\x3d\x30\x1d\x06\x03\x55\x1d\x25" "\x04\x16\x30\x14\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x01\x06\x08" "\x2b\x06\x01\x05\x05\x07\x03\x02\x30\x19\x06\x03\x55\x1d\x11\x04" "\x12\x30\x10\x82\x0e\x77\x77\x77\x2e\x67\x6f\x6f\x67\x6c\x65\x2e" "\x63\x6f\x6d\x30\x68\x06\x08\x2b\x06\x01\x05\x05\x07\x01\x01\x04" "\x5c\x30\x5a\x30\x2b\x06\x08\x2b\x06\x01\x05\x05\x07\x30\x02\x86" "\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x70\x6b\x69\x2e\x67\x6f\x6f\x67" "\x6c\x65\x2e\x63\x6f\x6d\x2f\x47\x49\x41\x47\x32\x2e\x63\x72\x74" "\x30\x2b\x06\x08\x2b\x06\x01\x05\x05\x07\x30\x01\x86\x1f\x68\x74" "\x74\x70\x3a\x2f\x2f\x63\x6c\x69\x65\x6e\x74\x73\x31\x2e\x67\x6f" "\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x6f\x63\x73\x70\x30\x1d\x06" "\x03\x55\x1d\x0e\x04\x16\x04\x14\x15\xc9\xbc\x55\xa7\x7d\x75\x7c" "\xa5\x0c\x5e\xdc\x92\xee\xdf\x42\x59\x0c\xe5\x3c\x30\x0c\x06\x03" "\x55\x1d\x13\x01\x01\xff\x04\x02\x30\x00\x30\x1f\x06\x03\x55\x1d" "\x23\x04\x18\x30\x16\x80\x14\x4a\xdd\x06\x16\x1b\xbc\xf6\x68\xb5" "\x76\xf5\x81\xb6\xbb\x62\x1a\xba\x5a\x81\x2f\x30\x17\x06\x03\x55" "\x1d\x20\x04\x10\x30\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\xd6" "\x79\x02\x05\x01\x30\x30\x06\x03\x55\x1d\x1f\x04\x29\x30\x27\x30" "\x25\xa0\x23\xa0\x21\x86\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x70\x6b" "\x69\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x47\x49\x41" "\x47\x32\x2e\x63\x72\x6c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d" "\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x2c\xfa\x64\xeb\xb3\x14" "\xe9\x84\x08\x0f\xf9\x30\x94\x19\x65\xff\x8f\xf5\x6e\x2c\x1f\xcd" "\x15\x94\x11\x48\xaf\x8d\x37\x8b\x7c\xa7\x02\x70\xde\x71\x57\x56" "\x0e\x46\xbc\xdd\x03\xb3\x34\x5e\x66\xf2\x85\xe8\x3b\xf6\xfe\x32" "\x48\xa0\xa8\x91\xc8\x80\xb9\xf1\xd2\x16\x5b\x7d\x2c\x93\xb3\x7c" "\x66\x82\x15\xb2\x84\x96\x47\xfa\xbe\x55\xb6\x4e\xa6\x56\x12\x69" "\x60\xa0\x90\x9f\xe2\xc2\x8e\x60\x5e\x62\x61\x56\x66\x77\x7d\x95" "\x37\xa8\x78\xd6\x94\xf2\x06\x50\x2f\x9c\xf8\x61\xc9\x57\x9c\x26" "\x60\x28\xcd\x7a\xe9\x77\xfe\xc9\x61\x38\x6c\x05\x5b\x38\x97\xf1" "\xff\x1e\xad\x78\xef\xad\x00\x64\x6b\x51\x90\xd3\x49\xce\xa1\x58" "\x63\xce\x66\x36\xe6\xde\x48\xf8\xe0\x6a\xe6\x27\xd4\xba\x62\x62" "\x9d\xdc\x7a\x29\x98\x2f\x9b\x11\x4c\x9a\x9a\x82\xcb\x31\x3c\x69" "\xff\xd2\xae\x56\x33\x45\x20\xdd\xc4\x66\x29\xc7\xd4\xff\x06\xf4" "\x26\x59\x43\x16\x2b\x3e\x7d\x73\x1e\x45\xfd\x8a\x5f\xd4\x77\x02" "\xfc\xe2\x49\xbd\x5e\x9d\xc3\xd9\x8c\x70\x97\x42\x3d\x7b\x63\x1c" "\xfc\xbc\x2e\x8d\x19\xc0\x5f\xaa\x2c\xa2\x15\x40\x61\x4d\xae\x9b" "\x2c\x17\x2d\xc3\xee\x38\x3e\x18\xd6\x32"; size_t google_cert_size = sizeof(google_cert) - 1; const char yahoo_cert[] = "\x30\x82\x07\xb9\x30\x82\x06\xa1\xa0\x03\x02\x01\x02\x02\x10\x1d" "\xc0\x12\x4a\x02\x4a\x2c\xd6\xce\x88\xc9\x4c\x0f\x24\xf1\xcf\x30" "\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x81" "\xb5\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x17" "\x30\x15\x06\x03\x55\x04\x0a\x13\x0e\x56\x65\x72\x69\x53\x69\x67" "\x6e\x2c\x20\x49\x6e\x63\x2e\x31\x1f\x30\x1d\x06\x03\x55\x04\x0b" "\x13\x16\x56\x65\x72\x69\x53\x69\x67\x6e\x20\x54\x72\x75\x73\x74" "\x20\x4e\x65\x74\x77\x6f\x72\x6b\x31\x3b\x30\x39\x06\x03\x55\x04" "\x0b\x13\x32\x54\x65\x72\x6d\x73\x20\x6f\x66\x20\x75\x73\x65\x20" "\x61\x74\x20\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x76" "\x65\x72\x69\x73\x69\x67\x6e\x2e\x63\x6f\x6d\x2f\x72\x70\x61\x20" "\x28\x63\x29\x31\x30\x31\x2f\x30\x2d\x06\x03\x55\x04\x03\x13\x26" "\x56\x65\x72\x69\x53\x69\x67\x6e\x20\x43\x6c\x61\x73\x73\x20\x33" "\x20\x53\x65\x63\x75\x72\x65\x20\x53\x65\x72\x76\x65\x72\x20\x43" "\x41\x20\x2d\x20\x47\x33\x30\x1e\x17\x0d\x31\x34\x30\x34\x30\x39" "\x30\x30\x30\x30\x30\x30\x5a\x17\x0d\x31\x35\x30\x34\x30\x39\x32" "\x33\x35\x39\x35\x39\x5a\x30\x81\x84\x31\x0b\x30\x09\x06\x03\x55" "\x04\x06\x13\x02\x55\x53\x31\x13\x30\x11\x06\x03\x55\x04\x08\x13" "\x0a\x43\x61\x6c\x69\x66\x6f\x72\x6e\x69\x61\x31\x12\x30\x10\x06" "\x03\x55\x04\x07\x14\x09\x53\x75\x6e\x6e\x79\x76\x61\x6c\x65\x31" "\x13\x30\x11\x06\x03\x55\x04\x0a\x14\x0a\x59\x61\x68\x6f\x6f\x20" "\x49\x6e\x63\x2e\x31\x1f\x30\x1d\x06\x03\x55\x04\x0b\x14\x16\x49" "\x6e\x66\x6f\x72\x6d\x61\x74\x69\x6f\x6e\x20\x54\x65\x63\x68\x6e" "\x6f\x6c\x6f\x67\x79\x31\x16\x30\x14\x06\x03\x55\x04\x03\x14\x0d" "\x77\x77\x77\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x30\x82\x01" "\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" "\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xbe\xac" "\xb9\x4f\xc4\xb7\xea\x0a\xa1\x6d\x56\xab\xee\x6a\x27\x06\x21\x8c" "\x82\x4f\x60\xfc\xbe\xab\x92\x80\xa1\x11\x6e\x36\x01\xa6\x1a\x3b" "\xdb\xf0\xb9\xda\x55\xed\x91\xa6\x1d\x1a\x5a\x30\xb7\x5c\xc9\x1b" "\xdb\x03\x8f\x7c\xeb\x74\x3a\x58\xaf\x0f\x6d\x08\xf5\x70\xd3\x42" "\xed\x1c\xd1\xce\xac\xea\xd7\xb8\x40\x10\xb5\x20\xf3\x04\x0a\x7d" "\x8c\x34\xac\x05\xba\xec\x84\x31\x30\x07\x00\x4d\xb9\xc5\x64\xb9" "\x61\x91\xae\x37\xe7\x4e\x91\x64\xbc\x51\x60\xd2\x61\xbb\x5a\x58" "\x4a\x05\x10\xae\xc8\x84\x80\x30\xaa\xd1\x37\x21\x4d\x46\x63\xaa" "\x5b\xaa\x31\x2e\x82\x3e\x58\x76\xb3\xaa\xb9\xb7\x91\xe2\x2a\xbf" "\x8e\x3c\x8b\xe5\x40\x16\xba\xcf\x6f\xbe\x90\x40\x2a\xe2\xe7\x6d" "\x6e\x0d\x2c\xb0\x39\xfa\x46\x83\x4c\x88\xea\xdb\x10\xb5\x25\x80" "\x3a\x29\x60\x40\x55\x25\x1a\xfa\xc4\x7f\x5d\xe9\x2e\x18\xac\x4e" "\x69\x3a\xcb\x3b\x30\xe9\xb2\xe0\xba\x91\x71\x6d\xb4\xcb\x1b\x0c" "\x13\x00\x51\x19\x1f\x36\x66\xfb\xf0\x22\x11\xef\xde\xd9\x24\xbf" "\x53\xdd\xa0\x6c\x94\x0c\x13\x51\xa1\x07\x26\xcf\xb7\x9e\xf4\x26" "\xe3\x88\x9f\x9f\x4e\x99\xf4\xe7\x8f\x90\x4c\xca\x7f\x39\x02\x03" "\x01\x00\x01\xa3\x82\x03\xf2\x30\x82\x03\xee\x30\x82\x02\x8b\x06" "\x03\x55\x1d\x11\x04\x82\x02\x82\x30\x82\x02\x7e\x82\x0d\x77\x77" "\x77\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x09\x79\x61\x68" "\x6f\x6f\x2e\x63\x6f\x6d\x82\x0e\x68\x73\x72\x64\x2e\x79\x61\x68" "\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x75\x73\x2e\x79\x61\x68\x6f\x6f" "\x2e\x63\x6f\x6d\x82\x0c\x66\x72\x2e\x79\x61\x68\x6f\x6f\x2e\x63" "\x6f\x6d\x82\x0c\x75\x6b\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d" "\x82\x0c\x7a\x61\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c" "\x69\x65\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x69\x74" "\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x65\x73\x2e\x79" "\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x64\x65\x2e\x79\x61\x68" "\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x63\x61\x2e\x79\x61\x68\x6f\x6f" "\x2e\x63\x6f\x6d\x82\x0c\x71\x63\x2e\x79\x61\x68\x6f\x6f\x2e\x63" "\x6f\x6d\x82\x0c\x62\x72\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d" "\x82\x0c\x72\x6f\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c" "\x73\x65\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x62\x65" "\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0f\x66\x72\x2d\x62" "\x65\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x61\x72\x2e" "\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x6d\x78\x2e\x79\x61" "\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x63\x6c\x2e\x79\x61\x68\x6f" "\x6f\x2e\x63\x6f\x6d\x82\x0c\x63\x6f\x2e\x79\x61\x68\x6f\x6f\x2e" "\x63\x6f\x6d\x82\x0c\x76\x65\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f" "\x6d\x82\x11\x65\x73\x70\x61\x6e\x6f\x6c\x2e\x79\x61\x68\x6f\x6f" "\x2e\x63\x6f\x6d\x82\x0c\x70\x65\x2e\x79\x61\x68\x6f\x6f\x2e\x63" "\x6f\x6d\x82\x0c\x69\x6e\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d" "\x82\x0c\x73\x67\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c" "\x69\x64\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x12\x6d\x61" "\x6c\x61\x79\x73\x69\x61\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d" "\x82\x0c\x70\x68\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c" "\x76\x6e\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x11\x6d\x61" "\x6b\x74\x6f\x6f\x62\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82" "\x14\x65\x6e\x2d\x6d\x61\x6b\x74\x6f\x6f\x62\x2e\x79\x61\x68\x6f" "\x6f\x2e\x63\x6f\x6d\x82\x0f\x63\x61\x2e\x6d\x79\x2e\x79\x61\x68" "\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x67\x72\x2e\x79\x61\x68\x6f\x6f" "\x2e\x63\x6f\x6d\x82\x0d\x61\x74\x74\x2e\x79\x61\x68\x6f\x6f\x2e" "\x63\x6f\x6d\x82\x0c\x61\x75\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f" "\x6d\x82\x0c\x6e\x7a\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82" "\x0c\x74\x77\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x68" "\x6b\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0d\x62\x72\x62" "\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x0c\x6d\x79\x2e\x79" "\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x82\x10\x61\x64\x64\x2e\x6d\x79" "\x2e\x79\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x30\x09\x06\x03\x55\x1d" "\x13\x04\x02\x30\x00\x30\x0e\x06\x03\x55\x1d\x0f\x01\x01\xff\x04" "\x04\x03\x02\x05\xa0\x30\x1d\x06\x03\x55\x1d\x25\x04\x16\x30\x14" "\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x01\x06\x08\x2b\x06\x01\x05" "\x05\x07\x03\x02\x30\x43\x06\x03\x55\x1d\x20\x04\x3c\x30\x3a\x30" "\x38\x06\x0a\x60\x86\x48\x01\x86\xf8\x45\x01\x07\x36\x30\x2a\x30" "\x28\x06\x08\x2b\x06\x01\x05\x05\x07\x02\x01\x16\x1c\x68\x74\x74" "\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x76\x65\x72\x69\x73\x69\x67" "\x6e\x2e\x63\x6f\x6d\x2f\x63\x70\x73\x30\x1f\x06\x03\x55\x1d\x23" "\x04\x18\x30\x16\x80\x14\x0d\x44\x5c\x16\x53\x44\xc1\x82\x7e\x1d" "\x20\xab\x25\xf4\x01\x63\xd8\xbe\x79\xa5\x30\x45\x06\x03\x55\x1d" "\x1f\x04\x3e\x30\x3c\x30\x3a\xa0\x38\xa0\x36\x86\x34\x68\x74\x74" "\x70\x3a\x2f\x2f\x53\x56\x52\x53\x65\x63\x75\x72\x65\x2d\x47\x33" "\x2d\x63\x72\x6c\x2e\x76\x65\x72\x69\x73\x69\x67\x6e\x2e\x63\x6f" "\x6d\x2f\x53\x56\x52\x53\x65\x63\x75\x72\x65\x47\x33\x2e\x63\x72" "\x6c\x30\x76\x06\x08\x2b\x06\x01\x05\x05\x07\x01\x01\x04\x6a\x30" "\x68\x30\x24\x06\x08\x2b\x06\x01\x05\x05\x07\x30\x01\x86\x18\x68" "\x74\x74\x70\x3a\x2f\x2f\x6f\x63\x73\x70\x2e\x76\x65\x72\x69\x73" "\x69\x67\x6e\x2e\x63\x6f\x6d\x30\x40\x06\x08\x2b\x06\x01\x05\x05" "\x07\x30\x02\x86\x34\x68\x74\x74\x70\x3a\x2f\x2f\x53\x56\x52\x53" "\x65\x63\x75\x72\x65\x2d\x47\x33\x2d\x61\x69\x61\x2e\x76\x65\x72" "\x69\x73\x69\x67\x6e\x2e\x63\x6f\x6d\x2f\x53\x56\x52\x53\x65\x63" "\x75\x72\x65\x47\x33\x2e\x63\x65\x72\x30\x0d\x06\x09\x2a\x86\x48" "\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x1f\x3c\xb5" "\x6f\x4d\xd3\x15\x6a\x7b\x02\x63\x7a\xe0\x10\xba\x45\xf4\xa6\x47" "\xca\x8c\x47\xc0\x1d\x13\x88\xed\xe7\xbe\x11\x34\x72\x89\x91\xd2" "\x53\x54\xaa\xd2\xc5\x3c\x00\xb7\x70\xcd\xe5\x30\xb8\xa1\x79\x47" "\xae\xbc\xeb\x5c\x48\x37\x57\xbe\xe6\x1b\x9e\x55\x30\x21\x7f\x17" "\x00\xad\xd6\x1b\x5c\xb9\x3b\x94\xd2\xe9\xfe\xb5\x04\xa9\x0d\x43" "\x6c\x20\x3b\x2d\xd2\xa7\xe5\x37\xb4\x68\x9f\xf9\x86\xd1\x73\x53" "\x4b\x15\x6a\x21\x84\xd3\xf1\x36\x82\xcc\xe3\x07\xab\x61\x58\x55" "\x12\x40\x23\x00\xf2\xef\xb6\xab\xa8\x2c\xc0\x5e\x23\x0c\x5c\x23" "\x42\xc4\xee\xe3\x2e\xe5\xf8\xf7\x8b\x0e\xec\xe3\xf5\x8f\x0b\x72" "\xde\x30\x15\x21\x7f\x85\x47\x7a\x4f\xab\x3a\x3a\x99\x38\x27\x11" "\x24\x94\x90\x96\x56\xab\xc6\x07\x4d\xd3\x5f\xec\x2d\x3c\x60\x03" "\xb3\x64\x78\x26\xa8\x22\xf7\x8f\x3c\x4c\x43\x31\x7b\x46\x6a\x38" "\x21\xb4\xc6\x69\xca\x14\x13\x88\x9c\x02\xeb\x7f\x3c\x91\x18\xe6" "\x27\x6a\x36\xba\x6b\x0a\xd5\x4f\xbc\x6a\x2b\xd3\x30\xf9\x30\x7d" "\xf0\xa1\x63\x2e\x68\x40\xd8\x3d\xc3\x25\x52\xd8\xda\xbb\xdf\x14" "\xbe\xbd\x25\x4f\x24\x9b\x26\x98\x2c\x04\x1c\x51\x2b"; size_t yahoo_cert_size = sizeof(yahoo_cert) - 1; ================================================ FILE: src/proto-ssl.c ================================================ /* SSL parser This parses SSL packets from the server. It is built in multiple levels: RECORDS - ssl_parse_record() | +---> heartbeat | | | +---> banner grab | +---> handshake | +---> server hello | | | +---> banner grab | +---> certificate | +---> X.509 parser | +---> subject name (banner) | +---> certificate (banner) For "heartbeat", we grab the so-called "heartbleed" exploit info. For "server hello", we grab which cipher is used For "certificate", we grab the subjectName of the server !!!!!!!!!!!! BIZARRE CODE ALERT !!!!!!!!!!!!!!! This module uses a "streaming state-machine" to parse the SSL protocol. In other words, this does not "reasemble" fragments. Instead, it allows state to cross packet-boundaries. Thus, it supports both fragmentation at the TCP layer and the SSL record layer, but without reassembling things. Only in the output, in the gathered "banners", does reassembly happen -- in other words, reassembly happens after OSI Layer 7 rather than OSI Layer 4. As many are unfamiliar with this technique, they'll find it a little weird. The upshot of doing things this way is that we can support 10 million open TCP connections with minimal memory usage. */ #include "proto-ssl.h" #include "stack-tcp-api.h" #include "unusedparm.h" #include "masscan-app.h" #include "crypto-siphash24.h" #include "util-safefunc.h" #include "util-malloc.h" #include #include #include /** * Fugly macro for doing state-machine parsing. I know it's bad, but * it makes stepping through the code in a debugger so much easier. */ #define DROPDOWN(i,length,state) (state)++;if (++(i)>=(length)) break /***************************************************************************** *****************************************************************************/ static void BANNER_CIPHER(struct BannerOutput *banout, unsigned cipher_suite) { //const char *notes = ""; char foo[64]; snprintf(foo, sizeof(foo), "cipher:0x%x", cipher_suite); banout_append(banout, PROTO_SSL3, foo, AUTO_LEN); /*switch (cipher_suite) { case 0x0005: notes = "(_/RSA/RC4/SHA)"; break; case 0x0035: notes = "(_/RSA/AES-CBC/SHA)"; break; case 0x002f: notes = "(_/RSA/AES-CBC/SHA)"; break; case 0xc013: notes = "(ECDHE/RSA/AES-CBC/SHA)"; break; } banout_append(banout, PROTO_SSL3, notes, AUTO_LEN);*/ } /***************************************************************************** *****************************************************************************/ static void BANNER_VERSION(struct BannerOutput *banout, unsigned version_major, unsigned version_minor) { char foo[64]; switch (version_major<<8 | version_minor) { case 0x0300: banout_append(banout, PROTO_SSL3, "SSLv3 ", AUTO_LEN); banout_append( banout, PROTO_VULN, "SSL[v3] ", AUTO_LEN); break; case 0x0301: banout_append(banout, PROTO_SSL3, "TLS/1.0 ", AUTO_LEN); break; case 0x0302: banout_append(banout, PROTO_SSL3, "TLS/1.1 ", AUTO_LEN); break; case 0x0303: banout_append(banout, PROTO_SSL3, "TLS/1.2 ", AUTO_LEN); break; case 0x0304: banout_append(banout, PROTO_SSL3, "TLS/1.3 ", AUTO_LEN); break; default: snprintf(foo, sizeof(foo), "SSLver[%u,%u] ", version_major, version_minor); banout_append(banout, PROTO_SSL3, foo, strlen(foo)); } } /***************************************************************************** * This parses the "Server Hello" packet, the response to our "ClientHello" * that we sent. We are looking for the following bits of information: * - cipher chosen by the server * - whether heartbeats are enabled *****************************************************************************/ static void parse_server_hello( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { struct SSL_SERVER_HELLO *hello = &pstate->sub.ssl.x.server_hello; unsigned state = hello->state; unsigned remaining = hello->remaining; unsigned i; enum { VERSION_MAJOR, VERSION_MINOR, TIME0, TIME1, TIME2, TIME3, RANDOM, SESSION_LENGTH, SESSION_ID, CIPHER0, CIPHER1, COMPRESSION, LENGTH0, LENGTH1, EXT_TAG0, EXT_TAG1, EXT_LEN0, EXT_LEN1, EXT_DATA, EXT_DATA_HEARTBEAT, UNKNOWN, }; UNUSEDPARM(banout); UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); UNUSEDPARM(socket); /* What this structure looks like in ASN.1 format struct { ProtocolVersion server_version; Random random; SessionID session_id; CipherSuite cipher_suite; CompressionMethod compression_method; } ServerHello; */ /* 'for all bytes in the packet...' */ for (i=0; iversion_major = px[i]; DROPDOWN(i,length,state); case VERSION_MINOR: hello->version_minor = px[i]; BANNER_VERSION(banout, hello->version_major, hello->version_minor); if (banner1->is_poodle_sslv3) { banout_append(banout, PROTO_VULN, " POODLE ", AUTO_LEN); } if (hello->version_major > 3 || hello->version_minor > 4) { state = UNKNOWN; break; } hello->timestamp = 0; DROPDOWN(i,length,state); case TIME0: hello->timestamp <<= 8; hello->timestamp |= px[i]; DROPDOWN(i,length,state); case TIME1: hello->timestamp <<= 8; hello->timestamp |= px[i]; DROPDOWN(i,length,state); case TIME2: hello->timestamp <<= 8; hello->timestamp |= px[i]; DROPDOWN(i,length,state); case TIME3: hello->timestamp <<= 8; hello->timestamp |= px[i]; remaining = 28; DROPDOWN(i,length,state); case RANDOM: { /* do our typical "skip" logic to skip this * 32 byte field */ unsigned len = (unsigned)length-i; if (len > remaining) len = remaining; remaining -= len; i += len-1; if (remaining != 0) { break; } } DROPDOWN(i,length,state); case SESSION_LENGTH: remaining = px[i]; if (banner1->is_ticketbleed && remaining > 16) { banout_append( banout, PROTO_VULN, "SSL[ticketbleed] ", 17); } DROPDOWN(i,length,state); case SESSION_ID: { unsigned len = (unsigned)length-i; if (len > remaining) len = remaining; remaining -= len; i += len-1; if (remaining != 0) { break; } } hello->cipher_suite = 0; DROPDOWN(i,length,state); case CIPHER0: hello->cipher_suite <<= 8; hello->cipher_suite |= px[i]; DROPDOWN(i,length,state); case CIPHER1: hello->cipher_suite <<= 8; hello->cipher_suite |= px[i]; /* cipher-suite recorded here */ BANNER_CIPHER(banout, hello->cipher_suite); DROPDOWN(i,length,state); case COMPRESSION: hello->compression_method = px[i]; DROPDOWN(i,length,state); case LENGTH0: remaining = px[i]; DROPDOWN(i,length,state); case LENGTH1: remaining <<= 8; remaining |= px[i]; DROPDOWN(i,length,state); case EXT_TAG0: ext_tag: if (remaining < 4) { state = UNKNOWN; continue; } hello->ext_tag = px[i]<<8; remaining--; DROPDOWN(i,length,state); case EXT_TAG1: hello->ext_tag |= px[i]; remaining--; DROPDOWN(i,length,state); case EXT_LEN0: hello->ext_remaining = px[i]<<8; remaining--; DROPDOWN(i,length,state); case EXT_LEN1: hello->ext_remaining |= px[i]; remaining--; switch (hello->ext_tag) { case 0x000f: /* heartbeat */ state = EXT_DATA_HEARTBEAT; continue; } DROPDOWN(i,length,state); case EXT_DATA: if (hello->ext_remaining == 0) { state = EXT_TAG0; goto ext_tag; } if (remaining == 0) { state = UNKNOWN; continue; } remaining--; hello->ext_remaining--; continue; case EXT_DATA_HEARTBEAT: if (hello->ext_remaining == 0) { state = EXT_TAG0; goto ext_tag; } if (remaining == 0) { state = UNKNOWN; continue; } remaining--; hello->ext_remaining--; if (px[i]) { banout_append( banout, PROTO_VULN, "SSL[heartbeat] ", 15); } state = EXT_DATA; continue; case UNKNOWN: default: i = (unsigned)length; } hello->state = state; hello->remaining = remaining; } /***************************************************************************** * This parses the certificates from the server. This contains an outer * length field for all certificates, and then uses a length field for * each certificate. The length fields are 3 bytes long. * * +--------+--------+--------+ * | length of all certs | * +--------+--------+--------+ * +--------+--------+--------+ * | cert length | * +--------+--------+--------+ * . . * . . . certificate . . . * . . * +--------+--------+--------+ * | cert length | * +--------+--------+--------+ * . . * . . . certificate . . . * . . * * This parser doesn't parse the certificates themselves, but initializes * and passes fragments to the X.509 parser. * * Called by ssl_parser_record()->parse_handshake() * Calls x509_decode() to parse the certificate * Calls banout_append_base64() to capture the certificate *****************************************************************************/ static void parse_server_cert( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { struct SSL_SERVER_CERT *data = &pstate->sub.ssl.x.server_cert; unsigned state = data->state; unsigned remaining = data->remaining; unsigned cert_remaining = data->sub.remaining; unsigned i; enum { LEN0, LEN1, LEN2, CLEN0, CLEN1, CLEN2, CERT, CALEN0, CALEN1, CALEN2, CACERT, UNKNOWN, }; UNUSEDPARM(banner1); UNUSEDPARM(banner1_private); UNUSEDPARM(socket); for (i=0; iis_capture_cert) { banout_init_base64(&pstate->base64); //banout_append( banout, PROTO_X509_CERT, "cert:", 5); } { unsigned count = data->x509.count; memset(&data->x509, 0, sizeof(data->x509)); x509_decode_init(&data->x509, cert_remaining); data->x509.count = (unsigned char)count + 1; } DROPDOWN(i,length,state); case CERT: case CACERT: { unsigned len = (unsigned)length-i; unsigned proto = (state == CERT ? PROTO_X509_CERT : PROTO_X509_CACERT); if (len > remaining) len = remaining; if (len > cert_remaining) len = cert_remaining; /* parse the certificate */ if (banner1->is_capture_cert) { banout_append_base64(banout, proto, px+i, len, &pstate->base64); } x509_decode(&data->x509, px+i, len, banout); remaining -= len; cert_remaining -= len; i += len-1; if (cert_remaining == 0) { /* We've reached the end of the certificate, so make * a record of it */ if (banner1->is_capture_cert) { banout_finalize_base64(banout, proto, &pstate->base64); banout_end(banout, proto); } state = CALEN0; if (remaining == 0) { /* FIXME: reduce this logic, it should only flush the * FIXME: ertificate, not close the connection*/ if (!banner1->is_heartbleed) { ; //tcpapi_close(socket); } } } } break; case UNKNOWN: default: i = (unsigned)length; } data->state = state; data->remaining = remaining; data->sub.remaining = cert_remaining; } /***************************************************************************** * Called from the SSL Record parser to parse the contents of * a handshake record. The way SSL handshaking works is that after we * have sent the "hello", the server then sends us a bunch of records, * including its certificate, then is done on their side with the handshake. * Then, the client sends a bunch of stuff, to complete their end of the * handshake (which we won't do). At that point, they then do a "change * cipher spec" to negotiate the encryption keys, which isn't technically * part of the handshaking. * * This is a four byte protocol: * +--------+ * | type | * +--------+--------+--------+ * | length | * +--------+--------+--------+ * | content ... * . * . * * Note that the "length" field is 3 bytes, supporting in theory 16-megs * of content, but the outer record that calls this uses only 2-byte length * fields. That's because records support fragmentation. This parser supports * this fragmentation -- the 'state' variable crosses fragment boundaries. *****************************************************************************/ static void parse_handshake( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { struct SSLRECORD *ssl = &pstate->sub.ssl; unsigned state = ssl->handshake.state; unsigned remaining = ssl->handshake.remaining; unsigned i; enum { START, LENGTH0, LENGTH1, LENGTH2, CONTENTS, UNKNOWN, }; /* * `for all bytes in the segment` * `do a state transition for that byte ` */ for (i=0; ihandshake.type = px[i]; /* initialize the state variable that will be used by the inner * parsers */ ssl->x.all.state = 0; DROPDOWN(i,length,state); /* This grabs the 'length' field. Note that unlike other length fields, * this one is 3 bytes long. That's because a single certificate * packet can contain so many certificates in a chain that it exceeds * 64k kilobytes in size. */ case LENGTH0: remaining = px[i]; DROPDOWN(i,length,state); case LENGTH1: remaining <<= 8; remaining |= px[i]; DROPDOWN(i,length,state); case LENGTH2: remaining <<= 8; remaining |= px[i]; /* If we get a "server done" response, then it's a good time to * send the heartbleed request. Note that these are usually zero * length, so we can't process this below in the CONTENT state * but have to do it here at the end of the LENGTH2 state */ if (ssl->handshake.type == 2 && banner1->is_heartbleed) { static const char heartbleed_request[] = "\x15\x03\x02\x00\x02\x01\x80" "\x18\x03\x02\x00\x03\x01" "\x40\x00"; tcpapi_send(socket, heartbleed_request, sizeof(heartbleed_request)-1, 0); } DROPDOWN(i,length,state); /* This parses the contents of the handshake. This parser just skips * the data, in the same way as explained in the "ssl_parse_record()" * function at its CONTENT state. We may pass the fragment to an inner * parser, but whatever the inner parser does is independent from this * parser, and has no effect on this parser */ case CONTENTS: { unsigned len = (unsigned)length-i; if (len > remaining) len = remaining; switch (ssl->handshake.type) { case 0: /* hello request*/ case 1: /* client hello */ case 3: /* DTLS hello verify request */ case 4: /* new session ticket */ case 12: /* server key exchange */ case 13: /* certificate request */ case 14: /* server done */ case 15: /* certificate verify */ case 16: /* client key exchange */ case 20: /* finished */ case 22: /* certificate status */ default: /* don't parse these types, just skip them */ break; case 2: /* server hello */ parse_server_hello( banner1, banner1_private, pstate, px+i, len, banout, socket); break; case 11: /* server certificate */ parse_server_cert( banner1, banner1_private, pstate, px+i, len, banout, socket); break; } remaining -= len; i += len-1; if (remaining == 0) state = START; } break; case UNKNOWN: default: i = (unsigned)length; } ssl->handshake.state = state; ssl->handshake.remaining = remaining; } /***************************************************************************** * Called to parse the "hearbeat" data. This consists of the following * structure: * * +--------+ * | type | 1=request, 2=response * +--------+--------+ * | length | * +--------+--------+ * * This is followed by the echoed bytes, followed by some padding. * *****************************************************************************/ static void parse_heartbeat( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { struct SSLRECORD *ssl = &pstate->sub.ssl; unsigned state = ssl->handshake.state; unsigned remaining = ssl->handshake.remaining; unsigned i; enum { START, LENGTH0, LENGTH1, CONTENTS, UNKNOWN, }; UNUSEDPARM(socket); UNUSEDPARM(banner1_private); /* * `for all bytes in the segment` * `do a state transition for that byte ` */ for (i=0; ihandshake.type = px[i]; DROPDOWN(i,length,state); /* Grab the two byte length field */ case LENGTH0: remaining = px[i]; DROPDOWN(i,length,state); case LENGTH1: remaining <<= 8; remaining |= px[i]; /* `if heartbeat response ` */ if (ssl->handshake.type == 2) { /* if we have a non-trivial amount of data in the response, then * it means the "bleed" attempt succeeded. */ if (remaining >= 16) banout_append( banout, PROTO_VULN, "SSL[HEARTBLEED] ", 16); /* if we've been configured to "capture" the heartbleed contents, * then initialize the BASE64 encoder */ if (banner1->is_capture_heartbleed) { banout_init_base64(&pstate->base64); banout_append(banout, PROTO_HEARTBLEED, "", 0); } } DROPDOWN(i,length,state); /* Here is where we parse the contents of the heartbeat. This is the same * skipping logic as the CONTENTS state within the ssl_parse_record() * function.*/ case CONTENTS: { unsigned len = (unsigned)length-i; if (len > remaining) len = remaining; /* If this is a RESPONSE, and we've been configured to CAPTURE * hearbleed responses, then we write the bleeding bytes in * BASE64 into the banner system. The user will be able to * then do research on those bleeding bytes */ if (ssl->handshake.type == 2 && banner1->is_capture_heartbleed) { banout_append_base64(banout, PROTO_HEARTBLEED, px+i, len, &pstate->base64); } remaining -= len; i += len-1; if (remaining == 0) state = UNKNOWN; /* padding */ } break; /* We reach this state either because the heartbeat data is corrupted or * encrypted, or because we've reached the padding area after the * heartbeat */ case UNKNOWN: default: i = (unsigned)length; } /* not the handshake protocol, but we re-use their variables */ ssl->handshake.state = state; ssl->handshake.remaining = remaining; } /***************************************************************************** * Called to parse the "hearbeat" data. This consists of the following * structure: * * +--------+ * | level | 1=warning, 2=fatal * +--------+ * | descr | * +--------+ * *****************************************************************************/ static void parse_alert( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { struct SSLRECORD *ssl = &pstate->sub.ssl; unsigned state = ssl->handshake.state; unsigned remaining = ssl->handshake.remaining; unsigned i; enum { START, DESCRIPTION, UNKNOWN, }; UNUSEDPARM(socket); UNUSEDPARM(banner1_private); /* * `for all bytes in the segment` * `do a state transition for that byte ` */ for (i=0; ix.server_alert.level = px[i]; DROPDOWN(i,length,state); case DESCRIPTION: ssl->x.server_alert.description = px[i]; if (banner1->is_poodle_sslv3 && ssl->x.server_alert.level == 2) { char foo[64]; /* fatal error */ switch (ssl->x.server_alert.description) { case 86: if (!banout_is_contains(banout, PROTO_SAFE, "TLS_FALLBACK_SCSV")) banout_append(banout, PROTO_SAFE, "poodle[TLS_FALLBACK_SCSV] ", AUTO_LEN); break; case 40: if (!banout_is_contains(banout, PROTO_SAFE, "TLS_FALLBACK_SCSV")) banout_append(banout, PROTO_SAFE, "poodle[no-SSLv3] ", AUTO_LEN); break; default: banout_append(banout, PROTO_SAFE, "poodle[no-SSLv3] ", AUTO_LEN); snprintf(foo, sizeof(foo), " ALERT(0x%02x%02x) ", ssl->x.server_alert.level, ssl->x.server_alert.description ); banout_append(banout, PROTO_SSL3, foo, AUTO_LEN); break; } } else { char foo[64]; snprintf(foo, sizeof(foo), " ALERT(0x%02x%02x) ", ssl->x.server_alert.level, ssl->x.server_alert.description ); banout_append(banout, PROTO_SSL3, foo, AUTO_LEN); } DROPDOWN(i,length,state); case UNKNOWN: default: i = (unsigned)length; } /* not the handshake protocol, but we re-use their variables */ ssl->handshake.state = state; ssl->handshake.remaining = remaining; } /***************************************************************************** * This is the main SSL parsing function. * * SSL is a multi-layered protocol, consisting of "Records" as the outer * protocol, with records containing data inside. The inner data is * unencrypted during the session handshake, but then encrypted from then on. * * The SSL Records are a simple 5 byte protocol: * * +--------+ * | type | * +--------+--------+ * |ver-mjr |ver-mnr | * +--------+--------+ * | length | * +--------+--------+ * * This allows simple state-machine parsing. We need only 6 states, one for * each byte, and then a "content" state tracking the contents of the record * until we've parsed "length" bytes, then back to the initial state. * *****************************************************************************/ static void ssl_parse_record( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned remaining = pstate->remaining; struct SSLRECORD *ssl = &pstate->sub.ssl; unsigned i; enum { START, VERSION_MAJOR, VERSION_MINOR, LENGTH0, LENGTH1, CONTENTS, UNKNOWN, }; /* * `for all bytes in the segment` * `do a state transition for that byte ` */ for (i=0; itype != px[i]) { ssl->type = px[i]; /* this is for some minimal fragmentation/reassembly */ ssl->handshake.state = 0; } DROPDOWN(i,length,state); /* This is the major version number, which must be the value '3', * which means both SSLv3 and TLSv1. This parser doesn't support * earlier versions of SSL. */ case VERSION_MAJOR: if (px[i] != 3) { state = UNKNOWN; break; } ssl->version_major = px[i]; DROPDOWN(i,length,state); /* This is the minor version number. It's a little weird: * 0 = SSLv3.0 * 1 = TLSv1.0 * 2 = TLSv1.1 * 3 = TLSv1.2 * 4 = TLSv1.3 */ case VERSION_MINOR: ssl->version_minor = px[i]; DROPDOWN(i,length,state); /* This is the length field. In theory, it can be the full 64k bytes * in length, but typical implements limit it to 16k */ case LENGTH0: remaining = px[i]<<8; DROPDOWN(i,length,state); case LENGTH1: remaining |= px[i]; DROPDOWN(i,length,state); ssl->handshake.state = 0; /* * This state parses the "contents" of a record. What we do here is at * this level of the parser is that we calculate a sub-segment size, * which is bounded by either the number of bytes in this records (when * there are multiple records per packet), or the packet size (when the * record exceeds the size of the packet). * We then pass this sub-segment to the inner content parser. However, the * inner parser has no effect on what happens in this parser. It's wholly * independent, doing it's own thing. */ case CONTENTS: { unsigned len; /* Size of this segment is either the bytes remaining in the * current packet, or the bytes remaining in the record */ len = (unsigned)length - i; if (len > remaining) len = remaining; /* Do an inner-parse of this segment. Note that the inner-parser * has no effect on this outer record parser */ switch (ssl->type) { case 20: /* change cipher spec */ break; case 21: /* alert */ /* encrypted, usually, but if we get one here, it won't * be encrypted */ parse_alert(banner1, banner1_private, pstate, px+i, len, banout, socket); break; case 22: /* handshake */ parse_handshake(banner1, banner1_private, pstate, px+i, len, banout, socket); break; case 23: /* application data */ /* encrypted, always*/ break; case 24: /* heartbeat */ /* encrypted, in theory, but not practice */ parse_heartbeat(banner1, banner1_private, pstate, px+i, len, banout, socket); break; } /* Skip ahead the number bytes in this segment. This makes the * parser very fast, because we aren't actually doing a single * byte at a time, but skipping forward large number of bytes * at a time -- except for the 5 byte headers */ remaining -= len; i += len-1; /* if 'len' is zero, this still works */ /* Once we've exhausted the contents of record, go back to the * start parsing the next record */ if (remaining == 0) state = START; } break; /* We reach the state when the protocol has become corrupted, such as in * those cases where it's not SSL */ case UNKNOWN: default: i = (unsigned)length; } pstate->state = state; pstate->remaining = remaining; } /***************************************************************************** * This is called at program startup to initialize any structures we need * for parsing. The SSL parser doesn't need anything in particular, so * we just ignore it. We have to implement the callback, however, which * is why this empty function exists. *****************************************************************************/ static void * ssl_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /***************************************************************************** * This is the template "Client Hello" packet that is sent to the server * to initiate the SSL connection. Right now, it's statically just transmitted * on to the wire. * TODO: we need to make this dynamically generated, so that users can * select various options. *****************************************************************************/ static const char ssl_hello_template[] = "\x16\x03\x01\x00\xc1" /* TLSv1.0 record layer */ "\x01" /* type = client-hello */ "\x00\x00\xbd" /* length = 193 */ "\x03\x03" /* version = 3.03 (TLS 1.2) */ "\x97\xe5\x60\x50\xc4\xa5\x4a\xe0\xb9\x01\x75\x15\x31\x23\x27\x68" /* random */ "\x87\xdc\x3d\x66\xec\x07\xdc\xa0\xe5\x1f\x1f\xa1\x3f\x49\xf8\xfc" /* TODO: re-randomize for each request, or at least on startup */ "\x00"/* session-id-length = 0 */ "\x00\x3c" /* cipher suites length */ "\xc0\x2b\xcc\xa9\xc0\x2c\xc0\x09\xc0\x0a\xc0\x23\xc0\x24\xc0\x2f" "\xcc\xa8\xc0\x30\xc0\x13\xc0\x14\xc0\x27\xc0\x28\x00\x9e\xcc\xaa" "\x00\x9f\x00\x33\x00\x39\x00\x67\x00\x6b\x00\x9c\x00\x9d\x00\x3c" "\x00\x3d\x00\x2f\x00\x35\x00\x0a\x00\x05\x00\xff" "\x01" /* compression-methods-length = 1 */ "\x00" "\x00\x58" /* extensions length = 88 */ /* extensions */ "\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0c\x00\x0a\x00\x1d" "\x00\x17\x00\x1e\x00\x19\x00\x18\x00\x23\x00\x00\x00\x16\x00\x00" "\x00\x17\x00\x00\x00\x0d\x00\x30\x00\x2e\x04\x03\x05\x03\x06\x03" "\x08\x07\x08\x08\x08\x09\x08\x0a\x08\x0b\x08\x04\x08\x05\x08\x06" "\x04\x01\x05\x01\x06\x01\x03\x03\x02\x03\x03\x01\x02\x01\x03\x02" "\x02\x02\x04\x02\x05\x02\x06\x02" ; /***************************************************************************** * This is the template "Client Hello" packet that is sent to the server * to initiate the SSL connection. Right now, it's statically just transmitted * on to the wire. * TODO: we need to make this dynamically generated, so that users can * select various options. *****************************************************************************/ static const char ssl_12_hello_template[] = "\x16\x03\x01\x01\x1a" "\x01" "\x00\x01\x16" "\x03\x03\x02\x58\x33\x79\x5f\x71\x03\xef\x07\xfe\x36\x61\xb0\x32\x81\xaa\x99\x10\x87\x6a\x8e\x5b\xf9\x03\x93\x44\x58\x4b\x19\xff\x42\x6a\x20\x64\x84\xcd\x28\x9c\xe9\xb1\x9d\xcd\x8a\x11\x4c\x3b\x40\x1c\x90\x02\xf2\xb5\x1a\xf1\x7e\x5d\xb8\x42\xc2\x1e\x17\x1e\x59\xa4\xac\x00\x3e\x13\x02\x13\x03\x13\x01\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x8f\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0c\x00\x0a\x00\x1d\x00\x17\x00\x1e\x00\x19\x00\x18\x00\x23\x00\x00\x00\x16\x00\x00\x00\x17\x00\x00\x00\x0d\x00\x2a\x00\x28\x04\x03\x05\x03\x06\x03\x08\x07\x08\x08\x08\x09\x08\x0a\x08\x0b\x08\x04\x08\x05\x08\x06\x04\x01\x05\x01\x06\x01\x03\x03\x03\x01\x03\x02\x04\x02\x05\x02\x06\x02\x00\x2b\x00\x09\x08\x03\x04\x03\x03\x03\x02\x03\x01\x00\x2d\x00\x02\x01\x01\x00\x33\x00\x26\x00\x24\x00\x1d\x00\x20\xb6\x87\xb7\x72\xb9\xcb\x07\xe0\x14\x0a\x14\x81\x3f\x3f\x0a\xcc\xc4\x7d\x80\xf7\xe8\xaa\x1e\x73\xb0\xa9\xad\xb8\x3a\xa7\x3c\x64"; ; /***************************************************************************** *****************************************************************************/ static char * ssl_add_cipherspec_sslv3(void *templ, unsigned cipher_spec, unsigned is_append) { unsigned char *px; size_t len0 = ssl_hello_size(templ); size_t len1; size_t len1b; size_t len2; size_t offset; size_t offset2; /* Increase space by 2 for additional cipherspec */ px = REALLOC(templ, ssl_hello_size(templ) + 2); /* parse the lengths */ len1 = px[3] << 8 | px[4]; len1b = px[6] << 16 | px[7] << 8 | px[8]; /* skip session id field */ offset = 43; offset += px[offset] + 1; /* do cipherspecs */ len2 = px[offset] << 8 | px[offset+1]; offset2 = offset+2; if (is_append) { /* append to end of list */ memmove(px + offset2 + len2 + 2, px + offset2 + len2, len0 - (offset2 + len2)); px[offset2 + len2 ] = (unsigned char)(cipher_spec>>8); px[offset2 + len2 + 1] = (unsigned char)(cipher_spec>>0); } else { /* prepend to start of list, making this the preferred cipherspec*/ memmove(px + offset2 + 2, px + offset2, len0 - offset2); px[offset2 ] = (unsigned char)(cipher_spec>>8); px[offset2 + 1] = (unsigned char)(cipher_spec>>0); } /* fix length fields */ len2 += 2; px[offset ] = (unsigned char)(len2>>8); px[offset + 1] = (unsigned char)(len2>>0); len1b += 2; px[6] = (unsigned char)(len1b>>16); px[7] = (unsigned char)(len1b>> 8); px[8] = (unsigned char)(len1b>> 0); len1 += 2; px[3] = (unsigned char)(len1>>8); px[4] = (unsigned char)(len1>>0); return (char*)px; } /***************************************************************************** *****************************************************************************/ char * ssl_add_cipherspec(void *templ, unsigned cipher_spec, unsigned is_append) { const unsigned char *px = (const unsigned char *)templ; unsigned version; /* ignore things that aren't "Hello" messages */ if (px[0] != 0x16) { fprintf(stderr, "internal error\n"); return templ; } /* figure out the proper version */ version = px[1] << 8 | px[2]; /* do different parsing depending on version */ switch (version) { case 0x300: return ssl_add_cipherspec_sslv3(templ, cipher_spec, is_append); default: /*TODO:*/ fprintf(stderr, "internal error\n"); return templ; } } /***************************************************************************** * Figure out the Hello message size by parsing the data *****************************************************************************/ unsigned ssl_hello_size(const void *templ) { const unsigned char *px = (const unsigned char *)templ; size_t template_size; template_size = (px[3]<<8 | px[4]) + 5; return (unsigned)template_size; } /***************************************************************************** *****************************************************************************/ char * ssl_hello(const void *templ) { unsigned char *px = (unsigned char *)templ; unsigned now = (unsigned)time(0); unsigned i; /* parse existing template to figure out size */ size_t template_size = (px[3]<<8 | px[4]) + 5; /* allocate memory for that size and copy */ px = MALLOC(template_size); memcpy(px, templ, template_size); /* set the new timestamp and randomize buffer */ px[11] = (unsigned char)(now>>24); px[12] = (unsigned char)(now>>16); px[13] = (unsigned char)(now>> 8); px[14] = (unsigned char)(now>> 0); /* create a pattern to make this detectable as specifically masscan */ for (i=4; i<32; i++) { static const uint64_t key[2] = {0,0}; unsigned val = i+now; unsigned char c = (unsigned char)siphash24(&val, sizeof(val), key); px[11+i] = c; } return (char*)px; } extern unsigned char ssl_test_case_1[]; extern size_t ssl_test_case_1_size; extern unsigned char ssl_test_case_3[]; extern size_t ssl_test_case_3_size; extern unsigned char google_cert[]; extern size_t google_cert_size; extern unsigned char yahoo_cert[]; extern size_t yahoo_cert_size; /***************************************************************************** *****************************************************************************/ static int ssl_selftest(void) { struct Banner1 *banner1; struct StreamState state[1]; unsigned ii; struct BannerOutput banout1[1]; struct BannerOutput banout2[1]; unsigned x; /* * Yahoo cert */ { struct CertDecode certstate[1]; memset(certstate, 0, sizeof(certstate)); x509_decode_init(certstate, yahoo_cert_size); banner1 = banner1_create(); banner1->is_capture_cert = 1; banout_init(banout1); x509_decode(certstate, yahoo_cert, yahoo_cert_size, banout1); x = banout_is_contains(banout1, PROTO_SSL3, ", fr.yahoo.com, "); if (!x) { printf("x.509 parser failure: google.com\n"); return 1; } banner1_destroy(banner1); banout_release(banout1); } /* * Google cert */ { struct CertDecode certstate[1]; memset(certstate, 0, sizeof(certstate)); x509_decode_init(certstate, google_cert_size); banner1 = banner1_create(); banner1->is_capture_cert = 1; banout_init(banout1); x509_decode(certstate, google_cert, google_cert_size, banout1); x = banout_is_equal(banout1, PROTO_SSL3, ", www.google.com, www.google.com"); if (!x) { printf("x.509 parser failure: google.com\n"); return 1; } banner1_destroy(banner1); banout_release(banout1); } /* * Do the normal parse */ banner1 = banner1_create(); banner1->is_capture_cert = 1; memset(state, 0, sizeof(state)); banout_init(banout1); { size_t i; for (i=0; i 72) { printf("%.*s\n", 72, foo); foo += 72; } else { printf("%s\n", foo); break; } } printf("-----END CERTIFICATE-----\n"); }*/ banner1_destroy(banner1); banout_release(banout1); /* * Do the fragmented parse */ banner1 = banner1_create(); banner1->is_capture_cert = 1; memset(state, 0, sizeof(state)); banout_init(banout2); for (ii=0; ii #include #include "util-safefunc.h" /*************************************************************************** * @param length * Number of bytes remaining in this header, or bytes remaining in * the packet, whichever is fewer. * @return the number of bytes processed ***************************************************************************/ static size_t cc_parse(struct BannerOutput *banout, struct RDPSTUFF *rdp, const unsigned char *px, size_t length) { size_t offset; unsigned state = rdp->cc.state; enum { TYPE, FLAGS, LENGTH, RESERVED, RESULT0, RESULT1, RESULT2, RESULT3, EXTRA, UNKNOWN_PROTOCOL }; for (offset = 0; offset < length; offset++) { unsigned char c = px[offset]; switch (state) { case TYPE: rdp->cc.type = c; state++; break; case FLAGS: rdp->cc.flags = c; state++; break; case LENGTH: rdp->cc.len = c; if (rdp->cc.len < 4) { state = UNKNOWN_PROTOCOL; } else { rdp->cc.len -= 4; state++; } break; case RESERVED: switch (rdp->cc.type) { case 2: /* negotiate success */ case 3: /* negotiate failure */ state = RESULT0; rdp->cc.result = 0; break; default: state = EXTRA; break; } break; case RESULT0: case RESULT1: case RESULT2: case RESULT3: if (rdp->cc.len == 0) state = EXTRA; else { rdp->cc.len--; rdp->cc.result = rdp->cc.result>>8 | (c << 24); state++; if (state == EXTRA) { switch (rdp->cc.type) { case 2: if (rdp->cc.result & 2) banout_append(banout, PROTO_RDP, " NLA-supported", AUTO_LEN); else banout_append(banout, PROTO_RDP, " NLA-unused", AUTO_LEN); break; case 3: if (rdp->cc.result == 5) banout_append(banout, PROTO_RDP, " NLA-unsupported", AUTO_LEN); else banout_append(banout, PROTO_RDP, " failure", AUTO_LEN); break; default: banout_append(banout, PROTO_RDP, " unknown", AUTO_LEN); break; } } } break; case EXTRA: offset = length; break; case UNKNOWN_PROTOCOL: banout_append(banout, PROTO_HEUR, px, length); offset = length; break; } } rdp->cc.state = state; return offset; } /*************************************************************************** * @param length * The number of bytes left in those received, or the number of bytes * left in the COTP contents, whichever is less. * @return the number of bytes processed ***************************************************************************/ static size_t cotp_parse(struct BannerOutput *banout, struct RDPSTUFF *rdp, const unsigned char *px, size_t length) { size_t offset; unsigned state = rdp->cotp.state; enum { LENGTH, PDU_TYPE, DSTREF0, DSTREF1, SRCREF0, SRCREF1, FLAGS, CONTENT, UNKNOWN_PROTOCOL, }; for (offset = 0; offset < length; offset++) { unsigned char c = px[offset]; switch (state) { case LENGTH: rdp->cotp.len = c; if (rdp->cotp.len < 6) { state = UNKNOWN_PROTOCOL; } else { rdp->cotp.len -= 6; state++; } break; case PDU_TYPE: rdp->cotp.type = c; rdp->cotp.srcref = 0; rdp->cotp.dstref = 0; state++; break; case DSTREF0: case DSTREF1: rdp->cotp.dstref = rdp->cotp.dstref<<8 | c; state++; break; case SRCREF0: case SRCREF1: rdp->cotp.dstref = rdp->cotp.dstref<<8 | c; state++; break; case FLAGS: rdp->cotp.flags = c; rdp->cc.state = 0; state++; break; case CONTENT: switch (rdp->cotp.type) { case 0xd0: /* connect confirm */ { size_t length2 = rdp->cotp.len; size_t bytes_parsed; /* In case the TPKT length is more bytes than are in this packet */ if (length2 >= length - offset) length2 = length - offset; bytes_parsed = cc_parse(banout, rdp, px + offset, length2); /* Track how many bytes the sub-parsers parsed, remembering * that when the for-loop increments, it'll increment the offset * by 1. */ assert(bytes_parsed != 0); offset += bytes_parsed - 1; rdp->cotp.len -= (unsigned char)bytes_parsed; /* If we have bytes left in the TPKT, then stay in this state, * otherwise transition to the next TPKT */ if (rdp->cotp.len) state = CONTENT; else state = UNKNOWN_PROTOCOL; } break; default: banout_append(banout, PROTO_RDP, " COTPPDU=unknown", AUTO_LEN); offset = length; break; } break; case UNKNOWN_PROTOCOL: banout_append(banout, PROTO_HEUR, px, length); offset = length; break; } } rdp->cotp.state = state; return offset; } /*************************************************************************** ***************************************************************************/ static void rdp_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state & 0xFFFFFF; struct RDPSTUFF *rdp = &pstate->sub.rdp; size_t offset; enum { TPKT_START, TPKT_RESERVED, TPKT_LENGTH0, TPKT_LENGTH1, TPKT_CONTENT, UNKNOWN_PROTOCOL, }; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); UNUSEDPARM(socket); for (offset=0; offsettpkt_length = 0; rdp->cotp.state = 0; state = TPKT_RESERVED; } break; case TPKT_RESERVED: state++; break; case TPKT_LENGTH0: rdp->tpkt_length = rdp->tpkt_length; state++; break; case TPKT_LENGTH1: rdp->tpkt_length = rdp->tpkt_length<<8 | c; if (rdp->tpkt_length < 4) { state = UNKNOWN_PROTOCOL; } else if (rdp->tpkt_length == 4) { state = 0; } else { rdp->tpkt_length -= 4; state++; } break; case TPKT_CONTENT: { size_t length2 = rdp->tpkt_length; size_t bytes_parsed; /* In case the TPKT length is more bytes than are in this packet */ if (length2 >= length - offset) length2 = length - offset; bytes_parsed = cotp_parse(banout, rdp, px + offset, length2); /* Track how many bytes the sub-parsers parsed, remembering * that when the for-loop increments, it'll increment the offset * by 1. */ assert(bytes_parsed != 0); offset += bytes_parsed - 1; rdp->tpkt_length -= (unsigned short)bytes_parsed; /* If we have bytes left in the TPKT, then stay in this state, * otherwise transition to the next TPKT */ if (rdp->tpkt_length) state = TPKT_CONTENT; else state = TPKT_START; } break; case UNKNOWN_PROTOCOL: banout_append(banout, PROTO_HEUR, px, length); offset = length; break; default: break; } } pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * rdp_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /*************************************************************************** ***************************************************************************/ static int rdp_selftest_item(const char *input, size_t length, const char *expect) { struct Banner1 *banner1; struct StreamState pstate[1]; struct BannerOutput banout1[1]; struct stack_handle_t more = {0}; int x; /* * Initiate a pseudo-environment for the parser */ banner1 = banner1_create(); banout_init(banout1); memset(&pstate[0], 0, sizeof(pstate[0])); /* * Parse the input payload */ rdp_parse(banner1, 0, pstate, (const unsigned char *)input, length, banout1, &more ); /* * Verify that somewhere in the output is the string * we are looking for */ x = banout_is_contains(banout1, PROTO_RDP, expect); if (x == 0) printf("RDP parser failure: %s\n", expect); banner1_destroy(banner1); banout_release(banout1); return (x?0:1); } /*************************************************************************** ***************************************************************************/ static int rdp_selftest(void) { static const char test1[] = "\x03\x00\x00\x13" "\x0e\xd0\x00\x00\x12\x34\x00\x02\x0f\x08\x00\x02\x00\x00\x00"; static const char test2[] = "\x03\x00\x00\x13" "\x0e\xd0\x00\x00\x12\x34\x00\x03\x00\x08\x00\x05\x00\x00\x00"; int result = 0; result += rdp_selftest_item(test1, sizeof(test1) - 1, "NLA-sup"); result += rdp_selftest_item(test2, sizeof(test2) - 1, "NLA-unsup"); return result; } /*************************************************************************** ***************************************************************************/ static const char rdp_hello[] = "\x03\x00\x00\x2d" "\x28\xe0\x00\x00\x00\x00\x00\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x6d" \ "\x73\x74\x73\x68\x61\x73\x68\x3d" "masscan" "\x0d\x0a\x01\x00" \ "\x08\x00\x03\x00\x00\x00"; /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_rdp = { "rdp", 3389, rdp_hello, sizeof(rdp_hello)-1, 0, rdp_selftest, rdp_init, rdp_parse, }; ================================================ FILE: src/proto-tcp-rdp.h ================================================ #ifndef PROTO_TCP_RDP_H #define PROTO_TCP_RDP_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_rdp; #endif ================================================ FILE: src/proto-tcp-telnet.c ================================================ #include "proto-tcp-telnet.h" #include "proto-banner1.h" #include "stack-tcp-api.h" #include "unusedparm.h" #include "masscan-app.h" #include "util-malloc.h" #include #include #include "util-safefunc.h" struct TelnetOptions { unsigned num; const char *text; }; /* This is a list of the options during negotiation that we might be interested in. */ struct TelnetOptions options[] = { { 0, "binary"}, /* 0x00 Binary */ { 1, "echo"}, /* 0x01 Echo */ //{ 2, "recon"}, /* 0x02 Reconnection */ { 3, "sga"}, /* 0x03 Suppress go ahead */ //{ 4, "msgsz"}, /* 0x04 Approx Message Size Negotiation */ { 5, "status"}, /* 0x05 Status */ { 6, "timing-mark"}, /* 0x06 Timing Mark */ /* 7 Remote Controlled Trans and Echo [107,JBP] 8 Output Line Width [40,JBP] 9 Output Page Size [41,JBP] 10 Output Carriage-Return Disposition [28,JBP] 11 Output Horizontal Tab Stops [32,JBP] 12 Output Horizontal Tab Disposition [31,JBP] 13 Output Formfeed Disposition [29,JBP] 14 Output Vertical Tabstops [34,JBP] 15 Output Vertical Tab Disposition [33,JBP] 16 Output Linefeed Disposition [30,JBP] 17 Extended ASCII [136,JBP] 18 Logout [25,MRC] 19 Byte Macro [35,JBP] 20 Data Entry Terminal [145,38,JBP]*/ //{21, "supdup"}, /* 0x15 SUPDUP */ {22, "supdupout"}, /* 0x16 SUPDUP Output */ {23, "sendloc"}, /* 0x17 Send Location */ {24, "term"}, /* 0x18 Terminal type */ /* 25 End of Record [103,JBP] 26 TACACS User Identification [1,BA4] 27 Output Marking [125,SXS] 28 Terminal Location Number [84,RN6] 29 Telnet 3270 Regime [116,JXR] 30 X.3 PAD [70,SL70] */ {31, "naws"}, /* 0x1f Negotiate About Window Size */ {32, "tspeed"}, /* 0x20 Terminal Speed */ {33, "rflow"}, /* 0x21 ! Remote Flow Control */ {34, "linemode"}, /* 0x22 " Linemode */ {35, "xloc"}, /* 0x23 # X Display Location */ {36, "env"}, /* 0x24 $ Environment Option [DB14]*/ {37, "auth"}, /* 0x25 % Authentication Option */ {38, "encrypt"}, /* 0x26 & Encryption Option */ {39, "new-env"}, /* 0x27 ' */ {46, "starttls"}, /* 0x2e . STARTTLS */ /* 255 Extended-Options-List [109,JBP] */ {0,0} }; #if 0 static const char * option_name_lookup(unsigned optnum) { size_t i; for (i=0; options[i].text; i++) { if (options[i].num == optnum) return options[i].text; } return 0; } #endif enum { FLAG_WILL=1, FLAG_WONT=2, FLAG_DO=4, FLAG_DONT=8, }; /*************************************************************************** ***************************************************************************/ static void telnet_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; size_t offset; enum { TELNET_DATA, TELNET_IAC, TELNET_DO, TELNET_DONT, TELNET_WILL, TELNET_WONT, TELNET_SB, TELNET_SB_DATA, TELNET_INVALID, }; //static const char *foobar[4] = {"DO", "DONT", "WILL", "WONT"}; unsigned char nego[256] = {0}; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); UNUSEDPARM(socket); for (offset=0; offsetstate = state; } /*************************************************************************** ***************************************************************************/ static void * telnet_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /*************************************************************************** ***************************************************************************/ static int telnet_selftest_item(const char *input, const char *output) { struct Banner1 *banner1; struct StreamState pstate[1]; struct BannerOutput banout1[1]; int x; /* * Initiate a pseudo-environment for the parser */ banner1 = banner1_create(); banout_init(banout1); memset(&pstate[0], 0, sizeof(pstate[0])); /* * Parse the input payload */ telnet_parse(banner1, 0, pstate, (const unsigned char *)input, strlen(input), banout1, 0 ); //fprintf(stderr, "%.*s\n", (int)banout_string_length(banout1, PROTO_TELNET), banout_string(banout1, PROTO_TELNET)); /* * Verify that somewhere in the output is the string * we are looking for */ x = banout_is_contains(banout1, PROTO_TELNET, output); if (x == 0) printf("telnet parser failure: %s\n", output); banner1_destroy(banner1); banout_release(banout1); return (x==0)?1:0; } /*************************************************************************** ***************************************************************************/ static int telnet_selftest(void) { struct { const char *input; const char *output; } tests[] = { {"\xff\xfd\x1flogin:", "login:"}, {"\xff\xfd\x27\xff\xfd\x18 ", " "}, { "\xff\xfb\x25\xff\xfd\x03\xff\xfb\x18\xff\xfb\x1f\xff\xfb\x20\xff" \ "\xfb\x21\xff\xfb\x22\xff\xfb\x27\xff\xfd\x05" "\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f" "\xff\xfa\x18\x01\xff\xf0" "\x0d\x0a\x55\x73\x65\x72\x20\x41\x63\x63\x65\x73\x73\x20\x56\x65" \ "\x72\x69\x66\x69\x63\x61\x74\x69\x6f\x6e\x0d\x0a\x0d\x0a" , "User Access" }, { "\xff\xfd\x01\xff\xfd\x1f\xff\xfd\x21\xff\xfb\x01\xff\xfb\x03\x46" "\x36\x37\x30\x0d\x0a\x0d\x4c\x6f\x67\x69\x6e\x3a\x20", "F670\r\n\rLogin:" }, {0,0} }; size_t i; for (i=0; tests[i].input; i++) { int err; err = telnet_selftest_item(tests[i].input, tests[i].output); if (err) { fprintf(stderr, "telnet: selftest fail, item %u\n", (unsigned)i); return err; } } return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_telnet = { "telnet", 23, "\xff\xf6", 2, 0, telnet_selftest, telnet_init, telnet_parse, }; ================================================ FILE: src/proto-tcp-telnet.h ================================================ #ifndef PROTO_TELNET_H #define PROTO_TELNET_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_telnet; #endif ================================================ FILE: src/proto-udp.c ================================================ #include "proto-udp.h" #include "proto-coap.h" #include "proto-dns.h" #include "proto-isakmp.h" #include "proto-netbios.h" #include "proto-snmp.h" #include "proto-memcached.h" #include "proto-ntp.h" #include "proto-zeroaccess.h" #include "proto-preprocess.h" #include "syn-cookie.h" #include "util-logger.h" #include "output.h" #include "masscan-status.h" #include "unusedparm.h" /**************************************************************************** * When the "--banner" command-line option is selected, this will * will take up to 64 bytes of a response and display it. Other UDP * protocol parsers may also default to this function when they detect * a response is not the protocol they expect. For example, if a response * to port 161 obviously isn't ASN.1 formatted, the SNMP parser will * call this function instead. In such cases, the protocool identifier will * be [unknown] rather than [snmp]. ****************************************************************************/ unsigned default_udp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy) { ipaddress ip_them = parsed->src_ip; unsigned port_them = parsed->port_src; UNUSEDPARM(entropy); if (length > 64) length = 64; output_report_banner( out, timestamp, ip_them, 17, port_them, PROTO_NONE, parsed->ip_ttl, px, length); return 0; } /**************************************************************************** ****************************************************************************/ void handle_udp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy) { ipaddress ip_them = parsed->src_ip; unsigned port_them = parsed->port_src; unsigned status = 0; /* Report "open" status regardless */ output_report_status( out, timestamp, PortStatus_Open, ip_them, 17, /* ip proto = udp */ port_them, 0, parsed->ip_ttl, parsed->mac_src); switch (port_them) { case 53: /* DNS - Domain Name System (amplifier) */ status = handle_dns(out, timestamp, px, length, parsed, entropy); break; case 123: /* NTP - Network Time Protocol (amplifier) */ status = ntp_handle_response(out, timestamp, px, length, parsed, entropy); break; case 137: /* NetBIOS (amplifier) */ status = handle_nbtstat(out, timestamp, px, length, parsed, entropy); break; case 161: /* SNMP - Simple Network Managment Protocol (amplifier) */ status = handle_snmp(out, timestamp, px, length, parsed, entropy); break; case 500: /* ISAKMP - IPsec key negotiation */ status = isakmp_parse(out, timestamp, px + parsed->app_offset, parsed->app_length, parsed, entropy); break; case 5683: status = coap_handle_response(out, timestamp, px + parsed->app_offset, parsed->app_length, parsed, entropy); break; case 11211: /* memcached (amplifier) */ px += parsed->app_offset; length = parsed->app_length; status = memcached_udp_parse(out, timestamp, px, length, parsed, entropy); break; case 16464: case 16465: case 16470: case 16471: status = handle_zeroaccess(out, timestamp, px, length, parsed, entropy); break; default: px += parsed->app_offset; length = parsed->app_length; status = default_udp_parse(out, timestamp, px, length, parsed, entropy); break; } /* Report banner if some parser didn't already do so. * Also report raw dump if `--rawudp` specified on the * command-line, even if a protocol above already created a more detailed * banner. */ if (status == 0 || out->is_banner_rawudp) { output_report_banner( out, timestamp, ip_them, 17, /* ip proto = udp */ port_them, PROTO_NONE, parsed->ip_ttl, px + parsed->app_offset, parsed->app_length); } } ================================================ FILE: src/proto-udp.h ================================================ #ifndef PROTO_UDP_H #define PROTO_UDP_H #include #include struct PreprocessedInfo; struct Output; /** * Parse an incoming UDP response. We parse the basics, then hand it off * to a protocol parser (SNMP, NetBIOS, NTP, etc.) * @param entropy * The random seed, used in calculating syn-cookies. */ void handle_udp(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); /** * Default banner for UDP, consisting of the first 64 bytes, when it isn't * detected as the appropriate protocol */ unsigned default_udp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); #endif ================================================ FILE: src/proto-versioning.c ================================================ /* SERVICE VERSIONING */ #include "proto-versioning.h" #include "proto-banner1.h" #include "smack.h" #include "unusedparm.h" #include "masscan-app.h" #include "output.h" #include "stack-tcp-api.h" #include "proto-preprocess.h" #include "proto-ssl.h" #include "proto-udp.h" #include "syn-cookie.h" #include "massip-port.h" #include #include #include /*************************************************************************** ***************************************************************************/ static void versioning_tcp_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); UNUSEDPARM(socket); UNUSEDPARM(px); UNUSEDPARM(length); UNUSEDPARM(banout); pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * versioning_init(struct Banner1 *b) { //b->memcached_responses = smack_create("memcached-responses", SMACK_CASE_INSENSITIVE); return b->http_fields; } /*************************************************************************** ***************************************************************************/ #if 0 static unsigned versioning_udp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ) { return default_udp_parse(out, timestamp, px, length, parsed, entropy); } #endif /**************************************************************************** ****************************************************************************/ #if 0 static unsigned versioning_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) { return 0; } #endif /*************************************************************************** ***************************************************************************/ static int versioning_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_versioning = { "versioning", 11211, "stats\r\n", 7, 0, versioning_selftest, versioning_init, versioning_tcp_parse, }; ================================================ FILE: src/proto-versioning.h ================================================ #ifndef VERSIONING_H #define VERSIONING_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_versioning; #endif ================================================ FILE: src/proto-vnc.c ================================================ #include "proto-vnc.h" #include "proto-banner1.h" #include "stack-tcp-api.h" #include "unusedparm.h" #include "masscan-app.h" #include "util-safefunc.h" #include "smack.h" #include static void vnc_append_sectype(struct BannerOutput *banout, unsigned sectype) { char foo[16]; /* http://www.iana.org/assignments/rfb/rfb.xml Value Name Reference 0 Invalid [RFC6143] 1 None [RFC6143] 2 VNC Authentication [RFC6143] 3-15 RealVNC historic assignment 16 Tight historic assignment 17 Ultra historic assignment 18 TLS historic assignment 19 VeNCrypt historic assignment 20 GTK-VNC SASL historic assignment 21 MD5 hash authentication historic assignment 22 Colin Dean xvp historic assignment 23-29 Unassigned 30-35 Apple Inc. [Michael_Stein] 36-127 Unassigned 128-255 RealVNC historic assignment */ switch (sectype) { case 0: banout_append(banout, PROTO_VNC_INFO, " invalid", AUTO_LEN); break; case 1: banout_append(banout, PROTO_VNC_INFO, " none", AUTO_LEN); break; case 2: banout_append(banout, PROTO_VNC_INFO, " VNC-chap", AUTO_LEN); break; case 5: banout_append(banout, PROTO_VNC_INFO, " RA2", AUTO_LEN); break; case 6: banout_append(banout, PROTO_VNC_INFO, " RA2ne", AUTO_LEN); break; case 7: banout_append(banout, PROTO_VNC_INFO, " SSPI", AUTO_LEN); break; case 8: banout_append(banout, PROTO_VNC_INFO, " SSPIne", AUTO_LEN); break; case 16: banout_append(banout, PROTO_VNC_INFO, " Tight", AUTO_LEN); break; case 17: banout_append(banout, PROTO_VNC_INFO, " Ultra", AUTO_LEN); break; case 18: banout_append(banout, PROTO_VNC_INFO, " TLS", AUTO_LEN); break; case 19: banout_append(banout, PROTO_VNC_INFO, " VeNCrypt", AUTO_LEN); break; case 20: banout_append(banout, PROTO_VNC_INFO, " GTK-VNC-SASL", AUTO_LEN); break; case 21: banout_append(banout, PROTO_VNC_INFO, " MD5", AUTO_LEN); break; case 22: banout_append(banout, PROTO_VNC_INFO, " Colin-Dean-xvp", AUTO_LEN); break; case 30: banout_append(banout, PROTO_VNC_INFO, " Apple30", AUTO_LEN); break; case 35: banout_append(banout, PROTO_VNC_INFO, " Apple35", AUTO_LEN); break; default: snprintf(foo, sizeof(foo), " %u", sectype); banout_append(banout, PROTO_VNC_INFO, foo, AUTO_LEN); break; } } /*************************************************************************** ***************************************************************************/ static void vnc_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; unsigned i; char foo[64]; enum { RFB3_3_SECURITYTYPES=50, RFB_SECURITYERROR=60, RFB3_7_SECURITYTYPES=100, RFB_SERVERINIT=200, RFB_SECURITYRESULT=300, RFB_DONE=0x7fffffff, }; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); for (i=0; isub.vnc.version % 10; tcpapi_send(socket, response[version], 12, 0); if (version < 7) /* Version 3.3: the server selects either "none" or * "vnc challenge/response" and informs us which one * to use */ state = RFB3_3_SECURITYTYPES; else { /* Version 3.7 onwards: the server will send us a list * of security types it supports, from which the * client will select one */ state = RFB3_7_SECURITYTYPES; } } else { state = 0xFFFFFFFF; tcpapi_close(socket); } break; case RFB3_3_SECURITYTYPES: case RFB_SECURITYERROR: case RFB_SECURITYRESULT: case RFB_SERVERINIT+20: pstate->sub.vnc.sectype = px[i]; state++; break; case RFB3_3_SECURITYTYPES+1: case RFB3_3_SECURITYTYPES+2: case RFB_SECURITYERROR+1: case RFB_SECURITYERROR+2: case RFB_SECURITYRESULT+1: case RFB_SECURITYRESULT+2: case RFB_SERVERINIT+21: case RFB_SERVERINIT+22: pstate->sub.vnc.sectype <<= 8; pstate->sub.vnc.sectype |= px[i]; state++; break; case RFB3_3_SECURITYTYPES+3: pstate->sub.vnc.sectype <<= 8; pstate->sub.vnc.sectype |= px[i]; banout_append(banout, PROTO_VNC_INFO, "Security types:\n", AUTO_LEN); vnc_append_sectype(banout, pstate->sub.vnc.sectype); if (pstate->sub.vnc.sectype == 0) state = RFB_SECURITYERROR; else if (pstate->sub.vnc.sectype == 1) { /* v3.3 sectype=none * We move immediately to ClientInit stage */ tcpapi_send(socket, "\x01", 1, 0); state = RFB_SERVERINIT; } else { state = RFB_DONE; tcpapi_close(socket); } break; case RFB_SECURITYRESULT+3: pstate->sub.vnc.sectype <<= 8; pstate->sub.vnc.sectype |= px[i]; if (pstate->sub.vnc.sectype == 0) { /* security OK, move to client init */ tcpapi_send(socket, "\x01", 1, 0); state = RFB_SERVERINIT; } else { /* error occurred, so grab error message */ state = RFB_SECURITYERROR; } break; case RFB_SECURITYERROR+3: pstate->sub.vnc.sectype <<= 8; pstate->sub.vnc.sectype = px[i]; banout_append(banout, PROTO_VNC_INFO, "ERROR: ", AUTO_LEN); state++; break; case RFB_SECURITYERROR+4: if (pstate->sub.vnc.sectype == 0) { state = RFB_DONE; tcpapi_close(socket); } else { pstate->sub.vnc.sectype--; banout_append_char(banout, PROTO_VNC_INFO, px[i]); } break; case RFB3_7_SECURITYTYPES: pstate->sub.vnc.len = px[i]; if (pstate->sub.vnc.len == 0) state = RFB_SECURITYERROR; else { state++; banout_append(banout, PROTO_VNC_INFO, "Security types:\n", AUTO_LEN); } break; case RFB3_7_SECURITYTYPES+1: if (pstate->sub.vnc.len != 0) { pstate->sub.vnc.len--; vnc_append_sectype(banout, px[i]); } if (pstate->sub.vnc.len == 0) { banout_append(banout, PROTO_VNC_INFO, "\n", AUTO_LEN); if (pstate->sub.vnc.version < 7) { state = RFB_SERVERINIT; tcpapi_send(socket, "\x01", 1, 0); } else if (pstate->sub.vnc.version == 7) { state = RFB_SERVERINIT; tcpapi_send(socket, "\x01\x01", 2, 0); } else { state = RFB_SECURITYRESULT; tcpapi_send(socket, "\x01", 1, 0); } } else { banout_append(banout, PROTO_VNC_INFO, "\n", AUTO_LEN); } break; case RFB_SERVERINIT: pstate->sub.vnc.width = px[i]; state++; break; case RFB_SERVERINIT+1: pstate->sub.vnc.width <<= 8; pstate->sub.vnc.width |= px[i]; snprintf(foo, sizeof(foo), " width=%u", pstate->sub.vnc.width); banout_append(banout, PROTO_VNC_RFB, foo, AUTO_LEN); state++; break; case RFB_SERVERINIT+2: pstate->sub.vnc.height = px[i]; state++; break; case RFB_SERVERINIT+3: pstate->sub.vnc.height <<= 8; pstate->sub.vnc.height |= px[i]; snprintf(foo, sizeof(foo), " height=%u", pstate->sub.vnc.height); banout_append(banout, PROTO_VNC_RFB, foo, AUTO_LEN); state++; break; case RFB_SERVERINIT+ 4: case RFB_SERVERINIT+ 5: case RFB_SERVERINIT+ 6: case RFB_SERVERINIT+ 7: case RFB_SERVERINIT+ 8: case RFB_SERVERINIT+ 9: case RFB_SERVERINIT+10: case RFB_SERVERINIT+11: case RFB_SERVERINIT+12: case RFB_SERVERINIT+13: case RFB_SERVERINIT+14: case RFB_SERVERINIT+15: case RFB_SERVERINIT+16: case RFB_SERVERINIT+17: case RFB_SERVERINIT+18: case RFB_SERVERINIT+19: state++; break; case RFB_SERVERINIT+23: pstate->sub.vnc.sectype <<= 8; pstate->sub.vnc.sectype |= px[i]; state++; if (pstate->sub.vnc.sectype) { banout_append(banout, PROTO_VNC_INFO, "Name: ", AUTO_LEN); } else { state = RFB_DONE; tcpapi_close(socket); } break; case RFB_SERVERINIT+24: pstate->sub.vnc.sectype--; banout_append_char(banout, PROTO_VNC_INFO, px[i]); if (pstate->sub.vnc.sectype == 0) { banout_append(banout, PROTO_VNC_INFO, "\n", AUTO_LEN); state = RFB_DONE; tcpapi_close(socket); } break; case RFB_DONE: tcpapi_close(socket); i = (unsigned)length; break; default: i = (unsigned)length; break; } pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void * vnc_init(struct Banner1 *banner1) { UNUSEDPARM(banner1); return 0; } /*************************************************************************** ***************************************************************************/ static int vnc_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_vnc = { "vnc", 5900, 0, 0, 0, vnc_selftest, vnc_init, vnc_parse, }; ================================================ FILE: src/proto-vnc.h ================================================ #ifndef PROTO_VNC_H #define PROTO_VNC_H #include "proto-banner1.h" extern const struct ProtocolParserStream banner_vnc; #endif ================================================ FILE: src/proto-x509.c ================================================ /* !!!!! BIZZARE CODE ALERT !!!! This module decodes X.509 public-key certificates using a "state-machine parser". If you are unfamiliar with such parsers, this will look very strange to you. The reason for such parsers is scalability. Certificates are so big that they typically cross packet boundaries. This requires some sort of "reassembly", which in term requires "memory allocation". This is done on a per-connection basis, resulting in running out of memory when dealing with millions of connections. With a state-machine parser, we don't need to reassemble certificates, or allocate memory. Instead, we maintain "state" between fragments. There is about 60 bytes of state that we must keep. If you are a code reviewer, you may care about looking into these common ASN.1 parsing errors. I've marked them with a [NAME] here, you can search these strings in the code to see how they are handled. [ASN1-CHILD-OVERFLOW] when the child length field causes it to exceed the length of its parent [ASN1-CHILD-UNDERFLOW] when there is padding after all the child fields within a larger parent field [ASN1-DER-LENGTH] when there are more bits used to encode a length field than necessary, such as using 0x82 0x00 0x12 instead of simply 0x12 as a length [ASN1-DER-NUMBER] When there are more bits than necessary to encode an integer, such as 0x00 0x00 0x00 0x20 rather than just 0x20. Since we don't deal with numbers, we don't check this. [ASN1-DER-SIGNED] issues with signed vs. unsigned numbers, where unsined 4 byte integers need an extra leading zero byte if their high-order bit is set Since we don't deal with numbers, we don't check this. [ASN1-DER-OID] Issues with inserting zeroes into OIDs. We explicitly deal with the opposite issue, allowing zeroes to be inserted. We should probably chainge that, and detect it as a DER error. CERTIFICATE FORMAT Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING } TBSCertificate ::= SEQUENCE { version [0] EXPLICIT Version DEFAULT v1, serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3 extensions [3] EXPLICIT Extensions OPTIONAL -- If present, version MUST be v3 } Version ::= INTEGER { v1(0), v2(1), v3(2) } CertificateSerialNumber ::= INTEGER Validity ::= SEQUENCE { notBefore Time, notAfter Time } Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } UniqueIdentifier ::= BIT STRING SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING -- contains the DER encoding of an ASN.1 value -- corresponding to the extension type identified -- by extnID } */ #include "proto-x509.h" #include "proto-spnego.h" #include "proto-banout.h" #include "masscan-app.h" #include "smack.h" #include "util-logger.h" #include #include #include #include #include #include #include /**************************************************************************** * The X.509 certificates mark certain extensible fields with ASN.1 * object-identifiers. Instead of copying these out of the certificate, * we match them using an Aho-Corasick DFA parser. These object-ids are * below. At program startup, the main() function must call x509_init() * to build the Aho-Corasick state-machine, which the main state-machine * will use to parse these object-ids. ****************************************************************************/ static struct SMACK *global_mib; /**************************************************************************** * Currently, the only field we extract is the "Common Name". ****************************************************************************/ enum { Subject_Unknown, Subject_Common, }; /**************************************************************************** * See "global_mib" above. ****************************************************************************/ static struct ObjectIdentifer { const char *oid; const char *name; int id; } mib[] = { {"43.1006.51.341332", "selftest"}, /* for regression test */ {"43", "iso.org"}, {"43.6", "dod"}, {"43.6.1", "inet"}, {"43.6.1.2", "mgmt"}, {"43.6.1.2.1", "mib2"}, {"43.6.1.2.1.", "sys"}, {"43.6.1.2.1.1.1", "sysDescr"}, {"43.6.1.2.1.1.2", "sysObjectID"}, {"43.6.1.2.1.1.3", "sysUpTime"}, {"43.6.1.2.1.1.4", "sysContact"}, {"43.6.1.2.1.1.5", "sysName"}, {"43.6.1.2.1.1.6", "sysLocation"}, {"43.6.1.2.1.1.7", "sysServices"}, {"43.6.1.4", "priv"}, {"43.6.1.4.1", "enterprise"}, {"43.6.1.4.1.2001", "okidata"}, {"42", "1.2"}, {"42.840", "1.2.840"}, {"42.840.52", "0"}, {"42.840.113549", "1.2.840.113549"}, {"42.840.113549.1", "1.2.840.113549.1"}, {"42.840.113549.1.1", "1.2.840.113549.1.1"}, {"42.840.113549.1.1.4", "md5WithRSAEncryption"}, {"42.840.113549.1.1.5", "shaWithRSAEncryption"}, {"42.840.113549.1.1.11", "sha256WithRSAEncryption"}, {"42.840.113549.1.9", "1.2.840.113549.1.9"}, {"42.840.113549.1.9.1", "email"}, {"85", "2.5"}, {"85.4", "2.5.4"}, {"85.4.3", "common", Subject_Common}, {"85.4.5", "serial"}, {"85.4.6", "country"}, {"85.4.7", "locality"}, {"85.4.8", "state"}, {"85.4.10", "organization"}, {"85.4.11", "unit"}, {"85.4.13", "description"}, {"85.29", "2.5.29"}, {"85.29.17", "altname", Subject_Common}, {0,0}, }; /**************************************************************************** * Used in converting text object-ids into their binary form. * @see convert_oid() ****************************************************************************/ static unsigned id_prefix_count(unsigned id) { #define TWO_BYTE ((unsigned long long)(~0)<<7) #define THREE_BYTE ((unsigned long long)(~0)<<14) #define FOUR_BYTE ((unsigned long long)(~0)<<21) #define FIVE_BYTE ((unsigned long long)(~0)<<28) if (id & FIVE_BYTE) return 4; if (id & FOUR_BYTE) return 3; if (id & THREE_BYTE) return 2; if (id & TWO_BYTE) return 1; return 0; } /**************************************************************************** * Convert text OID to binary. This is used when building a Aho-Corasick * table for matching object-identifiers: we type the object-ids in the * source-code in human-readable format, but must compile them to binary * pattenrs to match within the X.509 certificates. * @see x509_init() ****************************************************************************/ static unsigned convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src) { size_t offset = 0; /* 'for all text characters' */ while (*src) { const char *next_src; unsigned id; unsigned count; unsigned i; /* skip to next number */ while (*src == '.') src++; /* parse integer */ id = (unsigned)strtoul(src, (char**)&next_src, 0); if (src == next_src) break; /* invalid integer, programming error */ else src = next_src; /* find length of the integer */ count = id_prefix_count(id); /* add binary integer to pattern */ for (i=count; i>0; i--) { if (offset < sizeof_dst) dst[offset++] = ((id>>(7*i)) & 0x7F) | 0x80; } if (offset < sizeof_dst) dst[offset++] = (id & 0x7F); } return (unsigned)offset; } /**************************************************************************** * We need to initialize the OID/MIB parser * This should be called on program startup. * This is so that we can show short names, like "sysName", rather than * the entire OID. ****************************************************************************/ void x509_init(void) { unsigned i; /* We use an Aho-Corasick pattern matcher for this. Not necessarily * the most efficient, but also not bad */ global_mib = smack_create("ssl-oids", 0); /* We just go through the table of OIDs and add them all one by * one */ for (i=0; mib[i].name; i++) { unsigned char pattern[256]; unsigned len; len = convert_oid(pattern, sizeof(pattern), mib[i].oid); smack_add_pattern( global_mib, pattern, len, i, SMACK_ANCHOR_BEGIN | SMACK_SNMP_HACK ); } /* Now that we've added all the OIDs, we need to compile this into * an efficientdata structure. Later, when we get packets, we'll * use this for searching */ smack_compile(global_mib); } /**************************************************************************** * Since ASN.1 contains nested structures, each with their own length field, * we must maintain a small stack as we parse down the structure. Every time * we enter a field, this function "pushes" the ASN.1 "length" field onto * the stack. When we are done parsing the current field, we'll pop the * length back off the stack, and subtract from it the number of bytes * we've parsed. * * @param x * The X.509 certificate parsing structure. * @param next_state * Tells the parser the next field we'll be parsing after this field * at the same level of the nested ASN.1 structure, or nothing if * there are no more fields. * @param remaining * The 'length' field. We call it 'remaining' instead of 'length' * because as more bytes arrive, we decrement the length field until * it reaches zero. Thus, at any point of time, it doesn't represent * the length of the current ASN.1 field, but the remaining-length. ****************************************************************************/ static void ASN1_push(struct CertDecode *x, unsigned next_state, uint64_t remaining) { static const size_t STACK_DEPTH = sizeof(x->stack.remainings) / sizeof(x->stack.remainings[0]); /* X.509 certificates can't be more than 64k in size. Therefore, to * conserve space (as we must store the state for millions of TCP * connections), we use the smallest number possible for the length, * meaning a 16-bit 'unsigned short'. If the certificate has a larger * length field, we need to reject it. */ if ((remaining >> 16) != 0) { fprintf(stderr, "ASN.1 length field too big\n"); x->state = 0xFFFFFFFF; return; } /* Make sure we don't recurse too deep, past the end of the stack. Note * that this condition checks a PRGRAMMING error not an INPUT error, * because we skip over fields we don't care about, and don't recurse * into them even if they have many levels deep */ if (x->stack.depth >= STACK_DEPTH) { fprintf(stderr, "ASN.1 recursion too deep\n"); x->state = 0xFFFFFFFF; return; } /* Subtract this length from it's parent. * *[ASN1-CHILD-OVERFLOW] * It is here that we deal with the classic ASN.1 parsing problem in * which the child object claims a bigger length than its parent * object. We could shrink the length field to fit, then continue * parsing, but instead we choose to instead cease parsing the certificate. * Note that this property is recursive: I don't need to redo the check * all the way up the stack, because I know my parent's length does * not exceed my grandparent's length. * I know certificates exist that trigger this error -- I need to track * them down and figure out why. */ if (x->stack.depth) { if (remaining > x->stack.remainings[0]) { LOG(1, "ASN.1 inner object bigger than container [%u, %u]\n", next_state, x->stack.states[0]); x->state = 0xFFFFFFFF; return; } x->stack.remainings[0] = (unsigned short) (x->stack.remainings[0] - remaining); } /* Since 'remainings[0]' always represents the top of the stack, we * move all the bytes down one during the push operation. I suppose this * is more expensive than doing it the other way, where something * like "raminings[stack.depth]" represents the top of the stack, * meaning no moves are necessary, but I prefer the cleanliness of the * code using [0] index instead */ memmove( &x->stack.remainings[1], &x->stack.remainings[0], x->stack.depth * sizeof(x->stack.remainings[0])); x->stack.remainings[0] = (unsigned short)remaining; memmove( &x->stack.states[1], &x->stack.states[0], x->stack.depth * sizeof(x->stack.states[0])); x->stack.states[0] = (unsigned char)next_state; /* increment the count by one and exit */ x->stack.depth++; } /**************************************************************************** * This is the corresponding 'pop' operation to the ASN1_push() operation. * See that function for more details. * @see ASN1_push() ****************************************************************************/ static unsigned ASN1_pop(struct CertDecode *x) { unsigned next_state; next_state = x->stack.states[0]; x->stack.depth--; memmove( &x->stack.remainings[0], &x->stack.remainings[1], x->stack.depth * sizeof(x->stack.remainings[0])); memmove( &x->stack.states[0], &x->stack.states[1], x->stack.depth * sizeof(x->stack.states[0])); return next_state; } /**************************************************************************** * Called to skip the remainder of the ASN.1 field * @return * 1 if we've reached the end of the field * 0 otherwise ****************************************************************************/ static unsigned ASN1_skip(struct CertDecode *x, unsigned *i, size_t length) { unsigned len; if (x->stack.remainings[0] == 0) return 1; /* bytes remaining in packet */ len = (unsigned)(length - (*i) - 1); /* bytes remaining in field */ if (len > x->stack.remainings[0]) len = x->stack.remainings[0]; /* increment 'offset' by this length */ (*i) += len; /* decrement 'remaining' by this length */ x->stack.remainings[0] = (unsigned short)(x->stack.remainings[0] - len); return x->stack.remainings[0] == 0; } /**************************************************************************** * The X.509 ASN.1 parser is done with a state-machine, where each byte of * the certificate has a corresponding state value. This massive enum * is for all those states. * DANGER NOTE NOTE NOTE NOTE DANGER NOTE DANGER NOTE * These states are in a specific order. We'll just do 'state++' sometimes * to go the next state. Therefore, you can't change the order without * changing the code. ****************************************************************************/ enum X509state { TAG0, TAG0_LEN, TAG0_LENLEN, TAG1, TAG1_LEN, TAG1_LENLEN, VERSION0_TAG, VERSION0_LEN, VERSION0_LENLEN, VERSION1_TAG, VERSION1_LEN, VERSION1_LENLEN, VERSION_CONTENTS, SERIAL_TAG, SERIAL_LEN, SERIAL_LENLEN, SERIAL_CONTENTS, SIG0_TAG, SIG0_LEN, SIG0_LENLEN, SIG1_TAG, SIG1_LEN, SIG1_LENLEN, SIG1_CONTENTS0,SIG1_CONTENTS1, ISSUER0_TAG, ISSUER0_LEN, ISSUER0_LENLEN, ISSUER1_TAG, ISSUER1_LEN, ISSUER1_LENLEN, ISSUER2_TAG, ISSUER2_LEN, ISSUER2_LENLEN, ISSUERID_TAG, ISSUERID_LEN, ISSUERID_LENLEN, ISSUERID_CONTENTS0, ISSUERID_CONTENTS1, ISSUERNAME_TAG, ISSUERNAME_LEN, ISSUERNAME_LENLEN, ISSUERNAME_CONTENTS, VALIDITY_TAG, VALIDITY_LEN, VALIDITY_LENLEN, VNBEFORE_TAG, VNBEFORE_LEN, VNBEFORE_LENLEN, VNBEFORE_CONTENTS, VNAFTER_TAG, VNAFTER_LEN, VNAFTER_LENLEN, VNAFTER_CONTENTS, SUBJECT0_TAG, SUBJECT0_LEN, SUBJECT0_LENLEN, SUBJECT1_TAG, SUBJECT1_LEN, SUBJECT1_LENLEN, SUBJECT2_TAG, SUBJECT2_LEN, SUBJECT2_LENLEN, SUBJECTID_TAG, SUBJECTID_LEN, SUBJECTID_LENLEN, SUBJECTID_CONTENTS0, SUBJECTID_CONTENTS1, SUBJECTNAME_TAG,SUBJECTNAME_LEN,SUBJECTNAME_LENLEN, SUBJECTNAME_CONTENTS, PUBKEY0_TAG, PUBKEY0_LEN, PUBKEY0_LENLEN, PUBKEY0_CONTENTS, EXTENSIONS_A_TAG, EXTENSIONS_A_LEN, EXTENSIONS_A_LENLEN, EXTENSIONS_S_TAG, EXTENSIONS_S_LEN, EXTENSIONS_S_LENLEN, EXTENSION_TAG, EXTENSION_LEN, EXTENSION_LENLEN, EXTENSION_ID_TAG, EXTENSION_ID_LEN, EXTENSION_ID_LENLEN, EXTENSION_ID_CONTENTS0, EXTENSION_ID_CONTENTS1, EXTVALUE_TAG, EXTVALUE_LEN, EXTVALUE_LENLEN, EXTVALUE2_TAG, EXTVALUE2_LEN, EXTVALUE2_LENLEN, EXTVALUE3_TAG, EXTVALUE3_LEN, EXTVALUE3_LENLEN, EXT_DNSNAME_TAG, EXT_DNSNAME_LEN, EXT_DNSNAME_LENLEN, EXT_DNSNAME_CONTENTS, ALGOID0_TAG, ALGOID0_LEN, ALGOID0_LENLEN, ALGOID1_TAG, ALGOID1_LEN, ALGOID1_LENLEN, ALGOID1_CONTENTS0, ALGOID1_CONTENTS1, ENC_TAG, ENC_LEN, ENC_LENLEN, ENC_CONTENTS, PADDING=254, ERROR=0xFFFFFFFF, }; /**************************************************************************** * My parser was kludged together in a couple of hours, and has this bug * where I really don't know the next state like I should. Therefore, this * function patches it, converting the next state I think I want to the * next state that I really do want. * TODO: fix the parser so that this function is no longer necessary. ****************************************************************************/ static unsigned kludge_next(unsigned state) { switch (state) { case TAG1_LEN: return ALGOID0_TAG; case ALGOID0_LEN: return ENC_TAG; case SERIAL_LEN: return SIG0_TAG; case VERSION0_LEN: return SERIAL_TAG; case SIG0_LEN: return ISSUER0_TAG; case ISSUER0_LEN: return VALIDITY_TAG; case SUBJECT0_LEN: return PUBKEY0_TAG; case ISSUER1_LEN: return ISSUER1_TAG; case SUBJECT1_LEN: return SUBJECT1_TAG; case ISSUERID_LEN: return ISSUERNAME_TAG; case EXTENSION_LEN: return EXTENSION_TAG; case EXTENSION_ID_LEN: return EXTVALUE_TAG; case EXT_DNSNAME_LEN: return EXTVALUE3_TAG; case SUBJECTID_LEN: return SUBJECTNAME_TAG; case VALIDITY_LEN: return SUBJECT0_TAG; case VNBEFORE_LEN: return VNAFTER_TAG; case PUBKEY0_LEN: return EXTENSIONS_A_TAG; default: return PADDING; } } /**************************************************************************** * This is a parser for X.509 certificates. It uses "state-machine" * technology, so that it accepts an in-order sequence of fragments. The * entire x.509 certificate does not need to be in memory -- you can start * calling this function when you have only the first fragment. * * It works by enumerating every possible state. In other words, every * byte of an X.509 certificate has an enumerated 'state' variable. As * each byte arrives from the stream, we parse it, and change to the next * state. When we run out of input, we exit the function, saving the * current state-variable. When the next fragment arrives, we resume * at the same state where we left off. ****************************************************************************/ void x509_decode(struct CertDecode *x, const unsigned char *px, size_t length, struct BannerOutput *banout) { unsigned i; enum X509state state = x->state; #define GOTO_ERROR(state, i, length) (state)=0xFFFFFFFF;(i)=(length);continue /* 'for all bytes in the current fragment ...' * 'process that byte, causing a state-transition ' */ for (i=0; istack.remainings[0] == 0) { if (x->stack.depth == 0) return; state = ASN1_pop(x); } /* * Decrement the current 'remaining' length field. */ x->stack.remainings[0]--; /* * Jump to the current current state */ switch (state) { case ENC_TAG: if (px[i] != 0x03) { state = ERROR; continue; } state++; break; case ISSUERNAME_TAG: if (px[i] != 0x13 && px[i] != 0x0c) { state++; continue; } if (x->is_capture_issuer) { banout_append(banout, PROTO_SSL3, " issuer[", AUTO_LEN); } state++; break; case SUBJECTNAME_TAG: if (px[i] != 0x13 && px[i] != 0x0c) { state++; continue; } if (x->is_capture_subject) { banout_append(banout, PROTO_SSL3, " subject[", AUTO_LEN); } state++; break; case ISSUER1_TAG: case SUBJECT1_TAG: x->subject.type = 0; if (px[i] != 0x31) { state++; continue; } state++; break; case VNBEFORE_TAG: case VNAFTER_TAG: if (px[i] != 0x17) { state++; continue; } state++; break; case VERSION0_TAG: if (px[i] != 0xa0) { state = ERROR; continue; } state++; break; case SIG1_TAG: case ISSUERID_TAG: case SUBJECTID_TAG: case EXTENSION_ID_TAG: case ALGOID1_TAG: if (px[i] != 0x06) { state = ERROR; continue; } state++; break; case VERSION1_TAG: case SERIAL_TAG: if (px[i] != 0x02) { state = ERROR; continue; } x->u.num = 0; state++; break; case ISSUERNAME_CONTENTS: if (x->is_capture_issuer) { banout_append(banout, PROTO_SSL3, px+i, 1); if (x->stack.remainings[0] == 0) banout_append(banout, PROTO_SSL3, "]", 1); } break; case SUBJECTNAME_CONTENTS: case EXT_DNSNAME_CONTENTS: if (x->is_capture_subject) { banout_append(banout, PROTO_SSL3, px+i, 1); if (x->stack.remainings[0] == 0) banout_append(banout, PROTO_SSL3, "]", 1); } else if (x->subject.type == Subject_Common) banout_append(banout, PROTO_SSL3, px+i, 1); break; case VERSION_CONTENTS: x->u.num <<= 8; x->u.num |= px[i]; if (x->stack.remainings[0] == 0) state = PADDING; break; case ISSUERID_CONTENTS0: case SUBJECTID_CONTENTS0: case EXTENSION_ID_CONTENTS0: case ALGOID1_CONTENTS0: case SIG1_CONTENTS0: memset(&x->u.oid, 0, sizeof(x->u.oid)); state++; case ISSUERID_CONTENTS1: case SUBJECTID_CONTENTS1: case EXTENSION_ID_CONTENTS1: case ALGOID1_CONTENTS1: case SIG1_CONTENTS1: { size_t id; unsigned offset = i; unsigned oid_state = x->u.oid.state; /* First, look it up */ id = smack_search_next( global_mib, &oid_state, px, &offset, offset + 1); x->u.oid.state = (unsigned short)oid_state; /* Do the multibyte numbers */ x->u.oid.num <<= 7; x->u.oid.num |= px[i] & 0x7F; if (px[i] & 0x80) { /* This is a multibyte number, don't do anything at * this stage */ ; } else { if (id == SMACK_NOT_FOUND) { if (x->u.oid.last_id) { //printf("%s", mib[x->u.oid.last_id].name); x->u.oid.last_id = 0; } //printf(".%u", (unsigned)x->u.oid.num); } else { //printf("%s [%u]\n", mib[x->u.oid.last_id].name, mib[x->u.oid.last_id].id); x->subject.type = mib[id].id; if (x->subject.type == Subject_Common && state == SUBJECTID_CONTENTS1) { if (x->count <= 1) { /* only handle first certificate in the chain */ banout_append(banout, PROTO_SSL3, ", ", 2); } else { x->subject.type = 0; } } //if (x->subject.type == Subject_Common // && state == EXTENSION_ID_CONTENTS1) // ; //banout_append(banout, PROTO_SSL3, ", ", 2); x->u.oid.last_id = (unsigned char)id; } x->u.oid.num = 0; } if (x->stack.remainings[0] == 0) { if (x->u.oid.last_id) { //printf("%s", mib[x->u.oid.last_id].name); x->u.oid.last_id = 0; } state = PADDING; //printf("\n"); } } break; case SERIAL_CONTENTS: x->stack.states[0] = (unsigned char)(state+1); x->u.num <<= 8; x->u.num |= px[i]; if (x->stack.remainings[0] == 0) state = PADDING; break; case TAG0: case TAG1: case SIG0_TAG: case ISSUER0_TAG: case ISSUER2_TAG: case SUBJECT0_TAG: case SUBJECT2_TAG: case VALIDITY_TAG: case PUBKEY0_TAG: case EXTENSIONS_S_TAG: case EXTENSION_TAG: case EXTVALUE2_TAG: case ALGOID0_TAG: if (px[i] != 0x30) { state = ERROR; continue; } state++; break; case EXTENSIONS_A_TAG: if (px[i] != 0xa3) { state = ERROR; continue; } state++; break; /* GeneralName ::= CHOICE { otherName [0] OtherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER } */ case EXTVALUE3_TAG: if (x->subject.type == Subject_Common) { switch (px[i]) { case 0x82: /* dNSName */ banout_append(banout, PROTO_SSL3, ", ", 2); state = EXT_DNSNAME_LEN; break; default: state = PADDING; break; } } else { state = PADDING; } break; case EXTVALUE_TAG: /* can be anything */ switch (px[i]) { default: case 2: state = PADDING; break; case 4: state++; break; } break; case TAG0_LEN: case TAG1_LEN: case VERSION0_LEN: case VERSION1_LEN: case SERIAL_LEN: case SIG0_LEN: case SIG1_LEN: case ISSUER0_LEN: case ISSUER1_LEN: case ISSUER2_LEN: case ISSUERID_LEN: case ISSUERNAME_LEN: case VALIDITY_LEN: case VNBEFORE_LEN: case VNAFTER_LEN: case SUBJECT0_LEN: case SUBJECT1_LEN: case SUBJECT2_LEN: case SUBJECTID_LEN: case SUBJECTNAME_LEN: case EXTENSIONS_A_LEN: case EXTENSIONS_S_LEN: case EXTENSION_LEN: case EXTENSION_ID_LEN: case EXTVALUE_LEN: case EXTVALUE2_LEN: case EXTVALUE3_LEN: case EXT_DNSNAME_LEN: case PUBKEY0_LEN: case ALGOID0_LEN: case ALGOID1_LEN: case ENC_LEN: /* We do the same processing for all the various length fields. * There are three possible length fields: * 0x7F - for lengths 127 and below * 0x81 XX - for lengths 127 to 255 * 0x82 XX XX - for length 256 to 65535 * This state processes the first byte, and if it's an extended * field, switches to the corresponding xxx_LENLEN state */ if (px[i] & 0x80) { x->u.tag.length_of_length = px[i]&0x7F; x->u.tag.remaining = 0; state++; break; } else { x->u.tag.remaining = px[i]; ASN1_push(x, kludge_next(state), x->u.tag.remaining); state += 2; memset(&x->u, 0, sizeof(x->u)); break; } case TAG0_LENLEN: case TAG1_LENLEN: case VERSION0_LENLEN: case VERSION1_LENLEN: case SERIAL_LENLEN: case SIG0_LENLEN: case SIG1_LENLEN: case ISSUER0_LENLEN: case ISSUER1_LENLEN: case ISSUER2_LENLEN: case ISSUERID_LENLEN: case ISSUERNAME_LENLEN: case VALIDITY_LENLEN: case VNBEFORE_LENLEN: case VNAFTER_LENLEN: case SUBJECT0_LENLEN: case SUBJECT1_LENLEN: case SUBJECT2_LENLEN: case SUBJECTID_LENLEN: case SUBJECTNAME_LENLEN: case PUBKEY0_LENLEN: case EXTENSIONS_A_LENLEN: case EXTENSIONS_S_LENLEN: case EXTENSION_LENLEN: case EXTENSION_ID_LENLEN: case EXTVALUE_LENLEN: case EXTVALUE2_LENLEN: case EXTVALUE3_LENLEN: case EXT_DNSNAME_LENLEN: case ALGOID0_LENLEN: case ALGOID1_LENLEN: case ENC_LENLEN: /* We process all multibyte lengths the same way in this * state. */ /* [ASN1-DER-LENGTH] * Check for strict DER compliance, which says that there should * be no leading zero bytes */ if (x->u.tag.remaining == 0 && px[i] == 0) x->is_der_failure = 1; /* parse this byte */ x->u.tag.remaining = (x->u.tag.remaining)<<8 | px[i]; x->u.tag.length_of_length--; /* If we aren't finished yet, loop around and grab the next */ if (x->u.tag.length_of_length) continue; /* [ASN1-DER-LENGTH] * Check for strict DER compliance, which says that for lengths * 127 and below, we need only 1 byte to encode it, not many */ if (x->u.tag.remaining < 128) x->is_der_failure = 1; /* * We have finished parsing the tag-length fields, and are now * ready to parse the 'value'. Push the current state on the * stack, then descend into the child field. */ ASN1_push(x, kludge_next(state-1), x->u.tag.remaining); state++; memset(&x->u, 0, sizeof(x->u)); break; case VNBEFORE_CONTENTS: case VNAFTER_CONTENTS: switch (x->u.timestamp.state) { case 0: x->u.timestamp.year = (px[i] - '0') * 10; x->u.timestamp.state++; break; case 1: x->u.timestamp.year += (px[i] - '0'); x->u.timestamp.state++; break; case 2: x->u.timestamp.month = (px[i] - '0') * 10; x->u.timestamp.state++; break; case 3: x->u.timestamp.month += (px[i] - '0'); x->u.timestamp.state++; break; case 4: x->u.timestamp.day = (px[i] - '0') * 10; x->u.timestamp.state++; break; case 5: x->u.timestamp.day += (px[i] - '0'); x->u.timestamp.state++; break; case 6: x->u.timestamp.hour = (px[i] - '0') * 10; x->u.timestamp.state++; break; case 7: x->u.timestamp.hour += (px[i] - '0'); x->u.timestamp.state++; break; case 8: x->u.timestamp.minute = (px[i] - '0') * 10; x->u.timestamp.state++; break; case 9: x->u.timestamp.minute += (px[i] - '0'); x->u.timestamp.state++; break; case 10: x->u.timestamp.second = (px[i] - '0') * 10; x->u.timestamp.state++; break; case 11: x->u.timestamp.second += (px[i] - '0'); x->u.timestamp.state++; { struct tm tm; time_t now; tm.tm_hour = x->u.timestamp.hour; tm.tm_isdst = 0; tm.tm_mday = x->u.timestamp.day; tm.tm_min = x->u.timestamp.minute; tm.tm_mon = x->u.timestamp.month - 1; tm.tm_sec = x->u.timestamp.second; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_year = 100 + x->u.timestamp.year; now = mktime(&tm); //tm = *localtime(&now); if (state == VNBEFORE_CONTENTS) x->prev = now; else { ;//printf("validity:%u-days\n", (now-x->prev)/(24*60*60)); } } break; case 12: break; } break; case PADDING: /* [ASN1-CHILD-UNDERFLOW] * This state is reached when we've parsed everything inside an * ASN.1 field, yet there are still bytes left to parse. There * are TWO reasons why we reach this state. * #1 there is a strict DER encoding problem, and we ought * to flag the error * #2 are parser is incomplete; we simply haven't added code * for all fields yet, and therefore treat them as padding * We should flag the DER failure, but we can't, because the * existence of unparsed fields mean we'll falsely trigger DER * errors all the time. * * Note that due to the state-machine style parsing, we don't do * anything in this field. This problem naturally takes care of * itself. */ break; case PUBKEY0_CONTENTS: case ENC_CONTENTS: ASN1_skip(x, &i, length); break; case ERROR: default: ASN1_skip(x, &i, length); break; } } /* * Save the state variable and exit */ if (x->state != 0xFFFFFFFF) x->state = state; } void spnego_decode(struct SpnegoDecode *spnego, const unsigned char *px, size_t length, struct BannerOutput *banout) { struct CertDecode *x = spnego->x509; unsigned i; unsigned state = x->state; enum { /*NegotiationToken ::= CHOICE { negTokenInit [0] NegTokenInit, negTokenResp [1] NegTokenResp }*/ NegotiationToken_tag, len, lenlen, NegTokenInit_tag, NegTokenInit_choice, NegTokenResp_tag, NegTokenResp_choice, mechType_tag, negState_tag, supportedMech_tag, responseToken_tag, mechListMIC_tag, mechTypes_tag, reqFlags_tag, mechToken_tag, mechToken_content, responseToken_content, mechToken_content2, responseToken_content2, UnknownContents, }; #define GOTO_ERROR(state, i, length) (state)=0xFFFFFFFF;(i)=(length);continue /* 'for all bytes in the current fragment ...' * 'process that byte, causing a state-transition ' */ for (i=0; istack.remainings[0] == 0) { if (x->stack.depth == 0) return; state = ASN1_pop(x); } /* * Decrement the current 'remaining' length field. */ x->stack.remainings[0]--; /* * Jump to the current current state */ switch (state) { case NegotiationToken_tag: x->brother_state = UnknownContents; switch (px[i]) { case 0xa0: x->child_state = NegTokenInit_tag; break; case 0xa1: x->child_state = NegTokenResp_tag; break; case 0x60: x->child_state = mechType_tag; break; default: x->child_state = UnknownContents; break; } state = len; break; case NegTokenResp_choice: /* NegTokenResp ::= SEQUENCE { negState [0] ENUMERATED { accept-completed (0), accept-incomplete (1), reject (2), request-mic (3) } OPTIONAL, -- REQUIRED in the first reply from the target supportedMech [1] MechType OPTIONAL, -- present only in the first reply from the target responseToken [2] OCTET STRING OPTIONAL, mechListMIC [3] OCTET STRING OPTIONAL, ... }*/ x->brother_state = NegTokenResp_choice; switch (px[i]) { case 0xa0: x->child_state = negState_tag; break; case 0xa1: x->child_state = supportedMech_tag; break; case 0xa2: x->child_state = responseToken_tag; break; case 0xa3: x->child_state = mechListMIC_tag; break; default: x->child_state = UnknownContents; break; } state = len; break; case NegTokenResp_tag: if (px[i] != 0x30) { x->brother_state = UnknownContents; x->child_state = UnknownContents; } else { x->brother_state = UnknownContents; x->child_state = NegTokenResp_choice; } state = len; break; case NegTokenInit_choice: /* NegTokenInit ::= SEQUENCE { mechTypes [0] MechTypeList, reqFlags [1] ContextFlags OPTIONAL, -- inherited from RFC 2478 for backward compatibility, -- RECOMMENDED to be left out mechToken [2] OCTET STRING OPTIONAL, mechListMIC [3] OCTET STRING OPTIONAL, ... } }*/ x->brother_state = NegTokenInit_choice; switch (px[i]) { case 0xa0: x->child_state = mechTypes_tag; break; case 0xa1: x->child_state = reqFlags_tag; break; case 0xa2: x->child_state = mechToken_tag; break; case 0xa3: x->child_state = mechListMIC_tag; break; default: x->child_state = UnknownContents; break; } state = len; break; case NegTokenInit_tag: if (px[i] != 0x30) { x->brother_state = UnknownContents; x->child_state = UnknownContents; } else { x->brother_state = UnknownContents; x->child_state = NegTokenInit_choice; } state = len; break; case mechType_tag: if (px[i] == 0x06) { x->brother_state = NegotiationToken_tag; x->child_state = UnknownContents; } else { x->brother_state = NegotiationToken_tag; x->child_state = UnknownContents; } state = len; break; case negState_tag: case supportedMech_tag: case mechListMIC_tag: case mechTypes_tag: case reqFlags_tag: x->brother_state = UnknownContents; x->child_state = UnknownContents; state = len; break; case responseToken_tag: x->brother_state = UnknownContents; x->child_state = responseToken_content; state = len; break; case mechToken_tag: x->brother_state = UnknownContents; x->child_state = mechToken_content; state = len; break; case mechToken_content: case mechToken_content2: break; /************************************************************************ ************************************************************************ ************************************************************************ ************************************************************************ ************************************************************************ ************************************************************************ ************************************************************************ */ case responseToken_content: ntlmssp_decode_init(&spnego->ntlmssp, x->stack.remainings[0] + 1); state = responseToken_content2; /* fall through */ case responseToken_content2: { size_t new_max = length - i; if (new_max > x->stack.remainings[0] + 1U) new_max = x->stack.remainings[0] + 1; ntlmssp_decode(&spnego->ntlmssp, px+i, new_max, banout); x->stack.remainings[0] -= (unsigned short)(new_max - 1); if (x->stack.remainings[0] == 0) { if (spnego->ntlmssp.buf) free(spnego->ntlmssp.buf); } } break; case len: /* We do the same processing for all the various length fields. * There are three possible length fields: * 0x7F - for lengths 127 and below * 0x81 XX - for lengths 127 to 255 * 0x82 XX XX - for length 256 to 65535 * This state processes the first byte, and if it's an extended * field, switches to the corresponding xxx_LENLEN state */ if (px[i] & 0x80) { x->u.tag.length_of_length = px[i]&0x7F; x->u.tag.remaining = 0; state = lenlen; break; } else { x->u.tag.remaining = px[i]; ASN1_push(x, x->brother_state, x->u.tag.remaining); state = x->child_state; memset(&x->u, 0, sizeof(x->u)); break; } break; case lenlen: /* We process all multibyte lengths the same way in this * state. */ /* [ASN1-DER-LENGTH] * Check for strict DER compliance, which says that there should * be no leading zero bytes */ if (x->u.tag.remaining == 0 && px[i] == 0) x->is_der_failure = 1; /* parse this byte */ x->u.tag.remaining = (x->u.tag.remaining)<<8 | px[i]; x->u.tag.length_of_length--; /* If we aren't finished yet, loop around and grab the next */ if (x->u.tag.length_of_length) continue; /* [ASN1-DER-LENGTH] * Check for strict DER compliance, which says that for lengths * 127 and below, we need only 1 byte to encode it, not many */ if (x->u.tag.remaining < 128) x->is_der_failure = 1; /* * We have finished parsing the tag-length fields, and are now * ready to parse the 'value'. Push the current state on the * stack, then descend into the child field. */ ASN1_push(x, x->brother_state, x->u.tag.remaining); state = x->child_state; memset(&x->u, 0, sizeof(x->u)); break; default: ; } } } /**************************************************************************** * This function must be called to set the initial state. * @param length * The size of the certificate. This is parsed from the SSL/TLS field. * We know that if we exceed this number of bytes, then an overflow has * occurred. ****************************************************************************/ void x509_decode_init(struct CertDecode *x, size_t length) { memset(x, 0, sizeof(*x)); ASN1_push(x, 0xFFFFFFFF, length); } void spnego_decode_init(struct SpnegoDecode *x, size_t length) { memset(x, 0, sizeof(*x)); ASN1_push(x->x509, 0xFFFFFFFF, length); } ================================================ FILE: src/proto-x509.h ================================================ #ifndef PROTO_X509_H #define PROTO_X509_H #include #include struct BannerOutput; /**************************************************************************** * This stores the "state" of the X.509 certificate parser ****************************************************************************/ struct CertDecode { /** This is the master 'state' variable in the massive switch statement */ unsigned state; /** ASN.1 nests fields within fields. Therefore, as we parse down into * the structure, we push the parent length/state info on the stack, * and then when we exit a field, we pop it back off the stack. * NOTE: since space is at a premium, we have separate arrays * for the length/state, instead of an array of objects containing * both. */ struct { unsigned short remainings[9]; unsigned char states[9]; unsigned char depth; } stack; /** We catch some DER non-canonical encoding errors, but not all. Someday * we'll improve the parser to catch all of them */ unsigned is_der_failure:1; unsigned is_capture_subject:1; unsigned is_capture_issuer:1; /** Number of certificates we've processed */ unsigned char count; /** ??? */ time_t prev; /** This parser was originally written just to grab the "subect name" * of a certificate, i.e. "*.google.com" for Google's certificates. * However, there are many different types of subject names. Each * subject name comes in two parts, the first part being an OID * saying the type of subject, then the subject itself. We need to stash * the result of parsing the OID somewhere before parsing the subject */ struct { unsigned type; } subject; unsigned child_state; unsigned brother_state; /** * This union contains the intermediate/partial values as we are decoding * them. Since a packet may end with a field only partially decoded, * we have to stash that value someplace before the next bytes arive * that complete the decoding */ union { struct { unsigned short remaining; unsigned char length_of_length; } tag; uint64_t num; unsigned next_state; struct { uint64_t num; unsigned short state; unsigned char last_id; } oid; struct { unsigned state; unsigned year:7; unsigned month:4; unsigned day:5; unsigned hour:5; unsigned minute:6; unsigned second:6; } timestamp; } u; }; /** * Called before parsing the first fragment of an X.509 certificate */ void x509_decode_init(struct CertDecode *x, size_t length); /** * Called to decode the next fragment of an X.509 certificate. * Must call x509_decode_init() first. */ void x509_decode(struct CertDecode *x, const unsigned char *px, size_t length, struct BannerOutput *banout); /** * Called at program startup to initialize internal parsing structures * for certificates. Once called, it creates static read-only thread-safe * structures */ void x509_init(void); #endif ================================================ FILE: src/proto-zeroaccess.c ================================================ /* ZeroAccess botnet This scans for the P2P ports on the "ZeroAccess" botnet. http://www.symantec.com/connect/blogs/grappling-zeroaccess-botnet http://www.sophos.com/en-us/medialibrary/PDFs/technical%20papers/Sophos_ZeroAccess_Botnet.pdf */ #include "proto-zeroaccess.h" #include "proto-preprocess.h" #include "output.h" #include "proto-banner1.h" #include "util-safefunc.h" #include #include /*************************************************************************** * I hand-crafted this "getL" request packet. It has the ID set to "mass", * then has been CRCed and encrypted. ***************************************************************************/ const unsigned char zeroaccess_getL[] = { 0x46, 0x5d, 0x49, 0x9e, 0x28, 0x94, 0x8d, 0xab, 0xc9, 0xc0, 0xd1, 0x99, 0xe0, 0xf2, 0xc2, 0x5e, }; /*************************************************************************** * Table for the standard CRC32 algorithm used everywhere. ***************************************************************************/ static const unsigned crc32_table[256] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; /*************************************************************************** * Standard CRC32 calculation. ***************************************************************************/ static unsigned crc_calc(const unsigned char *px, unsigned length) { unsigned i; unsigned crc; crc = (unsigned)~0; for (i = 0; i < length; i++) { crc = crc32_table[(crc ^ px[i]) & 0xff] ^ (crc >> 8); } crc = ~crc; return crc; } /*************************************************************************** * Zero-Access encrypts packets with a simple encryption of starting * with the key "ftp2" XORed with each 4-byte word, then rotated left * by one bit after every XOR. Because it's XOR, encryption is also * decryption. ***************************************************************************/ static unsigned zadecrypt(const unsigned char *src, size_t src_len, unsigned char *dst, size_t dst_len) { unsigned key; size_t i; key = 'f'<<24 | 't'<<16 | 'p'<<8 | '2'<<0; for (i=0; i> 0); dst[i+1] = src[i+1] ^ (unsigned char)(key>> 8); dst[i+2] = src[i+2] ^ (unsigned char)(key>>16); dst[i+3] = src[i+3] ^ (unsigned char)(key>>24); key = key<<1 | key>>31; } return (unsigned)src_len; } /*************************************************************************** * Generate a "getL" request. I put this in the code, but I don't really * use it, because I ran it once to generate the hard-coded packet at * the top of this file. ***************************************************************************/ static unsigned generate_getL(unsigned char *out_buf, size_t out_buf_len, unsigned xrand) { unsigned char buf[16]; unsigned crc; if (out_buf_len < 16) return 0; memset(buf, 0, 16); memcpy(&buf[4], "Lteg", 4); /* "getL" */ buf[12] = (unsigned char)(xrand>>24); buf[13] = (unsigned char)(xrand>>16); buf[14] = (unsigned char)(xrand>> 8); buf[15] = (unsigned char)(xrand>> 0); crc = crc_calc(buf, 16); buf[3] = (unsigned char)(crc>>24); buf[2] = (unsigned char)(crc>>16); buf[1] = (unsigned char)(crc>> 8); buf[0] = (unsigned char)(crc>> 0); zadecrypt(buf, 16, out_buf, 16); return 16; } /*************************************************************************** * Handles the response packet from our "getL" request, which is known * as a "retL". This contains a list of IP addresses of infected machines. * therefore, we want to parse the response and grab those IP addresses * so that we know about even more infected machines. ***************************************************************************/ unsigned handle_zeroaccess( struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy) { unsigned char buf[2048]; unsigned len; ipaddress ip_them = parsed->src_ip; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; struct BannerOutput banout[1]; banout->length = 0; banout->next = 0; banout->protocol = PROTO_UDP_ZEROACCESS; UNUSEDPARM(entropy); UNUSEDPARM(px); UNUSEDPARM(length); UNUSEDPARM(port_me); /* Decrypt the response packet */ buf[0] = '\0'; len = zadecrypt(px + parsed->app_offset, parsed->app_length, buf, sizeof(buf)); if (len != parsed->app_length) { return 0; /* is not zeroaccess botnet */ } /* Validate the CRC */ { unsigned old_crc; unsigned new_crc; old_crc = buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24; memset(buf, 0, 4); new_crc = crc_calc(buf, len); if (old_crc != new_crc) return 0; /* not zeroaccess, or corrupted */ } /* Make sure this is a "retl" packet */ if (len < 16 || memcmp(buf+4, "Lter", 4) != 0) return 0; /* not "retL" */ /* List IP addresses */ banout_append(banout, PROTO_UDP_ZEROACCESS, "ZeroAccess:", 11); { unsigned i; unsigned ip_count = buf[12] | buf[13]<<8 | buf[14]<<16 | buf[15]<<24; if (ip_count > 256) return 0; /* too many addresses */ if (16 + ip_count*8 > len) return 0; /* packet overflow */ for (i=0; i>24), (unsigned char)(ip_found>>16), (unsigned char)(ip_found>> 8), (unsigned char)(ip_found>> 0) ); banout_append(banout, PROTO_UDP_ZEROACCESS, szaddr, strlen(szaddr)); } } output_report_banner( out, timestamp, ip_them, 17, port_them, PROTO_UDP_ZEROACCESS, parsed->ip_ttl, banout_string(banout, PROTO_UDP_ZEROACCESS), banout_string_length(banout, PROTO_UDP_ZEROACCESS)); return 0; /* is zeroaccess botnet*/ } /*************************************************************************** ***************************************************************************/ static const unsigned char sample[] = { 0xda, 0xbe, 0x6e, 0xce, 0x28, 0x94, 0x8d, 0xab, 0xc9, 0xc0, 0xd1, 0x99, 0xec, 0xd6, 0xa9, 0x3c }; /*************************************************************************** ***************************************************************************/ int zeroaccess_selftest(void) { unsigned char buf[128]; unsigned old_crc; unsigned new_crc; zadecrypt(sample, sizeof(sample), buf, sizeof(buf)); old_crc = buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24; memset(buf, 0, 4); new_crc = crc_calc(buf, sizeof(sample)); generate_getL(buf, sizeof(buf), 0x7f570a0f); if (memcmp(buf, sample, 16) != 0) return 1; /*fail*/ if (old_crc != new_crc) return 1; /*fail*/ /*generate_getL(buf, sizeof(buf), *(unsigned*)"mass"); { unsigned i; for (i=0; i<16; i++) printf("0x%02x, ", buf[i]); }*/ return 0; /*success*/ } ================================================ FILE: src/proto-zeroaccess.h ================================================ #ifndef PROTO_ZEROACCESS_H #define PROTO_ZEROACCESS_H #include #include struct PreprocessedInfo; struct Output; unsigned handle_zeroaccess( struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy); extern const unsigned char zeroaccess_getL[]; #define zeroaccess_getL_length 16 /** * Regression test this module. * @return * 0 on success, a positive integer otherwise. */ int zeroaccess_selftest(void); #endif ================================================ FILE: src/rawsock-adapter.h ================================================ #ifndef RAWSOCK_ADAPTER_H #define RAWSOCK_ADAPTER_H struct Adapter { struct pcap *pcap; struct pcap_send_queue *sendq; struct __pfring *ring; unsigned is_packet_trace:1; /* is --packet-trace option set? */ unsigned is_vlan:1; unsigned vlan_id; double pt_start; int link_type; }; /** * Retrieve the datalink type of the adapter * * 1 = Ethernet * 12 = Raw IP (no datalink) */ int stack_if_datalink(struct Adapter *adapter); #endif ================================================ FILE: src/rawsock-getif.c ================================================ /* get default route (gateway) IPv4 address of the named network interface/adapter (like "eth0"). This works on both Linux and windows. */ #include "rawsock.h" #include "util-safefunc.h" #include "util-malloc.h" #include "util-logger.h" #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun__) #include #include #include #include #include #include #define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) #if defined(__APPLE__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__NetBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) #elif defined(__FreeBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__OpenBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #else # error unknown platform #endif static struct sockaddr * get_rt_address(struct rt_msghdr *rtm, int desired) { int i; int bitmask = rtm->rtm_addrs; struct sockaddr *sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { if (bitmask & (1 << i)) { if ((1<sa_len) + (char *)sa); } else ; } return NULL; } int rawsock_get_default_interface(char *ifname, size_t sizeof_ifname) { int fd; int seq = (int)time(0); ssize_t err; struct rt_msghdr *rtm; size_t sizeof_buffer; /* * Requests/responses from the kernel are done with an "rt_msghdr" * structure followed by an array of "sockaddr" structures. */ sizeof_buffer = sizeof(*rtm) + 512; rtm = calloc(1, sizeof_buffer); /* * Create a socket for querying the kernel */ fd = socket(AF_ROUTE, SOCK_RAW, 0); if (fd < 0) { perror("socket(PF_ROUTE)"); free(rtm); return errno; } LOG(2, "[+] getif: got socket handle\n"); /* Needs a timeout. Sometimes it'll hang indefinitely waiting for a * response that will never arrive */ { struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); if (err < 0) LOG(0, "[-] SO_RCVTIMEO: %d %s\n", errno, strerror(errno)); err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); if (err < 0) LOG(0, "[-] SO_SNDTIMEO: %d %s\n", errno, strerror(errno)); } /* * Format and send request to kernel */ rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in); rtm->rtm_version = RTM_VERSION; rtm->rtm_flags = RTF_UP; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = RTA_DST | RTA_IFP; rtm->rtm_pid = getpid(); rtm->rtm_seq = seq; /* * Create an empty address of 0.0.0.0 */ { struct sockaddr_in *sin; sin = (struct sockaddr_in *)(rtm + 1); sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; sin->sin_addr.s_addr = 0; } err = write(fd, (char *)rtm, rtm->rtm_msglen); if (err <= 0) { LOG(0, "[-] getif: write(): returned %d %s\n", errno, strerror(errno)); goto fail; } /* * Read responses until we find one that belongs to us */ for (;;) { err = read(fd, (char *)rtm, sizeof_buffer); if (err <= 0) { LOG(0, "[-] getif: read(): returned %d %s\n", errno, strerror(errno)); goto fail; } LOG(2, "[+] getif: got response, len=%d\n", err); if (rtm->rtm_seq != seq) { printf("seq: %u %u\n", rtm->rtm_seq, seq); continue; } if (rtm->rtm_pid != getpid()) { printf("pid: %u %u\n", rtm->rtm_pid, getpid()); continue; } break; } close(fd); fd = -1; /* * Parse our data */ { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP); if (sdl) { size_t len = sdl->sdl_nlen; if (len > sizeof_ifname-1) len = sizeof_ifname-1; memcpy(ifname, sdl->sdl_data, len); ifname[len] = 0; free(rtm); return 0; } } fail: free(rtm); if (fd > 0) close(fd); return -1; } #elif defined(__linux__) #include #include #include #include #include #include #include #include #include #include #include #include #include struct route_info { int priority; struct in_addr dstAddr; struct in_addr srcAddr; struct in_addr gateWay; char ifName[IF_NAMESIZE]; }; static int read_netlink(int fd, char *bufPtr, size_t sizeof_buffer, int seqNum, int pId) { struct nlmsghdr *nlHdr; int readLen = 0, msgLen = 0; do { /* Receive response from the kernel */ if ((readLen = recv(fd, bufPtr, sizeof_buffer - msgLen, 0)) < 0) { perror("SOCK READ: "); return -1; } nlHdr = (struct nlmsghdr *) bufPtr; /* Check if the header is valid */ if ((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)) { perror("Error in received packet"); return -1; } /* Check if the its the last message */ if (nlHdr->nlmsg_type == NLMSG_DONE) { break; } else { /* Else move the pointer to buffer appropriately */ bufPtr += readLen; msgLen += readLen; } /* Check if its a multi part message */ if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) { /* return if its not */ break; } } while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId)); return msgLen; } /* For parsing the route info returned */ static int parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo) { struct rtmsg *rtMsg; struct rtattr *rtAttr; int rtLen = 0; rtMsg = (struct rtmsg *) NLMSG_DATA(nlHdr); /* This must be an IPv4 (AF_INET) route */ if (rtMsg->rtm_family != AF_INET) return 1; /* This must be in main routing table */ if (rtMsg->rtm_table != RT_TABLE_MAIN) return 1; /* Attributes field*/ rtAttr = (struct rtattr *)RTM_RTA(rtMsg); rtLen = RTM_PAYLOAD(nlHdr); #define FORMATADDR(n) ((n)&0xFF), ((n>>8)&0xFF), ((n>>16)&0xFF), ((n>>24)&0xFF) for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) { switch (rtAttr->rta_type) { case RTA_OIF: if_indextoname(*(int *) RTA_DATA(rtAttr), rtInfo->ifName); //LOG(4, "ifname=%s ", rtInfo->ifName); break; case RTA_GATEWAY: rtInfo->gateWay.s_addr = *(u_int *)RTA_DATA(rtAttr); //LOG(4, "gw=%u.%u.%u.%u ", FORMATADDR(rtInfo->gateWay.s_addr)); break; case RTA_PREFSRC: rtInfo->srcAddr.s_addr = *(u_int *)RTA_DATA(rtAttr); //LOG(4, "src=%u.%u.%u.%u ", FORMATADDR(rtInfo->srcAddr.s_addr)); break; case RTA_DST: rtInfo->dstAddr .s_addr = *(u_int *)RTA_DATA(rtAttr); //LOG(4, "dst=%u.%u.%u.%u ", FORMATADDR(rtInfo->dstAddr.s_addr)); break; case RTA_PRIORITY: rtInfo->priority = *(int*)RTA_DATA(rtAttr); //LOG(4, "priority=0x%08x ", rtInfo->priority); break; default: //LOG(4, "rta_type=%d ", rtAttr->rta_type) ; } } //LOG(4, "\n"); return 0; } int rawsock_get_default_interface(char *ifname, size_t sizeof_ifname) { int fd; struct nlmsghdr *nlMsg; char msgBuf[16384]; int len; int msgSeq = 0; unsigned ipv4 = 0; int priority = 0x7FFFFF; /* * Create 'netlink' socket to query kernel */ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { fprintf(stderr, "%s:%d: socket(NETLINK_ROUTE): %d\n", __FILE__, __LINE__, errno); return errno; } /* * format the netlink buffer */ memset(msgBuf, 0, sizeof(msgBuf)); nlMsg = (struct nlmsghdr *)msgBuf; nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlMsg->nlmsg_type = RTM_GETROUTE; nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; nlMsg->nlmsg_seq = msgSeq++; nlMsg->nlmsg_pid = getpid(); /* * send first request to kernel */ if (send(fd, nlMsg, nlMsg->nlmsg_len, 0) < 0) { fprintf(stderr, "%s:%d: send(NETLINK_ROUTE): %d\n", __FILE__, __LINE__, errno); return errno; } /* * Now read all the responses */ len = read_netlink(fd, msgBuf, sizeof(msgBuf), msgSeq, getpid()); if (len <= 0) { fprintf(stderr, "%s:%d: read_netlink: %d\n", __FILE__, __LINE__, errno); return errno; } /* * Parse the response */ for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) { struct route_info rtInfo[1]; int err; memset(rtInfo, 0, sizeof(struct route_info)); //LOG(3, "if: nlmsg_type=%d nlmsg_flags=0x%x\n", nlMsg->nlmsg_type, nlMsg->nlmsg_flags); err = parseRoutes(nlMsg, rtInfo); if (err != 0) continue; LOG(3, "if: route: '%12s' dst=%u.%u.%u.%u src=%u.%u.%u.%u gw=%u.%u.%u.%u priority=%d\n", rtInfo->ifName, FORMATADDR(rtInfo->dstAddr.s_addr), FORMATADDR(rtInfo->srcAddr.s_addr), FORMATADDR(rtInfo->gateWay.s_addr), rtInfo->priority ); /* make sure destination = 0.0.0.0 for "default route" */ if (rtInfo->dstAddr.s_addr != 0) continue; /* found the gateway! */ if (rtInfo->priority < priority) { priority = rtInfo->priority; ipv4 = ntohl(rtInfo->gateWay.s_addr); if (ipv4 == 0) continue; safe_strcpy(ifname, sizeof_ifname, rtInfo->ifName); } } close(fd); return 0; } #endif #if defined(WIN32) /* From: * https://stackoverflow.com/questions/10972794/undefined-reference-to-getadaptersaddresses20-but-i-included-liphlpapi * I think this fixed issue #734 */ #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #include #include #include "massip-parse.h" #ifdef _MSC_VER #pragma comment(lib, "IPHLPAPI.lib") #endif int rawsock_get_default_interface(char *ifname, size_t sizeof_ifname) { PIP_ADAPTER_INFO pAdapterInfo; PIP_ADAPTER_INFO pAdapter = NULL; DWORD err; ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); /* * Allocate a proper sized buffer */ pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO)); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } /* * Query the adapter info. If the buffer is not big enough, loop around * and try again */ again: err = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); if (err == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } goto again; } if (err != NO_ERROR) { fprintf(stderr, "GetAdaptersInfo failed with error: %u\n", (unsigned)err); return EFAULT; } /* * loop through all adapters looking for ours */ for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { unsigned ipv4 = 0; if (pAdapter->Type != MIB_IF_TYPE_ETHERNET && pAdapter->Type != 71 /*wifi*/) continue; /* See if this adapter has a default-route/gateway configured */ { const IP_ADDR_STRING *addr; for (addr = &pAdapter->GatewayList; addr; addr = addr->Next) { unsigned x; x = massip_parse_ipv4(addr->IpAddress.String); if (x != 0xFFFFFFFF) { ipv4 = x; break; } } } /* * When we reach the first adapter with an IP address, then * we'll use that one */ if (ipv4) { snprintf(ifname, sizeof_ifname, "\\Device\\NPF_%s", pAdapter->AdapterName); } } if (pAdapterInfo) free(pAdapterInfo); return 0; } #endif ================================================ FILE: src/rawsock-getip.c ================================================ /* retrieve IPv4 address of the named network interface/adapter like "eth0" This works on: - Windows - Linux - Apple - FreeBSD I think it'll work the same on any BSD system. */ #include "rawsock.h" #include "util-safefunc.h" #include "massip-parse.h" /***************************************************************************** *****************************************************************************/ #if defined(__linux__) #include #include #include #include #include #include #include unsigned rawsock_get_adapter_ip(const char *ifname) { int fd; struct ifreq ifr; struct sockaddr_in *sin; struct sockaddr *sa; int x; fd = socket(AF_INET, SOCK_DGRAM, 0); ifr.ifr_addr.sa_family = AF_INET; safe_strcpy(ifr.ifr_name, IFNAMSIZ, ifname); x = ioctl(fd, SIOCGIFADDR, &ifr); if (x < 0) { fprintf(stderr, "ERROR:'%s': %s\n", ifname, strerror(errno)); //fprintf(stderr, "ERROR:'%s': couldn't discover IP address of network interface\n", ifname); close(fd); return 0; } close(fd); sa = &ifr.ifr_addr; sin = (struct sockaddr_in *)sa; return ntohl(sin->sin_addr.s_addr); } /***************************************************************************** *****************************************************************************/ #elif defined(WIN32) /* From: * https://stackoverflow.com/questions/10972794/undefined-reference-to-getadaptersaddresses20-but-i-included-liphlpapi * I think this fixes issue #734 */ #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #include #include #ifdef _MSC_VER #pragma comment(lib, "IPHLPAPI.lib") #endif unsigned rawsock_get_adapter_ip(const char *ifname) { PIP_ADAPTER_INFO pAdapterInfo; PIP_ADAPTER_INFO pAdapter = NULL; DWORD err; ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); unsigned result = 0; ifname = rawsock_win_name(ifname); /* * Allocate a proper sized buffer */ pAdapterInfo = malloc(sizeof (IP_ADAPTER_INFO)); if (pAdapterInfo == NULL) { fprintf(stderr, "error:malloc(): for GetAdaptersinfo\n"); return 0; } /* * Query the adapter info. If the buffer is not big enough, loop around * and try again */ again: err = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); if (err == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); if (pAdapterInfo == NULL) { fprintf(stderr, "error:malloc(): for GetAdaptersinfo\n"); return 0; } goto again; } if (err != NO_ERROR) { fprintf(stderr, "GetAdaptersInfo failed with error: %u\n", (unsigned)err); return 0; } /* * loop through all adapters looking for ours */ for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { if (rawsock_is_adapter_names_equal(pAdapter->AdapterName, ifname)) break; } if (pAdapter) { const IP_ADDR_STRING *addr; for (addr = &pAdapter->IpAddressList; addr; addr = addr->Next) { unsigned x; x = massip_parse_ipv4(addr->IpAddress.String); if (x != 0xFFFFFFFF) { result = x; goto end; } } } end: if (pAdapterInfo) free(pAdapterInfo); return result; } /***************************************************************************** *****************************************************************************/ #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || 1 #include #include #include #include #include #include #ifdef AF_LINK # include #endif #ifdef AF_PACKET # include #endif unsigned rawsock_get_adapter_ip(const char *ifname) { int err; struct ifaddrs *ifap; struct ifaddrs *p; unsigned ip; /* Get the list of all network adapters */ err = getifaddrs(&ifap); if (err != 0) { perror("getifaddrs"); return 0; } /* Look through the list until we get our adapter */ for (p = ifap; p; p = p->ifa_next) { if (strcmp(ifname, p->ifa_name) == 0 && p->ifa_addr && p->ifa_addr->sa_family == AF_INET) break; } if (p == NULL) goto error; /* not found */ /* Return the address */ { struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; ip = ntohl(sin->sin_addr.s_addr); } freeifaddrs(ifap); return ip; error: freeifaddrs(ifap); return 0; } #endif ================================================ FILE: src/rawsock-getip6.c ================================================ /* retrieve IPv4 address of the named network interface/adapter like "eth0" This works on: - Windows - Linux - Apple - FreeBSD I think it'll work the same on any BSD system. */ #include "rawsock.h" #include "util-safefunc.h" #include "massip-parse.h" /***************************************************************************** *****************************************************************************/ #if defined(__linux__) #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ipv6address rawsock_get_adapter_ipv6(const char *ifname) { struct ifaddrs *list = NULL; struct ifaddrs *ifa; int err; ipv6address result = {0,0}; /* Fetch the list of addresses */ err = getifaddrs(&list); if (err == -1) { fprintf(stderr, "[-] getifaddrs(): %s\n", strerror(errno)); return result; } for (ifa = list; ifa != NULL; ifa = ifa->ifa_next) { ipv6address addr; if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_name == NULL) continue; if (strcmp(ifname, ifa->ifa_name) != 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; addr = ipv6address_from_bytes((const unsigned char *)&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr); if (addr.hi>>56ULL >= 0xFC) continue; if (addr.hi>>32ULL == 0x20010db8) continue; result = addr; break; } freeifaddrs(list); return result; } /***************************************************************************** *****************************************************************************/ #elif defined(WIN32) /* From: * https://stackoverflow.com/questions/10972794/undefined-reference-to-getadaptersaddresses20-but-i-included-liphlpapi */ #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #define WIN32_LEAN_AND_MEAN #include #include #include #ifdef _MSC_VER #pragma comment(lib, "IPHLPAPI.lib") #endif ipv6address rawsock_get_adapter_ipv6(const char *ifname) { ULONG err; ipv6address result = {0,0}; IP_ADAPTER_ADDRESSES *adapters = NULL; IP_ADAPTER_ADDRESSES *adapter; IP_ADAPTER_UNICAST_ADDRESS *addr; ULONG sizeof_addrs = 0; ifname = rawsock_win_name(ifname); again: err = GetAdaptersAddresses( AF_INET6, /* Get IPv6 addresses only */ 0, 0, adapters, &sizeof_addrs); if (err == ERROR_BUFFER_OVERFLOW) { free(adapters); adapters = malloc(sizeof_addrs); if (adapters == NULL) { fprintf(stderr, "GetAdaptersAddresses():malloc(): failed: out of memory\n"); return result; } goto again; } if (err != NO_ERROR) { fprintf(stderr, "GetAdaptersAddresses(): failed: %u\n", (unsigned)err); return result; } /* * loop through all adapters looking for ours */ for (adapter = adapters; adapter != NULL; adapter = adapter->Next) { if (rawsock_is_adapter_names_equal(adapter->AdapterName, ifname)) break; } /* * If our adapter isn't found, print an error. */ if (adapters == NULL) { fprintf(stderr, "GetAdaptersInfo: adapter not found: %s\n", ifname); goto end; } /* * Search through the list of returned addresses looking for the first * that matches an IPv6 address. */ for (addr = adapter->FirstUnicastAddress; addr; addr = addr->Next) { struct sockaddr_in6 *sa = (struct sockaddr_in6 *)addr->Address.lpSockaddr; //char buf[64]; ipv6address ipv6; /* Ignore any address that isn't IPv6 */ if (sa->sin6_family != AF_INET6) continue; /* Ignore transient cluster addresses */ if (addr->Flags == IP_ADAPTER_ADDRESS_TRANSIENT) continue; /* Don't use operating-system function for this, because they aren't * really portable or safe */ ipv6 = ipv6address_from_bytes((const unsigned char *)&sa->sin6_addr); if (addr->PrefixOrigin == IpPrefixOriginWellKnown) { /* This value applies to an IPv6 link-local address or an IPv6 loopback address */ continue; } if (addr->PrefixOrigin == IpPrefixOriginRouterAdvertisement && addr->SuffixOrigin == IpSuffixOriginRandom) { /* This is a temporary IPv6 address * See: http://technet.microsoft.com/en-us/ff568768(v=vs.60).aspx */ continue; } if (ipv6.hi>>56ULL >= 0xFC) continue; if (ipv6.hi>>32ULL == 0x20010db8) continue; result = ipv6; } end: free(adapters); return result; } /***************************************************************************** *****************************************************************************/ #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || 1 #include #include #include #include #include #include #ifdef AF_LINK # include #endif #ifdef AF_PACKET # include #endif ipv6address rawsock_get_adapter_ipv6(const char *ifname) { struct ifaddrs *list = NULL; struct ifaddrs *ifa; int err; ipv6address result = {0,0}; /* Fetch the list of addresses */ err = getifaddrs(&list); if (err == -1) { fprintf(stderr, "[-] getifaddrs(): %s\n", strerror(errno)); return result; } for (ifa = list; ifa != NULL; ifa = ifa->ifa_next) { ipv6address addr; if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_name == NULL) continue; if (strcmp(ifname, ifa->ifa_name) != 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; addr = ipv6address_from_bytes((const unsigned char *)&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr); if (addr.hi>>56ULL >= 0xFC) continue; if (addr.hi>>32ULL == 0x20010db8) continue; result = addr; break; } freeifaddrs(list); return result; } #endif ================================================ FILE: src/rawsock-getmac.c ================================================ /* get MAC address of named network interface/adapter like "eth0" This works on: - Windows - Linux - Apple - FreeBSD I think it'll work the same on any BSD system. */ #include "rawsock.h" #include "util-safefunc.h" #include "util-logger.h" /***************************************************************************** *****************************************************************************/ #if defined(__linux__) #include #include #include #include #include #include #include int rawsock_get_adapter_mac(const char *ifname, unsigned char *mac) { int fd; int x; struct ifreq ifr; fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0){ perror("socket"); goto end; } safe_strcpy(ifr.ifr_name, IFNAMSIZ, ifname); x = ioctl(fd, SIOCGIFHWADDR, (char *)&ifr); if (x < 0) { perror("ioctl"); goto end; } /* Log helpful info about the interface type */ switch (ifr.ifr_ifru.ifru_hwaddr.sa_family) { case 1: LOG(1, "if:%s: type=ethernet(1)\n", ifname); break; default: LOG(1, "if:%s: type=0x%04x\n", ifname, ifr.ifr_ifru.ifru_hwaddr.sa_family); } memcpy(mac, ifr.ifr_ifru.ifru_hwaddr.sa_data, 6); /* * [KLUDGE] * For VPN tunnels with raw IP there isn't a hardware address, so just * return a fake one instead. */ if (memcmp(mac, "\0\0\0\0\0\0", 6) == 0 && ifr.ifr_ifru.ifru_hwaddr.sa_family == 0xfffe) { LOG(1, "%s: creating fake address\n", ifname); mac[5] = 1; } end: close(fd); return 0; } /***************************************************************************** *****************************************************************************/ #elif defined(WIN32) /* From: * https://stackoverflow.com/questions/10972794/undefined-reference-to-getadaptersaddresses20-but-i-included-liphlpapi * I think this fixes issue #734 */ #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #include #include #ifdef _MSC_VER #pragma comment(lib, "IPHLPAPI.lib") #endif int rawsock_get_adapter_mac(const char *ifname, unsigned char *mac) { PIP_ADAPTER_INFO pAdapterInfo; PIP_ADAPTER_INFO pAdapter = NULL; DWORD err; ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); ifname = rawsock_win_name(ifname); /* * Allocate a proper sized buffer */ pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO)); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } /* * Query the adapter info. If the buffer is not big enough, loop around * and try again */ again: err = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); if (err == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } goto again; } if (err != NO_ERROR) { fprintf(stderr, "if: GetAdaptersInfo failed with error: %u\n", (unsigned)err); return EFAULT; } /* * loop through all adapters looking for ours */ for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { if (rawsock_is_adapter_names_equal(pAdapter->AdapterName, ifname)) break; } if (pAdapter) { if (pAdapter->AddressLength != 6) return EFAULT; memcpy(mac, pAdapter->Address, 6); } if (pAdapterInfo) free(pAdapterInfo); return 0; } /***************************************************************************** *****************************************************************************/ #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || 1 #include #include #include #include #include #ifdef AF_LINK # include #endif #ifdef AF_PACKET # include #endif int rawsock_get_adapter_mac(const char *ifname, unsigned char *mac) { int err; struct ifaddrs *ifap; struct ifaddrs *p; /* Get the list of all network adapters */ err = getifaddrs(&ifap); if (err != 0) { perror("getifaddrs"); return 1; } /* Look through the list until we get our adapter */ for (p = ifap; p; p = p->ifa_next) { if (strcmp(ifname, p->ifa_name) == 0 && p->ifa_addr && p->ifa_addr->sa_family == AF_LINK) break; } if (p == NULL) { LOG(1, "if:%s: not found\n", ifname); goto error; /* not found */ } /* Return the address */ { size_t len = 6; struct sockaddr_dl *link; link = (struct sockaddr_dl *)p->ifa_addr; if (len > link->sdl_alen) { memset(mac, 0, 6); len = link->sdl_alen; } LOG(1, "[+] if(%s): family=%u, type=%u, len=%u\n", ifname, link->sdl_family, link->sdl_type, len); memcpy(mac, link->sdl_data + link->sdl_nlen, len); } freeifaddrs(ifap); return 0; error: freeifaddrs(ifap); return -1; } #endif ================================================ FILE: src/rawsock-getroute.c ================================================ /* get default route (gateway) IPv4 address of the named network interface/adapter (like "eth0"). This works on both Linux and windows. */ #include "rawsock.h" #include "util-safefunc.h" #include "util-malloc.h" #include "massip-parse.h" #include "util-logger.h" #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun__) #include #include #include #include #include #include #define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) #if defined(__APPLE__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__NetBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) #elif defined(__FreeBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__OpenBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #else # error unknown platform #endif static struct sockaddr * get_rt_address(struct rt_msghdr *rtm, int desired) { int i; int bitmask = rtm->rtm_addrs; struct sockaddr *sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { if (bitmask & (1 << i)) { if ((1<sa_len) + (char *)sa); } else ; } return NULL; } static void hexdump(const void *v, size_t len) { const unsigned char *p = (const unsigned char *)v; size_t i; for (i=0; irtm_addrs; struct sockaddr *sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { if (bitmask & (1 << i)) { printf("b=%u fam=%u len=%u\n", (1<sa_family, sa->sa_len); hexdump(sa, sa->sa_len + sizeof(sa->sa_family)); sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa); } else ; } } int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4) { int fd; int seq = (int)time(0); ssize_t err; struct rt_msghdr *rtm; size_t sizeof_buffer; /* * Requests/responses from the kernel are done with an "rt_msghdr" * structure followed by an array of "sockaddr" structures. */ sizeof_buffer = sizeof(*rtm) + 512; rtm = calloc(1, sizeof_buffer); /* * Create a socket for querying the kernel */ fd = socket(AF_ROUTE, SOCK_RAW, 0); if (fd < 0) { perror("socket(AF_ROUTE)"); free(rtm); return errno; } /* Needs a timeout. Sometimes it'll hang indefinitely waiting for a * response that will never arrive */ { struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); if (err < 0) LOG(0, "[-] SO_RCVTIMEO: %d %s\n", errno, strerror(errno)); err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); if (err < 0) LOG(0, "[-] SO_SNDTIMEO: %d %s\n", errno, strerror(errno)); } /* * Format and send request to kernel */ rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in); rtm->rtm_version = RTM_VERSION; rtm->rtm_flags = RTF_UP; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = RTA_DST | RTA_IFP; rtm->rtm_pid = getpid(); rtm->rtm_seq = seq; /* * Create an empty address of 0.0.0.0 to query the route */ { struct sockaddr_in *sin; sin = (struct sockaddr_in *)(rtm + 1); sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; sin->sin_addr.s_addr = 0; } err = write(fd, (char *)rtm, rtm->rtm_msglen); if (err <= 0) { LOG(0, "[-] getroute: write(): returned %d %s\n", errno, strerror(errno)); goto fail; } /* * Read responses until we find one that belongs to us */ for (;;) { err = read(fd, (char *)rtm, sizeof_buffer); if (err <= 0) break; if (rtm->rtm_seq != seq) { printf("seq: %u %u\n", rtm->rtm_seq, seq); continue; } if (rtm->rtm_pid != getpid()) { printf("pid: %u %u\n", rtm->rtm_pid, getpid()); continue; } break; } close(fd); /* * Parse our data */ { struct sockaddr_in *sin; struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP); if (sdl) { //hexdump(sdl, sdl->sdl_len); //printf("%.*s\n", sdl->sdl_nlen, sdl->sdl_data); if (memcmp(ifname, sdl->sdl_data, sdl->sdl_nlen) != 0) { fprintf(stderr, "ERROR: ROUTE DOESN'T MATCH INTERFACE\n"); fprintf(stderr, "YOU'LL HAVE TO SET --router-mac MANUALLY\n"); exit(1); } } sin = (struct sockaddr_in *)get_rt_address(rtm, RTA_GATEWAY); if (sin) { *ipv4 = ntohl(sin->sin_addr.s_addr); free(rtm); return 0; } } fail: free(rtm); return -1; } #elif defined(__linux__) #include #include #include #include #include #include #include #include #include #include #include #include #include struct route_info { struct in_addr dstAddr; struct in_addr srcAddr; struct in_addr gateWay; char ifName[IF_NAMESIZE]; }; static int read_netlink(int fd, char *bufPtr, size_t sizeof_buffer, int seqNum, int pId) { struct nlmsghdr *nlHdr; int readLen = 0, msgLen = 0; do { /* Receive response from the kernel */ if ((readLen = recv(fd, bufPtr, sizeof_buffer - msgLen, 0)) < 0) { perror("SOCK READ: "); return -1; } nlHdr = (struct nlmsghdr *) bufPtr; /* Check if the header is valid */ if ((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)) { perror("Error in received packet"); return -1; } /* Check if the its the last message */ if (nlHdr->nlmsg_type == NLMSG_DONE) { break; } else { /* Else move the pointer to buffer appropriately */ bufPtr += readLen; msgLen += readLen; } /* Check if its a multi part message */ if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) { /* return if its not */ break; } } while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId)); return msgLen; } /* For parsing the route info returned */ static int parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo) { struct rtmsg *rtMsg; struct rtattr *rtAttr; int rtLen = 0; rtMsg = (struct rtmsg *) NLMSG_DATA(nlHdr); /* This must be an IPv4 (AF_INET) route */ if (rtMsg->rtm_family != AF_INET) return 1; /* This must be in main routing table */ if (rtMsg->rtm_table != RT_TABLE_MAIN) return 1; /* Attributes field*/ rtAttr = (struct rtattr *)RTM_RTA(rtMsg); rtLen = RTM_PAYLOAD(nlHdr); for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) { switch (rtAttr->rta_type) { case RTA_OIF: if_indextoname(*(int *) RTA_DATA(rtAttr), rtInfo->ifName); break; case RTA_GATEWAY: rtInfo->gateWay.s_addr = *(u_int *)RTA_DATA(rtAttr); break; case RTA_PREFSRC: rtInfo->srcAddr.s_addr = *(u_int *)RTA_DATA(rtAttr); break; case RTA_DST: rtInfo->dstAddr .s_addr = *(u_int *)RTA_DATA(rtAttr); break; } } return 0; } int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4) { int fd; struct nlmsghdr *nlMsg; char msgBuf[16384]; int len; int msgSeq = 0; /* * Set to zero, in case we cannot find it */ *ipv4 = 0; /* * Create 'netlink' socket to query kernel */ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { fprintf(stderr, "%s:%d: socket(NETLINK_ROUTE): %d\n", __FILE__, __LINE__, errno); return errno; } /* * format the netlink buffer */ memset(msgBuf, 0, sizeof(msgBuf)); nlMsg = (struct nlmsghdr *)msgBuf; nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlMsg->nlmsg_type = RTM_GETROUTE; nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; nlMsg->nlmsg_seq = msgSeq++; nlMsg->nlmsg_pid = getpid(); /* * send first request to kernel */ if (send(fd, nlMsg, nlMsg->nlmsg_len, 0) < 0) { fprintf(stderr, "%s:%d: send(NETLINK_ROUTE): %d\n", __FILE__, __LINE__, errno); return errno; } /* * Now read all the responses */ len = read_netlink(fd, msgBuf, sizeof(msgBuf), msgSeq, getpid()); if (len <= 0) { fprintf(stderr, "%s:%d: read_netlink: %d\n", __FILE__, __LINE__, errno); return errno; } /* * Parse the response */ for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) { struct route_info rtInfo[1]; int err; memset(rtInfo, 0, sizeof(struct route_info)); err = parseRoutes(nlMsg, rtInfo); if (err != 0) continue; /* make sure we match the desired network interface */ if (ifname && strcmp(rtInfo->ifName, ifname) != 0) continue; /* make sure destination = 0.0.0.0 for "default route" */ if (rtInfo->dstAddr.s_addr != 0) continue; /* found the gateway! */ *ipv4 = ntohl(rtInfo->gateWay.s_addr); } close(fd); return 0; } #endif #if defined(WIN32) /* From: * https://stackoverflow.com/questions/10972794/undefined-reference-to-getadaptersaddresses20-but-i-included-liphlpapi * I think this fixes issue #734 */ #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #include #include #ifdef _MSC_VER #pragma comment(lib, "IPHLPAPI.lib") #endif int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4) { PIP_ADAPTER_INFO pAdapterInfo; PIP_ADAPTER_INFO pAdapter = NULL; DWORD err; ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); /* * Translate numeric index (if it exists) to real name */ ifname = rawsock_win_name(ifname); //printf("------ %s -----\n", ifname); /* * Allocate a proper sized buffer */ pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO)); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } /* * Query the adapter info. If the buffer is not big enough, loop around * and try again */ again: err = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); if (err == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); if (pAdapterInfo == NULL) { fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n"); return EFAULT; } goto again; } if (err != NO_ERROR) { fprintf(stderr, "GetAdaptersInfo failed with error: %u\n", (unsigned)err); return EFAULT; } /* * loop through all adapters looking for ours */ for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { if (rawsock_is_adapter_names_equal(pAdapter->AdapterName, ifname)) break; } if (pAdapter) { //printf("\tComboIndex: \t%d\n", pAdapter->ComboIndex); //printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName); //printf("\tAdapter Desc: \t%s\n", pAdapter->Description); //printf("\tAdapter Addr: \t"); /*for (i = 0; i < pAdapter->AddressLength; i++) { if (i == (pAdapter->AddressLength - 1)) printf("%.2X\n", (int) pAdapter->Address[i]); else printf("%.2X-", (int) pAdapter->Address[i]); }*/ //printf("\tIndex: \t%d\n", pAdapter->Index); //printf("\tType: \t"); switch (pAdapter->Type) { case MIB_IF_TYPE_OTHER: //printf("Other\n"); break; case MIB_IF_TYPE_ETHERNET: //printf("Ethernet\n"); break; case MIB_IF_TYPE_TOKENRING: //printf("Token Ring\n"); break; case MIB_IF_TYPE_FDDI: //printf("FDDI\n"); break; case MIB_IF_TYPE_PPP: //printf("PPP\n"); break; case MIB_IF_TYPE_LOOPBACK: //printf("Lookback\n"); break; case MIB_IF_TYPE_SLIP: //printf("Slip\n"); break; default: //printf("Unknown type %ld\n", pAdapter->Type); break; } //printf("\tIP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String); //printf("\tIP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String); /*typedef struct _IP_ADDR_STRING { struct _IP_ADDR_STRING* Next; IP_ADDRESS_STRING IpAddress; IP_MASK_STRING IpMask; DWORD Context; } IP_ADDR_STRING, *PIP_ADDR_STRING;*/ { const IP_ADDR_STRING *addr; for (addr = &pAdapter->GatewayList; addr; addr = addr->Next) { unsigned x = massip_parse_ipv4(addr->IpAddress.String); if (x != 0xFFFFFFFF) { *ipv4 = x; goto end; } } } //printf("\n"); } end: if (pAdapterInfo) free(pAdapterInfo); return 0; } #endif ================================================ FILE: src/rawsock-pcapfile.c ================================================ /* Copyright (c) 2007 by Errata Security, All Rights Reserved * Programmer(s): Robert David Graham [rdg] */ /* PCAPFILE This is a small bit of code for reading/writing libpcap files. This is for offline use of the product in cases where people want to post-process files without having to install libpcap. Since the file format is trivial to parse, and a lot of people may not have libpcap installed, we decode it ourselves in this module rather than using the libpcap module. Also, this has the feature of being able to read corrupted files. When it encounters a malformed back (such as one with an impossibly large packet), it skips the malformed data and searches forward for a packet that looks good. */ #define _CRT_SECURE_NO_WARNINGS #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include "rawsock-pcapfile.h" /**************************************************************************** * ****************************************************************************/ #ifdef _MSC_VER #define snprintf _snprintf #define PRId64 "I64d" #define PRIu64 "I64u" #define PRIx64 "I64x" #else #include #endif static int64_t ftell_x(FILE *fp) { #if defined(WIN32) && defined(__GNUC__) return ftello64(fp); #elif defined(WIN32) && defined(_MSC_VER) return _ftelli64(fp); #else return ftello(fp); #endif } static int fseek_x(FILE *fp, int64_t offset, int origin) { #if defined(WIN32) && defined(__GNUC__) return fseeko64(fp, offset, origin); #elif defined(WIN32) && defined(_MSC_VER) return _fseeki64(fp, offset, origin); #else return fseeko(fp, offset, origin); #endif } /**************************************************************************** * ****************************************************************************/ /* PCAP file-format typedef struct pcap_hdr_s { guint32 magic_number; / * magic number * / guint16 version_major; / * major version number * / guint16 version_minor; / * minor version number * / gint32 thiszone; / * GMT to local correction * / guint32 sigfigs; / * accuracy of timestamps * / guint32 snaplen; / * max length of captured packets, in octets * / guint32 network; / * data link type * / } pcap_hdr_t; 0 32-bits - "magic number" 4 16-bits - major version 16-bits - minor version 8 32-bits - timezone offset (should be zero) 12 32-bits - time stamp accuracy (should be zero) 16 32-bits - snap/slice length (maximum packet size) 20 32-bits - link layer type Magic number: a1 b2 c3 d4 = big-endian d4 c3 b2 a1 = little-endian Version: 2.4 = most common version Timezone offset, Timestamp accuracy: these fields are no longer used Link-layer type: 0 BSD loopback devices, except for later OpenBSD 1 Ethernet, and Linux loopback devices 6 802.5 Token Ring 7 ARCnet 8 SLIP 9 PPP 10 FDDI 100 LLC/SNAP-encapsulated ATM 101 "raw IP", with no link 102 BSD/OS SLIP 103 BSD/OS PPP 104 Cisco HDLC 105 802.11 108 later OpenBSD loopback devices (with the AF_ value in network byte order) 113 special Linux "cooked" capture 114 LocalTalk */ /* 802.11 11 * 802.11b - 11-mbps 12 * 802.11d - operation in multiple regulatory domains 13 * 802.11e - wireless multimedia extensions 14 * 802.11g - 54-mbps 15 * 802.11h - power management 16 * 802.11i - MAC security enhancements */ struct PcapFile { FILE *fp; unsigned is_file_header_written:1; unsigned start_sec; unsigned start_usec; unsigned end_sec; unsigned end_usec; char filename[256]; int byte_order; int linktype; long long frame_number; uint64_t file_size; uint64_t bytes_read; /** * This is for asynchronous reading of the file. */ unsigned char *aio_buffer; size_t aio_buffer_size; }; #define CAPFILE_BIGENDIAN 1 #define CAPFILE_LITTLEENDIAN 2 #define CAPFILE_ENDIANUNKNOWN 3 /** Read a 16-bit value from a capture file, depending upon the byte * order within that file */ static unsigned PCAP16(unsigned byte_order, const unsigned char *buf) { switch (byte_order) { case CAPFILE_BIGENDIAN: return buf[0]*256 + buf[1]; case CAPFILE_LITTLEENDIAN: return buf[1]*256 + buf[0]; default: return (unsigned)0xa3a3; } } /** Read a 32-bit value from a capture file, depending upon the byte * order within that file */ static unsigned PCAP32(unsigned byte_order, const unsigned char *buf) { switch (byte_order) { case CAPFILE_BIGENDIAN: return buf[0]<<24 | buf[1]<<16 | buf[2] << 8 | buf[3]; case CAPFILE_LITTLEENDIAN: return buf[3]<<24 | buf[2]<<16 | buf[1] << 8 | buf[0]; default: return (unsigned)0xa3a3; } } /** * Return the "link" type, such as Ethernet, WiFi, Token Ring, etc. */ int pcapfile_datalink(struct PcapFile *handle) { if (handle) return handle->linktype; else return 0; } /** * Determine if the blob (the chunk of from the file read at a certain offset) * looks like a valid packet */ static unsigned smells_like_valid_packet(const unsigned char *px, unsigned length, unsigned byte_order, unsigned link_type) { unsigned secs, usecs, original_length, captured_length; if (length < 16) return 0; secs = PCAP32(byte_order, px+0); usecs = PCAP32(byte_order, px+4); captured_length = PCAP32(byte_order, px+8); original_length = PCAP32(byte_order, px+12); if (secs > 0x50000000) return 0; /* after 2010 */ if (secs < 0x26000000) return 0; /* before 1990 */ if (usecs > 1000000) return 0; if (captured_length > 10000) return 0; if (captured_length < 16) return 0; if (original_length < captured_length) return 0; if (original_length > 10000) return 0; if (captured_length + 16 < length) { unsigned secs2, usecs2, original_length2, captured_length2; const unsigned char *px2 = px + captured_length + 16; secs2 = PCAP32(byte_order, px2+0); usecs2 = PCAP32(byte_order, px2+4); captured_length2 = PCAP32(byte_order, px2+8); original_length2 = PCAP32(byte_order, px2+12); if (secs2 > 0x50000000) return 0; if (secs2 < 0x26000000) return 0; if (usecs2 > 1000000) return 0; if (captured_length2 > 10000) return 0; if (captured_length2 < 16) return 0; if (original_length2 < captured_length2) return 0; if (original_length2 > 10000) return 0; return 1; } else switch (link_type) { case 1: /*ethernet*/ if (px[12] == 0x08 && px[13] == 0x00 && px[14] == 0x45) return 1; } return 0; } unsigned pcapfile_percentdone(struct PcapFile *capfile, uint64_t *r_bytes_read) { if (r_bytes_read) *r_bytes_read = capfile->bytes_read; if (capfile->fp == NULL) return 100; return (unsigned)(capfile->bytes_read*100/capfile->file_size); } /** * Read the next packet from the file stream. */ int pcapfile_readframe( struct PcapFile *capfile, unsigned *r_time_secs, unsigned *r_time_usecs, unsigned *r_original_length, unsigned *r_captured_length, unsigned char *buf, unsigned sizeof_buf ) { size_t bytes_read; unsigned char header[16]; unsigned byte_order = capfile->byte_order; unsigned is_corrupt = 0; again: /* Read in the 16-byte frame header. */ bytes_read = fread(header, 1, 16, capfile->fp); if (bytes_read < 16) { if (bytes_read <= 0) { //fprintf(stderr, "%s: failed to read header\n", capfile->filename); //perror(capfile->filename); } else if (bytes_read == 0) ; /* normal end-of-file */ else fprintf(stderr, "%s: premature end-of-file\n", capfile->filename); return 0; } capfile->bytes_read += bytes_read; /* Parse the frame header into its four fields */ *r_time_secs = PCAP32(byte_order, header); *r_time_usecs = PCAP32(byte_order, header+4); *r_captured_length = PCAP32(byte_order, header+8); *r_original_length = PCAP32(byte_order, header+12); /* Test the frame heade fields to make sure they are sane */ if (*r_time_usecs > 1000100) { if (*r_time_usecs < 1000100) { *r_time_secs += 1; *r_time_usecs -= 1000000; } else if (*r_time_usecs > 0xFFFFFF00) { *r_time_secs -= 1; *r_time_usecs += 1000000; *r_time_usecs &= 0xFFFFFFFF; /* mask off in case of 64-bit ints */ } else is_corrupt = 1; /* shouldn't be more than 1-second, but some capture programs erroneously do that */ } if (*r_time_usecs == 0 && *r_time_secs == 0 && *r_original_length == 0 && *r_captured_length == 0) goto again; if (*r_captured_length > sizeof_buf) is_corrupt = 1; if (*r_original_length < *r_captured_length) is_corrupt = 1; if (*r_original_length < 8) is_corrupt = 1; if (*r_original_length > 160000) is_corrupt = 1; /* * If the file is corrupted, let's move forward in the * stream and look for packets that aren't corrupted */ while (is_corrupt) { /* TODO: we should go backwards a bit in the file */ unsigned char tmp[16384]; int64_t position; unsigned i; /* Remember the current location. We are going to seek * back to an offset from this location once we find a good * packet.*/ position = ftell_x(capfile->fp); if (position == -1) { fprintf(stderr, "%s:%lld: could not resolve file corruption (ftell)\n", capfile->filename, capfile->frame_number); perror(capfile->filename); fseek_x(capfile->fp, 0, SEEK_END); return 0; } /* Print an error message indicating corruption was found. Note * that if corruption happens past 4-gigs on a 32-bit system, this * will print an inaccurate number */ fprintf(stderr, "%s:%lld: corruption found at 0x%08" PRIx64 " (%" PRId64 ")\n", capfile->filename, capfile->frame_number, position, position ); /* Read in the next chunk of data following the corruption. We'll search * this chunk looking for a non-corrupt packet */ bytes_read = fread(tmp, 1, sizeof(tmp), capfile->fp); /* If we reach the end without finding a good frame, then stop */ if (bytes_read == 0) { if (bytes_read <= 0) { fprintf(stderr, "%s: error at end of file\n", capfile->filename); perror(capfile->filename); } else fprintf(stderr, "%s: premature end of file\n", capfile->filename); return 0; } capfile->bytes_read += bytes_read; /* Scan forward (one byte at a time ) looking for a non-corrupt * packet located at that spot */ for (i=0; ilinktype)) continue; /* Woot! We have a non-corrupt packet. Let's now change * the current file-pointer to point to that location. * Notice that we have to be careful when working with * large (>4gig) files on 32-bit systems. The 'fpos_t' is * usually a 64-bit value and can be used to set a position, * but we cannot manipulate it directory (it's an opaque * structure, not an integer), so we have to seek back to the * saved value, then seek relatively forward to the * known-good spot */ position = position + i; if (fseek_x(capfile->fp, position, SEEK_SET) != 0) { fprintf(stderr, "%s: could not resolve file corruption (seek forward)\n", capfile->filename); perror(capfile->filename); fseek_x(capfile->fp, 0, SEEK_END); return 0; } #if 0 /* We could stop here, but we are going to try one more thing. * Most cases of corruption will be because the PREVIOUS packet * was truncated, not because the CURRENT packet was bad. * Since we have seeked forward to find the NEXT packet, we * want to now seek backwards and see if there is actually * a good CURRENT packet. */ if (fseek(capfile->fp, -2000, SEEK_CUR) == 0) { unsigned endpoint = 2000; unsigned j; /* We read in the 2000 bytes prior to the known-good * packet that we discovered above, and also 16 bytes * of the current frame (because the validity check * looks for back-to-back good headers */ bytes_read = fread(tmp, 1, endpoint+16, capfile->fp); /* Scan BACKWARDS through this chunk looking for a * length field that points forward back to the known * good packet */ for (j=0; jlinktype)) { /* Woot! We have found a good packet. Let's now use that * as the new location. */ fseek(capfile->fp, -(signed)(j+16+16), SEEK_CUR); break; } } } else { /* Oops, there was an error seeking backwards. I'm * not quite sure what to do here, so we are just * going to repeat the reset of the file location * that we did above */ if (fseek_x(capfile->fp, position, SEEK_SET) != 0) { perror(capfile->filename); fseek_x(capfile->fp, 0, SEEK_END); return 0; } fseek_x(capfile->fp, i, SEEK_CUR); } #endif /* Print a message saying we've found a good packet. This will * help people figure out where in the file the corruption * happened, so they can figure out why it was corrupt.*/ position = ftell_x(capfile->fp); fprintf(stderr, "%s:%lld: good packet found at 0x%08" PRIx64 " (%" PRId64 ")\n", capfile->filename, capfile->frame_number, position, position ); /* Recurse, continue reading from where we know a good * packet is within the file */ return pcapfile_readframe(capfile, r_time_secs, r_time_usecs, r_original_length, r_captured_length, buf, sizeof_buf); } /* If we get to this point, we are totally hosed and the corruption * is more severe than a few packets. */ printf("No valid packet found in chunk\n"); } /* * Read the packet data */ bytes_read = fread(buf, 1, *r_captured_length, capfile->fp); if (bytes_read < *r_captured_length) { if (bytes_read <= 0) { fprintf(stderr, "%s: could not read packet data, frame #%lld\n", capfile->filename, (long long)capfile->frame_number); perror(capfile->filename); } else fprintf(stderr, "%s: premature end of file\n", capfile->filename); return 0; } capfile->bytes_read += bytes_read; if (capfile->frame_number == 0) { capfile->start_sec = *r_time_secs; capfile->start_usec = *r_time_usecs; } capfile->end_sec = *r_time_secs; capfile->end_usec = *r_time_usecs; capfile->frame_number++; return 1; } void pcapfile_get_timestamps(struct PcapFile *capfile, time_t *start, time_t *end) { *start = capfile->start_sec; *end = capfile->end_sec; } /** * Open a capture file for reading. */ struct PcapFile *pcapfile_openread(const char *capfilename) { FILE *fp; size_t bytes_read; unsigned char buf[24]; unsigned byte_order; unsigned linktype; uint64_t file_size = 0xFFFFffff; if (capfilename == NULL) return 0; /* * Open the file */ fp = fopen(capfilename, "rb"); if (fp == NULL) { fprintf(stderr, "%s: could not open capture file\n", capfilename); perror(capfilename); return 0; } /* Grab info about the file */ { struct stat s; memset(&s, 0, sizeof(s)); if (stat(capfilename, &s) == 0) { file_size = s.st_size; } } /* * Read in the file header */ bytes_read = fread(buf, 1, 24, fp); if (bytes_read < 24) { if (bytes_read <= 0) { fprintf(stderr, "%s: could not read PCAP header\n", capfilename); perror(capfilename); } else fprintf(stderr, "%s: file too short\n", capfilename); fclose(fp); return 0; } /* * Find the "Magic Number", which will tell us what the byte-order * is going to be. There are also odd magic number used by some * speciality systems that hint at other features, such as a 64-bit * version of the file. */ switch (buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]) { case 0xa1b2c3d4: byte_order = CAPFILE_BIGENDIAN; break; case 0xd4c3b2a1: byte_order = CAPFILE_LITTLEENDIAN; break; default: fprintf(stderr, "%s: unknown byte-order in cap file\n", capfilename); byte_order = CAPFILE_ENDIANUNKNOWN; break; } /* Version (of the libpcap standard) */ { unsigned major = PCAP16(byte_order, buf+4); unsigned minor = PCAP16(byte_order, buf+6); if (major != 2 || minor != 4) fprintf(stderr, "%s: unknown version %d.%d\n", capfilename, major, minor); } /* Protocol (ethernet, wifi, etc.) */ linktype = PCAP32(byte_order, buf+20); if (linktype == 0) linktype = 1; switch (linktype) { case 0x7f: /* WiFi, with radiotap headers */ case 1: /*ethernet*/ case 0x69: /* WiFi, no radiotap headers */ case 119: /* Prism II headers (also used for things like Atheros madwifi) */ break; default: fprintf(stderr, "%s: unknown cap file linktype = %d (expected Ethernet or wifi)\n", capfilename, linktype); fclose(fp); return 0; break; } /* Read the first frame's timestamp */ { long loc; char tsbuf[8]; size_t x; loc = ftell(fp); if (loc == -1) { fprintf(stderr, "%s: ftell failed (file system error? seen with VMware HGFS bug)\n", capfilename); perror(capfilename); fclose(fp); return 0; } x = fread(tsbuf, 1, 8, fp); if (x != 8) { perror(capfilename); fclose(fp); return 0; } if (fseek(fp, loc, SEEK_SET) != 0) { fprintf(stderr, "%s: fseek failed (file system error?)\n", capfilename); perror(capfilename); fclose(fp); return 0; } } /* * Now that the file is open and we have read in the header, * allocate a structure that contains this information * and return that structure. */ { struct PcapFile *capfile = 0; capfile = (struct PcapFile*)malloc(sizeof(*capfile)); if (capfile == NULL) exit(1); memset(capfile,0,sizeof(*capfile)); capfile->byte_order = byte_order; snprintf(capfile->filename, sizeof(capfile->filename), "%s", capfilename); capfile->fp = fp; capfile->byte_order = byte_order; capfile->linktype = linktype; capfile->file_size = file_size; capfile->bytes_read = 24; /*from the header*/ return capfile; } } /** * Open a capture file for writing */ struct PcapFile *pcapfile_openwrite(const char *capfilename, unsigned linktype) { char buf[] = "\xd4\xc3\xb2\xa1\x02\x00\x04\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\xff\xff\x00\x00\x69\x00\x00\x00"; FILE *fp; buf[20] = (char)(linktype>>0); buf[21] = (char)(linktype>>8); fp = fopen(capfilename, "wb"); if (fp == NULL) { fprintf(stderr, "Could not open capture file\n"); perror(capfilename); return 0; } if (fwrite(buf, 1, 24, fp) != 24) { fprintf(stderr, "Could not write capture file header\n"); perror(capfilename); fclose(fp); return 0; } { struct PcapFile *capfile = 0; capfile = (struct PcapFile*)malloc(sizeof(*capfile)); if (capfile == NULL) exit(1); memset(capfile,0,sizeof(*capfile)); snprintf(capfile->filename, sizeof(capfile->filename), "%s", capfilename); capfile->fp = fp; capfile->byte_order = CAPFILE_LITTLEENDIAN; capfile->linktype = linktype; return capfile; } } /** * Open a capture file for "appending". This requires that we first * read from it and find out how it's formatted, then figure out * where the end of the file is so that we can start adding * packets at that point. */ struct PcapFile *pcapfile_openappend(const char *capfilename, unsigned linktype) { struct PcapFile *capfile; unsigned char buf[24]; unsigned byte_order; unsigned file_linktype; FILE *fp; /* open the file for appending and reading */ fp = fopen(capfilename, "ab+"); if (fp == NULL && errno == ENOENT) { return pcapfile_openwrite(capfilename, linktype); } if (fp == NULL) { fprintf(stderr, "Could not open capture file to append frame\n"); perror(capfilename); return pcapfile_openappend(capfilename, linktype); } /* Read in the header to discover link type and byte order */ if (fread(buf, 1, 24, fp) != 24) { fprintf(stderr, "Error reading capture file header\n"); perror(capfilename); fclose(fp); return pcapfile_openappend(capfilename, linktype); } /* Seek to the end of the file, where we will start writing * frames from now on. Note that we aren't checking to see if the frames * are corrupt at the end (which happens when the program crashes), * so we may end up writing these frames in a way that cannot be read. */ if (fseek(fp, 0, SEEK_END) != 0) { fprintf(stderr, "Could not seek to end of capture file\n"); perror(capfilename); fclose(fp); return 0; } /* Find out the byte order */ switch (buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]) { case 0xa1b2c3d4: byte_order = CAPFILE_BIGENDIAN; break; case 0xd4c3b2a1: byte_order = CAPFILE_LITTLEENDIAN; break; default: fprintf(stderr, "%s: unknown byte-order in cap file\n", capfilename); fclose(fp); return pcapfile_openappend(capfilename, linktype); } /* Version */ { unsigned major = PCAP16(byte_order, buf+4); unsigned minor = PCAP16(byte_order, buf+6); if (major != 2 || minor != 4) fprintf(stderr, "%s: unknown version %u.%u\n", capfilename, major, minor); } /* Protocol */ file_linktype = PCAP32(byte_order, buf+20); if (linktype != file_linktype) { /* oops, the link types do not agree. Since we want a program to generate * dumps while simultaneously processing multiple inputs, we are going to * create a kludge. Instead of writing to the originally specified file, * we are going to create a new file with the linktype added to it's name */ char linkspec[32]; size_t linkspec_length; char newname[sizeof(capfile->filename)]; size_t i; fclose(fp); linkspec_length = snprintf(linkspec, sizeof(linkspec), "-linktype%d", linktype); if (strstr(capfilename, linkspec) || strlen(capfilename) + linkspec_length + 1 > sizeof(newname)) { /* Oops, we have a problem, it looks like the filename already * has the previous linktype in its name for some reason. At this * unlikely point, we just give up */ fprintf(stderr, "Giving up on appending %u-type frames onto a %u-type file\n", linktype, file_linktype); return 0; } if (strchr(capfilename, '.')) i = strchr(capfilename, '.')-capfilename; else i = strlen(capfilename); memcpy(newname, capfilename, i); memcpy(newname+i, linkspec, linkspec_length); memcpy(newname+i+linkspec_length, capfilename+i, strlen(capfilename+i)+1); return pcapfile_openappend(newname, linktype); } /* Now that everything has checked out, create a file structure and * return it */ { capfile = (struct PcapFile*)malloc(sizeof(*capfile)); if (capfile == NULL) exit(1); memset(capfile,0,sizeof(*capfile)); capfile->byte_order = byte_order; snprintf(capfile->filename, sizeof(capfile->filename), "%s", capfilename); capfile->fp = fp; capfile->byte_order = byte_order; capfile->linktype = linktype; } return capfile; } /** * Close a capture file created by one of the open functions * such as 'pcapfile_openread()', 'pcapfile_openwrite()', or * 'pcapfile_openappend()'. */ void pcapfile_close(struct PcapFile *handle) { if (handle == NULL) return; if (handle->fp) fclose(handle->fp); free(handle); } /** * Called to write a frame of data in libpcap format. This format has a * 16-byte header (microseconds, seconds, sliced-length, original-length) * followed by the captured data */ void pcapfile_writeframe( struct PcapFile *capfile, const void *buffer, unsigned buffer_size, unsigned original_length, unsigned time_sec, unsigned time_usec) { unsigned char header[16]; if (capfile == NULL || capfile->fp == NULL) return; /* * Write timestamp */ if (capfile->byte_order == CAPFILE_BIGENDIAN) { header[ 0] = (unsigned char)(time_sec>>24); header[ 1] = (unsigned char)(time_sec>>16); header[ 2] = (unsigned char)(time_sec>> 8); header[ 3] = (unsigned char)(time_sec>> 0); header[ 4] = (unsigned char)(time_usec>>24); header[ 5] = (unsigned char)(time_usec>>16); header[ 6] = (unsigned char)(time_usec>> 8); header[ 7] = (unsigned char)(time_usec>> 0); header[ 8] = (unsigned char)(buffer_size>>24); header[ 9] = (unsigned char)(buffer_size>>16); header[10] = (unsigned char)(buffer_size>> 8); header[11] = (unsigned char)(buffer_size>> 0); header[12] = (unsigned char)(original_length>>24); header[13] = (unsigned char)(original_length>>16); header[14] = (unsigned char)(original_length>> 8); header[15] = (unsigned char)(original_length>> 0); } else { header[ 0] = (unsigned char)(time_sec>> 0); header[ 1] = (unsigned char)(time_sec>> 8); header[ 2] = (unsigned char)(time_sec>>16); header[ 3] = (unsigned char)(time_sec>>24); header[ 4] = (unsigned char)(time_usec>> 0); header[ 5] = (unsigned char)(time_usec>> 8); header[ 6] = (unsigned char)(time_usec>>16); header[ 7] = (unsigned char)(time_usec>>24); header[ 8] = (unsigned char)(buffer_size>> 0); header[ 9] = (unsigned char)(buffer_size>> 8); header[10] = (unsigned char)(buffer_size>>16); header[11] = (unsigned char)(buffer_size>>24); header[12] = (unsigned char)(original_length>> 0); header[13] = (unsigned char)(original_length>> 8); header[14] = (unsigned char)(original_length>>16); header[15] = (unsigned char)(original_length>>24); } if (fwrite(header, 1, 16, capfile->fp) != 16) { fprintf(stderr, "%s:%lld: could not write packet header\n", capfile->filename, capfile->frame_number); perror(capfile->filename); fclose(capfile->fp); capfile->fp = NULL; } if (fwrite(buffer, 1, buffer_size, capfile->fp) != buffer_size) { fprintf(stderr, "%s:%lld: could not write packet contents\n", capfile->filename, capfile->frame_number); perror(capfile->filename); fclose(capfile->fp); capfile->fp = NULL; } } ================================================ FILE: src/rawsock-pcapfile.h ================================================ /* Copyright (c) 2007 by Errata Security, All Rights Reserved * Programmer(s): Robert David Graham [rdg] */ #ifndef __PCAPFILE_H #define __PCAPFILE_H #ifdef __cplusplus extern "C" { #endif #include #include struct PcapFile; enum pcapfile_datalink_t { PCAPFILE_ETHERNET = 1, PCAPFILE_WIFi = 105, }; int pcapfile_datalink(struct PcapFile *handle); void pcapfile_writeframe( struct PcapFile *capfile, const void *buffer, unsigned buffer_size, unsigned original_length, unsigned time_sec, unsigned time_usec ); struct PcapFile *pcapfile_openread(const char *capfilename); struct PcapFile *pcapfile_openwrite(const char *capfilename, unsigned linktype); struct PcapFile *pcapfile_openappend(const char *capfilename, unsigned linktype); unsigned pcapfile_percentdone(struct PcapFile *handle, uint64_t *r_bytes_read); void pcapfile_get_timestamps(struct PcapFile *handle, time_t *start, time_t *end); /** * Set a "maximum" size for a file. When the current file fills up with data, * it will close that file and open a new one, then continue to write * from that point on in the new file. */ void pcapfile_set_max(struct PcapFile *capfile, unsigned max_megabytes, unsigned max_files); /** * Read a single frame from the file. * Returns 0 if failed to read (from error or end of file), and * returns 1 if successful. */ int pcapfile_readframe( struct PcapFile *capfile, unsigned *r_time_secs, unsigned *r_time_usecs, unsigned *r_original_length, unsigned *r_captured_length, unsigned char *buf, unsigned sizeof_buf ); void pcapfile_close(struct PcapFile *handle); #ifdef __cplusplus } #endif #endif /*__PCAPFILE_H*/ ================================================ FILE: src/rawsock.c ================================================ /* portable interface to "raw sockets" This uses both "libpcap" on systems, but on Linux, we try to use the basic raw sockets, bypassing libpcap for better performance. */ #include "rawsock.h" #include "templ-pkt.h" #include "util-logger.h" #include "main-ptrace.h" #include "util-safefunc.h" #include "stub-pcap.h" #include "stub-pfring.h" #include "pixie-timer.h" #include "main-globals.h" #include "proto-preprocess.h" #include "stack-arpv4.h" #include "stack-ndpv6.h" #include "unusedparm.h" #include "util-malloc.h" #include #include static int is_pcap_file = 0; #ifdef WIN32 #include #include #if defined(_MSC_VER) #pragma comment(lib, "IPHLPAPI.lib") #endif #elif defined(__GNUC__) #include #include #include #include #include #include #include #else #endif #include "rawsock-adapter.h" #define SENDQ_SIZE 65536 * 8 struct AdapterNames { char *easy_name; char *hard_name; }; struct AdapterNames adapter_names[64]; unsigned adapter_name_count = 0; /*************************************************************************** ***************************************************************************/ #ifdef WIN32 int pcap_setdirection(pcap_t *pcap, pcap_direction_t direction) { static int (*real_setdirection)(pcap_t *, pcap_direction_t) = 0; if (real_setdirection == 0) { void* h = LoadLibraryA("wpcap.dll"); if (h == NULL) { fprintf(stderr, "couldn't load wpcap.dll: %u\n", (unsigned)GetLastError()); return -1; } real_setdirection = (int (*)(pcap_t*,pcap_direction_t)) GetProcAddress(h, "pcap_setdirection"); if (real_setdirection == 0) { fprintf(stderr, "couldn't find pcap_setdirection(): %u\n", (unsigned)GetLastError()); return -1; } } return real_setdirection(pcap, direction); } #endif /*************************************************************************** ***************************************************************************/ void rawsock_init(void) { #ifdef WIN32 /* Declare and initialize variables */ // It is possible for an adapter to have multiple // IPv4 addresses, gateways, and secondary WINS servers // assigned to the adapter. // // Note that this sample code only prints out the // first entry for the IP address/mask, and gateway, and // the primary and secondary WINS server for each adapter. PIP_ADAPTER_INFO pAdapterInfo; PIP_ADAPTER_INFO pAdapter = NULL; DWORD dwRetVal = 0; UINT i; /* variables used to print DHCP time info */ //struct tm newtime; //char buffer[32]; ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof (IP_ADAPTER_INFO)); if (pAdapterInfo == NULL) { printf("Error allocating memory needed to call GetAdaptersinfo\n"); return; } // Make an initial call to GetAdaptersInfo to get // the necessary size into the ulOutBufLen variable if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); if (pAdapterInfo == NULL) { printf("Error allocating memory needed to call GetAdaptersinfo\n"); return; } } if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { if (pAdapter->Type != MIB_IF_TYPE_ETHERNET) continue; //printf("\tComboIndex: \t%d\n", pAdapter->ComboIndex); //printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName); { size_t name_len = strlen(pAdapter->AdapterName) + 12 + 1; char *name = (char*)malloc(name_len); size_t addr_len = pAdapter->AddressLength * 3 + 1; char *addr = (char*)malloc(addr_len); if (name == NULL || addr == NULL) exit(1); snprintf(name, name_len, "\\Device\\NPF_%s", pAdapter->AdapterName); //printf("\tAdapter Desc: \t%s\n", pAdapter->Description); //printf("\tAdapter Addr: \t"); for (i = 0; i < pAdapter->AddressLength; i++) { if (i == (pAdapter->AddressLength - 1)) snprintf(addr+i*3, addr_len-i*3, "%.2X", pAdapter->Address[i]); else snprintf(addr+i*3, addr_len-i*3, "%.2X-", pAdapter->Address[i]); } //printf("%s -> %s\n", addr, name); adapter_names[adapter_name_count].easy_name = addr; adapter_names[adapter_name_count].hard_name = name; adapter_name_count++; } //printf("\tIndex: \t%d\n", pAdapter->Index); { size_t name_len = strlen(pAdapter->AdapterName) + 12 + 1; char *name = (char*)malloc(name_len); size_t addr_len = strlen(pAdapter->IpAddressList.IpAddress.String) + 1; char *addr = (char*)malloc(addr_len); if (name == NULL || addr == NULL) exit(1); snprintf(name, name_len, "\\Device\\NPF_%s", pAdapter->AdapterName); snprintf(addr, addr_len, "%s", pAdapter->IpAddressList.IpAddress.String); //printf("%s -> %s\n", addr, name); adapter_names[adapter_name_count].easy_name = addr; adapter_names[adapter_name_count].hard_name = name; adapter_name_count++; } } } else { printf("GetAdaptersInfo failed with error: %u\n", (unsigned)dwRetVal); } if (pAdapterInfo) free(pAdapterInfo); #else PFRING_init(); #endif return; } /*************************************************************************** * This function prints to the command line a list of all the network * interfaces/devices. ***************************************************************************/ void rawsock_list_adapters(void) { pcap_if_t *alldevs; char errbuf[PCAP_ERRBUF_SIZE]; if (PCAP.findalldevs(&alldevs, errbuf) != -1) { int i; const pcap_if_t *d; i=0; if (alldevs == NULL) { fprintf(stderr, "ERR:libpcap: no adapters found, are you sure you are root?\n"); } /* Print the list */ for(d=alldevs; d; d=PCAP.dev_next(d)) { fprintf(stderr, " %d %s \t", i++, PCAP.dev_name(d)); if (PCAP.dev_description(d)) fprintf(stderr, "(%s)\n", PCAP.dev_description(d)); else fprintf(stderr, "(No description available)\n"); } fprintf(stderr,"\n"); PCAP.freealldevs(alldevs); } else { fprintf(stderr, "%s\n", errbuf); } } /*************************************************************************** ***************************************************************************/ static const char * adapter_from_index(unsigned index) { pcap_if_t *alldevs; char errbuf[PCAP_ERRBUF_SIZE]; int x; x = PCAP.findalldevs(&alldevs, errbuf); if (x != -1) { const pcap_if_t *d; if (alldevs == NULL) { fprintf(stderr, "ERR:libpcap: no adapters found, are you sure you are root?\n"); } /* Print the list */ for(d=alldevs; d; d=PCAP.dev_next(d)) { if (index-- == 0) return PCAP.dev_name(d); } return 0; } else { return 0; } } /*************************************************************************** * Some methods of transmit queue multiple packets in a buffer then * send all queued packets at once. At the end of a scan, we might have * some pending packets that haven't been transmitted yet. Therefore, * we'll have to flush them. ***************************************************************************/ void rawsock_flush(struct Adapter *adapter) { if (adapter->sendq) { PCAP.sendqueue_transmit(adapter->pcap, adapter->sendq, 0); /* Dude, I totally forget why this step is necessary. I vaguely * remember there's a good reason for it though */ PCAP.sendqueue_destroy(adapter->sendq); adapter->sendq = PCAP.sendqueue_alloc(SENDQ_SIZE); } } /*************************************************************************** * wrapper for libpcap's sendpacket * * PORTABILITY: WINDOWS and PF_RING * For performance, Windows and PF_RING can queue up multiple packets, then * transmit them all in a chunk. If we stop and wait for a bit, we need * to flush the queue to force packets to be transmitted immediately. ***************************************************************************/ int rawsock_send_packet( struct Adapter *adapter, const unsigned char *packet, unsigned length, unsigned flush) { /* Why: this happens in "offline mode", when we are benchmarking the * core algorithms without sending packets. */ if (adapter == 0) return 0; /* Print --packet-trace if debugging */ if (adapter->is_packet_trace) { packet_trace(stdout, adapter->pt_start, packet, length, 1); } /* PF_RING */ if (adapter->ring) { int err = PF_RING_ERROR_NO_TX_SLOT_AVAILABLE; while (err == PF_RING_ERROR_NO_TX_SLOT_AVAILABLE) { err = PFRING.send(adapter->ring, packet, length, (unsigned char)flush); } if (err < 0) LOG(1, "pfring:xmit: ERROR %d\n", err); return err; } /* WINDOWS PCAP */ if (adapter->sendq) { int err; struct pcap_pkthdr hdr; hdr.len = length; hdr.caplen = length; err = PCAP.sendqueue_queue(adapter->sendq, &hdr, packet); if (err) { rawsock_flush(adapter); PCAP.sendqueue_queue(adapter->sendq, &hdr, packet); } if (flush) { rawsock_flush(adapter); } return 0; } /* LIBPCAP */ if (adapter->pcap) return PCAP.sendpacket(adapter->pcap, packet, length); return 0; } /*************************************************************************** ***************************************************************************/ int rawsock_recv_packet( struct Adapter *adapter, unsigned *length, unsigned *secs, unsigned *usecs, const unsigned char **packet) { if (adapter->ring) { /* This is for doing libpfring instead of libpcap */ struct pfring_pkthdr hdr; int err; again: err = PFRING.recv(adapter->ring, (unsigned char**)packet, 0, /* zero-copy */ &hdr, 0 /* return immediately */ ); if (err == PF_RING_ERROR_NO_PKT_AVAILABLE || hdr.caplen == 0) { PFRING.poll(adapter->ring, 1); if (is_tx_done) return 1; goto again; } if (err) return 1; *length = hdr.caplen; *secs = (unsigned)hdr.ts.tv_sec; *usecs = (unsigned)hdr.ts.tv_usec; } else if (adapter->pcap) { struct pcap_pkthdr hdr; *packet = PCAP.next(adapter->pcap, &hdr); if (*packet == NULL) { if (is_pcap_file) { //pixie_time_set_offset(10*100000); is_tx_done = 1; is_rx_done = 1; } return 1; } *length = hdr.caplen; *secs = (unsigned)hdr.ts.tv_sec; *usecs = (unsigned)hdr.ts.tv_usec; } return 0; } /*************************************************************************** * Sends the TCP SYN probe packet. * * Step 1: format the packet * Step 2: send it in a portable manner ***************************************************************************/ void rawsock_send_probe_ipv4( struct Adapter *adapter, ipv4address ip_them, unsigned port_them, ipv4address ip_me, unsigned port_me, unsigned seqno, unsigned flush, struct TemplateSet *tmplset) { unsigned char px[2048]; size_t packet_length; /* * Construct the destination packet */ template_set_target_ipv4(tmplset, ip_them, port_them, ip_me, port_me, seqno, px, sizeof(px), &packet_length); /* * Send it */ rawsock_send_packet(adapter, px, (unsigned)packet_length, flush); } void rawsock_send_probe_ipv6( struct Adapter *adapter, ipv6address ip_them, unsigned port_them, ipv6address ip_me, unsigned port_me, unsigned seqno, unsigned flush, struct TemplateSet *tmplset) { unsigned char px[2048]; size_t packet_length; /* * Construct the destination packet */ template_set_target_ipv6(tmplset, ip_them, port_them, ip_me, port_me, seqno, px, sizeof(px), &packet_length); /* * Send it */ rawsock_send_packet(adapter, px, (unsigned)packet_length, flush); } /*************************************************************************** * Used on Windows: network adapters have horrible names, so therefore we * use numeric indexes instead. You can which adapter you are looking for * by typing "--iflist" as an option. ***************************************************************************/ static int is_numeric_index(const char *ifname) { int result = 1; int i; /* empty strings aren't numbers */ if (ifname[0] == '\0') return 0; /* 'true' if all digits */ for (i=0; ifname[i]; i++) { char c = ifname[i]; if (c < '0' || '9' < c) result = 0; } return result; } /*************************************************************************** * Used on Windows: if the adapter name is a numeric index, convert it to * the full name. ***************************************************************************/ const char * rawsock_win_name(const char *ifname) { if (is_numeric_index(ifname)) { const char *new_adapter_name; new_adapter_name = adapter_from_index(atoi(ifname)); if (new_adapter_name) return new_adapter_name; } return ifname; } /*************************************************************************** * Configure the socket to not capture transmitted packets. This is needed * because we transmit packets at a rate of millions per second, which will * overwhelm the receive thread. * * PORTABILITY: Windows doesn't seem to support this feature, so instead * what we do is apply a BPF filter to ignore the transmits, so that they * still get filtered at a low level. ***************************************************************************/ void rawsock_ignore_transmits(struct Adapter *adapter, const char *ifname) { if (adapter->ring) { /* PORTABILITY: don't do anything for PF_RING, because it's * actually done when we create the adapter, because we can't * reconfigure the adapter after it's been activated. */ return; } if (adapter->pcap) { int err; err = PCAP.setdirection(adapter->pcap, PCAP_D_IN); if (err) { ; //PCAP.perror(adapter->pcap, "if: pcap_setdirection(IN)"); } else { LOG(2, "if:%s: not receiving transmits\n", ifname); } } } /*************************************************************************** ***************************************************************************/ static void rawsock_close_adapter(struct Adapter *adapter) { if (adapter->ring) { PFRING.close(adapter->ring); } if (adapter->pcap) { PCAP.close(adapter->pcap); } if (adapter->sendq) { PCAP.sendqueue_destroy(adapter->sendq); } free(adapter); } /*************************************************************************** * Does the name look like a PF_RING DNA adapter? Common names are: * dna0 * dna1 * dna0@1 * ***************************************************************************/ static int is_pfring_dna(const char *name) { if (strlen(name) < 4) return 0; if (memcmp(name, "zc:", 3) == 0) return 1; if (memcmp(name, "dna", 3) != 0) return 0; name +=3; if (!isdigit(name[0]&0xFF)) return 0; while (isdigit(name[0]&0xFF)) name++; if (name[0] == '\0') return 1; if (name[0] != '@') return 0; else name++; if (!isdigit(name[0]&0xFF)) return 0; while (isdigit(name[0]&0xFF)) name++; if (name[0] == '\0') return 1; else return 0; } /*************************************************************************** ***************************************************************************/ struct Adapter * rawsock_init_adapter(const char *adapter_name, unsigned is_pfring, unsigned is_sendq, unsigned is_packet_trace, unsigned is_offline, const char *bpf_filter, unsigned is_vlan, unsigned vlan_id) { struct Adapter *adapter; char errbuf[PCAP_ERRBUF_SIZE] = "pcap"; /* BPF filter not supported on some platforms, so ignore this compiler * warning when unused */ UNUSEDPARM(bpf_filter); adapter = CALLOC(1, sizeof(*adapter)); adapter->is_packet_trace = is_packet_trace; adapter->pt_start = 1.0 * pixie_gettime() / 1000000.0; adapter->is_vlan = is_vlan; adapter->vlan_id = vlan_id; if (is_offline) return adapter; /*---------------------------------------------------------------- * PORTABILITY: WINDOWS * If is all digits index, then look in indexed list *----------------------------------------------------------------*/ if (is_numeric_index(adapter_name)) { const char *new_adapter_name; new_adapter_name = adapter_from_index(atoi(adapter_name)); if (new_adapter_name == 0) { fprintf(stderr, "pcap_open_live(%s) error: bad index\n", adapter_name); return 0; } else adapter_name = new_adapter_name; } /*---------------------------------------------------------------- * PORTABILITY: PF_RING * If we've been told to use --pfring, then attempt to open the * network adapter using the PF_RING API rather than libpcap. * Since a lot of things can go wrong, we do a lot of extra * logging here. *----------------------------------------------------------------*/ if(is_pfring && !is_pfring_dna(adapter_name)){ /*First ensure pfring dna adapter is available*/ fprintf(stderr,"No pfring adapter available. Please install pfring or run masscan without the --pfring option.\n"); return 0; } if (is_pfring_dna(adapter_name)) { int err; unsigned version; /* * Open * * TODO: Do we need the PF_RING_REENTRANT flag? We only have one * transmit and one receive thread, so I don't think we need it. * Also, this reduces performance in half, from 12-mpps to * 6-mpps. * NOTE: I don't think it needs the "re-entrant" flag, because it * transmit and receive are separate functions? */ LOG(2, "pfring:'%s': opening...\n", adapter_name); adapter->ring = PFRING.open(adapter_name, 1500, 0);//PF_RING_REENTRANT); adapter->pcap = (pcap_t*)adapter->ring; adapter->link_type = 1; if (adapter->ring == NULL) { LOG(0, "pfring:'%s': OPEN ERROR: %s\n", adapter_name, strerror(errno)); return 0; } else LOG(1, "pfring:'%s': successfully opened\n", adapter_name); /* * Housekeeping */ PFRING.set_application_name(adapter->ring, "masscan"); PFRING.version(adapter->ring, &version); LOG(1, "pfring: version %d.%d.%d\n", (version >> 16) & 0xFFFF, (version >> 8) & 0xFF, (version >> 0) & 0xFF); LOG(2, "pfring:'%s': setting direction\n", adapter_name); err = PFRING.set_direction(adapter->ring, rx_only_direction); if (err) { fprintf(stderr, "pfring:'%s': setdirection = %d\n", adapter_name, err); } else LOG(2, "pfring:'%s': direction success\n", adapter_name); /* * Activate * * PF_RING requires a separate activation step. */ LOG(2, "pfring:'%s': activating\n", adapter_name); err = PFRING.enable_ring(adapter->ring); if (err != 0) { LOG(0, "pfring: '%s': ENABLE ERROR: %s\n", adapter_name, strerror(errno)); PFRING.close(adapter->ring); adapter->ring = 0; return 0; } else LOG(1, "pfring:'%s': successfully enabled\n", adapter_name); return adapter; } /*---------------------------------------------------------------- * Kludge: for using files *----------------------------------------------------------------*/ if (memcmp(adapter_name, "file:", 5) == 0) { LOG(1, "pcap: file: %s\n", adapter_name+5); is_pcap_file = 1; adapter->pcap = PCAP.open_offline(adapter_name+5, errbuf); adapter->link_type = PCAP.datalink(adapter->pcap); } /*---------------------------------------------------------------- * PORTABILITY: LIBPCAP * * This is the standard that should work everywhere. *----------------------------------------------------------------*/ { int err; LOG(1, "[+] if(%s): pcap: %s\n", adapter_name, PCAP.lib_version()); LOG(2, "[+] if(%s): opening...\n", adapter_name); /* This reserves resources, but doesn't actually open the * adapter until we call pcap_activate */ adapter->pcap = PCAP.create(adapter_name, errbuf); if (adapter->pcap == NULL) { adapter->pcap = PCAP.open_live( adapter_name, /* interface name */ 65536, /* max packet size */ 8, /* promiscuous mode */ 1000, /* read timeout in milliseconds */ errbuf); if (adapter->pcap == NULL) { LOG(0, "FAIL:%s: can't open adapter: %s\n", adapter_name, errbuf); if (strstr(errbuf, "perm")) { LOG(0, "FAIL: permission denied\n"); LOG(0, " [hint] need to sudo or run as root or something\n"); } return 0; } } else { err = PCAP.set_snaplen(adapter->pcap, 65536); if (err) { PCAP.perror(adapter->pcap, "if: set_snaplen"); goto pcap_error; } err = PCAP.set_promisc(adapter->pcap, 8); if (err) { PCAP.perror(adapter->pcap, "if: set_promisc"); goto pcap_error; } err = PCAP.set_timeout(adapter->pcap, 1000); if (err) { PCAP.perror(adapter->pcap, "if: set_timeout"); goto pcap_error; } err = PCAP.set_immediate_mode(adapter->pcap, 1); if (err) { PCAP.perror(adapter->pcap, "if: set_immediate_mode"); goto pcap_error; } /* If errors happen, they aren't likely to happen above, but will * happen where when they are applied */ err = PCAP.activate(adapter->pcap); switch (err) { case 0: /* drop down below */ break; case PCAP_ERROR_PERM_DENIED: LOG(0, "[-] FAIL: permission denied\n"); LOG(0, " [hint] need to sudo or run as root or something\n"); goto pcap_error; default: LOG(0, "[-] if(%s): activate:%d: %s\n", adapter_name, err, PCAP.geterr(adapter->pcap)); if (err < 0) goto pcap_error; } } LOG(1, "[+] if(%s): successfully opened\n", adapter_name); /* Figure out the link-type. We suport Ethernet and IP */ adapter->link_type = PCAP.datalink(adapter->pcap); switch (adapter->link_type) { case -1: PCAP.perror(adapter->pcap, "if: datalink"); goto pcap_error; case 0: /* Null/Loopback [VPN tunnel] */ LOG(1, "[+] if(%s): VPN tunnel interface found\n", adapter_name); break; case 1: /* Ethernet */ case 12: /* IP Raw */ break; default: LOG(0, "[-] if(%s): unknown data link type: %u(%s)\n", adapter_name, adapter->link_type, PCAP.datalink_val_to_name(adapter->link_type)); break; } } /*---------------------------------------------------------------- * PORTABILITY: WINDOWS * * The transmit rate on Windows is really slow, like 40-kpps. * The speed can be increased by using the "sendqueue" feature * to roughly 300-kpps. *----------------------------------------------------------------*/ adapter->sendq = 0; #if defined(WIN32) if (is_sendq) adapter->sendq = PCAP.sendqueue_alloc(SENDQ_SIZE); #endif return adapter; pcap_error: if (adapter->pcap) { PCAP.close(adapter->pcap); adapter->pcap = NULL; } if (adapter->pcap == NULL) { if (strcmp(adapter_name, "vmnet1") == 0) { LOG(0, " [hint] VMware on Macintosh doesn't support masscan\n"); } return 0; } return NULL; } /*************************************************************************** * for testing when two Windows adapters have the same name. Sometimes * the \Device\NPF_ string is prepended, sometimes not. ***************************************************************************/ int rawsock_is_adapter_names_equal(const char *lhs, const char *rhs) { if (memcmp(lhs, "\\Device\\NPF_", 12) == 0) lhs += 12; if (memcmp(rhs, "\\Device\\NPF_", 12) == 0) rhs += 12; return strcmp(lhs, rhs) == 0; } /*************************************************************************** * Runs some tests when the "--debug if" option is given on the * command-line. This is useful to figure out why the interface you * are accessing doesn't work. ***************************************************************************/ int rawsock_selftest_if(const char *ifname) { int err; ipv4address_t ipv4 = 0; ipv6address_t ipv6; ipv4address_t router_ipv4 = 0; macaddress_t source_mac = {{0,0,0,0,0,0}}; struct Adapter *adapter; char ifname2[246]; ipaddress_formatted_t fmt; /* * Get the interface */ if (ifname == NULL || ifname[0] == 0) { err = rawsock_get_default_interface(ifname2, sizeof(ifname2)); if (err) { printf("[-] if = not found (err=%d)\n", err); return -1; } ifname = ifname2; } printf("[+] if = %s\n", ifname); /* * Initialize the adapter. */ adapter = rawsock_init_adapter(ifname, 0, 0, 0, 0, 0, 0, 0); if (adapter == 0) { printf("[-] pcap = failed\n"); return -1; } else { printf("[+] pcap = opened\n"); } /* IPv4 address */ ipv4 = rawsock_get_adapter_ip(ifname); if (ipv4 == 0) { printf("[-] source-ipv4 = not found (err)\n"); } else { fmt = ipv4address_fmt(ipv4); printf("[+] source-ipv4 = %s\n", fmt.string); } /* IPv6 address */ ipv6 = rawsock_get_adapter_ipv6(ifname); if (ipv6address_is_zero(ipv6)) { printf("[-] source-ipv6 = not found\n"); } else { fmt = ipv6address_fmt(ipv6); printf("[+] source-ipv6 = [%s]\n", fmt.string); } /* MAC address */ err = rawsock_get_adapter_mac(ifname, source_mac.addr); if (err) { printf("[-] source-mac = not found (err=%d)\n", err); } else { fmt = macaddress_fmt(source_mac); printf("[+] source-mac = %s\n", fmt.string); } switch (adapter->link_type) { case 0: printf("[+] router-ip = implicit\n"); printf("[+] router-mac = implicit\n"); break; default: /* IPv4 router IP address */ err = rawsock_get_default_gateway(ifname, &router_ipv4); if (err) { fprintf(stderr, "[-] router-ip = not found(err=%d)\n", err); } else { fmt = ipv4address_fmt(router_ipv4); printf("[+] router-ip = %s\n", fmt.string); } /* IPv4 router MAC address */ { macaddress_t router_mac = {{0,0,0,0,0,0}}; stack_arp_resolve( adapter, ipv4, source_mac, router_ipv4, &router_mac); if (macaddress_is_zero(router_mac)) { printf("[-] router-mac-ipv4 = not found\n"); } else { fmt = macaddress_fmt(router_mac); printf("[+] router-mac-ipv4 = %s\n", fmt.string); } } /* * IPv6 router MAC address. * If it's not configured, then we need to send a (synchronous) query * to the network in order to discover the location of routers on * the local network */ if (!ipv6address_is_zero(ipv6)) { macaddress_t router_mac = {{0,0,0,0,0,0}}; stack_ndpv6_resolve( adapter, ipv6, source_mac, &router_mac); if (macaddress_is_zero(router_mac)) { printf("[-] router-mac-ipv6 = not found\n"); } else { fmt = macaddress_fmt(router_mac); printf("[+] router-mac-ipv6 = %s\n", fmt.string); } } } rawsock_close_adapter(adapter); return 0; } /*************************************************************************** ***************************************************************************/ int rawsock_selftest() { return 0; } ================================================ FILE: src/rawsock.h ================================================ /* raw sockets stuff */ #ifndef RAWSOCK_H #define RAWSOCK_H #include "massip-addr.h" #include struct Adapter; struct TemplateSet; #include "stack-queue.h" /** * @return * 1 on failure * 0 on success */ int rawsock_selftest(void); int rawsock_selftest_if(const char *ifname); void rawsock_init(void); /** * Does an "open" on the network adapter. What actually happens depends upon * the operating system and drivers that we are using, but usually this just * calls "pcap_open()" * @param adapter_name * The name of the adapter, like "eth0" or "dna1". * @param is_pfring * Whether we should attempt to use the PF_RING driver (Linux-only) * @param is_sendq * Whether we should attempt to use a ring-buffer for sending packets. * Currently Windows-only, but it'll be enabled for Linux soon. Big * performance gains for Windows, but insignificant performance * difference for Linux. * @param is_packet_trace * Whether then Nmap --packet-trace option was set on the command-line * @param is_offline * Whether the --offline parameter was set on the command-line. If so, * then no network adapter will actually be opened. * @return * a fully instantiated network adapter */ struct Adapter * rawsock_init_adapter(const char *adapter_name, unsigned is_pfring, unsigned is_sendq, unsigned is_packet_trace, unsigned is_offline, const char *bpf_filter, unsigned is_vlan, unsigned vlan_id); /** * Print to the command-line the list of available adapters. It's called * when the "--iflist" option is specified on the command-line. */ void rawsock_list_adapters(void); void rawsock_send_probe_ipv4( struct Adapter *adapter, ipv4address ip_them, unsigned port_them, ipv4address ip_me, unsigned port_me, unsigned seqno, unsigned flush, struct TemplateSet *tmplset); void rawsock_send_probe_ipv6( struct Adapter *adapter, ipv6address ip_them, unsigned port_them, ipv6address ip_me, unsigned port_me, unsigned seqno, unsigned flush, struct TemplateSet *tmplset); /** * Queries the operating-system's network-stack in order to discover * the best IPv4 address to use inside our own custom network-stack. */ unsigned rawsock_get_adapter_ip(const char *ifname); /** * Queries the operating-system's network-stack in order to discover * the best IPv6 address to use inside our own custom network-stack. */ ipv6address rawsock_get_adapter_ipv6(const char *ifname); /** * Given the network adapter name, like 'eth0', find the hardware * MAC address. This is needed because we construct raw Ethernet * packets, and need to use the interface's MAC address as the * source address */ int rawsock_get_adapter_mac(const char *ifname, unsigned char *mac); int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4); int rawsock_get_default_interface(char *ifname, size_t sizeof_ifname); const char *rawsock_win_name(const char *ifname); int rawsock_is_adapter_names_equal(const char *lhs, const char *rhs); /** * Transmit any queued (but not yet transmitted) packets. Useful only when * using a high-speed transmit mechanism. Since flushing happens automatically * whenever the transmit queue is full, this is only needed in boundary * cases, like when shutting down. */ void rawsock_flush(struct Adapter *adapter); int rawsock_send_packet( struct Adapter *adapter, const unsigned char *packet, unsigned length, unsigned flush); /** * Called to read the next packet from the network. * @param adapter * The network interface on which to receive packets. * @param length * returns the length of the packet * @param secs * returns the timestamp of the packet as a time_t value (the number * of seconds since Jan 1 1970). * @param usecs * returns part of the timestamp, the number of microseconds since the * start of the current second * @param packet * returns a pointer to the packet that was read from the network. * The contents of this pointer are good until the next call to this * function. * @return * 0 for success, something else for failure * */ int rawsock_recv_packet( struct Adapter *adapter, unsigned *length, unsigned *secs, unsigned *usecs, const unsigned char **packet); /** * Optimization functions to tell the underlying network stack * to not capture the packets we transmit. Most of the time, Ethernet * adapters receive the packets they send, which will cause us a lot * of work requiring us to process the flood of packets we generate. */ void rawsock_ignore_transmits(struct Adapter *adapter, const char *ifname); #endif ================================================ FILE: src/read-service-probes.c ================================================ #include "read-service-probes.h" #include "util-malloc.h" #include "massip-port.h" #include "unusedparm.h" #include #include #include #include #ifdef _MSC_VER #pragma warning(disable:4996) #endif #if defined(WIN32) #define strncasecmp _strnicmp #endif /***************************************************************************** * Translate string name into enumerated type *****************************************************************************/ static enum SvcP_RecordType parse_type(const char *line, size_t *r_offset, size_t line_length) { static const struct { const char *name; size_t length; enum SvcP_RecordType type; } name_to_types[] = { {"exclude", 7, SvcP_Exclude}, {"probe", 5, SvcP_Probe}, {"match", 5, SvcP_Match}, {"softmatch", 9, SvcP_Softmatch}, {"ports", 5, SvcP_Ports}, {"sslports", 8, SvcP_Sslports}, {"totalwaitms", 11, SvcP_Totalwaitms}, {"tcpwrappedms",12, SvcP_Tcpwrappedms}, {"rarity", 6, SvcP_Rarity}, {"fallback", 8, SvcP_Fallback}, {0, SvcP_Unknown} }; size_t i; size_t offset = *r_offset; size_t name_length; size_t name_offset; enum SvcP_RecordType result; /* find length of command name */ name_offset = offset; while (offset < line_length && !isspace(line[offset])) offset++; /* name = all non-space chars until first space */ name_length = offset - name_offset; while (offset < line_length && isspace(line[offset])) offset++; /* trim whitespace after name */ *r_offset = offset; /* Lookup the command name */ for (i=0; name_to_types[i].name; i++) { if (name_length != name_to_types[i].length) continue; if (strncasecmp(line+name_offset, name_to_types[i].name, name_length) == 0) { break; } } result = name_to_types[i].type; /* return the type */ return result; } /***************************************************************************** *****************************************************************************/ static int is_hexchar(int c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return 1; default: return 0; } } /***************************************************************************** *****************************************************************************/ static unsigned hexval(int c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c - '0'; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return c - 'a' + 10; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return c - 'A' + 10; default: return (unsigned)~0; } } /***************************************************************************** *****************************************************************************/ static struct RangeList parse_ports(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: Exclude 53,T:9100,U:30000-40000 ports 21,43,110,113,199,505,540,1248,5432,30444 ports 111,4045,32750-32810,38978 sslports 443 */ unsigned is_error = 0; const char *p; struct RangeList ranges = {0}; UNUSEDPARM(line_length); p = rangelist_parse_ports(&ranges, line + offset, &is_error, 0); if (is_error) { fprintf(stderr, "%s:%u:%u: bad port spec\n", list->filename, list->line_number, (unsigned)(p-line)); rangelist_remove_all(&ranges); } return ranges; } /***************************************************************************** *****************************************************************************/ static unsigned parse_number(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: totalwaitms 6000 tcpwrappedms 3000 rarity 6 */ unsigned number = 0; while (offset < line_length && isdigit(line[offset])) { number = number * 10; number = number + (line[offset] - '0'); offset++; } while (offset < line_length && isspace(line[offset])) offset++; if (offset != line_length) { fprintf(stderr, "%s:%u:%u: unexpected character '%c'\n", list->filename, list->line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); } return number; } /***************************************************************************** *****************************************************************************/ static char * parse_name(const char *line, size_t *r_offset, size_t line_length) { size_t name_offset = *r_offset; size_t name_length; char *result; /* grab all characters until first space */ while (*r_offset < line_length && !isspace(line[*r_offset])) (*r_offset)++; name_length = *r_offset - name_offset; if (name_length == 0) return 0; /* trim trailing white space */ while (*r_offset < line_length && isspace(line[*r_offset])) (*r_offset)++; /* allocate result string */ result = MALLOC(name_length+1); memcpy(result, line + name_offset, name_length+1); result[name_length] = '\0'; return result; } /***************************************************************************** *****************************************************************************/ static struct ServiceProbeFallback * parse_fallback(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: fallback GetRequest,GenericLines */ struct ServiceProbeFallback *result = 0; while (offset < line_length) { size_t name_offset; size_t name_length; struct ServiceProbeFallback *fallback; struct ServiceProbeFallback **r_fallback; /* grab all characters until first space */ name_offset = offset; while (offset < line_length && !isspace(line[offset]) && line[offset] != ',') offset++; name_length = offset - name_offset; while (offset < line_length && (isspace(line[offset]) || line[offset] == ',')) offset++; /* trim trailing whitespace */ if (name_length == 0) { fprintf(stderr, "%s:%u:%u: name too short\n", list->filename, list->line_number, (unsigned)name_offset); break; } /* Allocate a record */ fallback = CALLOC(1, sizeof(*fallback)); fallback->name = MALLOC(name_length+1); memcpy(fallback->name, line+name_offset, name_length+1); fallback->name[name_length] = '\0'; /* append to end of list */ for (r_fallback=&result; *r_fallback; r_fallback = &(*r_fallback)->next) ; fallback->next = *r_fallback; *r_fallback = fallback; } return result; } /***************************************************************************** *****************************************************************************/ static void parse_probe(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: Probe TCP GetRequest q|GET / HTTP/1.0\r\n\r\n| Probe UDP DNSStatusRequest q|\0\0\x10\0\0\0\0\0\0\0\0\0| Probe TCP NULL q|| */ const char *filename = list->filename; unsigned line_number = list->line_number; struct NmapServiceProbe *probe; /* * We have a new 'Probe', so append a blank record to the end of * our list */ probe = CALLOC(1, sizeof(*probe)); if (list->count + 1 >= list->max) { list->max = list->max * 2 + 1; list->list = REALLOCARRAY(list->list, sizeof(list->list[0]), list->max); } list->list[list->count++] = probe; /* * */ if (line_length - offset <= 3) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } if (memcmp(line+offset, "TCP", 3) == 0) probe->protocol = 6; else if (memcmp(line+offset, "UDP", 3) == 0) probe->protocol = 17; else { fprintf(stderr, "%s:%u:%u: unknown protocol\n", filename, line_number, (unsigned)offset); goto parse_error; } offset += 3; if (!isspace(line[offset])) { fprintf(stderr, "%s:%u:%u: unexpected character\n", filename, line_number, (unsigned)offset); goto parse_error; } while (offset < line_length && isspace(line[offset])) offset++; /* * */ probe->name = parse_name(line, &offset, line_length); if (probe->name == 0) { fprintf(stderr, "%s:%u:%u: probename parse error\n", filename, line_number, (unsigned)offset); goto parse_error; } /* * * - must start with a 'q' character * - a delimiter character starts/stop the string, typically '|' * - Traditional C-style escapes work: * \\ \0, \a, \b, \f, \n, \r, \t, \v, and \xXX */ { char delimiter; char *x; size_t x_offset; if (line_length - offset <= 2) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } if (line[offset++] != 'q') { fprintf(stderr, "%s:%u:%u: expected 'q', found '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset-1])?line[offset-1]:'.'); goto parse_error; } /* The next character is a 'delimiter' that starts and stops the next * string of characters, it it usually '|' but may be anything, like '/', * as long as the delimiter itself is not contained inside the string */ delimiter = line[offset++]; /* allocate a buffer at least as long as the remainder of the line. This is * probably too large, but cannot be too small. It's okay if we waste a * few characters. */ x = CALLOC(1, line_length - offset + 1); probe->hellostring = x; /* Grab all the characters until the next delimiter, translating escaped * characters as needed */ x_offset = 0; while (offset < line_length && line[offset] != delimiter) { /* Normal case: unescaped characters */ if (line[offset] != '\\') { x[x_offset++] = line[offset++]; continue; } /* skip escape character '\\' */ offset++; if (offset >= line_length || line[offset] == delimiter) { fprintf(stderr, "%s:%u:%u: premature end of field\n", filename, line_number, (unsigned)offset); goto parse_error; } /* Handled escape sequence */ switch (line[offset++]) { default: fprintf(stderr, "%s:%u: %.*s\n", filename, line_number, (unsigned)line_length, line); fprintf(stderr, "%s:%u:%u: unexpected escape character '%c'\n", filename, line_number, (unsigned)offset-1, isprint(line[offset-1])?line[offset-1]:'.'); goto parse_error; case '\\': x[x_offset++] = '\\'; break; case '0': x[x_offset++] = '\0'; break; case 'a': x[x_offset++] = '\a'; break; case 'b': x[x_offset++] = '\b'; break; case 'f': x[x_offset++] = '\f'; break; case 'n': x[x_offset++] = '\n'; break; case 'r': x[x_offset++] = '\r'; break; case 't': x[x_offset++] = '\t'; break; case 'v': x[x_offset++] = '\v'; break; case 'x': /* make sure at least 2 characters exist in input, either due * to line-length or the delimiter */ if (offset + 2 >= line_length || line[offset+0] == delimiter || line[offset+1] == delimiter) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } /* make sure those two characters are hex digits */ if (!is_hexchar(line[offset+0]) || !is_hexchar(line[offset+1])) { fprintf(stderr, "%s:%u:%u: expected hex, found '%c%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset+1])?line[offset+1]:'.', isprint(line[offset+2])?line[offset+2]:'.' ); goto parse_error; } /* parse those two hex digits */ x[x_offset++] = (char)(hexval(line[offset+0])<< 4 | hexval(line[offset+1])); offset += 2; break; } } probe->hellolength = x_offset; if (offset >= line_length || line[offset] != delimiter) { fprintf(stderr, "%s:%u:%u: missing end delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } //offset++; } return; parse_error: if (probe->name != 0) free(probe->name); if (probe->hellostring != 0) free(probe->hellostring); probe->hellostring = 0; free(probe); list->count--; } /***************************************************************************** *****************************************************************************/ static struct ServiceProbeMatch * parse_match(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: match ftp m/^220.*Welcome to .*Pure-?FTPd (\d\S+\s*)/ p/Pure-FTPd/ v/$1/ cpe:/a:pureftpd:pure-ftpd:$1/ match ssh m/^SSH-([\d.]+)-OpenSSH[_-]([\w.]+)\r?\n/i p/OpenSSH/ v/$2/ i/protocol $1/ cpe:/a:openbsd:openssh:$2/ match mysql m|^\x10\0\0\x01\xff\x13\x04Bad handshake$| p/MySQL/ cpe:/a:mysql:mysql/ match chargen m|@ABCDEFGHIJKLMNOPQRSTUVWXYZ| match uucp m|^login: login: login: $| p/NetBSD uucpd/ o/NetBSD/ cpe:/o:netbsd:netbsd/a match printer m|^([\w-_.]+): lpd: Illegal service request\n$| p/lpd/ h/$1/ match afs m|^[\d\D]{28}\s*(OpenAFS)([\d\.]{3}[^\s\0]*)\0| p/$1/ v/$2/ */ const char *filename = list->filename; unsigned line_number = list->line_number; struct ServiceProbeMatch *match; match = CALLOC(1, sizeof(*match)); /* * */ match->service = parse_name(line, &offset, line_length); if (match->service == 0) { fprintf(stderr, "%s:%u:%u: servicename is empty\n", filename, line_number, (unsigned)offset); goto parse_error; } /* * * - must start with a 'm' character * - a delimiter character starts/stop the string, typically '/' or '|' * - contents are PCRE regex */ { char delimiter; size_t regex_offset; size_t regex_length; /* line must start with 'm' */ if (line_length - offset <= 2) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } if (line[offset] != 'm') { fprintf(stderr, "%s:%u:%u: expected 'm', found '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } offset++; /* next character is the delimiter */ delimiter = line[offset++]; /* Find the length of the regex */ regex_offset = offset; while (offset < line_length && line[offset] != delimiter) offset++; regex_length = offset - regex_offset; if (offset >= line_length || line[offset] != delimiter) { fprintf(stderr, "%s:%u:%u: missing ending delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } else offset++; /* add regex pattern to record */ match->regex_length = regex_length; match->regex = MALLOC(regex_length + 1); memcpy(match->regex, line+regex_offset, regex_length + 1); match->regex[regex_length] = '\0'; /* Verify the regex options characters */ while (offsetis_case_insensitive = 1; break; case 's': match->is_include_newlines = 1; break; default: fprintf(stderr, "%s:%u:%u: unknown regex pattern option '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } offset++; } while (offset * - several optional fields * - each file starts with identifier (p v i h o d cpe:) * - next comes the delimiter character (preferably '/' slash) * - next comes data * - ends with delimiter */ while (offset < line_length) { char id; char delimiter; size_t value_length; size_t value_offset; int is_a = 0; enum SvcV_InfoType type; /* Make sure we have enough characters for a versioninfo string */ if (offset >= line_length) break; if (offset + 2 >= line_length) { fprintf(stderr, "%s:%u:%u: unexpected character at end of line '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } /* grab the 'id' character, which is either singe letter or the string 'cpe:' */ id = line[offset++]; if (id == 'c') { if (offset + 3 >= line_length) { fprintf(stderr, "%s:%u:%u: unexpected character at end of line '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } if (memcmp(line+offset, "pe:", 3) != 0) { fprintf(stderr, "%s:%u:%u: expected string 'cpe:'\n", filename, line_number, (unsigned)offset); goto parse_error; } offset += 3; } switch (id) { case 'p': type = SvcV_ProductName; break; case 'v': type = SvcV_Version; break; case 'i': type = SvcV_Info; break; case 'h': type = SvcV_Hostname; break; case 'o': type = SvcV_OperatingSystem; break; case 'd': type = SvcV_DeviceType; break; case 'c': type = SvcV_CpeName; break; default: fprintf(stderr, "%s:%u:%u: versioninfo unknown identifier '%c'\n", filename, line_number, (unsigned)offset, isprint(id)?id:'.'); goto parse_error; } /* grab the delimiter */ if (offset + 2 >= line_length) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } delimiter = line[offset++]; /* Grab the contents of this string */ value_offset = offset; while (offset < line_length && line[offset] != delimiter) offset++; value_length = offset - value_offset; if (offset >= line_length || line[offset] != delimiter) { fprintf(stderr, "%s:%u:%u: missing ending delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } else offset++; if (id == 'c' && offset + 1 <= line_length && line[offset] == 'a') { is_a = 1; offset++; } if (offset < line_length && !isspace(line[offset])) { fprintf(stderr, "%s:%u:%u: unexpected character after delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } while (offset < line_length && isspace(line[offset])) offset++; /* Create a versioninfo record */ { struct ServiceVersionInfo *v; struct ServiceVersionInfo **r_v; v = CALLOC(1, sizeof(*v)); v->type = type; v->value = MALLOC(value_length + 1); memcpy(v->value, line+value_offset, value_length+1); v->value[value_length] = '\0'; v->is_a = is_a; /* insert at end of list */ for (r_v = &match->versioninfo; *r_v; r_v = &(*r_v)->next) ; v->next = *r_v; *r_v = v; } } return match; parse_error: free(match->regex); free(match->service); while (match->versioninfo) { struct ServiceVersionInfo *v = match->versioninfo; match->versioninfo = v->next; if (v->value) free(v->value); free(v); } free(match); return 0; } /***************************************************************************** *****************************************************************************/ static void parse_line(struct NmapServiceProbeList *list, const char *line) { const char *filename = list->filename; unsigned line_number = list->line_number; size_t line_length; size_t offset; enum SvcP_RecordType type; struct RangeList ranges = {0}; struct NmapServiceProbe *probe; /* trim whitespace */ offset = 0; line_length = strlen(line); while (offset && isspace(line[offset])) offset++; while (line_length && isspace(line[line_length-1])) line_length--; /* Ignore comment lines */ if (ispunct(line[offset])) return; /* Ignore empty lines */ if (offset >= line_length) return; /* parse the type field field */ type = parse_type(line, &offset, line_length); /* parse the remainder of the line, depending upon the type */ switch ((int)type) { case SvcP_Unknown: fprintf(stderr, "%s:%u:%u: unknown type: '%.*s'\n", filename, line_number, (unsigned)offset, (int)offset-0, line); return; case SvcP_Exclude: if (list->count) { /* The 'Exclude' directive is only valid at the top of the file, * before any Probes */ fprintf(stderr, "%s:%u:%u: 'Exclude' directive only valid before any 'Probe'\n", filename, line_number, (unsigned)offset); } else { ranges = parse_ports(list, line, offset, line_length); if (ranges.count == 0) { fprintf(stderr, "%s:%u:%u: 'Exclude' bad format\n", filename, line_number, (unsigned)offset); } else { rangelist_merge(&list->exclude, &ranges); rangelist_remove_all(&ranges); } } return; case SvcP_Probe: /* Creates a new probe record, all the other types (except 'Exclude') operate * on the current probe record */ parse_probe(list, line, offset, line_length); return; } /* * The remaining items only work in the context of the current 'Probe' * directive */ if (list->count == 0) { fprintf(stderr, "%s:%u:%u: 'directive only valid after a 'Probe'\n", filename, line_number, (unsigned)offset); return; } probe = list->list[list->count-1]; switch ((int)type) { case SvcP_Ports: ranges = parse_ports(list, line, offset, line_length); if (ranges.count == 0) { fprintf(stderr, "%s:%u:%u: bad ports format\n", filename, line_number, (unsigned)offset); } else { rangelist_merge(&probe->ports, &ranges); rangelist_remove_all(&ranges); } break; case SvcP_Sslports: ranges = parse_ports(list, line, offset, line_length); if (ranges.count == 0) { fprintf(stderr, "%s:%u:%u: bad ports format\n", filename, line_number, (unsigned)offset); } else { rangelist_merge(&probe->sslports, &ranges); rangelist_remove_all(&ranges); } break; case SvcP_Match: case SvcP_Softmatch: { struct ServiceProbeMatch *match; match = parse_match(list, line, offset, line_length); if (match) { struct ServiceProbeMatch **r_match; /* put at end of list */ for (r_match = &probe->match; *r_match; r_match = &(*r_match)->next) ; match->next = *r_match; *r_match = match; match->is_softmatch = (type == SvcP_Softmatch); } } break; case SvcP_Totalwaitms: probe->totalwaitms = parse_number(list, line, offset, line_length); break; case SvcP_Tcpwrappedms: probe->tcpwrappedms = parse_number(list, line, offset, line_length); break; case SvcP_Rarity: probe->rarity = parse_number(list, line, offset, line_length); break; case SvcP_Fallback: { struct ServiceProbeFallback *fallback; fallback = parse_fallback(list, line, offset, line_length); if (fallback) { fallback->next = probe->fallback; probe->fallback = fallback; } } break; } } /***************************************************************************** *****************************************************************************/ static struct NmapServiceProbeList * nmapserviceprobes_new(const char *filename) { struct NmapServiceProbeList *result; result = CALLOC(1, sizeof(*result)); result->filename = filename; return result; } /***************************************************************************** *****************************************************************************/ struct NmapServiceProbeList * nmapserviceprobes_read_file(const char *filename) { FILE *fp; char line[32768]; struct NmapServiceProbeList *result; /* * Open the file */ fp = fopen(filename, "rt"); if (fp == NULL) { perror(filename); return 0; } /* * Create the result structure */ result = nmapserviceprobes_new(filename); /* * parse all lines in the text file */ while (fgets(line, sizeof(line), fp)) { /* Track line number for error messages */ result->line_number++; /* Parse this string into a record */ parse_line(result, line); } fclose(fp); result->filename = 0; /* name no longer valid after this point */ result->line_number = (unsigned)~0; /* line number no longer valid after this point */ nmapserviceprobes_print(result, stdout); return result; } /***************************************************************************** *****************************************************************************/ static void nmapserviceprobes_free_record(struct NmapServiceProbe *probe) { if (probe->name) free(probe->name); if (probe->hellostring) free(probe->hellostring); rangelist_remove_all(&probe->ports); rangelist_remove_all(&probe->sslports); while (probe->match) { struct ServiceProbeMatch *match = probe->match; probe->match = match->next; free(match->regex); free(match->service); while (match->versioninfo) { struct ServiceVersionInfo *v = match->versioninfo; match->versioninfo = v->next; if (v->value) free(v->value); free(v); } free(match); } while (probe->fallback) { struct ServiceProbeFallback *fallback; fallback = probe->fallback; probe->fallback = fallback->next; if (fallback->name) free(fallback->name); free(fallback); } free(probe); } /***************************************************************************** *****************************************************************************/ static void nmapserviceprobes_print_ports(const struct RangeList *ranges, FILE *fp, const char *prefix, int default_proto) { unsigned i; /* don't print anything if no ports */ if (ranges == NULL || ranges->count == 0) return; /* 'Exclude', 'ports', 'sslports' */ fprintf(fp, "%s ", prefix); /* print all ports */ for (i=0; icount; i++) { int proto; int begin = ranges->list[i].begin; int end = ranges->list[i].end; if (Templ_TCP <= begin && begin < Templ_UDP) proto = Templ_TCP; else if (Templ_UDP <= begin && begin < Templ_SCTP) proto = Templ_UDP; else proto = Templ_SCTP; /* If UDP, shift down */ begin -= proto; end -= proto; /* print comma between ports, but not for first port */ if (i) fprintf(fp, ","); /* Print either one number for a single port, or two numbers for a range */ if (default_proto != proto) { default_proto = proto; switch (proto) { case Templ_TCP: fprintf(fp, "T:"); break; case Templ_UDP: fprintf(fp, "U:"); break; case Templ_SCTP: fprintf(fp, "S"); break; case Templ_ICMP_echo: fprintf(fp, "e"); break; case Templ_ICMP_timestamp: fprintf(fp, "t"); break; case Templ_ARP: fprintf(fp, "A"); break; case Templ_VulnCheck: fprintf(fp, "v"); break; } } fprintf(fp, "%u", begin); if (end > begin) fprintf(fp, "-%u", end); } fprintf(fp, "\n"); } /***************************************************************************** *****************************************************************************/ static int contains_char(const char *string, size_t length, int c) { size_t i; for (i=0; iexclude, fp, "Exclude", ~0); for (i=0; icount; i++) { struct NmapServiceProbe *probe = list->list[i]; struct ServiceProbeMatch *match; /* print the first part of the probe */ fprintf(fp, "Probe %s %s q", (probe->protocol==6)?"TCP":"UDP", probe->name); /* print the query/hello string */ nmapserviceprobes_print_hello(fp, probe->hellostring, probe->hellolength, '|'); fprintf(fp, "\n"); if (probe->rarity) fprintf(fp, "rarity %u\n", probe->rarity); if (probe->totalwaitms) fprintf(fp, "totalwaitms %u\n", probe->totalwaitms); if (probe->tcpwrappedms) fprintf(fp, "tcpwrappedms %u\n", probe->tcpwrappedms); nmapserviceprobes_print_ports(&probe->ports, fp, "ports", (probe->protocol==6)?Templ_TCP:Templ_UDP); nmapserviceprobes_print_ports(&probe->sslports, fp, "sslports", (probe->protocol==6)?Templ_TCP:Templ_UDP); for (match=probe->match; match; match = match->next) { struct ServiceVersionInfo *vi; fprintf(fp, "match %s m", match->service); nmapserviceprobes_print_dstring(fp, match->regex, match->regex_length, '/'); if (match->is_case_insensitive) fprintf(fp, "i"); if (match->is_include_newlines) fprintf(fp, "s"); fprintf(fp, " "); for (vi=match->versioninfo; vi; vi=vi->next) { const char *tag; switch (vi->type) { case SvcV_Unknown: tag = "u"; break; case SvcV_ProductName: tag = "p"; break; case SvcV_Version: tag = "v"; break; case SvcV_Info: tag = "i"; break; case SvcV_Hostname: tag = "h"; break; case SvcV_OperatingSystem: tag = "o"; break; case SvcV_DeviceType: tag = "e"; break; case SvcV_CpeName: tag = "cpe:"; break; default: tag = ""; } fprintf(fp, "%s", tag); nmapserviceprobes_print_dstring(fp, vi->value, strlen(vi->value), '/'); if (vi->is_a) fprintf(fp, "a"); fprintf(fp, " "); } fprintf(fp, "\n"); } } } /***************************************************************************** *****************************************************************************/ void nmapserviceprobes_free(struct NmapServiceProbeList *list) { unsigned i; if (list == NULL) return; for (i=0; list->count; i++) { nmapserviceprobes_free_record(list->list[i]); } if (list->list) free(list->list); free(list); } /***************************************************************************** *****************************************************************************/ int nmapserviceprobes_selftest(void) { const char *lines[] = { "Exclude 53,T:9100,U:30000-40000\n", "Probe UDP DNSStatusRequest q|\\0\\0\\x10\\0\\0\\0\\0\\0\\0\\0\\0\\0|\n", "Probe TCP GetRequest q|GET / HTTP/1.0\r\n\r\n|\n", "ports 80\n", "sslports 443\n", "Probe TCP NULL q||\n", "ports 21,43,110,113,199,505,540,1248,5432,30444\n", "match ftp m/^220.*Welcome to .*Pure-?FTPd (\\d\\S+\\s*)/ p/Pure-FTPd/ v/$1/ cpe:/a:pureftpd:pure-ftpd:$1/\n", "match ssh m/^SSH-([\\d.]+)-OpenSSH[_-]([\\w.]+)\\r?\\n/i p/OpenSSH/ v/$2/ i/protocol $1/ cpe:/a:openbsd:openssh:$2/\n", "match mysql m|^\\x10\\0\\0\\x01\\xff\\x13\\x04Bad handshake$| p/MySQL/ cpe:/a:mysql:mysql/\n", "match chargen m|@ABCDEFGHIJKLMNOPQRSTUVWXYZ|\n", "match uucp m|^login: login: login: $| p/NetBSD uucpd/ o/NetBSD/ cpe:/o:netbsd:netbsd/a\n", "match printer m|^([\\w-_.]+): lpd: Illegal service request\\n$| p/lpd/ h/$1/\n", "match afs m|^[\\d\\D]{28}\\s*(OpenAFS)([\\d\\.]{3}[^\\s\\0]*)\\0| p/$1/ v/$2/\n", 0 }; unsigned i; struct NmapServiceProbeList *list = nmapserviceprobes_new(""); for (i=0; lines[i]; i++) { list->line_number = i; parse_line(list, lines[i]); } //nmapserviceprobes_print(list, stdout); return 0; } ================================================ FILE: src/read-service-probes.h ================================================ /* Reads the 'nmap-service-probes' file. */ #ifndef READ_SERVICE_PROBES_H #define READ_SERVICE_PROBES_H #include #include "massip-rangesv4.h" /* Exclude Probe match [] softmatch ports sslports totalwaitms tcpwrappedms rarity fallback */ enum SvcP_RecordType { SvcP_Unknown, SvcP_Exclude, SvcP_Probe, SvcP_Match, SvcP_Softmatch, SvcP_Ports, SvcP_Sslports, SvcP_Totalwaitms, SvcP_Tcpwrappedms, SvcP_Rarity, SvcP_Fallback, }; enum SvcV_InfoType { SvcV_Unknown, SvcV_ProductName, SvcV_Version, SvcV_Info, SvcV_Hostname, SvcV_OperatingSystem, SvcV_DeviceType, SvcV_CpeName, }; struct ServiceVersionInfo { enum SvcV_InfoType type; char *value; struct ServiceVersionInfo *next; unsigned is_a:1; }; struct ServiceProbeFallback { char *name; struct ServiceProbeFallback *next; }; struct ServiceProbeMatch { struct ServiceProbeMatch *next; char *service; char *regex; size_t regex_length; struct ServiceVersionInfo *versioninfo; unsigned is_case_insensitive:1; unsigned is_include_newlines:1; unsigned is_softmatch:1; }; struct NmapServiceProbe { char *name; char *hellostring; size_t hellolength; unsigned protocol; unsigned totalwaitms; unsigned tcpwrappedms; unsigned rarity; struct RangeList ports; struct RangeList sslports; struct ServiceProbeMatch *match; struct ServiceProbeFallback *fallback; }; struct NmapServiceProbeList { struct NmapServiceProbe **list; struct RangeList exclude; unsigned count; unsigned max; const char *filename; unsigned line_number; }; struct NmapServiceProbeList * nmapserviceprobes_read_file(const char *filename); void nmapserviceprobes_free(struct NmapServiceProbeList *service_probes); int nmapserviceprobes_selftest(void); /** * Print to a file for testing purposes */ void nmapserviceprobes_print(const struct NmapServiceProbeList *list, FILE *fp); #endif ================================================ FILE: src/rte-ring.c ================================================ /* RING DERIVED FROM INTEL DPDK DERIVED FROM FREEBSD BUFRING */ /*- * BSD LICENSE * * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * Derived from FreeBSD's bufring.c * ************************************************************************** * * Copyright (c) 2007,2008 Kip Macy kmacy@freebsd.org * 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. The name of Kip Macy nor the names of other * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ***************************************************************************/ #include "util-safefunc.h" #include #include #include #include #include #include #include #include "pixie-threads.h" #include "pixie-timer.h" #if 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #include "rte-ring.h" /* true if x is a power of 2 */ #define POWEROF2(x) ((((x)-1) & (x)) == 0) /* create the ring */ struct rte_ring * rte_ring_create(unsigned count, unsigned flags) { struct rte_ring *r; size_t ring_size; #if 0 /* compilation-time checks */ RTE_BUILD_BUG_ON((sizeof(struct rte_ring) & CACHE_LINE_MASK) != 0); RTE_BUILD_BUG_ON((offsetof(struct rte_ring, cons) & CACHE_LINE_MASK) != 0); RTE_BUILD_BUG_ON((offsetof(struct rte_ring, prod) & CACHE_LINE_MASK) != 0); #ifdef RTE_LIBRTE_RING_DEBUG RTE_BUILD_BUG_ON((sizeof(struct rte_ring_debug_stats) & CACHE_LINE_MASK) != 0); RTE_BUILD_BUG_ON((offsetof(struct rte_ring, stats) & CACHE_LINE_MASK) != 0); #endif #endif /* count must be a power of 2 */ if ((!POWEROF2(count)) || (count > RTE_RING_SZ_MASK )) { rte_errno = EINVAL; fprintf(stderr, "Requested size is invalid, must be power of 2, and " "do not exceed the size limit %u\n", RTE_RING_SZ_MASK); return NULL; } ring_size = count * sizeof(void *) + sizeof(struct rte_ring); r = (struct rte_ring*)malloc(ring_size); if (r == NULL) abort(); /* init the ring structure */ memset(r, 0, sizeof(*r)); r->flags = flags; r->prod.watermark = count; r->prod.sp_enqueue = !!(flags & RING_F_SP_ENQ); r->cons.sc_dequeue = !!(flags & RING_F_SC_DEQ); r->prod.size = r->cons.size = count; r->prod.mask = r->cons.mask = count-1; r->prod.head = r->cons.head = 0; r->prod.tail = r->cons.tail = 0; return r; } /* * change the high water mark. If *count* is 0, water marking is * disabled */ int rte_ring_set_water_mark(struct rte_ring *r, unsigned count) { if (count >= r->prod.size) return -EINVAL; /* if count is 0, disable the watermarking */ if (count == 0) count = r->prod.size; r->prod.watermark = count; return 0; } /* dump the status of the ring on the console */ void rte_ring_dump(const struct rte_ring *r) { #ifdef RTE_LIBRTE_RING_DEBUG struct rte_ring_debug_stats sum; unsigned lcore_id; #endif printf(" flags=%x\n", r->flags); printf(" size=%u\n", r->prod.size); printf(" ct=%u\n", r->cons.tail); printf(" ch=%u\n", r->cons.head); printf(" pt=%u\n", r->prod.tail); printf(" ph=%u\n", r->prod.head); printf(" used=%u\n", rte_ring_count(r)); printf(" avail=%u\n", rte_ring_free_count(r)); if (r->prod.watermark == r->prod.size) printf(" watermark=0\n"); else printf(" watermark=%u\n", r->prod.watermark); /* sum and dump statistics */ #ifdef RTE_LIBRTE_RING_DEBUG memset(&sum, 0, sizeof(sum)); for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { sum.enq_success_bulk += r->stats[lcore_id].enq_success_bulk; sum.enq_success_objs += r->stats[lcore_id].enq_success_objs; sum.enq_quota_bulk += r->stats[lcore_id].enq_quota_bulk; sum.enq_quota_objs += r->stats[lcore_id].enq_quota_objs; sum.enq_fail_bulk += r->stats[lcore_id].enq_fail_bulk; sum.enq_fail_objs += r->stats[lcore_id].enq_fail_objs; sum.deq_success_bulk += r->stats[lcore_id].deq_success_bulk; sum.deq_success_objs += r->stats[lcore_id].deq_success_objs; sum.deq_fail_bulk += r->stats[lcore_id].deq_fail_bulk; sum.deq_fail_objs += r->stats[lcore_id].deq_fail_objs; } printf(" size=%u\n", r->prod.size); printf(" enq_success_bulk=%"PRIu64"\n", sum.enq_success_bulk); printf(" enq_success_objs=%"PRIu64"\n", sum.enq_success_objs); printf(" enq_quota_bulk=%"PRIu64"\n", sum.enq_quota_bulk); printf(" enq_quota_objs=%"PRIu64"\n", sum.enq_quota_objs); printf(" enq_fail_bulk=%"PRIu64"\n", sum.enq_fail_bulk); printf(" enq_fail_objs=%"PRIu64"\n", sum.enq_fail_objs); printf(" deq_success_bulk=%"PRIu64"\n", sum.deq_success_bulk); printf(" deq_success_objs=%"PRIu64"\n", sum.deq_success_objs); printf(" deq_fail_bulk=%"PRIu64"\n", sum.deq_fail_bulk); printf(" deq_fail_objs=%"PRIu64"\n", sum.deq_fail_objs); #else printf(" no statistics available\n"); #endif } /* dump the status of all rings on the console */ #if 0 void rte_ring_list_dump(void) { const struct rte_ring *mp; struct rte_ring_list *ring_list; /* check that we have an initialised tail queue */ if ((ring_list = RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_RING, rte_ring_list)) == NULL) { rte_errno = E_RTE_NO_TAILQ; return; } rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); TAILQ_FOREACH(mp, ring_list, next) { rte_ring_dump(mp); } rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); } #endif typedef size_t Element; /*************************************************************************** ***************************************************************************/ struct Test { struct rte_ring *ring; unsigned producer_started; unsigned producer_done; unsigned consumer_done; unsigned long long total_count; volatile int not_active; volatile unsigned test_count; } *x_test; /*************************************************************************** ***************************************************************************/ static void test_consumer_thread(void *v) { struct Test *test = (struct Test *)v; struct rte_ring *ring = test->ring; int err; test->total_count = 0; while (!test->not_active) { Element e; err = rte_ring_sc_dequeue(ring, (void**)&e); if (err == 0) test->total_count += e; else { ; } } /* Wait until ring is empty before exiting */ while (!rte_ring_empty(ring)) { Element e; err = rte_ring_sc_dequeue(ring, (void**)&e); if (err == 0) test->total_count += e; else { ; } } test->consumer_done = 1; } /*************************************************************************** ***************************************************************************/ static void test_producer_thread(void *v) { struct Test *test = (struct Test *)v; unsigned i = 1000; struct rte_ring *ring = test->ring; pixie_locked_add_u32(&test->producer_started, 1); while (i) { int err; for (;;) { err = rte_ring_sp_enqueue(ring, (void*)(size_t)i); if (err == 0) break; } i--; } pixie_locked_add_u32(&test->producer_done, 1); } /*************************************************************************** ***************************************************************************/ static uint64_t run_test(struct Test *test) { unsigned i; const unsigned THREADS = 1; memset(test, 0, sizeof(*test)); test->ring = rte_ring_create(16, RING_F_SP_ENQ|RING_F_SC_DEQ); /* Generate producer threads */ for (i=0; iproducer_started < THREADS) pixie_usleep(10); /* Now start consuming */ pixie_begin_thread(test_consumer_thread, 0, test); /* Wait for producer threads to end */ while (test->producer_done < THREADS) pixie_usleep(10); /* Tell consumer thread to end */ test->not_active = 1; /* Wait for consumer thread to end */ while (!test->consumer_done) pixie_usleep(10); return test->total_count; } /*************************************************************************** ***************************************************************************/ int rte_ring_selftest(void) { unsigned i; for (i=0; i<100; i++) { uint64_t result; struct Test test[1]; x_test = test; result = run_test(test); if (result != 500500) { printf("xring: selftest failed with %" PRIu64 "\n", result); return 1; } else ; } return 0; } ================================================ FILE: src/rte-ring.h ================================================ #ifndef _RTE_RING_H_ #define _RTE_RING_H_ /* RING DERIVED FROM INTEL DPDK DERIVED FROM FREEBSD BUFRING */ /*- * BSD LICENSE * * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * Derived from FreeBSD's bufring.h * ************************************************************************** * * Copyright (c) 2007-2009 Kip Macy kmacy@freebsd.org * 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. The name of Kip Macy nor the names of other * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ***************************************************************************/ #include "pixie-threads.h" #include #ifndef ENOBUFS #define ENOBUFS 119 #endif #ifndef EDQUOT #define EDQUOT 122 #endif #if defined(_MSC_VER) #define inline __inline #define unlikely(x) x #define likely(x) x #include #define rte_wmb() _WriteBarrier() #define rte_pause() _mm_pause() #define rte_rmb() _ReadBarrier() #ifndef EDQUOT #define EDQUOT EOVERFLOW #endif #define rte_snprintf snprintf #define PRIu32 "u" #endif /** * @file * RTE Ring * * The Ring Manager is a fixed-size queue, implemented as a table of * pointers. Head and tail pointers are modified atomically, allowing * concurrent access to it. It has the following features: * * - FIFO (First In First Out) * - Maximum size is fixed; the pointers are stored in a table. * - Lockless implementation. * - Multi- or single-consumer dequeue. * - Multi- or single-producer enqueue. * - Bulk dequeue. * - Bulk enqueue. * * Note: the ring implementation is not preemptable. A lcore must not * be interrupted by another task that uses the same ring. * */ #ifdef __cplusplus extern "C" { #endif #include #include #ifndef __rte_cache_aligned #define __rte_cache_aligned #endif #ifndef RTE_MEMZONE_NAMESIZE #define RTE_MEMZONE_NAMESIZE 32 #endif #ifndef CACHE_LINE_SIZE #define CACHE_LINE_SIZE 64 #endif #ifndef CACHE_LINE_MASK #define CACHE_LINE_MASK (CACHE_LINE_SIZE-1) #endif #define rte_errno errno #ifndef likely #define likely(expr) __builtin_expect((expr), !0) #endif #ifndef unlikely #define unlikely(expr) __builtin_expect((expr), 0) #endif #define RTE_BUILD_BUG_ON #define xRTE_BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) enum rte_ring_queue_behavior { RTE_RING_QUEUE_FIXED = 0, /* Enq/Deq a fixed number of items from a ring */ RTE_RING_QUEUE_VARIABLE /* Enq/Deq as many items a possible from ring */ }; /** * An RTE ring structure. * * The producer and the consumer have a head and a tail index. The particularity * of these index is that they are not between 0 and size(ring). These indexes * are between 0 and 2^32, and we mask their value when we access the ring[] * field. Thanks to this assumption, we can do subtractions between 2 index * values in a modulo-32bit base: that's why the overflow of the indexes is not * a problem. */ struct rte_ring { int flags; /**< Flags supplied at creation. */ /** Ring producer status. */ struct prod { uint32_t watermark; /**< Maximum items before EDQUOT. */ uint32_t sp_enqueue; /**< True, if single producer. */ uint32_t size; /**< Size of ring. */ uint32_t mask; /**< Mask (size-1) of ring. */ volatile uint32_t head; /**< Producer head. */ volatile uint32_t tail; /**< Producer tail. */ } prod __rte_cache_aligned; /** Ring consumer status. */ struct cons { uint32_t sc_dequeue; /**< True, if single consumer. */ uint32_t size; /**< Size of the ring. */ uint32_t mask; /**< Mask (size-1) of ring. */ volatile uint32_t head; /**< Consumer head. */ volatile uint32_t tail; /**< Consumer tail. */ } cons __rte_cache_aligned; #ifdef RTE_LIBRTE_RING_DEBUG struct rte_ring_debug_stats stats[RTE_MAX_LCORE]; #endif void * volatile ring[1] \ __rte_cache_aligned; /**< Memory space of ring starts here. */ }; #define RING_F_SP_ENQ 0x0001 /**< The default enqueue is "single-producer". */ #define RING_F_SC_DEQ 0x0002 /**< The default dequeue is "single-consumer". */ #define RTE_RING_QUOT_EXCEED (1 << 31) /**< Quota exceed for burst ops */ #define RTE_RING_SZ_MASK (unsigned)(0x0fffffff) /**< Ring size mask */ /** * @internal When debug is enabled, store ring statistics. * @param r * A pointer to the ring. * @param name * The name of the statistics field to increment in the ring. * @param n * The number to add to the object-oriented statistics. */ #ifdef RTE_LIBRTE_RING_DEBUG #define __RING_STAT_ADD(r, name, n) do { \ unsigned __lcore_id = rte_lcore_id(); \ r->stats[__lcore_id].name##_objs += n; \ r->stats[__lcore_id].name##_bulk += 1; \ } while(0) #else #define __RING_STAT_ADD(r, name, n) #endif /** * Create a new ring named *name* in memory. * * This function uses ``memzone_reserve()`` to allocate memory. Its size is * set to *count*, which must be a power of two. Water marking is * disabled by default. * Note that the real usable ring size is *count-1* instead of * *count*. * * @param count * The size of the ring (must be a power of 2). * @param flags * An OR of the following: * - RING_F_SP_ENQ: If this flag is set, the default behavior when * using ``rte_ring_enqueue()`` or ``rte_ring_enqueue_bulk()`` * is "single-producer". Otherwise, it is "multi-producers". * - RING_F_SC_DEQ: If this flag is set, the default behavior when * using ``rte_ring_dequeue()`` or ``rte_ring_dequeue_bulk()`` * is "single-consumer". Otherwise, it is "multi-consumers". * @return * On success, the pointer to the new allocated ring. NULL on error with * rte_errno set appropriately. Possible errno values include: * - E_RTE_NO_CONFIG - function could not get pointer to rte_config structure * - E_RTE_SECONDARY - function was called from a secondary process instance * - E_RTE_NO_TAILQ - no tailq list could be got for the ring list * - EINVAL - count provided is not a power of 2 * - ENOSPC - the maximum number of memzones has already been allocated * - EEXIST - a memzone with the same name already exists * - ENOMEM - no appropriate memory area found in which to create memzone */ struct rte_ring *rte_ring_create(unsigned count, unsigned flags); /** * Change the high water mark. * * If *count* is 0, water marking is disabled. Otherwise, it is set to the * *count* value. The *count* value must be greater than 0 and less * than the ring size. * * This function can be called at any time (not necessarily at * initialization). * * @param r * A pointer to the ring structure. * @param count * The new water mark value. * @return * - 0: Success; water mark changed. * - -EINVAL: Invalid water mark value. */ int rte_ring_set_water_mark(struct rte_ring *r, unsigned count); /** * Dump the status of the ring to the console. * * @param r * A pointer to the ring structure. */ void rte_ring_dump(const struct rte_ring *r); /** * @internal Enqueue several objects on the ring (multi-producers safe). * * This function uses a "compare and set" instruction to move the * producer index atomically. * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @param behavior * RTE_RING_QUEUE_FIXED: Enqueue a fixed number of items from a ring * RTE_RING_QUEUE_VARIABLE: Enqueue as many items a possible from ring * @return * Depend on the behavior value * if behavior = RTE_RING_QUEUE_FIXED * - 0: Success; objects enqueue. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue, no object is enqueued. * if behavior = RTE_RING_QUEUE_VARIABLE * - n: Actual number of objects enqueued. */ static inline int __rte_ring_mp_do_enqueue(struct rte_ring *r, void * const *obj_table, unsigned n, enum rte_ring_queue_behavior behavior) { uint32_t prod_head, prod_next; uint32_t free_entries; const unsigned max = n; int success; unsigned i; uint32_t mask = r->prod.mask; int ret; /* move prod.head atomically */ do { uint32_t cons_tail; /* Reset n to the initial burst count */ n = max; prod_head = r->prod.head; cons_tail = r->cons.tail; /* The subtraction is done between two unsigned 32bits value * (the result is always modulo 32 bits even if we have * prod_head > cons_tail). So 'free_entries' is always between 0 * and size(ring)-1. */ free_entries = (mask + cons_tail - prod_head); /* check that we have enough room in ring */ if (unlikely(n > free_entries)) { if (behavior == RTE_RING_QUEUE_FIXED) { __RING_STAT_ADD(r, enq_fail, n); return -ENOBUFS; } else { /* No free entry available */ if (unlikely(free_entries == 0)) { __RING_STAT_ADD(r, enq_fail, n); return 0; } n = free_entries; } } prod_next = prod_head + n; success = rte_atomic32_cmpset(&r->prod.head, prod_head, prod_next); } while (unlikely(success == 0)); /* write entries in ring */ for (i = 0; likely(i < n); i++) r->ring[(prod_head + i) & mask] = obj_table[i]; rte_wmb(); /* if we exceed the watermark */ if (unlikely(((mask + 1) - free_entries + n) > r->prod.watermark)) { ret = (behavior == RTE_RING_QUEUE_FIXED) ? -EDQUOT : (int)(n | RTE_RING_QUOT_EXCEED); __RING_STAT_ADD(r, enq_quota, n); } else { ret = (behavior == RTE_RING_QUEUE_FIXED) ? 0 : n; __RING_STAT_ADD(r, enq_success, n); } /* * If there are other enqueues in progress that preceded us, * we need to wait for them to complete */ while (unlikely(r->prod.tail != prod_head)) rte_pause(); r->prod.tail = prod_next; return ret; } /** * @internal Enqueue several objects on a ring (NOT multi-producers safe). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @param behavior * RTE_RING_QUEUE_FIXED: Enqueue a fixed number of items from a ring * RTE_RING_QUEUE_VARIABLE: Enqueue as many items a possible from ring * @return * Depend on the behavior value * if behavior = RTE_RING_QUEUE_FIXED * - 0: Success; objects enqueue. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue, no object is enqueued. * if behavior = RTE_RING_QUEUE_VARIABLE * - n: Actual number of objects enqueued. */ static inline int __rte_ring_sp_do_enqueue(struct rte_ring *r, void * const *obj_table, unsigned n, enum rte_ring_queue_behavior behavior) { uint32_t prod_head, cons_tail; uint32_t prod_next, free_entries; unsigned i; uint32_t mask = r->prod.mask; int ret; prod_head = r->prod.head; cons_tail = r->cons.tail; /* The subtraction is done between two unsigned 32bits value * (the result is always modulo 32 bits even if we have * prod_head > cons_tail). So 'free_entries' is always between 0 * and size(ring)-1. */ free_entries = mask + cons_tail - prod_head; /* check that we have enough room in ring */ if (unlikely(n > free_entries)) { if (behavior == RTE_RING_QUEUE_FIXED) { __RING_STAT_ADD(r, enq_fail, n); return -ENOBUFS; } else { /* No free entry available */ if (unlikely(free_entries == 0)) { __RING_STAT_ADD(r, enq_fail, n); return 0; } n = free_entries; } } prod_next = prod_head + n; r->prod.head = prod_next; /* write entries in ring */ for (i = 0; likely(i < n); i++) r->ring[(prod_head + i) & mask] = obj_table[i]; rte_wmb(); /* if we exceed the watermark */ if (unlikely(((mask + 1) - free_entries + n) > r->prod.watermark)) { ret = (behavior == RTE_RING_QUEUE_FIXED) ? -EDQUOT : (int)(n | RTE_RING_QUOT_EXCEED); __RING_STAT_ADD(r, enq_quota, n); } else { ret = (behavior == RTE_RING_QUEUE_FIXED) ? 0 : n; __RING_STAT_ADD(r, enq_success, n); } r->prod.tail = prod_next; return ret; } /** * @internal Dequeue several objects from a ring (multi-consumers safe). When * the request objects are more than the available objects, only dequeue the * actual number of objects * * This function uses a "compare and set" instruction to move the * consumer index atomically. * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table. * @param behavior * RTE_RING_QUEUE_FIXED: Dequeue a fixed number of items from a ring * RTE_RING_QUEUE_VARIABLE: Dequeue as many items a possible from ring * @return * Depend on the behavior value * if behavior = RTE_RING_QUEUE_FIXED * - 0: Success; objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue; no object is * dequeued. * if behavior = RTE_RING_QUEUE_VARIABLE * - n: Actual number of objects dequeued. */ static inline int __rte_ring_mc_do_dequeue(struct rte_ring *r, void **obj_table, unsigned n, enum rte_ring_queue_behavior behavior) { uint32_t cons_head; uint32_t cons_next; const unsigned max = n; int success; unsigned i; uint32_t mask = r->prod.mask; /* move cons.head atomically */ do { uint32_t prod_tail; uint32_t entries; /* Restore n as it may change every loop */ n = max; cons_head = r->cons.head; prod_tail = r->prod.tail; /* The subtraction is done between two unsigned 32bits value * (the result is always modulo 32 bits even if we have * cons_head > prod_tail). So 'entries' is always between 0 * and size(ring)-1. */ entries = (prod_tail - cons_head); /* Set the actual entries for dequeue */ if (unlikely(n > entries)) { if (behavior == RTE_RING_QUEUE_FIXED) { __RING_STAT_ADD(r, deq_fail, n); return -ENOENT; } else { if (unlikely(entries == 0)){ __RING_STAT_ADD(r, deq_fail, n); return 0; } n = entries; } } cons_next = cons_head + n; success = rte_atomic32_cmpset(&r->cons.head, cons_head, cons_next); } while (unlikely(success == 0)); /* copy in table */ rte_rmb(); for (i = 0; likely(i < n); i++) { obj_table[i] = r->ring[(cons_head + i) & mask]; } /* * If there are other dequeues in progress that preceded us, * we need to wait for them to complete */ while (unlikely(r->cons.tail != cons_head)) rte_pause(); __RING_STAT_ADD(r, deq_success, n); r->cons.tail = cons_next; return behavior == RTE_RING_QUEUE_FIXED ? 0 : n; } /** * @internal Dequeue several objects from a ring (NOT multi-consumers safe). * When the request objects are more than the available objects, only dequeue * the actual number of objects * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table. * @param behavior * RTE_RING_QUEUE_FIXED: Dequeue a fixed number of items from a ring * RTE_RING_QUEUE_VARIABLE: Dequeue as many items a possible from ring * @return * Depend on the behavior value * if behavior = RTE_RING_QUEUE_FIXED * - 0: Success; objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue; no object is * dequeued. * if behavior = RTE_RING_QUEUE_VARIABLE * - n: Actual number of objects dequeued. */ static inline int __rte_ring_sc_do_dequeue(struct rte_ring *r, void **obj_table, unsigned n, enum rte_ring_queue_behavior behavior) { uint32_t cons_head, prod_tail; uint32_t cons_next, entries; unsigned i; uint32_t mask = r->prod.mask; cons_head = r->cons.head; prod_tail = r->prod.tail; /* The subtraction is done between two unsigned 32bits value * (the result is always modulo 32 bits even if we have * cons_head > prod_tail). So 'entries' is always between 0 * and size(ring)-1. */ entries = prod_tail - cons_head; if (unlikely(n > entries)) { if (behavior == RTE_RING_QUEUE_FIXED) { __RING_STAT_ADD(r, deq_fail, n); return -ENOENT; } else { if (unlikely(entries == 0)){ __RING_STAT_ADD(r, deq_fail, n); return 0; } n = entries; } } cons_next = cons_head + n; r->cons.head = cons_next; /* copy in table */ rte_rmb(); for (i = 0; likely(i < n); i++) { /* WTF??? WHY DOES THIS CODE GIVE STRICT-ALIASING WARNINGS * ON SOME GCC. THEY ARE FREAKING VOID* !!! */ obj_table[i] = r->ring[(cons_head + i) & mask]; } __RING_STAT_ADD(r, deq_success, n); r->cons.tail = cons_next; return behavior == RTE_RING_QUEUE_FIXED ? 0 : n; } /** * Enqueue several objects on the ring (multi-producers safe). * * This function uses a "compare and set" instruction to move the * producer index atomically. * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @return * - 0: Success; objects enqueue. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue, no object is enqueued. */ static inline int rte_ring_mp_enqueue_bulk(struct rte_ring *r, void * const *obj_table, unsigned n) { return __rte_ring_mp_do_enqueue(r, obj_table, n, RTE_RING_QUEUE_FIXED); } /** * Enqueue several objects on a ring (NOT multi-producers safe). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @return * - 0: Success; objects enqueued. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue; no object is enqueued. */ static inline int rte_ring_sp_enqueue_bulk(struct rte_ring *r, void * const *obj_table, unsigned n) { return __rte_ring_sp_do_enqueue(r, obj_table, n, RTE_RING_QUEUE_FIXED); } /** * Enqueue several objects on a ring. * * This function calls the multi-producer or the single-producer * version depending on the default behavior that was specified at * ring creation time (see flags). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @return * - 0: Success; objects enqueued. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue; no object is enqueued. */ static inline int rte_ring_enqueue_bulk(struct rte_ring *r, void * const *obj_table, unsigned n) { if (r->prod.sp_enqueue) return rte_ring_sp_enqueue_bulk(r, obj_table, n); else return rte_ring_mp_enqueue_bulk(r, obj_table, n); } /** * Enqueue one object on a ring (multi-producers safe). * * This function uses a "compare and set" instruction to move the * producer index atomically. * * @param r * A pointer to the ring structure. * @param obj * A pointer to the object to be added. * @return * - 0: Success; objects enqueued. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue; no object is enqueued. */ static inline int rte_ring_mp_enqueue(struct rte_ring *r, void *obj) { return rte_ring_mp_enqueue_bulk(r, &obj, 1); } /** * Enqueue one object on a ring (NOT multi-producers safe). * * @param r * A pointer to the ring structure. * @param obj * A pointer to the object to be added. * @return * - 0: Success; objects enqueued. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue; no object is enqueued. */ static inline int rte_ring_sp_enqueue(struct rte_ring *r, void *obj) { return rte_ring_sp_enqueue_bulk(r, &obj, 1); } /** * Enqueue one object on a ring. * * This function calls the multi-producer or the single-producer * version, depending on the default behaviour that was specified at * ring creation time (see flags). * * @param r * A pointer to the ring structure. * @param obj * A pointer to the object to be added. * @return * - 0: Success; objects enqueued. * - -EDQUOT: Quota exceeded. The objects have been enqueued, but the * high water mark is exceeded. * - -ENOBUFS: Not enough room in the ring to enqueue; no object is enqueued. */ static inline int rte_ring_enqueue(struct rte_ring *r, void *obj) { if (r->prod.sp_enqueue) return rte_ring_sp_enqueue(r, obj); else return rte_ring_mp_enqueue(r, obj); } /** * Dequeue several objects from a ring (multi-consumers safe). * * This function uses a "compare and set" instruction to move the * consumer index atomically. * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table. * @return * - 0: Success; objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue; no object is * dequeued. */ static inline int rte_ring_mc_dequeue_bulk(struct rte_ring *r, void **obj_table, unsigned n) { return __rte_ring_mc_do_dequeue(r, obj_table, n, RTE_RING_QUEUE_FIXED); } /** * Dequeue several objects from a ring (NOT multi-consumers safe). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table, * must be strictly positive. * @return * - 0: Success; objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue; no object is * dequeued. */ static inline int rte_ring_sc_dequeue_bulk(struct rte_ring *r, void **obj_table, unsigned n) { return __rte_ring_sc_do_dequeue(r, obj_table, n, RTE_RING_QUEUE_FIXED); } /** * Dequeue several objects from a ring. * * This function calls the multi-consumers or the single-consumer * version, depending on the default behaviour that was specified at * ring creation time (see flags). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table. * @return * - 0: Success; objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue, no object is * dequeued. */ static inline int rte_ring_dequeue_bulk(struct rte_ring *r, void **obj_table, unsigned n) { if (r->cons.sc_dequeue) return rte_ring_sc_dequeue_bulk(r, obj_table, n); else return rte_ring_mc_dequeue_bulk(r, obj_table, n); } /** * Dequeue one object from a ring (multi-consumers safe). * * This function uses a "compare and set" instruction to move the * consumer index atomically. * * @param r * A pointer to the ring structure. * @param obj_p * A pointer to a void * pointer (object) that will be filled. * @return * - 0: Success; objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue; no object is * dequeued. */ static inline int rte_ring_mc_dequeue(struct rte_ring *r, void **obj_p) { return rte_ring_mc_dequeue_bulk(r, obj_p, 1); } /** * Dequeue one object from a ring (NOT multi-consumers safe). * * @param r * A pointer to the ring structure. * @param obj_p * A pointer to a void * pointer (object) that will be filled. * @return * - 0: Success; objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue, no object is * dequeued. */ static inline int rte_ring_sc_dequeue(struct rte_ring *r, void **obj_p) { return rte_ring_sc_dequeue_bulk(r, obj_p, 1); } /** * Dequeue one object from a ring. * * This function calls the multi-consumers or the single-consumer * version depending on the default behaviour that was specified at * ring creation time (see flags). * * @param r * A pointer to the ring structure. * @param obj_p * A pointer to a void * pointer (object) that will be filled. * @return * - 0: Success, objects dequeued. * - -ENOENT: Not enough entries in the ring to dequeue, no object is * dequeued. */ static inline int rte_ring_dequeue(struct rte_ring *r, void **obj_p) { if (r->cons.sc_dequeue) return rte_ring_sc_dequeue(r, obj_p); else return rte_ring_mc_dequeue(r, obj_p); } /** * Test if a ring is full. * * @param r * A pointer to the ring structure. * @return * - 1: The ring is full. * - 0: The ring is not full. */ static inline int rte_ring_full(const struct rte_ring *r) { uint32_t prod_tail = r->prod.tail; uint32_t cons_tail = r->cons.tail; return (((cons_tail - prod_tail - 1) & r->prod.mask) == 0); } /** * Test if a ring is empty. * * @param r * A pointer to the ring structure. * @return * - 1: The ring is empty. * - 0: The ring is not empty. */ static inline int rte_ring_empty(const struct rte_ring *r) { uint32_t prod_tail = r->prod.tail; uint32_t cons_tail = r->cons.tail; return !!(cons_tail == prod_tail); } /** * Return the number of entries in a ring. * * @param r * A pointer to the ring structure. * @return * The number of entries in the ring. */ static inline unsigned rte_ring_count(const struct rte_ring *r) { uint32_t prod_tail = r->prod.tail; uint32_t cons_tail = r->cons.tail; return ((prod_tail - cons_tail) & r->prod.mask); } /** * Return the number of free entries in a ring. * * @param r * A pointer to the ring structure. * @return * The number of free entries in the ring. */ static inline unsigned rte_ring_free_count(const struct rte_ring *r) { uint32_t prod_tail = r->prod.tail; uint32_t cons_tail = r->cons.tail; return ((cons_tail - prod_tail - 1) & r->prod.mask); } /** * Dump the status of all rings on the console */ void rte_ring_list_dump(void); /** * Search a ring from its name * * @param name * The name of the ring. * @return * The pointer to the ring matching the name, or NULL if not found, * with rte_errno set appropriately. Possible rte_errno values include: * - ENOENT - required entry not available to return. */ struct rte_ring *rte_ring_lookup(const char *name); /** * Enqueue several objects on the ring (multi-producers safe). * * This function uses a "compare and set" instruction to move the * producer index atomically. * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @return * - n: Actual number of objects enqueued. */ static inline int rte_ring_mp_enqueue_burst(struct rte_ring *r, void * const *obj_table, unsigned n) { return __rte_ring_mp_do_enqueue(r, obj_table, n, RTE_RING_QUEUE_VARIABLE); } /** * Enqueue several objects on a ring (NOT multi-producers safe). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @return * - n: Actual number of objects enqueued. */ static inline int rte_ring_sp_enqueue_burst(struct rte_ring *r, void * const *obj_table, unsigned n) { return __rte_ring_sp_do_enqueue(r, obj_table, n, RTE_RING_QUEUE_VARIABLE); } /** * Enqueue several objects on a ring. * * This function calls the multi-producer or the single-producer * version depending on the default behavior that was specified at * ring creation time (see flags). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects). * @param n * The number of objects to add in the ring from the obj_table. * @return * - n: Actual number of objects enqueued. */ static inline int rte_ring_enqueue_burst(struct rte_ring *r, void * const *obj_table, unsigned n) { if (r->prod.sp_enqueue) return rte_ring_sp_enqueue_burst(r, obj_table, n); else return rte_ring_mp_enqueue_burst(r, obj_table, n); } /** * Dequeue several objects from a ring (multi-consumers safe). When the request * objects are more than the available objects, only dequeue the actual number * of objects * * This function uses a "compare and set" instruction to move the * consumer index atomically. * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table. * @return * - n: Actual number of objects dequeued, 0 if ring is empty */ static inline int rte_ring_mc_dequeue_burst(struct rte_ring *r, void **obj_table, unsigned n) { return __rte_ring_mc_do_dequeue(r, obj_table, n, RTE_RING_QUEUE_VARIABLE); } /** * Dequeue several objects from a ring (NOT multi-consumers safe).When the * request objects are more than the available objects, only dequeue the * actual number of objects * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table. * @return * - n: Actual number of objects dequeued, 0 if ring is empty */ static inline int rte_ring_sc_dequeue_burst(struct rte_ring *r, void **obj_table, unsigned n) { return __rte_ring_sc_do_dequeue(r, obj_table, n, RTE_RING_QUEUE_VARIABLE); } /** * Dequeue multiple objects from a ring up to a maximum number. * * This function calls the multi-consumers or the single-consumer * version, depending on the default behaviour that was specified at * ring creation time (see flags). * * @param r * A pointer to the ring structure. * @param obj_table * A pointer to a table of void * pointers (objects) that will be filled. * @param n * The number of objects to dequeue from the ring to the obj_table. * @return * - Number of objects dequeued, or a negative error code on error */ static inline int rte_ring_dequeue_burst(struct rte_ring *r, void **obj_table, unsigned n) { if (r->cons.sc_dequeue) return rte_ring_sc_dequeue_burst(r, obj_table, n); else return rte_ring_mc_dequeue_burst(r, obj_table, n); } int rte_ring_selftest(void); #ifdef __cplusplus } #endif #endif /* _RTE_RING_H_ */ ================================================ FILE: src/scripting-banner.c ================================================ /* Using "banner" system for TCP scripting */ #include "scripting.h" #include "stub-lua.h" #include "proto-banner1.h" #include "smack.h" #include "unusedparm.h" #include "util-logger.h" #include "masscan-app.h" #include "output.h" #include "stack-tcp-api.h" #include "proto-preprocess.h" #include "proto-ssl.h" #include "proto-udp.h" #include "syn-cookie.h" #include "unusedparm.h" #include "massip-rangesv4.h" /* kludge: TODO: FIXME: change this */ #include "massip-port.h" #include #include #include /*************************************************************************** ***************************************************************************/ static void scripting_transmit_hello(const struct Banner1 *banner1, struct stack_handle_t *socket) { UNUSEDPARM(banner1); UNUSEDPARM(socket); LOG(0, "SCRIPTING: HELLO\n"); } /*************************************************************************** ***************************************************************************/ static void scripting_tcp_parse( const struct Banner1 *banner1, void *banner1_private, struct StreamState *pstate, const unsigned char *px, size_t length, struct BannerOutput *banout, struct stack_handle_t *socket) { unsigned state = pstate->state; UNUSEDPARM(banner1_private); UNUSEDPARM(banner1); UNUSEDPARM(socket); UNUSEDPARM(banout); UNUSEDPARM(px); UNUSEDPARM(length); pstate->state = state; } /*************************************************************************** ***************************************************************************/ static void register_script_for_port(struct Banner1 *b, int port) { LOG(0, "SCRIPTING: using port %d\n", port); b->payloads.tcp[port] = (const struct ProtocolParserStream *)&banner_scripting; } /*************************************************************************** ***************************************************************************/ static void register_script_for_ports(struct Banner1 *b, const char *value) { struct RangeList ports = {0}; unsigned is_error = 0; unsigned i; rangelist_parse_ports(&ports, value, &is_error, 0); if (is_error) { LOG(0, "SCRIPTING: invalid 'setTcpPorts' range: %s\n", value); exit(1); } for (i=0; ibegin; j<=range->end; j++) { register_script_for_port(b, j); } } } /*************************************************************************** ***************************************************************************/ static void * scripting_banner_init(struct Banner1 *b) { struct lua_State *L = b->L; /* Kludge: this gets called prematurely, without scripting, so * just return */ if (L == NULL) return 0; LOG(0, "SCRIPTING: banner init \n"); /* * Register TCP ports to run on */ lua_getglobal(L, "setTcpPorts"); if (lua_isstring(L, -1)) { register_script_for_ports(b, lua_tostring(L, -1)); } else if (lua_isinteger(L, -1)) { register_script_for_port(b, (int)lua_tointeger(L, -1)); } else if (lua_istable(L, -1)) { lua_Integer n = luaL_len(L, -1); int i; for (i=1; i<=n; i++) { lua_geti(L, -1, i); if (lua_isstring(L, -1)) { register_script_for_ports(b, lua_tostring(L, -1)); } else if (lua_isinteger(L, -1)) { register_script_for_port(b, (int)lua_tointeger(L, -1)); } } } return 0; } /*************************************************************************** ***************************************************************************/ #if 0 static unsigned scripting_udp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, uint64_t entropy ) { return default_udp_parse(out, timestamp, px, length, parsed, entropy); } #endif /**************************************************************************** ****************************************************************************/ #if 0 static unsigned scripting_udp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) { return 0; } #endif /*************************************************************************** ***************************************************************************/ static int scripting_banner_selftest(void) { return 0; } /*************************************************************************** ***************************************************************************/ const struct ProtocolParserStream banner_scripting = { "scripting", 11211, "stats\r\n", 7, 0, scripting_banner_selftest, scripting_banner_init, scripting_tcp_parse, 0, scripting_transmit_hello }; ================================================ FILE: src/scripting-masscan.c ================================================ #include "masscan.h" #include "scripting.h" #include "stub-lua.h" #include "unusedparm.h" #define MASSCAN_CLASS "Masscan Class" struct MasscanWrapper { struct Masscan *masscan; }; /*************************************************************************** * "setconfig" function in Lua. * * Called to set a generic name=value parameter. Any configuration * option that can be set on the command-line, or from within a config * file, can be set via this function. ***************************************************************************/ static int mass_setconfig(struct lua_State *L) { struct MasscanWrapper *wrapper; struct Masscan *masscan; const char *name; const char *value; wrapper = luaL_checkudata(L, 1, MASSCAN_CLASS); masscan = wrapper->masscan; name = luaL_checkstring(L, 2); value = luaL_checkstring(L, 3); masscan_set_parameter(masscan, name, value); return 0; } /*************************************************************************** ***************************************************************************/ static int mass_gc(struct lua_State *L) { //struct MasscanWrapper *wrapper; //struct Masscan *masscan; UNUSEDPARM(L); //wrapper = luaL_checkudata(L, 1, MASSCAN_CLASS); //masscan = wrapper->masscan; /* I'm hot sure what I should do here for shutting down this object, * but I'm registering a garbage collection function anyway */ return 0; } /*************************************************************************** * This function creases the object called "Masscan" in the global * variable space of a Lua script. The script can then interact * with this object in order to setup the scan that it wants to * do. ***************************************************************************/ void scripting_masscan_init(struct Masscan *masscan) { struct MasscanWrapper *wrapper; struct lua_State *L = masscan->scripting.L; static const luaL_Reg my_methods[] = { {"setconfig", mass_setconfig}, {"__gc", mass_gc}, {NULL, NULL} }; /* * Lua: Create a class to wrap a 'socket' */ luaL_newmetatable(L, MASSCAN_CLASS); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_setfuncs(L, my_methods, 0); lua_pop(L, 1); /* Lua: create a wrapper object and push it onto the stack */ wrapper = lua_newuserdata(L, sizeof(*wrapper)); memset(wrapper, 0, sizeof(*wrapper)); wrapper->masscan = masscan; /* Lua: set the class/type */ luaL_setmetatable(L, MASSCAN_CLASS); lua_setglobal(L, "Masscan"); } ================================================ FILE: src/scripting.c ================================================ #include "masscan.h" #include "scripting.h" #include "stub-lua.h" #include "util-logger.h" #include /*************************************************************************** ***************************************************************************/ void scripting_init(struct Masscan *masscan) { int version; struct lua_State *L; const char *filename = masscan->scripting.name; int x; if (filename == 0 || filename[0] == '\0') { LOG(0, "%s: no script specified, use --script option\n", "SCRIPTING"); exit(1); } /* Dynamically link the library */ stublua_init(); /* Test to see if the loading was successful */ version = (int)*lua_version(0); LOG(1, "Lua version = %d\n", version); if (version != 503 && version != 502) { LOG(0, "Unable to load Lua library\n"); exit(1); } /* * Create a Lua VM */ L = luaL_newstate(); luaL_openlibs(L); masscan->scripting.L = L; /* * TODO: Sandbox stuff */ /* We need to do a bunch of sandboxing here to prevent hostile or badly * written scripts from disrupting the system */ /* * Create the Masscan object */ scripting_masscan_init(masscan); /* * Load the script. This will verify the syntax. */ x = luaL_loadfile(L, filename); if (x != LUA_OK) { LOG(0, "%s error loading: %s: %s\n", "SCRIPTING:", filename, lua_tostring(L, -1)); lua_close(L); exit(1); } /* * Lua: Start running the script. At this stage, the "onConnection()" function doesn't * run. Instead, it's registered as a global function to be called later. */ LOG(1, "%s running script: %s\n", "SCRIPTING:", filename); x = lua_pcall(L, 0, 0, 0); if (x != LUA_OK) { LOG(0, "%s error running: %s: %s\n", "SCRIPTING:", filename, lua_tostring(L, -1)); lua_close(L); exit(1); } } ================================================ FILE: src/scripting.h ================================================ /* Masscan scripting subsystem */ #ifndef SCRIPTING_H #define SCRIPTING_H #include "proto-banner1.h" struct Masscan; extern const struct ProtocolParserStream banner_scripting; /** * Load the Lua scripting library and run the initialization * stage of all the specified scripts */ void scripting_init(struct Masscan *masscan); /** * Create the "Masscan" object within the scripting subsystem */ void scripting_masscan_init(struct Masscan *masscan); #endif ================================================ FILE: src/smack.h ================================================ #ifndef _SMACK_H #define _SMACK_H #include #define SMACK_NOT_FOUND ((size_t)(~0)) /** * "Anchor" flags are specified only for patterns that must * match at the start of input, at the end, or both. * These are equivalent to the regex specifiers "^" and "$" * respectively. */ enum { SMACK_ANCHOR_BEGIN = 0x01, SMACK_ANCHOR_END = 0x02, SMACK_SNMP_HACK = 0x04, SMACK_WILDCARDS = 0x08, }; enum { SMACK_CASE_SENSITIVE = 0, SMACK_CASE_INSENSITIVE = 1, }; /** * This is the function that will be called whenever SMACK * finds a pattern. */ typedef int (*FOUND_CALLBACK)(size_t id, int offset, void *data); /** * Create the Aho-Corasick search object. After creation, you can start * adding patterns, but you cannot use it for searching until you've * compiled the patterns. */ struct SMACK * smack_create(const char *name, unsigned nocase); /** * Cleans up and frees an object created with smack_create(). */ void smack_destroy(struct SMACK *smack); /** * Registers a pattern with the search engine. The 'smack' object * must have been created with 'smack_create()', but you must not * have yet called 'smack_compile()' to compile the patterns. The * "id" field can contain a pointer (size_t is 64-bit on 64-bit * systems). */ void smack_add_pattern( struct SMACK * smack, const void * pattern, unsigned pattern_length, size_t id, unsigned flags); /** * You call this function after you have registered all the patterns * using 'smack_add_pattern()' in order to compile the state-machine. * Don't use the state-machine with 'smack_search()' until you have * compiled all the patterns with this function. */ void smack_compile(struct SMACK *smack); /** * Run the state-machine, searching for the compiled patterns within * a block of data/text. This can only be called after "smack_compile()" * has created the state-machine. * * If the input is fragmented, this function can be called repeatedly * for each fragment. Patterns that cross fragments will still be * detected. * * The caller must initialize "*state" to zero "0" before running this * function on the first fragment, but must thereafter leave it * unchanged between fragments. (If the caller resets the *state variable * to zero between each fragment, then patterns that cross fragment * boundaries cannot be detected). */ unsigned smack_search( struct SMACK * smack, const void * px, unsigned length, FOUND_CALLBACK cb_found, void * cb_data, unsigned * state); size_t smack_search_next( struct SMACK * smack, unsigned * state, const void * px, unsigned * offset, unsigned length ); /** * Called to terminate a search (after multiple calls to `smack_search_next()`. * This triggers any patterns that SMACK_ANCHOR_END attribute on them. * If more than one pattern can match at the end of the search text, then * this should be called multiple times until SMACK_NOT_FOUND is * returned. * @return The 'id' field of a pattenr registered with the SMACK_ANCHOR_END * attribute if that was found in the searched buffer, or SMACK_NOT_FOUND. */ size_t smack_search_next_end( struct SMACK * smack, unsigned * state ); #define smack_search_start(state) (*(state)) = 0 /** * If there are multiple matches at the current state, returns the next * one. Otherwise, returns NOT_FOUND. Used with "smack_search_next()". */ size_t smack_next_match( struct SMACK * smack, unsigned * state); /** * Call this after search is done. This is not generally necessary. * It's only purpose is to detect patterns that have the * SMACK_ANCHOR_END flag set. If no pattern has that flag, then * this function will do nothing. */ unsigned smack_search_end( struct SMACK * smack, FOUND_CALLBACK cb_found, void * cb_data, unsigned * state); /** * Runs a regression test on the module to make sure it's compiled * correctly for the current platform. * * @return * zero if regression test succeeds, non-zero on failure */ int smack_selftest(void); int smack_benchmark(void); #endif /*_SMACK_H*/ ================================================ FILE: src/smack1.c ================================================ /**************************************************************************** SMACK1 - an Aho-Corasick search engine This creates a state-machine out of patterns, like a DFA implementation of a regex. This can be used for intrusion-detection "signature" matching to search network traffic for a lot of signatures. This can also be used for protocol parsers, where fields are "matched" using a state-machine rather than buffered and compared. In other words, instead of extracting the "method" from HTTP and later comparing against GET, PUT, POST, etc., we create an Aho-Corasick pattern matcher for those patterns, match all known ones, and otherwise generate an error for unknown methods. In addition to normal and nocase patterns, SMACK also supports "anchored" patterns. This is like the (^) and ($) symbols in regex patterns, although we use flags in this code when adding patterns. THE ALGORITHM The basic engine is TEXTBOOK "Aho-Corasic". I've used macros to capture the flavor of the textbook example pseudo-code. Therefore, you see code that looks like the following: while (queue_has_more_items(queue)) { r = dequeue(queue); for (a=0; a #include #include #include #include #include #ifndef NOBENCHMARK #include "pixie-timer.h" #if defined(_MSC_VER) #include #elif defined(__FreeBSD__) #include #include #if (__ARM_ARCH >= 6 && __ARM_ARCH <= 7) // V6 is the earliest arch that has a standard cyclecount unsigned long long __rdtsc(void) { uint32_t pmccntr; uint32_t pmuseren; uint32_t pmcntenset; // Read the user mode perf monitor counter access permissions. asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren)); if (pmuseren & 1) { // Allows reading perfmon counters for user mode code. asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset)); if (pmcntenset & 0x80000000ul) { // Is it counting? asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr)); // The counter is set up to count every 64th cycle return (unsigned long long)(pmccntr) * 64ULL; } } return 0; } #elif defined(__powerpc64__) unsigned long long __rdtsc(void) { unsigned long long rval; __asm__ __volatile__("mfspr %%r3, 268": "=r" (rval)); return rval; } #elif defined(__aarch64__) #define __rdtsc() 0 #else #define __rdtsc rdtsc #endif #elif defined (__llvm__) #if defined(i386) || defined(__i386__) #include #else #define __rdtsc() 0 #endif #elif defined(__GNUC__) || defined(__llvm__) static __inline__ unsigned long long __rdtsc(void) { #if defined(i386) || defined(__i386__) unsigned long hi = 0, lo = 0; __asm__ __volatile__ ("lfence\n\trdtsc" : "=a"(lo), "=d"(hi)); return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); #else return 0; #endif } #endif #endif /** * By default, the table holds only 64k states using 2-byte * integers. If you want more states, simply change this to * a 4-byte or 8-byte integer */ typedef unsigned short transition_t; /** * These constants represent the anchor-start (^) and anchor-end ($) symbols. * Since we use all 256 combinations of a byte, these values are set to * 0x100 and 0x101, so they don't collide with bytes that might otherwise * be used */ enum { CHAR_ANCHOR_START=256, CHAR_ANCHOR_END=257, }; /** * The alphabet consists of all combinations for a byte PLUS two special * symbols: the anchor-start (^) and anchor-end ($) symbol. */ #define ALPHABET_SIZE (256+2) /** * During the initial phases of compilation, the "FAIL" state points to * an impossible state. Later phases of compilation then figure out a * better fail state (one that shares many of the same trailing * characters of the current prefix). */ #define FAIL (unsigned)(-1) static const unsigned BASE_STATE = 0; static const unsigned UNANCHORED_STATE = 1; #define GOTO(r, a) smack->m_state_table[r].m_next_state[a] #define GOTO_FAIL(r) smack->m_state_table[r].m_fail_state /** * This variable can be used to visualize transitions while debugging */ #ifdef _DEBUG #define DEBUG #endif #ifdef DEBUG unsigned print_transitions = 0; #endif /**************************************************************************** ****************************************************************************/ struct SmackPattern { /** The 'id' is what's reported back when the pattern matches. * This value can be either an integer, or a pointer to some * private data structure */ size_t id; /** This holds a malloc-ed copy of the pattern the caller gave us. * If the engine is running "nocase", then this is converted to * lower case */ unsigned char * pattern; /** The number of characters in the pattern */ unsigned pattern_length; /** Whether this pattern is "anchored" (^) at the start of the * input. This means the pattern will only trigger when it's at the * very start, but not when it's in the middle */ unsigned is_anchor_begin:1; /** Whether this pattern is "anchored" ($) at the END of the * input. This means the pattern will only trigger when it's * the last characters of the input, and the caller also calls * "smack_search_end()" */ unsigned is_anchor_end:1; unsigned is_snmp_hack:1; unsigned is_wildcards:1; }; /**************************************************************************** * This is an INTERMEDIATE structure created and freed during compilation. * It holds the expanded state-table before it is compressed. ****************************************************************************/ struct SmackRow { unsigned m_next_state[ALPHABET_SIZE]; unsigned m_fail_state; }; /**************************************************************************** * This table indicates which states are matches. A state may have more than * one match. If we are debugging this, this table will also have a name * for the state, based on the pattern. ****************************************************************************/ struct SmackMatches { size_t * m_ids; unsigned m_count; #ifdef DEBUG char *DEBUG_name; #endif }; /**************************************************************************** * This is the master structure for the SMACK engine. ****************************************************************************/ struct SMACK { /** * Since a typical application may have multiple instances of this * structure for different pattern sets, we allow the application to * give a helpful name to this table */ char * name; /** * Whether or not this table is case-sensitive or case-insensitive */ unsigned is_nocase:1; /** * Whether one of the patterns contains an anchor (^) at the beginning * of the pattern. If so, we need to add a symbol in our symbol table * for the anchor. */ unsigned is_anchor_begin:1; /** * Whether one of the patterns contains an anchor ($) at the end * of the pattern. If so, we need to add a symbol in our symbol table * for the anchor. */ unsigned is_anchor_end:1; /** * Temporary pattern list. Patterns are added here at the beginning. * However, after the patterns have been compiled, this structure can * be freed. */ struct SmackPattern **m_pattern_list; unsigned m_pattern_count; unsigned m_pattern_max; /** * Temporary place holder for DFA state transitions. After we * compress the table, this is thrown away */ struct SmackRow * m_state_table; unsigned m_state_count; unsigned m_state_max; struct SmackMatches *m_match; unsigned m_match_limit; /** * The opposite of "char_to_symbol", this table takes a symbol and * converts it back to a character. This can be useful in debugging, * but it's real purpose is just during compilation in order to * count the number of characters used in patterns, and consequently, * how wide 'rows' need to be. */ unsigned symbol_to_char[ALPHABET_SIZE]; /** * The "symbol compression dictionary". As we parse incoming bytes, * they are first translated to a symbol. This is useful for two reasons. * The first reason is that this allows us to create "case-insensitive" * pattern-matches, where upper-case is converted to lower-case. The * second reason is that it allows us to compress the table. If only * 32 different characters are used in the patterns, then rows only * have to be 32 symbols wide, instead of 256 characters wide. */ unsigned char char_to_symbol[ALPHABET_SIZE]; /** * The number different characters used in all the patterns we are * looking for. See "char_to_symbol" for more information. */ unsigned symbol_count; /** * This is the row width, log2. Rows are expanded to the nearest * power-of-2. Thus, if we have 26 different characters used in * patterns, then we'll use row sizes of 32 elements. This allows * use to optimize row lookups using the fast "shift" operation * rather than the slow "multiplication" operation. */ unsigned row_shift; /** * This is the final compressed table. It contains one row for each * sub-pattern, and each row is wide enough to hold all the symbols * (must be a power of two) */ transition_t * table; }; /**************************************************************************** * Given a row-width, figure out the nearest power-of-two (16,32,64,128,etc.) * that can hold that row. Return the number of bits needed to left-shift * in order to multiple by that number (shifts are faster than multiply). ****************************************************************************/ static unsigned row_shift_from_symbol_count(unsigned symbol_count) { unsigned row_shift = 1; symbol_count++; while ((unsigned)(1 << row_shift) < symbol_count) row_shift++; return row_shift; } /**************************************************************************** * Creates this engine, use "smack_destroy()" to free all the resources ****************************************************************************/ struct SMACK * smack_create(const char *name, unsigned nocase) { struct SMACK *smack; smack = (struct SMACK *)malloc(sizeof (struct SMACK)); if (smack == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } memset (smack, 0, sizeof (struct SMACK)); smack->is_nocase = nocase; smack->name = (char*)malloc(strlen(name)+1); if (smack->name == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } memcpy(smack->name, name, strlen(name)+1); return smack; } /**************************************************************************** ****************************************************************************/ static void create_intermediate_table(struct SMACK *smack, unsigned size) { struct SmackRow *x; x = (struct SmackRow *)malloc(sizeof(*x) * size); if (x == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } memset(x, 0, sizeof(*x) * size); smack->m_state_table = x; } /**************************************************************************** ****************************************************************************/ static void destroy_intermediate_table(struct SMACK *smack) { if (smack->m_state_table) { free(smack->m_state_table); smack->m_state_table = 0; } } /**************************************************************************** ****************************************************************************/ static void create_matches_table(struct SMACK *smack, unsigned size) { struct SmackMatches *x; x = (struct SmackMatches *)malloc(sizeof(*x) * size); if (x == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } memset(x, 0, sizeof(*x) * size); smack->m_match = x; } /**************************************************************************** ****************************************************************************/ static void destroy_matches_table(struct SMACK *smack) { unsigned i; if (!smack->m_match) return; for (i=0; im_state_count; i++) { struct SmackMatches *match; match = &smack->m_match[i]; if (match->m_count) { free(match->m_ids); } else { assert(match->m_ids == NULL); } #ifdef DEBUG if (match->DEBUG_name) free(match->DEBUG_name); #endif } free(smack->m_match); smack->m_match = 0; } /**************************************************************************** * Destroy the patterns that were registered by the caller. This will be * called during the compilation process to free the memory, since we no * longer need the original patterns during searching. ****************************************************************************/ static void destroy_pattern_table(struct SMACK *smack) { unsigned i; if (!smack->m_pattern_list) return; for (i=0; im_pattern_count; i++) { struct SmackPattern *pat; pat = smack->m_pattern_list[i]; free(pat->pattern); free(pat); } free(smack->m_pattern_list); smack->m_pattern_list = 0; } /**************************************************************************** * Frees everything allocated by "smack_create()", and further allocated * during compilation. ****************************************************************************/ void smack_destroy(struct SMACK *smack) { destroy_intermediate_table(smack); destroy_matches_table(smack); destroy_pattern_table(smack); if (smack->table) free(smack->table); free(smack); } /**************************************************************************** * In the function "smack_copy_matches()", this tests to see if the match * already exists in the old list, in which case it won't copy a duplicate * from the new list. ****************************************************************************/ static unsigned id_already_exists(const size_t *ids, unsigned count, size_t new_id) { unsigned i; for (i=0; im_ids; unsigned old_count = row->m_count; unsigned i; /* Allocate space for both lists */ total_ids = (size_t *)malloc((old_count + new_count)*sizeof(*total_ids)); if (total_ids == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } /* Copy existing matches */ for (i=0; im_ids) { free(row->m_ids); } /* Replace old list with total combined list */ row->m_ids = total_ids; row->m_count = total_count; } /**************************************************************************** * In order to compress the size of the table, and to do case-insensitive * pattern-matches, we convert characters into symbols. We need to create * a new symbol for different type of character in a pattern. Note that this * function will be called with the pseudo-characters representing anchors, * which have a value of 256 and 257. ****************************************************************************/ static unsigned smack_add_symbol(struct SMACK *smack, unsigned c) { unsigned i; unsigned symbol; /* See if we already know the symbol */ for (i=1; i<=smack->symbol_count; i++) { if (smack->symbol_to_char[i] == c) return i; } /* Add the symbol to our list */ smack->symbol_count++; symbol = smack->symbol_count; /* Map it in both directions */ smack->symbol_to_char[symbol] = c; smack->char_to_symbol[c] = (unsigned char)symbol; return symbol; } /**************************************************************************** * Add all the symbols in a pattern. ****************************************************************************/ static void smack_add_symbols(struct SMACK *smack, const unsigned char *pattern, unsigned pattern_length) { unsigned i; /* Add all the bytes in this pattern to the symbol table */ for (i=0; iis_nocase) smack_add_symbol(smack, tolower(pattern[i])); else smack_add_symbol(smack, pattern[i]); } } /**************************************************************************** * Make a copy of the pattern. We need to do this for two reasons. The first * is that the caller may immediately release the memory in the pattern * he gave us. Therefore, we have to allocate our own memory to hold it. * The second is if it's a case-insensitive pattern. In that case, we are * going to normalize it to all lower case. ****************************************************************************/ static unsigned char * make_copy_of_pattern( const unsigned char *pattern, unsigned pattern_length, unsigned is_nocase) { unsigned char *result; /* allocate space */ result = (unsigned char *)malloc(pattern_length+1); if (result == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } /* copy, removing case if necessary */ if (is_nocase) { unsigned i; for (i=0; ipattern_length = pattern_length; pat->is_anchor_begin = ((flags & SMACK_ANCHOR_BEGIN) > 0); pat->is_anchor_end = ((flags & SMACK_ANCHOR_END) > 0); pat->is_snmp_hack = ((flags & SMACK_SNMP_HACK) > 0); pat->is_wildcards = ((flags & SMACK_WILDCARDS) > 0); pat->id = id; pat->pattern = make_copy_of_pattern(pattern, pattern_length, smack->is_nocase); if (pat->is_anchor_begin) smack->is_anchor_begin = 1; if (pat->is_anchor_end) smack->is_anchor_end = 1; /* * Register the symbols used in the pattern. Hopefully, not all 256 * possible combinations will be used, allowing us to shrink the * size of the rows in the final table */ smack_add_symbols(smack, pattern, pattern_length); if (pat->is_snmp_hack) smack_add_symbols(smack, (const unsigned char *)"\x80", 1); /* * Automatically expand the table in order to hold more patterns, * as the caller keeps adding more. */ if (smack->m_pattern_count + 1 >= smack->m_pattern_max) { struct SmackPattern **new_list; unsigned new_max; new_max = smack->m_pattern_max * 2 + 1; new_list = (struct SmackPattern **)malloc(sizeof(*new_list)*new_max); if (new_list == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } if (smack->m_pattern_list) { memcpy( new_list, smack->m_pattern_list, sizeof(*new_list) * smack->m_pattern_count); free(smack->m_pattern_list); } smack->m_pattern_list = new_list; smack->m_pattern_max = new_max; } /* * Put this pattern onto the end of our list */ smack->m_pattern_list[smack->m_pattern_count] = pat; smack->m_pattern_count++; } /**************************************************************************** ****************************************************************************/ #ifdef DEBUG static void DEBUG_set_name(struct SMACK *smack, const void *pattern, unsigned length, unsigned state) { char *name = (char*)malloc(length+1); if (name == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } memcpy(name, pattern, length); name[length] = '\0'; smack->m_match[state].DEBUG_name = name; } #else #define DEBUG_set_name(a,b,c,d); #endif /**************************************************************************** ****************************************************************************/ static void smack_add_prefixes(struct SMACK *smack, struct SmackPattern *pat) { unsigned i; unsigned pattern_length; unsigned char *pattern; int state=0; pattern_length = pat->pattern_length; pattern = pat->pattern; /* * If we anchor at the beginning, then start with that */ if (pat->is_anchor_begin) state = GOTO(state, CHAR_ANCHOR_START); /* * Match the existing prefix patterns. For example, if we add all the * prefixes for "football", then add "foobar", there will already be * prefixes for 'f', 'fo', and 'foo'. We won't start adding new states * until we reach 'foob', 'fooba', and 'foobar'. */ for (i=0; im_state_count++; if (pat->is_snmp_hack) GOTO(state, 0x80) = state; /* snmp_hack, space_hack */ GOTO(state, pattern[i]) = new_state; state = new_state; DEBUG_set_name(smack, pattern, i+1, new_state); } /* * If there is an anchor at the end, then create one more state */ if (pat->is_anchor_end) { unsigned new_state = smack->m_state_count++; GOTO(state, CHAR_ANCHOR_END) = new_state; state = new_state; #ifdef DEBUG DEBUG_set_name(smack, pattern, i+1, new_state); smack->m_match[new_state].DEBUG_name[i] = '$'; #endif } /* * Now mark the final state as a "match" state. */ smack_copy_matches(&smack->m_match[state], &pat->id, 1); } /**************************************************************************** ****************************************************************************/ static void smack_stage0_compile_prefixes(struct SMACK *smack) { unsigned s; unsigned a; /* * Initialize the base-state. */ smack->m_state_count = 1; for (s=0; sm_state_max; s++) { for (a=0; ais_anchor_begin) { unsigned anchor_begin = smack->m_state_count++; GOTO(BASE_STATE, CHAR_ANCHOR_START) = anchor_begin; DEBUG_set_name(smack, "^", 1, anchor_begin); } /* * Split a pattern into its sub patterns and add each of them * to the table. */ for (a=0; a<(int)smack->m_pattern_count; a++) smack_add_prefixes(smack, smack->m_pattern_list[a]); /* Set all failed state transitions to return to the 0'th state */ for (a=0; am_match[GOTO(f,a)].m_count) smack_copy_matches( &smack->m_match[s], smack->m_match[GOTO(f,a)].m_ids, smack->m_match[GOTO(f,a)].m_count ); } } queue_destroy(queue); } /**************************************************************************** ****************************************************************************/ static void smack_stage2_link_fails(struct SMACK * smack) { unsigned a; struct Queue *queue; queue = queue_create(); for (a=0; am_state_count; unsigned column_count; transition_t *table; unsigned char *char_to_symbol = smack->char_to_symbol; /* * Figure out the row-size-shift. Instead of doing a multiply by the * row-width, we expand it out to the nearest pattern of two, and * then use shifts instead of multiplies. */ smack->row_shift = row_shift_from_symbol_count(smack->symbol_count); column_count = 1 << smack->row_shift; /* * Allocate table: * rows*columns */ table = malloc(sizeof(transition_t) * row_count * column_count); if (table == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } memset(table, 0, sizeof(transition_t) * row_count * column_count); for (row=0; rowtable = table; } /**************************************************************************** ****************************************************************************/ static void swap_rows(struct SMACK *smack, unsigned row0, unsigned row1) { struct SmackRow swap; struct SmackMatches swapm; unsigned s; /* Swap the first two states */ memcpy(&swap, &smack->m_state_table[row0], sizeof(swap)); memcpy(&smack->m_state_table[row0], &smack->m_state_table[row1], sizeof(swap)); memcpy(&smack->m_state_table[row1], &swap, sizeof(swap)); /* Swap the 'match' info */ memcpy(&swapm, &smack->m_match[row0], sizeof(swapm)); memcpy(&smack->m_match[row0], &smack->m_match[row1], sizeof(swapm)); memcpy(&smack->m_match[row1], &swapm, sizeof(swapm)); /* Now reset any pointers to the swapped states in existing states */ for (s=0; sm_state_count; s++) { unsigned a; for (a=0; am_state_count; for (;;) { while (start < end && smack->m_match[start].m_count == 0) start++; while (start < end && smack->m_match[end-1].m_count != 0) end--; if (start >= end) break; swap_rows(smack, start, end-1); } smack->m_match_limit = start; } /**************************************************************************** * * KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE * * This function currently only works in a very narrow case, for the SMB * parser, where all the patterns are "anchored" and none overlap with * the SMB patterns. This allows us to modify existing states with * the wildcards, without adding new states. Do do this right we need * to duplicate states in order to track wildcards ****************************************************************************/ static void smack_fixup_wildcards(struct SMACK *smack) { size_t i; for (i=0; im_pattern_count; i++) { size_t j; struct SmackPattern *pat = smack->m_pattern_list[i]; /* skip patterns that aren't wildcards */ if (!pat->is_wildcards) continue; /* find the state leading up to the wilcard * character */ for (j=0; jpattern_length; j++) { unsigned row = 0; unsigned offset = 0; size_t row_size = ((size_t)1 << smack->row_shift); transition_t *table; transition_t next_pattern; transition_t base_state = (smack->is_anchor_begin?1:0); size_t k; /* Skip non-wildcard characters */ if (pat->pattern[j] != '*') continue; /* find the current 'row' */ while (offset < j) smack_search_next(smack, &row, pat->pattern, &offset, (unsigned)j); row = row & 0xFFFFFF; table = smack->table + (row << smack->row_shift); next_pattern = table[smack->char_to_symbol['*']]; for (k=0; kis_anchor_begin) smack_add_symbol(smack, CHAR_ANCHOR_START); if (smack->is_anchor_end) smack_add_symbol(smack, CHAR_ANCHOR_END); if (smack->is_nocase) { for (i='A'; i<='Z'; i++) { smack->char_to_symbol[i] = smack->char_to_symbol[tolower(i)]; } } /* * Calculate the maximum possible number of states. This will be somewhat * larger than the number of states we'll actually use because there can * be overlaps */ smack->m_state_max = 1; for (i=0; i<(int)smack->m_pattern_count; i++) { struct SmackPattern *pat = smack->m_pattern_list[i]; smack->m_state_max += pat->pattern_length; smack->m_state_max += pat->is_anchor_begin; smack->m_state_max += pat->is_anchor_end; } /* * Allocate a state-table that can hold that number of states */ create_intermediate_table(smack, smack->m_state_max); create_matches_table(smack, smack->m_state_max); /* * Go through the various compilation stages */ smack_stage0_compile_prefixes(smack); smack_stage1_generate_fails(smack); smack_stage2_link_fails(smack); /* If we have an anchor pattern, then swap * the first two states. */ if (smack->is_anchor_begin) { swap_rows(smack, BASE_STATE, UNANCHORED_STATE); } /* prettify table for debugging */ smack_stage3_sort(smack); /* * Build the final table we use for evaluation */ smack_stage4_make_final_table(smack); /* * Fixup the wildcard states */ smack_fixup_wildcards(smack); /* * Get rid of the original pattern tables, since we no longer need them. * However, if this is a debug build, keep the tables around to make * debugging easier */ #ifndef DEBUG destroy_pattern_table(smack); destroy_intermediate_table(smack); #endif } /**************************************************************************** * Found! * * We found one or more patterns in the input stream. Go through the list * and notify the caller of "smack_search()" which ones we found. ****************************************************************************/ static unsigned handle_match( struct SMACK * smack, unsigned index, int (*callback_function)(size_t id, int index, void *callback_data), void *callback_data, unsigned state) { unsigned i; struct SmackMatches *match = &smack->m_match[state]; /* * Notify caller of all possible matches. */ for (i=0; im_count; i++) { size_t id = match->m_ids[i]; callback_function(id, index, callback_data); } return match->m_count; } /**************************************************************************** ****************************************************************************/ unsigned smack_search( struct SMACK * smack, const void *v_px, unsigned length, FOUND_CALLBACK cb_found, void *callback_data, unsigned *current_state) { const unsigned char *px = (const unsigned char*)v_px; unsigned row; unsigned i; const unsigned char *char_to_symbol = smack->char_to_symbol; transition_t *table = smack->table; unsigned row_shift = smack->row_shift; unsigned found_count = 0; const struct SmackMatches *match = smack->m_match; /* Get the row. This is encoded as the lower 24-bits of the state * variable */ row = *current_state & 0xFFFFFF; /* 'for all bytes in this block' */ for (i=0; im_match[row].DEBUG_name, c, smack->m_match[*(table + (row<m_match[*(table + (row<= match_limit) break; } *state = row; return px - px_start; } /***************************************************************************** *****************************************************************************/ static size_t inner_match_shift7( const unsigned char *px, size_t length, const unsigned char *char_to_symbol, const transition_t *table, unsigned *state, unsigned match_limit) { const unsigned char *px_start = px; const unsigned char *px_end = px + length; unsigned row = *state; for ( ; px= match_limit) break; } *state = row; return px - px_start; } /***************************************************************************** *****************************************************************************/ size_t smack_search_next( struct SMACK * smack, unsigned * current_state, const void * v_px, unsigned * offset, unsigned length ) { const unsigned char *px = (const unsigned char*)v_px; unsigned row; register size_t i = *offset; const unsigned char *char_to_symbol = smack->char_to_symbol; const transition_t *table = smack->table; register unsigned row_shift = smack->row_shift; const struct SmackMatches *match = smack->m_match; unsigned current_matches = 0; size_t id = (size_t)-1; register unsigned match_limit = smack->m_match_limit; /* Get the row. This is encoded as the lower 24-bits of the state * variable */ row = *current_state & 0xFFFFFF; /* See if there are current matches we are processing */ current_matches = (*current_state)>>24; /* 'for all bytes in this block' */ if (current_matches == 0 /*no previous matches*/) { /*if ((length-i) & 1) i += inner_match(px + i, length - i, char_to_symbol, table, &row, match_limit, row_shift); if (row < match_limit && i < length)*/ switch (row_shift) { case 7: i += inner_match_shift7(px + i, length - i, char_to_symbol, table, &row, match_limit); break; default: i += inner_match(px + i, length - i, char_to_symbol, table, &row, match_limit, row_shift); break; } //printf("*** row=%u, i=%u, limit=%u\n", row, i, match_limit); /* Test to see if we have one (or more) matches, and if so, call * the callback function */ if (match[row].m_count) { i++; /* points to first byte after match */ current_matches = match[row].m_count; } } *offset = (unsigned)i; /* If we broke early because we found a match, return that match */ if (current_matches) { current_matches--; id = match[row].m_ids[current_matches]; } *current_state = row | (current_matches<<24); return id; } /**************************************************************************** ****************************************************************************/ size_t smack_next_match(struct SMACK *smack, unsigned *current_state) { unsigned row, current_matches; size_t id = SMACK_NOT_FOUND; /* split the state variable */ row = *current_state & 0xFFFFFF; current_matches = (*current_state)>>24; /* If we broke early because we found a match, return that match */ if (current_matches) { const struct SmackMatches *match = smack->m_match; id = match[row].m_ids[current_matches-1]; current_matches--; } /* Recombine the state */ *current_state = row | (current_matches<<24); return id; } /**************************************************************************** ****************************************************************************/ unsigned smack_search_end( struct SMACK * smack, FOUND_CALLBACK cb_found, void * callback_data, unsigned * current_state) { unsigned found_count = 0; transition_t *table = smack->table; unsigned row_shift = smack->row_shift; unsigned row = *current_state; const struct SmackMatches *match = smack->m_match; unsigned column = smack->char_to_symbol[CHAR_ANCHOR_END]; /* * This is the same logic as for "smack_search()", except there is * only one byte of input -- the virtual character ($) that represents * the anchor at the end of some patterns. */ row = *(table + (row<table; unsigned row_shift = smack->row_shift; unsigned row = *current_state & 0xFFFFFF; unsigned current_matches = (*current_state)>>24; const struct SmackMatches *match = smack->m_match; unsigned column = smack->char_to_symbol[CHAR_ANCHOR_END]; size_t id = SMACK_NOT_FOUND; /* * We can enumerate more than one matching end patterns. When we * reach the end of that list, return NOT FOUND. */ if (current_matches == 0xFF) { return SMACK_NOT_FOUND; } /* * If we've already returned the first result in our list, * then return the next result. */ if (current_matches) { current_matches -= 1; id = match[row].m_ids[current_matches]; } else { /* * This is the same logic as for "smack_search()", except there is * only one byte of input -- the virtual character ($) that represents * the anchor at the end of some patterns. */ row = *(table + (row<>16 & 0x7fff; } /**************************************************************************** ****************************************************************************/ int smack_benchmark(void) { char *buf; unsigned seed = 0; static unsigned BUF_SIZE = 1024*1024; static uint64_t ITERATIONS = 30; unsigned i; struct SMACK *s; uint64_t start, stop; uint64_t result = 0; uint64_t cycle1, cycle2; printf("-- smack-1 -- \n"); s = smack_create("benchmark1", 1); /* Fill a buffer full of junk */ buf = (char*)malloc(BUF_SIZE); if (buf == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } for (i=0; i #include #include /**************************************************************************** * Build a queue so that we can do a breadth-first enumeration of the * sub-patterns ****************************************************************************/ struct QueueElement { unsigned m_data; struct QueueElement *m_next; }; struct Queue { struct QueueElement *m_head; struct QueueElement *m_tail; }; struct Queue * queue_create(void) { struct Queue *queue; queue = (struct Queue *)malloc(sizeof(*queue)); if (queue == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } memset(queue, 0, sizeof(*queue)); return queue; } void queue_destroy(struct Queue * queue) { if (queue == NULL) return; while (queue_has_more_items(queue)) dequeue(queue); free(queue); } void enqueue(struct Queue *queue, unsigned data) { struct QueueElement *element; element = (struct QueueElement *)malloc(sizeof (struct QueueElement)); if (element == NULL) { fprintf(stderr, "%s: out of memory error\n", "smack"); exit(1); } if (queue->m_head == NULL) { /* If nothing in the queue, initialize the queue with the * first data */ queue->m_head = element; } else { /* Else, add the data to the tail of the queue */ queue->m_tail->m_next = element; } element->m_data = data; element->m_next = NULL; queue->m_tail = element; } unsigned dequeue(struct Queue *queue) { if (queue->m_head == NULL) return 0; else { struct QueueElement *element; unsigned result; element = queue->m_head; result = element->m_data; queue->m_head = element->m_next; free(element); return result; } } unsigned queue_has_more_items(struct Queue * queue) { return queue->m_head != NULL; } ================================================ FILE: src/smackqueue.h ================================================ #ifndef SMACKQUEUE_H #define SMACKQUEUE_H struct Queue * queue_create(void); void queue_destroy(struct Queue *queue); void enqueue(struct Queue *queue, unsigned data); unsigned dequeue(struct Queue *queue); unsigned queue_has_more_items(struct Queue *queue); #endif ================================================ FILE: src/stack-arpv4.c ================================================ /* handle ARP Usage #1: At startup, we make a synchronous request for the local router. We'll wait several seconds for a response, but abort the program if we don't receive a response. Usage #2: While running, we'll need to respond to ARPs. That's because we may be bypassing the stack of the local machine with a "spoofed" IP address. Every so often, the local router may drop it's route entry and re-request our address. */ #include "rawsock.h" #include "rawsock-adapter.h" #include "stack-src.h" #include "stack-arpv4.h" #include "stack-queue.h" #include "util-safefunc.h" #include "util-logger.h" #include "pixie-timer.h" #include "proto-preprocess.h" #include "util-checksum.h" #define VERIFY_REMAINING(n) if (offset+(n) > max) return; /** * A structure representing the information parsed from an incoming * ARP packet. Note: unlike normal programming style, this isn't * overlayed on the incoming ARP header, but instead each field * is parsed one-by-one and converted into this internal structure. */ struct ARP_IncomingRequest { unsigned is_valid; unsigned opcode; unsigned hardware_type; unsigned protocol_type; unsigned hardware_length; unsigned protocol_length; unsigned ip_src; unsigned ip_dst; const unsigned char *mac_src; const unsigned char *mac_dst; }; /**************************************************************************** ****************************************************************************/ static void proto_arp_parse(struct ARP_IncomingRequest *arp, const unsigned char px[], unsigned offset, unsigned max) { /* * parse the header */ VERIFY_REMAINING(8); arp->is_valid = 0; /* not valid yet */ arp->hardware_type = px[offset]<<8 | px[offset+1]; arp->protocol_type = px[offset+2]<<8 | px[offset+3]; arp->hardware_length = px[offset+4]; arp->protocol_length = px[offset+5]; arp->opcode = px[offset+6]<<8 | px[offset+7]; offset += 8; /* We only support IPv4 and Ethernet addresses */ if (arp->protocol_length != 4 && arp->hardware_length != 6) return; if (arp->protocol_type != 0x0800) return; if (arp->hardware_type != 1 && arp->hardware_type != 6) return; /* * parse the addresses */ VERIFY_REMAINING(2 * arp->hardware_length + 2 * arp->protocol_length); arp->mac_src = px+offset; offset += arp->hardware_length; arp->ip_src = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3]; offset += arp->protocol_length; arp->mac_dst = px+offset; offset += arp->hardware_length; arp->ip_dst = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3]; //offset += arp->protocol_length; arp->is_valid = 1; } /**************************************************************************** * Resolve the IP address into a MAC address. Do this synchronously, meaning, * we'll stop and wait for the response. This is done at program startup, * but not during then normal asynchronous operation during the scan. ****************************************************************************/ int stack_arp_resolve(struct Adapter *adapter, ipv4address_t my_ipv4, macaddress_t my_mac_address, ipv4address_t your_ipv4, macaddress_t *your_mac_address) { unsigned char xarp_packet[64]; unsigned char *arp_packet = &xarp_packet[0]; unsigned i; time_t start; unsigned is_arp_notice_given = 0; struct ARP_IncomingRequest response; int is_delay_reported = 0; /* * [KLUDGE] * If this is a VPN connection */ if (stack_if_datalink(adapter) == 12) { memcpy(your_mac_address->addr, "\0\0\0\0\0\2", 6); return 0; /* success */ } memset(&response, 0, sizeof(response)); /* zero out bytes in packet to avoid leaking stuff in the padding * (ARP is 42 byte packet, Ethernet is 60 byte minimum) */ memset(arp_packet, 0, sizeof(xarp_packet)); /* * Create the request packet */ memcpy(arp_packet + 0, "\xFF\xFF\xFF\xFF\xFF\xFF", 6); memcpy(arp_packet + 6, my_mac_address.addr, 6); if (adapter->is_vlan) { memcpy(arp_packet + 12, "\x81\x00", 2); arp_packet[14] = (unsigned char)(adapter->vlan_id>>8); arp_packet[15] = (unsigned char)(adapter->vlan_id&0xFF); arp_packet += 4; } memcpy(arp_packet + 12, "\x08\x06", 2); memcpy(arp_packet + 14, "\x00\x01" /* hardware = Ethernet */ "\x08\x00" /* protocol = IPv4 */ "\x06\x04" /* MAC length = 6, IPv4 length = 4 */ "\x00\x01" /* opcode = request */ , 8); memcpy(arp_packet + 22, my_mac_address.addr, 6); arp_packet[28] = (unsigned char)(my_ipv4 >> 24); arp_packet[29] = (unsigned char)(my_ipv4 >> 16); arp_packet[30] = (unsigned char)(my_ipv4 >> 8); arp_packet[31] = (unsigned char)(my_ipv4 >> 0); memcpy(arp_packet + 32, "\x00\x00\x00\x00\x00\x00", 6); arp_packet[38] = (unsigned char)(your_ipv4 >> 24); arp_packet[39] = (unsigned char)(your_ipv4 >> 16); arp_packet[40] = (unsigned char)(your_ipv4 >> 8); arp_packet[41] = (unsigned char)(your_ipv4 >> 0); /* Kludge: handle VLNA header if it exists. This is probably * the wrong way to handle this. */ if (adapter->is_vlan) arp_packet -= 4; /* * Now loop for a few seconds looking for the response */ rawsock_send_packet(adapter, arp_packet, 60, 1); start = time(0); i = 0; for (;;) { unsigned length; unsigned secs; unsigned usecs; const unsigned char *px; int err; if (time(0) != start) { start = time(0); rawsock_send_packet(adapter, arp_packet, 60, 1); if (i++ >= 10) break; /* timeout */ /* It's taking too long, so notify the user */ if (!is_delay_reported) { ipaddress_formatted_t fmt = ipv4address_fmt(your_ipv4); LOG(0, "[+] resolving router %s with ARP (may take some time)...\n", fmt.string); is_delay_reported = 1; } } /* If we aren't getting a response back to our ARP, then print a * status message */ if (time(0) > start+1 && !is_arp_notice_given) { ipaddress_formatted_t fmt = ipv4address_fmt(your_ipv4); LOG(0, "[+] arping local router %s\n", fmt.string); is_arp_notice_given = 1; } err = rawsock_recv_packet( adapter, &length, &secs, &usecs, &px); if (err != 0) continue; if (adapter->is_vlan && px[17] != 6) continue; if (!adapter->is_vlan && px[13] != 6) continue; /* * Parse the response as an ARP packet */ if (adapter->is_vlan) proto_arp_parse(&response, px, 18, length); else proto_arp_parse(&response, px, 14, length); /* Is this an ARP packet? */ if (!response.is_valid) { LOG(2, "[-] arp: etype=0x%04x, not ARP\n", px[12]*256 + px[13]); continue; } /* Is this an ARP "reply"? */ if (response.opcode != 2) { LOG(2, "[-] arp: opcode=%u, not reply(2)\n", response.opcode); continue; } /* Is this response directed at us? */ if (response.ip_dst != my_ipv4) { LOG(2, "[-] arp: dst=%08x, not my ip 0x%08x\n", response.ip_dst, my_ipv4); continue; } if (memcmp(response.mac_dst, my_mac_address.addr, 6) != 0) continue; /* Is this the droid we are looking for? */ if (response.ip_src != your_ipv4) { ipaddress_formatted_t fmt1 = ipv4address_fmt(response.ip_src); ipaddress_formatted_t fmt2 = ipv4address_fmt(your_ipv4); LOG(2, "[-] arp: target=%s, not desired %s\n", fmt1.string, fmt2.string); continue; } /* * GOT IT! * we've got a valid response, so save the results and * return. */ memcpy(your_mac_address->addr, response.mac_src, 6); { ipaddress_formatted_t fmt1 = ipv4address_fmt(response.ip_src); ipaddress_formatted_t fmt2 = macaddress_fmt(*your_mac_address); LOG(1, "[+] arp: %s == %s\n", fmt1.string, fmt2.string); } return 0; } return 1; } /**************************************************************************** * Handle an incoming ARP request. ****************************************************************************/ int stack_arp_incoming_request( struct stack_t *stack, ipv4address_t my_ip, macaddress_t my_mac, const unsigned char *px, unsigned length) { struct PacketBuffer *response = 0; struct ARP_IncomingRequest request; memset(&request, 0, sizeof(request)); /* Get a buffer for sending the response packet. This thread doesn't * send the packet itself. Instead, it formats a packet, then hands * that packet off to a transmit thread for later transmission. */ response = stack_get_packetbuffer(stack); if (response == NULL) return -1; /* ARP packets are too short, so increase the packet size to * the Ethernet minimum */ response->length = 60; /* Fill the padded area with zeroes to avoid leaking data */ memset(response->px, 0, response->length); /* * Parse the response as an ARP packet */ proto_arp_parse(&request, px, 14, length); /* Is this an ARP packet? */ if (!request.is_valid) { LOG(2, "arp: etype=0x%04x, not ARP\n", px[12]*256 + px[13]); return -1; } /* Is this an ARP "request"? */ if (request.opcode != 1) { LOG(2, "arp: opcode=%u, not request(1)\n", request.opcode); return -1; } /* Is this response directed at us? */ if (request.ip_dst != my_ip) { LOG(2, "arp: dst=%08x, not my ip 0x%08x\n", request.ip_dst, my_ip); return -1; } /* * Create the response packet */ memcpy(response->px + 0, request.mac_src, 6); memcpy(response->px + 6, my_mac.addr, 6); memcpy(response->px + 12, "\x08\x06", 2); memcpy(response->px + 14, "\x00\x01" /* hardware = Ethernet */ "\x08\x00" /* protocol = IPv4 */ "\x06\x04" /* MAC length = 6, IPv4 length = 4 */ "\x00\x02" /* opcode = reply(2) */ , 8); memcpy(response->px + 22, my_mac.addr, 6); response->px[28] = (unsigned char)(my_ip >> 24); response->px[29] = (unsigned char)(my_ip >> 16); response->px[30] = (unsigned char)(my_ip >> 8); response->px[31] = (unsigned char)(my_ip >> 0); memcpy(response->px + 32, request.mac_src, 6); response->px[38] = (unsigned char)(request.ip_src >> 24); response->px[39] = (unsigned char)(request.ip_src >> 16); response->px[40] = (unsigned char)(request.ip_src >> 8); response->px[41] = (unsigned char)(request.ip_src >> 0); /* * Now queue the packet up for transmission */ stack_transmit_packetbuffer(stack, response); return 0; } ================================================ FILE: src/stack-arpv4.h ================================================ #ifndef STACK_ARP_H #define STACK_ARP_H struct Adapter; #include "stack-queue.h" #include "massip-addr.h" /** * Response to an ARP request for our IP address. * * @param my_ip * My IP address * @param my_mac * My Ethernet MAC address that matches this IP address. * @param px * The incoming ARP request * @param length * The length of the incoming ARP request. * @param packet_buffers * Free packet buffers I can use to format the request * @param transmit_queue * I put the formatted response onto this queue for later * transmission by a transmit thread. */ int stack_arp_incoming_request(struct stack_t *stack, ipv4address_t my_ip, macaddress_t my_mac, const unsigned char *px, unsigned length); /** * Send an ARP request in order to resolve an IPv4 address into a * MAC address. Usually done in order to find the local router's * MAC address when given the IPv4 address of the router. */ int stack_arp_resolve(struct Adapter *adapter, ipv4address_t my_ipv4, macaddress_t my_mac_address, ipv4address_t your_ipv4, macaddress_t *your_mac_address); #endif ================================================ FILE: src/stack-if.c ================================================ #include "rawsock.h" #include "rawsock-adapter.h" /*************************************************************************** ***************************************************************************/ int stack_if_datalink(struct Adapter *adapter) { if (adapter->ring) return 1; /* ethernet */ else { return adapter->link_type; } } ================================================ FILE: src/stack-ndpv6.c ================================================ #include "stack-ndpv6.h" #include "proto-preprocess.h" #include "stack-src.h" #include "util-checksum.h" #include "rawsock-adapter.h" #include "rawsock.h" #include "util-logger.h" #include static inline void _append(unsigned char *buf, size_t *r_offset, size_t max, unsigned x) { if (*r_offset >= max) return; buf[(*r_offset)++] = (unsigned char)x; } static inline void _append_bytes(unsigned char *buf, size_t *r_offset, size_t max, const void *v_bytes, size_t len) { const unsigned char *bytes = (const unsigned char *)v_bytes; if (*r_offset + len >= max) return; memcpy(buf + *r_offset, bytes, len); *r_offset += len; } static inline void _append_short(unsigned char *buf, size_t *offset, size_t max, unsigned num) { if (2 > max - *offset) { *offset = max; return; } buf[(*offset)++] = (unsigned char)(num>>8); buf[(*offset)++] = (unsigned char)(num & 0xFF); } static inline unsigned _read_byte(const unsigned char *buf, size_t *offset, size_t max) { if (*offset + 1 < max) { return buf[(*offset)++]; } else return (unsigned)~0; } static inline unsigned _read_short(const unsigned char *buf, size_t *offset, size_t max) { if (*offset + 2 <= max) { unsigned result; result = buf[(*offset)++] << 8; result |= buf[(*offset)++]; return result; } else return (unsigned)~0; } static inline unsigned _read_number(const unsigned char *buf, size_t *offset, size_t max) { if (*offset + 4 <= max) { unsigned result; result = buf[(*offset)++] << 24; result |= buf[(*offset)++] << 16; result |= buf[(*offset)++] << 8; result |= buf[(*offset)++]; return result; } else return (unsigned)~0; } static inline ipv6address_t _read_ipv6(const unsigned char *buf, size_t *offset, size_t max) { ipv6address_t result = {0,0}; if (*offset + 16 <= max) { result = ipv6address_from_bytes(buf + *offset); *offset += 16; } else { *offset = max; } return result; } /** * Handle the IPv6 Neighbor Solicitation request. * This happens after we've transmitted a packet, a response is on * it's way back, and the router needs to give us the response * packet. The router sends us a solicitation, like an ARP request, * to which we must respond. */ int stack_ndpv6_incoming_request(struct stack_t *stack, struct PreprocessedInfo *parsed, const unsigned char *px, size_t length) { struct PacketBuffer *response = 0; size_t offset; size_t remaining; ipaddress target_ip; const unsigned char *target_ip_buf; macaddress_t target_mac = stack->source_mac; unsigned xsum; unsigned char *buf2; static const size_t max = sizeof(response->px); size_t offset_ip = parsed->ip_offset; size_t offset_ip_src = offset_ip + 8; /* offset in packet to the source IPv6 address */ size_t offset_ip_dst = offset_ip + 24; size_t offset_icmpv6 = parsed->transport_offset; /* Verify it's a "Neighbor Solitication" opcode */ if (parsed->opcode != 135) return -1; /* Make sure there's at least a full header */ offset = parsed->transport_offset; remaining = length - offset; if (remaining < 24) return -1; /* Make sure it's looking for our own address */ target_ip_buf = px + offset + 8; target_ip.version = 6; target_ip.ipv6 = ipv6address_from_bytes(target_ip_buf); if (!is_my_ip(stack->src, target_ip)) return -1; /* Print a log message */ { ipv6address_t a = ipv6address_from_bytes(px + offset_ip_src); ipaddress_formatted_t fmt1 = ipv6address_fmt(a); ipaddress_formatted_t fmt2 = ipaddress_fmt(target_ip); LOG(1, "[+] received NDP request from %s for %s\n", fmt1.string, fmt2.string); } /* Get a buffer for sending the response packet. This thread doesn't * send the packet itself. Instead, it formats a packet, then hands * that packet off to a transmit thread for later transmission. */ response = stack_get_packetbuffer(stack); if (response == NULL) return -1; /* Use the request packet as a template for the response */ memcpy(response->px, px, length); buf2 = response->px; /* Set the destination MAC address and destination IPv6 address*/ memcpy(buf2 + 0, px + 6, 6); memcpy(buf2 + offset_ip_dst, px + offset_ip_src, 16); /* Set the source MAC address and source IPv6 address */ memcpy(buf2 + offset_ip_src, target_ip_buf, 16); memcpy(buf2 + 6, target_mac.addr, 6); /* Format the response */ _append(buf2, &offset, max, 136); /* type */ _append(buf2, &offset, max, 0); /* code */ _append(buf2, &offset, max, 0); /*checksum[hi] */ _append(buf2, &offset, max, 0); /*checksum[lo] */ _append(buf2, &offset, max, 0x60); /* flags*/ _append(buf2, &offset, max, 0); _append(buf2, &offset, max, 0); _append(buf2, &offset, max, 0); _append_bytes(buf2, &offset, max, target_ip_buf, 16); _append(buf2, &offset, max, 2); _append(buf2, &offset, max, 1); _append_bytes(buf2, &offset, max, target_mac.addr, 6); xsum = checksum_ipv6( buf2 + offset_ip_src, buf2 + offset_ip_dst, 58, offset - offset_icmpv6, buf2 +offset_icmpv6); buf2[offset_icmpv6 + 2] = (unsigned char)(xsum >> 8); buf2[offset_icmpv6 + 3] = (unsigned char)(xsum >> 0); /* Transmit the packet-buffer */ response->length = offset; stack_transmit_packetbuffer(stack, response); return 0; } static int _extract_router_advertisement( const unsigned char *buf, size_t length, struct PreprocessedInfo *parsed, ipv6address my_ipv6, ipv6address *router_ip, macaddress_t *router_mac) { size_t offset; int is_same_prefix = 1; int is_mac_explicit = 0; if (parsed->ip_version != 6) return 1; *router_ip = parsed->src_ip.ipv6; if (parsed->ip_protocol != 58) return 1; offset = parsed->transport_offset; /* type = Router Advertisement */ if (_read_byte(buf, &offset, length) != 134) return 1; /* code = 0 */ if (_read_byte(buf, &offset, length) != 0) return 1; /* checksum */ _read_short(buf, &offset, length); /* hop limit */ _read_byte(buf, &offset, length); /* flags */ _read_byte(buf, &offset, length); /* router life time */ _read_short(buf, &offset, length); /* reachable time */ _read_number(buf, &offset, length); /* retrans timer */ _read_number(buf, &offset, length); while (offset + 8 <= length) { unsigned type = buf[offset + 0]; size_t len2 = buf[offset + 1] * 8; size_t off2 = 2; const unsigned char *buf2 = buf + offset; switch (type) { case 3: /* prefix info */ { unsigned prefix_len; ipv6address prefix; ipaddress_formatted_t fmt; prefix_len = _read_byte(buf2, &off2, len2); _read_byte(buf2, &off2, len2); /* flags */ _read_number(buf2, &off2, len2); /* valid lifetime */ _read_number(buf2, &off2, len2); /* preferred lifetime */ _read_number(buf2, &off2, len2); /* reserved */ prefix = _read_ipv6(buf2, &off2, len2); fmt = ipv6address_fmt(prefix); LOG(1, "[+] IPv6.prefix = %s/%u\n", fmt.string, prefix_len); if (ipv6address_is_equal_prefixed(my_ipv6, prefix, prefix_len)) { is_same_prefix = 1; } else { ipaddress_formatted_t fmt1 = ipv6address_fmt(my_ipv6); ipaddress_formatted_t fmt2 = ipv6address_fmt(prefix); LOG(0, "[-] WARNING: our source-ip is %s, but router prefix announces %s/%u\n", fmt1.string, fmt2.string, prefix_len); is_same_prefix = 0; } } break; case 25: /* recursive DNS server */ _read_short(buf2, &off2, len2); _read_number(buf2, &off2, len2); while (off2 + 16 <= len2) { ipv6address resolver = _read_ipv6(buf2, &off2, len2); ipaddress_formatted_t fmt = ipv6address_fmt(resolver); LOG(1, "[+] IPv6.DNS = %s\n", fmt.string); } break; case 1: if (len2 == 8) { memcpy(router_mac->addr, buf2 + 2, 6); is_mac_explicit = 1; } break; } offset += len2; } if (!is_mac_explicit) { /* The router advertisement didn't include an explicit * source address. Therefore, pull the response from * the Ethernet header of the packet instead */ memcpy(router_mac->addr, parsed->mac_src, 6); } if (!is_same_prefix) { /* We had a valid router advertisement, but it didn't * match the IPv6 address we are using. This presumably * means there are multiple possible IPv6 routers on the * network. Therefore, we are going to discard this * packet and wait for another one */ return 1; } return 0; } /**************************************************************************** ****************************************************************************/ int stack_ndpv6_resolve(struct Adapter *adapter, ipv6address my_ipv6, macaddress_t my_mac_address, macaddress_t *router_mac) { unsigned char buf[128]; size_t max = sizeof(buf); size_t offset = 0; unsigned i; time_t start; unsigned is_arp_notice_given = 0; int is_delay_reported = 0; size_t offset_ip; size_t offset_ip_src; size_t offset_ip_dst; size_t offset_icmpv6; unsigned xsum; struct PreprocessedInfo parsed = {0}; /* * [KLUDGE] * If this is a VPN connection, then there is no answer */ if (stack_if_datalink(adapter) == 12) { memcpy(router_mac->addr, "\0\0\0\0\0\2", 6); return 0; /* success */ } /* * Ethernet header */ _append_bytes(buf, &offset, max, "\x33\x33\x00\x00\x00\x02", 6); _append_bytes(buf, &offset, max, my_mac_address.addr, 6); if (adapter->is_vlan) { _append_short(buf, &offset, max, 0x8100); _append_short(buf, &offset, max, adapter->vlan_id); } _append_short(buf, &offset, max, 0x86dd); /* * Create IPv6 header */ offset_ip = offset; _append(buf, &offset, max, 0x60); /* version = 6 */ _append(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); /* length = 0 */ _append(buf, &offset, max, 58); /* proto = ICMPv6 */ _append(buf, &offset, max, 255); /*hop limit = 255 */ /* Link local source address based on MAC address */ offset_ip_src = offset; _append_short(buf, &offset, max, 0xfe80); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_bytes(buf, &offset, max, my_mac_address.addr, 3); buf[offset-3] |= 2; _append_short(buf, &offset, max, 0xfffe); _append_bytes(buf, &offset, max, my_mac_address.addr+3, 3); /* All-routers link local address */ offset_ip_dst = offset; _append_short(buf, &offset, max, 0xff02); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); _append_short(buf, &offset, max, 2); /* ICMPv6 Router Solicitation */ offset_icmpv6 = offset; _append(buf, &offset, max, 133); /* type = Router Solicitation */ _append(buf, &offset, max, 0); _append_short(buf, &offset, max, 0); /* checksum = 0 (for the moment) */ _append_short(buf, &offset, max, 0); /* reserved */ _append_short(buf, &offset, max, 0); /* reserved */ _append(buf, &offset, max, 1); /* option = source link layer address */ _append(buf, &offset, max, 1); /* length = 2 + 6 / 8*/ _append_bytes(buf, &offset, max, my_mac_address.addr, 6); buf[offset_ip + 4] = (unsigned char)( (offset - offset_icmpv6) >> 8); buf[offset_ip + 5] = (unsigned char)( (offset - offset_icmpv6) & 0xFF); xsum = checksum_ipv6( buf + offset_ip_src, buf + offset_ip_dst, 58, offset - offset_icmpv6, buf + offset_icmpv6); buf[offset_icmpv6 + 2] = (unsigned char)(xsum >> 8); buf[offset_icmpv6 + 3] = (unsigned char)(xsum >> 0); rawsock_send_packet(adapter, buf, (unsigned)offset, 1); /* * Send a shorter version after the long version. I don't know * why, but some do this on the Internet. */ offset -= 8; buf[offset_ip + 4] = (unsigned char)( (offset - offset_icmpv6) >> 8); buf[offset_ip + 5] = (unsigned char)( (offset - offset_icmpv6) & 0xFF); xsum = checksum_ipv6( buf + offset_ip_src, buf + offset_ip_dst, 58, offset - offset_icmpv6, buf + offset_icmpv6); buf[offset_icmpv6 + 2] = (unsigned char)(xsum >> 8); buf[offset_icmpv6 + 3] = (unsigned char)(xsum >> 0); rawsock_send_packet(adapter, buf, (unsigned)offset, 1); start = time(0); i = 0; for (;;) { unsigned length2; unsigned secs; unsigned usecs; const unsigned char *buf2; int err; ipv6address router_ip; /* Resend every so often */ if (time(0) != start) { start = time(0); rawsock_send_packet(adapter, buf, (unsigned)offset, 1); if (i++ >= 10) break; /* timeout */ /* It's taking too long, so notify the user */ if (!is_delay_reported) { fprintf(stderr, "[ ] resolving IPv6 router MAC address (may take some time)...\n"); is_delay_reported = 1; } } /* If we aren't getting a response back to our ARP, then print a * status message */ if (time(0) > start+1 && !is_arp_notice_given) { fprintf(stderr, "[ ] resolving local IPv6 router\n"); is_arp_notice_given = 1; } err = rawsock_recv_packet( adapter, &length2, &secs, &usecs, &buf2); if (err != 0) continue; /* * Parse the packet. We'll get lots of packets we aren't interested * in,so we'll just loop around and keep searching until we find * one. */ err = preprocess_frame(buf2, length2, 1, &parsed); if (err != 1) continue; if (parsed.found != FOUND_NDPv6) continue; /* We've found a packet that may be the one we want, so parse it */ err = _extract_router_advertisement(buf2, length2, &parsed, my_ipv6, &router_ip, router_mac); if (err) continue; /* The previous call found 'router_mac", so now return */ return 0; } return 1; } ================================================ FILE: src/stack-ndpv6.h ================================================ /* IPv6 Neighbor Discovery Protocol This module is needed to talk to the local IPv6 router. It does two things: 1. find the local router, so that we can send packets to it 2. response to Neighbor Discovery Requests, to the router can find us */ #ifndef STACK_NDPV6_H #define STACK_NDPV6_H #include #include #include "stack-queue.h" #include "massip-addr.h" struct PreprocessedInfo; /** * Handle an incoming IPv6 neighbor notification request. We must send * back our MAC address. */ int stack_ndpv6_incoming_request(struct stack_t *stack, struct PreprocessedInfo *parsed, const unsigned char *px, size_t length); /** * Find the MAC address for the local router. */ int stack_ndpv6_resolve(struct Adapter *adapter, ipv6address my_ipv6, macaddress_t my_mac_address, macaddress_t *your_mac_address); #endif ================================================ FILE: src/stack-queue.c ================================================ #include "stack-queue.h" #include "pixie-timer.h" #include "rawsock.h" #include "util-malloc.h" #include #include struct PacketBuffer * stack_get_packetbuffer(struct stack_t *stack) { int err; struct PacketBuffer *response = NULL; for (err=1; err; ) { err = rte_ring_sc_dequeue(stack->packet_buffers, (void**)&response); if (err != 0) { /* Pause and wait for a buffer to become available */ pixie_usleep(1000); } } return response; } void stack_transmit_packetbuffer(struct stack_t *stack, struct PacketBuffer *response) { int err; for (err=1; err; ) { err = rte_ring_sp_enqueue(stack->transmit_queue, response); if (err) { fprintf(stderr, "[-] transmit queue full (should be impossible)\n"); pixie_usleep(1000); } } } /*************************************************************************** * The receive thread doesn't transmit packets. Instead, it queues them * up on the transmit thread. Every so often, the transmit thread needs * to flush this transmit queue and send everything. * * This is an inherent design issue trying to send things as batches rather * than individually. It increases latency, but increases performance. We * don't really care about latency. ***************************************************************************/ void stack_flush_packets( struct stack_t *stack, struct Adapter *adapter, uint64_t *packets_sent, uint64_t *batchsize) { /* * Send a batch of queued packets */ for ( ; (*batchsize); (*batchsize)--) { int err; struct PacketBuffer *p; /* * Get the next packet from the transmit queue. This packet was * put there by a receive thread, and will contain things like * an ACK or an HTTP request */ err = rte_ring_sc_dequeue(stack->transmit_queue, (void**)&p); if (err) { break; /* queue is empty, nothing to send */ } /* * Actually send the packet */ rawsock_send_packet(adapter, p->px, (unsigned)p->length, 1); /* * Now that we are done with the packet, put it on the free list * of buffers that the transmit thread can reuse */ for (err=1; err; ) { err = rte_ring_sp_enqueue(stack->packet_buffers, p); if (err) { fprintf(stderr, "[-] transmit queue full (should be impossible)\n"); pixie_usleep(10000); } } /* * Remember that we sent a packet, which will be used in * throttling. */ (*packets_sent)++; } } struct stack_t * stack_create(macaddress_t source_mac, struct stack_src_t *src) { struct stack_t *stack; size_t i; stack = CALLOC(1, sizeof(*stack)); stack->source_mac = source_mac; stack->src = src; /* * Allocate packet buffers for sending */ #define BUFFER_COUNT 16384 stack->packet_buffers = rte_ring_create(BUFFER_COUNT, RING_F_SP_ENQ|RING_F_SC_DEQ); stack->transmit_queue = rte_ring_create(BUFFER_COUNT, RING_F_SP_ENQ|RING_F_SC_DEQ); for (i=0; ipacket_buffers, p); if (err) { /* I dunno why but I can't queue all 256 packets, just 255 */ fprintf(stderr, "[-] packet_buffers: enqueue: error %d\n", err); } } return stack; } ================================================ FILE: src/stack-queue.h ================================================ #ifndef PACKET_QUEUE_H #define PACKET_QUEUE_H #include "rte-ring.h" #include "massip-addr.h" #include struct stack_src_t; struct Adapter; typedef struct rte_ring PACKET_QUEUE; struct PacketBuffer { size_t length; unsigned char px[2040]; }; struct stack_t { PACKET_QUEUE *packet_buffers; PACKET_QUEUE *transmit_queue; macaddress_t source_mac; struct stack_src_t *src; }; /** * Get a packet-buffer that we can use to create a packet before * sending */ struct PacketBuffer * stack_get_packetbuffer(struct stack_t *stack); /** * Queue up the packet for sending. This doesn't send the packet immediately, * but puts it into a queue to be sent later, when the throttler allows it * to be sent. */ void stack_transmit_packetbuffer(struct stack_t *stack, struct PacketBuffer *response); void stack_flush_packets( struct stack_t *stack, struct Adapter *adapter, uint64_t *packets_sent, uint64_t *batchsize); struct stack_t * stack_create(macaddress_t source_mac, struct stack_src_t *src); #endif ================================================ FILE: src/stack-src.c ================================================ #include "stack-src.h" int is_myself(const struct stack_src_t *src, ipaddress ip, unsigned port) { return is_my_ip(src, ip) && is_my_port(src, port); } int is_my_ip(const struct stack_src_t *src, ipaddress ip) { switch (ip.version) { case 4: return src->ipv4.first <= ip.ipv4 && ip.ipv4 <= src->ipv4.last; case 6: return src->ipv6.first.hi == ip.ipv6.hi && src->ipv6.first.lo == ip.ipv6.lo; default: return 0; } } int is_my_port(const struct stack_src_t *src, unsigned port) { return src->port.first <= port && port <= src->port.last; } ================================================ FILE: src/stack-src.h ================================================ #ifndef STACK_SOURCE_H #define STACK_SOURCE_H #include "massip-addr.h" /** * These the source IP addresses that we'll be spoofing. IP addresses * and port numbers come from this list. */ struct stack_src_t { struct { unsigned first; unsigned last; unsigned range; } ipv4; struct { unsigned first; unsigned last; unsigned range; } port; struct { ipv6address first; ipv6address last; unsigned range; } ipv6; }; int is_myself(const struct stack_src_t *src, ipaddress ip, unsigned port); int is_my_ip(const struct stack_src_t *src, ipaddress ip); int is_my_port(const struct stack_src_t *src, unsigned ip); #endif ================================================ FILE: src/stack-tcp-api.h ================================================ #ifndef STACK_HANDLE_H #define STACK_HANDLE_H #include #include "util-bool.h" /* */ struct ProtocolParserStream; enum TCP__flags { TCP__static,/* it's static data, so the send function can point to it */ TCP__copy, /* the send function must copy the data */ TCP__adopt, /* the buffer was just allocated, so the send function can adopt the pointer */ TCP__close_fin /* close connection */ }; enum { SOCKERR_NONE=0, /* no error */ SOCKERR_EBADF=10, /* bad socket descriptor */ }; typedef struct stack_handle_t { void *tcpcon; void *tcb; unsigned secs; unsigned usecs; } stack_handle_t; /** * Set a new default timeout. */ int tcpapi_set_timeout(struct stack_handle_t *socket, unsigned secs, unsigned usecs ); /** * Change from the "send" state to the "receive" state. * Has no effect if in any state other than "send". * This is none-blocking, an event will be triggered * later that has the data. */ int tcpapi_recv(struct stack_handle_t *socket); int tcpapi_send(struct stack_handle_t *socket, const void *buf, size_t length, enum TCP__flags flags); /** * Re-connect to the target, same IP and port, creating a new connection * from a different port on this side. */ int tcpapi_reconnect(struct stack_handle_t *old_socket, struct ProtocolParserStream *new_stream, unsigned new_app_state); /** * The "app state" variable is stored opaquely in the `tcb` structure, so * to reset it, we need an access function. */ unsigned tcpapi_change_app_state(struct stack_handle_t *socket, unsigned new_app_state); /** Perform the sockets half-close function (calling `close()`). This * doesn't actually get rid of the socket, but only stops sending. * It sends a FIN packet to the other side, and transitions to the * TCP CLOSE-WAIT state. * The socket will continue to receive from the opposing side until they * give us a FIN packet. */ int tcpapi_close(struct stack_handle_t *socket); #endif ================================================ FILE: src/stack-tcp-app.c ================================================ #include "stack-tcp-app.h" #include "stack-tcp-api.h" #include "proto-banner1.h" #include "proto-ssl.h" #include "unusedparm.h" #include "util-malloc.h" #include "util-logger.h" #include "util-errormsg.h" #include enum { App_Connect, App_ReceiveHello, App_ReceiveNext, App_SendFirst, App_SendNext, App_Close, }; static const char *state_to_string(unsigned state) { switch (state) { case App_Connect: return "connect"; case App_ReceiveHello: return "wait-for-hello"; case App_ReceiveNext: return "receive"; case App_SendFirst: return "send-first"; case App_SendNext: return "send"; case App_Close: return "close"; default: return "unknown"; } } static const char *event_to_string(enum App_Event ev) { switch (ev) { case APP_CONNECTED: return "connected"; case APP_RECV_TIMEOUT: return "timeout"; case APP_RECV_PAYLOAD: return "payload"; case APP_SEND_SENT: return "sent"; case APP_CLOSE: return "close"; case APP_SENDING: return "sending"; default: return "unknown"; } } unsigned application_event(struct stack_handle_t *socket, unsigned state, enum App_Event event, const struct ProtocolParserStream *stream, struct Banner1 *banner1, const void *payload, size_t payload_length ) { again: switch (state) { case App_Connect: switch (event) { case APP_CONNECTED: /* We have a receive a SYNACK here. If there are multiple handlers * for this port, then attempt another connection using the * other protocol handlers. For example, for SSL, we might want * to try both TLSv1.0 and TLSv1.3 */ if (stream && stream->next) { tcpapi_reconnect(socket, stream->next, App_Connect); } /* * By default, wait for the "hello timeout" period * receiving any packets they send us. If nothing is * received in this period, then timeout will cause us * to switch to sending */ if (stream != NULL && (stream->flags & SF__nowait_hello) != 0) { tcpapi_change_app_state(socket, App_SendFirst); state = App_SendFirst; goto again; } else { tcpapi_set_timeout(socket, 2 /*tcpcon->timeout_hello*/, 0); tcpapi_recv(socket); tcpapi_change_app_state(socket, App_ReceiveHello); } break; default: ERRMSG("TCP.app: unhandled event: state=%s event=%s\n", state_to_string(state), event_to_string(event)); break; } break; case App_ReceiveHello: switch (event) { case APP_RECV_TIMEOUT: /* We've got no response from the initial connection, * so switch from them being responsible for communications * to us being responsible, and start sending */ if (stream) { tcpapi_change_app_state(socket, App_SendFirst); state = App_SendFirst; goto again; } break; case APP_RECV_PAYLOAD: /* We've receive some data from them, so wait for some more. * This means we won't be transmitting anything to them. */ tcpapi_change_app_state(socket, App_ReceiveNext); state = App_ReceiveNext; goto again; case APP_CLOSE: banner_flush(socket); tcpapi_close(socket); break; default: ERRMSG("TCP.app: unhandled event: state=%s event=%s\n", state_to_string(state), event_to_string(event)); break; } break; case App_ReceiveNext: switch (event) { case APP_RECV_PAYLOAD: /* [--banners] * This is an important part of the system, where the TCP * stack passes incoming packet payloads off to the application * layer protocol parsers. This is where, in Sockets API, you * might call the 'recv()' function. */ banner_parse(socket, payload, payload_length ); break; case APP_CLOSE: /* The other side has sent us a FIN, therefore, we need * to likewise close our end. */ banner_flush(socket); tcpapi_close(socket); break; case APP_RECV_TIMEOUT: break; case APP_SENDING: /* A higher level protocol has started sending packets while processing * a receive, therefore, change to the SEND state */ tcpapi_change_app_state(socket, App_SendNext); break; case APP_SEND_SENT: /* FIXME */ break; default: ERRMSG("TCP.app: unhandled event: state=%s event=%s\n", state_to_string(state), event_to_string(event)); break; } break; case App_SendFirst: /* This isn't called from the outside, but from one of the * states internally whhen we transmit for the first time */ if (stream == &banner_ssl || stream == &banner_ssl_12) { /* * Kludge, extreme kludge * I don't even know what this does any longer */ banner_set_sslhello(socket, true); } if (banner_is_heartbleed(socket)) { /* * Kludge, extreme kludge * I don't even know what this does any longer */ banner_set_small_window(socket, true); } /* * We either have a CALLBACK that will handle the * sending/receiving of packets, or we will send a fixed * "probe" string that will hopefull trigger a response. */ if (stream && stream->transmit_hello) { /* We have a callback function for the protocol stream that will * craft a packet, such as maybe generate an HTTP request containing * valid "Host:" field. */ stream->transmit_hello(banner1, socket); } else if (stream && stream->hello_length) { /* We just have a template to blindly copy some bytes onto the wire * in order to trigger/probe for a response */ tcpapi_send(socket, stream->hello, stream->hello_length, TCP__static); /* If specified, then send a FIN right after the hello data. * This will complete a reponse faster from the server. */ if ((stream->flags & SF__close) != 0) tcpapi_close(socket); } tcpapi_change_app_state(socket, App_SendNext); break; case App_SendNext: switch (event) { case APP_SEND_SENT: /* We've got an acknowledgement that all our data * was sent. Therefore, change the receive state */ tcpapi_recv(socket); tcpapi_change_app_state(socket, App_ReceiveNext); break; case APP_SENDING: break; default: ERRMSG("TCP.app: unhandled event: state=%s event=%s\n", state_to_string(state), event_to_string(event)); break; } break; default: ERRMSG("TCP.app: unhandled event: state=%s event=%s\n", state_to_string(state), event_to_string(event)); break; } return 0; } ================================================ FILE: src/stack-tcp-app.h ================================================ #ifndef STACK_TCP_APP_H #define STACK_TCP_APP_H #include #include "util-bool.h" /* */ struct stack_handle_t; struct ProtocolParserStream; struct Banner1; enum App_Event { APP_CONNECTED, APP_RECV_TIMEOUT, APP_RECV_PAYLOAD, APP_SENDING, APP_SEND_SENT, APP_CLOSE /*FIN received */ }; /** * This is the interface between the underlying custom TCP/IP stack and * the rest of masscan. SCRIPTING will eventually go in here. */ unsigned application_event( struct stack_handle_t *socket, unsigned state, enum App_Event event, const struct ProtocolParserStream *stream, struct Banner1 *banner1, const void *payload, size_t payload_length ); void banner_set_sslhello(struct stack_handle_t *socket, bool is_true); void banner_set_small_window(struct stack_handle_t *socket, bool is_true); bool banner_is_heartbleed(const struct stack_handle_t *socket); void banner_flush(struct stack_handle_t *socket); size_t banner_parse( struct stack_handle_t *socket, const unsigned char *payload, size_t payload_length ); #endif ================================================ FILE: src/stack-tcp-core.c ================================================ /* * This is the core TCP layer in the stack. It's notified of incoming * IP datagrams containing TCP protocols. This is where the TCP state * diagram is handled. * * * +---------+ ---------\ active OPEN * | CLOSED | \ ----------- * +---------+<---------\ \ create TCB * | ^ \ \ snd SYN * passive OPEN | | CLOSE \ \ * ------------ | | ---------- \ \ * create TCB | | delete TCB \ \ * V | \ \ * +---------+ CLOSE | \ * | LISTEN | ---------- | | * +---------+ delete TCB | | * rcv SYN | | SEND | | * ----------- | | ------- | V * +---------+ snd SYN,ACK / \ snd SYN +---------+ * | |<----------------- ------------------>| | * | SYN | rcv SYN | SYN | * | RCVD |<-----------------------------------------------| SENT | * | | snd ACK | | * | |------------------ -------------------| | * +---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+ * | -------------- | | ----------- * | x | | snd ACK * | V V * | CLOSE +---------+ * | ------- | ESTAB | * | snd FIN +---------+ * | CLOSE | | rcv FIN * V ------- | | ------- * +---------+ snd FIN / \ snd ACK +---------+ * | FIN |<----------------- ------------------>| CLOSE | * | WAIT-1 |------------------ | WAIT | * +---------+ rcv FIN \ +---------+ * | rcv ACK of FIN ------- | CLOSE | * | -------------- snd ACK | ------- | * V x V snd FIN V * +---------+ +---------+ +---------+ * |FINWAIT-2| | CLOSING | | LAST-ACK| * +---------+ +---------+ +---------+ * | rcv ACK of FIN | rcv ACK of FIN | * | rcv FIN -------------- | Timeout=2MSL -------------- | * | ------- x V ------------ x V * \ snd ACK +---------+delete TCB +---------+ * ------------------------>|TIME WAIT|------------------>| CLOSED | * +---------+ +---------+ * */ #include "stack-tcp-core.h" #include "stack-tcp-api.h" #include "stack-tcp-app.h" #include #include #include #include #include #include #include #include #include #include "syn-cookie.h" #include "event-timeout.h" /* for tracking future events */ #include "rawsock.h" #include "util-logger.h" #include "templ-pkt.h" #include "pixie-timer.h" #include "stack-queue.h" #include "proto-banner1.h" #include "proto-ssl.h" #include "proto-http.h" #include "proto-smb.h" #include "proto-versioning.h" #include "output.h" #include "util-safefunc.h" #include "main-globals.h" #include "crypto-base64.h" #include "util-malloc.h" #include "util-errormsg.h" #include "scripting.h" #ifdef _MSC_VER #pragma warning(disable:4204) #define snprintf _snprintf #pragma warning(disable:4996) #endif struct TCP_Segment { unsigned seqno; unsigned char *buf; size_t length; enum TCP__flags flags; bool is_fin; /* was fin sent */ struct TCP_Segment *next; }; /*************************************************************************** * A "TCP control block" is what most operating-systems/network-stack * calls the structure that corresponds to a TCP connection. It contains * things like the IP addresses, port numbers, sequence numbers, timers, * and other things. ***************************************************************************/ struct TCP_Control_Block { ipaddress ip_me; ipaddress ip_them; unsigned short port_me; unsigned short port_them; uint32_t seqno_me; /* next seqno I will use for transmit */ uint32_t seqno_them; /* the next seqno I expect to receive */ uint32_t ackno_me; uint32_t ackno_them; uint32_t seqno_me_first; uint32_t seqno_them_first; struct TCP_Control_Block *next; struct TimeoutEntry timeout[1]; unsigned char ttl; unsigned char syns_sent; /* reconnect */ unsigned short mss; /* maximum segment size 1460 */ unsigned tcpstate:4; unsigned is_ipv6:1; unsigned is_small_window:1; /* send with smaller window */ unsigned is_their_fin:1; /** Set to true when the TCB is in-use/allocated, set to zero * when it's about to be deleted soon */ unsigned is_active:1; /* If the payload we've sent was dynamically allocated with * malloc() from the heap, in which case we'll have to free() * it. (Most payloads are static memory) */ unsigned is_payload_dynamic:1; unsigned app_state; struct TCP_Segment *segments; /* unsigned short payload_length; const unsigned char *payload; */ time_t when_created; /* * If Running a script, the thread object */ struct ScriptingThread *scripting_thread; const struct ProtocolParserStream *stream; struct BannerOutput banout; struct StreamState banner1_state; unsigned packet_number; }; struct TCP_ConnectionTable { struct TCP_Control_Block **entries; struct TCP_Control_Block *freed_list; unsigned count; unsigned mask; unsigned timeout_connection; unsigned timeout_hello; uint64_t active_count; uint64_t entropy; struct Timeouts *timeouts; struct TemplatePacket *pkt_template; struct stack_t *stack; struct Banner1 *banner1; OUTPUT_REPORT_BANNER report_banner; struct Output *out; struct ScriptingVM *scripting_vm; /** This is for creating follow-up connections based on the first * connection. Given an existing IP/port, it returns a different * one for the new conenction. */ struct { const void *data; void *(*cb)(const void *in_src, const ipaddress ip, unsigned port, ipaddress *next_ip, unsigned *next_port); } next_ip_port; }; enum { STATE_SYN_SENT=0, /* must be zero */ //STATE_SYN_RECEIVED, STATE_ESTABLISHED_SEND, /* our own special state, can only send */ STATE_ESTABLISHED_RECV, /* our own special state, can only receive */ STATE_CLOSE_WAIT, STATE_LAST_ACK, STATE_FIN_WAIT1_SEND, STATE_FIN_WAIT1_RECV, STATE_FIN_WAIT2, STATE_CLOSING, STATE_TIME_WAIT, }; /*************************************************************************** * DEBUG: when printing debug messages (-d option), this prints a string * for the given state. ***************************************************************************/ static const char * state_to_string(int state) { static char buf[64]; switch (state) { //STATE_SYN_RECEIVED, case STATE_CLOSE_WAIT: return "CLOSE-WAIT"; case STATE_LAST_ACK: return "LAST-ACK"; case STATE_FIN_WAIT1_SEND: return "FIN-WAIT-1-SEND"; case STATE_FIN_WAIT1_RECV: return "FIN-WAIT-1-RECV"; case STATE_FIN_WAIT2: return "FIN-WAIT-2"; case STATE_CLOSING: return "CLOSING"; case STATE_TIME_WAIT: return "TIME-WAIT"; case STATE_SYN_SENT: return "SYN_SENT"; case STATE_ESTABLISHED_SEND:return "ESTABLISHED_SEND"; case STATE_ESTABLISHED_RECV:return "ESTABLISHED_RECV"; default: snprintf(buf, sizeof(buf), "%d", state); return buf; } } static void vLOGtcb(const struct TCP_Control_Block *tcb, int dir, const char *fmt, va_list marker) { char sz[256]; ipaddress_formatted_t fmt1 = ipaddress_fmt(tcb->ip_them); snprintf(sz, sizeof(sz), "[%s:%u %4u,%4u] %s:%5u [%4u,%4u] {%s} ", fmt1.string, tcb->port_them, tcb->seqno_them - tcb->seqno_them_first, tcb->ackno_me - tcb->seqno_them_first, (dir > 0) ? "-->" : "<--", tcb->port_me, tcb->seqno_me - tcb->seqno_me_first, tcb->ackno_them - tcb->seqno_me_first, state_to_string(tcb->tcpstate) ); if (dir == 2) { char *brace = strchr(sz, '{'); memset(sz, ' ', brace-sz); } fprintf(stderr, "%s", sz); vfprintf(stderr, fmt, marker); fflush(stderr); } int is_tcp_debug = 0; static void LOGtcb(const struct TCP_Control_Block *tcb, int dir, const char *fmt, ...) { va_list marker; if (!is_tcp_debug) return; va_start(marker, fmt); vLOGtcb(tcb, dir, fmt, marker); va_end(marker); } /*************************************************************************** * Process all events, up to the current time, that need timing out. ***************************************************************************/ void tcpcon_timeouts(struct TCP_ConnectionTable *tcpcon, unsigned secs, unsigned usecs) { uint64_t timestamp = TICKS_FROM_TV(secs, usecs); for (;;) { struct TCP_Control_Block *tcb; enum TCB_result x; /* * Get the next event that is older than the current time */ tcb = (struct TCP_Control_Block *)timeouts_remove(tcpcon->timeouts, timestamp); /* * If everything up to the current time has already been processed, * then exit this loop */ if (tcb == NULL) break; /* * Process this timeout */ x = stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_TIMEOUT, 0, 0, secs, usecs, tcb->seqno_them, tcb->ackno_them); /* If the TCB hasn't been destroyed, then we need to make sure * there is a timeout associated with it. KLUDGE: here is the problem: * there must ALWAYS be a 'timeout' associated with a TCB, otherwise, * we'll lose track of it and leak memory. In theory, this should be * automatically handled elsewhere, but I have bugs, and it's not, * so I put some code here as a catch-all: if the TCB hasn't been * deleted, but hasn't been inserted back into the timeout system, * then insert it here. */ if (x != TCB__destroyed && timeout_is_unlinked(tcb->timeout)) { timeouts_add( tcpcon->timeouts, tcb->timeout, offsetof(struct TCP_Control_Block, timeout), TICKS_FROM_TV(secs+2, usecs)); } } } /*************************************************************************** ***************************************************************************/ static int name_equals(const char *lhs, const char *rhs) { for (;;) { while (*lhs == '-' || *lhs == '.' || *lhs == '_') lhs++; while (*rhs == '-' || *rhs == '.' || *rhs == '_') rhs++; if (*lhs == '\0' && *rhs == '[') return 1; /*arrays*/ if (*rhs == '\0' && *lhs == '[') return 1; /*arrays*/ if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF)) return 0; if (*lhs == '\0') return 1; lhs++; rhs++; } } /*************************************************************************** * When setting parameters, this will parse integers from the config * parameter strings. ***************************************************************************/ static uint64_t parseInt(const void *vstr, size_t length) { const char *str = (const char *)vstr; uint64_t result = 0; size_t i; for (i=0; ibanner1; if (name_equals(name, "http-payload")) { char lenstr[64]; snprintf(lenstr, sizeof(lenstr), "%u", (unsigned)value_length); banner_http.hello_length = http_change_requestline( (unsigned char**)&banner_http.hello, banner_http.hello_length, (const unsigned char *)value, value_length, 3); /* payload*/ banner_http.hello_length = http_change_field( (unsigned char**)&banner_http.hello, banner_http.hello_length, "Content-Length:", (const unsigned char *)lenstr, strlen(lenstr), http_field_replace); return; } /* * You can reset your user-agent here. Whenever I do a scan, I always * reset my user-agent. That's now you know it's not me scanning * you on the open Internet -- I would never use the default user-agent * string built into masscan */ if (name_equals(name, "http-user-agent")) { banner_http.hello_length = http_change_field( (unsigned char**)&banner_http.hello, banner_http.hello_length, "User-Agent:", (const unsigned char *)value, value_length, http_field_replace); return; } if (name_equals(name, "http-host")) { banner_http.hello_length = http_change_field( (unsigned char**)&banner_http.hello, banner_http.hello_length, "Host:", (const unsigned char *)value, value_length, http_field_replace); return; } /** * Changes the URL */ if (name_equals(name, "http-method")) { banner_http.hello_length = http_change_requestline( (unsigned char**)&banner_http.hello, banner_http.hello_length, (const unsigned char *)value, value_length, 0); /* method*/ return; } if (name_equals(name, "http-url")) { banner_http.hello_length = http_change_requestline( (unsigned char**)&banner_http.hello, banner_http.hello_length, (const unsigned char *)value, value_length, 1); /* url */ return; } if (name_equals(name, "http-version")) { banner_http.hello_length = http_change_requestline( (unsigned char**)&banner_http.hello, banner_http.hello_length, (const unsigned char *)value, value_length, 2); /* version */ return; } if (name_equals(name, "timeout") || name_equals(name, "connection-timeout")) { uint64_t n = parseInt(value, value_length); tcpcon->timeout_connection = (unsigned)n; LOG(1, "TCP connection-timeout = %u\n", tcpcon->timeout_connection); return; } if (name_equals(name, "hello-timeout")) { uint64_t n = parseInt(value, value_length); tcpcon->timeout_hello = (unsigned)n; LOG(1, "TCP hello-timeout = \"%.*s\"\n", (int)value_length, (const char *)value); LOG(1, "TCP hello-timeout = %u\n", (unsigned)tcpcon->timeout_hello); return; } /* * Force SSL processing on all ports */ if (name_equals(name, "hello") && name_equals(value, "ssl")) { unsigned i; LOG(2, "HELLO: setting SSL hello message\n"); for (i=0; i<65535; i++) { banner1->payloads.tcp[i] = &banner_ssl; } return; } /* * Force HTTP processing on all ports */ if (name_equals(name, "hello") && name_equals(value, "http")) { unsigned i; LOG(2, "HELLO: setting HTTP hello message\n"); for (i=0; i<65535; i++) { banner1->payloads.tcp[i] = &banner_http; } return; } /* * Downgrade SMB hello from v1/v2 to use only v1 */ if (name_equals(name, "hello") && name_equals(value, "smbv1")) { smb_set_hello_v1(&banner_smb1); return; } /* * 2014-04-08: scan for Neel Mehta's "heartbleed" bug */ if (name_equals(name, "heartbleed")) { unsigned i; /* Change the hello message to including negotiating the use of * the "heartbeat" extension */ banner_ssl.hello = ssl_hello(ssl_hello_heartbeat_template); banner_ssl.hello_length = ssl_hello_size(banner_ssl.hello); tcpcon->banner1->is_heartbleed = 1; for (i=0; i<65535; i++) { banner1->payloads.tcp[i] = &banner_ssl; } return; } if (name_equals(name, "ticketbleed")) { unsigned i; /* Change the hello message to including negotiating the use of * the "heartbeat" extension */ banner_ssl.hello = ssl_hello(ssl_hello_ticketbleed_template); banner_ssl.hello_length = ssl_hello_size(banner_ssl.hello); tcpcon->banner1->is_ticketbleed = 1; for (i=0; i<65535; i++) { banner1->payloads.tcp[i] = &banner_ssl; } return; } /* * 2014-10-16: scan for SSLv3 servers (POODLE) */ if (name_equals(name, "poodle") || name_equals(name, "sslv3")) { unsigned i; void *px; /* Change the hello message to including negotiating the use of * the "heartbeat" extension */ px = ssl_hello(ssl_hello_sslv3_template); banner_ssl.hello = ssl_add_cipherspec(px, 0x5600, 1); banner_ssl.hello_length = ssl_hello_size(banner_ssl.hello); tcpcon->banner1->is_poodle_sslv3 = 1; for (i=0; i<65535; i++) { banner1->payloads.tcp[i] = &banner_ssl; } return; } /* * You can reconfigure the "hello" message to be anything * you want. */ if (name_equals(name, "hello-string")) { struct ProtocolParserStream *x; const char *p = strchr(name, '['); unsigned port; if (p == NULL) { ERRMSG("tcpcon: parameter: expected array []: %s\n", name); return; } port = (unsigned)strtoul(p+1, 0, 0); x = CALLOC(1, sizeof(*x)); if (banner1->payloads.tcp[port]) memcpy(x, banner1->payloads.tcp[port], sizeof (*x)); x->name = "(allocated)"; x->hello = MALLOC(value_length); x->hello_length = base64_decode((char*)x->hello, value_length, value, value_length); banner1->payloads.tcp[port] = x; } } /*************************************************************************** ***************************************************************************/ void tcpcon_set_banner_flags(struct TCP_ConnectionTable *tcpcon, unsigned is_capture_cert, unsigned is_capture_servername, unsigned is_capture_html, unsigned is_capture_heartbleed, unsigned is_capture_ticketbleed) { tcpcon->banner1->is_capture_cert = is_capture_cert; tcpcon->banner1->is_capture_servername = is_capture_servername; tcpcon->banner1->is_capture_html = is_capture_html; tcpcon->banner1->is_capture_heartbleed = is_capture_heartbleed; tcpcon->banner1->is_capture_ticketbleed = is_capture_ticketbleed; } /*************************************************************************** ***************************************************************************/ void scripting_init_tcp(struct TCP_ConnectionTable *tcpcon, struct lua_State *L) { tcpcon->banner1->L = L; banner_scripting.init(tcpcon->banner1); } /*************************************************************************** * Called at startup, by a receive thread, to create a TCP connection * table. ***************************************************************************/ struct TCP_ConnectionTable * tcpcon_create_table( size_t entry_count, struct stack_t *stack, struct TemplatePacket *pkt_template, OUTPUT_REPORT_BANNER report_banner, struct Output *out, unsigned connection_timeout, uint64_t entropy ) { struct TCP_ConnectionTable *tcpcon; tcpcon = CALLOC(1, sizeof(*tcpcon)); tcpcon->timeout_connection = connection_timeout; if (tcpcon->timeout_connection == 0) tcpcon->timeout_connection = 30; /* half a minute before destroying tcb */ tcpcon->timeout_hello = 2; tcpcon->entropy = entropy; /* Find nearest power of 2 to the tcb count, but don't go * over the number 16-million */ { size_t new_entry_count; new_entry_count = 1; while (new_entry_count < entry_count) { new_entry_count *= 2; if (new_entry_count == 0) { new_entry_count = (1<<24); break; } } if (new_entry_count > (1<<24)) new_entry_count = (1<<24); if (new_entry_count < (1<<10)) new_entry_count = (1<<10); entry_count = new_entry_count; } /* Create the table. If we can't allocate enough memory, then shrink * the desired size of the table */ while (tcpcon->entries == 0) { tcpcon->entries = malloc(entry_count * sizeof(*tcpcon->entries)); if (tcpcon->entries == NULL) { entry_count >>= 1; } } memset(tcpcon->entries, 0, entry_count * sizeof(*tcpcon->entries)); /* fill in the table structure */ tcpcon->count = (unsigned)entry_count; tcpcon->mask = (unsigned)(entry_count-1); /* create an event/timeouts structure */ tcpcon->timeouts = timeouts_create(TICKS_FROM_SECS(time(0))); tcpcon->pkt_template = pkt_template; tcpcon->stack = stack; tcpcon->banner1 = banner1_create(); tcpcon->report_banner = report_banner; tcpcon->out = out; return tcpcon; } static int TCB_EQUALS(const struct TCP_Control_Block *lhs, const struct TCP_Control_Block *rhs) { if (lhs->port_me != rhs->port_me || lhs->port_them != rhs->port_them) return 0; if (lhs->ip_me.version != rhs->ip_me.version) return 0; if (lhs->ip_me.version == 6) { if (memcmp(&lhs->ip_me.ipv6, &rhs->ip_me.ipv6, sizeof(rhs->ip_me.ipv6)) != 0) return 0; if (memcmp(&lhs->ip_them.ipv6, &rhs->ip_them.ipv6, sizeof(rhs->ip_them.ipv6)) != 0) return 0; } else { if (lhs->ip_me.ipv4 != rhs->ip_me.ipv4) return 0; if (lhs->ip_them.ipv4 != rhs->ip_them.ipv4) return 0; } return 1; } /*************************************************************************** ***************************************************************************/ static void _tcb_change_state_to(struct TCP_Control_Block *tcb, unsigned new_state) { LOGtcb(tcb, 2, "to {%s}\n", state_to_string(new_state)); tcb->tcpstate = new_state; } /*************************************************************************** ***************************************************************************/ static unsigned tcb_hash( ipaddress ip_me, unsigned port_me, ipaddress ip_them, unsigned port_them, uint64_t entropy) { unsigned index; /* TCB hash table uses symmetric hash, so incoming/outgoing packets * get the same hash. */ if (ip_me.version == 6) { ipv6address ipv6 = ip_me.ipv6; ipv6.hi ^= ip_them.ipv6.hi; ipv6.lo ^= ip_them.ipv6.lo; index = (unsigned)syn_cookie_ipv6( ipv6, port_me ^ port_them, ipv6, port_me ^ port_them, entropy); } else { index = (unsigned)syn_cookie_ipv4( ip_me.ipv4 ^ ip_them.ipv4, port_me ^ port_them, ip_me.ipv4 ^ ip_them.ipv4, port_me ^ port_them, entropy ); } return index; } enum DestroyReason { Reason_Timeout = 1, Reason_FIN = 2, Reason_RST = 3, Reason_Foo = 4, Reason_Shutdown = 5, Reason_StateDone = 6, }; /*************************************************************************** * Flush all the banners associated with this TCP connection. This always * called when TCB is destroyed. This may also be called earlier, such * as when a FIN is received. ***************************************************************************/ void banner_flush(struct stack_handle_t *socket) { struct TCP_ConnectionTable *tcpcon = socket->tcpcon; struct TCP_Control_Block *tcb = socket->tcb; struct BannerOutput *banout; /* Go through and print all the banners. Some protocols have * multiple banners. For example, web servers have both * HTTP and HTML banners, and SSL also has several * X.509 certificate banners */ for (banout = &tcb->banout; banout != NULL; banout = banout->next) { if (banout->length && banout->protocol) { tcpcon->report_banner( tcpcon->out, global_now, tcb->ip_them, 6, /*TCP protocol*/ tcb->port_them, banout->protocol & 0x0FFFFFFF, tcb->ttl, banout->banner, banout->length); } } /* * Free up all the banners. */ banout_release(&tcb->banout); } /*************************************************************************** * Destroy a TCP connection entry. We have to unlink both from the * TCB-table as well as the timeout-table. * Called from ***************************************************************************/ static void tcpcon_destroy_tcb( struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb, enum DestroyReason reason) { unsigned index; struct TCP_Control_Block **r_entry; UNUSEDPARM(reason); /* * The TCB doesn't point to it's location in the table. Therefore, we * have to do a lookup to find the head pointer in the table. */ index = tcb_hash( tcb->ip_me, tcb->port_me, tcb->ip_them, tcb->port_them, tcpcon->entropy); /* * At this point, we have the head of a linked list of TCBs. Now, * traverse that linked list until we find our TCB */ r_entry = &tcpcon->entries[index & tcpcon->mask]; while (*r_entry && *r_entry != tcb) r_entry = &(*r_entry)->next; if (*r_entry == NULL) { LOG(1, "tcb: double free\n"); return; } /* * Print out any banners associated with this TCP session. Most of the * time, there'll only be one. After printing them out, delete the * banners. */ { struct stack_handle_t socket = {tcpcon, tcb, 0, 0}; banner_flush(&socket); } LOGtcb(tcb, 2, "--DESTROYED--\n"); /* * If there are any queued segments to transmit, then free them */ while (tcb->segments) { struct TCP_Segment *seg; seg = tcb->segments; tcb->segments = seg->next; if (seg->flags == TCP__copy || seg->flags == TCP__adopt) { free(seg->buf); seg->buf = 0; } free(seg); } if (tcb->scripting_thread) ; //scripting_thread_close(tcb->scripting_thread); tcb->scripting_thread = 0; /* KLUDGE: this needs to be made elegant */ switch (tcb->banner1_state.app_proto) { case PROTO_SMB: banner_smb1.cleanup(&tcb->banner1_state); break; } /* * Unlink this from the timeout system. */ timeout_unlink(tcb->timeout); tcb->ip_them.ipv4 = (unsigned)~0; tcb->port_them = (unsigned short)~0; tcb->ip_me.ipv4 = (unsigned)~0; tcb->port_me = (unsigned short)~0; tcb->is_active = 0; (*r_entry) = tcb->next; tcb->next = tcpcon->freed_list; tcpcon->freed_list = tcb; tcpcon->active_count--; } /*************************************************************************** * Called at shutdown to free up all the memory used by the TCP * connection table. ***************************************************************************/ void tcpcon_destroy_table(struct TCP_ConnectionTable *tcpcon) { unsigned i; if (tcpcon == NULL) return; /* * Do a graceful destruction of all the entires. If they have banners, * they will be sent to the output */ for (i=0; i<=tcpcon->mask; i++) { while (tcpcon->entries[i]) tcpcon_destroy_tcb(tcpcon, tcpcon->entries[i], Reason_Shutdown); } /* * Now free the memory */ while (tcpcon->freed_list) { struct TCP_Control_Block *tcb = tcpcon->freed_list; tcpcon->freed_list = tcb->next; free(tcb); } banner1_destroy(tcpcon->banner1); free(tcpcon->entries); free(tcpcon); } /*************************************************************************** * * Called when we receive a "SYN-ACK" packet with the correct SYN-cookie. * ***************************************************************************/ struct TCP_Control_Block * tcpcon_create_tcb( struct TCP_ConnectionTable *tcpcon, ipaddress ip_me, ipaddress ip_them, unsigned port_me, unsigned port_them, unsigned seqno_me, unsigned seqno_them, unsigned ttl, const struct ProtocolParserStream *stream, unsigned secs, unsigned usecs) { unsigned index; struct TCP_Control_Block tmp; struct TCP_Control_Block *tcb; assert(ip_me.version != 0 && ip_them.version != 0); tmp.ip_me = ip_me; tmp.ip_them = ip_them; tmp.port_me = (unsigned short)port_me; tmp.port_them = (unsigned short)port_them; /* Lookup the location in the hash table where this tcb should be * placed */ index = tcb_hash(ip_me, port_me, ip_them, port_them, tcpcon->entropy); tcb = tcpcon->entries[index & tcpcon->mask]; while (tcb && !TCB_EQUALS(tcb, &tmp)) { tcb = tcb->next; } if (tcb != NULL) { /* If it already exists, just return the existing one */ return tcb; } /* Allocate a new TCB, using a pool */ if (tcpcon->freed_list) { tcb = tcpcon->freed_list; tcpcon->freed_list = tcb->next; } else { tcb = MALLOC(sizeof(*tcb)); } memset(tcb, 0, sizeof(*tcb)); /* Add it to this spot in the hash table */ tcb->next = tcpcon->entries[index & tcpcon->mask]; tcpcon->entries[index & tcpcon->mask] = tcb; /* * Initialize the entry */ tcb->ip_me = ip_me; tcb->ip_them = ip_them; tcb->port_me = (unsigned short)port_me; tcb->port_them = (unsigned short)port_them; tcb->seqno_them_first = seqno_them; tcb->seqno_me_first = seqno_me; tcb->seqno_me = seqno_me; tcb->seqno_them = seqno_them; tcb->ackno_me = seqno_them; tcb->ackno_them = seqno_me; tcb->when_created = global_now; tcb->ttl = (unsigned char)ttl; tcb->mss = 1400; /* Insert the TCB into the timeout. A TCB must always have a timeout * active. */ timeout_init(tcb->timeout); timeouts_add(tcpcon->timeouts, tcb->timeout, offsetof(struct TCP_Control_Block, timeout), TICKS_FROM_TV(secs+1,usecs) ); /* Get the protocol handler assigned to this port */ tcb->banner1_state.port = (unsigned short)port_them; if (stream == NULL) { struct Banner1 *banner1 = tcpcon->banner1; stream = banner1->payloads.tcp[port_them]; } tcb->stream = stream; banout_init(&tcb->banout); /* The TCB is now allocated/in-use */ assert(tcb->ip_me.version != 0 && tcb->ip_them.version != 0); tcb->is_active = 1; tcpcon->active_count++; tcpcon_lookup_tcb(tcpcon, ip_me, ip_them, port_me, port_them); return tcb; } /*************************************************************************** ***************************************************************************/ struct TCP_Control_Block * tcpcon_lookup_tcb( struct TCP_ConnectionTable *tcpcon, ipaddress ip_me, ipaddress ip_them, unsigned port_me, unsigned port_them) { unsigned index; struct TCP_Control_Block tmp; struct TCP_Control_Block *tcb; ipaddress_formatted_t fmt1; ipaddress_formatted_t fmt2; tmp.ip_me = ip_me; tmp.ip_them = ip_them; tmp.port_me = (unsigned short)port_me; tmp.port_them = (unsigned short)port_them; index = tcb_hash(ip_me, port_me, ip_them, port_them, tcpcon->entropy); fmt1 = ipaddress_fmt(ip_me); fmt2 = ipaddress_fmt(ip_them); LOG(1, "tcb_hash(0x%08x) = %s %u %s %u\n", (unsigned)index, fmt1.string, port_me, fmt2.string, port_them); /* Hash to an entry in the table, then follow a linked list from * that point forward. */ tcb = tcpcon->entries[index & tcpcon->mask]; while (tcb && !TCB_EQUALS(tcb, &tmp)) { tcb = tcb->next; } return tcb; } /*************************************************************************** ***************************************************************************/ static void tcpcon_send_packet( struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb, unsigned tcp_flags, const unsigned char *payload, size_t payload_length) { struct PacketBuffer *response = 0; unsigned is_syn = (tcp_flags == 0x02); assert(tcb->ip_me.version != 0 && tcb->ip_them.version != 0); /* If sending an ACK, print a message */ if ((tcp_flags & 0x10) == 0x10) { LOGtcb(tcb, 0, "xmit ACK ackingthem=%u\n", tcb->seqno_them-tcb->seqno_them_first); } /* Get a buffer for sending the response packet. This thread doesn't * send the packet itself. Instead, it formats a packet, then hands * that packet off to a transmit thread for later transmission. */ response = stack_get_packetbuffer(tcpcon->stack); if (response == NULL) { static int is_warning_printed = 0; if (!is_warning_printed) { LOG(0, "packet buffers empty (should be impossible)\n"); is_warning_printed = 1; } fflush(stdout); /* FIXME: I'm no sure the best way to handle this. * This would result from a bug in the code, * but I'm not sure what should be done in response */ pixie_usleep(100); /* no packet available */ } if (response == NULL) return; /* Format the packet as requested. Note that there are really only * four types of packets: * 1. a SYN-ACK packet with no payload * 2. an ACK packet with no payload * 3. a RST packet with no payload * 4. a PSH-ACK packet WITH PAYLOAD */ response->length = tcp_create_packet( tcpcon->pkt_template, tcb->ip_them, tcb->port_them, tcb->ip_me, tcb->port_me, tcb->seqno_me - is_syn, tcb->seqno_them, tcp_flags, payload, payload_length, response->px, sizeof(response->px) ); /* * KLUDGE: */ if (tcb->is_small_window) tcp_set_window(response->px, response->length, 600); /* Put this buffer on the transmit queue. Remember: transmits happen * from a transmit-thread only, and this function is being called * from a receive-thread. Therefore, instead of transmiting ourselves, * we hae to queue it up for later transmission. */ stack_transmit_packetbuffer(tcpcon->stack, response); } /*************************************************************************** ***************************************************************************/ void tcp_send_RST( struct TemplatePacket *templ, struct stack_t *stack, ipaddress ip_them, ipaddress ip_me, unsigned port_them, unsigned port_me, unsigned seqno_them, unsigned seqno_me ) { struct PacketBuffer *response = 0; /* Get a buffer for sending the response packet. This thread doesn't * send the packet itself. Instead, it formats a packet, then hands * that packet off to a transmit thread for later transmission. */ response = stack_get_packetbuffer(stack); if (response == NULL) { static int is_warning_printed = 0; if (!is_warning_printed) { LOG(0, "packet buffers empty (should be impossible)\n"); is_warning_printed = 1; } fflush(stdout); pixie_usleep(100); /* no packet available */ } if (response == NULL) return; response->length = tcp_create_packet( templ, ip_them, port_them, ip_me, port_me, seqno_me, seqno_them, 0x04, /*RST*/ 0, 0, response->px, sizeof(response->px) ); /* Put this buffer on the transmit queue. Remember: transmits happen * from a transmit-thread only, and this function is being called * from a receive-thread. Therefore, instead of transmitting ourselves, * we have to queue it up for later transmission. */ stack_transmit_packetbuffer(stack, response); } /*************************************************************************** * DEBUG: when printing debug messages (-d option), this prints a string * for the given state. ***************************************************************************/ static const char * what_to_string(enum TCP_What state) { static char buf[64]; switch (state) { case TCP_WHAT_TIMEOUT: return "TIMEOUT"; case TCP_WHAT_SYNACK: return "SYNACK"; case TCP_WHAT_RST: return "RST"; case TCP_WHAT_FIN: return "FIN"; case TCP_WHAT_ACK: return "ACK"; case TCP_WHAT_DATA: return "DATA"; case TCP_WHAT_CLOSE: return "CLOSE"; default: snprintf(buf, sizeof(buf), "%d", state); return buf; } } /*************************************************************************** ***************************************************************************/ static void LOGSEND(struct TCP_Control_Block *tcb, const char *what) { if (tcb == NULL) return; LOGip(5, tcb->ip_them, tcb->port_them, "=%s : --->> %s \n", state_to_string(tcb->tcpstate), what); } void tcpcon_send_RST( struct TCP_ConnectionTable *tcpcon, ipaddress ip_me, ipaddress ip_them, unsigned port_me, unsigned port_them, uint32_t seqno_them, uint32_t ackno_them) { struct TCP_Control_Block tcb; memset(&tcb, 0, sizeof(tcb)); tcb.ip_me = ip_me; tcb.ip_them = ip_them; tcb.port_me = (unsigned short)port_me; tcb.port_them = (unsigned short)port_them; tcb.seqno_me = ackno_them; tcb.ackno_me = seqno_them + 1; tcb.seqno_them = seqno_them + 1; tcb.ackno_them = ackno_them; LOGSEND(&tcb, "send RST"); tcpcon_send_packet(tcpcon, &tcb, 0x04 /*RST*/, 0, 0); } /*************************************************************************** * Called upon timeouts when an acknowledgement hasn't been received in * time. Will resend the segments. ***************************************************************************/ static void _tcb_seg_resend(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb) { struct TCP_Segment *seg = tcb->segments; if (seg) { if (tcb->seqno_me != seg->seqno) { ERRMSG("SEQNO FAILURE diff=%d %s\n", tcb->seqno_me - seg->seqno, seg->is_fin?"FIN":""); return; } if (seg->is_fin && seg->length == 0) { tcpcon_send_packet(tcpcon, tcb, 0x11, /*FIN-ACK*/ 0, /*FIN has no data */ 0 /*logically is 1 byte, but not payload byte */); } else { tcpcon_send_packet(tcpcon, tcb, 0x18 | (seg->is_fin?0x01:0x00), seg->buf, seg->length); } } } /*************************************************************************** ***************************************************************************/ static unsigned application_notify(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb, enum App_Event event, const void *payload, size_t payload_length, unsigned secs, unsigned usecs) { struct Banner1 *banner1 = tcpcon->banner1; const struct ProtocolParserStream *stream = tcb->stream; struct stack_handle_t socket = { tcpcon, tcb, secs, usecs}; return application_event(&socket, tcb->app_state, event, stream, banner1, payload, payload_length ); } /*************************************************************************** ***************************************************************************/ static void _tcb_seg_send(void *in_tcpcon, void *in_tcb, const void *buf, size_t length, enum TCP__flags flags) { struct TCP_ConnectionTable *tcpcon = (struct TCP_ConnectionTable *)in_tcpcon; struct TCP_Control_Block *tcb = (struct TCP_Control_Block *)in_tcb; struct TCP_Segment *seg; struct TCP_Segment **next; unsigned seqno = tcb->seqno_me; size_t length_more = 0; bool is_fin = (flags == TCP__close_fin); if (length > tcb->mss) { length_more = length - tcb->mss; length = tcb->mss; } if (length == 0 && !is_fin) return; /* Go to the end of the segment list */ for (next = &tcb->segments; *next; next = &(*next)->next) { seqno = (unsigned)((*next)->seqno + (*next)->length); if ((*next)->is_fin) { /* can't send past a FIN */ LOGip(0, tcb->ip_them, tcb->port_them, "can't send past a FIN\n"); if (flags == TCP__adopt) { free((void*)buf); /* discard const */ buf = NULL; } return; } } /* Append this segment to the list */ seg = calloc(1, sizeof(*seg)); *next = seg; /* Fill in this segment's members */ seg->seqno = seqno; seg->length = length; seg->flags = flags; switch (flags) { case TCP__static: case TCP__adopt: seg->buf = (void *)buf; break; case TCP__copy: seg->buf = malloc(length); memcpy(seg->buf, buf, length); break; case TCP__close_fin: seg->buf = 0; break; } if (length_more == 0) seg->is_fin = is_fin; if (!seg->is_fin && seg->length && tcb->tcpstate != STATE_ESTABLISHED_SEND) application_notify(tcpcon, tcb, APP_SENDING, seg->buf, seg->length, 0, 0); LOGtcb(tcb, 0, "send = %u-bytes %s @ %u\n", length, is_fin?"FIN":"", seg->seqno-tcb->seqno_me_first); /* If this is the head of the segment list, then transmit right away */ if (tcb->segments == seg) { LOGtcb(tcb, 0, "xmit = %u-bytes %s @ %u\n", length, is_fin?"FIN":"", seg->seqno-tcb->seqno_me_first); tcpcon_send_packet(tcpcon, tcb, 0x18 | (is_fin?1:0), seg->buf, seg->length); if (!is_fin) _tcb_change_state_to(tcb, STATE_ESTABLISHED_SEND); } /* If the input buffer was too large to fit a single segment, then * split it up into multiple segments */ if (length_more) { if (flags == TCP__adopt) flags = TCP__copy; _tcb_seg_send(tcpcon, tcb, (unsigned char*)buf + length, length_more, flags); } //tcb->established = App_SendNext; } /*************************************************************************** ***************************************************************************/ static int _tcp_seg_acknowledge( struct TCP_Control_Block *tcb, uint32_t ackno) { /*LOG(4, "%s - %u-sending, %u-reciving\n", fmt.string, tcb->seqno_me - ackno, ackno - tcb->ackno_them );*/ /* Normal: just discard repeats */ if (ackno == tcb->seqno_me) { return 0; } /* Make sure this isn't a duplicate ACK from past * WRAPPING of 32-bit arithmetic happens here */ if (ackno - tcb->seqno_me > 100000) { ipaddress_formatted_t fmt = ipaddress_fmt(tcb->ip_them); LOG(4, "%s - " "tcb: ackno from past: " "old ackno = 0x%08x, this ackno = 0x%08x\n", fmt.string, tcb->ackno_me, ackno); return 0; } /* Make sure this isn't invalid ACK from the future * WRAPPING of 32-bit arithmetic happens here */ if (tcb->seqno_me - ackno < 100000) { ipaddress_formatted_t fmt = ipaddress_fmt(tcb->ip_them); LOG(0, "%s - " "tcb: ackno from future: " "my seqno = 0x%08x, their ackno = 0x%08x\n", fmt.string, tcb->seqno_me, ackno); return 0; } /* Handle FIN specially */ handle_fin: if (tcb->segments && tcb->segments->is_fin) { struct TCP_Segment *seg = tcb->segments; if (seg->seqno+1 == ackno) { LOGtcb(tcb, 1, "ACKed FIN\n"); tcb->seqno_me += 1; tcb->ackno_them += 1; return 1; } else if (seg->seqno == ackno) { return 0; } else { LOGtcb(tcb, 1, "@@@@@BAD ACK of FIN@@@@\n", seg->length); return 0; } } /* Retire outstanding segments */ { unsigned length = ackno - tcb->seqno_me; while (tcb->segments && length >= tcb->segments->length) { struct TCP_Segment *seg = tcb->segments; if (seg->is_fin) goto handle_fin; tcb->segments = seg->next; length -= seg->length; tcb->seqno_me += seg->length; tcb->ackno_them += seg->length; LOGtcb(tcb, 1, "ACKed %u-bytes\n", seg->length); /* free the old segment */ switch (seg->flags) { case TCP__static: break; case TCP__adopt: case TCP__copy: if (seg->buf) { free(seg->buf); seg->buf = NULL; } break; default: ; } free(seg); if (ackno == tcb->ackno_them) return 1; /* good ACK */ } if (tcb->segments && length < tcb->segments->length) { struct TCP_Segment *seg = tcb->segments; tcb->seqno_me += length + seg->is_fin; tcb->ackno_them += length + seg->is_fin; LOGtcb(tcb, 1, "ACKed %u-bytes %s\n", length, seg->is_fin?"FIN":""); /* This segment needs to be reduced */ if (seg->flags == TCP__adopt || seg->flags == TCP__copy) { size_t new_length = seg->length - length; unsigned char *buf = malloc(new_length); memcpy(buf, seg->buf + length, new_length); free(seg->buf); seg->buf = buf; seg->length -= length; seg->flags = TCP__copy; } else { seg->buf += length; } } } /* Mark that this was a good ack */ return 1; } void banner_set_sslhello(struct stack_handle_t *socket, bool is_true) { struct TCP_Control_Block *tcb = socket->tcb; tcb->banner1_state.is_sent_sslhello = is_true; } void banner_set_small_window(struct stack_handle_t *socket, bool is_true) { struct TCP_Control_Block *tcb = socket->tcb; tcb->is_small_window = is_true; } bool banner_is_heartbleed(const struct stack_handle_t *socket) { struct TCP_ConnectionTable *tcpcon = socket->tcpcon; return tcpcon->banner1->is_heartbleed != 0; } /*************************************************************************** * Parse the information we get from the server we are scanning. Typical * examples are SSH banners, FTP banners, or the response from HTTP * requests ***************************************************************************/ size_t banner_parse( struct stack_handle_t *socket, const unsigned char *payload, size_t payload_length ) { struct TCP_ConnectionTable *tcpcon = socket->tcpcon; struct TCP_Control_Block *tcb = socket->tcb; assert(tcb->banout.max_length); banner1_parse( tcpcon->banner1, &tcb->banner1_state, payload, payload_length, &tcb->banout, socket); return payload_length; } /*************************************************************************** ***************************************************************************/ static void _next_IP_port(struct TCP_ConnectionTable *tcpcon, ipaddress *ip_me, unsigned *port_me) { const struct stack_src_t *src = tcpcon->stack->src; unsigned index; /* Get another source port, because we can't use the existing * one for new connection */ index = *port_me - src->port.first + 1; *port_me = src->port.first + index; if (*port_me >= src->port.last) { *port_me = src->port.first; /* We've wrapped the ports, so therefore choose another source * IP address as well. */ switch (ip_me->version) { case 4: index = ip_me->ipv4 - src->ipv4.first + 1; ip_me->ipv4 = src->ipv4.first + index; if (ip_me->ipv4 >= src->ipv4.last) ip_me->ipv4 = src->ipv4.first; break; case 6: { /* TODO: this code is untested, yolo */ ipv6address_t diff; diff = ipv6address_subtract(ip_me->ipv6, src->ipv6.first); diff = ipv6address_add_uint64(diff, 1); ip_me->ipv6 = ipv6address_add(src->ipv6.first, diff); if (ipv6address_is_lessthan(src->ipv6.last, ip_me->ipv6)) ip_me->ipv6 = src->ipv6.first; break; } default: break; } } } /*************************************************************************** ***************************************************************************/ static void _do_reconnect(struct TCP_ConnectionTable *tcpcon, const struct TCP_Control_Block *old_tcb, const struct ProtocolParserStream *stream, unsigned secs, unsigned usecs, unsigned established) { struct TCP_Control_Block *new_tcb; ipaddress ip_them = old_tcb->ip_them; unsigned port_them = old_tcb->port_them; ipaddress ip_me = old_tcb->ip_me; unsigned port_me = old_tcb->port_me; unsigned seqno; /* * First, get another port number and potentially ip address */ { ipaddress prev_ip = ip_me; unsigned prev_port = port_me; _next_IP_port(tcpcon, &ip_me, &port_me); if (ipaddress_is_equal(ip_me, prev_ip) && port_me == prev_port) ERRMSG("There must be multiple source ports/addresses for reconnection\n"); } /* * Calculate the SYN cookie, the same algorithm as for when spewing * SYN packets. However, since we'll probably be using a different * port or IP address, it'll be different in practice. */ seqno = (unsigned)syn_cookie(ip_them, port_them, ip_me, port_me, tcpcon->entropy); /* * Now create a new TCB for this new connection */ new_tcb = tcpcon_create_tcb( tcpcon, ip_me, ip_them, port_me, port_them, seqno+1, 0, 255, stream, secs, usecs); new_tcb->app_state = established; } static void _tcb_seg_close(void *in_tcpcon, void *in_tcb, unsigned secs, unsigned usecs) { struct TCP_ConnectionTable *tcpcon = (struct TCP_ConnectionTable *)in_tcpcon; struct TCP_Control_Block *tcb = (struct TCP_Control_Block *)in_tcb; stack_incoming_tcp(tcpcon, tcb, TCP_WHAT_CLOSE, 0, 0, secs, usecs, tcb->seqno_them, tcb->ackno_them); } /*************************************************************************** ***************************************************************************/ int tcpapi_set_timeout(struct stack_handle_t *socket, unsigned secs, unsigned usecs ) { struct TCP_ConnectionTable *tcpcon = socket->tcpcon; struct TCP_Control_Block *tcb = socket->tcb; if (socket == NULL) return SOCKERR_EBADF; timeouts_add(tcpcon->timeouts, tcb->timeout, offsetof(struct TCP_Control_Block, timeout), TICKS_FROM_TV(socket->secs+secs, socket->usecs + usecs) ); return 0; } /*************************************************************************** ***************************************************************************/ int tcpapi_recv(struct stack_handle_t *socket) { struct TCP_Control_Block *tcb; if (socket == 0 || socket->tcb == 0) return SOCKERR_EBADF; tcb = socket->tcb; switch (tcb->tcpstate) { default: case STATE_ESTABLISHED_SEND: _tcb_change_state_to(socket->tcb, STATE_ESTABLISHED_RECV); break; case STATE_FIN_WAIT1_RECV: _tcb_change_state_to(socket->tcb, STATE_FIN_WAIT1_RECV); break; case STATE_FIN_WAIT1_SEND: _tcb_change_state_to(socket->tcb, STATE_FIN_WAIT1_RECV); break; } return 0; } int tcpapi_send(struct stack_handle_t *socket, const void *buf, size_t length, enum TCP__flags flags) { struct TCP_Control_Block *tcb; if (socket == 0 || socket->tcb == 0) return SOCKERR_EBADF; tcb = socket->tcb; switch (tcb->tcpstate) { case STATE_ESTABLISHED_RECV: _tcb_change_state_to(tcb, STATE_ESTABLISHED_SEND); /*follow through*/ case STATE_ESTABLISHED_SEND: _tcb_seg_send(socket->tcpcon, tcb, buf, length, flags); return 0; default: LOG(1, "TCP app attempted SEND in wrong state\n"); return 1; } } int tcpapi_reconnect(struct stack_handle_t *old_socket, struct ProtocolParserStream *new_stream, unsigned new_app_state) { if (old_socket == 0 || old_socket->tcb == 0) return SOCKERR_EBADF; _do_reconnect(old_socket->tcpcon, old_socket->tcb, new_stream, old_socket->secs, old_socket->usecs, new_app_state); return 0; } unsigned tcpapi_change_app_state(struct stack_handle_t *socket, unsigned new_app_state) { struct TCP_Control_Block *tcb; if (socket == 0 || socket->tcb == 0) return SOCKERR_EBADF; tcb = socket->tcb; //printf("%u --> %u\n", tcb->app_state, new_app_state); tcb->app_state = new_app_state; return new_app_state; } int tcpapi_close(struct stack_handle_t *socket) { if (socket == NULL || socket->tcb == NULL) return SOCKERR_EBADF; _tcb_seg_close(socket->tcpcon, socket->tcb, socket->secs, socket->usecs); return 0; } static bool _tcb_they_have_acked_my_fin(struct TCP_Control_Block *tcb) { if (tcb->segments && tcb->segments->is_fin && tcb->segments->length == 0) { if (tcb->ackno_them >= tcb->segments->seqno + 1) return true; return false; } else return false; } static void _tcb_send_ack(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb) { tcpcon_send_packet(tcpcon, tcb, 0x10, 0, 0); } static int _tcb_seg_recv(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb, const unsigned char *payload, size_t payload_length, unsigned seqno_them, unsigned secs, unsigned usecs, bool is_fin) { /* Special case when packet contains only a FIN */ if (payload_length == 0 && is_fin && (tcb->seqno_them - seqno_them) == 0) { tcb->is_their_fin = 1; tcb->seqno_them += 1; tcb->ackno_me += 1; tcpcon_send_packet(tcpcon, tcb, 0x10/*ACK*/, 0, 0); return 1; } if ((tcb->seqno_them - seqno_them) > payload_length) { LOGSEND(tcb, "peer(ACK) [acknowledge payload 1]"); tcpcon_send_packet(tcpcon, tcb, 0x10 /*ACK*/, 0, 0); return 1; } while (seqno_them != tcb->seqno_them && payload_length) { seqno_them++; payload_length--; payload++; } if (tcb->is_their_fin) { /* payload cannot be received after a FIN */ return 1; } if (payload_length == 0) { tcpcon_send_packet(tcpcon, tcb, 0x10/*ACK*/, 0, 0); return 1; } LOGtcb(tcb, 2, "received %u bytes\n", payload_length); tcb->seqno_them += payload_length + is_fin; tcb->ackno_me += payload_length + is_fin; application_notify(tcpcon, tcb, APP_RECV_PAYLOAD, payload, payload_length, secs, usecs); if (is_fin) tcb->is_their_fin = true; /* Send ack for the data */ _tcb_send_ack(tcpcon, tcb); return 0; } /***************************************************************************** * Handles incoming events, like timeouts and packets, that cause a change * in the TCP control block "state". * * This is the part of the code that implements the famous TCP state-machine * you see drawn everywhere, where they have states like "TIME_WAIT". Only * we don't really have those states. *****************************************************************************/ enum TCB_result stack_incoming_tcp(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb, enum TCP_What what, const unsigned char *payload, size_t payload_length, unsigned secs, unsigned usecs, unsigned seqno_them, unsigned ackno_them) { /* FILTER * Reject out-of-order payloads */ if (payload_length) { /* Wrapping technique: If there is a gap between this * packet and the last one, then it means there is a missing * packet somewhere. In that case, this calculation will * wrap and `payload_offset` will be some huge number in the future. * If there is no gap, then this will be zero. * If there's overlap between this packet and the previous, `payload_offset` * will be a small number less than the `length` of this packet. * If it's a retransmission, the numbers will be the same */ int payload_offset = seqno_them - tcb->seqno_them; if (payload_offset < 0) { /* This is a retrnasmission that we've already acknowledged */ if (payload_offset <= 0 - (int)payload_length) { /* Both begin and end are old, so simply discard it */ return TCB__okay; } else { /* Otherwise shorten the payload */ payload_length += payload_offset; payload -= payload_offset; seqno_them -= payload_offset; assert(payload_length < 2000); } } else if (payload_offset > 0) { /* This is an out-of-order fragment in the future. an important design * of this light-weight stack is that we don't support this, and * force the other side to retransmit such packets */ return TCB__okay; } } /* FILTER: * Reject out-of-order FINs. * Handle duplicate FINs here */ if (what == TCP_WHAT_FIN) { if (seqno_them == tcb->seqno_them - 1) { /* Duplicate FIN, respond with ACK */ LOGtcb(tcb, 1, "duplicate FIN\n"); _tcb_send_ack(tcpcon, tcb); return TCB__okay; } else if (seqno_them != tcb->seqno_them) { /* out of order FIN, so drop it */ LOGtcb(tcb, 1, "out-of-order FIN\n"); return TCB__okay; } } LOGtcb(tcb, 1, "##%s##\n", what_to_string(what)); /* Make sure no connection lasts longer than ~30 seconds */ if (what == TCP_WHAT_TIMEOUT) { if (tcb->when_created + tcpcon->timeout_connection < secs) { LOGip(8, tcb->ip_them, tcb->port_them, "%s \n", "CONNECTION TIMEOUT---"); LOGSEND(tcb, "peer(RST)"); tcpcon_send_packet(tcpcon, tcb, 0x04 /*RST*/, 0, 0); tcpcon_destroy_tcb(tcpcon, tcb, Reason_Timeout); return TCB__destroyed; } } if (what == TCP_WHAT_RST) { LOGSEND(tcb, "tcb(destroy)"); tcpcon_destroy_tcb(tcpcon, tcb, Reason_RST); return TCB__destroyed; } /* * * * * * * */ switch (tcb->tcpstate) { /* TODO: validate any SYNACK is real before sending it here * to the state-machine, by validating that it's acking * something */ case STATE_SYN_SENT: switch (what) { case TCP_WHAT_TIMEOUT: /* We've sent a SYN, but didn't get SYN-ACK, so * send another */ tcb->syns_sent++; /* Send a SYN */ tcpcon_send_packet(tcpcon, tcb, 0x02 /*SYN*/, 0, 0); break; case TCP_WHAT_SYNACK: tcb->seqno_them = seqno_them; tcb->seqno_them_first = seqno_them - 1; tcb->seqno_me = ackno_them; tcb->seqno_me_first = ackno_them - 1; LOGtcb(tcb, 1, "%s connection established\n", what_to_string(what)); /* Send "ACK" to acknowlege their "SYN-ACK" */ _tcb_send_ack(tcpcon, tcb); _tcb_change_state_to(tcb, STATE_ESTABLISHED_RECV); application_notify(tcpcon, tcb, APP_CONNECTED, 0, 0, secs, usecs); break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; case STATE_ESTABLISHED_SEND: switch (what) { case TCP_WHAT_CLOSE: _tcb_seg_send(tcpcon, tcb, 0, 0, TCP__close_fin); _tcb_change_state_to(tcb, STATE_FIN_WAIT1_SEND); break; case TCP_WHAT_FIN: if (seqno_them == tcb->seqno_them) { /* I have ACKed all their data, so therefore process this */ _tcb_seg_recv(tcpcon, tcb, 0, 0, seqno_them, secs, usecs, true); _tcb_change_state_to(tcb, STATE_FIN_WAIT1_SEND); _tcb_send_ack(tcpcon, tcb); } else { /* I haven't received all their data, so ignore it until I do */ _tcb_send_ack(tcpcon, tcb); } break; case TCP_WHAT_ACK: _tcp_seg_acknowledge(tcb, ackno_them); if (tcb->segments == NULL || tcb->segments->length == 0) { /* We've finished sending everything, so switch our application state * back to sending */ _tcb_change_state_to(tcb, STATE_ESTABLISHED_RECV); /* All the payload has been sent. Notify the application of this, so that they * can send more if the want, or switch to listening. */ application_notify(tcpcon, tcb, APP_SEND_SENT, 0, 0, secs, usecs); } break; case TCP_WHAT_TIMEOUT: /* They haven't acknowledged everything yet, so resend the last segment */ _tcb_seg_resend(tcpcon, tcb); break; case TCP_WHAT_DATA: /* We don't receive data while in the sending state. We force them * to keep re-sending it until we are prepared to receive it. This * saves us from having to buffer it in this stack. */ break; case TCP_WHAT_SYNACK: break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; case STATE_ESTABLISHED_RECV: switch (what) { case TCP_WHAT_CLOSE: _tcb_seg_send(tcpcon, tcb, 0, 0, TCP__close_fin); _tcb_change_state_to(tcb, STATE_FIN_WAIT1_RECV); break; case TCP_WHAT_FIN: if (seqno_them == tcb->seqno_them) { /* I have ACKed all their data, so therefore process this */ _tcb_seg_recv(tcpcon, tcb, 0, 0, seqno_them, secs, usecs, true); _tcb_change_state_to(tcb, STATE_CLOSE_WAIT); //_tcb_send_ack(tcpcon, tcb); application_notify(tcpcon, tcb, APP_CLOSE, 0, payload_length, secs, usecs); } else { /* I haven't received all their data, so ignore it until I do */ _tcb_send_ack(tcpcon, tcb); } break; case TCP_WHAT_ACK: _tcp_seg_acknowledge(tcb, ackno_them); break; case TCP_WHAT_TIMEOUT: application_notify(tcpcon, tcb, APP_RECV_TIMEOUT, 0, 0, secs, usecs); break; case TCP_WHAT_DATA: _tcb_seg_recv(tcpcon, tcb, payload, payload_length, seqno_them, secs, usecs, false); break; case TCP_WHAT_SYNACK: /* This happens when a delayed SYN-ACK arrives from the target. * I see these fairly often from host 178.159.37.125. * We are going to make them silent for now, but eventually, keep * statistics about this sort of thing. */ break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; /* SYN-RCVD + FIN = FIN-WAIT-1 ESTAB + FIN = FIN-WAIT-1 +---------+ | FIN | | WAIT-1 | +---------+ FIN-WAIT-1 + FIN --> CLOSING FIN-WAIT-1 + ACK-of-FIN --> FIN-WAIT-2 */ case STATE_FIN_WAIT1_SEND: switch (what) { case TCP_WHAT_FIN: /* Ignore their FIN while in the SENDing state. */ break; case TCP_WHAT_ACK: /* Apply the ack */ if (_tcp_seg_acknowledge(tcb, ackno_them)) { /* Same a in ESTABLISHED_SEND, once they've acknowledged * all reception BEFORE THE FIN, then change the state */ if (tcb->segments == NULL || tcb->segments->length == 0) { /* All the payload has been sent. Notify the application of this, so that they * can send more if the want, or switch to listening. */ _tcb_change_state_to(tcb, STATE_FIN_WAIT1_RECV); application_notify(tcpcon, tcb, APP_SEND_SENT, 0, 0, secs, usecs); } } break; case TCP_WHAT_TIMEOUT: _tcb_seg_resend(tcpcon, tcb); /* also resends FINs */ break; case TCP_WHAT_DATA: /* Ignore any data received while in the SEND state */ break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; case STATE_FIN_WAIT1_RECV: switch (what) { case TCP_WHAT_FIN: _tcb_seg_recv(tcpcon, tcb, 0, 0, seqno_them, secs, usecs, true); _tcb_change_state_to(tcb, STATE_CLOSING); _tcb_send_ack(tcpcon, tcb); application_notify(tcpcon, tcb, APP_CLOSE, 0, 0, secs, usecs); break; case TCP_WHAT_ACK: /* Apply the ack */ if (_tcp_seg_acknowledge(tcb, ackno_them)) { if (_tcb_they_have_acked_my_fin(tcb)) { _tcb_change_state_to(tcb, STATE_FIN_WAIT2); application_notify(tcpcon, tcb, APP_CLOSE, 0, 0, secs, usecs); } } break; case TCP_WHAT_TIMEOUT: _tcb_seg_resend(tcpcon, tcb); /* also recv FIN */ break; case TCP_WHAT_DATA: _tcb_seg_recv(tcpcon, tcb, payload, payload_length, seqno_them, secs, usecs, false); break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; case STATE_CLOSING: switch (what) { case TCP_WHAT_TIMEOUT: tcpcon_destroy_tcb(tcpcon, tcb, Reason_Timeout); return TCB__destroyed; case TCP_WHAT_ACK: _tcp_seg_acknowledge(tcb, ackno_them); if (_tcb_they_have_acked_my_fin(tcb)) { tcpcon_destroy_tcb(tcpcon, tcb, Reason_FIN); return TCB__destroyed; } break; case TCP_WHAT_FIN: /* I've already acknowledged their FIN, but hey, do it again */ _tcb_send_ack(tcpcon, tcb); break; case TCP_WHAT_CLOSE: /* The application this machine has issued a second `tcpapi_close()` request. * This represents a bug in the application process. One place where I see this * when scanning 193.109.9.122:992. * FIXME TODO */ ; /* make this silent for now */ break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; case STATE_FIN_WAIT2: case STATE_TIME_WAIT: switch (what) { case TCP_WHAT_TIMEOUT: /* giving up */ if (tcb->tcpstate == STATE_TIME_WAIT) { tcpcon_destroy_tcb(tcpcon, tcb, Reason_Timeout); return TCB__destroyed; } break; case TCP_WHAT_ACK: break; case TCP_WHAT_FIN: /* Processing incoming FIN as an empty paylaod */ _tcb_seg_recv(tcpcon, tcb, 0, 0, seqno_them, secs, usecs, true); _tcb_change_state_to(tcb, STATE_TIME_WAIT); timeouts_add( tcpcon->timeouts, tcb->timeout, offsetof(struct TCP_Control_Block, timeout), TICKS_FROM_TV(secs+5,usecs) ); break; case TCP_WHAT_SYNACK: case TCP_WHAT_RST: case TCP_WHAT_DATA: break; case TCP_WHAT_CLOSE: /* FIXME: to reach this state, we've already done a close. * FIXME: but this happens twice, because only have * FIXME: a single close function. */ break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; case STATE_CLOSE_WAIT: /* Waiting for app to call `close()` */ switch (what) { case TCP_WHAT_CLOSE: _tcb_seg_send(tcpcon, tcb, 0, 0, TCP__close_fin); _tcb_change_state_to(tcb, STATE_LAST_ACK); break; case TCP_WHAT_TIMEOUT: /* Remind the app that it's waiting for it to be closed */ application_notify(tcpcon, tcb, APP_CLOSE, 0, payload_length, secs, usecs); break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; case STATE_LAST_ACK: switch (what) { case TCP_WHAT_TIMEOUT: /* They haven't acknowledged everything yet, so resend the last segment */ _tcb_seg_resend(tcpcon, tcb); break; case TCP_WHAT_ACK: if (_tcp_seg_acknowledge(tcb, ackno_them)) { tcpcon_destroy_tcb(tcpcon, tcb, Reason_Shutdown); return TCB__destroyed; } break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } break; break; default: ERRMSGip(tcb->ip_them, tcb->port_them, "%s:%s **** UNHANDLED EVENT ****\n", state_to_string(tcb->tcpstate), what_to_string(what)); break; } return TCB__okay; } ================================================ FILE: src/stack-tcp-core.h ================================================ #ifndef PROTO_TCP_H #define PROTO_TCP_H #include "massip-addr.h" #include "stack-queue.h" #include "output.h" #include "util-bool.h" struct Adapter; struct TCP_Control_Block; struct TemplatePacket; struct TCP_ConnectionTable; struct lua_State; struct ProtocolParserStream; #define TCP_SEQNO(px,i) (px[i+4]<<24|px[i+5]<<16|px[i+6]<<8|px[i+7]) #define TCP_ACKNO(px,i) (px[i+8]<<24|px[i+9]<<16|px[i+10]<<8|px[i+11]) #define TCP_FLAGS(px,i) (px[(i)+13]) #define TCP_IS_SYNACK(px,i) ((TCP_FLAGS(px,i) & 0x12) == 0x12) #define TCP_IS_ACK(px,i) ((TCP_FLAGS(px,i) & 0x10) == 0x10) #define TCP_IS_RST(px,i) ((TCP_FLAGS(px,i) & 0x4) == 0x4) #define TCP_IS_FIN(px,i) ((TCP_FLAGS(px,i) & 0x1) == 0x1) /** * [KLUDGE] The 'tcpcon' module doesn't have access to the main configuration, * so specific configuration options have to be sent to it using this * function. */ void tcpcon_set_parameter(struct TCP_ConnectionTable *tcpcon, const char *name, size_t value_length, const void *value); enum http_field_t { http_field_replace, http_field_add, http_field_remove, http_field_method, http_field_url, http_field_version, }; void tcpcon_set_http_header(struct TCP_ConnectionTable *tcpcon, const char *name, size_t value_length, const void *value, enum http_field_t what); void scripting_init_tcp(struct TCP_ConnectionTable *tcpcon, struct lua_State *L); /** * Create a TCP connection table (to store TCP control blocks) with * the desired initial size. * * @param entry_count * A hint about the desired initial size. This should be about twice * the number of outstanding connections, so you should base this number * on your transmit rate (the faster the transmit rate, the more * outstanding connections you'll have). This function will automatically * round this number up to the nearest power of 2, or round it down * if it causes malloc() to not be able to allocate enough memory. * @param entropy * Seed for syn-cookie randomization */ struct TCP_ConnectionTable * tcpcon_create_table( size_t entry_count, struct stack_t *stack, struct TemplatePacket *pkt_template, OUTPUT_REPORT_BANNER report_banner, struct Output *out, unsigned timeout, uint64_t entropy ); void tcpcon_set_banner_flags(struct TCP_ConnectionTable *tcpcon, unsigned is_capture_cert, unsigned is_capture_servername, unsigned is_capture_html, unsigned is_capture_heartbleed, unsigned is_capture_ticketbleed); /** * Gracefully destroy a TCP connection table. This is the last chance for any * partial banners (like HTTP server version) to be sent to the output. At the * end of a scan, you'll see a bunch of banners all at once due to this call. * * @param tcpcon * A TCP connection table created with a matching call to * 'tcpcon_create_table()'. */ void tcpcon_destroy_table(struct TCP_ConnectionTable *tcpcon); void tcpcon_timeouts(struct TCP_ConnectionTable *tcpcon, unsigned secs, unsigned usecs); enum TCP_What { TCP_WHAT_TIMEOUT, TCP_WHAT_SYNACK, TCP_WHAT_RST, TCP_WHAT_FIN, TCP_WHAT_ACK, TCP_WHAT_DATA, TCP_WHAT_CLOSE }; enum TCB_result { TCB__okay, TCB__destroyed }; enum TCB_result stack_incoming_tcp(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *entry, enum TCP_What what, const unsigned char *payload, size_t payload_length, unsigned secs, unsigned usecs, unsigned seqno_them, unsigned ackno_them); /** * Lookup a connection record based on IP/ports. */ struct TCP_Control_Block * tcpcon_lookup_tcb( struct TCP_ConnectionTable *tcpcon, ipaddress ip_src, ipaddress ip_dst, unsigned port_src, unsigned port_dst); /** * Create a new TCB (TCP control block. It's created only in two places, * either because we've initiated an outbound TCP connection, or we've * received incoming SYN-ACK from a probe. */ struct TCP_Control_Block * tcpcon_create_tcb( struct TCP_ConnectionTable *tcpcon, ipaddress ip_src, ipaddress ip_dst, unsigned port_src, unsigned port_dst, unsigned my_seqno, unsigned their_seqno, unsigned ttl, const struct ProtocolParserStream *stream, unsigned secs, unsigned usecs); void tcpcon_send_RST( struct TCP_ConnectionTable *tcpcon, ipaddress ip_me, ipaddress ip_them, unsigned port_me, unsigned port_them, uint32_t seqno_them, uint32_t ackno_them); /** * Send a reset packet back, even if we don't have a TCP connection * table */ void tcp_send_RST( struct TemplatePacket *templ, struct stack_t *stack, ipaddress ip_them, ipaddress ip_me, unsigned port_them, unsigned port_me, unsigned seqno_them, unsigned seqno_me ); #endif ================================================ FILE: src/stub-lua.c ================================================ #define LUAAPI #include "stub-lua.h" #ifdef _MSC_VER #pragma warning(disable: 4133 4113 4047) #endif #if defined(WIN32) #define WIN32_LEAN_AND_MEAN #include #else #include #endif #if defined(__GNUC__) /* Disable MinGW warnings for Windows */ #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" #endif int stublua_init(void) { void *lib = NULL; { #if defined(__APPLE__) static const char *possible_names[] = { "liblua.5.3.5.dylib", "liblua.5.3.dylib", "liblua5.3.dylib", "liblua.dylib", 0 }; #elif defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) static const char *possible_names[] = { "lua53.dll", "lua.dll", 0 }; #else static const char *possible_names[] = { "liblua5.3.so", "liblua5.3.so.0", "liblua5.3.so.0.0.0", 0 }; #endif unsigned i; for (i=0; possible_names[i]; i++) { #if defined(WIN32) lib = LoadLibraryA(possible_names[i]); #else lib = dlopen(possible_names[i], RTLD_LAZY); #endif if (lib) { break; } else { ; } } if (lib == NULL) { fprintf(stderr, "liblua: failed to load Lua shared library\n"); fprintf(stderr, " HINT: you must install Lua library\n"); } } #if defined(WIN32) #define DOLINK(name) \ name = (void (*)())GetProcAddress(lib, #name); \ if (name == NULL) fprintf(stderr, "liblua: %s: failed\n", #name); #else #define DOLINK(name) \ name = dlsym(lib, #name); \ if (name == NULL) fprintf(stderr, "liblua: %s: failed\n", #name); #endif DOLINK(lua_version); DOLINK(lua_close) DOLINK(lua_getfield) DOLINK(lua_getglobal) DOLINK(lua_geti) DOLINK(lua_gettop) DOLINK(lua_isnumber); DOLINK(lua_isstring); DOLINK(lua_iscfunction); DOLINK(lua_isinteger); DOLINK(lua_isuserdata); DOLINK(lua_newthread) DOLINK(lua_newuserdata) DOLINK(lua_pcallk) DOLINK(lua_pushcclosure) DOLINK(lua_pushinteger) DOLINK(lua_pushlstring) DOLINK(lua_pushnumber) DOLINK(lua_pushstring) DOLINK(lua_pushvalue) DOLINK(lua_resume) DOLINK(lua_setfield) DOLINK(lua_setglobal) DOLINK(lua_seti) DOLINK(lua_settop) DOLINK(lua_toboolean) DOLINK(lua_tointegerx) DOLINK(lua_tolstring) DOLINK(lua_tonumberx) DOLINK(lua_type) DOLINK(lua_typename) DOLINK(lua_version) DOLINK(lua_xmove) DOLINK(lua_yieldk) DOLINK(luaL_checkinteger) DOLINK(luaL_checklstring) DOLINK(luaL_checkudata) DOLINK(luaL_len) DOLINK(luaL_loadbufferx) DOLINK(luaL_loadfilex) DOLINK(luaL_loadstring) DOLINK(luaL_newmetatable) DOLINK(luaL_newstate) DOLINK(luaL_openlibs) DOLINK(luaL_ref) DOLINK(luaL_setfuncs) DOLINK(luaL_setmetatable) DOLINK(luaL_unref) return 0; } ================================================ FILE: src/stub-lua.h ================================================ /* Stub declarations, for loading Lua dynamically at runtime using openlib() or LoadLibrary(). */ #ifndef STUB_LUA_H #define STUB_LUA_H #include #include #include /** * Called to load the Lua dynamic library, suh as lua53.dll or lua5.3.so.0, * and link all the function pointers. This must be called before calling any * Lua functions, or the program will crash. */ int stublua_init(void); struct lua_State; typedef struct lua_State lua_State; typedef long long lua_Integer; typedef double lua_Number; typedef unsigned long long lua_Unsigned; typedef ptrdiff_t lua_KContext; typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); typedef int (*lua_CFunction) (lua_State *L); typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg; #define LUA_MULTRET (-1) #define LUAI_MAXSTACK 1000000 #define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) #define LUA_OK 0 #define LUA_YIELD 1 #define LUA_ERRRUN 2 #define LUA_ERRSYNTAX 3 #define LUA_ERRMEM 4 #define LUA_ERRGCMM 5 #define LUA_ERRERR 6 #define LUA_TNONE (-1) #define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUMBER 3 #define LUA_TSTRING 4 #define LUA_TTABLE 5 #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8 #define LUA_NUMTAGS 9 #define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) #define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) #define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) #define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) #define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) #define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) #define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) #define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) #define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) #define lua_pop(L,n) lua_settop(L, -(n)-1) #define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) #define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) #define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) #define lua_tostring(L,i) lua_tolstring(L, (i), NULL) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) #define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) #define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) #define luaL_dofile(L, fn) (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_dostring(L, s) (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) #define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) #define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) #define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) #ifndef LUAAPI #define LUAAPI extern #endif LUAAPI void (*lua_close)(lua_State *L); LUAAPI int (*lua_getfield)(lua_State *L, int idx, const char *k); LUAAPI int (*lua_getglobal)(lua_State *L, const char *name); LUAAPI int (*lua_geti)(lua_State *L, int idx, lua_Integer n); LUAAPI int (*lua_gettop)(lua_State *L); LUAAPI int (*lua_isnumber)(lua_State *L, int idx); LUAAPI int (*lua_isstring)(lua_State *L, int idx); LUAAPI int (*lua_iscfunction)(lua_State *L, int idx); LUAAPI int (*lua_isinteger)(lua_State *L, int idx); LUAAPI int (*lua_isuserdata)(lua_State *L, int idx); LUAAPI lua_State * (*lua_newthread)(lua_State *L); LUAAPI void * (*lua_newuserdata)(lua_State *L, size_t size); LUAAPI int (*lua_pcallk)(lua_State *L, int nargs, int nresults, int errfunc,lua_KContext ctx,lua_KFunction k); LUAAPI void (*lua_pushcclosure)(lua_State *L,lua_CFunction fn, int n); LUAAPI void (*lua_pushinteger)(lua_State *L,lua_Integer n); LUAAPI const char *(*lua_pushlstring)(lua_State *L, const char *s, size_t len); LUAAPI void (*lua_pushnumber)(lua_State *L,lua_Number n); LUAAPI const char *(*lua_pushstring)(lua_State *L, const char *s); LUAAPI void (*lua_pushvalue )(lua_State *L, int idx); LUAAPI int (*lua_resume)(lua_State *L,lua_State *from, int nargs); LUAAPI void (*lua_setfield)(lua_State *L, int idx, const char *k); LUAAPI void (*lua_setglobal)(lua_State *L, const char *name); LUAAPI void (*lua_seti)(lua_State *L, int idx, lua_Integer n); LUAAPI void (*lua_settop)(lua_State *L, int idx); LUAAPI int (*lua_toboolean)(lua_State *L, int idx); LUAAPI lua_Integer (*lua_tointegerx)(lua_State *L, int idx, int *pisnum); LUAAPI const char *(*lua_tolstring)(lua_State *L, int idx, size_t *len); LUAAPI lua_Number (*lua_tonumberx)(lua_State *L, int idx, int *pisnum); LUAAPI int (*lua_type)(lua_State *L, int idx); LUAAPI const char *(*lua_typename)(lua_State *L, int t); LUAAPI const lua_Number *(*lua_version)(lua_State *L); LUAAPI void (*lua_xmove)(lua_State *from,lua_State *to, int n); LUAAPI int (*lua_yieldk)(lua_State *L, int nresults,lua_KContext ctx,lua_KFunction k); LUAAPI lua_Integer (*luaL_checkinteger)(lua_State *L, int arg); LUAAPI const char *(*luaL_checklstring)(lua_State *L, int arg, size_t *len); LUAAPI void * (*luaL_checkudata)(lua_State *L, int ud, const char *tname); LUAAPI lua_Integer (*luaL_len)(lua_State *L, int idx); LUAAPI int (*luaL_loadbufferx)(lua_State *L, const char *buff, size_t size, const char *name, const char *mode); LUAAPI int (*luaL_loadfilex)(lua_State *L, const char *filename, const char *mode); LUAAPI int (*luaL_loadstring)(lua_State *L, const char *s); LUAAPI int (*luaL_newmetatable)(lua_State *L, const char *tname); LUAAPI lua_State * (*luaL_newstate)(void); LUAAPI void (*luaL_openlibs)(lua_State *L); LUAAPI int (*luaL_ref)(lua_State *L, int t); LUAAPI void (*luaL_setfuncs)(lua_State *L, const luaL_Reg *l, int nup); LUAAPI void (*luaL_setmetatable)(lua_State *L, const char *tname); LUAAPI void (*luaL_unref)(lua_State *L, int t, int ref); #endif ================================================ FILE: src/stub-pcap-dlt.h ================================================ #ifndef STUB_PCAP_DLT_H #define STUB_PCAP_DLT_H typedef enum { /* Packets are prefixed by an integer indicating * the protocol type in host-byte-order (4-bytes) * followed by the raw IPv4 or IPv6 header */ PCAP_DLT_NULL = 0, /* Ethernet */ PCAP_DLT_ETHERNET = 1, /* Packets are 'raw' on the network. The first byte * will be the first byte of the IPv4/IPv6 header */ PCAP_DLT_RAW = 12, } pcap_dlt_t; #endif ================================================ FILE: src/stub-pcap.c ================================================ /* Copyright (c) 2007 by Errata Security, All Rights Reserved * Copyright (c) 2017 by Robert David Graham * Programmer(s): Robert David Graham [rdg] */ /* LIBPCAP INTERFACE This VERY MESSY code is a hack to load the 'libpcap' library at runtime rather than compile time. This reason for this mess is that it gets rid of a dependency when compiling this project. Otherwise, developers would have to download the 'libpcap-dev' dependency in order to build this project. Almost every platform these days (OpenBSD, FreeBSD, macOS, Debian, RedHat) comes with a "libpcap.so" library already installed by default with a known BINARY interface. Thus, we can include the data structures definitions directly in this project, then load the library dynamically. For those systems without libpcap.so already installed, the user can either install those on the system, or compile this project in "STATIC" mode, which will link to the static libpcap.a library. */ #include "util-logger.h" #if defined(_MSC_VER) #pragma warning(disable:4115 4201) #pragma warning(disable:4100) /* unreferenced formal parameter */ //#include #endif #include "stub-pcap.h" #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #endif #include #include #include #ifndef UNUSEDPARM #ifdef __GNUC__ #define UNUSEDPARM(x) #else #define UNUSEDPARM(x) x=(x) #endif #endif struct pcap_if { struct pcap_if *next; char *name; /* name to hand to "pcap_open_live()" */ char *description; /* textual description of interface, or NULL */ void *addresses; unsigned flags; /* PCAP_IF_ interface flags */ }; static void seterr(char *errbuf, const char *msg) { size_t length = strlen(msg); if (length > PCAP_ERRBUF_SIZE-1) length = PCAP_ERRBUF_SIZE-1; memcpy(errbuf, msg, length); errbuf[length] = '\0'; } static void null_PCAP_CLOSE(void *hPcap) { #ifdef STATICPCAP pcap_close(hPcap); return; #endif UNUSEDPARM(hPcap); } #ifdef STATICPCAP static pcap_t *(*null_PCAP_CREATE)(const char *source, char *errbuf); static int (*null_PCAP_SET_SNAPLEN)(pcap_t *p, int snaplen); static int (*null_PCAP_SET_PROMISC)(pcap_t *p, int promisc); static int (*null_PCAP_SET_TIMEOUT)(pcap_t *p, int to_ms); static int (*null_PCAP_SET_IMMEDIATE_MODE)(pcap_t *p, int immediate_mode); static int (*null_PCAP_SET_BUFFER_SIZE)(pcap_t *p, int buffer_size); static int (*null_PCAP_SET_RFMON)(pcap_t *p, int rfmon); static int (*null_PCAP_CAN_SET_RFMON)(pcap_t *p); static int (*null_PCAP_ACTIVATE)(pcap_t *p); #else static pcap_t *null_PCAP_CREATE(const char *source, char *errbuf) {return 0;} static int null_PCAP_SET_SNAPLEN(pcap_t *p, int snaplen) {return 0;} static int null_PCAP_SET_PROMISC(pcap_t *p, int promisc) {return 0;} static int null_PCAP_SET_TIMEOUT(pcap_t *p, int to_ms) {return 0;} static int null_PCAP_SET_IMMEDIATE_MODE(pcap_t *p, int immediate_mode) {return 0;} static int null_PCAP_SET_BUFFER_SIZE(pcap_t *p, int buffer_size) {return 0;} static int null_PCAP_SET_RFMON(pcap_t *p, int rfmon) {return 0;} static int null_PCAP_CAN_SET_RFMON(pcap_t *p) {return 0;} static int null_PCAP_ACTIVATE(pcap_t *p) {return 0;} #endif static unsigned null_PCAP_DATALINK(void *hPcap) { #ifdef STATICPCAP return pcap_datalink(hPcap); #endif UNUSEDPARM(hPcap); return 0; } static unsigned null_PCAP_DISPATCH(void *hPcap, unsigned how_many_packets, PCAP_HANDLE_PACKET handler, void *handle_data) { #ifdef STATICPCAP return pcap_dispatch(hPcap, how_many_packets, handler, handle_data); #endif UNUSEDPARM(hPcap);UNUSEDPARM(how_many_packets);UNUSEDPARM(handler);UNUSEDPARM(handle_data); return 0; } static int null_PCAP_FINDALLDEVS(pcap_if_t **alldevs, char *errbuf) { #ifdef STATICPCAP return pcap_findalldevs(alldevs, errbuf); #endif *alldevs = 0; seterr(errbuf, "libpcap not loaded"); return -1; } static void null_PCAP_FREEALLDEVS(pcap_if_t *alldevs) { #ifdef STATICPCAP return pcap_freealldevs(alldevs); #endif UNUSEDPARM(alldevs); return; } static char *null_PCAP_LOOKUPDEV(char *errbuf) { #ifdef STATICPCAP return pcap_lookupdev(errbuf); #endif seterr(errbuf, "libpcap not loaded"); return ""; } static void * null_PCAP_OPEN_LIVE(const char *devicename, unsigned snap_length, unsigned is_promiscuous, unsigned read_timeout, char *errbuf) { #ifdef STATICPCAP return pcap_open_live(devicename, snap_length, is_promiscuous, read_timeout, errbuf); #endif seterr(errbuf, "libpcap not loaded"); UNUSEDPARM(devicename);UNUSEDPARM(snap_length);UNUSEDPARM(is_promiscuous);UNUSEDPARM(read_timeout); return NULL; } static int null_PCAP_MAJOR_VERSION(void *p) { #ifdef STATICPCAP return pcap_major_version(p); #endif UNUSEDPARM(p); return 0; } static int null_PCAP_MINOR_VERSION(void *p) { #ifdef STATICPCAP return pcap_minor_version(p); #endif UNUSEDPARM(p); return 0; } static const char *null_PCAP_LIB_VERSION(void) { #ifdef STATICPCAP return pcap_lib_version(); #endif return "stub/0.0"; } struct PcapFunctions PCAP = { 0,0,0,0,0, null_PCAP_CLOSE, }; static void *my_null(int x, ...) { UNUSEDPARM(x); printf("%.*s", 0, "a"); /* Remove warnings about no effects */ return 0; } static pcap_t *null_PCAP_OPEN_OFFLINE(const char *fname, char *errbuf) { #ifdef STATICPCAP return pcap_open_offline(fname, errbuf); #endif return my_null(2, fname, errbuf); } static int null_PCAP_SENDPACKET(pcap_t *p, const unsigned char *buf, int size) { #ifdef STATICPCAP return pcap_sendpacket(p, buf, size); #endif my_null(3, p, buf, size); return 0; } static const unsigned char *null_PCAP_NEXT(pcap_t *p, struct pcap_pkthdr *h) { #ifdef STATICPCAP return pcap_next(p, h); #endif my_null(3, p, h); return 0; } static int null_PCAP_SETDIRECTION(pcap_t *p, pcap_direction_t d) { #ifdef STATICPCAP return pcap_setdirection(p, d); #endif my_null(2, p, d); return 0; } static const char *null_PCAP_DATALINK_VAL_TO_NAME(int dlt) { #ifdef STATICPCAP return pcap_datalink_val_to_name(dlt); #endif my_null(1, dlt); return 0; } static void null_PCAP_PERROR(pcap_t *p, char *prefix) { #ifdef STATICPCAP pcap_perror(p, prefix); return; #endif UNUSEDPARM(p); fprintf(stderr, "%s\n", prefix); perror("pcap"); } static const char*null_PCAP_GETERR(pcap_t *p) { #ifdef STATICPCAP return pcap_geterr(p); #endif UNUSEDPARM(p); return "(unknown)"; } static const char *null_PCAP_DEV_NAME(const pcap_if_t *dev) { return dev->name; } static const char *null_PCAP_DEV_DESCRIPTION(const pcap_if_t *dev) { return dev->description; } static const pcap_if_t *null_PCAP_DEV_NEXT(const pcap_if_t *dev) { return dev->next; } /* * Some Windows-specific functions to improve speed */ #if defined(WIN32) static pcap_send_queue *null_PCAP_SENDQUEUE_ALLOC(size_t size) { UNUSEDPARM(size); return 0; } static unsigned null_PCAP_SENDQUEUE_TRANSMIT(pcap_t *p, pcap_send_queue *queue, int sync) { my_null(3, p, queue, sync); return 0; } static void null_PCAP_SENDQUEUE_DESTROY(pcap_send_queue *queue) { my_null(1, queue); UNUSEDPARM(queue); } static int null_PCAP_SENDQUEUE_QUEUE(pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const unsigned char *pkt_data) { my_null(4, queue, pkt_header, pkt_data); return 0; } #endif /*WIN32*/ /** * Runtime-load the libpcap shared-object or the winpcap DLL. We * load at runtime rather than loadtime to allow this program to * be used to process offline content, and to provide more helpful * messages to people who don't realize they need to install PCAP. */ int pcap_init(void) { struct PcapFunctions *pl = &PCAP; #ifdef WIN32 void * hPacket; void * hLibpcap; pl->is_available = 0; pl->is_printing_debug = 1; /* Look for the Packet.dll */ hPacket = LoadLibraryA("NPcap\\Packet.dll"); if (hPacket == NULL) hPacket = LoadLibraryA("Packet.dll"); if (hPacket == NULL) { if (pl->is_printing_debug) switch (GetLastError()) { case ERROR_MOD_NOT_FOUND: fprintf(stderr, "%s: not found\n", "Packet.dll"); fprintf(stderr, " HINT: you must install either WinPcap or Npcap\n"); return -1; default: fprintf(stderr, "%s: couldn't load %d\n", "Packet.dll", (int)GetLastError()); return -1; } } /* Look for the winpcap.dll */ hLibpcap = LoadLibraryA("Npcap\\wpcap.dll"); if (hLibpcap == NULL) hLibpcap = LoadLibraryA("wpcap.dll"); if (hLibpcap == NULL) { if (pl->is_printing_debug) fprintf(stderr, "%s: couldn't load %d\n", "wpcap.dll", (int)GetLastError()); return -1; } #define DOLINK(PCAP_DATALINK, datalink) \ pl->datalink = (PCAP_DATALINK)GetProcAddress(hLibpcap, "pcap_"#datalink); \ if (pl->datalink == NULL) pl->func_err=1, pl->datalink = null_##PCAP_DATALINK; #endif #ifndef WIN32 #ifndef STATICPCAP void *hLibpcap; pl->is_available = 0; pl->is_printing_debug = 1; { static const char *possible_names[] = { "libpcap.so", "libpcap.A.dylib", "libpcap.dylib", "libpcap.so.1", "libpcap.so.0.9.5", "libpcap.so.0.9.4", "libpcap.so.0.8", 0 }; unsigned i; for (i=0; possible_names[i]; i++) { hLibpcap = dlopen(possible_names[i], RTLD_LAZY); if (hLibpcap) { LOG(1, "[+] pcap: found library: %s\n", possible_names[i]); break; } else { LOG(1, "[-] pcap: failed to load: %s\n", possible_names[i]); } } if (hLibpcap == NULL) { LOG(0, "[-] FAIL: failed to load libpcap shared library\n"); LOG(0, " [hint]: you must install libpcap or WinPcap\n"); } } #define DOLINK(PCAP_DATALINK, datalink) \ pl->datalink = (PCAP_DATALINK)dlsym(hLibpcap, "pcap_"#datalink); \ if (pl->datalink == NULL) LOG(1, "pcap: pcap_%s: failed\n", #datalink); \ if (pl->datalink == NULL) pl->func_err=1, pl->datalink = null_##PCAP_DATALINK; #else #define DOLINK(PCAP_DATALINK, datalink) \ pl->func_err=0, pl->datalink = null_##PCAP_DATALINK; #endif #endif DOLINK(PCAP_CLOSE , close); DOLINK(PCAP_DATALINK , datalink); DOLINK(PCAP_DISPATCH , dispatch); DOLINK(PCAP_FINDALLDEVS , findalldevs); DOLINK(PCAP_FREEALLDEVS , freealldevs); DOLINK(PCAP_LIB_VERSION , lib_version); DOLINK(PCAP_LOOKUPDEV , lookupdev); DOLINK(PCAP_MAJOR_VERSION , major_version); DOLINK(PCAP_MINOR_VERSION , minor_version); DOLINK(PCAP_OPEN_LIVE , open_live); DOLINK(PCAP_OPEN_OFFLINE , open_offline); DOLINK(PCAP_SENDPACKET , sendpacket); DOLINK(PCAP_NEXT , next); DOLINK(PCAP_SETDIRECTION , setdirection); DOLINK(PCAP_DATALINK_VAL_TO_NAME , datalink_val_to_name); DOLINK(PCAP_PERROR , perror); DOLINK(PCAP_GETERR , geterr); /* pseudo functions that don't exist in the libpcap interface */ pl->dev_name = null_PCAP_DEV_NAME; pl->dev_description = null_PCAP_DEV_DESCRIPTION; pl->dev_next = null_PCAP_DEV_NEXT; /* windows-only functions that might improve speed */ #if defined(WIN32) DOLINK(PCAP_SENDQUEUE_ALLOC , sendqueue_alloc); DOLINK(PCAP_SENDQUEUE_TRANSMIT , sendqueue_transmit); DOLINK(PCAP_SENDQUEUE_DESTROY , sendqueue_destroy); DOLINK(PCAP_SENDQUEUE_QUEUE , sendqueue_queue); #endif DOLINK(PCAP_CREATE , create); DOLINK(PCAP_SET_SNAPLEN , set_snaplen); DOLINK(PCAP_SET_PROMISC , set_promisc); DOLINK(PCAP_SET_TIMEOUT , set_timeout); DOLINK(PCAP_SET_IMMEDIATE_MODE , set_immediate_mode); DOLINK(PCAP_SET_BUFFER_SIZE , set_buffer_size); DOLINK(PCAP_SET_RFMON , set_rfmon); DOLINK(PCAP_CAN_SET_RFMON , can_set_rfmon); DOLINK(PCAP_ACTIVATE , activate); if (!pl->func_err) pl->is_available = 1; else pl->is_available = 0; return 0; } ================================================ FILE: src/stub-pcap.h ================================================ /* Dynamically load libpcap at runtime This library optionally loads the 'libpcap' library at runtime, rather than statically linked at compile time. The advantage of this is that the user can build this project with no dependencies -- although they may require this dependency in order to run the program. As of 2017, libpcap shared libraries are standard on major Linux distributions (Debian, Readhat), FreeBSD, OpenBSD, and macOS. On Windows, "winpcap" must be downloaded. */ #ifndef STUB_PCAP_H #define STUB_PCAP_H #include /* Including the right ".h" file to define "timeval" is difficult, so instead * so instead we are simply going to define our own structure. This should * match the binary definition within the operating system */ #if __NetBSD__ #include #define pcap_timeval timeval #elif __OpenBSD__ #include #define pcap_timeval bpf_timeval #else struct pcap_timeval { long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ }; #endif /* Forward reference of opaque 'pcap_t' structure */ struct pcap; typedef struct pcap pcap_t; /* Forward reference of opaque 'pcap_if_t' structure */ struct pcap_if; typedef struct pcap_if pcap_if_t; /* How many bytes to reserve for error messages. This is the number specified * in libpcap, smaller numbers can crash */ enum { PCAP_ERRBUF_SIZE=256, }; /* used in pcap_setdirection() */ typedef enum { PCAP_D_INOUT = 0, PCAP_D_IN = 1, PCAP_D_OUT = 2, } pcap_direction_t; enum { PCAP_ERROR = -1, PCAP_ERROR_BREAK = -2, PCAP_ERROR_NOT_ACTIVATED = -3, PCAP_ERROR_ACTIVATED = -4, PCAP_ERROR_NO_SUCH_DEVICE = -5, PCAP_ERROR_RFMON_NOTSUP = -6, PCAP_ERROR_NOT_RFMON = -7, PCAP_ERROR_PERM_DENIED = -8, PCAP_ERROR_IFACE_NOT_UP = -9, PCAP_ERROR_CANTSET_TSTAMP_TYPE = -10, PCAP_ERROR_PROMISC_PERM_DENIED = -11, PCAP_ERROR_TSTAMP_PRECISION_NOTSUP = -12, /* warnings, not errors */ PCAP_WARNING = 1, PCAP_WARNING_PROMISC_NOTSUP = 2, PCAP_WARNING_TSTAMP_TYPE_NOTSUP = 3, }; /* The packet header for capturing packets. Apple macOS inexplicably adds * an extra comment-field onto the end of this, so the definition needs * to be careful to match the real definition */ struct pcap_pkthdr { struct pcap_timeval ts; unsigned caplen; unsigned len; #ifdef __APPLE__ char comment[256]; #endif }; /* * This block is for function declarations. Consult the libpcap * documentation for what these functions really mean */ typedef void (*PCAP_HANDLE_PACKET)(unsigned char *v_seap, const struct pcap_pkthdr *framehdr, const unsigned char *buf); typedef void (*PCAP_CLOSE)(void *hPcap); typedef unsigned (*PCAP_DATALINK)(void *hPcap); typedef unsigned (*PCAP_DISPATCH)(void *hPcap, unsigned how_many_packets, PCAP_HANDLE_PACKET handler, void *handle_data); typedef int (*PCAP_FINDALLDEVS)(pcap_if_t **alldevs, char *errbuf); typedef const char *(*PCAP_LIB_VERSION)(void); typedef char * (*PCAP_LOOKUPDEV)(char *errbuf); typedef int (*PCAP_MAJOR_VERSION)(void *p); typedef int (*PCAP_MINOR_VERSION)(void *p); typedef void * (*PCAP_OPEN_LIVE)(const char *devicename, unsigned snap_length, unsigned is_promiscuous, unsigned read_timeout, char *errbuf); typedef void (*PCAP_FREEALLDEVS)(pcap_if_t *alldevs); typedef pcap_t * (*PCAP_OPEN_OFFLINE)(const char *fname, char *errbuf); typedef int (*PCAP_SENDPACKET)(pcap_t *p, const unsigned char *buf, int size); typedef const unsigned char *(*PCAP_NEXT)(pcap_t *p, struct pcap_pkthdr *h); typedef int (*PCAP_SETDIRECTION)(pcap_t *, pcap_direction_t); typedef const char *(*PCAP_DATALINK_VAL_TO_NAME)(int dlt); typedef void (*PCAP_PERROR)(pcap_t *p, char *prefix); typedef const char *(*PCAP_GETERR)(pcap_t *p); typedef const char *(*PCAP_DEV_NAME)(const pcap_if_t *dev); typedef const char *(*PCAP_DEV_DESCRIPTION)(const pcap_if_t *dev); typedef const pcap_if_t *(*PCAP_DEV_NEXT)(const pcap_if_t *dev); /* pcap_open() replaced with a series of calls to: p = pcap_create(device, errbuf); pcap_set_snaplen(p, snaplen); pcap_set_promisc(p, promisc); pcap_set_timeout(p, to_ms); pcap_activate(p); */ typedef pcap_t *(*PCAP_CREATE)(const char *source, char *errbuf); typedef int (*PCAP_SET_SNAPLEN)(pcap_t *p, int snaplen); typedef int (*PCAP_SET_PROMISC)(pcap_t *p, int promisc); typedef int (*PCAP_SET_TIMEOUT)(pcap_t *p, int to_ms); typedef int (*PCAP_SET_IMMEDIATE_MODE)(pcap_t *p, int immediate_mode); typedef int (*PCAP_SET_BUFFER_SIZE)(pcap_t *p, int buffer_size); typedef int (*PCAP_SET_RFMON)(pcap_t *p, int rfmon); typedef int (*PCAP_CAN_SET_RFMON)(pcap_t *p); typedef int (*PCAP_ACTIVATE)(pcap_t *p); /* * PORTABILITY: Windows supports the "sendq" feature, and is really slow * without this feature. It's not needed on Linux, so we just create * equivalent functions that do nothing */ struct pcap_send_queue; typedef struct pcap_send_queue pcap_send_queue; typedef pcap_send_queue *(*PCAP_SENDQUEUE_ALLOC)(size_t size); typedef unsigned (*PCAP_SENDQUEUE_TRANSMIT)(pcap_t *p, pcap_send_queue *queue, int sync); typedef void (*PCAP_SENDQUEUE_DESTROY)(pcap_send_queue *queue); typedef int (*PCAP_SENDQUEUE_QUEUE)(pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const unsigned char *pkt_data); struct PcapFunctions { unsigned func_err:1; unsigned is_available:1; unsigned is_printing_debug:1; unsigned status; unsigned errcode; PCAP_CLOSE close; PCAP_DATALINK datalink; PCAP_DISPATCH dispatch; PCAP_FINDALLDEVS findalldevs; PCAP_FREEALLDEVS freealldevs; PCAP_LOOKUPDEV lookupdev; PCAP_LIB_VERSION lib_version; PCAP_MAJOR_VERSION major_version; PCAP_MINOR_VERSION minor_version; PCAP_OPEN_LIVE open_live; PCAP_OPEN_OFFLINE open_offline; PCAP_SENDPACKET sendpacket; PCAP_NEXT next; PCAP_SETDIRECTION setdirection; PCAP_DATALINK_VAL_TO_NAME datalink_val_to_name; PCAP_PERROR perror; PCAP_GETERR geterr; /* Accessor functions for opaque data structure, don't really * exist in libpcap */ PCAP_DEV_NAME dev_name; PCAP_DEV_DESCRIPTION dev_description; PCAP_DEV_NEXT dev_next; /* Windows-only functions */ PCAP_SENDQUEUE_ALLOC sendqueue_alloc; PCAP_SENDQUEUE_TRANSMIT sendqueue_transmit; PCAP_SENDQUEUE_DESTROY sendqueue_destroy; PCAP_SENDQUEUE_QUEUE sendqueue_queue; PCAP_CREATE create; PCAP_SET_SNAPLEN set_snaplen; PCAP_SET_PROMISC set_promisc; PCAP_SET_TIMEOUT set_timeout; PCAP_SET_IMMEDIATE_MODE set_immediate_mode; PCAP_SET_BUFFER_SIZE set_buffer_size; PCAP_SET_RFMON set_rfmon; PCAP_CAN_SET_RFMON can_set_rfmon; PCAP_ACTIVATE activate; }; /** * This is global structure containing all the libpcap function pointers. * use in the form "PCAP.functionname()" rather than "pcap_functioname()". */ extern struct PcapFunctions PCAP; /** * Dynamically loads the shared library (libpcap.so, libpcap.dynlib, * or libpcap.dll. Call this during program startup like main() in order * to load the libraries. Not thread safe, so call from the startup * thread, but not within threads. * @return * 0 on success or * -1 on failure */ int pcap_init(void); #endif ================================================ FILE: src/stub-pfring.c ================================================ /* PF_RING compatibility layer In order to avoid special build hassle, this code links to PF_RING at runtime instead compile-time. That means you can compile this code BEFORE installing and building PF_RING. */ #include "stub-pfring.h" #include "util-safefunc.h" #include "util-logger.h" struct PFRING PFRING; #if defined(__linux__) #include #endif /*************************************************************************** * This checks whether the "pf_ring" driver is installed. ***************************************************************************/ int PFRING_is_installed(void); int PFRING_is_installed(void) { #if defined(__linux__) FILE *fp; char line[256]; int found = 0; fp = fopen("/proc/modules", "rb"); if (fp == NULL) return 0; while (fgets(line, sizeof(line), fp)) { if (memcmp(line, "pf_ring ", 8) == 0) { found = 1; LOG(2, "pfring: found 'pf_ring' driver\n"); } if (memcmp(line, "ixgbe ", 6) == 0) { LOG(2, "pfring: found 'ixgbe' driver\n"); } if (memcmp(line, "e1000e ", 8) == 0) { LOG(2, "pfring: found 'e1000e' driver\n"); } } fclose(fp); return found; #else return 0; #endif } /*************************************************************************** ***************************************************************************/ int PFRING_init(void) { #if defined(__linux__) void *h; int err = 0; LOG(6, "pfring: initializing subsystem\n"); LOG(6, "pfring: looking for 'libpfring.so'\n"); h = dlopen("libpfring.so", RTLD_LAZY); if (h == NULL) { LOG(2, "pfring: error: dlopen('libpfring.so'): %s\n", strerror(errno)); return 0; } else LOG(2, "pfring: found 'libpfring.so'!\n"); #define LOADSYM(name) if ((PFRING.name = dlsym(h, "pfring_"#name)) == 0) {LOG(2, "pfring_%s: not found in 'libpfring.so': %s\n", #name, strerror(errno));err=1;} LOADSYM(open); LOADSYM(close); LOADSYM(enable_ring); LOADSYM(send); LOADSYM(recv); LOADSYM(poll); LOADSYM(version); LOADSYM(set_direction); LOADSYM(set_application_name); //LOADSYM(get_bound_device); if (err) { memset(&PFRING, 0, sizeof(PFRING)); LOG(2, "pfring: failed to load\n"); } else { LOG(2, "pfring: successfully loaded PF_RING API\n"); if (!PFRING_is_installed()) { LOG(0, "pfring: ERROR: 'pf_ring' driver module not found!!!!!\n"); } else LOG(2, "pfring: found 'pf_ring' driver module\n"); } #endif return 0; } ================================================ FILE: src/stub-pfring.h ================================================ #ifndef RAWSOCK_PFRING_H #define RAWSOCK_PFRING_H #include #include /* * Various PF_RING defines */ struct __pfring; typedef struct __pfring pfring; typedef enum { rx_and_tx_direction = 0, rx_only_direction, tx_only_direction } packet_direction; struct pfring_pkthdr { struct xtimeval { long tv_sec; long tv_usec; } ts; unsigned caplen; unsigned len; /* only filled in if PF_RING_LONG_HEADER set */ unsigned char extended_hdr[512]; }; #define PF_RING_ERROR_GENERIC -1 #define PF_RING_ERROR_INVALID_ARGUMENT -2 #define PF_RING_ERROR_NO_PKT_AVAILABLE -3 #define PF_RING_ERROR_NO_TX_SLOT_AVAILABLE -4 #define PF_RING_ERROR_WRONG_CONFIGURATION -5 #define PF_RING_ERROR_END_OF_DEMO_MODE -6 #define PF_RING_ERROR_NOT_SUPPORTED -7 #define PF_RING_ERROR_INVALID_LIB_VERSION -8 #define PF_RING_ERROR_UNKNOWN_ADAPTER -9 #define PF_RING_ERROR_NOT_ENOUGH_MEMORY -10 #define PF_RING_ERROR_INVALID_STATUS -11 #define PF_RING_DNA_SYMMETRIC_RSS 1 << 0 #define PF_RING_REENTRANT 1 << 1 #define PF_RING_LONG_HEADER 1 << 2 #define PF_RING_PROMISC 1 << 3 #define PF_RING_TIMESTAMP 1 << 4 #define PF_RING_HW_TIMESTAMP 1 << 5 #define PF_RING_RX_PACKET_BOUNCE 1 << 6 #define PF_RING_DNA_FIXED_RSS_Q_0 1 << 7 #define PF_RING_STRIP_HW_TIMESTAMP 1 << 8 #define PF_RING_DO_NOT_PARSE 1 << 9 /* parsing already disabled in zero-copy */ #define PF_RING_DO_NOT_TIMESTAMP 1 << 10 /* sw timestamp already disabled in zero-copy */ /* * function prototypes */ typedef pfring*(*PFRING_OPEN)( const char *device_name, unsigned caplen, unsigned flags); typedef void (*PFRING_CLOSE)(pfring *ring); typedef int (*PFRING_ENABLE_RING)(pfring *ring); typedef int (*PFRING_SEND)( pfring *ring, const unsigned char *buffer, unsigned buffer_length, unsigned char flush_packet); typedef int (*PFRING_RECV)( pfring *ring, unsigned char** buffer, unsigned buffer_length, struct pfring_pkthdr *hdr, unsigned char wait_for_incoming_packet); typedef int (*PFRING_POLL)(pfring *ring, unsigned wait_duration); typedef int (*PFRING_VERSION)(pfring *ring, unsigned *version); typedef int (*PFRING_SET_DIRECTION)(pfring *ring, int direction); typedef int (*PFRING_SET_APPLICATION_NAME)(pfring *ring, char *name); typedef int (*PFRING_GET_BOUND_DEVICE)(pfring *ring, unsigned char mac_address[6]); /* * scoped object */ extern struct PFRING { PFRING_OPEN open; PFRING_CLOSE close; PFRING_ENABLE_RING enable_ring; PFRING_SEND send; PFRING_RECV recv; PFRING_POLL poll; PFRING_VERSION version; PFRING_SET_DIRECTION set_direction; PFRING_SET_APPLICATION_NAME set_application_name; PFRING_GET_BOUND_DEVICE get_bound_device; } PFRING; /* * call this to load the library */ int PFRING_init(void); #endif ================================================ FILE: src/syn-cookie.c ================================================ #include "syn-cookie.h" #include "pixie-timer.h" #include "util-safefunc.h" #include "crypto-siphash24.h" #include #include #include #if defined(_MSC_VER) #include #endif /*************************************************************************** * Go gather some entropy (aka. randmoness) to seed hashing with. * * NOTE: Mostly it's here to amuse cryptographers with its lulz. ***************************************************************************/ uint64_t get_entropy(void) { uint64_t entropy[2] = {0,0}; unsigned i; /* * Gather some random bits */ for (i=0; i<64; i++) { FILE *fp; entropy[0] += pixie_nanotime(); #if defined(_MSC_VER) entropy[0] ^= __rdtsc(); #endif time(0); fp = fopen("/", "r"); entropy[1] <<= 1; entropy[1] |= entropy[0]>>63; entropy[0] <<= 1; if (fp) { fclose(fp); } } entropy[0] ^= time(0); /* Always try to open this file, even on platforms like Windows * where it's not going to exist. */ { FILE *fp; fp = fopen("/dev/urandom", "r"); if (fp) { size_t x; uint64_t urand = 0; x = fread(&urand, 1, sizeof(urand), fp); entropy[0] ^= urand; entropy[0] ^= x; x = fread(&urand, 1, sizeof(urand), fp); entropy[1] ^= urand; entropy[1] ^= x; fclose(fp); } entropy[0] ^= pixie_nanotime(); } return entropy[0] ^ entropy[1]; } #if 0 /*************************************************************************** * This implements the "murmur" hash function. ***************************************************************************/ static unsigned murmur(uint64_t entropy, ...) { /* reference: * http://en.wikipedia.org/wiki/MurmurHash */ static const unsigned c1 = 0xcc9e2d51; static const unsigned c2 = 0x1b873593; unsigned r1 = 15; unsigned r2 = 13; unsigned m = 5; unsigned n = 0xe6546b64; va_list key; unsigned len; unsigned hash = (unsigned)entropy; va_start(key, entropy); for (len=0; len<2; len++) { unsigned k = va_arg(key, unsigned); k = k * c1; k = (k << r1) | (k >> (32-r1)); k = k * c2; hash = hash ^ k; hash = (hash << r2) | (hash >> (32-r2)); hash = hash * m + n; } hash = hash ^ (len*4); hash = hash ^ (hash >> 16); hash = hash * 0x85ebca6b; hash = hash ^ (hash >> 13); hash = hash * 0xc2b2ae35; hash = hash ^ (hash >> 16); return hash; } #endif /*************************************************************************** ***************************************************************************/ uint64_t syn_cookie( ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me, uint64_t entropy) { switch (ip_them.version) { case 4: return syn_cookie_ipv4(ip_them.ipv4, port_them, ip_me.ipv4, port_me, entropy); case 6: return syn_cookie_ipv6(ip_them.ipv6, port_them, ip_me.ipv6, port_me, entropy); default: assert(!"unknown ip version"); return 0; } } /*************************************************************************** ***************************************************************************/ uint64_t syn_cookie_ipv4( unsigned ip_them, unsigned port_them, unsigned ip_me, unsigned port_me, uint64_t entropy) { unsigned data[4]; uint64_t x[2]; x[0] = entropy; x[1] = entropy; data[0] = ip_them; data[1] = port_them; data[2] = ip_me; data[3] = port_me; return siphash24(data, sizeof(data), x); } /*************************************************************************** ***************************************************************************/ uint64_t syn_cookie_ipv6( ipv6address ip_them, unsigned port_them, ipv6address ip_me, unsigned port_me, uint64_t entropy) { uint64_t data[5]; uint64_t x[2]; x[0] = entropy; x[1] = entropy; data[0] = ip_them.hi; data[1] = ip_them.lo; data[2] = ip_me.hi; data[3] = ip_me.lo; data[4] = port_them<<16ULL | port_me; return siphash24(data, sizeof(data), x); } ================================================ FILE: src/syn-cookie.h ================================================ #ifndef SYN_COOKIE_H #define SYN_COOKIE_H #include #include "massip-addr.h" /** * Create a hash of the src/dst IP/port combination. This allows us to match * incoming responses with their original requests */ uint64_t syn_cookie_ipv4( unsigned ip_dst, unsigned port_dst, unsigned ip_src, unsigned port_src, uint64_t entropy); uint64_t syn_cookie( ipaddress ip_dst, unsigned port_dst, ipaddress ip_src, unsigned port_src, uint64_t entropy); uint64_t syn_cookie_ipv6( ipv6address ip_dst, unsigned port_dst, ipv6address ip_src, unsigned port_src, uint64_t entropy); /** * Called on startup to set a secret key */ uint64_t get_entropy(void); #endif ================================================ FILE: src/templ-nmap-payloads.c ================================================ #include "templ-nmap-payloads.h" #include "massip-port.h" #include "massip-rangesv4.h" #include #include #include /*************************************************************************** * remove leading/trailing whitespace ***************************************************************************/ static void trim(char *line, size_t sizeof_line) { if (sizeof_line > strlen(line)) sizeof_line = strlen(line); while (isspace(*line & 0xFF)) memmove(line, line+1, sizeof_line--); while (isspace(line[sizeof_line-1] & 0xFF)) line[--sizeof_line] = '\0'; } /*************************************************************************** ***************************************************************************/ static int is_comment(const char *line) { if (line[0] == '#' || line[0] == '/' || line[0] == ';') return 1; else return 0; } /*************************************************************************** ***************************************************************************/ static char * get_next_line(FILE *fp, unsigned *line_number, char *line, size_t sizeof_line) { if (line[0] != '\0') return line; for (;;) { char *p; p = fgets(line, (unsigned)sizeof_line, fp); if (p == NULL) { line[0] = '\0'; return NULL; } (*line_number)++; trim(line, sizeof_line); if (is_comment(line)) continue; if (line[0] == '\0') continue; return line; } } /*************************************************************************** ***************************************************************************/ static void append_byte(unsigned char *buf, size_t *buf_length, size_t buf_max, unsigned c) { if (*buf_length < buf_max) buf[(*buf_length)++] = (unsigned char)c; } /*************************************************************************** ***************************************************************************/ static int isodigit(int c) { if ('0' <= c && c <= '7') return 1; else return 0; } /*************************************************************************** ***************************************************************************/ static unsigned hexval(unsigned c) { if ('0' <= c && c <= '9') return c - '0'; if ('a' <= c && c <= 'f') return c - 'a' + 10; if ('A' <= c && c <= 'F') return c - 'A' + 10; return 0; } /*************************************************************************** ***************************************************************************/ static const char * parse_c_string(unsigned char *buf, size_t *buf_length, size_t buf_max, const char *line) { size_t offset; if (*line != '\"') return line; else offset = 1; while (line[offset] && line[offset] != '\"') { if (line[offset] == '\\') { offset++; switch (line[offset]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { unsigned val = 0; if (isodigit(line[offset])) val = val * 8 + hexval(line[offset++]); if (isodigit(line[offset])) val = val * 8 + hexval(line[offset++]); if (isodigit(line[offset])) val = val * 8 + hexval(line[offset++]); append_byte(buf, buf_length, buf_max, val); continue; } break; case 'x': offset++; { unsigned val = 0; if (isxdigit(line[offset])) val = val * 16 + hexval(line[offset++]); if (isxdigit(line[offset])) val = val * 16 + hexval(line[offset++]); append_byte(buf, buf_length, buf_max, val); continue; } break; case 'a': append_byte(buf, buf_length, buf_max, '\a'); break; case 'b': append_byte(buf, buf_length, buf_max, '\b'); break; case 'f': append_byte(buf, buf_length, buf_max, '\f'); break; case 'n': append_byte(buf, buf_length, buf_max, '\n'); break; case 'r': append_byte(buf, buf_length, buf_max, '\r'); break; case 't': append_byte(buf, buf_length, buf_max, '\t'); break; case 'v': append_byte(buf, buf_length, buf_max, '\v'); break; default: case '\\': append_byte(buf, buf_length, buf_max, line[offset]); break; } } else append_byte(buf, buf_length, buf_max, line[offset]); offset++; } if (line[offset] == '\"') offset++; return line + offset; } /*************************************************************************** * Called during processing of the "--nmap-payloads " directive. ***************************************************************************/ void read_nmap_payloads(FILE *fp, const char *filename, struct PayloadsUDP *payloads, payloads_datagram_add_cb add_payload ) { char line[16384]; unsigned line_number = 0; line[0] = '\0'; for (;;) { unsigned is_error = 0; const char *p; struct RangeList ports[1] = {{0}}; unsigned source_port = 0x10000; unsigned char buf[1500] = {0}; size_t buf_length = 0; memset(ports, 0, sizeof(ports[0])); /* [UDP] */ if (!get_next_line(fp, &line_number, line, sizeof(line))) break; if (memcmp(line, "udp", 3) != 0) { fprintf(stderr, "%s:%u: syntax error, expected \"udp\".\n", filename, line_number); goto end; } else memmove(line, line+3, strlen(line)); trim(line, sizeof(line)); /* [ports] */ if (!get_next_line(fp, &line_number, line, sizeof(line))) break; p = rangelist_parse_ports(ports, line, &is_error, 0); if (is_error) { fprintf(stderr, "%s:%u: syntax error, expected ports\n", filename, line_number); goto end; } memmove(line, p, strlen(p)+1); trim(line, sizeof(line)); /* [C string] */ for (;;) { trim(line, sizeof(line)); if (!get_next_line(fp, &line_number, line, sizeof(line))) break; if (line[0] != '\"') break; p = parse_c_string(buf, &buf_length, sizeof(buf), line); memmove(line, p, strlen(p)+1); trim(line, sizeof(line)); } /* [source] */ if (memcmp(line, "source", 6) == 0) { memmove(line, line+6, strlen(line+5)); trim(line, sizeof(line)); if (!isdigit(line[0])) { fprintf(stderr, "%s:%u: expected source port\n", filename, line_number); goto end; } source_port = (unsigned)strtoul(line, 0, 0); line[0] = '\0'; } /* * Now we've completely parsed the record, so add it to our * list of payloads */ if (buf_length) add_payload(payloads, buf, buf_length, ports, source_port); rangelist_remove_all(ports); } end: ;//fclose(fp); } /**************************************************************************** ****************************************************************************/ int templ_nmap_selftest(void) { unsigned char buf[1024]; size_t buf_length; buf_length = 0; parse_c_string(buf, &buf_length, sizeof(buf), "\"\\t\\n\\r\\x1f\\123\""); if (memcmp(buf, "\t\n\r\x1f\123", 5) != 0) return 1; return 0; /* "OPTIONS sip:carol@chicago.com SIP/2.0\r\n" "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKhjhs8ass877\r\n" "Max-Forwards: 70\r\n" "To: \r\n" "From: Alice ;tag=1928301774\r\n" "Call-ID: a84b4c76e66710\r\n" "CSeq: 63104 OPTIONS\r\n" "Contact: \r\n" "Accept: application/sdp\r\n" "Content-Length: 0\r\n" */ } ================================================ FILE: src/templ-nmap-payloads.h ================================================ /* Parses the "nmap-payloads" file. */ #ifndef TEMPL_NMAP_PAYLOADS_H #define TEMPL_NMAP_PAYLOADS_H #include struct PayloadsUDP; struct RangeList; typedef unsigned (*payloads_datagram_add_cb)(struct PayloadsUDP *payloads, const unsigned char *buf, size_t length, struct RangeList *ports, unsigned source_port ); void read_nmap_payloads(FILE *fp, const char *filename, struct PayloadsUDP *payloads, payloads_datagram_add_cb add_payload ); int templ_nmap_selftest(void); #endif ================================================ FILE: src/templ-opts.h ================================================ #ifndef TEMPL_OPTS_H #define TEMPL_OPTS_H #include "massip-addr.h" #ifdef _MSC_VER #pragma warning(disable:4214) #endif /** * This tells us whether we should add, remove, or leave default * a field in the packet headers. * FIXME: not all of these are supported */ typedef enum {Default, Add, Remove} addremove_t; struct TemplateOptions { struct { addremove_t is_badsum:4; /* intentionally bad checksum */ addremove_t is_tsecho:4; /* enable timestamp echo */ addremove_t is_tsreply:4; /* enable timestamp echo */ addremove_t is_flags:4; addremove_t is_ackno:4; addremove_t is_seqno:4; addremove_t is_win:4; addremove_t is_mss:4; addremove_t is_sackok:4; addremove_t is_wscale:4; unsigned flags; unsigned ackno; unsigned seqno; unsigned win; unsigned mss; unsigned sackok; unsigned wscale; unsigned tsecho; unsigned tsreply; } tcp; struct { addremove_t is_badsum:4; /* intentionally bad checksum */ } udp; struct { addremove_t is_sender_mac:4; addremove_t is_sender_ip:4; addremove_t is_target_mac:4; addremove_t is_target_ip:4; macaddress_t sender_mac; ipaddress sender_ip; macaddress_t target_mac; ipaddress target_ip; } arp; struct { addremove_t is_badsum:4; /* intentionally bad checksum */ addremove_t is_tos:4; addremove_t is_ipid:4; addremove_t is_df:4; addremove_t is_mf:4; addremove_t is_ttl:4; unsigned tos; unsigned ipid; unsigned ttl; } ipv4; }; #endif ================================================ FILE: src/templ-payloads.c ================================================ /* Reads in UDP payload templates. This supports two formats. The first format is the "nmap-payloads" file included with the nmap port scanner. The second is the "libpcap" format that reads in real packets, extracting just the payloads, associated them with the destination UDP port. */ #include "templ-payloads.h" #include "massip-port.h" #include "rawsock-pcapfile.h" /* for reading payloads from pcap files */ #include "proto-preprocess.h" /* parse packets */ #include "util-logger.h" #include "proto-zeroaccess.h" /* botnet p2p protocol */ #include "proto-snmp.h" #include "proto-memcached.h" #include "proto-coap.h" /* constrained app proto for IoT udp/5683*/ #include "proto-ntp.h" #include "proto-dns.h" #include "proto-isakmp.h" #include "util-malloc.h" #include "massip.h" #include "templ-nmap-payloads.h" #include #include #include #include struct PayloadUDP_Item { unsigned port; unsigned source_port; /* not used yet */ unsigned length; unsigned xsum; unsigned rarity; SET_COOKIE set_cookie; unsigned char buf[1]; }; struct PayloadUDP_Default { unsigned port; unsigned source_port; unsigned length; unsigned xsum; SET_COOKIE set_cookie; char *buf; }; struct PayloadsUDP { unsigned count; size_t max; struct PayloadUDP_Item **list; }; struct PayloadUDP_Default hard_coded_oproto_payloads[] = { /* ECHO protocol - echoes back whatever we send */ {47, 65536, 4, 0, 0, "\0\0\0\0"}, {0,0,0,0,0} }; struct PayloadUDP_Default hard_coded_udp_payloads[] = { /* ECHO protocol - echoes back whatever we send */ {7, 65536, 12, 0, 0, "masscan-test 0x00000000"}, /* QOTD - quote of the day (amplifier) */ {17, 65536, 12, 0, 0, "masscan-test"}, /* chargen - character generator (amplifier) */ {19, 65536, 12, 0, 0, "masscan-test"}, {53, 65536, 0x1f, 0, dns_set_cookie, /* 00 */"\x50\xb6" /* transaction id */ /* 02 */"\x01\x20" /* query */ /* 04 */"\x00\x01" /* query = 1 */ /* 06 */"\x00\x00\x00\x00\x00\x00" /* 0c */"\x07" "version" "\x04" "bind" "\x00" /* 1b */"\x00\x10" /* TXT */ /* 1d */"\x00\x03" /* CHAOS */ /* 1f */ }, {69, 65536, 24, 0, 0, "\x00\x01" /* opcode = read */ "masscan-test" "\0" /* filename = "masscan-test" */ "netascii" "\0" /* type = "netascii" */ }, /* portmapper */ {111, 65536, 40, 0, dns_set_cookie, "\x00\x00\x00\x00" /* xid - first two bytes set by dns_set_cookie() */ "\x00\x00\x00\x00" /* RPC opcode = CALL*/ "\x00\x00\x00\x02" /* RPC version = 2 */ "\x00\x01\x86\xa0" /* RPC program = NFS */ "\x00\x00\x00\x02" /* portmapper version = 2 */ "\x00\x00\x00\x00" /* portmapper procedure = 0 (NULL, ping) */ "\x00\x00\x00\x00\x00\x00\x00\x00" /* credentials = none*/ "\x00\x00\x00\x00\x00\x00\x00\x00" /* verifier = none */ }, {123, 65536, 48, 0, ntp_set_cookie, "\x17\x00\x03\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, {137, 65536, 50, 0, dns_set_cookie, "\xab\x12" /* transaction id */ "\x00\x00" /* query */ "\x00\x01\x00\x00\x00\x00\x00\x00" /* one question */ "\x20" /*name length*/ "CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "\x00" "\x00\x21" /* type = nbt */ "\x00\x01" /* class = iternet*/ }, /* NetBIOS-SMB BROWSER protocol */ {138, 65536, 174, 0, 0, "\x11" /* broadcast datagram */ "\x0a" /* flags */ "\xc1\x00" /* datagram id */ "\x0a\x01\x01\xd5" /* source IP */ "\x00\x8a" /* source port */ "\x00\xa0" /* length */ "\x00\x00" /* packet offset */ "\x20" /* namelength = 32 bytes*/ "ENEBFDFDEDEBEOCNFEEFFDFECACACAAA" /* "MASSCAN-TEST<00>" */ "\x00" "\x20" "FHEPFCELEHFCEPFFFACACACACACACABN" /* "WORKGROUP<1D>*/ "\x00" "\xff\x53\x4d\x42\x25\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x11\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00" "\x00\x00\x00\x00\x00\x00\x00\x06\x00\x56\x00\x03\x00\x01\x00\x01" "\x00\x02\x00\x17\x00\x5c\x4d\x41\x49\x4c\x53\x4c\x4f\x54\x5c\x42" "\x52\x4f\x57\x53\x45\x00" "\x09\x04\x01\x00\x00\x00" }, {161, 65536, 59, 0, snmp_set_cookie, "\x30" "\x39" "\x02\x01\x00" /* version */ "\x04\x06" "public" /* community = public */ "\xa0" "\x2c" /* type = GET */ "\x02\x04\x00\x00\x00\x00" /* transaction id = ???? */ "\x02\x01\x00" /* error = 0 */ "\x02\x01\x00" /* error index = 0 */ "\x30\x1e" "\x30\x0d" "\x06\x09\x2b\x06\x01\x80\x02\x01\x01\x01\x00" /*sysName*/ "\x05\x00" /*^^^^_____IDS LULZ HAH HA HAH*/ "\x30\x0d" "\x06\x09\x2b\x06\x01\x80\x02\x01\x01\x05\x00" /*sysDesc*/ "\x05\x00"}, /*^^^^_____IDS LULZ HAH HA HAH*/ {443, 65536, 115, 0, 0, "\x16" /* opcode = handshake */ "\xfe\xff" /* version = dTLS v1.0 */ "\x00\x00" /* epoch = 0 */ "\x00\x00\x00\x00\x00\x07" /* sequence number = 7 */ "\x00\x66" /* length 104 */ "\x01" /* opcode = client hello */ "\x00\x00\x5a" /* length 90 */ "\x00\x00" /* sequence number = 0 */ "\x00\x00\x00" /* fragment offset = 0 */ "\x00\x00\x5a" /* framgent length = 90 */ "\xfe\xfd" /* version = dTLS v1.2 */ "\x1d\xb1\xe3\x52\x2e\x89\x94\xb7\x15\x33\x2f\x30\xff\xff\xcf\x76" "\x27\x77\xab\x04\xe4\x86\x6f\x21\x18\x0e\xf8\xdd\x70\xcc\xab\x9e" "\x00" /* session id length = 0 */ "\x00" /* cookie length = 0 */ "\x00\x04" /* cipher suites length = 4 */ "\xc0\x30" /* TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 */ "\x00\xff" "\x01" /* compression methods length = 1*/ "\x00" /* NULL compression */ "\x00\x2c" /* extensions length = 44 */ "\x00\x0b\x00\x04\x03\x00\x01\x02" "\x00\x0a\x00\x0c\x00\x0a\x00\x1d\x00\x17\x00\x1e\x00\x19\x00\x18" "\x00\x23\x00\x00" "\x00\x16\x00\x00" "\x00\x17\x00\x00" "\x00\x0d\x00\x04\x00\x02\x05\x01" }, {520, 65536, 24, 0, 0, "\x01" /* opcode = request */ "\x01" /* version = 1 */ "\x00\x00" /* padding */ "\x00\x02" /* address familly = IPv4 */ "\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x10" /* metric = 16 */ }, /* RADIUS */ {1645, 65536, 20, 0, 0, "\x01" /* opcode = access request */ "\x00" /* packet id = 0 */ "\x00\x14" /* length = 20 */ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, {1812, 65536, 20, 0, 0, "\x01" /* opcode = access request */ "\x00" /* packet id = 0 */ "\x00\x14" /* length = 20 */ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, {1646, 65536, 20, 0, 0, "\x04" /* opcode = access request */ "\x00" /* packet id = 0 */ "\x00\x14" /* length = 20 */ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, {1813, 65536, 20, 0, 0, "\x04" /* opcode = access request */ "\x00" /* packet id = 0 */ "\x00\x14" /* length = 20 */ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, /* L2TP */ {1701, 65536, 60, 0, 0, "\xc8\x02" /* flags */ "\x00\x3c" /* length = 60 */ "\x00\x00" /* tunnel id = 0 */ "\x00\x00" /* session id = 0 */ "\x00\x00" /* Nsent = 0 */ "\x00\x00" /* Nrecvd = 0 */ "\x80\x08\x00\x00\x00\x00\x00\x01" /* control message */ "\x80\x08\x00\x00\x00\x02\x01\x00" /* protocol version */ "\x80\x0e\x00\x00\x00\x07" "masscan1" /* hostname */ "\x80\x0a\x00\x00\x00\x03\x00\x00\x00\x03" /* framing capabilities */ "\x80\x08\x00\x00\x00\x09\x00\x00" /* assigned tunnel */ }, /* UPnP SSDP - Univeral Plug-n-Play Simple Service Discovery Protocol */ {1900, 65536, 0xFFFFFFFF, 0, 0, "M-SEARCH * HTTP/1.1\r\n" "HOST: 239.255.255.250:1900\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: 1\r\n" "ST: ssdp:all\r\n" "USER-AGENT: unix/1.0 UPnP/1.1 masscan/1.x\r\n"}, /* NFS - kludge: use the DNS cookie, setting first 2 bytes instead of 4 */ {2049, 65536, 40, 0, dns_set_cookie, "\x00\x00\x00\x00" /* xid - first two bytes set by dns_set_cookie() */ "\x00\x00\x00\x00" /* RPC opcode = CALL*/ "\x00\x00\x00\x02" /* RPC version = 2 */ "\x00\x01\x86\xa3" /* RPC program = NFS */ "\x00\x00\x00\x02" /* NFS version = 2 */ "\x00\x00\x00\x00" /* NFS procedure = 0 (NULL, ping) */ "\x00\x00\x00\x00\x00\x00\x00\x00" /* credentials = none*/ "\x00\x00\x00\x00\x00\x00\x00\x00" /* verifier = none */ }, {5060, 65536, 0xFFFFFFFF, 0, 0, "OPTIONS sip:carol@chicago.com SIP/2.0\r\n" "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKhjhs8ass877\r\n" "Max-Forwards: 70\r\n" "To: \r\n" "From: Alice ;tag=1928301774\r\n" "Call-ID: a84b4c76e66710\r\n" "CSeq: 63104 OPTIONS\r\n" "Contact: \r\n" "Accept: application/sdp\r\n" "Content-Length: 0\r\n" }, /* CoAP (contrained app proto for IoT) GET /.well-known/core request */ {5683, 65536, 21, 0, coap_udp_set_cookie, "\x40" /* ver=1 type=con */ "\x01" /* code=GET */ "\x01\xce" /* message id (changed by set-cookie) */ "\xbb" /* ".well-known */ "\x2e\x77\x65\x6c\x6c\x2d\x6b\x6e\x6f\x77\x6e" "\x04" /* "core" */ "\x63\x6f\x72\x65" }, /* memcached "stats" request. This looks for memcached systems that can * be used for DDoS amplifiers */ {11211, 65536, 15, 0, memcached_udp_set_cookie, "\x00\x00\x00\x00\x00\x01\x00\x00stats\r\n" }, //16464,16465,16470, 16471 {16464, 65536, zeroaccess_getL_length, 0, 0, (char *)zeroaccess_getL}, {16465, 65536, zeroaccess_getL_length, 0, 0, (char *)zeroaccess_getL}, {16470, 65536, zeroaccess_getL_length, 0, 0, (char *)zeroaccess_getL}, {16471, 65536, zeroaccess_getL_length, 0, 0, (char *)zeroaccess_getL}, /* Quake 3 (amplifier) * http://blog.alejandronolla.com/2013/06/24/amplification-ddos-attack-with-quake3-servers-an-analysis-1-slash-2/ */ {27960, 65536, 0xFFFFFFFF, 0, 0, "\xFF\xFF\xFF\xFF\x67\x65\x74\x73\x74\x61\x74\x75\x73\x10"}, /* ISAKMP */ {500, 500, 352, 0, isakmp_set_cookie, /* ISAKMP */ "\x00\x11\x22\x33\x44\x55\x66\x77"/* init_cookie, overwritten on send() */ "\x00\x00\x00\x00\x00\x00\x00\x00" /* resp_cookie*/ "\x01" /* next_payload: SA */ "\x10" /* version */ "\x02" /* exch_type: identity prot. */ "\x00" /* flags */ "\x00\x00\x00\x00" /* id */ "\x00\x00\x01\x60" /* length: 352 */ /* ISAKMP_SA */ "\x00" /* next_payload: None */ "\x00" /* reserved */ "\x01\x44" /* length: 324 */ "\x00\x00\x00\x01" /* DOI: IPSEC */ "\x00\x00\x00\x01" /* situation: identity */ /* Proposal */ "\x00" /* next_payload: None */ "\x00" /* reserved */ "\x01\x38" /* length: 312 */ "\x01" /* proposal: 1 */ "\x01" /* protocol: ISAKMP */ "\x00" /* SPIsize: 0 */ "\x0d" /* trans_count: 13 */ "" /* SPI */ /* Tranforms */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x20" /* length: 32 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x01\x80\x04\x00\x02" "\x80\x0b\x00\x01\x80\x0c\x00\x01" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'PSK'), ('GroupDesc', '1024MODPgr'), ('LifeType', 'Seconds'), ('LifeDuration', 1) */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x20" /* length: 32 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x01\x80\x02\x00\x01\x80\x03\x00\x01\x80\x04\x00\x02" "\x80\x0b\x00\x01\x80\x0c\x00\x01" /* ('Encryption', 'DES-CBC'), ('Hash', 'MD5'), ('Authentication', 'PSK'), ('GroupDesc', '1024MODPgr'), ('LifeType', 'Seconds'), ('LifeDuration', 1) */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x20" /* length: 32 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x07\x80\x02\x00\x04\x80\x03\x00\x01\x80\x04\x00\x0e" "\x80\x0b\x00\x01\x80\x0c\x00\x01" /* ('Encryption', 'AES-CBC'), ('Hash', 'SHA2-256'), ('Authentication', 'PSK'), ('GroupDesc', '2048MODPgr'), ('LifeType', 'Seconds'), ('LifeDuration', 1) */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x02" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'DSS') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x03" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'RSA Sig') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x04" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'RSA Encryption') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x08" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'ECDSA Sig') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfa\xdd" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'HybridInitRSA') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfa\xdf" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'HybridInitDSS') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfd\xe9" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'XAUTHInitPreShared') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfd\xeb" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'XAUTHInitDSS') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfd\xed" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'XAUTHInitRSA') */ "\x03" /* next_payload: Transform */ "\x00" /* reserved */ "\x00\x14" /* length: 20 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */ "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfd\xef" /* ('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'XAUTHInitRSAEncryption') */ "\x00" /* next_payload: None */ "\x00" /* reserved */ "\x00\x08" /* length: 8 */ "\x00" /* num */ "\x01" /* id: KEY_IKE */ "\x00\x00" /* reserved */}, {0,0,0,0,0} }; /*************************************************************************** * Calculate the partial checksum of the payload. This allows us to simply * add this to the checksum when transmitting instead of recalculating * everything. ***************************************************************************/ static unsigned partial_checksum(const unsigned char *px, size_t icmp_length) { uint64_t xsum = 0; unsigned i; for (i=0; i> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); return (unsigned)xsum; } /*************************************************************************** * If we have the port, return the best payload for that port. ***************************************************************************/ int payloads_udp_lookup( const struct PayloadsUDP *payloads, unsigned port, const unsigned char **px, unsigned *length, unsigned *source_port, uint64_t *xsum, SET_COOKIE *set_cookie) { unsigned i; if (payloads == 0) return 0; port &= 0xFFFF; /* This is just a linear search, done once at startup, to search * through all the payloads for the best match. */ for (i=0; icount; i++) { if (payloads->list[i]->port == port) { *px = payloads->list[i]->buf; *length = payloads->list[i]->length; *source_port = payloads->list[i]->source_port; *xsum = payloads->list[i]->xsum; *set_cookie = payloads->list[i]->set_cookie; return 1; } } return 0; } /*************************************************************************** * cleanup on program shutdown ***************************************************************************/ void payloads_udp_destroy(struct PayloadsUDP *payloads) { unsigned i; if (payloads == NULL) return; for (i=0; icount; i++) free(payloads->list[i]); if (payloads->list) free(payloads->list); free(payloads); } /*************************************************************************** * We read lots of UDP payloads from the files. However, we probably * aren't using most, or even any, of them. Therefore, we use this * function to remove the ones we won't be using. This makes lookups * faster, ideally looking up only zero or one rather than twenty. ***************************************************************************/ void payloads_udp_trim(struct PayloadsUDP *payloads, const struct MassIP *targets) { unsigned i; struct PayloadUDP_Item **list2; unsigned count2 = 0; /* Create a new list */ list2 = REALLOCARRAY(0, payloads->max, sizeof(list2[0])); /* Add to the new list any used ports */ for (i=0; icount; i++) { unsigned found; found = massip_has_port(targets, payloads->list[i]->port + Templ_UDP); if (found) { list2[count2++] = payloads->list[i]; } else { free(payloads->list[i]); } //payloads->list[i] = 0; } /* Replace the old list */ free(payloads->list); payloads->list = list2; payloads->count = count2; } void payloads_oproto_trim(struct PayloadsUDP *payloads, const struct MassIP *targets) { unsigned i; struct PayloadUDP_Item **list2; unsigned count2 = 0; /* Create a new list */ list2 = REALLOCARRAY(0, payloads->max, sizeof(list2[0])); /* Add to the new list any used ports */ for (i=0; icount; i++) { unsigned found; found = massip_has_port(targets, payloads->list[i]->port + Templ_Oproto_first); if (found) { list2[count2++] = payloads->list[i]; } else { free(payloads->list[i]); } } /* Replace the old list */ free(payloads->list); payloads->list = list2; payloads->count = count2; } /*************************************************************************** * Adds a payloads template for the indicated datagram protocol, which * is UDP or Oproto ("other IP protocol"). ***************************************************************************/ static unsigned payloads_datagram_add(struct PayloadsUDP *payloads, const unsigned char *buf, size_t length, struct RangeList *ports, unsigned source_port, SET_COOKIE set_cookie) { unsigned count = 1; struct PayloadUDP_Item *p; uint64_t port_count = rangelist_count(ports); uint64_t i; for (i=0; icount + 1 > payloads->max) { size_t new_max = payloads->max*2 + 1; payloads->list = REALLOCARRAY(payloads->list, new_max, sizeof(payloads->list[0])); payloads->max = new_max; } /* allocate space for this record */ p = MALLOC(sizeof(p[0]) + length); p->port = rangelist_pick(ports, i); p->source_port = source_port; p->length = (unsigned)length; memcpy(p->buf, buf, length); p->xsum = partial_checksum(buf, length); p->set_cookie = set_cookie; /* insert in sorted order */ { unsigned j; for (j=0; jcount; j++) { if (p->port <= payloads->list[j]->port) break; } if (j < payloads->count) { if (p->port == payloads->list[j]->port) { free(payloads->list[j]); count = 0; /* don't increment count */ } else memmove(payloads->list + j + 1, payloads->list + j, (payloads->count-j) * sizeof(payloads->list[0])); } payloads->list[j] = p; payloads->count += count; count = 1; } } return count; /* zero or one */ } static unsigned payloads_datagram_add_nocookie(struct PayloadsUDP *payloads, const unsigned char *buf, size_t length, struct RangeList *ports, unsigned source_port ) { return payloads_datagram_add(payloads, buf, length, ports, source_port, 0); } /*************************************************************************** * Called during processing of the "--pcap-payloads " directive. * This is the well-known 'pcap' file format. This code strips off the * headers of the packets then preserves just the payload portion * and port number. ***************************************************************************/ void payloads_read_pcap(const char *filename, struct PayloadsUDP *payloads, struct PayloadsUDP *oproto_payloads) { struct PcapFile *pcap; unsigned count = 0; LOG(2, "payloads:'%s': opening packet capture\n", filename); /* open packet-capture */ pcap = pcapfile_openread(filename); if (pcap == NULL) { fprintf(stderr, "payloads: can't read from file '%s'\n", filename); return; } /* for all packets in the capture file * - read in packet * - parse packet * - save payload */ for (;;) { unsigned x; unsigned captured_length; unsigned char buf[65536]; struct PreprocessedInfo parsed; struct RangeList ports[1] = {{0}}; struct Range range[1] = {{0}}; /* * Read the next packet from the capture file */ { unsigned time_secs; unsigned time_usecs; unsigned original_length; x = pcapfile_readframe(pcap, &time_secs, &time_usecs, &original_length, &captured_length, buf, (unsigned)sizeof(buf)); } if (!x) break; /* * Parse the packet up to its headers */ x = preprocess_frame(buf, captured_length, 1, &parsed); if (!x) continue; /* corrupt packet */ /* * Make sure it has UDP */ switch (parsed.found) { case FOUND_DNS: case FOUND_UDP: /* * Kludge: mark the port in the format the API wants */ ports->list = range; ports->count = 1; ports->max = 1; range->begin = parsed.port_dst; range->end = range->begin; /* * Now we've completely parsed the record, so add it to our * list of payloads */ count += payloads_datagram_add( payloads, buf + parsed.app_offset, parsed.app_length, ports, 0x10000, 0); break; case FOUND_OPROTO: /* * Kludge: mark the port in the format the API wants */ ports->list = range; ports->count = 1; ports->max = 1; range->begin = parsed.ip_protocol; range->end = range->begin; /* * Now we've completely parsed the record, so add it to our * list of payloads */ count += payloads_datagram_add(oproto_payloads, buf + parsed.transport_offset, parsed.transport_length, ports, 0x10000, 0); break; default: continue; } } LOG(2, "payloads:'%s': imported %u unique payloads\n", filename, count); LOG(2, "payloads:'%s': closed packet capture\n", filename); pcapfile_close(pcap); } /*************************************************************************** * Called from the "conf" subsystem in order read in the file * "nmap-payloads". We call the function 'read_nmap_payloads()" defined * in a different file that focuses on parsing that file format. ***************************************************************************/ void payloads_udp_readfile(FILE *fp, const char *filename, struct PayloadsUDP *payloads) { read_nmap_payloads(fp, filename, payloads, payloads_datagram_add_nocookie); } /*************************************************************************** ***************************************************************************/ struct PayloadsUDP * payloads_udp_create(void) { unsigned i; struct PayloadsUDP *payloads; struct PayloadUDP_Default *hard_coded = hard_coded_udp_payloads; payloads = CALLOC(1, sizeof(*payloads)); /* * For popular parts, include some hard-coded default UDP payloads */ for (i=0; hard_coded[i].length; i++) { //struct Range range; struct RangeList list = {0}; unsigned length; /* Kludge: create a pseudo-rangelist to hold the one port */ /*list.list = ⦥ list.count = 1; range.begin = hard_coded[i].port; range.end = range.begin;*/ rangelist_add_range(&list, hard_coded[i].port, hard_coded[i].port); length = hard_coded[i].length; if (length == 0xFFFFFFFF) length = (unsigned)strlen(hard_coded[i].buf); /* Add this to our real payloads. This will get overwritten * if the user adds their own with the same port */ payloads_datagram_add(payloads, (const unsigned char*)hard_coded[i].buf, length, &list, hard_coded[i].source_port, hard_coded[i].set_cookie); rangelist_remove_all(&list); } return payloads; } /*************************************************************************** * (same code as for UDP) ***************************************************************************/ struct PayloadsUDP * payloads_oproto_create(void) { unsigned i; struct PayloadsUDP *payloads; struct PayloadUDP_Default *hard_coded = hard_coded_oproto_payloads; payloads = CALLOC(1, sizeof(*payloads)); /* * Some hard-coded ones, like GRE */ for (i=0; hard_coded[i].length; i++) { //struct Range range; struct RangeList list = {0}; unsigned length; /* Kludge: create a pseudo-rangelist to hold the one port */ rangelist_add_range(&list, hard_coded[i].port, hard_coded[i].port); length = hard_coded[i].length; if (length == 0xFFFFFFFF) length = (unsigned)strlen(hard_coded[i].buf); /* Add this to our real payloads. This will get overwritten * if the user adds their own with the same port */ payloads_datagram_add(payloads, (const unsigned char*)hard_coded[i].buf, length, &list, hard_coded[i].source_port, hard_coded[i].set_cookie); rangelist_remove_all(&list); } return payloads; } int templ_payloads_selftest(void) { return templ_nmap_selftest(); } ================================================ FILE: src/templ-payloads.h ================================================ #ifndef TEMPL_PAYLOADS_H #define TEMPL_PAYLOADS_H #include #include struct MassIP; /** * Regression test this module. * @return * 0 on success, or positive integer on failure. */ int templ_payloads_selftest(void); /** * Create this module. Must be matched with the 'destroy()' function on exit */ struct PayloadsUDP * payloads_udp_create(void); struct PayloadsUDP * payloads_oproto_create(void); /** * Free the resources of an object created with a matching call to * 'payloads_create()' */ void payloads_udp_destroy(struct PayloadsUDP *payloads); void payloads_oproto_destroy(struct PayloadsUDP *payloads); /** * Read payloads from an "nmap-payloads" formatted file. The caller is * responsible for opening/closing the file, but should passing the * filename so that we can print helpful error messages. */ void payloads_udp_readfile(FILE *fp, const char *filename, struct PayloadsUDP *payloads); /** * Read payloads from a libpcap formatted file. */ void payloads_read_pcap(const char *filename, struct PayloadsUDP *payloads, struct PayloadsUDP *oproto_payloads); /** * Called to remove any payloads that aren't be used in the scan. This makes * lookups faster when generating packets. */ void payloads_udp_trim(struct PayloadsUDP *payloads, const struct MassIP *targets); void payloads_oproto_trim(struct PayloadsUDP *payloads, const struct MassIP *targets); /** * The port scanner creates a "cookie" for every packet that it sends, which * will be a 64-bit value, whose low-order bits will be trimmed to fit whatever * size is available. For TCP, this becomes the 32-bit seqno of the SYN packet. * For UDP protocols, however, each application layer protocol will be * different. For example, SNMP can use a 32-bit transaction ID, whereas DNS * can use only a 16-bit transaction ID. */ typedef unsigned (*SET_COOKIE)(unsigned char *px, size_t length, uint64_t seqno); /** * Given a UDP port number, return the payload we have that is associated * with that port number. * @param payloads * A table full over payloads. * @param port * The input port number. * @param px * The returned payload bytes. * @param length * The returned count of payload bytes. * @param source_port * The returned port that should be used when sending packets. * @param xsum * The returned partial checksum of the payload bytes, so that it * doesn't need to be recalculated for every packet. * @param set_cookie * The returned function that will set the "cookie" field in the * packet for each transmission */ int payloads_udp_lookup( const struct PayloadsUDP *payloads, unsigned port, const unsigned char **px, unsigned *length, unsigned *source_port, uint64_t *xsum, SET_COOKIE *set_cookie); int payloads_oproto_lookup( const struct PayloadsUDP *payloads, unsigned port, const unsigned char **px, unsigned *length, unsigned *source_port, uint64_t *xsum, SET_COOKIE *set_cookie); #endif ================================================ FILE: src/templ-pkt.c ================================================ /* Construct a TCP packet based upon a template. The (eventual) idea of this module is to make this scanner extensible by providing an arbitrary packet template. Thus, the of this module is to take an existing packet template, parse it, then make appropriate changes. */ #include "templ-pkt.h" #include "templ-tcp-hdr.h" #include "templ-opts.h" #include "massip-port.h" #include "proto-preprocess.h" #include "proto-sctp.h" #include "util-safefunc.h" #include "pixie-timer.h" #include "util-logger.h" #include "templ-payloads.h" #include "syn-cookie.h" #include "unusedparm.h" #include "vulncheck.h" #include "util-checksum.h" #include "util-malloc.h" #include "stub-pcap-dlt.h" /* data link types, like NULL, RAW, or ETHERNET */ #include #include #include static unsigned char default_tcp_template[] = "\0\1\2\3\4\5" /* Ethernet: destination */ "\6\7\x8\x9\xa\xb" /* Ethernet: source */ "\x08\x00" /* Ethernet type: IPv4 */ "\x45" /* IP type */ "\x00" "\x00\x2c" /* total length = 40 bytes */ "\x00\x00" /* identification */ "\x00\x00" /* fragmentation flags */ "\xFF\x06" /* TTL=255, proto=TCP */ "\xFF\xFF" /* checksum */ "\0\0\0\0" /* source address */ "\0\0\0\0" /* destination address */ "\0\0" /* source port */ "\0\0" /* destination port */ "\0\0\0\0" /* sequence number */ "\0\0\0\0" /* ACK number */ "\x60" /* header length */ "\x02" /* SYN */ "\x04\x01" /* window fixed to 1024 */ "\xFF\xFF" /* checksum */ "\x00\x00" /* urgent pointer */ "\x02\x04\x05\xb4" /* opt [mss 1460] h/t @IvreRocks */ ; static unsigned char default_udp_template[] = "\0\1\2\3\4\5" /* Ethernet: destination */ "\6\7\x8\x9\xa\xb" /* Ethernet: source */ "\x08\x00" /* Ethernet type: IPv4 */ "\x45" /* IP type */ "\x00" "\x00\x1c" /* total length = 28 bytes */ "\x00\x00" /* identification */ "\x00\x00" /* fragmentation flags */ "\xFF\x11" /* TTL=255, proto=UDP */ "\xFF\xFF" /* checksum */ "\0\0\0\0" /* source address */ "\0\0\0\0" /* destination address */ "\xfe\xdc" /* source port */ "\x00\x00" /* destination port */ "\x00\x08" /* length */ "\x00\x00" /* checksum */ ; static unsigned char default_sctp_template[] = "\0\1\2\3\4\5" /* Ethernet: destination */ "\6\7\x8\x9\xa\xb" /* Ethernet: source */ "\x08\x00" /* Ethernet type: IPv4 */ "\x45" /* IP type */ "\x00" "\x00\x34" /* total length = 52 bytes */ "\x00\x00" /* identification */ "\x00\x00" /* fragmentation flags */ "\xFF\x84" /* TTL=255, proto = SCTP */ "\x00\x00" /* checksum */ "\0\0\0\0" /* source address */ "\0\0\0\0" /* destination address */ "\x00\x00" /* source port */ "\x00\x00" /* destination port */ "\x00\x00\x00\x00" /* verification tag */ "\x58\xe4\x5d\x36" /* checksum */ "\x01" /* type = init */ "\x00" /* flags = none */ "\x00\x14" /* length = 20 */ "\x9e\x8d\x52\x25" /* initiate tag */ "\x00\x00\x80\x00" /* receiver window credit */ "\x00\x0a" /* outbound streams = 10 */ "\x08\x00" /* inbound streams = 2048 */ "\x46\x1a\xdf\x3d" /* initial TSN */ ; static unsigned char default_icmp_ping_template[] = "\0\1\2\3\4\5" /* Ethernet: destination */ "\6\7\x8\x9\xa\xb" /* Ethernet: source */ "\x08\x00" /* Ethernet type: IPv4 */ "\x45" /* IP type */ "\x00" "\x00\x4c" /* total length = 76 bytes */ "\x00\x00" /* identification */ "\x00\x00" /* fragmentation flags */ "\xFF\x01" /* TTL=255, proto=ICMP */ "\xFF\xFF" /* checksum */ "\0\0\0\0" /* source address */ "\0\0\0\0" /* destination address */ "\x08\x00" /* Ping Request */ "\x00\x00" /* checksum */ "\x00\x00\x00\x00" /* ID, seqno */ "\x08\x09\x0a\x0b" /* payload */ "\x0c\x0d\x0e\x0f" "\x10\x11\x12\x13" "\x14\x15\x16\x17" "\x18\x19\x1a\x1b" "\x1c\x1d\x1e\x1f" "\x20\x21\x22\x23" "\x24\x25\x26\x27" "\x28\x29\x2a\x2b" "\x2c\x2d\x2e\x2f" "\x30\x31\x32\x33" "\x34\x35\x36\x37" ; static unsigned char default_icmp_timestamp_template[] = "\0\1\2\3\4\5" /* Ethernet: destination */ "\6\7\x8\x9\xa\xb" /* Ethernet: source */ "\x08\x00" /* Ethernet type: IPv4 */ "\x45" /* IP type */ "\x00" "\x00\x28" /* total length = 84 bytes */ "\x00\x00" /* identification */ "\x00\x00" /* fragmentation flags */ "\xFF\x01" /* TTL=255, proto=UDP */ "\xFF\xFF" /* checksum */ "\0\0\0\0" /* source address */ "\0\0\0\0" /* destination address */ "\x0d\x00" /* timestamp request */ "\x00\x00" /* checksum */ "\x00\x00" /* identifier */ "\x00\x00" /* sequence number */ "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" ; static unsigned char default_arp_template[] = "\xff\xff\xff\xff\xff\xff" /* Ethernet: destination */ "\x00\x00\x00\x00\x00\x00" /* Ethernet: source */ "\x08\x06" /* Ethernet type: ARP */ "\x00\x01" /* hardware = Ethernet */ "\x08\x00" /* protocol = IPv4 */ "\x06\x04" /* MAC length = 6, IPv4 length = 4 */ "\x00\x01" /* opcode = request */ "\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00" ; /*************************************************************************** * Checksum the IP header. This is a "partial" checksum, so we * don't reverse the bits ~. ***************************************************************************/ static unsigned ip_header_checksum(const unsigned char *px, unsigned offset, unsigned max_offset) { unsigned header_length = (px[offset]&0xF) * 4; unsigned xsum = 0; unsigned i; /* restrict check only over packet */ if (max_offset > offset + header_length) max_offset = offset + header_length; /* add all the two-byte words together */ xsum = 0; for (i = offset; i < max_offset; i += 2) { xsum += px[i]<<8 | px[i+1]; } /* if more than 16 bits in result, reduce to 16 bits */ xsum = (xsum & 0xFFFF) + (xsum >> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); return xsum; } /*************************************************************************** ***************************************************************************/ static unsigned tcp_checksum2(const unsigned char *px, unsigned offset_ip, unsigned offset_tcp, size_t tcp_length) { uint64_t xsum = 0; unsigned i; /* pseudo checksum */ xsum = 6; xsum += tcp_length; xsum += px[offset_ip + 12] << 8 | px[offset_ip + 13]; xsum += px[offset_ip + 14] << 8 | px[offset_ip + 15]; xsum += px[offset_ip + 16] << 8 | px[offset_ip + 17]; xsum += px[offset_ip + 18] << 8 | px[offset_ip + 19]; /* TCP checksum */ for (i=0; i> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); return (unsigned)xsum; } /*************************************************************************** ***************************************************************************/ /*************************************************************************** ***************************************************************************/ static unsigned tcp_ipv4_checksum(struct TemplatePacket *tmpl) { const unsigned char *px = tmpl->ipv4.packet; unsigned offset_ip = tmpl->ipv4.offset_ip; unsigned offset_app = tmpl->ipv4.offset_app; unsigned offset_tcp = tmpl->ipv4.offset_tcp; unsigned xsum = 0; unsigned i; /* pseudo checksum */ xsum = 6; xsum += offset_app - offset_tcp; xsum += px[offset_ip + 12] << 8 | px[offset_ip + 13]; xsum += px[offset_ip + 14] << 8 | px[offset_ip + 15]; xsum += px[offset_ip + 16] << 8 | px[offset_ip + 17]; xsum += px[offset_ip + 18] << 8 | px[offset_ip + 19]; /* TCP checksum */ for (i=offset_tcp; i> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); return xsum; } /*************************************************************************** ***************************************************************************/ unsigned udp_checksum2(const unsigned char *px, unsigned offset_ip, unsigned offset_tcp, size_t tcp_length) { uint64_t xsum = 0; unsigned i; /* pseudo checksum */ xsum = 17; xsum += tcp_length; xsum += px[offset_ip + 12] << 8 | px[offset_ip + 13]; xsum += px[offset_ip + 14] << 8 | px[offset_ip + 15]; xsum += px[offset_ip + 16] << 8 | px[offset_ip + 17]; xsum += px[offset_ip + 18] << 8 | px[offset_ip + 19]; /* TCP checksum */ for (i=0; i> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); return (unsigned)xsum; } /*************************************************************************** ***************************************************************************/ static unsigned udp_ipv4_checksum(struct TemplatePacket *tmpl) { return udp_checksum2( tmpl->ipv4.packet, tmpl->ipv4.offset_ip, tmpl->ipv4.offset_tcp, tmpl->ipv4.length - tmpl->ipv4.offset_tcp); } /*************************************************************************** ***************************************************************************/ static unsigned icmp_checksum2(const unsigned char *px, unsigned offset_icmp, size_t icmp_length) { uint64_t xsum = 0; unsigned i; for (i=0; i> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); xsum = (xsum & 0xFFFF) + (xsum >> 16); return (unsigned)xsum; } /*************************************************************************** ***************************************************************************/ static unsigned icmp_ipv4_checksum(struct TemplatePacket *tmpl) { return icmp_checksum2( tmpl->ipv4.packet, tmpl->ipv4.offset_tcp, tmpl->ipv4.length - tmpl->ipv4.offset_tcp); } /*************************************************************************** ***************************************************************************/ struct TemplateSet templ_copy(const struct TemplateSet *templset) { struct TemplateSet result; unsigned i; memcpy(&result, templset, sizeof(result)); for (i=0; icount; i++) { const struct TemplatePacket *p1 = &templset->pkts[i]; struct TemplatePacket *p2 = &result.pkts[i]; p2->ipv4.packet = MALLOC(2048+p2->ipv4.length); memcpy(p2->ipv4.packet, p1->ipv4.packet, p2->ipv4.length); p2->ipv6.packet = MALLOC(2048+p2->ipv6.length); memcpy(p2->ipv6.packet, p1->ipv6.packet, p2->ipv6.length); } return result; } /*************************************************************************** ***************************************************************************/ void tcp_set_window(unsigned char *px, size_t px_length, unsigned window) { struct PreprocessedInfo parsed; unsigned x; size_t offset; unsigned xsum; /* Parse the frame looking for the TCP header */ x = preprocess_frame(px, (unsigned)px_length, 1 /*enet*/, &parsed); if (!x || parsed.found == FOUND_NOTHING) return; if (parsed.ip_protocol != 6) return; offset = parsed.transport_offset; if (offset + 20 > px_length) return; /* set the new window */ #if 0 xsum = px[offset + 16] << 8 | px[offset + 17]; xsum = (~xsum)&0xFFFF; xsum += window & 0xFFFF; xsum -= px[offset + 14] << 8 | px[offset + 15]; xsum = ((xsum)&0xFFFF) + (xsum >> 16); xsum = ((xsum)&0xFFFF) + (xsum >> 16); xsum = ((xsum)&0xFFFF) + (xsum >> 16); xsum = (~xsum)&0xFFFF; #endif px[offset + 14] = (unsigned char)(window>>8); px[offset + 15] = (unsigned char)(window>>0); px[offset + 16] = (unsigned char)(0); px[offset + 17] = (unsigned char)(0); xsum = ~tcp_checksum2(px, parsed.ip_offset, parsed.transport_offset, parsed.transport_length); px[offset + 16] = (unsigned char)(xsum>>8); px[offset + 17] = (unsigned char)(xsum>>0); } /*************************************************************************** ***************************************************************************/ size_t tcp_create_packet( struct TemplatePacket *tmpl, ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me, unsigned seqno, unsigned ackno, unsigned flags, const unsigned char *payload, size_t payload_length, unsigned char *px, size_t px_length) { uint64_t xsum; if (ip_them.version == 4) { unsigned ip_id = ip_them.ipv4 ^ port_them ^ seqno; unsigned offset_ip = tmpl->ipv4.offset_ip; unsigned offset_tcp = tmpl->ipv4. offset_tcp; unsigned offset_payload = offset_tcp + ((tmpl->ipv4.packet[offset_tcp+12]&0xF0)>>2); size_t new_length = offset_payload + payload_length; size_t ip_len = (offset_payload - offset_ip) + payload_length; unsigned old_len; if (new_length > px_length) { fprintf(stderr, "tcp: err generating packet: too much payload\n"); return 0; } memcpy(px + 0, tmpl->ipv4.packet, tmpl->ipv4.length); memcpy(px + offset_payload, payload, payload_length); old_len = px[offset_ip+2]<<8 | px[offset_ip+3]; /* * Fill in the empty fields in the IP header and then re-calculate * the checksum. */ px[offset_ip+2] = (unsigned char)(ip_len>> 8); px[offset_ip+3] = (unsigned char)(ip_len & 0xFF); px[offset_ip+4] = (unsigned char)(ip_id >> 8); px[offset_ip+5] = (unsigned char)(ip_id & 0xFF); px[offset_ip+12] = (unsigned char)((ip_me.ipv4 >> 24) & 0xFF); px[offset_ip+13] = (unsigned char)((ip_me.ipv4 >> 16) & 0xFF); px[offset_ip+14] = (unsigned char)((ip_me.ipv4 >> 8) & 0xFF); px[offset_ip+15] = (unsigned char)((ip_me.ipv4 >> 0) & 0xFF); px[offset_ip+16] = (unsigned char)((ip_them.ipv4 >> 24) & 0xFF); px[offset_ip+17] = (unsigned char)((ip_them.ipv4 >> 16) & 0xFF); px[offset_ip+18] = (unsigned char)((ip_them.ipv4 >> 8) & 0xFF); px[offset_ip+19] = (unsigned char)((ip_them.ipv4 >> 0) & 0xFF); xsum = tmpl->ipv4.checksum_ip; xsum += (ip_id&0xFFFF); xsum += ip_me.ipv4; xsum += ip_them.ipv4; xsum += ip_len - old_len; xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = ~xsum; px[offset_ip+10] = (unsigned char)(xsum >> 8); px[offset_ip+11] = (unsigned char)(xsum & 0xFF); /* * now do the same for TCP */ px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+ 4] = (unsigned char)(seqno >> 24); px[offset_tcp+ 5] = (unsigned char)(seqno >> 16); px[offset_tcp+ 6] = (unsigned char)(seqno >> 8); px[offset_tcp+ 7] = (unsigned char)(seqno >> 0); px[offset_tcp+ 8] = (unsigned char)(ackno >> 24); px[offset_tcp+ 9] = (unsigned char)(ackno >> 16); px[offset_tcp+10] = (unsigned char)(ackno >> 8); px[offset_tcp+11] = (unsigned char)(ackno >> 0); px[offset_tcp+13] = (unsigned char)flags; px[offset_tcp+14] = (unsigned char)(1200>>8); px[offset_tcp+15] = (unsigned char)(1200 & 0xFF); px[offset_tcp+16] = (unsigned char)(0 >> 8); px[offset_tcp+17] = (unsigned char)(0 >> 0); xsum = tcp_checksum2(px, tmpl->ipv4.offset_ip, tmpl->ipv4.offset_tcp, new_length - tmpl->ipv4.offset_tcp); xsum = ~xsum; px[offset_tcp+16] = (unsigned char)(xsum >> 8); px[offset_tcp+17] = (unsigned char)(xsum >> 0); if (new_length < 60) { memset(px+new_length, 0, 60-new_length); new_length = 60; } return new_length; } else { unsigned offset_ip = tmpl->ipv6.offset_ip; unsigned offset_tcp = tmpl->ipv6.offset_tcp; unsigned offset_app = tmpl->ipv6.offset_app; /* Make sure the new packet won't exceed buffer size */ if (offset_app + payload_length > px_length) { fprintf(stderr, "tcp: err generating packet: too much payload\n"); return 0; } /* Copy over everything up to the new application-layer-payload */ memcpy(px, tmpl->ipv6.packet, tmpl->ipv6.offset_app); /* Replace the template's application-layer-payload with the new app-payload */ memcpy(px + tmpl->ipv6.offset_app, payload, payload_length); /* Fixup the "payload length" field in the IPv6 header. This is everything * after the IPv6 header. There may be additional headers between the IPv6 * and TCP headers, so the calculation isn't simply the length of the TCP portion */ { size_t len = tmpl->ipv6.offset_app + payload_length - tmpl->ipv6.offset_ip - 40; px[offset_ip + 4] = (unsigned char)(len>>8) & 0xFF; px[offset_ip + 5] = (unsigned char)(len>>0) & 0xFF; } /* Copy over the IP addresses */ px[offset_ip+ 8] = (unsigned char)((ip_me.ipv6.hi >> 56ULL) & 0xFF); px[offset_ip+ 9] = (unsigned char)((ip_me.ipv6.hi >> 48ULL) & 0xFF); px[offset_ip+10] = (unsigned char)((ip_me.ipv6.hi >> 40ULL) & 0xFF); px[offset_ip+11] = (unsigned char)((ip_me.ipv6.hi >> 32ULL) & 0xFF); px[offset_ip+12] = (unsigned char)((ip_me.ipv6.hi >> 24ULL) & 0xFF); px[offset_ip+13] = (unsigned char)((ip_me.ipv6.hi >> 16ULL) & 0xFF); px[offset_ip+14] = (unsigned char)((ip_me.ipv6.hi >> 8ULL) & 0xFF); px[offset_ip+15] = (unsigned char)((ip_me.ipv6.hi >> 0ULL) & 0xFF); px[offset_ip+16] = (unsigned char)((ip_me.ipv6.lo >> 56ULL) & 0xFF); px[offset_ip+17] = (unsigned char)((ip_me.ipv6.lo >> 48ULL) & 0xFF); px[offset_ip+18] = (unsigned char)((ip_me.ipv6.lo >> 40ULL) & 0xFF); px[offset_ip+19] = (unsigned char)((ip_me.ipv6.lo >> 32ULL) & 0xFF); px[offset_ip+20] = (unsigned char)((ip_me.ipv6.lo >> 24ULL) & 0xFF); px[offset_ip+21] = (unsigned char)((ip_me.ipv6.lo >> 16ULL) & 0xFF); px[offset_ip+22] = (unsigned char)((ip_me.ipv6.lo >> 8ULL) & 0xFF); px[offset_ip+23] = (unsigned char)((ip_me.ipv6.lo >> 0ULL) & 0xFF); px[offset_ip+24] = (unsigned char)((ip_them.ipv6.hi >> 56ULL) & 0xFF); px[offset_ip+25] = (unsigned char)((ip_them.ipv6.hi >> 48ULL) & 0xFF); px[offset_ip+26] = (unsigned char)((ip_them.ipv6.hi >> 40ULL) & 0xFF); px[offset_ip+27] = (unsigned char)((ip_them.ipv6.hi >> 32ULL) & 0xFF); px[offset_ip+28] = (unsigned char)((ip_them.ipv6.hi >> 24ULL) & 0xFF); px[offset_ip+29] = (unsigned char)((ip_them.ipv6.hi >> 16ULL) & 0xFF); px[offset_ip+30] = (unsigned char)((ip_them.ipv6.hi >> 8ULL) & 0xFF); px[offset_ip+31] = (unsigned char)((ip_them.ipv6.hi >> 0ULL) & 0xFF); px[offset_ip+32] = (unsigned char)((ip_them.ipv6.lo >> 56ULL) & 0xFF); px[offset_ip+33] = (unsigned char)((ip_them.ipv6.lo >> 48ULL) & 0xFF); px[offset_ip+34] = (unsigned char)((ip_them.ipv6.lo >> 40ULL) & 0xFF); px[offset_ip+35] = (unsigned char)((ip_them.ipv6.lo >> 32ULL) & 0xFF); px[offset_ip+36] = (unsigned char)((ip_them.ipv6.lo >> 24ULL) & 0xFF); px[offset_ip+37] = (unsigned char)((ip_them.ipv6.lo >> 16ULL) & 0xFF); px[offset_ip+38] = (unsigned char)((ip_them.ipv6.lo >> 8ULL) & 0xFF); px[offset_ip+39] = (unsigned char)((ip_them.ipv6.lo >> 0ULL) & 0xFF); /* * now do the same for TCP */ px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+ 4] = (unsigned char)(seqno >> 24); px[offset_tcp+ 5] = (unsigned char)(seqno >> 16); px[offset_tcp+ 6] = (unsigned char)(seqno >> 8); px[offset_tcp+ 7] = (unsigned char)(seqno >> 0); px[offset_tcp+ 8] = (unsigned char)(ackno >> 24); px[offset_tcp+ 9] = (unsigned char)(ackno >> 16); px[offset_tcp+10] = (unsigned char)(ackno >> 8); px[offset_tcp+11] = (unsigned char)(ackno >> 0); px[offset_tcp+13] = (unsigned char)flags; px[offset_tcp+14] = (unsigned char)(1200>>8); px[offset_tcp+15] = (unsigned char)(1200 & 0xFF); px[offset_tcp+16] = (unsigned char)(0 >> 8); px[offset_tcp+17] = (unsigned char)(0 >> 0); xsum = checksum_ipv6(px + offset_ip + 8, px + offset_ip + 24, 6, (offset_app - offset_tcp) + payload_length, px + offset_tcp); px[offset_tcp+16] = (unsigned char)(xsum >> 8); px[offset_tcp+17] = (unsigned char)(xsum >> 0); px[offset_tcp+16] = (unsigned char)(xsum >> 8); px[offset_tcp+17] = (unsigned char)(xsum >> 0); return offset_app + payload_length; } } /*************************************************************************** ***************************************************************************/ static void udp_payload_fixup(struct TemplatePacket *tmpl, unsigned port, unsigned seqno) { const unsigned char *px2 = 0; unsigned length2 = 0; unsigned source_port2 = 0x1000; uint64_t xsum2 = 0; //unsigned char *px = tmpl->packet; SET_COOKIE set_cookie = 0; UNUSEDPARM(seqno); payloads_udp_lookup(tmpl->payloads, port, &px2, &length2, &source_port2, &xsum2, &set_cookie); /* Copy over the payloads */ memcpy( tmpl->ipv4.packet + tmpl->ipv4.offset_app, px2, length2); memcpy( tmpl->ipv6.packet + tmpl->ipv6.offset_app, px2, length2); /* Change the cookie values */ if (set_cookie) { set_cookie( tmpl->ipv4.packet + tmpl->ipv4.offset_app, length2, seqno); set_cookie( tmpl->ipv6.packet + tmpl->ipv6.offset_app, length2, seqno); } tmpl->ipv4.length = tmpl->ipv4.offset_app + length2; tmpl->ipv6.length = tmpl->ipv6.offset_app + length2; } void template_set_target_ipv6( struct TemplateSet *tmplset, ipv6address ip_them, unsigned port_them, ipv6address ip_me, unsigned port_me, unsigned seqno, unsigned char *px, size_t sizeof_px, size_t *r_length ) { unsigned offset_ip; unsigned offset_tcp; uint64_t xsum; struct TemplatePacket *tmpl = NULL; uint64_t entropy = tmplset->entropy; unsigned payload_length; *r_length = sizeof_px; /* * Find out which packet template to use. This is because we can * simultaneously scan for both TCP and UDP (and others). We've * just overloaded the "port" field to signal which protocol we * are using */ if (port_them < Templ_TCP + 65536) tmpl = &tmplset->pkts[Proto_TCP]; else if (port_them < Templ_UDP + 65536) { tmpl = &tmplset->pkts[Proto_UDP]; port_them &= 0xFFFF; udp_payload_fixup(tmpl, port_them, seqno); } else if (port_them < Templ_SCTP + 65536) { tmpl = &tmplset->pkts[Proto_SCTP]; port_them &= 0xFFFF; } else if (port_them == Templ_ICMP_echo) { tmpl = &tmplset->pkts[Proto_ICMP_ping]; } else if (port_them == Templ_ICMP_timestamp) { tmpl = &tmplset->pkts[Proto_ICMP_timestamp]; } else if (port_them == Templ_ARP) { tmpl = &tmplset->pkts[Proto_ARP]; if (*r_length > tmpl->ipv6.length) *r_length = tmpl->ipv6.length; memcpy(px, tmpl->ipv6.packet, *r_length); return; } else if (port_them == Templ_VulnCheck) { tmpl = &tmplset->pkts[Proto_VulnCheck]; port_them &= 0xFFFF; } else { return; } /* Create some shorter local variables to work with */ if (*r_length > tmpl->ipv6.length) *r_length = tmpl->ipv6.length; memcpy(px, tmpl->ipv6.packet, *r_length); offset_ip = tmpl->ipv6.offset_ip; offset_tcp = tmpl->ipv6.offset_tcp; //ip_id = ip_them ^ port_them ^ seqno; /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| Traffic Class | Flow Label | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Payload Length | Next Header | Hop Limit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | | + Source Address + | | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | | + Destination Address + | | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /* * Fill in the empty fields in the IP header and then re-calculate * the checksum. */ payload_length = tmpl->ipv6.length - tmpl->ipv6.offset_ip - 40; px[offset_ip+4] = (unsigned char)(payload_length>>8); px[offset_ip+5] = (unsigned char)(payload_length>>0); px[offset_ip+ 8] = (unsigned char)((ip_me.hi >> 56ULL) & 0xFF); px[offset_ip+ 9] = (unsigned char)((ip_me.hi >> 48ULL) & 0xFF); px[offset_ip+10] = (unsigned char)((ip_me.hi >> 40ULL) & 0xFF); px[offset_ip+11] = (unsigned char)((ip_me.hi >> 32ULL) & 0xFF); px[offset_ip+12] = (unsigned char)((ip_me.hi >> 24ULL) & 0xFF); px[offset_ip+13] = (unsigned char)((ip_me.hi >> 16ULL) & 0xFF); px[offset_ip+14] = (unsigned char)((ip_me.hi >> 8ULL) & 0xFF); px[offset_ip+15] = (unsigned char)((ip_me.hi >> 0ULL) & 0xFF); px[offset_ip+16] = (unsigned char)((ip_me.lo >> 56ULL) & 0xFF); px[offset_ip+17] = (unsigned char)((ip_me.lo >> 48ULL) & 0xFF); px[offset_ip+18] = (unsigned char)((ip_me.lo >> 40ULL) & 0xFF); px[offset_ip+19] = (unsigned char)((ip_me.lo >> 32ULL) & 0xFF); px[offset_ip+20] = (unsigned char)((ip_me.lo >> 24ULL) & 0xFF); px[offset_ip+21] = (unsigned char)((ip_me.lo >> 16ULL) & 0xFF); px[offset_ip+22] = (unsigned char)((ip_me.lo >> 8ULL) & 0xFF); px[offset_ip+23] = (unsigned char)((ip_me.lo >> 0ULL) & 0xFF); px[offset_ip+24] = (unsigned char)((ip_them.hi >> 56ULL) & 0xFF); px[offset_ip+25] = (unsigned char)((ip_them.hi >> 48ULL) & 0xFF); px[offset_ip+26] = (unsigned char)((ip_them.hi >> 40ULL) & 0xFF); px[offset_ip+27] = (unsigned char)((ip_them.hi >> 32ULL) & 0xFF); px[offset_ip+28] = (unsigned char)((ip_them.hi >> 24ULL) & 0xFF); px[offset_ip+29] = (unsigned char)((ip_them.hi >> 16ULL) & 0xFF); px[offset_ip+30] = (unsigned char)((ip_them.hi >> 8ULL) & 0xFF); px[offset_ip+31] = (unsigned char)((ip_them.hi >> 0ULL) & 0xFF); px[offset_ip+32] = (unsigned char)((ip_them.lo >> 56ULL) & 0xFF); px[offset_ip+33] = (unsigned char)((ip_them.lo >> 48ULL) & 0xFF); px[offset_ip+34] = (unsigned char)((ip_them.lo >> 40ULL) & 0xFF); px[offset_ip+35] = (unsigned char)((ip_them.lo >> 32ULL) & 0xFF); px[offset_ip+36] = (unsigned char)((ip_them.lo >> 24ULL) & 0xFF); px[offset_ip+37] = (unsigned char)((ip_them.lo >> 16ULL) & 0xFF); px[offset_ip+38] = (unsigned char)((ip_them.lo >> 8ULL) & 0xFF); px[offset_ip+39] = (unsigned char)((ip_them.lo >> 0ULL) & 0xFF); /* * Now do the checksum for the higher layer protocols */ switch (tmpl->proto) { case Proto_TCP: px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+ 4] = (unsigned char)(seqno >> 24); px[offset_tcp+ 5] = (unsigned char)(seqno >> 16); px[offset_tcp+ 6] = (unsigned char)(seqno >> 8); px[offset_tcp+ 7] = (unsigned char)(seqno >> 0); xsum = checksum_ipv6(px + offset_ip + 8, px + offset_ip + 24, 6, tmpl->ipv6.length - offset_tcp, px + offset_tcp); px[offset_tcp+16] = (unsigned char)(xsum >> 8); px[offset_tcp+17] = (unsigned char)(xsum >> 0); break; case Proto_UDP: /* TODO: IPv6 */ px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+ 4] = (unsigned char)((tmpl->ipv6.length - tmpl->ipv6.offset_app + 8)>>8); px[offset_tcp+ 5] = (unsigned char)((tmpl->ipv6.length - tmpl->ipv6.offset_app + 8)&0xFF); px[offset_tcp+6] = (unsigned char)(0); px[offset_tcp+7] = (unsigned char)(0); xsum = checksum_ipv6(px + offset_ip + 8, px + offset_ip + 24, 17, tmpl->ipv6.length - offset_tcp, px + offset_tcp); px[offset_tcp+6] = (unsigned char)(xsum >> 8); px[offset_tcp+7] = (unsigned char)(xsum >> 0); break; case Proto_SCTP: /* TODO: IPv6 */ px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+16] = (unsigned char)(seqno >> 24); px[offset_tcp+17] = (unsigned char)(seqno >> 16); px[offset_tcp+18] = (unsigned char)(seqno >> 8); px[offset_tcp+19] = (unsigned char)(seqno >> 0); xsum = sctp_checksum(px + offset_tcp, tmpl->ipv6.length - offset_tcp); px[offset_tcp+ 8] = (unsigned char)(xsum >> 24); px[offset_tcp+ 9] = (unsigned char)(xsum >> 16); px[offset_tcp+10] = (unsigned char)(xsum >> 8); px[offset_tcp+11] = (unsigned char)(xsum >> 0); break; case Proto_ICMP_ping: case Proto_ICMP_timestamp: /* TODO: IPv6 */ seqno = (unsigned)syn_cookie_ipv6(ip_them, port_them, ip_me, 0, entropy); px[offset_tcp+ 4] = (unsigned char)(seqno >> 24); px[offset_tcp+ 5] = (unsigned char)(seqno >> 16); px[offset_tcp+ 6] = (unsigned char)(seqno >> 8); px[offset_tcp+ 7] = (unsigned char)(seqno >> 0); xsum = checksum_ipv6(px + offset_ip + 8, px + offset_ip + 24, 58, tmpl->ipv6.length - offset_tcp, px + offset_tcp); px[offset_tcp+2] = (unsigned char)(xsum >> 8); px[offset_tcp+3] = (unsigned char)(xsum >> 0); break; case Proto_VulnCheck: /* TODO: IPv6 */ /*tmplset->vulncheck->set_target(tmpl, ip_them, port_them, ip_me, port_me, seqno, px, sizeof_px, r_length);*/ break; case Proto_ARP: /* TODO: IPv6 */ /* don't do any checksumming */ break; case Proto_Oproto: /* TODO: IPv6 */ /* TODO: probably need to add checksums for certain protocols */ break; case Proto_Count: break; } } /*************************************************************************** * This is the function that formats the transmitted packets for probing * machines. It takes a template for the protocol (usually a TCP SYN * packet), then sets the destination IP address and port numbers. ***************************************************************************/ void template_set_target_ipv4( struct TemplateSet *tmplset, ipv4address ip_them, unsigned port_them, ipv4address ip_me, unsigned port_me, unsigned seqno, unsigned char *px, size_t sizeof_px, size_t *r_length ) { unsigned offset_ip; unsigned offset_tcp; uint64_t xsum; unsigned ip_id; struct TemplatePacket *tmpl = NULL; unsigned xsum2; uint64_t entropy = tmplset->entropy; *r_length = sizeof_px; /* * Find out which packet template to use. This is because we can * simultaneously scan for both TCP and UDP (and others). We've * just overloaded the "port" field to signal which protocol we * are using */ if (port_them < Templ_TCP + 65536) tmpl = &tmplset->pkts[Proto_TCP]; else if (port_them < Templ_UDP + 65536) { tmpl = &tmplset->pkts[Proto_UDP]; port_them &= 0xFFFF; udp_payload_fixup(tmpl, port_them, seqno); } else if (port_them < Templ_SCTP + 65536) { tmpl = &tmplset->pkts[Proto_SCTP]; port_them &= 0xFFFF; } else if (port_them == Templ_ICMP_echo) { tmpl = &tmplset->pkts[Proto_ICMP_ping]; } else if (port_them == Templ_ICMP_timestamp) { tmpl = &tmplset->pkts[Proto_ICMP_timestamp]; } else if (port_them == Templ_ARP) { tmpl = &tmplset->pkts[Proto_ARP]; if (*r_length > tmpl->ipv4.length) *r_length = tmpl->ipv4.length; memcpy(px, tmpl->ipv4.packet, *r_length); px = px + tmpl->ipv4.offset_ip; px[14] = (unsigned char)((ip_me >> 24) & 0xFF); px[15] = (unsigned char)((ip_me >> 16) & 0xFF); px[16] = (unsigned char)((ip_me >> 8) & 0xFF); px[17] = (unsigned char)((ip_me >> 0) & 0xFF); px[24] = (unsigned char)((ip_them >> 24) & 0xFF); px[25] = (unsigned char)((ip_them >> 16) & 0xFF); px[26] = (unsigned char)((ip_them >> 8) & 0xFF); px[27] = (unsigned char)((ip_them >> 0) & 0xFF); return; } else if (port_them == Templ_VulnCheck) { tmpl = &tmplset->pkts[Proto_VulnCheck]; port_them &= 0xFFFF; } else { return; } /* Create some shorter local variables to work with */ if (*r_length > tmpl->ipv4.length) *r_length = tmpl->ipv4.length; memcpy(px, tmpl->ipv4.packet, *r_length); offset_ip = tmpl->ipv4.offset_ip; offset_tcp = tmpl->ipv4.offset_tcp; ip_id = ip_them ^ port_them ^ seqno; /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identification |Flags| Fragment Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time to Live | Protocol | Header Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /* * Fill in the empty fields in the IP header and then re-calculate * the checksum. */ { unsigned total_length = tmpl->ipv4.length - tmpl->ipv4.offset_ip; px[offset_ip+2] = (unsigned char)(total_length>>8); px[offset_ip+3] = (unsigned char)(total_length>>0); } px[offset_ip+4] = (unsigned char)(ip_id >> 8); px[offset_ip+5] = (unsigned char)(ip_id & 0xFF); px[offset_ip+12] = (unsigned char)((ip_me >> 24) & 0xFF); px[offset_ip+13] = (unsigned char)((ip_me >> 16) & 0xFF); px[offset_ip+14] = (unsigned char)((ip_me >> 8) & 0xFF); px[offset_ip+15] = (unsigned char)((ip_me >> 0) & 0xFF); px[offset_ip+16] = (unsigned char)((ip_them >> 24) & 0xFF); px[offset_ip+17] = (unsigned char)((ip_them >> 16) & 0xFF); px[offset_ip+18] = (unsigned char)((ip_them >> 8) & 0xFF); px[offset_ip+19] = (unsigned char)((ip_them >> 0) & 0xFF); px[offset_ip+10] = (unsigned char)(0); px[offset_ip+11] = (unsigned char)(0); xsum2 = (unsigned)~ip_header_checksum(px, offset_ip, tmpl->ipv4.length); px[offset_ip+10] = (unsigned char)(xsum2 >> 8); px[offset_ip+11] = (unsigned char)(xsum2 & 0xFF); /* * Now do the checksum for the higher layer protocols */ xsum = 0; switch (tmpl->proto) { case Proto_TCP: px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+ 4] = (unsigned char)(seqno >> 24); px[offset_tcp+ 5] = (unsigned char)(seqno >> 16); px[offset_tcp+ 6] = (unsigned char)(seqno >> 8); px[offset_tcp+ 7] = (unsigned char)(seqno >> 0); xsum += (uint64_t)tmpl->ipv4.checksum_tcp + (uint64_t)ip_me + (uint64_t)ip_them + (uint64_t)port_me + (uint64_t)port_them + (uint64_t)seqno; xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = ~xsum; px[offset_tcp+16] = (unsigned char)(xsum >> 8); px[offset_tcp+17] = (unsigned char)(xsum >> 0); break; case Proto_UDP: px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+ 4] = (unsigned char)((tmpl->ipv4.length - tmpl->ipv4.offset_app + 8)>>8); px[offset_tcp+ 5] = (unsigned char)((tmpl->ipv4.length - tmpl->ipv4.offset_app + 8)&0xFF); px[offset_tcp+6] = (unsigned char)(0); px[offset_tcp+7] = (unsigned char)(0); xsum = udp_checksum2(px, offset_ip, offset_tcp, tmpl->ipv4.length - offset_tcp); /*xsum += (uint64_t)tmpl->checksum_tcp + (uint64_t)ip_me + (uint64_t)ip_them + (uint64_t)port_me + (uint64_t)port_them + (uint64_t)2*(tmpl->length - tmpl->offset_app); xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = (xsum >> 16) + (xsum & 0xFFFF); printf("%04x\n", xsum);*/ xsum = ~xsum; px[offset_tcp+6] = (unsigned char)(xsum >> 8); px[offset_tcp+7] = (unsigned char)(xsum >> 0); break; case Proto_SCTP: px[offset_tcp+ 0] = (unsigned char)(port_me >> 8); px[offset_tcp+ 1] = (unsigned char)(port_me & 0xFF); px[offset_tcp+ 2] = (unsigned char)(port_them >> 8); px[offset_tcp+ 3] = (unsigned char)(port_them & 0xFF); px[offset_tcp+16] = (unsigned char)(seqno >> 24); px[offset_tcp+17] = (unsigned char)(seqno >> 16); px[offset_tcp+18] = (unsigned char)(seqno >> 8); px[offset_tcp+19] = (unsigned char)(seqno >> 0); xsum = sctp_checksum(px + offset_tcp, tmpl->ipv4.length - offset_tcp); px[offset_tcp+ 8] = (unsigned char)(xsum >> 24); px[offset_tcp+ 9] = (unsigned char)(xsum >> 16); px[offset_tcp+10] = (unsigned char)(xsum >> 8); px[offset_tcp+11] = (unsigned char)(xsum >> 0); break; case Proto_ICMP_ping: case Proto_ICMP_timestamp: seqno = (unsigned)syn_cookie_ipv4(ip_them, port_them, ip_me, 0, entropy); px[offset_tcp+ 4] = (unsigned char)(seqno >> 24); px[offset_tcp+ 5] = (unsigned char)(seqno >> 16); px[offset_tcp+ 6] = (unsigned char)(seqno >> 8); px[offset_tcp+ 7] = (unsigned char)(seqno >> 0); xsum = (uint64_t)tmpl->ipv4.checksum_tcp + (uint64_t)seqno; xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = (xsum >> 16) + (xsum & 0xFFFF); xsum = ~xsum; px[offset_tcp+2] = (unsigned char)(xsum >> 8); px[offset_tcp+3] = (unsigned char)(xsum >> 0); break; case Proto_VulnCheck: tmplset->vulncheck->set_target(tmpl, ip_them, port_them, ip_me, port_me, seqno, px, sizeof_px, r_length); break; case Proto_ARP: /* don't do any checksumming */ break; case Proto_Oproto: /* TODO: probably need to add checksums for certain protocols */ break; case Proto_Count: break; } } #if defined(WIN32) || defined(_WIN32) #define AF_INET6 23 #else #include #endif /*************************************************************************** * Creates an IPv6 packet from an IPv4 template, by simply replacing * the IPv4 header with the IPv6 header. ***************************************************************************/ static void _template_init_ipv6(struct TemplatePacket *tmpl, macaddress_t router_mac_ipv6, unsigned data_link_type) { struct PreprocessedInfo parsed; unsigned x; unsigned payload_length; unsigned offset_ip; unsigned offset_tcp; unsigned offset_tcp6; unsigned char *buf; /* Zero out everything and start from scratch */ if (tmpl->ipv6.packet) { free(tmpl->ipv6.packet); memset(&tmpl->ipv6, 0, sizeof(tmpl->ipv6)); } /* Parse the existing IPv4 packet */ x = preprocess_frame(tmpl->ipv4.packet, tmpl->ipv4.length, data_link_type, &parsed); if (!x || parsed.found == FOUND_NOTHING) { LOG(0, "ERROR: bad packet template\n"); exit(1); } /* The "payload" in this case is everything past the IP header, * so TCP or UDP headers are inside the IP payload */ payload_length = tmpl->ipv4.length - tmpl->ipv4.offset_tcp; offset_ip = tmpl->ipv4.offset_ip; offset_tcp = tmpl->ipv4.offset_tcp; /* Create a copy of the IPv4 packet */ buf = MALLOC(tmpl->ipv4.length + 40); memcpy(buf, tmpl->ipv4.packet, tmpl->ipv4.length); tmpl->ipv6.packet = buf; /* destination = end of IPv6 header * source = end of IPv4 header * contents = everything after IPv4/IPv6 header */ offset_tcp6 = offset_ip + 40; memmove(buf + offset_tcp6, buf + offset_tcp, payload_length ); /* fill the IPv6 header with zeroes */ memset(buf + offset_ip, 0, 40); tmpl->ipv6.length = offset_ip + 40 + payload_length; switch (data_link_type) { case PCAP_DLT_NULL: /* Null VPN tunnel */ /* FIXME: insert platform dependent value here */ *(int*)buf = AF_INET6; break; case PCAP_DLT_RAW: /* Raw (nothing before IP header) */ break; case PCAP_DLT_ETHERNET: /* Ethernet */ /* Reset the destination MAC address to be the IPv6 router * instead of the IPv4 router, which sometimes are different */ memcpy(buf + 0, router_mac_ipv6.addr, 6); /* Reset the Ethertype field to 0x86dd (meaning IPv6) */ buf[12] = 0x86; buf[13] = 0xdd; break; } /* IP.version = 6 */ buf[offset_ip + 0] = 0x60; /* Set payload length field. In IPv4, this field included the header, * but in IPv6, it's everything after the header. In other words, * the size of an IPv6 packet is 40+payload_length, whereas in IPv4 * it was total_length. */ buf[offset_ip + 4] = (unsigned char)(payload_length >> 8); buf[offset_ip + 5] = (unsigned char)(payload_length >> 0); /* Set the "next header" field. * TODO: need to fix ICMP */ buf[offset_ip + 6] = (unsigned char)parsed.ip_protocol; if (parsed.ip_protocol == 1) { buf[offset_ip + 6] = 58; /* ICMPv6 */ if (payload_length > 0 && buf[offset_tcp6 + 0] == 8) { /* PING -> PINGv6 */ buf[offset_tcp6 + 0] = 128; } } /* Hop limit starts out as 255 */ buf[offset_ip + 7] = 0xFF; /* Parse our newly construct IPv6 packet */ x = preprocess_frame(buf, tmpl->ipv6.length, data_link_type, &parsed); if (!x || parsed.found == FOUND_NOTHING) { LOG(0, "[-] FAILED: bad packet template\n"); exit(1); } tmpl->ipv6.offset_ip = parsed.ip_offset; tmpl->ipv6.offset_tcp = parsed.transport_offset; tmpl->ipv6.offset_app = parsed.app_offset; } /*************************************************************************** * Here we take a packet template, parse it, then make it easier to work * with. ***************************************************************************/ static void _template_init( struct TemplatePacket *tmpl, macaddress_t source_mac, macaddress_t router_mac_ipv4, macaddress_t router_mac_ipv6, const void *packet_bytes, size_t packet_size, unsigned data_link_type ) { unsigned char *px; struct PreprocessedInfo parsed; unsigned x; /* * Create the new template structure: * - zero it out * - make copy of the old packet to serve as new template */ memset(tmpl, 0, sizeof(*tmpl)); tmpl->ipv4.length = (unsigned)packet_size; tmpl->ipv4.packet = MALLOC(2048 + packet_size); memcpy(tmpl->ipv4.packet, packet_bytes, tmpl->ipv4.length); px = tmpl->ipv4.packet; /* * Parse the existing packet template. We support TCP, UDP, ICMP, * and ARP packets. */ x = preprocess_frame(px, tmpl->ipv4.length, 1 /*enet*/, &parsed); if (!x || parsed.found == FOUND_NOTHING) { LOG(0, "ERROR: bad packet template\n"); exit(1); } tmpl->ipv4.offset_ip = parsed.ip_offset; tmpl->ipv4.offset_tcp = parsed.transport_offset; tmpl->ipv4.offset_app = parsed.app_offset; if (parsed.found == FOUND_ARP) { tmpl->ipv4.length = parsed.ip_offset + 28; } else tmpl->ipv4.length = parsed.ip_offset + parsed.ip_length; /* * Overwrite the MAC and IP addresses */ memcpy(px+0, router_mac_ipv4.addr, 6); memcpy(px+6, source_mac.addr, 6); memset((void*)parsed._ip_src, 0, 4); memset((void*)parsed._ip_dst, 0, 4); /* * ARP * * If this is an ARP template (for doing arpscans), then just set our * configured source IP and MAC addresses. */ if (parsed.found == FOUND_ARP) { memcpy((char*)parsed._ip_src - 6, source_mac.addr, 6); tmpl->proto = Proto_ARP; return; } /* * IPv4 * * Calculate the partial checksum. We zero out the fields that will be * added later the packet, then calculate the checksum as if they were * zero. This makes recalculation of the checksum easier when we transmit */ memset(px + tmpl->ipv4.offset_ip + 4, 0, 2); /* IP ID field */ memset(px + tmpl->ipv4.offset_ip + 10, 0, 2); /* checksum */ memset(px + tmpl->ipv4.offset_ip + 12, 0, 8); /* addresses */ tmpl->ipv4.checksum_ip = ip_header_checksum( tmpl->ipv4.packet, tmpl->ipv4.offset_ip, tmpl->ipv4.length); /* * Higher layer protocols: zero out dest/checksum fields, then calculate * a partial checksum */ switch (parsed.ip_protocol) { case 1: /* ICMP */ tmpl->ipv4.offset_app = tmpl->ipv4.length; tmpl->ipv4.checksum_tcp = icmp_ipv4_checksum(tmpl); switch (px[tmpl->ipv4.offset_tcp]) { case 8: tmpl->proto = Proto_ICMP_ping; break; case 13: tmpl->proto = Proto_ICMP_timestamp; break; } break; break; case 6: /* TCP */ /* zero out fields that'll be overwritten */ memset(px + tmpl->ipv4.offset_tcp + 0, 0, 8); /* destination port and seqno */ memset(px + tmpl->ipv4.offset_tcp + 16, 0, 2); /* checksum */ tmpl->ipv4.checksum_tcp = tcp_ipv4_checksum(tmpl); tmpl->proto = Proto_TCP; break; case 17: /* UDP */ memset(px + tmpl->ipv4.offset_tcp + 6, 0, 2); /* checksum */ tmpl->ipv4.checksum_tcp = udp_ipv4_checksum(tmpl); tmpl->proto = Proto_UDP; break; case 132: /* SCTP */ tmpl->ipv4.checksum_tcp = sctp_checksum( tmpl->ipv4.packet + tmpl->ipv4.offset_tcp, tmpl->ipv4.length - tmpl->ipv4.offset_tcp); tmpl->proto = Proto_SCTP; break; } /* * DATALINK KLUDGE * * Adjust the data link header in case of Raw IP packets. This isn't * the correct way to do this, but I'm too lazy to refactor code * for the right way, so we'll do it this way now. */ if (data_link_type == PCAP_DLT_NULL /* Null VPN tunnel */) { int linkproto = 2; /* AF_INET */ tmpl->ipv4.length -= tmpl->ipv4.offset_ip - sizeof(int); tmpl->ipv4.offset_tcp -= tmpl->ipv4.offset_ip - sizeof(int); tmpl->ipv4.offset_app -= tmpl->ipv4.offset_ip - sizeof(int); memmove(tmpl->ipv4.packet + sizeof(int), tmpl->ipv4.packet + tmpl->ipv4.offset_ip, tmpl->ipv4.length); tmpl->ipv4.offset_ip = 4; memcpy(tmpl->ipv4.packet, &linkproto, sizeof(int)); } else if (data_link_type == PCAP_DLT_RAW /* Raw IP */) { tmpl->ipv4.length -= tmpl->ipv4.offset_ip; tmpl->ipv4.offset_tcp -= tmpl->ipv4.offset_ip; tmpl->ipv4.offset_app -= tmpl->ipv4.offset_ip; memmove(tmpl->ipv4.packet, tmpl->ipv4.packet + tmpl->ipv4.offset_ip, tmpl->ipv4.length); tmpl->ipv4.offset_ip = 0; } else if (data_link_type == PCAP_DLT_ETHERNET) { /* the default, do nothing */ } else { LOG(0, "[-] FAILED: bad packet template, unknown data link type\n"); LOG(0, " [hint] masscan doesn't know how to format packets for this interface\n"); exit(1); } /* Now create an IPv6 template based upon the IPv4 template */ _template_init_ipv6(tmpl, router_mac_ipv6, data_link_type); } /*************************************************************************** ***************************************************************************/ void template_packet_init( struct TemplateSet *templset, macaddress_t source_mac, macaddress_t router_mac_ipv4, macaddress_t router_mac_ipv6, struct PayloadsUDP *udp_payloads, struct PayloadsUDP *oproto_payloads, int data_link, uint64_t entropy, const struct TemplateOptions *templ_opts) { unsigned char *buf; size_t length; templset->count = 0; templset->entropy = entropy; /* [SCTP] */ _template_init(&templset->pkts[Proto_SCTP], source_mac, router_mac_ipv4, router_mac_ipv6, default_sctp_template, sizeof(default_sctp_template)-1, data_link); templset->count++; /* [TCP] */ length = sizeof(default_tcp_template) - 1; buf = MALLOC(length); memcpy(buf, default_tcp_template, length); templ_tcp_apply_options(&buf, &length, templ_opts); _template_init(&templset->pkts[Proto_TCP], source_mac, router_mac_ipv4, router_mac_ipv6, buf, length, data_link); templset->count++; free(buf); /* [UDP] */ _template_init(&templset->pkts[Proto_UDP], source_mac, router_mac_ipv4, router_mac_ipv6, default_udp_template, sizeof(default_udp_template)-1, data_link); templset->pkts[Proto_UDP].payloads = udp_payloads; templset->count++; /* [UDP oproto] */ _template_init(&templset->pkts[Proto_Oproto], source_mac, router_mac_ipv4, router_mac_ipv6, default_udp_template, sizeof(default_udp_template)-1, data_link); templset->pkts[Proto_Oproto].payloads = oproto_payloads; templset->count++; /* [ICMP ping] */ _template_init(&templset->pkts[Proto_ICMP_ping], source_mac, router_mac_ipv4, router_mac_ipv6, default_icmp_ping_template, sizeof(default_icmp_ping_template)-1, data_link); templset->count++; /* [ICMP timestamp] */ _template_init(&templset->pkts[Proto_ICMP_timestamp], source_mac, router_mac_ipv4, router_mac_ipv6, default_icmp_timestamp_template, sizeof(default_icmp_timestamp_template)-1, data_link); templset->count++; /* [ARP] */ _template_init( &templset->pkts[Proto_ARP], source_mac, router_mac_ipv4, router_mac_ipv6, default_arp_template, sizeof(default_arp_template)-1, data_link); templset->count++; /* [VulnCheck] */ if (templset->vulncheck) { _template_init( &templset->pkts[Proto_VulnCheck], source_mac, router_mac_ipv4, router_mac_ipv6, templset->vulncheck->packet, templset->vulncheck->packet_length, data_link); templset->count++; } } /*************************************************************************** * Overwrites the TTL of the packet ***************************************************************************/ void template_set_ttl(struct TemplateSet *tmplset, unsigned ttl) { unsigned i; for (i=0; icount; i++) { struct TemplatePacket *tmpl = &tmplset->pkts[i]; unsigned char *px = tmpl->ipv4.packet; unsigned offset = tmpl->ipv4.offset_ip; px[offset+8] = (unsigned char)(ttl); tmpl->ipv4.checksum_ip = ip_header_checksum( tmpl->ipv4.packet, tmpl->ipv4.offset_ip, tmpl->ipv4.length); } } void template_set_vlan(struct TemplateSet *tmplset, unsigned vlan) { unsigned i; for (i=0; icount; i++) { struct TemplatePacket *tmpl = &tmplset->pkts[i]; unsigned char *px; if (tmpl->ipv4.length < 14) continue; px = MALLOC(tmpl->ipv4.length + 4); memcpy(px, tmpl->ipv4.packet, 12); memcpy(px+16, tmpl->ipv4.packet+12, tmpl->ipv4.length - 12); px[12] = 0x81; px[13] = 0x00; px[14] = (unsigned char)(vlan>>8); px[15] = (unsigned char)(vlan>>0); tmpl->ipv4.packet = px; tmpl->ipv4.length += 4; tmpl->ipv4.offset_ip += 4; tmpl->ipv4.offset_tcp += 4; tmpl->ipv4.offset_app += 4; } } /*************************************************************************** ***************************************************************************/ int template_selftest(void) { struct TemplateSet tmplset[1]; int failures = 0; struct TemplateOptions templ_opts = {{0}}; /* Test the module that edits TCP headers */ if (templ_tcp_selftest()) { fprintf(stderr, "[-] templ-tcp-hdr: selftest failed\n"); return 1; } memset(tmplset, 0, sizeof(tmplset[0])); template_packet_init( tmplset, macaddress_from_bytes("\x00\x11\x22\x33\x44\x55"), macaddress_from_bytes("\x66\x55\x44\x33\x22\x11"), macaddress_from_bytes("\x66\x55\x44\x33\x22\x11"), 0, /* UDP payloads = empty */ 0, /* Oproto payloads = empty */ 1, /* Ethernet */ 0, /* no entropy */ &templ_opts ); failures += tmplset->pkts[Proto_TCP].proto != Proto_TCP; failures += tmplset->pkts[Proto_UDP].proto != Proto_UDP; //failures += tmplset->pkts[Proto_SCTP].proto != Proto_SCTP; failures += tmplset->pkts[Proto_ICMP_ping].proto != Proto_ICMP_ping; //failures += tmplset->pkts[Proto_ICMP_timestamp].proto != Proto_ICMP_timestamp; //failures += tmplset->pkts[Proto_ARP].proto != Proto_ARP; if (failures) fprintf(stderr, "template: failed\n"); return failures; } ================================================ FILE: src/templ-pkt.h ================================================ #ifndef TCP_PACKET_H #define TCP_PACKET_H #include #include #include "massip-addr.h" struct PayloadsUDP; struct MassVulnCheck; struct TemplateOptions; /** * Does a regression test of this module. * @return * 1 on failure * 0 on success */ int template_selftest(void); enum TemplateProtocol { Proto_TCP, Proto_UDP, Proto_SCTP, Proto_ICMP_ping, Proto_ICMP_timestamp, Proto_ARP, Proto_Oproto, Proto_VulnCheck, //Proto_IP, //Proto_Custom, Proto_Count }; struct TemplatePayload { unsigned length; unsigned checksum; unsigned char buf[1500]; }; unsigned udp_checksum2(const unsigned char *px, unsigned offset_ip, unsigned offset_tcp, size_t tcp_length); /** * Describes a packet template. The scan packets we transmit are based on a * a template containing most of the data, and we fill in just the necessary * bits, like the destination IP address and port */ struct TemplatePacket { struct { unsigned length; unsigned offset_ip; unsigned offset_tcp; unsigned offset_app; unsigned char *packet; unsigned checksum_ip; unsigned checksum_tcp; unsigned ip_id; } ipv4; struct { unsigned length; unsigned offset_ip; unsigned offset_tcp; unsigned offset_app; unsigned char *packet; unsigned checksum_ip; unsigned checksum_tcp; unsigned ip_id; } ipv6; enum TemplateProtocol proto; struct PayloadsUDP *payloads; }; /** * We can run multiple types of scans (TCP, UDP, vulns, etc.) at the same * time. Therefore, instead of one packet prototype for all scans, we have * a set of prototypes/templates. */ struct TemplateSet { unsigned count; struct MassVulnCheck *vulncheck; uint64_t entropy; struct TemplatePacket pkts[Proto_Count]; }; struct TemplateSet templ_copy(const struct TemplateSet *templ); /** * Initialize the "template" packets. As we spew out probes, we simply make * minor adjustments to the template, such as changing the target IP * address or port number * * @param templset * The template we are creating. * @param source_ip * Our own IP address that we send packets from. The caller will have * retrieved this automatically from the network interface/adapter, or * the user will have set this with --source-ip parameter. * @param source_mac * Our own MAC address. Gotten automatically from the network adapter, * or on the commandline with --source-mac parameter * @param router_mac * The MAC address of the local router/gateway, which will be placed in * the Ethernet destination address field. This is gotten by ARPing * the local router, or by --router-mac configuration parameter. * @param data_link * The OSI layer 2 protocol, as defined in standard. * 1 = Ethernet * 12 = Raw IP (no data link) */ void template_packet_init( struct TemplateSet *templset, macaddress_t source_mac, macaddress_t router_mac_ipv4, macaddress_t router_mac_ipv6, struct PayloadsUDP *udp_payloads, struct PayloadsUDP *oproto_payloads, int data_link, uint64_t entropy, const struct TemplateOptions *templ_opts); /** * Sets the target/destination IP address of the packet, the destination port * number, and other bits of interest about the packet, such as a unique * sequence number. The template can contain things like IP or TCP options * with specific values. The program contains several built-in templates, * but they can also be read from a file. * * @param templset * A template created by "template_packet_init()" and further modified * by various configuration parameters. * @param ip * The target/destination IPv4 address. * @param port * The TCP port number, or port number from another protocol that will * be shifted into the appropriate range. We actually build six base * templates, one for each of these six protocols. * [ 0.. 65535] = TCP port number * [ 65536..131071] = UDP port number * [131072..196607] = SCTP port number * [ 196608 ] = ICMP * [ 196609 ] = ARP * [ 196610 ] = IP * [ more ] = custom * @param seqno * On TCP, this will be the desired sequence number, which the caller * will create from SYN-cookies. Other protocols may use this in a * different manner. For example, if the UDP port is 161, then * this will be the transaction ID of the SNMP request template. */ void template_set_target_ipv4( struct TemplateSet *templset, ipv4address ip_them, unsigned port_them, ipv4address ip_me, unsigned port_me, unsigned seqno, unsigned char *px, size_t sizeof_px, size_t *r_length); void template_set_target_ipv6( struct TemplateSet *templset, ipv6address ip_them, unsigned port_them, ipv6address ip_me, unsigned port_me, unsigned seqno, unsigned char *px, size_t sizeof_px, size_t *r_length); /** * Create a TCP packet containing a payload, based on the original * template used for the SYN */ size_t tcp_create_packet( struct TemplatePacket *pkt, ipaddress ip_them, unsigned port_them, ipaddress ip_me, unsigned port_me, unsigned seqno, unsigned ackno, unsigned flags, const unsigned char *payload, size_t payload_length, unsigned char *px, size_t px_length); /** * Set's the TCP "window" field. The purpose is to cause the recipient * to fragment data on the response, thus evading IDS that triggers on * out going packets */ void tcp_set_window(unsigned char *px, size_t px_length, unsigned window); void template_set_ttl(struct TemplateSet *tmplset, unsigned ttl); void template_set_vlan(struct TemplateSet *tmplset, unsigned vlan); #endif ================================================ FILE: src/templ-tcp-hdr.c ================================================ /* This module edits an existing TCP packet, adding and removing options, setting the values of certain fields. From RFC793: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ TCP Window Scale Option (WSopt): Kind: 3 Length: 3 bytes +---------+---------+---------+ | Kind=3 |Length=3 |shift.cnt| +---------+---------+---------+ TCP Timestamps Option (TSopt): Kind: 8 Length: 10 bytes +-------+-------+---------------------+---------------------+ |Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)| +-------+-------+---------------------+---------------------+ 1 1 4 4 TCP Sack-Permitted Option: Kind: 4 +---------+---------+ | Kind=4 | Length=2| +---------+---------+ TCP SACK Option: Kind: 5 Length: Variable +--------+--------+ | Kind=5 | Length | +--------+--------+--------+--------+ | Left Edge of 1st Block | +--------+--------+--------+--------+ | Right Edge of 1st Block | +--------+--------+--------+--------+ | | / . . . / | | +--------+--------+--------+--------+ | Left Edge of nth Block | +--------+--------+--------+--------+ | Right Edge of nth Block | +--------+--------+--------+--------+ */ #include "templ-tcp-hdr.h" #include "templ-opts.h" #include "util-logger.h" #include "proto-preprocess.h" #include #include #include struct tcp_opt_t { const unsigned char *buf; size_t length; unsigned kind; bool is_found; }; struct tcp_hdr_t { size_t begin; size_t max; size_t ip_offset; unsigned char ip_version; bool is_found; }; /** * Do a memmove() of a chunk of memory within a buffer with bounds checking. */ static void safe_memmove(unsigned char *buf, size_t length, size_t to, size_t from, size_t chunklength) { if (chunklength + to > length) { fprintf(stderr, "+"); fflush(stderr); chunklength = length - to; } if (chunklength + from > length) { fprintf(stderr, "-"); fflush(stderr); chunklength = length - from; } memmove(buf + to, buf + from, chunklength); } /** * Do a memset() of a chunk of memory within a buffer with bounds checking */ static void safe_memset(unsigned char *buf, size_t length, size_t offset, int c, size_t chunklength) { if (chunklength + offset > length) { chunklength = length - offset; fprintf(stderr, "*"); fflush(stderr); } memset(buf + offset, c, chunklength); } /*************************************************************************** * A typical hexdump function, but dumps specifically the * section of a TCP header. An added feature is that it marks the byte * at "offset". This makes debugging easier, so I can see the * as I'm stepping through code. You'll see this commented-out throughout * the code. ***************************************************************************/ static void _HEXDUMP(const void *v, struct tcp_hdr_t hdr, size_t offset, const char *name) { const unsigned char *p = ((const unsigned char *)v) + hdr.begin + 20; size_t i; size_t len = hdr.max - hdr.begin + 8 - 20; printf("%s:\n", name); offset -= hdr.begin + 20; for (i=0; i> 4) * 4; } /*************************************************************************** * Does a consistency check of the whole packet, including IP header, * TCP header, and the options in the field. This is used * in the self-test feature after test cases, to make sure the packet * hasn't bee corrupted. ***************************************************************************/ static int _consistancy_check(const unsigned char *buf, size_t length, const void *payload, size_t payload_length) { struct PreprocessedInfo parsed; unsigned is_success; /* Parse the packet */ is_success = preprocess_frame(buf, (unsigned)length, 1 /*enet*/, &parsed); if (!is_success || parsed.found != FOUND_TCP) { fprintf(stderr, "[-] check: TCP header not found\n"); goto fail; } /* Check the lengths */ switch (parsed.ip_version) { case 4: if (parsed.ip_length + 14 != length) { fprintf(stderr, "[-] check: IP length bad\n"); goto fail; } break; case 6: break; default: fprintf(stderr, "[-] check: IPv?\n"); goto fail; } /* Validate TCP header options */ { size_t offset = parsed.transport_offset; size_t max = offset + _tcp_header_length(buf, offset); /* Get the start of the section of the header. This is defined * as 20 bytes into the TCP header. */ offset += 20; /* Enumerate any existing options one-by-one. */ while (offset < max) { unsigned kind; unsigned len; /* Get the option type (aka. "kind") */ kind = buf[offset++]; if (kind == 0x00) { /* EOL - end of options list * According to the spec, processing should stop here, even if * there are additional options after this point. */ break; } else if (kind == 0x01) { /* NOP - No-operation * This is a single byte option, used to pad other options to * even 4 byte boundaries. Padding is optional. */ continue; } /* If we've reached the end of */ if (offset > max) goto fail; if (offset == max) break; len = buf[offset++]; /* Check for corruption, the lenth field is inclusive, so should * equal at least two. It's maximum length should be bfore the end * of the packet */ if (len < 2 || len > (max-offset+2)) { goto fail; } offset += len - 2; } } /* Check the payload */ if (parsed.app_length != payload_length) goto fail; if (memcmp(buf + parsed.app_offset, payload, payload_length) != 0) goto fail; return 0; fail: return 1; } /*************************************************************************** * Find the TCP header in the packet. We can't be sure what's in the * current template because it could've been provided by the user, so * we instead parse it as if we've received it from the network wire. ***************************************************************************/ static struct tcp_hdr_t _find_tcp_header(const unsigned char *buf, size_t length) { struct tcp_hdr_t hdr = {0}; struct PreprocessedInfo parsed; unsigned is_success; /* * Parse the packet, telling us where the TCP header is. This works * for both IPv4 and IPv6, we care only about the TCP header portion. */ is_success = preprocess_frame(buf, /* the packet, including Ethernet hdr */ (unsigned)length, 1 /*enet*/, &parsed); if (!is_success || parsed.found != FOUND_TCP) { /* We were unable to parse a well-formatted TCP packet. This * might've been UDP or something. */ goto fail; } hdr.begin = parsed.transport_offset; hdr.max = hdr.begin + _tcp_header_length(buf, hdr.begin); hdr.ip_offset = parsed.ip_offset; hdr.ip_version = (unsigned char)parsed.ip_version; hdr.is_found = true; return hdr; fail: hdr.is_found = false; return hdr; } /*************************************************************************** * A quick macro at the start of for(;;) loops that enumerate all the * options in the ***************************************************************************/ static inline size_t _opt_begin(struct tcp_hdr_t hdr) { return hdr.begin + 20; /* start of field */ } /*************************************************************************** * A quick macro in the for(;;) loop that enumerates all the options * in the . It has three possibilities based on the KIND: * 0x00 - we've reached the end of the options-list * 0x01 - padding NOP byte, which we skipo * 0x?? - some option, the following byte is the length. We skip * that `len` bytes. ***************************************************************************/ static inline size_t _opt_next(struct tcp_hdr_t hdr, size_t offset, const unsigned char *buf) { unsigned kind = buf[offset]; if (kind == 0x00) { return hdr.max; } else if (kind == 0x01) { return offset + 1; } else if (offset + 2 > hdr.max) { return hdr.max; /* corruption */ } else { unsigned len = buf[offset+1]; if (len < 2 || offset + len > hdr.max) return hdr.max; /* corruption */ else return offset + len; } } /*************************************************************************** ***************************************************************************/ static void _HEXDUMPopt(const unsigned char *buf, size_t length, const char *name) { struct tcp_hdr_t hdr; hdr = _find_tcp_header(buf, length); if (!hdr.is_found) { fprintf(stderr, "[-] templ.tcp.hdr: failure\n"); } _HEXDUMP(buf, hdr, _opt_begin(hdr), name); } /*************************************************************************** * Search throgh the until we find the specified option, * 'kind', or reach the end of the list. An impossible 'kind', like 0x100, * will force finding the end of the list before padding starts. ***************************************************************************/ static size_t _find_opt(const unsigned char *buf, struct tcp_hdr_t hdr, unsigned in_kind, unsigned *nop_count) { size_t offset; /* This field is optional, if used, set it to zero */ if (nop_count) *nop_count = 0; /* enumerate all looking for a match */ for (offset = _opt_begin(hdr); offset < hdr.max; offset = _opt_next(hdr, offset, buf)) { unsigned kind; /* get the option type/kind */ kind = buf[offset]; /* Stop search if we hit an EOL marker */ if (kind == 0x00) break; /* Stop search when we find our option */ if (kind == in_kind) break; /* Count the number of NOPs leading up to where we end */ if (nop_count) { if (kind == 0x01) (*nop_count)++; else (*nop_count) = 0; } } return offset; } /*************************************************************************** * Search the TCP header's field for the specified kind/type. * Typical kinds of options are MSS, window scale, SACK, timestamp. ***************************************************************************/ static struct tcp_opt_t tcp_find_opt(const unsigned char *buf, size_t length, unsigned in_kind) { struct tcp_opt_t result = {0}; struct tcp_hdr_t hdr; size_t offset; /* Get the TCP header in the packet */ hdr = _find_tcp_header(buf, length); if (!hdr.is_found) goto fail; /* Search for a matchin