pax_global_header00006660000000000000000000000064146147360260014523gustar00rootroot0000000000000052 comment=635faf96e43e7119e6c7aac9e480fb42573c1d0f weakforced-2.10.2/000077500000000000000000000000001461473602600137175ustar00rootroot00000000000000weakforced-2.10.2/.github/000077500000000000000000000000001461473602600152575ustar00rootroot00000000000000weakforced-2.10.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001461473602600174425ustar00rootroot00000000000000weakforced-2.10.2/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000012301461473602600221300ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "[BUG]" labels: bug assignees: neilcook --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **OS (please complete the following information):** - OS: [e.g. Centos, RHEL, Debian] - Version [e.g. 7, stretch] **Additional context** Add any other context about the problem here. weakforced-2.10.2/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011531461473602600231670ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "[FEATURE]" labels: enhancement assignees: neilcook --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. weakforced-2.10.2/.github/workflows/000077500000000000000000000000001461473602600173145ustar00rootroot00000000000000weakforced-2.10.2/.github/workflows/builder.yml000066400000000000000000000011141461473602600214620ustar00rootroot00000000000000--- name: 'Test package building for specific distributions' on: push: pull_request: schedule: - cron: '0 22 * * 2' jobs: build: name: build.sh # on a ubuntu-20.04 VM runs-on: ubuntu-20.04 strategy: matrix: os: - centos-7 - ol-8 - el-9 - amazon-2 - debian-buster - debian-bullseye steps: - uses: actions/checkout@v3 with: fetch-depth: 0 # for correct version numbers submodules: recursive - run: builder/build.sh -e 1 -v ${{ matrix.os }} weakforced-2.10.2/.github/workflows/docker-minimal.yml000066400000000000000000000011331461473602600227300ustar00rootroot00000000000000--- name: 'Build minimal docker image' on: push: pull_request: jobs: docker_test: name: docker_test runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: fetch-depth: 0 submodules: recursive - run: cd docker/wforce_image && docker buildx build -f Dockerfile.minimal weakforced -t powerdns/wforce-minimal:`git describe --tags` --load - run: cd docker && bash docker_push.sh "powerdns/wforce-minimal" env: DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} weakforced-2.10.2/.github/workflows/docker.yml000066400000000000000000000037411461473602600213130ustar00rootroot00000000000000--- name: 'Build docker image and test' on: push: pull_request: jobs: docker_test: name: docker_test runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: fetch-depth: 0 submodules: recursive - name: Install dependencies run: sudo apt-get update && sudo apt-get -qq -y install gcc g++ cmake libboost-all-dev libcurl4-openssl-dev libgetdns-dev libhiredis-dev libmaxminddb-dev libluajit-5.1-dev libprotobuf-dev libreadline-dev libssl-dev libsodium-dev libsystemd-dev libyaml-cpp-dev libjsoncpp-dev uuid-dev libz-dev libtool pkg-config protobuf-compiler pandoc wget autoconf automake - name: Download prometheus-cpp run: git clone https://github.com/jupp0r/prometheus-cpp.git && cd prometheus-cpp && git checkout tags/v1.0.1 -b v1.0.1 - name: Build and Install prometheus-cpp run: cd prometheus-cpp && mkdir _build && cd _build && cmake .. -DBUILD_SHARED_LIBS=off -DENABLE_PULL=off -DENABLE_PUSH=off -DENABLE_COMPRESSION=off -DENABLE_TESTING=off -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_POSITION_INDEPENDENT_CODE=ON && make && sudo make install - name: Download Drogon run: git clone https://github.com/drogonframework/drogon.git && cd drogon && git checkout tags/v1.9.1 -b v1.9.1 - name: Build and Install Drogon run: cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_ORM=OFF -DCMAKE_BUILD_TYPE=Release && make && sudo make install - run: sudo sysctl -w vm.max_map_count=262144 - run: autoreconf -i - run: ./configure --enable-docker --disable-dns --disable-sodium --disable-geoip - run: cd docker/wforce_image && make test_wforce_image - run: cd docker/wforce_image && make build_wforce_image - run: cd docker && bash docker_push.sh "powerdns/wforce" env: DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}weakforced-2.10.2/.github/workflows/pubtests.yml000066400000000000000000000020251461473602600217070ustar00rootroot00000000000000--- name: Publish Test Results on: workflow_run: workflows: ["Call reusable regression workflow"] types: - completed permissions: {} jobs: test-results: name: Publish Test Results runs-on: ubuntu-latest if: github.event.workflow_run.conclusion != 'skipped' permissions: checks: write # needed unless run with comment_mode: off pull-requests: write # required by download step to access artifacts API actions: read steps: - name: Download and Extract Artifacts uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 with: run_id: ${{ github.event.workflow_run.id }} path: artifacts - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 with: commit: ${{ github.event.workflow_run.head_sha }} event_file: artifacts/Event File/event.json event_name: ${{ github.event.workflow_run.event }} files: "artifacts/**/**/*.xml"weakforced-2.10.2/.github/workflows/regression.yml000066400000000000000000000005061461473602600222200ustar00rootroot00000000000000--- name: 'Call reusable regression workflow' on: push: pull_request: schedule: - cron: '0 22 * * 3' jobs: regression_clang: uses: ./.github/workflows/regression_reusable.yml with: compiler: clang regression_gcc: uses: ./.github/workflows/regression_reusable.yml with: compiler: gccweakforced-2.10.2/.github/workflows/regression_reusable.yml000066400000000000000000000045251461473602600241070ustar00rootroot00000000000000--- name: 'Run regression tests' on: workflow_call: inputs: compiler: description: 'Compiler to use' type: string required: true jobs: run-regression: name: regression-${{ inputs.compiler }} runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: fetch-depth: 5 submodules: recursive - name: Install dependencies run: sudo apt-get update && sudo apt-get -qq -y install gcc g++ cmake libboost-all-dev libcurl4-openssl-dev libgetdns-dev libhiredis-dev libmaxminddb-dev libluajit-5.1-dev libprotobuf-dev libreadline-dev libssl-dev libsodium-dev libsystemd-dev libyaml-cpp-dev libjsoncpp-dev uuid-dev libz-dev libtool pkg-config protobuf-compiler pandoc wget autoconf automake - name: Download prometheus-cpp run: git clone https://github.com/jupp0r/prometheus-cpp.git && cd prometheus-cpp && git checkout tags/v1.0.1 -b v1.0.1 && echo 'include(CPack)' >> CMakeLists.txt - name: Build and Install prometheus-cpp run: cd prometheus-cpp && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_SHARED_LIBS=off -DENABLE_PULL=off -DENABLE_PUSH=off -DENABLE_COMPRESSION=off -DENABLE_TESTING=off -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_POSITION_INDEPENDENT_CODE=ON && make && sudo make install - name: Download Drogon run: git clone https://github.com/drogonframework/drogon.git && cd drogon && git checkout tags/v1.9.1 -b v1.9.1 - name: Build and Install Drogon run: cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_ORM=OFF -DCMAKE_BUILD_TYPE=Release && make && sudo make install - run: sudo sysctl -w vm.max_map_count=262144 - run: autoreconf -i - run: ./configure --enable-trackalert - run: cd docker && make regression-${{ inputs.compiler }} - run: make dist - run: make distcheck - name: Upload Test Results if: always() uses: actions/upload-artifact@v3 with: name: Test Results path: | docker/tmp/*.xml event_file: name: "Event File" runs-on: ubuntu-latest steps: - name: Upload uses: actions/upload-artifact@v3 with: name: Event File path: ${{ github.event_path }} weakforced-2.10.2/.gitignore000066400000000000000000000013331461473602600157070ustar00rootroot00000000000000.deps .dirstamp .history /.tags* /aclocal.m4 /autom4te.cache /compile /config.guess /config.h /config.h.in /config.log /config.status /config.sub /configure /depcomp /install-sh /libtool /ltmain.sh Makefile Makefile.in /missing /stamp-h1 /*.service /regexes.yaml test-driver testrunner testrunner.trs *.libs/ *.DSYM/ *~ libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4 # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # Compressed files *.gz *.bz2 # Other .DS_Store # Protobuf files *.pb.h *.pb.cc # Log files *.log weakforced-2.10.2/.gitmodules000066400000000000000000000001321461473602600160700ustar00rootroot00000000000000[submodule "builder"] path = builder url = https://github.com/PowerDNS/pdns-builder.git weakforced-2.10.2/CHANGELOG.md000066400000000000000000000146511461473602600155370ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [2.10.2] ### Changed - Fixed LuaState selection algorithm to use a free pool, which should lead to faster/more consistent selection of lua states by threads. ### Added - CI now builds an additional image 'powerdns/wforce-minimal' using alpine for more secure and much smaller image ## [2.10.1] ### Changed - Fixed bug in GeoIP2 lookups where return values were not populated ## [2.10.0] ### Added - Add Enterprise Linux 9 Build Target - Option to use OpenSSL instead of Libsodium for encryption ### Removed - Remove Legacy GeoIP from Packages and Dockerfiles/Images - Remove the report_api from weakforced entirely ### Changed - Move to pytest instead of nose for regression tests ## [2.8.0] ### Added - Support ELK 7.x Stack - Support Date Expansion in WebHook URLs - Enable IP and Login substitution in blocklist return messages - Add config option to disable password for /metrics endpoint - Support redis usernames and passwords for redis authentication - Support hostnames for redis configuration in addition to IP addresses ## Changed - Fix an issue where IPv6 ComboAddress returned zero port number (which caused v6 HTTP listen addresses to not work) - Set V6ONLY socket option to stop v6 sockets from managing v4 addresses for replication - Return the IP address of the client in JSON of ACL denied response ## [2.6.1] - Fix issue where wforce was complaining about not being able to create tmp file on startup - Fix timing issue whereby the webserver was not started before syncDB leading to syncDone failures - Use debian bullseye-slim in wforce docker image - Fix issue in wforce docker image where the default config file was overridden with a volume mount but not used ## [2.6.0] - Support HTTPS in webserver using libdrogon - Add support for configuring TLS behaviour of outbound HTTPS connections - Remove support for building debian stretch packages - Change behaviour of default ACLs such that they are overridden by setACL() - Build for debian bullseye ## [2.4.1] ### Added - New API and Lua commands to dynamically manage siblings - Siblings can now have unique encryption keys - Amazon Linux packaging target for pdns-builder ### Changed - Fix issue where replication length bytes can be truncated causing syncDB problems ## [2.4.0] ### Added - New wf_dump_entries tool to dump stats DBs to file - Support for new "forwarding" type in replication messages - Support for Prometheus via the new /metrics REST endpoint - Enable TCP keepalive for redis connections - Configurable timeout for R/W on redis connections ### Changed - Fix duplicate command stats under some circumstances ## [2.2.2] ### Added - Support building debian buster packages - StatsDBs can now be sharded for better performance - StatsDB expiry thread now runs more often by default - StatsDB expiry thread sleep time is now configurable - Support for Kafka REST Proxy in webhooks ### Changed - Fix control socket leak when client closes connection immediately ## [2.2.1] ### Changed - Fix wforce crash in Sibling send thread triggered by syncDB operation ## [2.2.0] ### Added - Lookup individual GeoIP2 values (Unsigned Integer, String, Double/Float and Boolean) - Add session_id to wforce LoginTuple - The report_api is now deployable via proper packaging, systemd and gunicorn - Custom GET Endpoints supporting non-JSON return values - New Kibana Reports and Dashboard to view effectiveness of wforce policy - New "type" field added to built-in webhooks - Built-in whitelists added in addition to built-in blacklists - New checkXXXBlacklist (and Whitelist) function in Lua - Built-in black/whitelisting can be disabled and checked from Lua instead - Thread names support - Blacklisting return messages are configurable - Add support for TCP keepalive - New functions to retrieve the custom return message for blacklisted IPs, Logins and IP/Logins. ### Changed - Fix typo in wforce.conf.example blackistLogin->blacklistLogin - Python3 support throughout wforce (regression tests and deployment) - Uses "bytes" instead of "string" in replication, which avoids protobuf errors for non-UTF-8 StatsDB keys or values. - Remove ctpl from wforce - Sibling threads now use explicit std::queue instead of ctpl - Perform replication to siblings asynchronously instead of synchronously, preventing delays at startup and in REST API functions which triggered replication. ## [2.0.0] ### Added - Add configuration setting "setNumWebHookConnsPerThread" - Add support for querying replication status in showStringStatsDB() - Add sibling received success/fail stats to sibling() command - New custom stats framework - New stats for all commands, including custom commands - GeoIP2 support (MMDB-style DBs) - New resetField() function for statsDBs - Support for building packages using pdnsbuilder - Configurable accuracy for HLL and CountMin types - DB Synchronization for newly started wforce instances - Add configuration settings "addSyncHost" and "setMinSyncHostUptime" - Add configuration setting "setWebHookTimeoutSecs" - Support for replication over TCP - Customizable log facility via a command line option - Example logstash config and elasticsearch template in docs ### Deprecated - GeoIP Legacy support ### Changed - Refactor webhooks to use libcurl multi interface for performance and deprecate per-webhook "num_conns" config - Change log level of informational messages from warn to notice/info ## [1.4.3] ### Fixed - Fix broken setVerboseAllowLog() function ## [1.4.2] ### Fixed - Fix memory leak in statsDBs ## [1.4.1] ### Fixed - Fix issue where mapped v4 addresses in v6 were not handled correctly ## [1.4.0] - 2017-10-04 ### Added - Support for additional GeoIP DBs: City and ISP - Support for reloading GeoIP databases - Blacklists now support IP netmasks as well as individual IP addresses - Configurable timeout for Redis connections - New Lua logging function: debugLog - Webhook support for Basic Authentication - Support for parsing device_id from http, imap and OX mobile clients - New tls parameter to report/allow commands - New console commands - showPerfStats() and reloadGeoIPDBs() ### Changed - Wforce daemon changes working directory to config file directory - Use Content-Length instead of chunked encoding for Webhooks ### Removed ### Deprecated ### Fixed - Fixed a two bugs in dependent getdns library that could cause crashes ### Security weakforced-2.10.2/LICENSE000066400000000000000000001045131461473602600147300ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . weakforced-2.10.2/Makefile.am000066400000000000000000000006021461473602600157510ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 if WITH_DOCKER BUILD_DOCKER = docker endif if WITH_TRACKALERT BUILD_TRACKALERT = trackalert endif SUBDIRS=ext docs common wforce $(BUILD_TRACKALERT) $(BUILD_DOCKER) LOGSTASH_DIST = elk/logstash/config/logstash.conf elk/logstash/templates/wforce_template.json elk/kibana/kibana_saved_objects.ndjson EXTRA_DIST= README.md LICENSE CHANGELOG.md $(LOGSTASH_DIST) weakforced-2.10.2/NOTICE000066400000000000000000000014361461473602600146270ustar00rootroot00000000000000This program is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. In addition, for the avoidance of any doubt, permission is granted to link this program with OpenSSL and to (re)distribute the binaries produced as the result of such linking. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. weakforced-2.10.2/README.md000066400000000000000000000474241461473602600152110ustar00rootroot00000000000000Weakforced ---------- The goal of 'wforce' is to detect brute forcing of passwords across many servers, services and instances. In order to support the real world, brute force detection policy can be tailored to deal with "bulk, but legitimate" users of your service, as well as botnet-wide slowscans of passwords. The aim is to support the largest of installations, providing services to hundreds of millions of users. The current version of weakforced is not quite there yet, although it certainly scales to support up to ten million users, if not more. The limiting factor is number of logins per second at peak. Wforce is a project by Dovecot, PowerDNS and Open-Xchange. For historical reasons, it lives in the PowerDNS github organization. If you have any questions, email neil.cook@open-xchange.com. For detailed technical documentation, please go to [https://powerdns.github.io/weakforced/](https://powerdns.github.io/weakforced/). Here is how it works: * Report successful logins via JSON http-api * Report unsuccessful logins via JSON http-api * Query if a login should be allowed to proceed, should be delayed, or ignored via http-api * API for querying the status of logins, IP addresses etc. * Runtime console for server introspection wforce is aimed to receive message from services like: * IMAP * POP3 * Webmail logins * FTP logins * Authenticated SMTP * Self-service logins * Password recovery services By gathering failed and successful login attempts from as many services as possible, brute forcing attacks as well as other suspicious behaviour can be detected and prevented more effectively. Inspiration: http://www.techspot.com/news/58199-developer-reported-icloud-brute-force-password-hack-to-apple-nearly-six-month-ago.html Installing ---------- Docker: There is a docker image hosted on docker hub, see [https://powerdns.github.io/weakforced/](https://powerdns.github.io/weakforced/) for more details. From GitHub: The easy way: ``` $ git clone https://github.com/PowerDNS/weakforced.git $ cd weakforced $ git submodule init $ git submodule update $ builder/build.sh debian-bullseye | debian-stretch | centos-7 | ol-8 | amazon-2 ``` This will build packages for the appropriate OS. You will need docker and docker-compose for the builder to work. The hard way: ``` $ git clone https://github.com/PowerDNS/weakforced.git $ cd weakforced $ autoreconf -i $ ./configure $ make ``` This requires recent versions of libtool, automake and autoconf to be installed. It also requires: * A compiler supporting C++ 17 * Lua 5.1+ development libraries (or LuaJIT if you configure --with-luajit) * Boost 1.61+ * Protobuf compiler and protobuf development libraries * Getdns development libraries (if you want to use the DNS lookup functionality) * libsodium * python + virtualenv for regression testing * libgeoip-dev for GeoIP support * libsystemd-dev for systemd support * pandoc for building the manpages * libcurl-dev (OpenSSL version) * libhiredis-dev * libssl-dev * libprometheus-cpp (https://github.com/jupp0r/prometheus-cpp) * libmaxminddb-dev * libyaml-cpp-dev * libdrogon (https://github.com/drogonframework/drogon) - Used for the HTTP server * libjsoncpp-dev * libuuid-dev * libz-dev * docker for regression testing * python3 rather than python2 * python-bottle for regression testing of webhooks To build on OS X, `brew install readline` and use `./configure PKG_CONFIG_PATH= LDFLAGS=-L/usr/local/opt/readline/lib CPPFLAGS=-I/usr/local/opt/readline/include` Add --with-luajit to the end of the configure line if you want to use LuaJIT. Policies -------- There is a sensible, if very simple, default policy in wforce.conf (running without this means *no* policy), and extensive support for crafting your own policies using the insanely great Lua scripting language. Note that although there is a single Lua configuration file, the canonicalize, reset, report and allow functions run in different lua states from the rest of the configuration. This mostly "just works", but may lead to unexpected behaviour such as running Lua commands at the server Lua prompt, and getting multiple answers (because Lua commands are passed to all Lua states). Sample: ```lua -- set up the things we want to track field_map = {} -- use hyperloglog to track cardinality of (failed) password attempts field_map["diffFailedPasswords"] = "hll" -- track those things over 6x10 minute windows newStringStatsDB("OneHourDB", 600, 6, field_map) -- this function counts interesting things when "report" is invoked function twreport(lt) sdb = getStringStatsDB("OneHourDB") if (not lt.success) then sdb:twAdd(lt.remote, "diffFailedPasswords", lt.pwhash) addrlogin = lt.remote:tostring() .. lt.login sdb:twAdd(addrlogin, "diffFailedPasswords", lt.pwhash) end end function allow(lt) sdb = getStringStatsDB("OneHourDB") if(sdb:twGet(lt.remote, "diffFailedPasswords") > 50) then return -1, "", "", {} -- BLOCK! end // concatenate the IP address and login string addrlogin = lt.remote:tostring() .. lt.login if(sdb:twGet(addrlogin, "diffFailedPasswords") > 3) then return 3, "tarpitted", "diffFailedPasswords", {} -- must wait for 3 seconds end return 0, "", "", {} -- OK! end ``` Many more metrics are available to base decisions on. Some example code is in [wforce.conf](wforce/wforce.conf), and more extensive examples are in [wforce.conf.example](wforce/wforce.conf.example). For full [documentation](docs/manpages), use "man wforce.conf". To report (if you configured with 'webserver("127.0.0.1:8084", "secret")'): ```bash $ for a in {1..101} do curl -X POST -H "Content-Type: application/json" --data '{"login":"ahu", "remote": "127.0.0.1", "pwhash":"1234'$a'", "success":"false"}' \ http://127.0.0.1:8084/?command=report -u wforce:secret done ``` This reports 101 failed logins for one user, but with different password hashes. Now to look up if we're still allowed in: ```bash $ curl -X POST -H "Content-Type: application/json" --data '{"login":"ahu", "remote": "127.0.0.1", "pwhash":"1234"}' \ http://127.0.0.1:8084/?command=allow -u wforce:super {"status": -1, "msg": "diffFailedPasswords"} ``` It appears we are not! You can also provide additional information for use by weakforce using the optional "attrs" object. An example: ```bash $ curl -X POST -H "Content-Type: application/json" --data '{"login":"ahu", "remote": "127.0.0.1", "pwhash":"1234", "attrs":{"attr1":"val1", "attr2":"val2"}}' \ http://127.0.0.1:8084/?command=allow -u wforce:super {"status": 0, "msg": ""} ``` An example using the optional attrs object using multi-valued attributes: ```bash $ curl -X POST -H "Content-Type: application/json" --data '{"login":"ahu", "remote": "127.0.0.1", "pwhash":"1234", "attrs":{"attr1":"val1", "attr2":["val2","val3"]}}' \ http://127.0.0.1:8084/?command=allow -u wforce:super {"status": 0, "msg": ""} ``` There is also a command to reset the stats for a given login and/or IP Address, using the 'reset' command, the logic for which is also implemented in Lua. The default configuration for reset is as follows: ```lua function reset(type, login, ip) sdb = getStringStatsDB("OneHourDB") if (string.find(type, "ip")) then sdb:twReset(ip) end if (string.find(type, "login")) then sdb:twReset(login) end if (string.find(type, "ip") and string.find(type, "login")) then iplogin = ip:tostring() .. login sdb:twReset(iplogin) end return true end ``` To test it out, try the following to reset the login 'ahu': ```bash $ curl -X POST -H "Content-Type: application/json" --data '{"login":"ahu"}'\ http://127.0.0.1:8084/?command=reset -u wforce:super {"status": "ok"} ``` You can reset IP addresses also: ```bash $ curl -X POST -H "Content-Type: application/json" --data '{"ip":"128.243.21.16"}'\ http://127.0.0.1:8084/?command=reset -u wforce:super {"status": "ok"} ``` Or both in the same command (this helps if you are tracking stats using compound keys combining both IP address and login): ```bash $ curl -X POST -H "Content-Type: application/json" --data '{"login":"ahu", "ip":"FE80::0202:B3FF:FE1E:8329"}'\ http://127.0.0.1:8084/?command=reset -u wforce:super {"status": "ok"} ``` Finally there is a "ping" command, to check the server is up and answering requests: ```bash $ curl -X GET http://127.0.0.1:8084/?command=ping -u wforce:super {"status": "ok"} ``` Console ------- Available over TCP/IP, like this: ```lua setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") controlSocket("0.0.0.0:4004") ``` Launch wforce as a daemon (`wforce --daemon`), to connect, run `wforce -c`. Comes with autocomplete and command history. If you put an actual IP address in place of 0.0.0.0, you can use the same config to listen and connect remotely. To get some stats, try: ```lua > stats() 40 reports, 8 allow-queries, 40 entries in database ``` The [wforce manpage](docs/manpages/wforce.1.md) describes the command-line options and all the possible console commands in more detail. Spec ---- Wforce accepts reports with 4 mandatory fields plus multiple optional fields. Mandatory: * login (string): the user name or number or whatever * remote (ip address): the address the user arrived on * pwhash (string): a highly truncated hash of the password used * success (boolean): was the login a success or not? Optional: * policy_reject (boolean) - If the login was not successful only because of a policy-based reject from wforce (i.e. the username and password were correct). * attrs (json object): additional information about the login. For example, attributes from a user database. * device_id (string) - A string that represents the device that the user logged in from. For HTTP this would typically be the User-Agent string, and for IMAP it would be the IMAP client ID command string. * protocol (string) - A string representing the protocol used to login, e.g. "http", "imap", "pop3". * tls (boolean) - Whether or not the login was secured with TLS. The entire HTTP API is [documented](docs/swagger/wforce_api.7.yml) using the excellent OpenAPI (swagger) specification. The pwhash field deserves some clarification. In order to distinguish actual brute forcing of a password, and repeated incorrect but identical login attempts, we need some marker that tells us if passwords are different. Naively, we could hash the password, but this would spread knowledge of secret credentials beyond where it should reasonably be. Even if we salt and iterate the hash, or use a specific 'slow' hash, we're still spreading knowledge. However, if we take any kind of hash and truncate it severely, for example to 12 bits, the hash tells us very little about the password itself - since one in 4096 random strings will match it anyhow. But for detecting multiple identical logins, it is good enough. For additional security, hash the login name together with the password - this prevents detecting different logins that might have the same password. NOTE: wforce does not require any specific kind of hashing scheme, but it is important that all services reporting successful/failed logins use the same scheme! When in doubt, try: ```SQL TRUNCATE(SHA256(SECRET + LOGIN + '\x00' + PASSWORD), 12) ``` Which denotes to take the first 12 bits of the hash of the concatenation of a secret, the login, a 0 byte and the password. Prepend 4 0 bits to get something that can be expressed as two bytes. API Calls --------- We can call 'report', and 'allow' commands. The optional 'attrs' field enables the client to send additional data to weakforced. To report, POST to /?command=report a JSON object with fields from the LoginTuple as described above. To request if a login should be allowed, POST to /?command=allow, again with the LoginTuple. The result is a JSON object with a "status" field. If this is -1, do not perform login validation (i.e. provide no clue to the client if the password was correct or not, or even if the account exists). If 0, allow login validation to proceed. If a positive number, sleep this many seconds until allowing login validation to proceed. Custom API Endpoints -------------------- You can create custom API commands (REST Endpoints) using the following configuration: ```lua setCustomEndpoint("custom", customfunc) ``` which will create a new API command "custom", which calls the Lua function "customfunc" whenever that command is invoked. Parameters to custom commands are always in the same form, which is key-value pairs wrapped in an 'attrs' object. For example, the following parameters sents as json in the message body would be valid: ```json { "attrs" : { "key" : "value" }} ``` Custom functions return values are also key-value pairs, this time wrapped in an 'r_attrs' object, along with a boolean success field, for example: ```json { "r_attrs" : { "key" : "value" }, "success" : true} ``` An example configuration for a custom API endpoint would look like: ```lua function custom(args) for k,v in pairs(args.attrs) do infoLog("custom func argument attrs", { key=k, value=v }); end -- return consists of a boolean, followed by { key-value pairs } return true, { key=value } end setCustomEndpoint("custom", custom) ``` An example curl command would be: ```lua % curl -v -X POST -H "Content-Type: application/json" --data '{"attrs":{"login1":"ahu", "remote": "127.0.0.1", "pwhash":"1234"}}' http://127.0.0.1:8084/?command=custom -u wforce:super {"r_attrs": {}, "success": true} ``` WebHooks --------- It is possible to configure webhooks, which get called whenever specific events occur. To do this, use the "addWebHook" configuration command. For example: ```lua config_keys={} config_keys["url"] = "http://webhooks.example.com:8080/webhook/" config_keys["secret"] = "verysecretcode" events = { "report", "allow" } addWebHook(events, config_keys) ``` The above will call the webhook at the specified url, for every report and allow command received, with the body of the POST containing the original json data sent to wforce. For more information use ["man wforce.conf"](docs/manpages/wforce.conf.5.md) and ["man wforce_webhook"](docs/manpages/wforce_webhook.5.md). Custom WebHooks ---------------- Custom webhooks can also be defined, which are not invoked based on specific events, but instead from Lua. Configuration is similar to normal webhooks: ```lua config_keys={} config_keys["url"] = "http://webhooks.example.com:8080/webhook/" config_keys["secret"] = "verysecretcode" config_keys["content-type"] = "application/json" addCustomWebHook("mycustomhook", config_keys) ``` However, the webhook will only be invoked via the Lua "runCustomWebHook" command, for example: ```lua runCustomWebHook(mycustomhook", "{ \"foo\":\"bar\" }") ``` The above command will invoke the custom webhook "mycustomhook" with the data contained in the second argument, which is simply a Lua string. No parsing of the data is performed, however the Content-Type of the webhook, which defaults to application/json can be customized as shown above. Blacklists ---------- Blacklisting capability is provided via either REST endpoints or Lua commands, to add/delete IP addresses, logins or IP:login tuples from the Blacklist. Blacklist information can be replicated (see below), and also optionally persisted in a Redis DB. Use "man wforce.conf" to learn more about the blacklist commands. Load balancing: siblings ------------------------ For high-availability or performance reasons it may be desireable to run multiple instances of wforce. To present a unified view of status however, these instances then need to share data. To do so, wforce implements a simple knowledge-sharing system. The original version of wforce simply broadcast all received report tuples (best effort, UDP) to all siblings. However the latest version only broadcasts incremental changes to the underlying state databases, namely the stats dbs and the blacklist. The sibling list is parsed such that we don't broadcast messages to ourselves accidentally, and can thus be identical across all servers. Even if you configure siblings, stats db data is not replicated by default. To do this, use the "twEnableReplication()" command on each stats db for which you wish to enable replication. Blacklist information is automatically replicated if you have configured siblings. To define siblings, use: ```lua setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") addSibling("192.168.1.79") addSibling("192.168.1.30") addSibling("192.168.1.54") siblingListener("0.0.0.0") ``` The first line sets the authentication and encryption key for our sibling communications. To make your own key (recommended), run `makeKey()` on the console and paste the output in all your configuration files. This last line configures that we also listen to our other siblings (which is nice). The default port is 4001, the protocol is UDP. To view sibling stats: ```lua > siblings() Address Send Successes Send Failures Rcv Successes Rcv Failures Note 192.168.1.79:4001 18 7 0 0 192.168.1.30:4001 25 0 0 0 192.168.1.54:4001 0 0 0 0 Self ``` With this setup, several wforces are all kept in sync, and can be load balanced behind (for example) haproxy, which incidentally can also offer SSL. GeoIP2 Support ------------- GeoIP support is provided using the GeoIP2 Maxmind APIs DBs (i.e. DBs ending in .mmdb). This is the preferred integration to use, as support for GeoIP Legacy DBs will be discontinued by Maxmind in 2019. GeoIP2 DBs are represented by a Lua object that is created with the following call: ```lua newGeoIP2DB("Name", "/path/to/file.mmdb") ``` The Lua object is retrieved with the following call: ```lua local mygeodb = getGeoIP2DB("Name") ``` You can then lookup information using the following calls: * lookupCountry() - Returns the 2 letter country code associated with the IP address * lookupISP() - Returns the name of the ISP associated with the IP address (requires the Maxmind ISP DB, which is only available on subscription) * lookupCity - Rather than only returning a city name, this call returns a Lua table which includes the following information: * country_code * country_name * region * city * postal_code * continent_code * latitude * longitude For example: ```lua local geoip_data = mygeodp:lookupCity(newCA("128.243.21.16")) print(geoip_data.city) print(geoip_data.longitude) print(geoip_data.latitude) ``` Legacy GeoIP Support ------------- Support for legacy GeoIP databases (i.e. ending in .dat) is deprecated, since Maxmind will be discontinuing support for them in 2019. Three types of GeoIP lookup are supported: * Country lookups - Initialized with initGeoIPDB() and looked up with lookupCountry() * ISP Lookups - Initialized with initGeoIPISPDB() and looked up with lookupISP() * City Lookup - Initialized with initGeoIPCityDB() and looked up with lookupCity() The Country and ISP lookups return a string, while lookupCity() returns a Lua map consisting of the following keys: * country_code * country_name * region * city * postal_code * continent_code * latitude * longitude For example: ```lua local geoip_data = lookupCity(newCA("128.243.21.16")) print(geoip_data.city) ``` When a DB is initialized, wforce attempts to open both v4 and v6 versions of the database. If either is not found an error is thrown, so make sure both ipv4 and v6 versions of each DB are installed. Additionally, when using the free/lite versions of the databases, you may see errors such as "initGeoIPCityDB(): Error initialising GeoIP (No geoip v6 city db available)". This is usually because the filenames for the "lite" DBs are not the same as the expected filenames for the full DBs, specifically all files must start with GeoIP rather than GeoLite. Creating symbolic links to the expected filenames will fix this problem, for example: ``` ln -s GeoLiteCityv6.dat GeoIPCityv6.dat ``` weakforced-2.10.2/builder/000077500000000000000000000000001461473602600153455ustar00rootroot00000000000000weakforced-2.10.2/builder-support/000077500000000000000000000000001461473602600170575ustar00rootroot00000000000000weakforced-2.10.2/builder-support/debian/000077500000000000000000000000001461473602600203015ustar00rootroot00000000000000weakforced-2.10.2/builder-support/debian/compat000066400000000000000000000000021461473602600214770ustar00rootroot000000000000009 weakforced-2.10.2/builder-support/debian/control000066400000000000000000000035651461473602600217150ustar00rootroot00000000000000Source: wforce Maintainer: Neil Cook Section: web Priority: optional Standards-Version: 3.9.2 Build-Depends: cdbs, autoconf, automake, debhelper (>= 9.20160709), dh-autoreconf, libboost-all-dev (>= 1.4.0), libcurl4-openssl-dev, libgetdns-dev, libhiredis-dev, libmaxminddb-dev, libluajit-5.1-dev, libprotobuf-dev, libreadline-dev, libssl-dev, libsodium-dev, libsystemd-dev, libyaml-cpp-dev, libjsoncpp-dev, uuid-dev, libz-dev, libtool, pkg-config, protobuf-compiler, pandoc, wget Package: wforce Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Daemon for detecting brute force attacks The goal of 'wforce' is to detect brute forcing of passwords across many servers, services and instances. In order to support the real world, brute force detection policy can be tailored to deal with "bulk, but legitimate" users of your service, as well as botnet-wide slowscans of passwords. The aim is to support the largest of installations, providing services to hundreds of millions of users. Package: wforce-trackalert Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Longterm abuse data reporting and alerter Trackalert is designed to be an optional service to complement wforce. Whereas wforce provides a toolkit to combat abuse of logins such as password brute forcing in realtime, trackalert is designed to look at abuse asynchronously, using long-term report data stored in an external DB such as elasticsearch, and to send alerts on potential login abuse. weakforced-2.10.2/builder-support/debian/rules000077500000000000000000000007231461473602600213630ustar00rootroot00000000000000#!/usr/bin/make -f # # See debhelper(7) (uncomment to enable) # output every command that modifies files on the build system. #DH_VERBOSE = 1 # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/default.mk %: dh $@ \ --with autotools-dev \ --parallel \ --with systemd override_dh_auto_configure: dh_auto_configure -- \ --sysconfdir=/etc/wforce \ --with-luajit \ --enable-trackalert weakforced-2.10.2/builder-support/debian/source/000077500000000000000000000000001461473602600216015ustar00rootroot00000000000000weakforced-2.10.2/builder-support/debian/source/format000066400000000000000000000000151461473602600230100ustar00rootroot000000000000003.0 (quilt) weakforced-2.10.2/builder-support/debian/wforce-trackalert.docs000066400000000000000000000000521461473602600245670ustar00rootroot00000000000000trackalert/README.md LICENSE CHANGELOG.md weakforced-2.10.2/builder-support/debian/wforce-trackalert.install000066400000000000000000000001301461473602600253020ustar00rootroot00000000000000usr/bin/trackalert usr/lib/systemd/system/trackalert.service etc/wforce/trackalert.conf weakforced-2.10.2/builder-support/debian/wforce-trackalert.manpages000066400000000000000000000001331461473602600254320ustar00rootroot00000000000000debian/tmp/usr/share/man/man1/trackalert.1 debian/tmp/usr/share/man/man5/trackalert.conf.5 weakforced-2.10.2/builder-support/debian/wforce-trackalert.postinst000077500000000000000000000020101461473602600255210ustar00rootroot00000000000000#!/bin/sh # # set -e TRACKALERTCONF=/etc/wforce/trackalert.conf case "$1" in configure) if [ -z "`getent group wforce`" ]; then addgroup --quiet --system wforce fi if [ -z "`getent passwd wforce`" ]; then echo -n "Creating user and group wforce..." adduser --quiet --system --home /var/spool/wforce --shell /bin/false --ingroup wforce --disabled-password --disabled-login --gecos "wforce" wforce echo "done" fi echo -n "Modifying trackalert.conf to replace password and key..." SETKEY=`echo "makeKey()" | trackalert | grep setKey` WEBPWD=`dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 | rev | cut -b 2-14 | rev` sed -e "s#--WEBPWD#$WEBPWD#" -e "s#--SETKEY#$SETKEY#" -i $TRACKALERTCONF echo "done" ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # Init script has errors in previous versions. Postinst script should just # return the exit status of this script initscript_error() { return $1 } #DEBHELPER# exit 0 weakforced-2.10.2/builder-support/debian/wforce.docs000066400000000000000000000002241461473602600224360ustar00rootroot00000000000000README.md LICENSE CHANGELOG.md elk/logstash/config/logstash.conf elk/logstash/templates/wforce_template.json elk/kibana/kibana_saved_objects.ndjson weakforced-2.10.2/builder-support/debian/wforce.install000066400000000000000000000001441461473602600231550ustar00rootroot00000000000000usr/bin/wforce usr/lib/systemd/system/wforce.service etc/wforce/regexes.yaml etc/wforce/wforce.conf weakforced-2.10.2/builder-support/debian/wforce.manpages000066400000000000000000000002021461473602600232750ustar00rootroot00000000000000debian/tmp/usr/share/man/man1/wforce.1 debian/tmp/usr/share/man/man5/wforce.conf.5 debian/tmp/usr/share/man/man5/wforce_webhook.5 weakforced-2.10.2/builder-support/debian/wforce.postinst000077500000000000000000000032361461473602600234020ustar00rootroot00000000000000#!/bin/sh # # set -e WFORCECONF=/etc/wforce/wforce.conf case "$1" in configure) if [ -z "`getent group wforce`" ]; then addgroup --quiet --system wforce fi if [ -z "`getent passwd wforce`" ]; then echo -n "Creating user and group wforce..." adduser --quiet --system --home /var/spool/wforce --shell /bin/false --ingroup wforce --disabled-password --disabled-login --gecos "wforce" wforce echo "done" fi if [ -e "/etc/wforce.conf" ]; then echo "Found old config file /etc/wforce.conf. Copying it to /etc/wforce/wforce.conf" if [ -e "/etc/wforce/wforce.conf" ]; then echo "Found also new config file /etc/wforce/wforce.conf" mv /etc/wforce/wforce.conf /etc/wforce/wforce.conf.backup.$$ && \ echo "Renamed /etc/wforce/wforce.conf to /etc/wforce/wforce.conf.backup.$$" fi cp -a /etc/wforce.conf /etc/wforce.conf.backup.$$ && \ echo "Renamed old /etc/wforce.conf to /etc/wforce.conf.backup.$$" mv /etc/wforce.conf /etc/wforce/wforce.conf && \ echo "Moved /etc/wforce.conf to /etc/wforce/wforce.conf" fi echo -n "Modifying wforce.conf to replace password and key..." SETKEY=`echo "makeKey()" | wforce | grep setKey` WEBPWD=`dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 | rev | cut -b 2-14 | rev` sed -e "s#--WEBPWD#$WEBPWD#" -e "s#--SETKEY#$SETKEY#" -i $WFORCECONF echo "done" ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # Init script has errors in previous versions. Postinst script should just # return the exit status of this script initscript_error() { return $1 } #DEBHELPER# exit 0 weakforced-2.10.2/builder-support/dockerfiles/000077500000000000000000000000001461473602600213515ustar00rootroot00000000000000weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.debbuild000066400000000000000000000026151461473602600251200ustar00rootroot00000000000000FROM dist-base as package-builder ARG APT_URL RUN apt-get -y install devscripts dpkg-dev build-essential wget curl \ python3 python3-pip python3-setuptools \ libjsoncpp-dev uuid-dev libz-dev libssl-dev RUN mkdir -p /dist /wforce ADD builder/helpers/ /wforce/builder/helpers/ # Used for -p option to only build specific spec files ARG BUILDER_PACKAGE_MATCH @IF [ ! -z "$M_all$M_wforce" ] # Build weakforce WORKDIR /wforce ARG BUILDER_VERSION ARG BUILDER_RELEASE ARG BUILDER_EPOCH COPY --from=sdist /sdist/ /sdist/ RUN wget https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-Linux-x86_64.sh RUN sh cmake-3.23.2-Linux-x86_64.sh --skip-license --prefix=/usr RUN tar xvf /sdist/prometheus-cpp*Source.tar.gz RUN mv prometheus-cpp*Source prometheus-cpp RUN cd prometheus-cpp/_build && make install RUN git clone https://github.com/drogonframework/drogon.git RUN cd drogon && git checkout tags/v1.9.1 -b v1.9.1 RUN cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_REDIS=OFF -DBUILD_ORM=OFF -DCMAKE_BUILD_TYPE=Release && make && make install RUN tar xvf /sdist/wforce-${BUILDER_VERSION}.tar.bz2 COPY builder-support/debian wforce-${BUILDER_VERSION}/debian RUN builder/helpers/build-debs.sh wforce-$BUILDER_VERSION @ENDIF RUN mv wforce-${BUILDER_VERSION}/wforce*.deb /dist RUN mv wforce*.deb /dist weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.rpmbuild000066400000000000000000000052651461473602600251700ustar00rootroot00000000000000FROM dist-base as package-builder RUN yum install -y rpm-build rpmdevtools python3-rpm-macros wget \ /usr/bin/python3 /usr/bin/pip3 && \ yum groupinstall -y "Development Tools" RUN yum install -y openssl-devel libuuid-devel zlib-devel RUN rpmdev-setuptree SHELL ["bash", "--login", "-c"] ARG CC=clang ARG CXX=clang++ RUN mkdir /dist /wforce WORKDIR /wforce # Used for -p option to only build specific spec files ARG BUILDER_PACKAGE_MATCH ARG BUILDER_VERSION ARG BUILDER_RELEASE ARG BUILDER_EPOCH @IF [ ! -z "$M_all$M_wforce" ] COPY --from=sdist /sdist /sdist RUN for file in /sdist/* ; do ln -s $file /root/rpmbuild/SOURCES/ ; done && ls /root/rpmbuild/SOURCES/ @ENDIF RUN wget https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-Linux-x86_64.sh RUN sh cmake-3.23.2-Linux-x86_64.sh --skip-license --prefix=/usr RUN tar xvf /sdist/prometheus-cpp*Source.tar.gz RUN mv prometheus-cpp*Source prometheus-cpp RUN cd prometheus-cpp/_build && make clean && make install RUN wget https://github.com/jbeder/yaml-cpp/archive/0.8.0.tar.gz RUN tar xvf 0.8.0.tar.gz RUN cd yaml-cpp* && mkdir build && cd build && cmake .. -DCMAKE_POSITION_INDEPENDENT_CODE=ON && make install RUN git clone https://github.com/open-source-parsers/jsoncpp RUN cd jsoncpp && git checkout tags/1.9.4 -b 1.9.4 RUN cd jsoncpp && mkdir _build && cd _build && cmake .. -DBUILD_SHARED_LIBS=OFF && make && make install @IF [ "$BUILDER_RHEL_FLAVOUR" = "amazon" ] ARG DROGON_VERSION=v1.7.4 @ENDIF @IF [ "$BUILDER_RHEL_FLAVOUR" != "amazon" ] @IF [ "$BUILDER_RHEL_VERSION" = "7" ] ARG DROGON_VERSION=v1.7.4 @ENDIF @ENDIF @IF [ "$BUILDER_RHEL_FLAVOUR" != "amazon" ] @IF [ "$BUILDER_RHEL_VERSION" != "7" ] ARG DROGON_VERSION=v1.9.1 @ENDIF @ENDIF RUN git clone https://github.com/drogonframework/drogon.git RUN cd drogon && git checkout tags/"$DROGON_VERSION" -b "$DROGON_VERSION" RUN cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_REDIS=OFF -DBUILD_ORM=OFF -DCMAKE_BUILD_TYPE=Release && make && make install RUN wget https://github.com/maxmind/libmaxminddb/releases/download/1.7.1/libmaxminddb-1.7.1.tar.gz && tar xvf libmaxminddb-1.7.1.tar.gz RUN cd libmaxminddb-1.7.1 && ./configure --enable-shared=no && make && make install ADD builder-support/specs/ /wforce/builder-support/specs/ ADD builder/helpers /wforce/builder/helpers @IF [ ! -z "$M_all$M_wforce" ] RUN yum install -y /usr/bin/python3 RUN builder/helpers/build-specs.sh builder-support/specs/wforce.spec @ENDIF # mv across layers with overlay2 is buggy in some kernel versions (results in empty dirs) # See: https://github.com/moby/moby/issues/33733 #RUN mv /root/rpmbuild/RPMS/* /dist/ RUN cp -R /root/rpmbuild/RPMS/* /dist/ weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.rpmtest000066400000000000000000000006241461473602600250420ustar00rootroot00000000000000# Install the built rpms and test them FROM dist-base as dist # If you want to install extra packages or do generic configuration, # do it before the COPY. Either here, or in the dist-base layer. COPY --from=sdist /sdist /sdist COPY --from=package-builder /dist /dist # Install built packages with dependencies RUN yum localinstall -y /dist/*/*.rpm # Installation tests @INCLUDE Dockerfile.commontest weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.amazon-2000066400000000000000000000007221461473602600262540ustar00rootroot00000000000000@EXEC export BUILDER_RHEL_VERSION=2 @EXEC export BUILDER_RHEL_FLAVOUR=amazon # First do the source builds @INCLUDE Dockerfile.target.sdist # This defines the distribution base layer # Put only the bare minimum of common commands here, without dev tools FROM amazonlinux:2 as dist-base RUN amazon-linux-extras install epel -y RUN yum install -y clang clang-devel llvm llvm-devel boost-devel wget tar gzip make # Do the actual rpm build @INCLUDE Dockerfile.rpmbuild weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.centos-7000066400000000000000000000007751461473602600262770ustar00rootroot00000000000000@EXEC export BUILDER_RHEL_VERSION=7 @EXEC export BUILDER_RHEL_FLAVOUR=centos # First do the source builds @INCLUDE Dockerfile.target.sdist # This defines the distribution base layer # Put only the bare minimum of common commands here, without dev tools FROM centos:7 as dist-base RUN yum install -y epel-release RUN yum install -y centos-release-scl && yum install -y llvm-toolset-7.0 RUN echo "source /opt/rh/llvm-toolset-7.0/enable" >> /etc/bashrc # Do the actual rpm build @INCLUDE Dockerfile.rpmbuild weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.debian-bullseye000066400000000000000000000002711461473602600276730ustar00rootroot00000000000000# First do the source builds @INCLUDE Dockerfile.target.sdist FROM debian:bullseye as dist-base ARG APT_URL RUN apt-get update && apt-get -y dist-upgrade @INCLUDE Dockerfile.debbuild weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.debian-buster000066400000000000000000000002671461473602600273600ustar00rootroot00000000000000# First do the source builds @INCLUDE Dockerfile.target.sdist FROM debian:buster as dist-base ARG APT_URL RUN apt-get update && apt-get -y dist-upgrade @INCLUDE Dockerfile.debbuild weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.debuild000066400000000000000000000011351461473602600262370ustar00rootroot00000000000000FROM dist-base as package-builder ARG APT_URL RUN apt-get -y install devscripts dpkg-dev build-essential RUN mkdir /dist /wforce WORKDIR /wforce ADD builder/helpers/ /wforce/builder/helpers/ # Used for -p option to only build specific spec files ARG BUILDER_PACKAGE_MATCH ARG BUILDER_VERSION ARG BUILDER_RELEASE COPY --from=sdist /sdist /sdist @IF [ ! -z "$M_all$M_wforce" ] RUN tar xvf /sdist/wforce-${BUILDER_VERSION}.tar.bz2 COPY builder-support/debian wforce-${BUILDER_VERSION}/debian RUN builder/helpers/build-debs.sh wforce-$BUILDER_VERSION @ENDIF RUN mv dstore*${BUILDER_VERSION}*.deb /dist weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.el-7000066400000000000000000000000431461473602600253700ustar00rootroot00000000000000@INCLUDE Dockerfile.target.centos-7weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.el-8000066400000000000000000000000401461473602600253660ustar00rootroot00000000000000@INCLUDE Dockerfile.target.ol-8 weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.el-9000066400000000000000000000000401461473602600253670ustar00rootroot00000000000000@INCLUDE Dockerfile.target.ol-9 weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.ol-8000066400000000000000000000012111461473602600254010ustar00rootroot00000000000000@EXEC export BUILDER_RHEL_VERSION=8 @EXEC export BUILDER_RHEL_FLAVOUR=oracle # First do the source builds @INCLUDE Dockerfile.target.sdist # This defines the distribution base layer # Put only the bare minimum of common commands here, without dev tools FROM oraclelinux:8 as dist-base ARG BUILDER_CACHE_BUSTER= RUN touch /var/lib/rpm/* && dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \ dnf install -y 'dnf-command(config-manager)' yum && \ dnf config-manager --set-enabled ol8_codeready_builder RUN dnf -y install dnf-plugins-core clang # Do the actual rpm build @INCLUDE Dockerfile.rpmbuild weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.ol-9000066400000000000000000000013421461473602600254070ustar00rootroot00000000000000@EXEC export BUILDER_RHEL_VERSION=9 @EXEC export BUILDER_RHEL_FLAVOUR=oracle # First do the source builds @INCLUDE Dockerfile.target.sdist # This defines the distribution base layer # Put only the bare minimum of common commands here, without dev tools FROM oraclelinux:9 as dist-base RUN touch /var/lib/rpm/* && dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && \ dnf install -y 'dnf-command(config-manager)' yum && dnf config-manager --set-enabled ol9_codeready_builder && \ dnf install -y curl procps-ng --allowerasing # This is to ensure we don't have curlminimal and we do have pkill RUN dnf -y install dnf-plugins-core clang # Do the actual rpm build @INCLUDE Dockerfile.rpmbuild weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.sdist000066400000000000000000000006021461473602600257530ustar00rootroot00000000000000# This builds the source distributions for all components # Builds the wforce sdist in a separate stage @IF [ ! -z "$M_all$M_wforce" ] @INCLUDE Dockerfile.wforce @ENDIF FROM alpine:3.6 as sdist # Copy wforce tarball @IF [ ! -z "$M_all$M_wforce" ] COPY --from=wforce /sdist/ /sdist/ @ENDIF # Show contents for build debugging @IF [ ! -z "$M_all$M_wforce" ] RUN ls -l /sdist/* @ENDIF weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.wforce000066400000000000000000000044001461473602600246250ustar00rootroot00000000000000FROM ubuntu:focal as wforce ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get dist-upgrade -y && \ apt-get -y -f install \ autoconf \ automake \ libboost-all-dev \ libcurl4-openssl-dev \ libgetdns-dev \ libhiredis-dev \ libmaxminddb-dev \ liblua5.1-0-dev \ libluajit-5.1-dev \ libprotobuf-dev \ libssl-dev \ libsodium-dev \ libsystemd-dev \ libyaml-cpp-dev \ libjsoncpp-dev \ uuid-dev \ libz-dev \ libtool \ pkg-config \ protobuf-compiler \ pandoc \ wget \ cmake \ git \ gcc \ g++ WORKDIR /wforce/ RUN mkdir /sdist RUN git clone https://github.com/jupp0r/prometheus-cpp.git RUN cd prometheus-cpp && git checkout tags/v1.0.1 -b v1.0.1 RUN cd prometheus-cpp && \ mkdir _build && cd _build && cmake .. -DBUILD_SHARED_LIBS=off -DENABLE_PULL=off \ -DENABLE_PUSH=off -DENABLE_COMPRESSION=off -DENABLE_TESTING=off -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ \ -DCMAKE_POSITION_INDEPENDENT_CODE=ON && make && make install && make package_source RUN cd prometheus-cpp/_build && cp prometheus-cpp*Source.tar.gz /sdist/ RUN git clone https://github.com/drogonframework/drogon.git RUN cd drogon && git checkout tags/v1.9.1 -b v1.9.1 RUN cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_REDIS=OFF -DCMAKE_BUILD_TYPE=Release -DBUILD_ORM=OFF && make && make install ADD CHANGELOG.md configure.ac ext LICENSE Makefile.am README.md NOTICE /wforce/ @EXEC sdist_dirs=(m4 ext docs regression-tests wforce common trackalert docker elk) @EXEC for d in ${sdist_dirs[@]} ; do echo "COPY $d/ /wforce/$d/" ; done ADD builder/helpers/set-configure-ac-version.sh /wforce/builder/helpers/ ARG BUILDER_VERSION ARG BUILDER_PYTHON_SRC_VERSION RUN /wforce/builder/helpers/set-configure-ac-version.sh && \ autoreconf -v -i -f && \ ./configure \ --disable-dependency-tracking && \ make dist RUN cp wforce-${BUILDER_VERSION}.tar.bz2 /sdist/ weakforced-2.10.2/builder-support/gen-version000077500000000000000000000042721461473602600212460ustar00rootroot00000000000000#!/bin/bash VERSION="unknown" DIRTY="" git status | grep -q clean || DIRTY='.dirty' # Special environment variable to signal that we are building a release, as this # has consequences for the version number. if [ "${IS_RELEASE}" = "YES" ]; then TAG="$(git describe --tags --exact-match 2> /dev/null)" if [ -n "${TAG}" ]; then # We're on a tag echo "${TAG}${DIRTY}" > .version printf "${TAG}${DIRTY}" exit 0 fi echo 'This is not a tag, either tag this commit or do not set $IS_RELEASE' >&2 exit 1 fi # # Generate the version number based on the branch # if [ ! -z "$(git rev-parse --abbrev-ref HEAD 2> /dev/null)" ]; then # Possible `git describe --tags` outputs: # 2.4.1 (when on a tag) # 2.4.1-alpha1 (when on a tag) # 2.4.2-1-gdad05a9 (not on a tag) # 2.4.2-alpha1-1-gdad05a9 (not on a tag) OIFS=$IFS IFS='-' GIT_VERSION=( $(git describe --tags 2> /dev/null) ) IFS=$OIFS LAST_TAG="${GIT_VERSION[0]}" COMMITS_SINCE_TAG='' GIT_HASH='' if [ ${#GIT_VERSION[@]} -eq 1 ]; then # We're on a tag, but IS_RELEASE was unset COMMITS_SINCE_TAG=0 fi if [ ${#GIT_VERSION[@]} -eq 2 ]; then # On a tag for a pre-release, e.g. 1.2.3-beta2 LAST_TAG="${LAST_TAG}-${GIT_VERSION[1]}" COMMITS_SINCE_TAG=0 fi if [ ${#GIT_VERSION[@]} -eq 3 ]; then # Not on a tag # 1.2.3-100-g123456 COMMITS_SINCE_TAG="${GIT_VERSION[1]}" GIT_HASH="${GIT_VERSION[2]}" fi if [ ${#GIT_VERSION[@]} -eq 4 ]; then # Not on a tag, but a pre-release was made before # 1.2.3-rc1-100-g123456 LAST_TAG="${LAST_TAG}-${GIT_VERSION[1]}" COMMITS_SINCE_TAG="${GIT_VERSION[2]}" GIT_HASH="${GIT_VERSION[3]}" fi if [ -z "${GIT_HASH}" ]; then GIT_HASH="g$(git show --no-patch --format=format:%h HEAD 2>/dev/null)" if [ -z "$GIT_HASH" ]; then GIT_HASH="g$(git show --format=format:%h HEAD | head -n1)" fi fi if [ -z "${LAST_TAG}" ]; then LAST_TAG=0.0.0 COMMITS_SINCE_TAG="$(git log --oneline | wc -l)" fi BRANCH=".$(git rev-parse --abbrev-ref HEAD | perl -p -e 's/[^[:alnum:]]//g;')" [ "${BRANCH}" = ".master" ] && BRANCH='' VERSION="${LAST_TAG}.${COMMITS_SINCE_TAG}${BRANCH}.${GIT_HASH}${DIRTY}" fi printf ${VERSION##v} weakforced-2.10.2/builder-support/specs/000077500000000000000000000000001461473602600201745ustar00rootroot00000000000000weakforced-2.10.2/builder-support/specs/wforce.spec000066400000000000000000000216661461473602600223500ustar00rootroot00000000000000%if %{?fedora}0 > 140 || %{?rhel}0 > 60 || %{?centos}0 > 60 %bcond_without systemd %else %bcond_with systemd %endif %define venv_cmd %{__python3} -m venv --symlinks %define venv_name %{name}-report-api %define venv_install_dir /usr/share/%{venv_name} %define venv_dir %{buildroot}%{venv_install_dir} %define venv_bin %{venv_dir}/bin %define venv_python %{venv_bin}/python %define venv_pip %{venv_python} %{venv_bin}/pip install %define configdir /etc/%{venv_name} %define vardir /var/lib/%{venv_name} %define __prelink_undo_cmd %{nil} %global __os_install_post %(echo '%{__os_install_post}' | sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g') %global wforce_restart_flag /var/run/wforce-restart-after-rpm-install %global trackalert_restart_flag /var/run/wforce-trackalert-restart-after-rpm-install Summary: Weakforce daemon for detecting brute force attacks Name: wforce Version: %{getenv:BUILDER_RPM_VERSION} Release: %{getenv:BUILDER_RPM_RELEASE}%{?dist} License: GPLv3 Group: System Environment/Daemons URL: http://www.open-xchange.com/ Source0: %{name}-%{getenv:BUILDER_VERSION}.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: libtool BuildRequires: automake BuildRequires: readline-devel BuildRequires: gcc-c++ BuildRequires: lua-devel BuildRequires: libidn-devel BuildRequires: boost-devel BuildRequires: bzip2 BuildRequires: pkgconfig BuildRequires: getdns-devel BuildRequires: libsodium-devel BuildRequires: pandoc BuildRequires: protobuf-compiler BuildRequires: protobuf-devel BuildRequires: curl-devel BuildRequires: luajit BuildRequires: luajit-devel BuildRequires: hiredis BuildRequires: hiredis-devel BuildRequires: openssl-devel BuildRequires: boost-regex BuildRequires: wget BuildRequires: boost-system BuildRequires: boost-filesystem BuildRequires: libuuid-devel BuildRequires: zlib-devel %if %{with systemd} BuildRequires: systemd-devel Requires: systemd Requires(postun): systemd %else Requires: initscripts Requires(postun): /sbin/service %endif %define scl bash AutoReqProv: yes %description The goal of 'wforce' is to detect brute forcing of passwords across many servers, services and instances. In order to support the real world, brute force detection policy can be tailored to deal with "bulk, but legitimate" users of your service, as well as botnet-wide slowscans of passwords. The aim is to support the largest of installations, providing services to hundreds of millions of users. %package trackalert Summary: Longterm abuse data reporting and alerter %description trackalert Trackalert is designed to be an optional service to complement wforce. Whereas wforce provides a toolkit to combat abuse of logins such as password brute forcing in realtime, trackalert is designed to look at abuse asynchronously, using long-term report data stored in an external DB such as elasticsearch, and to send alerts on potential login abuse. %prep %setup -n %{name}-%{getenv:BUILDER_VERSION} %build %{?scl: %{scl} - << \EOF} %configure \ --disable-dependency-tracking \ --docdir=%{_docdir}/%{name}-%{getenv:BUILDER_VERSION} \ --disable-static --with-luajit --sysconfdir=/etc/%{name} \ --enable-trackalert %{?scl:EOF} %{?scl: %{scl} - << \EOF} make %{?_smp_mflags} %{?scl:EOF} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} mkdir -p %{buildroot}/%{_docdir}/%{name}-%{version} mv %{buildroot}/etc/%{name}/%{name}.conf.example %{buildroot}/%{_docdir}/%{name}-%{version}/ mv elk/logstash/config/logstash.conf %{buildroot}/%{_docdir}/%{name}-%{version}/ mv elk/logstash/templates/wforce_template.json %{buildroot}/%{_docdir}/%{name}-%{version}/ mv elk/kibana/kibana_saved_objects.ndjson %{buildroot}/%{_docdir}/%{name}-%{version}/ %clean rm -rf %{buildroot} %pre trackalert if [ "$1" = "2" ] || [ "$1" = "1" ]; then rm -f %trackalert_restart_flag %if %{with systemd} /bin/systemctl is-active %{name}.service >/dev/null 2>&1 && touch %trackalert_restart_flag || : /bin/systemctl is-active %{name}.service >/dev/null 2>&1 && \ /bin/systemctl stop %{name}.service >/dev/null 2>&1 || : %endif fi %pre getent group %{name} >/dev/null || groupadd -r %{name} getent passwd %{name} >/dev/null || \ useradd -r -g %{name} -d /var/spool/%{name} -s /bin/false -c "wforce" %{name} if [ "$1" = "2" ] || [ "$1" = "1" ]; then rm -f %wforce_restart_flag %if %{with systemd} /bin/systemctl is-active %{name}.service >/dev/null 2>&1 && touch %wforce_restart_flag || : /bin/systemctl is-active %{name}.service >/dev/null 2>&1 && \ /bin/systemctl stop %{name}.service >/dev/null 2>&1 || : %else /sbin/service %{name} status >/dev/null 2>&1 && touch %wforce_restart_flag || : /sbin/service %{name} stop >/dev/null 2>&1 || : %endif fi %post trackalert # Post-Install if [ $1 -eq 1 ]; then TRACKALERTCONF=/etc/wforce/trackalert.conf echo -n "Modifying trackalert.conf to replace password and key..." SETKEY=`echo "makeKey()" | trackalert | grep setKey` WEBPWD=`dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 | rev | cut -b 2-14 | rev` sed -e "s#--WEBPWD#$WEBPWD#" -e "s#--SETKEY#$SETKEY#" -i $TRACKALERTCONF echo "done" %if %{with systemd} systemctl daemon-reload systemctl enable trackalert.service %endif fi %post # Post-Install if [ $1 -eq 1 ]; then WFORCECONF=/etc/%{name}/%{name}.conf echo -n "Modifying %{name}.conf to replace password and key..." SETKEY=`echo "makeKey()" | %{name} | grep setKey` WEBPWD=`dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 | rev | cut -b 2-14 | rev` sed -e "s#--WEBPWD#$WEBPWD#" -e "s#--SETKEY#$SETKEY#" -i $WFORCECONF echo "done" %if %{with systemd} systemctl daemon-reload systemctl enable %{name}.service %else chkconfig --add %{name} %endif fi # Post-Upgrade if [ $1 -eq 2 ]; then if [ -e "/etc/%{name}.conf" ]; then echo "Found old config file /etc/%{name}.conf. Copying it to /etc/%{name}/%{name}.conf" if [ -e "/etc/%{name}/%{name}.conf" ]; then echo "Found also new config file /etc/%{name}/%{name}.conf" mv /etc/%{name}/%{name}.conf /etc/%{name}/%{name}.conf.backup.$$ && \ echo "Renamed /etc/%{name}/%{name}.conf to /etc/%{name}/%{name}.conf.backup.$$" fi cp -a /etc/%{name}.conf /etc/%{name}.conf.backup.$$ && \ echo "Renamed old /etc/%{name}.conf to /etc/%{name}.conf.backup.$$" mv /etc/%{name}.conf /etc/%{name}/%{name}.conf && \ echo "Moved /etc/%{name}.conf to /etc/%{name}/%{name}.conf" fi fi %preun trackalert if [ $1 = 0 ]; then %if %{with systemd} /bin/systemctl disable trackalert.service trackalert.socket >/dev/null 2>&1 || : /bin/systemctl stop trackalert.service trackalert.socket >/dev/null 2>&1 || : %endif fi %preun if [ $1 = 0 ]; then %if %{with systemd} /bin/systemctl disable %{name}.service %{name}.socket >/dev/null 2>&1 || : /bin/systemctl stop %{name}.service %{name}.socket >/dev/null 2>&1 || : %else /sbin/service %{name} stop > /dev/null 2>&1 /sbin/chkconfig --del %{name} %endif fi %postun trackalert %if %{with systemd} /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif if [ "$1" -ge "1" -a -e %trackalert_restart_flag ]; then %if %{with systemd} /bin/systemctl start trackalert.service >/dev/null 2>&1 || : %endif rm -f %trackalert_restart_flag fi %postun %if %{with systemd} /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif if [ "$1" -ge "1" -a -e %wforce_restart_flag ]; then %if %{with systemd} /bin/systemctl start %{name}.service >/dev/null 2>&1 || : %else /sbin/service %{name} start >/dev/null 2>&1 || : %endif rm -f %wforce_restart_flag fi %posttrans trackalert # trackalert should be started again in %postun, but it's not executed on reinstall # if it was already started, restart_flag won't be here, so it's ok to test it again if [ -e %trackalert_restart_flag ]; then %if %{with systemd} /bin/systemctl start trackalert.service >/dev/null 2>&1 || : %endif rm -f %trackalert_restart_flag fi %posttrans # %{name} should be started again in %postun, but it's not executed on reinstall # if it was already started, wforce_restart_flag won't be here, so it's ok to test it again if [ -e %wforce_restart_flag ]; then %if %{with systemd} /bin/systemctl start %{name}.service >/dev/null 2>&1 || : %else /sbin/service %{name} start >/dev/null 2>&1 || : %endif rm -f %wforce_restart_flag fi %files %defattr(-,root,root,-) %{_bindir}/%{name} %{_bindir}/wf_dump_entries %ghost %{_sysconfdir}/%{name}.conf %attr(0644,root,root) %config(noreplace,missingok) %{_sysconfdir}/%{name}/%{name}.conf %{_sysconfdir}/%{name}/regexes.yaml %{_docdir}/%{name}-%{version}/* %{_unitdir}/%{name}.service %{_mandir}/man1/%{name}.1.gz %{_mandir}/man1/wf_dump_entries.1.gz %{_mandir}/man5/%{name}.conf.5.gz %{_mandir}/man5/%{name}_webhook.5.gz %doc CHANGELOG.md README.md %license LICENSE %files trackalert %{_bindir}/trackalert %{_mandir}/man1/trackalert.1.gz %{_mandir}/man5/trackalert.conf.5.gz %doc trackalert/README.md CHANGELOG.md %license LICENSE %{_unitdir}/trackalert.service %{_sysconfdir}/%{name}/trackalert.conf weakforced-2.10.2/common/000077500000000000000000000000001461473602600152075ustar00rootroot00000000000000weakforced-2.10.2/common/Makefile.am000066400000000000000000000055111461473602600172450ustar00rootroot00000000000000AM_CPPFLAGS = \ $(LUA_CFLAGS) \ $(EXT_CFLAGS) \ $(libsodium_CFLAGS) \ $(GETDNS_CFLAGS) \ $(JSON11_CFLAGS) \ $(LIBCURL_CFLAGS) \ $(LIBCRYPTO_INCLUDES) \ $(LIBDROGON_INCLUDES) \ $(LIBJSONCPP_INCLUDES) \ $(LIBZ_INCLUDES) \ $(LIBPROMETHEUS_INCLUDES) \ $(YAMLCPP_FLAGS) \ $(GEOIP_CFLAGS) \ $(MMDB_CFLAGS) \ -pthread \ -DSYSCONFDIR='"$(sysconfdir)"' \ $(SANITIZER_FLAGS) AM_LDFLAGS = \ $(PROGRAM_LDFLAGS) \ $(SANITIZER_FLAGS) noinst_LTLIBRARIES=libweakforce.la libweakforce_la_LIBADD=$(EXT_LIBS) $(JSON11_LIBS) $(BOOST_FILESYSTEM_LIBS) $(BOOST_SYSTEM_LIBS) \ $(GEOIP_LIBS) $(MMDB_LIBS) $(LIBPROMETHEUS_LIBS) $(LIBDROGON_LIBS) \ $(LIBJSONCPP_LIBS) $(LIBZ_LIBS) $(LIBCURL) $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS) libweakforce_la_SOURCES= \ base64.hh \ dns_lookup.cc dns_lookup.hh \ wforce-geoip.cc wforce-geoip.hh \ wforce-geoip2.cc wforce-geoip2.hh \ twmap.hh twmap-static.cc customfunc.hh\ dolog.hh wforce_ns.hh \ iputils.cc iputils.hh \ misc.cc misc.hh \ namespaces.hh \ perf-stats.cc perf-stats.hh \ webhook.cc webhook.hh \ minicurl.cc minicurl.hh \ hmac.hh hmac.cc \ wforce_exception.hh \ sholder.hh \ sodcrypto.cc sodcrypto.hh \ lock.hh \ pdnsexception.hh \ device_parser.hh device_parser.cc \ wforce-webserver.cc wforce-webserver.hh \ sstuff.hh \ login_tuple.cc login_tuple.hh \ common-lua.cc common-lua.hh \ prometheus.hh prometheus.cc noinst_HEADERS = \ base64.hh \ dns_lookup.hh \ dolog.hh \ iputils.hh \ misc.hh \ namespaces.hh \ sholder.hh \ sodcrypto.hh \ sstuff.hh \ twmap.hh \ customfunc.hh \ wforce-geoip.hh \ wforce-geoip2.hh \ perf-stats.hh \ webhook.hh \ minicurl.hh \ hmac.hh \ wforce_exception.hh \ wforce_ns.hh \ device_parser.hh \ lock.hh \ pdnsexception.hh \ wforce-webserver.hh \ login_tuple.hh \ common-lua.hh \ prometheus.hh if UNIT_TESTS if HAVE_BOOST_GE_148 TESTS_ENVIRONMENT = env BOOST_TEST_LOG_LEVEL=message SRCDIR='$(srcdir)' TESTS=testrunner noinst_PROGRAMS = \ testrunner else check-local: @echo "Unit tests disabled, boost is too old" endif else check-local: @echo "Unit tests are not enabled" @echo "Run ./configure --enable-unit-tests" endif testrunner_SOURCES = \ testrunner.cc \ test-minicurl.cc \ test-serialize.cc EXTRA_testrunner_DEPENDENCIES = \ $(EXT_LIBS) libweakforce.la testrunner_LDFLAGS = \ $(AM_LDFLAGS) \ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) testrunner_LDADD = \ $(WFORCE_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) \ $(LUA_LIBS) $(EXT_LIBS) $(WFORCE_LIBS) \ ${libsodium_LIBS} $(GEOIP_LIBS) $(GETDNS_LIBS) $(PROTOBUF_LIBS) \ $(LIBSYSTEMD_LIBS) $(JSON11_LIBS) $(BOOST_DATE_TIME_LIBS) \ $(BOOST_REGEX_LIBS) $(LIBHIREDIS_LIBS) $(LIBCURL) $(LIBCRYPTO_LDFLAGS) \ $(LIBCRYPTO_LIBS) $(LIBPROMETHEUS_LDFLAGS) $(LIBPROMETHEUS_LIBS)\ $(YAMLCPP_LIBS) $(MMDB_LIBS)\ $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) \ $(LIBDL) weakforced-2.10.2/common/base64.hh000066400000000000000000000021631461473602600166160ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PDNS_BASE64_HH #define PDNS_BASE64_HH #include int B64Decode(const std::string& src, std::string& dst); std::string Base64Encode (const std::string& src); #endif weakforced-2.10.2/common/common-lua.cc000066400000000000000000000405231461473602600175710ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "common-lua.hh" #ifdef HAVE_GEOIP #include "wforce-geoip.hh" #endif #ifdef HAVE_MMDB #include "wforce-geoip2.hh" #endif #include "prometheus.hh" using std::thread; void setupCommonLua(bool client, bool multi_lua, LuaContext& c_lua, const std::string& config) { if (!multi_lua) { c_lua.writeFunction("addACL", [](const std::string& domain) { g_webserver.addACL(domain); }); } else { // empty function for allow/report - this stops parsing errors or weirdness c_lua.writeFunction("addACL", [](const std::string& domain) { }); } if (!multi_lua) { c_lua.writeFunction("setACL", [](const vector>& parts) { NetmaskGroup nmg; for(const auto& p : parts) { nmg.addMask(p.second); } g_webserver.setACL(nmg); }); } else { c_lua.writeFunction("setACL", [](const vector>& parts) { }); } if (!multi_lua) { c_lua.writeFunction("showACL", []() { vector vec; g_webserver.getACL().toStringVector(&vec); for(const auto& s : vec) g_outputBuffer+=s+"\n"; }); } else { c_lua.writeFunction("showACL", []() { }); } if (!multi_lua) { c_lua.writeFunction("setNumLuaStates", [](int numStates) { g_num_luastates = numStates; }); } else { c_lua.writeFunction("setNumLuaStates", [](int numStates) { }); } if (!multi_lua) { c_lua.writeFunction("setNumWorkerThreads", [](int numThreads) { // the number of threads used to process webserver commands g_webserver.setNumWorkerThreads(numThreads); }); } else { c_lua.writeFunction("setNumWorkerThreads", [](int numThreads) { }); } if (!multi_lua) { c_lua.writeFunction("setMaxWebserverConns", [](int max_conns) { // the max number of active webserver connections g_webserver.setMaxConns(max_conns); }); } else { c_lua.writeFunction("setMaxWebserverConns", [](int max_conns) { }); } if (!multi_lua) { c_lua.writeFunction("setMetricsNoPassword", []() { g_webserver.setMetricsNoPassword(); }); } else { c_lua.writeFunction("setMetricsNoPassword", []() { }); } c_lua.writeFunction("debugLog", [](const std::string& msg, const std::vector>& kvs) { if (g_verbose) { std::ostringstream os; os << msg; if (kvs.size()) os << ": "; for (const auto& i : kvs) { os << i.first << "="<< "\"" << i.second << "\"" << " "; } debuglog(os.str().c_str()); } }); c_lua.writeFunction("vinfoLog", [](const std::string& msg, const std::vector>& kvs) { if (g_verbose) { std::ostringstream os; os << msg; if (kvs.size()) os << ": "; for (const auto& i : kvs) { os << i.first << "="<< "\"" << i.second << "\"" << " "; } infolog(os.str().c_str()); } }); c_lua.writeFunction("infoLog", [](const std::string& msg, const std::vector>& kvs) { std::ostringstream os; os << msg; if (kvs.size()) os << ": "; for (const auto& i : kvs) { os << i.first << "="<< "\"" << i.second << "\"" << " "; } infolog(os.str().c_str()); }); c_lua.writeFunction("warnLog", [](const std::string& msg, const std::vector>& kvs) { std::ostringstream os; os << msg; if (kvs.size()) os << ": "; for (const auto& i : kvs) { os << i.first << "="<< "\"" << i.second << "\"" << " "; } warnlog(os.str().c_str()); }); c_lua.writeFunction("errorLog", [](const std::string& msg, const std::vector>& kvs) { std::ostringstream os; os << msg; if (kvs.size()) os << ": "; for (const auto& i : kvs) { os << i.first << "="<< "\"" << i.second << "\"" << " "; } errlog(os.str().c_str()); }); c_lua.registerMember("t", &LoginTuple::t); c_lua.registerMember("remote", &LoginTuple::remote); c_lua.registerMember("login", &LoginTuple::login); c_lua.registerMember("pwhash", &LoginTuple::pwhash); c_lua.registerMember("success", &LoginTuple::success); c_lua.registerMember("policy_reject", &LoginTuple::policy_reject); c_lua.registerMember("attrs", &LoginTuple::attrs); c_lua.registerMember("attrs_mv", &LoginTuple::attrs_mv); c_lua.registerMember("protocol", &LoginTuple::protocol); c_lua.registerMember("tls", &LoginTuple::tls); c_lua.registerMember("device_id", &LoginTuple::device_id); c_lua.registerMember("device_attrs", &LoginTuple::device_attrs); c_lua.registerMember("session_id", &LoginTuple::session_id); c_lua.registerFunction("tostring", [](const ComboAddress& ca) { return ca.toString(); }); c_lua.writeFunction("newCA", [](string address) { try { return ComboAddress(address); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); g_outputBuffer += (fmt % "newCA(): error parsing address/port. Make sure to use IP addresses not hostnames" % e.reason).str(); errlog("newCA() error parsing address/port [%s]. Make sure to use IP addresses not hostnames", address); return ComboAddress(); } } ); c_lua.writeFunction("newNetmask", [](string address) { try { return Netmask(address); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); g_outputBuffer += (fmt % "newNetmask(): error parsing address/port. Make sure to use IP addresses not hostnames" % e.reason).str(); errlog("newNetmask() error parsing netmask [%s]. Use x.x.x.x/y notation.", address); return Netmask(); } } ); c_lua.writeFunction("newNetmaskGroup", []() { return NetmaskGroup(); } ); c_lua.registerFunction("addMask", [](NetmaskGroup& nmg, const std::string& mask) { nmg.addMask(mask); }); c_lua.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match); g_lua.registerFunction("size", &NetmaskGroup::size); g_lua.registerFunction("clear", &NetmaskGroup::clear); if (!multi_lua) { c_lua.writeFunction("addCustomStat", [](const std::string& stat_name) { addCustomStat(stat_name); addPrometheusCustomMetric(stat_name);}); } else { c_lua.writeFunction("addCustomStat", [](const std::string& stat_name) {} ); } if (multi_lua) { c_lua.writeFunction("incCustomStat", [](const std::string& stat_name) { incCustomStat(stat_name); incPrometheusCustomMetric(stat_name);}); } else { c_lua.writeFunction("incCustomStat", [](const std::string& stat_name) {} ); } // XXX BEGIN Deprecated functions - will be removed in a later release if (!multi_lua) { c_lua.writeFunction("showPerfStats", []() { g_outputBuffer += getPerfStatsString(); }); } else { c_lua.writeFunction("showPerfStats", []() { }); } if (!multi_lua) { c_lua.writeFunction("showCommandStats", []() { g_outputBuffer += getCommandStatsString(); }); } else { c_lua.writeFunction("showCommandStats", []() { }); } if (!multi_lua) { c_lua.writeFunction("showCustomStats", []() { g_outputBuffer += getCustomStatsString(); }); } else { c_lua.writeFunction("showCustomStats", []() { }); } // XXX END Deprecated functions - will be removed in a later release if (!multi_lua) { c_lua.writeFunction("setNumWebHookThreads", [](unsigned int num_threads) { g_webhook_runner.setNumThreads(num_threads); }); } else { c_lua.writeFunction("setNumWebHookThreads", [](unsigned int num_threads) { }); } if (!multi_lua) { c_lua.writeFunction("setWebHookQueueSize", [](unsigned int queue_size) { g_webhook_runner.setMaxQueueSize(queue_size); }); } else { c_lua.writeFunction("setWebHookQueueSize", [](unsigned int queue_size) { }); } if (!multi_lua) { c_lua.writeFunction("setNumWebHookConnsPerThread", [](unsigned int num_conns) { g_webhook_runner.setMaxConns(num_conns); }); } else { c_lua.writeFunction("setNumWebHookConnsPerThread", [](unsigned int num_conns) { }); } if (!multi_lua) { c_lua.writeFunction("setWebHookTimeoutSecs", [](uint64_t timeout_secs) { g_webhook_runner.setTimeout(timeout_secs); }); } else { c_lua.writeFunction("setWebHookTimeoutSecs", [](uint64_t timeout_secs) { }); } if (!multi_lua) { c_lua.writeFunction("showCustomWebHooks", []() { auto webhooks = g_custom_webhook_db.getWebHooks(); boost::format fmt("%-9d %-20.20s %-9d %-9d %-s\n"); g_outputBuffer= (fmt % "ID" % "Name" % "Successes" % "Failures" % "URL").str(); for(const auto& i : webhooks) { if (auto is = i.lock()) { g_outputBuffer += (fmt % is->getID() % is->getName() % is->getSuccess() % is->getFailed() % is->getConfigKey("url")).str(); } } }); } else { c_lua.writeFunction("showCustomWebHooks", []() { }); } if (multi_lua) { c_lua.writeFunction("runCustomWebHook", [](const std::string& wh_name, const std::string& wh_data) { auto whwp = g_custom_webhook_db.getWebHook(wh_name); if (auto whp = whwp.lock()) { g_webhook_runner.runHook(wh_name, whp, wh_data); } else { errlog("Attempting to run custom webhook with name %d but no such webhook exists!", wh_name); } }); } else { c_lua.writeFunction("runCustomWebHook", [](const std::string& wh_name, const std::string& wh_data) { }); } if (!(multi_lua || client)) { c_lua.writeFunction("addCustomWebHook", [](const std::string& name, const std::vector>& ck_vec) { std::string err; WHConfigMap config_keys; auto id = g_custom_webhook_db.getNewID(); for (const auto& ck : ck_vec) { config_keys.insert(ck); } auto ret = g_custom_webhook_db.addWebHook(CustomWebHook(id, name, true, config_keys), err); if (ret != true) { errlog("Registering custom webhook id=%d name=%d from Lua failed [%s]", id, name, err); } }); } else { c_lua.writeFunction("addCustomWebHook", [](const std::string& name, const std::vector>& ck_vec) { }); } if (!multi_lua) { c_lua.writeFunction("makeKey", []() { g_outputBuffer="setKey("+newKey()+")\n"; }); } else { c_lua.writeFunction("makeKey", []() { }); } #ifdef HAVE_GEOIP if (!multi_lua) { c_lua.writeFunction("initGeoIPDB", []() { try { g_wfgeodb.initGeoIPDB(WFGeoIPDBType::GEOIP_COUNTRY|WFGeoIPDBType::GEOIP_COUNTRY_V6); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("initGeoIPDB(): Error initialising GeoIP (%s)", e.reason); g_outputBuffer += (fmt % "initGeoIPDB(): Error loading GeoIP" % e.reason).str(); } }); } else { c_lua.writeFunction("initGeoIPDB", []() { }); } if (!multi_lua) { c_lua.writeFunction("reloadGeoIPDBs", []() { try { g_wfgeodb.reload(); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("reloadGeoIPDBs(): Error reloading GeoIP (%s)", e.reason); g_outputBuffer += (fmt % "reloadGeoIPDBs(): Error reloading GeoIP" % e.reason).str(); } g_outputBuffer += "reloadGeoIPDBs() successful\n"; }); } else { c_lua.writeFunction("reloadGeoIPDBs", []() { }); } c_lua.writeFunction("lookupCountry", [](ComboAddress address) { return g_wfgeodb.lookupCountry(address); }); if (!multi_lua) { c_lua.writeFunction("initGeoIPCityDB", []() { try { g_wfgeodb.initGeoIPDB(WFGeoIPDBType::GEOIP_CITY|WFGeoIPDBType::GEOIP_CITY_V6); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("initGeoIPCityDB(): Error initialising GeoIP (%s)", e.reason); g_outputBuffer += (fmt % "initGeoIPCityDB(): Error loading GeoIP" % e.reason).str(); } }); } else { c_lua.writeFunction("initGeoIPCityDB", []() { }); } c_lua.writeFunction("lookupCity", [](ComboAddress address) { return g_wfgeodb.lookupCity(address); }); c_lua.registerMember("country_code", &WFGeoIPRecord::country_code); c_lua.registerMember("country_name", &WFGeoIPRecord::country_name); c_lua.registerMember("region", &WFGeoIPRecord::region); c_lua.registerMember("city", &WFGeoIPRecord::city); c_lua.registerMember("postal_code", &WFGeoIPRecord::postal_code); c_lua.registerMember("continent_code", &WFGeoIPRecord::continent_code); c_lua.registerMember("latitude", &WFGeoIPRecord::latitude); c_lua.registerMember("longitude", &WFGeoIPRecord::longitude); if (!multi_lua) { c_lua.writeFunction("initGeoIPISPDB", []() { try { g_wfgeodb.initGeoIPDB(WFGeoIPDBType::GEOIP_ISP|WFGeoIPDBType::GEOIP_ISP_V6); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("initGeoIPISPDB(): Error initialising GeoIP (%s)", e.reason); g_outputBuffer += (fmt % "initGeoIPISPDB(): Error loading GeoIP" % e.reason).str(); } }); } else { c_lua.writeFunction("initGeoIPISPDB", []() { }); } c_lua.writeFunction("lookupISP", [](ComboAddress address) { return g_wfgeodb.lookupISP(address); }); #endif // HAVE_GEOIP #ifdef HAVE_MMDB if (!multi_lua) { c_lua.writeFunction("newGeoIP2DB", [](const std::string& name, const std::string& filename) { try { std::lock_guard lock(geoip2_mutx); geoip2Map.emplace(std::make_pair(name, WFGeoIP2DB::makeWFGeoIP2DB(filename))); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("newGeoIPDB(): Error initialising GeoIP2DB (%s)", e.reason); g_outputBuffer += (fmt % "newGeoIP2DB(): Error loading GeoIP2 DB" % e.reason).str(); } }); } else { c_lua.writeFunction("newGeoIP2DB", [](const std::string& name, const std::string& filename) { }); } c_lua.writeFunction("getGeoIP2DB", [](const std::string& name) { std::lock_guard lock(geoip2_mutx); auto it = geoip2Map.find(name); if (it != geoip2Map.end()) return it->second; else { errlog("getGeoIP2DB(): Could not find database named %s", name); return std::make_shared(); } }); c_lua.registerFunction("lookupCountry", &WFGeoIP2DB::lookupCountry); c_lua.registerFunction("lookupISP", &WFGeoIP2DB::lookupISP); c_lua.registerFunction("lookupCity", &WFGeoIP2DB::lookupCity); c_lua.registerFunction("lookupStringValue", &WFGeoIP2DB::lookupStringValue); c_lua.registerFunction("lookupUIntValue", &WFGeoIP2DB::lookupUIntValue); c_lua.registerFunction("lookupBoolValue", &WFGeoIP2DB::lookupBoolValue); c_lua.registerFunction("lookupDoubleValue", &WFGeoIP2DB::lookupDoubleValue); c_lua.registerMember("city", &WFGeoIPRecord::city); c_lua.registerMember("country_code", &WFGeoIPRecord::country_code); c_lua.registerMember("country_name", &WFGeoIPRecord::country_name); c_lua.registerMember("region", &WFGeoIPRecord::region); c_lua.registerMember("continent_code", &WFGeoIPRecord::continent_code); c_lua.registerMember("postal_code", &WFGeoIPRecord::postal_code); c_lua.registerMember("latitude", &WFGeoIPRecord::latitude); c_lua.registerMember("longitude", &WFGeoIPRecord::longitude); #endif // HAVE_MMDB } weakforced-2.10.2/common/common-lua.hh000066400000000000000000000036511461473602600176040ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/luawrapper/include/LuaContext.hpp" #include #include "misc.hh" #include "iputils.hh" #include #include #include #include "sholder.hh" #include "sstuff.hh" #include "webhook.hh" #include "wforce-webserver.hh" #include "wforce_exception.hh" #include "login_tuple.hh" #include "perf-stats.hh" #include "sodcrypto.hh" #include "base64.hh" extern std::mutex g_luamutex; extern LuaContext g_lua; extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex (functions using g_outputBuffer MUST NOT be enabled for the allow/report lua contexts) extern WforceWebserver g_webserver; extern GlobalStateHolder g_ACL; extern WebHookRunner g_webhook_runner; extern WebHookDB g_webhook_db; extern WebHookDB g_custom_webhook_db; extern int g_num_luastates; void setupCommonLua(bool client, bool multi_lua, LuaContext& c_lua, const std::string& config); weakforced-2.10.2/common/customfunc.hh000066400000000000000000000040331461473602600177160ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "misc.hh" #include #include #include #include "json11.hpp" #include "login_tuple.hh" typedef std::tuple CustomFuncReturn; struct CustomFuncArgs { std::map attrs; // additional attributes std::map> attrs_mv; // additional multi-valued attributes void setAttrs(const json11::Json& msg) { LoginTuple lt; lt.setLtAttrs(msg); attrs = std::move(lt.attrs); attrs_mv = std::move(lt.attrs_mv); } json11::Json to_json() const { json11::Json::object jattrs; for (auto& i : attrs_mv) { jattrs.insert(make_pair(i.first, json11::Json(i.second))); } for (auto& i : attrs) { jattrs.insert(make_pair(i.first, json11::Json(i.second))); } return json11::Json::object{ {"attrs", jattrs}, }; } std::string serialize() const { json11::Json msg=to_json(); return msg.dump(); } }; typedef std::function custom_func_t; typedef std::function custom_get_func_t; weakforced-2.10.2/common/device_parser.cc000066400000000000000000000105151461473602600203330ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "device_parser.hh" #include #include #include // Example IMAP Client ID string (Apple Mail on macOS): // "name" "Mac OS X Mail" "version" "10.0 (3226)" "os" "Mac OS X" "os-version" "10.12 (16A323)" "vendor" "Apple Inc." // // This class maps IMAP Client ID fields as follows: // os -> os.family // os-version -> os.major, os.minor // name -> imapc.family // version -> imapc.major, imapc.minor // // IMAP fixes the field names that can be used so this doesn't // need to be very extensible boost::regex name_expr{"\"name\"\\s?\"(.*?)\""}; boost::regex os_expr{"\"os\"\\s?\"(.*?)\""}; boost::regex os_version_expr{"\"os-version\"\\s?\"(\\d+)\\.(\\d+).*?\""}; boost::regex version_expr{"\"version\"\\s?\"(\\d+)\\.(\\d+).*?\""}; IMAPClientID IMAPClientIDParser::parse(const std::string& clientid_str) const { boost::smatch what; IMAPClientID ic; if (boost::regex_search(clientid_str, what, name_expr)) { ic.imapc.family = std::string(what[1].first, what[1].second); } if (boost::regex_search(clientid_str, what, os_expr)) { ic.os.family = std::string(what[1].first, what[1].second); } if (boost::regex_search(clientid_str, what, version_expr)) { ic.imapc.major = std::string(what[1].first, what[1].second); ic.imapc.minor = std::string(what[2].first, what[2].second); } if (boost::regex_search(clientid_str, what, os_version_expr)) { ic.os.major = std::string(what[1].first, what[1].second); ic.os.minor = std::string(what[2].first, what[2].second); } return ic; } // OX Mobile App Parser // Format of device_id is: ../.. ... (OS: .; device: ) // e.g. OpenXchange.iOS.Mail/1.0.3 (OS: 10.0.3; device: iPhone 7 Plus) // e.g. OpenXchange.Android.Mail/1.0+1234 (OS: 7.0; device: Samsung/GT9700) boost::regex ox_mob_app_expr{"^(.*?)\\.(.*?)\\.(.*?)/(\\d+)\\.(\\d+)"}; boost::regex ox_mob_os_expr{"OS:\\s*(\\d+)\\.(\\d+)"}; boost::regex ox_mob_device_expr{"device:\\s*([a-zA-Z0-9\\-\\._/ \\t]+)"}; OXMobileAppDevice OXMobileAppDeviceParser::parse(const std::string& device_id) const { boost::smatch what; OXMobileAppDevice oxmad; if (boost::regex_search(device_id, what, ox_mob_app_expr)) { oxmad.app.brand = std::string(what[1].first, what[1].second); oxmad.os.family = std::string(what[2].first, what[2].second); oxmad.app.name = std::string(what[3].first, what[3].second); oxmad.app.major = std::string(what[4].first, what[4].second); oxmad.app.minor = std::string(what[5].first, what[5].second); } if (boost::regex_search(device_id, what, ox_mob_os_expr)) { oxmad.os.major = std::string(what[1].first, what[1].second); oxmad.os.minor = std::string(what[2].first, what[2].second); } if (boost::regex_search(device_id, what, ox_mob_device_expr)) { oxmad.device.family = std::string(what[1].first, what[1].second); } return oxmad; } bool DeviceCache::readFromCache(const std::string& device_id, std::map& retattrs) const { ReadLock rl(&d_rwlock); auto i = d_devicemap.find(device_id); if (i != d_devicemap.end()) { retattrs = i->second; // copy return true; } return false; } void DeviceCache::addToCache(const std::string& device_id, const std::map& device_attrs) { WriteLock wl(&d_rwlock); d_devicemap.emplace(device_id, device_attrs); } weakforced-2.10.2/common/device_parser.hh000066400000000000000000000046121461473602600203460ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/uap-cpp/UaParser.h" #include "lock.hh" #include struct IMAPClient : Generic { std::string major; std::string minor; std::string toString() const { return family + " " + toVersionString(); } std::string toVersionString() const { return (major.empty() ? "0" : major) + "." + (minor.empty() ? "0" : minor); } }; struct IMAPClientID { Agent os; IMAPClient imapc; }; class IMAPClientIDParser { public: IMAPClientID parse(const std::string&) const; }; // The following parser is specific to Open-Xchange Mobile Apps // Only invoked if "protocol" field is "mobileapi" struct OXMobileApp { std::string major; std::string minor; std::string brand; std::string name; std::string toString() const { return brand + "/" + name + "/" + toVersionString(); } std::string toVersionString() const { return (major.empty() ? "0" : major) + "." + (minor.empty() ? "0" : minor); } }; struct OXMobileAppDevice { Agent os; Device device; OXMobileApp app; }; class OXMobileAppDeviceParser { public: OXMobileAppDevice parse(const std::string&) const; }; class DeviceCache { public: bool readFromCache(const std::string&, std::map&) const; void addToCache(const std::string&, const std::map&); private: mutable pthread_rwlock_t d_rwlock = PTHREAD_RWLOCK_INITIALIZER; std::unordered_map> d_devicemap; }; weakforced-2.10.2/common/dns_lookup.cc000066400000000000000000000342641461473602600177040ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #ifdef HAVE_GETDNS #include "dns_lookup.hh" #include "misc.hh" #include "iputils.hh" #include "prometheus.hh" #include "dolog.hh" #include #include #include #include #include #define GETDNS_STR_IPV4 "IPv4" #define GETDNS_STR_IPV6 "IPv6" #define GETDNS_STR_ADDRESS_TYPE "address_type" #define GETDNS_STR_ADDRESS_DATA "address_data" #define GETDNS_STR_PORT "port" std::mutex resolv_mutx; std::map> resolvMap; WFResolver::WFResolver(): WFResolver(std::string("default")) { } WFResolver::WFResolver(const std::string& name) : num_contexts(NUM_GETDNS_CONTEXTS), resolver_name(name) { resolver_list = getdns_list_create(); req_timeout = DNS_REQUEST_TIMEOUT; context_index = 0; } WFResolver::~WFResolver() { } void WFResolver::set_request_timeout(uint64_t timeout) { // atomic type req_timeout = timeout; } void WFResolver::set_num_contexts(unsigned int nc) { std::lock_guard lock(mutx); // only do this before any contexts are created if (contexts.size() == 0) num_contexts = nc; } void setAddressDict(getdns_dict* dict, const ComboAddress& ca) { getdns_bindata address_data; getdns_bindata addrtype; addrtype.size = 4; addrtype.data = ca.isIpv4() ? (uint8_t*)GETDNS_STR_IPV4 : (uint8_t*)GETDNS_STR_IPV6; address_data.size = ca.isIpv4() ? 4 : 16; address_data.data = ca.isIpv4() ? (uint8_t*)&(ca.sin4.sin_addr.s_addr) : (uint8_t*)ca.sin6.sin6_addr.s6_addr; if (dict) { getdns_dict_set_bindata(dict, GETDNS_STR_ADDRESS_TYPE, &addrtype); getdns_dict_set_bindata(dict, GETDNS_STR_ADDRESS_DATA, &address_data); } } void WFResolver::add_resolver(const std::string& address, int port) { getdns_dict *resolver_dict = getdns_dict_create(); ComboAddress ca = ComboAddress(address); if (resolver_dict && resolver_list) { setAddressDict(resolver_dict, ca); getdns_dict_set_int(resolver_dict, GETDNS_STR_PORT, port); size_t list_index=0; std::lock_guard lock(mutx); getdns_list_get_length(resolver_list, &list_index); getdns_list_set_dict(resolver_list, list_index, resolver_dict); } getdns_dict_destroy(resolver_dict); } bool WFResolver::create_dns_context(getdns_context **context) { getdns_namespace_t d_namespace = GETDNS_NAMESPACE_DNS; // we don't want the set_from_os=1 because we want stub resolver behavior if (!context || (getdns_context_create(context, 0) != GETDNS_RETURN_GOOD)) { return false; } if ((getdns_context_set_context_update_callback(*context, NULL)) || (getdns_context_set_resolution_type(*context, GETDNS_RESOLUTION_STUB)) || (getdns_context_set_namespaces(*context, (size_t)1, &d_namespace)) || (getdns_context_set_dns_transport(*context, GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP)) || (getdns_context_set_timeout(*context, req_timeout))) return false; if (*context && resolver_list) { getdns_return_t r = getdns_context_set_upstream_recursive_servers(*context, resolver_list); if (r == GETDNS_RETURN_GOOD) { return true; } } return false; } void WFResolver::init_dns_contexts() { for (unsigned int i=0; i ctxp = std::make_shared(); ctxp->context_ctx = my_ctx; contexts.push_back(ctxp); } } } bool WFResolver::get_dns_context(std::shared_ptr* ret_ctx) { std::lock_guard lock(mutx); if (contexts.size() == 0) { init_dns_contexts(); } if (contexts.size() > 0) { if (context_index >= num_contexts) context_index = 0; *ret_ctx = contexts[context_index++]; return true; } else return false; } const char hexDigits[] = "0123456789abcdef"; std::vector WFResolver::lookupRBL(const ComboAddress& ca, const std::string& rblname, boost::optional num_retries_p) { std::stringstream ss; size_t num_retries=0; if (num_retries_p) num_retries = *num_retries_p; if (ca.isIpv4()) { for (int i=3; i>=0; i--) { uint8_t bite = (ca.sin4.sin_addr.s_addr >> (i*8)) & 0x000000ff; ss << +bite << "."; } ss << rblname; return(lookup_address_by_name(ss.str(), num_retries)); } else { for (int i=15; i>=0; i--) { uint8_t lownib = ca.sin6.sin6_addr.s6_addr[i] & 0x0f; uint8_t highnib = (ca.sin6.sin6_addr.s6_addr[i] >> 4) & 0x0f; ss << hexDigits[lownib] << "." << hexDigits[highnib] << "."; } ss << rblname; return(lookup_address_by_name(ss.str(), num_retries)); } } std::vector WFResolver::do_lookup_address_by_name(getdns_context *context, const std::string& name, size_t num_retries) { getdns_dict *response; std::vector retvec; auto start_time = std::chrono::steady_clock::now(); if (!getdns_address_sync(context, name.c_str(), NULL, &response)) { uint32_t status; if (!getdns_dict_get_int(response, "status", &status)) { if (status == GETDNS_RESPSTATUS_GOOD) { getdns_list *resplist; if (!getdns_dict_get_list(response, "just_address_answers", &resplist)) { size_t listlen; if (!getdns_list_get_length(resplist, &listlen)) { for (size_t i=0; i < listlen; i++) { getdns_dict *addrdict; if (!getdns_list_get_dict(resplist, i, &addrdict)) { getdns_bindata *address_data; if (!getdns_dict_get_bindata(addrdict, "address_data", &address_data)) { char* addr = getdns_display_ip_address(address_data); if (addr) retvec.push_back(std::string(addr)); free(addr); } } } } } } else if (status == GETDNS_RESPSTATUS_ALL_TIMEOUT && num_retries--) { retvec = do_lookup_address_by_name(context, name, num_retries); } } getdns_dict_destroy(response); } auto end_time = std::chrono::steady_clock::now(); auto elapsed = end_time - start_time; observePrometheusDNSResolverLatency(resolver_name, std::chrono::duration(elapsed).count()); incPrometheusDNSResolverMetric(resolver_name); return retvec; } void lookup_callback(getdns_context *context, getdns_callback_type_t callback_type, struct getdns_dict *response, void *ud, getdns_transaction_t tid) { AsyncThreadUserData* myud = (AsyncThreadUserData*)ud; myud->callback_type = callback_type; myud->response = response; } std::vector WFResolver::do_lookup_address_by_name_async(getdns_context *context, const std::string& name, size_t num_retries) { std::vector retvec; AsyncThreadUserData ud; getdns_transaction_t tid; auto start_time = std::chrono::steady_clock::now(); ud.response = NULL; if (!getdns_address(context, name.c_str(), NULL, &ud, &tid, lookup_callback)) { // this runs until outstanding requests are completed getdns_context_run(context); if ((ud.callback_type == GETDNS_CALLBACK_COMPLETE) || (ud.callback_type == GETDNS_CALLBACK_TIMEOUT)) { uint32_t status; if (!getdns_dict_get_int(ud.response, "status", &status)) { if (status == GETDNS_RESPSTATUS_GOOD) { getdns_list *resplist; if (!getdns_dict_get_list(ud.response, "just_address_answers", &resplist)) { size_t listlen; if (!getdns_list_get_length(resplist, &listlen)) { for (size_t i=0; i < listlen; i++) { getdns_dict *addrdict; if (!getdns_list_get_dict(resplist, i, &addrdict)) { getdns_bindata *address_data; if (!getdns_dict_get_bindata(addrdict, "address_data", &address_data)) { char* addr = getdns_display_ip_address(address_data); if (addr) retvec.push_back(std::string(addr)); free(addr); } } } } } } else if (status == GETDNS_RESPSTATUS_ALL_TIMEOUT && num_retries--) { retvec = do_lookup_address_by_name_async(context, name, num_retries); } } getdns_dict_destroy(ud.response); } } auto end_time = std::chrono::steady_clock::now(); auto elapsed = end_time - start_time; observePrometheusDNSResolverLatency(resolver_name, std::chrono::duration(elapsed).count()); incPrometheusDNSResolverMetric(resolver_name); return retvec; } std::vector WFResolver::lookup_address_by_name(const std::string& name, boost::optional num_retries_p) { std::shared_ptr context; std::vector retvec; size_t num_retries=0; if (num_retries_p) num_retries = *num_retries_p; size_t num_resolvers=0; { std::lock_guard lock(mutx); getdns_list_get_length(resolver_list, &num_resolvers); } if (num_retries >= num_resolvers) num_retries = num_resolvers - 1; // if we can get a context we can do a lookup if (get_dns_context(&context) == true) { std::lock_guard lock(context->context_mutex); retvec = do_lookup_address_by_name_async(context->context_ctx, name, num_retries); } return retvec; } std::vector WFResolver::do_lookup_name_by_address(getdns_context* context, getdns_dict* addr_dict, size_t num_retries) { struct getdns_dict* response; std::vector retvec; auto start_time = std::chrono::steady_clock::now(); if (!getdns_hostname_sync(context, addr_dict, NULL, &response)) { getdns_list* answer; size_t n_answers; getdns_return_t r=GETDNS_RETURN_GOOD; uint32_t status; if (!getdns_dict_get_int(response, "status", &status)) { if (status == GETDNS_RESPSTATUS_ALL_TIMEOUT && num_retries--) { retvec = do_lookup_name_by_address(context, addr_dict, num_retries); } else { if (getdns_dict_get_list(response, "/replies_tree/0/answer", &answer)) { // do nothing } else if (getdns_list_get_length(answer, &n_answers)) { // do nothing } else for (size_t i=0; i(elapsed).count()); incPrometheusDNSResolverMetric(resolver_name); return retvec; } std::vector WFResolver::do_lookup_name_by_address_async(getdns_context* context, getdns_dict* addr_dict, size_t num_retries) { std::vector retvec; AsyncThreadUserData ud; getdns_transaction_t tid; auto start_time = std::chrono::steady_clock::now(); ud.response = NULL; if (!getdns_hostname(context, addr_dict, NULL, &ud, &tid, lookup_callback)) { getdns_list* answer; size_t n_answers; getdns_return_t r=GETDNS_RETURN_GOOD; uint32_t status; // this runs until outstanding requests are completed getdns_context_run(context); if ((ud.callback_type == GETDNS_CALLBACK_COMPLETE) || (ud.callback_type == GETDNS_CALLBACK_TIMEOUT)) { if (!getdns_dict_get_int(ud.response, "status", &status)) { if (status == GETDNS_RESPSTATUS_ALL_TIMEOUT && num_retries--) { retvec = do_lookup_name_by_address(context, addr_dict, num_retries); } else { if (getdns_dict_get_list(ud.response, "/replies_tree/0/answer", &answer)) { // do nothing } else if (getdns_list_get_length(answer, &n_answers)) { // do nothing } else for (size_t i=0; i(elapsed).count()); incPrometheusDNSResolverMetric(resolver_name); return retvec; } std::vector WFResolver::lookup_name_by_address(const ComboAddress& ca, boost::optional num_retries_p) { std::shared_ptr context; getdns_dict *address; std::vector retvec; size_t num_retries=0; if (num_retries_p) num_retries = *num_retries_p; size_t num_resolvers=0; { std::lock_guard lock(mutx); getdns_list_get_length(resolver_list, &num_resolvers); } if (num_retries >= num_resolvers) num_retries = num_resolvers - 1; address = getdns_dict_create(); // if we can setup the context we can do the lookup if (address && get_dns_context(&context)) { std::lock_guard lock(context->context_mutex); setAddressDict(address, ca); retvec = do_lookup_name_by_address_async(context->context_ctx, address, num_retries); } if (address) getdns_dict_destroy(address); return retvec; } #endif // HAVE_GETDNS weakforced-2.10.2/common/dns_lookup.hh000066400000000000000000000060471461473602600177140ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include #include #include #include #include "iputils.hh" struct GetDNSContext { getdns_context* context_ctx; std::mutex context_mutex; }; struct AsyncThreadUserData { getdns_dict* response; getdns_callback_type_t callback_type; }; #define DNS_REQUEST_TIMEOUT 1000 #define NUM_GETDNS_CONTEXTS 12 class WFResolver { public: WFResolver(); WFResolver(const std::string& name); ~WFResolver(); WFResolver(const WFResolver&) = delete; WFResolver& operator=(const WFResolver&) = delete; void add_resolver(const std::string& address, int port); void set_request_timeout(uint64_t timeout); void set_num_contexts(unsigned int nc); std::vector lookup_address_by_name(const std::string& name, boost::optional num_retries); std::vector lookup_name_by_address(const ComboAddress& ca, boost::optional num_retries); std::vector lookupRBL(const ComboAddress& ca, const std::string& rblname, boost::optional num_retries); protected: void init_dns_contexts(); bool create_dns_context(getdns_context **context); bool get_dns_context(std::shared_ptr* ret_ctx); std::vector do_lookup_address_by_name(getdns_context *context, const std::string& name, size_t num_retries); std::vector do_lookup_name_by_address(getdns_context *context, getdns_dict* addr_dict, size_t num_retries=0); std::vector do_lookup_address_by_name_async(getdns_context *context, const std::string& name, size_t num_retries); std::vector do_lookup_name_by_address_async(getdns_context *context, getdns_dict* addr_dict, size_t num_retries=0); private: getdns_list* resolver_list; std::atomic req_timeout; std::mutex mutx; unsigned int num_contexts; std::vector> contexts; std::atomic context_index; std::string resolver_name; }; extern std::mutex resolv_mutx; extern std::map> resolvMap; weakforced-2.10.2/common/dolog.hh000066400000000000000000000075261461473602600166460ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include #include #include /* This file is intended not to be metronome specific, and is simple example of C++2011 variadic templates in action. The goal is rapid easy to use logging to console & syslog. Usage: string address="localhost"; infolog("Bound to %s port %d", address, port); warnlog("Query took %d milliseconds", 1232.4); // yes, %d errlog("Unable to bind to %s: %s", ca.toStringWithPort(), strerr(errno)); If bool g_console is true, will log to stdout. Will syslog in any case with LOG_INFO, LOG_WARNING, LOG_ERR respectively. More generically, dolog(someiostream, "Hello %s", stream) will log to someiostream This will happily print a string to %d! Doesn't do further format processing. */ inline void dolog(std::ostream& os, const char*s) { os< void dolog(std::ostream& os, const char* s, T value, Args... args) { while (*s) { if (*s == '%') { if (*(s + 1) == '%') { ++s; } else { os << value; s += 2; dolog(os, s, args...); return; } } os << *s++; } } extern bool g_console; extern bool g_verbose; extern bool g_docker; enum class LogLevel { Emerg=0, Alert, Crit, Err, Warning, Notice, Info, Debug}; extern LogLevel g_loglevel; template void genlog(int level, const char* s, Args... args) { using namespace std::chrono; std::ostringstream str; dolog(str, s, args...); syslog(level, "%s", str.str().c_str()); if(g_console && level <= static_cast(g_loglevel)) { // For docker we include a datetime string if (g_docker) { auto now = system_clock::now(); std::time_t ct = system_clock::to_time_t(now); auto ms = duration_cast(now.time_since_epoch()); struct tm currentLocalTime; localtime_r(&ct, ¤tLocalTime); // The format means that it cannot be longer than 24 bytes (including null byte) char timebuf[24]; std::strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", ¤tLocalTime); std::cout << "[" << timebuf << "," << std::setfill('0') << std::setw(3) << ms.count() % 1000 << "] "; } std::cout< void infolog(const char* s, Args... args) { genlog(LOG_INFO, s, args...); } template void noticelog(const char* s, Args... args) { genlog(LOG_NOTICE, s, args...); } template void warnlog(const char* s, Args... args) { genlog(LOG_WARNING, s, args...); } template void errlog(const char* s, Args... args) { genlog(LOG_ERR, s, args...); } #define vdebuglog if(g_verbose)debuglog template void debuglog(const char* s, Args... args) { genlog(LOG_DEBUG, s, args...); } weakforced-2.10.2/common/hmac.cc000066400000000000000000000050221461473602600164250ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "hmac.hh" #include "wforce_exception.hh" static const EVP_MD* getMDType(HashAlgo algo) { switch(algo) { case HashAlgo::SHA224: return EVP_sha224(); break; case HashAlgo::SHA256: return EVP_sha256(); break; case HashAlgo::SHA384: return EVP_sha384(); break; case HashAlgo::SHA512: return EVP_sha512(); break; default: throw WforceException("Unknown hash algorithm requested from calculateHMAC()"); } } // This function is borrowed and modified from pdns src std::string calculateHMAC(const std::string& key, const std::string& text, HashAlgo algo) { const EVP_MD* md_type; unsigned int outlen; unsigned char hash[EVP_MAX_MD_SIZE]; md_type = getMDType(algo); unsigned char* out = HMAC(md_type, reinterpret_cast(key.c_str()), key.size(), reinterpret_cast(text.c_str()), text.size(), hash, &outlen); if (out != NULL && outlen > 0) { return std::string((char*) hash, outlen); } return ""; } std::string calculateHash(const std::string& text, HashAlgo algo) { EVP_MD_CTX *mdctx; const EVP_MD *md_type; unsigned char hash[EVP_MAX_MD_SIZE]; unsigned int out_len; md_type = getMDType(algo); mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, md_type, NULL); EVP_DigestUpdate(mdctx, reinterpret_cast(text.c_str()), text.size()); int ret = EVP_DigestFinal_ex(mdctx, hash, &out_len); EVP_MD_CTX_destroy(mdctx); if ((ret == 1) && (out_len > 0)) { return std::string((char*) hash, out_len); } return ""; } weakforced-2.10.2/common/hmac.hh000066400000000000000000000023261461473602600164430ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include enum class HashAlgo { SHA224, SHA256, SHA384, SHA512 }; std::string calculateHMAC(const std::string& key, const std::string& text, HashAlgo algo); std::string calculateHash(const std::string& text, HashAlgo algo); weakforced-2.10.2/common/iputils.cc000066400000000000000000000304201461473602600172060ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "iputils.hh" #include /** these functions provide a very lightweight wrapper to the Berkeley sockets API. Errors -> exceptions! */ static void RuntimeError(const boost::format& fmt) { throw runtime_error(fmt.str()); } int SSocket(int family, int type, int flags) { int ret = socket(family, type, flags); if(ret < 0) RuntimeError(boost::format("creating socket of type %d: %s") % family % strerror(errno)); return ret; } int SConnect(int sockfd, const ComboAddress& remote) { int ret = connect(sockfd, (struct sockaddr*)&remote, remote.getSocklen()); if(ret < 0) { int savederrno = errno; RuntimeError(boost::format("connecting socket to %s: %s") % remote.toStringWithPort() % strerror(savederrno)); } return ret; } int SConnectWithTimeout(int sockfd, const ComboAddress& remote, int timeout) { int ret = connect(sockfd, (struct sockaddr*)&remote, remote.getSocklen()); if(ret < 0) { int savederrno = errno; if (savederrno == EINPROGRESS) { /* we wait until the connection has been established */ bool error = false; bool disconnected = false; int res = waitForRWData(sockfd, false, timeout, 0, &error, &disconnected); if (res == 1) { if (error) { savederrno = 0; socklen_t errlen = sizeof(savederrno); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&savederrno, &errlen) == 0) { RuntimeError(boost::format("connecting to %s failed: %s") % remote.toStringWithPort() % string(strerror(savederrno))); } else { RuntimeError(boost::format("connecting to %s failed") % remote.toStringWithPort()); } } if (disconnected) { RuntimeError(boost::format("%s closed the connection") % remote.toStringWithPort()); } return 0; } else if (res == 0) { RuntimeError(boost::format("timeout while connecting to %s") % remote.toStringWithPort()); } else if (res < 0) { savederrno = errno; RuntimeError(boost::format("waiting to connect to %s: %s") % remote.toStringWithPort() % string(strerror(savederrno))); } } else { RuntimeError(boost::format("connecting to %s: %s") % remote.toStringWithPort() % string(strerror(savederrno))); } } return ret; } int SBind(int sockfd, const ComboAddress& local) { int ret = bind(sockfd, (struct sockaddr*)&local, local.getSocklen()); if(ret < 0) { int savederrno = errno; RuntimeError(boost::format("binding socket to %s: %s") % local.toStringWithPort() % strerror(savederrno)); } return ret; } int SAccept(int sockfd, ComboAddress& remote) { socklen_t remlen = remote.getSocklen(); int ret = accept(sockfd, (struct sockaddr*)&remote, &remlen); if(ret < 0) RuntimeError(boost::format("accepting new connection on socket: %s") % strerror(errno)); return ret; } int SListen(int sockfd, int limit) { int ret = listen(sockfd, limit); if(ret < 0) RuntimeError(boost::format("setting socket to listen: %s") % strerror(errno)); return ret; } int SSetsockopt(int sockfd, int level, int opname, int value) { int ret = setsockopt(sockfd, level, opname, &value, sizeof(value)); if(ret < 0) RuntimeError(boost::format("setsockopt for level %d and opname %d to %d failed: %s") % level % opname % value % strerror(errno)); return ret; } bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv) { #ifdef SO_TIMESTAMP struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh,cmsg)) { if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) && CMSG_LEN(sizeof(*tv)) == cmsg->cmsg_len) { memcpy(tv, CMSG_DATA(cmsg), sizeof(*tv)); return true; } } #endif return false; } bool HarvestDestinationAddress(struct msghdr* msgh, ComboAddress* destination) { destination->reset(); struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh,cmsg)) { #if defined(IP_PKTINFO) if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) { struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg); destination->sin4.sin_addr = i->ipi_addr; destination->sin4.sin_family = AF_INET; return true; } #elif defined(IP_RECVDSTADDR) if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) { struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg); destination->sin4.sin_addr = *i; destination->sin4.sin_family = AF_INET; return true; } #endif #if defined(IPV6_PKTINFO) if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) { struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg); destination->sin6.sin6_addr = i->ipi6_addr; destination->sin4.sin_family = AF_INET6; return true; } #endif } return false; } bool IsAnyAddress(const ComboAddress& addr) { if(addr.sin4.sin_family == AF_INET) return addr.sin4.sin_addr.s_addr == 0; else if(addr.sin4.sin_family == AF_INET6) return !memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr)); return false; } ssize_t sendfromto(int sock, const char* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& to) { struct msghdr msgh; struct iovec iov; /* Set up iov and msgh structures. */ memset(&msgh, 0, sizeof(struct msghdr)); iov.iov_base = (void*)data; iov.iov_len = len; msgh.msg_iov = &iov; msgh.msg_iovlen = 1; msgh.msg_name = (struct sockaddr*)&to; msgh.msg_namelen = to.getSocklen(); if(from.sin4.sin_family) { char cbuf[256]; addCMsgSrcAddr(&msgh, cbuf, &from, 0); } else { msgh.msg_control=NULL; } return sendmsg(sock, &msgh, flags); } // be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works! // be careful: when using this function for *send* purposes, be sure to set cbufsize to 0! // be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, char* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr) { iov->iov_base = data; iov->iov_len = datalen; memset(msgh, 0, sizeof(struct msghdr)); msgh->msg_control = cbuf; msgh->msg_controllen = cbufsize; msgh->msg_name = addr; msgh->msg_namelen = addr->getSocklen(); msgh->msg_iov = iov; msgh->msg_iovlen = 1; msgh->msg_flags = 0; } // warning: various parts of PowerDNS assume 'truncate' will never throw void ComboAddress::truncate(unsigned int bits) noexcept { uint8_t* start; int len=4; if(sin4.sin_family==AF_INET) { if(bits >= 32) return; start = (uint8_t*)&sin4.sin_addr.s_addr; len=4; } else { if(bits >= 128) return; start = (uint8_t*)&sin6.sin6_addr.s6_addr; len=16; } auto tozero= len*8 - bits; // if set to 22, this will clear 1 byte, as it should memset(start + len - tozero/8, 0, tozero/8); // blot out the whole bytes on the right auto bitsleft=tozero % 8; // 2 bits left to clear // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c // so and by '11111100', which is ~((1<<2)-1) = ~3 uint8_t* place = start + len - 1 - tozero/8; *place &= (~((1<(buffer), len, &dest); addCMsgSrcAddr(&msgh, cbuf, &local, localItf); do { ssize_t written = sendmsg(fd, &msgh, 0); if (written > 0) return written; if (errno == EAGAIN) { if (firstTry) { int res = waitForRWData(fd, false, timeout, 0); if (res > 0) { /* there is room available */ firstTry = false; } else if (res == 0) { throw runtime_error("Timeout while waiting to write data"); } else { throw runtime_error("Error while waiting for room to write data"); } } else { throw runtime_error("Timeout while waiting to write data"); } } else { unixDie("failed in write2WithTimeout"); } } while (firstTry); return 0; } template class NetmaskTree; bool sendSizeAndMsgWithTimeout(int sock, uint16_t bufferLen, const char* buffer, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags) { uint16_t size = htons(bufferLen); struct msghdr msgh; struct iovec iov[2]; int remainingTime = totalTimeout; time_t start = 0; if (totalTimeout) { start = time(NULL); } /* Set up iov and msgh structures. */ memset(&msgh, 0, sizeof(struct msghdr)); msgh.msg_control = nullptr; msgh.msg_controllen = 0; if (dest) { msgh.msg_name = reinterpret_cast(const_cast(dest)); msgh.msg_namelen = dest->getSocklen(); } else { msgh.msg_name = nullptr; msgh.msg_namelen = 0; } msgh.msg_flags = 0; if (localItf != 0 && local) { char cbuf[256]; addCMsgSrcAddr(&msgh, cbuf, local, localItf); } iov[0].iov_base = &size; iov[0].iov_len = sizeof(size); iov[1].iov_base = reinterpret_cast(const_cast(buffer)); iov[1].iov_len = bufferLen; size_t pos = 0; size_t sent = 0; size_t nbElements = sizeof(iov)/sizeof(*iov); while (true) { msgh.msg_iov = &iov[pos]; msgh.msg_iovlen = nbElements - pos; ssize_t res = sendmsg(sock, &msgh, flags); if (res > 0) { size_t written = static_cast(res); sent += written; if (sent == (sizeof(size) + bufferLen)) { return true; } /* partial write, we need to keep only the (parts of) elements that have not been written. */ do { if (written < iov[pos].iov_len) { iov[pos].iov_len -= written; written = 0; } else { written -= iov[pos].iov_len; iov[pos].iov_len = 0; pos++; } } while (written > 0 && pos < nbElements); } else if (res == -1) { if (errno == EINTR) { continue; } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) { /* EINPROGRESS might happen with non blocking socket, especially with TCP Fast Open */ int ret = waitForRWData(sock, false, (totalTimeout == 0 || idleTimeout <= remainingTime) ? idleTimeout : remainingTime, 0); if (ret > 0) { /* there is room available */ } else if (ret == 0) { throw runtime_error("Timeout while waiting to send data"); } else { throw runtime_error("Error while waiting for room to send data"); } } else { unixDie("failed in sendSizeAndMsgWithTimeout"); } } if (totalTimeout) { time_t now = time(NULL); int elapsed = now - start; if (elapsed >= remainingTime) { throw runtime_error("Timeout while sending data"); } start = now; remainingTime -= elapsed; } } return false; } weakforced-2.10.2/common/iputils.hh000066400000000000000000000673231461473602600172340ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PDNS_IPUTILSHH #define PDNS_IPUTILSHH #include #include #include #include #include #include #include #include #include "wforce_exception.hh" #include "misc.hh" #include #include #include #include #include #include "namespaces.hh" #ifdef __APPLE__ #include #define htobe16(x) OSSwapHostToBigInt16(x) #define htole16(x) OSSwapHostToLittleInt16(x) #define be16toh(x) OSSwapBigToHostInt16(x) #define le16toh(x) OSSwapLittleToHostInt16(x) #define htobe32(x) OSSwapHostToBigInt32(x) #define htole32(x) OSSwapHostToLittleInt32(x) #define be32toh(x) OSSwapBigToHostInt32(x) #define le32toh(x) OSSwapLittleToHostInt32(x) #define htobe64(x) OSSwapHostToBigInt64(x) #define htole64(x) OSSwapHostToLittleInt64(x) #define be64toh(x) OSSwapBigToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x) #endif // for illumos #ifdef BE_64 #define htobe16(x) BE_16(x) #define htole16(x) LE_16(x) #define be16toh(x) BE_IN16(&(x)) #define le16toh(x) LE_IN16(&(x)) #define htobe32(x) BE_32(x) #define htole32(x) LE_32(x) #define be32toh(x) BE_IN32(&(x)) #define le32toh(x) LE_IN32(&(x)) #define htobe64(x) BE_64(x) #define htole64(x) LE_64(x) #define be64toh(x) BE_IN64(&(x)) #define le64toh(x) LE_IN64(&(x)) #endif #ifdef __FreeBSD__ #include #endif union ComboAddress { struct sockaddr_in sin4; struct sockaddr_in6 sin6; bool operator==(const ComboAddress& rhs) const { if(boost::tie(sin4.sin_family, sin4.sin_port) != boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port)) return false; if(sin4.sin_family == AF_INET) return sin4.sin_addr.s_addr == rhs.sin4.sin_addr.s_addr; else return memcmp(&sin6.sin6_addr.s6_addr, &rhs.sin6.sin6_addr.s6_addr, sizeof(sin6.sin6_addr.s6_addr))==0; } bool operator!=(const ComboAddress& rhs) const { return(!operator==(rhs)); } bool operator<(const ComboAddress& rhs) const { if(sin4.sin_family == 0) { return false; } if(boost::tie(sin4.sin_family, sin4.sin_port) < boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port)) return true; if(boost::tie(sin4.sin_family, sin4.sin_port) > boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port)) return false; if(sin4.sin_family == AF_INET) return sin4.sin_addr.s_addr < rhs.sin4.sin_addr.s_addr; else return memcmp(&sin6.sin6_addr.s6_addr, &rhs.sin6.sin6_addr.s6_addr, sizeof(sin6.sin6_addr.s6_addr)) < 0; } bool operator>(const ComboAddress& rhs) const { return rhs.operator<(*this); } struct addressOnlyHash { uint32_t operator()(const ComboAddress& ca) const { const unsigned char* start; int len; if(ca.sin4.sin_family == AF_INET) { start =(const unsigned char*)&ca.sin4.sin_addr.s_addr; len=4; } else { start =(const unsigned char*)&ca.sin6.sin6_addr.s6_addr; len=16; } return burtle(start, len, 0); } }; struct addressOnlyLessThan { bool operator()(const ComboAddress& a, const ComboAddress& b) const { if(a.sin4.sin_family < b.sin4.sin_family) return true; if(a.sin4.sin_family > b.sin4.sin_family) return false; if(a.sin4.sin_family == AF_INET) return a.sin4.sin_addr.s_addr < b.sin4.sin_addr.s_addr; else return memcmp(&a.sin6.sin6_addr.s6_addr, &b.sin6.sin6_addr.s6_addr, sizeof(a.sin6.sin6_addr.s6_addr)) < 0; } }; struct addressOnlyEqual { bool operator()(const ComboAddress& a, const ComboAddress& b) const { if(a.sin4.sin_family != b.sin4.sin_family) return false; if(a.sin4.sin_family == AF_INET) return a.sin4.sin_addr.s_addr == b.sin4.sin_addr.s_addr; else return !memcmp(&a.sin6.sin6_addr.s6_addr, &b.sin6.sin6_addr.s6_addr, sizeof(a.sin6.sin6_addr.s6_addr)); } }; socklen_t getSocklen() const { if(sin4.sin_family == AF_INET) return sizeof(sin4); else return sizeof(sin6); } ComboAddress() { sin4.sin_family=AF_INET; sin4.sin_addr.s_addr=0; sin4.sin_port=0; } ComboAddress(const struct sockaddr *sa, socklen_t salen) { setSockaddr(sa, salen); }; ComboAddress(const struct sockaddr_in6 *sa) { setSockaddr((const struct sockaddr*)sa, sizeof(struct sockaddr_in6)); }; ComboAddress(const struct sockaddr_in *sa) { setSockaddr((const struct sockaddr*)sa, sizeof(struct sockaddr_in)); }; void setSockaddr(const struct sockaddr *sa, socklen_t salen) { if (salen > sizeof(struct sockaddr_in6)) throw PDNSException("ComboAddress can't handle other than sockaddr_in or sockaddr_in6"); memcpy(this, sa, salen); } // 'port' sets a default value in case 'str' does not set a port explicit ComboAddress(const string& str, uint16_t port=0) { memset(&sin6, 0, sizeof(sin6)); sin4.sin_family = AF_INET; sin4.sin_port = 0; if(makeIPv4sockaddr(str, &sin4)) { sin6.sin6_family = AF_INET6; if(makeIPv6sockaddr(str, &sin6) < 0) throw PDNSException("Unable to convert presentation address '"+ str +"'"); } if(!sin4.sin_port) // 'str' overrides port! sin4.sin_port=htons(port); } bool isIpv6() const { return sin6.sin6_family == AF_INET6; } bool isIpv4() const { return sin4.sin_family == AF_INET; } bool isMappedIPv4() const { if(sin4.sin_family!=AF_INET6) return false; int n=0; const unsigned char*ptr = (unsigned char*) &sin6.sin6_addr.s6_addr; for(n=0; n < 10; ++n) if(ptr[n]) return false; for(; n < 12; ++n) if(ptr[n]!=0xff) return false; return true; } ComboAddress mapToIPv4() const { if(!isMappedIPv4()) throw PDNSException("ComboAddress can't map non-mapped IPv6 address back to IPv4"); ComboAddress ret; ret.sin4.sin_family=AF_INET; ret.sin4.sin_port=sin4.sin_port; const unsigned char*ptr = (unsigned char*) &sin6.sin6_addr.s6_addr; ptr+=(sizeof(sin6.sin6_addr.s6_addr) - sizeof(ret.sin4.sin_addr.s_addr)); memcpy(&ret.sin4.sin_addr.s_addr, ptr, sizeof(ret.sin4.sin_addr.s_addr)); return ret; } string toString() const { char host[1024]; if(sin4.sin_family && !getnameinfo((struct sockaddr*) this, getSocklen(), host, sizeof(host),0, 0, NI_NUMERICHOST)) return host; else return "invalid"; } string toStringWithPort() const { if(sin4.sin_family==AF_INET) return toString() + ":" + std::to_string(ntohs(sin4.sin_port)); else return "["+toString() + "]:" + std::to_string(ntohs(sin4.sin_port)); } unsigned int getPort() const { return ntohs(sin4.sin_port); } void truncate(unsigned int bits) noexcept; void reset(); }; /** This exception is thrown by the Netmask class and by extension by the NetmaskGroup class */ class NetmaskException: public PDNSException { public: NetmaskException(const string &a) : PDNSException(a) {} }; inline ComboAddress makeComboAddress(const string& str) { ComboAddress address; address.sin4.sin_family=AF_INET; if(inet_pton(AF_INET, str.c_str(), &address.sin4.sin_addr) <= 0) { address.sin4.sin_family=AF_INET6; if(makeIPv6sockaddr(str, &address.sin6) < 0) throw NetmaskException("Unable to convert '"+str+"' to a netmask"); } return address; } /** This class represents a netmask and can be queried to see if a certain IP address is matched by this mask */ class Netmask { public: Netmask() { d_network.sin4.sin_family=0; // disable this doing anything useful d_network.sin4.sin_port = 0; // this guarantees d_network compares identical d_mask=0; d_bits=0; } Netmask(const ComboAddress& network, uint8_t bits=0xff) : d_network(network) { if(bits == 0xff) bits = (network.sin4.sin_family == AF_INET) ? 32 : 128; if ((network.sin4.sin_family == AF_INET) && bits > 32) bits = 32; if ((network.sin6.sin6_family == AF_INET6) && bits > 128) bits = 128; d_bits = bits; if(d_bits<32) d_mask=~(0xFFFFFFFF>>d_bits); else d_mask=0xFFFFFFFF; // not actually used for IPv6 } //! Constructor supplies the mask, which cannot be changed Netmask(const string &mask) { pair split=splitField(mask,'/'); d_network=makeComboAddress(split.first); if(!split.second.empty()) { d_bits = (uint8_t)pdns_stou(split.second); if(d_bits<32) d_mask=~(0xFFFFFFFF>>d_bits); else d_mask=0xFFFFFFFF; } else if(d_network.sin4.sin_family==AF_INET) { d_bits = 32; d_mask = 0xFFFFFFFF; } else { d_bits=128; d_mask=0; // silence silly warning - d_mask is unused for IPv6 } } bool match(const ComboAddress& ip) const { return match(&ip); } //! If this IP address in socket address matches bool match(const ComboAddress *ip) const { if(d_network.sin4.sin_family != ip->sin4.sin_family) { return false; } if(d_network.sin4.sin_family == AF_INET) { return match4(htonl((unsigned int)ip->sin4.sin_addr.s_addr)); } if(d_network.sin6.sin6_family == AF_INET6) { uint8_t bytes=d_bits/8, n; const uint8_t *us=(const uint8_t*) &d_network.sin6.sin6_addr.s6_addr; const uint8_t *them=(const uint8_t*) &ip->sin6.sin6_addr.s6_addr; for(n=0; n < bytes; ++n) { if(us[n]!=them[n]) { return false; } } // still here, now match remaining bits uint8_t bits= d_bits % 8; uint8_t mask= (uint8_t) ~(0xFF>>bits); return((us[n] & mask) == (them[n] & mask)); } return false; } //! If this ASCII IP address matches bool match(const string &ip) const { ComboAddress address=makeComboAddress(ip); return match(&address); } //! If this IP address in native format matches bool match4(uint32_t ip) const { return (ip & d_mask) == (ntohl(d_network.sin4.sin_addr.s_addr) & d_mask); } string toString() const { return d_network.toString()+"/"+std::to_string((unsigned int)d_bits); } string toStringNoMask() const { return d_network.toString(); } string toStringNetwork() const { if (d_network.sin4.sin_family == AF_INET) { struct sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_addr.s_addr = ntohl((ntohl(d_network.sin4.sin_addr.s_addr) & d_mask)); return (ComboAddress(&sa).toString()+"/"+std::to_string((unsigned int)d_bits)); } else if (d_network.sin6.sin6_family == AF_INET6) { uint8_t bytes = d_bits/8, n; struct sockaddr_in6 sa6 = { 0 }; sa6.sin6_family = AF_INET6; for (n=0; n>bits); sa6.sin6_addr.s6_addr[n] = d_network.sin6.sin6_addr.s6_addr[n] & mask; } return (ComboAddress(&sa6).toString()+"/"+std::to_string((unsigned int)d_bits)); } else return(string("")); } const ComboAddress& getNetwork() const { return d_network; } const ComboAddress getMaskedNetwork() const { ComboAddress result(d_network); if(isIpv4()) { result.sin4.sin_addr.s_addr = htonl(ntohl(result.sin4.sin_addr.s_addr) & d_mask); } else if(isIpv6()) { size_t idx; uint8_t bytes=d_bits/8; uint8_t *us=(uint8_t*) &result.sin6.sin6_addr.s6_addr; uint8_t bits= d_bits % 8; uint8_t mask= (uint8_t) ~(0xFF>>bits); if (bytes < sizeof(result.sin6.sin6_addr.s6_addr)) { us[bytes] &= mask; } for(idx = bytes + 1; idx < sizeof(result.sin6.sin6_addr.s6_addr); ++idx) { us[idx] = 0; } } return result; } int getBits() const { return d_bits; } bool isIpv6() const { return d_network.sin6.sin6_family == AF_INET6; } bool isIpv4() const { return d_network.sin4.sin_family == AF_INET; } bool operator<(const Netmask& rhs) const { return tie(d_network, d_bits) < tie(rhs.d_network, rhs.d_bits); } bool operator==(const Netmask& rhs) const { return tie(d_network, d_bits) == tie(rhs.d_network, rhs.d_bits); } bool empty() const { return d_network.sin4.sin_family==0; } private: ComboAddress d_network; uint32_t d_mask; uint8_t d_bits; }; /** Per-bit binary tree map implementation with pair. * * This is an binary tree implementation for storing attributes for IPv4 and IPv6 prefixes. * The most simple use case is simple NetmaskTree used by NetmaskGroup, which only * wants to know if given IP address is matched in the prefixes stored. * * This element is useful for anything that needs to *STORE* prefixes, and *MATCH* IP addresses * to a *LIST* of *PREFIXES*. Not the other way round. * * You can store IPv4 and IPv6 addresses to same tree, separate payload storage is kept per AFI. * * To erase something copy values to new tree sans the value you want to erase. * * Use swap if you need to move the tree to another NetmaskTree instance, it is WAY faster * than using copy ctor or assignment operator, since it moves the nodes and tree root to * new home instead of actually recreating the tree. * * Please see NetmaskGroup for example of simple use case. Other usecases can be found * from GeoIPBackend and Sortlist, and from dnsdist. */ template class NetmaskTree { public: typedef Netmask key_type; typedef T value_type; typedef std::pair node_type; typedef size_t size_type; private: /** Single node in tree, internal use only. */ class TreeNode : boost::noncopyable { public: explicit TreeNode(int bits) noexcept : parent(NULL),d_bits(bits) { } //(new TreeNode(d_bits+1)); left->parent = this; } return left.get(); } //(new TreeNode(d_bits+1)); right->parent = this; } return right.get(); } std::unique_ptr left; std::unique_ptr right; TreeNode* parent; std::unique_ptr node4; // node6; //first).second = node->second; } NetmaskTree& operator=(const NetmaskTree& rhs) { clear(); // see above. for(auto const& node: rhs._nodes) insert(node->first).second = node->second; return *this; } const typename std::set::const_iterator begin() const { return _nodes.begin(); } const typename std::set::const_iterator end() const { return _nodes.end(); } typename std::set::iterator begin() { return _nodes.begin(); } typename std::set::iterator end() { return _nodes.end(); } node_type& insert(const string &mask) { return insert(key_type(mask)); } //(new TreeNode(0)); TreeNode* node = root.get(); node_type* value = nullptr; if (key.getNetwork().sin4.sin_family == AF_INET) { std::bitset<32> addr(be32toh(key.getNetwork().sin4.sin_addr.s_addr)); int bits = 0; // we turn left on 0 and right on 1 while(bits < key.getBits()) { uint8_t val = addr[31-bits]; if (val) node = node->make_right(); else node = node->make_left(); bits++; } // only create node if not yet assigned if (!node->node4) { node->node4 = std::unique_ptr(new node_type()); _nodes.insert(node->node4.get()); } value = node->node4.get(); } else { uint64_t* addr = (uint64_t*)key.getNetwork().sin6.sin6_addr.s6_addr; std::bitset<64> addr_low(be64toh(addr[1])); std::bitset<64> addr_high(be64toh(addr[0])); int bits = 0; while(bits < key.getBits()) { uint8_t val; // we use high address until we are if (bits < 64) val = addr_high[63-bits]; // past 64 bits, and start using low address else val = addr_low[127-bits]; // we turn left on 0 and right on 1 if (val) node = node->make_right(); else node = node->make_left(); bits++; } // only create node if not yet assigned if (!node->node6) { node->node6 = std::unique_ptr(new node_type()); _nodes.insert(node->node6.get()); } value = node->node6.get(); } // assign key value->first = key; return *value; } //first == key; } // addr(be32toh(value.sin4.sin_addr.s_addr)); int bits = 0; while(bits < max_bits) { // ...we keep track of last non-empty node if (node->node4) ret = node->node4.get(); uint8_t val = addr[31-bits]; // ...and we don't create left/right hand if (val) { if (node->right) node = node->right.get(); // ..and we break when road ends else break; } else { if (node->left) node = node->left.get(); else break; } bits++; } // needed if we did not find one in loop if (node->node4) ret = node->node4.get(); } else { uint64_t* addr = (uint64_t*)value.sin6.sin6_addr.s6_addr; max_bits = std::max(0,std::min(max_bits,128)); std::bitset<64> addr_low(be64toh(addr[1])); std::bitset<64> addr_high(be64toh(addr[0])); int bits = 0; while(bits < max_bits) { if (node->node6) ret = node->node6.get(); uint8_t val; if (bits < 64) val = addr_high[63-bits]; else val = addr_low[127-bits]; if (val) { if (node->right) node = node->right.get(); else break; } else { if (node->left) node = node->left.get(); else break; } bits++; } if (node->node6) ret = node->node6.get(); } // this can be nullptr. return ret; } void cleanup_tree(TreeNode* node) { // only cleanup this node if it has no children and node4 and node6 are both empty if (!(node->left || node->right || node->node6 || node->node4)) { // get parent node ptr TreeNode* parent = node->parent; // delete this node if (parent) { if (parent->left.get() == node) parent->left.reset(); else parent->right.reset(); // now recurse up to the parent cleanup_tree(parent); } } } // addr(be32toh(key.getNetwork().sin4.sin_addr.s_addr)); int bits = 0; while(node && bits < key.getBits()) { uint8_t val = addr[31-bits]; if (val) { node = node->right.get(); } else { node = node->left.get(); } bits++; } if (node) { _nodes.erase(node->node4.get()); node->node4.reset(); if (d_cleanup_tree) cleanup_tree(node); } } else { uint64_t* addr = (uint64_t*)key.getNetwork().sin6.sin6_addr.s6_addr; std::bitset<64> addr_low(be64toh(addr[1])); std::bitset<64> addr_high(be64toh(addr[0])); int bits = 0; while(node && bits < key.getBits()) { uint8_t val; if (bits < 64) val = addr_high[63-bits]; else val = addr_low[127-bits]; if (val) { node = node->right.get(); } else { node = node->left.get(); } bits++; } if (node) { _nodes.erase(node->node6.get()); node->node6.reset(); if (d_cleanup_tree) cleanup_tree(node); } } } void erase(const string& key) { erase(key_type(key)); } // root; // _nodes; //second; return false; } bool match(const ComboAddress& ip) const { return match(&ip); } bool lookup(const ComboAddress* ip, Netmask* nmp) const { const auto &ret = tree.lookup(*ip); if (ret) { if (nmp != nullptr) *nmp = ret->first; return ret->second; } return false; } bool lookup(const ComboAddress& ip, Netmask* nmp) const { return lookup(&ip, nmp); } //! Add this string to the list of possible matches void addMask(const std::string &ip, bool positive=true) { if(!ip.empty() && ip[0] == '!') { addMask(Netmask(ip.substr(1)), false); } else { addMask(Netmask(ip), positive); } } //! Add this Netmask to the list of possible matches void addMask(const Netmask& nm, bool positive=true) { tree.insert(nm).second=positive; } //! Delete this Netmask from the list of possible matches void deleteMask(const Netmask& nm) { tree.erase(nm); } void deleteMask(const std::string& ip) { // This will not delete anything if ip is empty which is fine if (!ip.empty()) deleteMask(Netmask(ip)); } void clear() { tree.clear(); } bool empty() const { return tree.empty(); } size_t size() const { return tree.size(); } string toString() const { std::ostringstream str; for(auto iter = tree.begin(); iter != tree.end(); ++iter) { if(iter != tree.begin()) str <<", "; if(!((*iter)->second)) str<<"!"; str<<(*iter)->first.toString(); } return str.str(); } void toStringVector(std::vector* vec) const { for(auto iter = tree.begin(); iter != tree.end(); ++iter) { vec->push_back(((*iter)->second ? "" : "!") + (*iter)->first.toString()); } } void toMasks(const std::string &ips) { std::vector parts; stringtok(parts, ips, ", \t"); for (std::vector::const_iterator iter = parts.begin(); iter != parts.end(); ++iter) addMask(*iter); } private: NetmaskTree tree; }; struct SComboAddress { SComboAddress(const ComboAddress& orig) : ca(orig) {} ComboAddress ca; bool operator<(const SComboAddress& rhs) const { return ComboAddress::addressOnlyLessThan()(ca, rhs.ca); } operator const ComboAddress&() { return ca; } }; int SSocket(int family, int type, int flags); int SConnect(int sockfd, const ComboAddress& remote); /* tries to connect to remote for a maximum of timeout seconds. sockfd should be set to non-blocking beforehand. returns 0 on success (the socket is writable), throw a runtime_error otherwise */ int SConnectWithTimeout(int sockfd, const ComboAddress& remote, int timeout); int SBind(int sockfd, const ComboAddress& local); int SAccept(int sockfd, ComboAddress& remote); int SListen(int sockfd, int limit); int SSetsockopt(int sockfd, int level, int opname, int value); #if defined(IP_PKTINFO) #define GEN_IP_PKTINFO IP_PKTINFO #elif defined(IP_RECVDSTADDR) #define GEN_IP_PKTINFO IP_RECVDSTADDR #endif bool IsAnyAddress(const ComboAddress& addr); bool HarvestDestinationAddress(struct msghdr* msgh, ComboAddress* destination); bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv); void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, char* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr); ssize_t sendfromto(int sock, const char* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& to); ssize_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int timeout, ComboAddress& dest, const ComboAddress& local, unsigned int localItf); bool sendSizeAndMsgWithTimeout(int sock, uint16_t bufferLen, const char* buffer, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags); extern template class NetmaskTree; #endif weakforced-2.10.2/common/lock.hh000066400000000000000000000104431461473602600164620ustar00rootroot00000000000000/* * This file is part of PowerDNS or dnsdist. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef LOCK_HH #define LOCK_HH #include #include #include "misc.hh" #include "pdnsexception.hh" extern bool g_singleThreaded; class Lock { pthread_mutex_t *d_lock; public: Lock(const Lock& rhs) = delete; Lock& operator=(const Lock& rhs) = delete; Lock(pthread_mutex_t *lock) : d_lock(lock) { if(g_singleThreaded) return; if((errno=pthread_mutex_lock(d_lock))) throw PDNSException("error acquiring lock: "+stringerror()); } ~Lock() { if(g_singleThreaded) return; pthread_mutex_unlock(d_lock); } }; class WriteLock { pthread_rwlock_t *d_lock; public: WriteLock(pthread_rwlock_t *lock) : d_lock(lock) { if(g_singleThreaded) return; if((errno=pthread_rwlock_wrlock(d_lock))) { throw PDNSException("error acquiring rwlock wrlock: "+stringerror()); } } ~WriteLock() { if(g_singleThreaded) return; if(d_lock) // might have been moved pthread_rwlock_unlock(d_lock); } WriteLock(WriteLock&& rhs) { d_lock = rhs.d_lock; rhs.d_lock=0; } WriteLock(const WriteLock& rhs) = delete; WriteLock& operator=(const WriteLock& rhs) = delete; }; class TryWriteLock { pthread_rwlock_t *d_lock; bool d_havelock; public: TryWriteLock(const TryWriteLock& rhs) = delete; TryWriteLock& operator=(const TryWriteLock& rhs) = delete; TryWriteLock(pthread_rwlock_t *lock) : d_lock(lock) { if(g_singleThreaded) { d_havelock=true; return; } d_havelock=false; if((errno=pthread_rwlock_trywrlock(d_lock)) && errno!=EBUSY) throw PDNSException("error acquiring rwlock tryrwlock: "+stringerror()); d_havelock=(errno==0); } TryWriteLock(TryWriteLock&& rhs) { d_lock = rhs.d_lock; rhs.d_lock=0; } ~TryWriteLock() { if(g_singleThreaded) return; if(d_havelock && d_lock) // we might be moved pthread_rwlock_unlock(d_lock); } bool gotIt() { if(g_singleThreaded) return true; return d_havelock; } }; class TryReadLock { pthread_rwlock_t *d_lock; bool d_havelock; public: TryReadLock(const TryReadLock& rhs) = delete; TryReadLock& operator=(const TryReadLock& rhs) = delete; TryReadLock(pthread_rwlock_t *lock) : d_lock(lock) { if(g_singleThreaded) { d_havelock=true; return; } if((errno=pthread_rwlock_tryrdlock(d_lock)) && errno!=EBUSY) throw PDNSException("error acquiring rwlock tryrdlock: "+stringerror()); d_havelock=(errno==0); } TryReadLock(TryReadLock&& rhs) { d_lock = rhs.d_lock; rhs.d_lock=0; } ~TryReadLock() { if(g_singleThreaded) return; if(d_havelock && d_lock) pthread_rwlock_unlock(d_lock); } bool gotIt() { if(g_singleThreaded) return true; return d_havelock; } }; class ReadLock { pthread_rwlock_t *d_lock; public: ReadLock(pthread_rwlock_t *lock) : d_lock(lock) { if(g_singleThreaded) return; if((errno=pthread_rwlock_rdlock(d_lock))) throw PDNSException("error acquiring rwlock readlock: "+stringerror()); } ~ReadLock() { if(g_singleThreaded) return; if(d_lock) // may have been moved pthread_rwlock_unlock(d_lock); } ReadLock(ReadLock&& rhs) { d_lock = rhs.d_lock; rhs.d_lock=0; } ReadLock(const ReadLock& rhs) = delete; ReadLock& operator=(const ReadLock& rhs) = delete; }; #endif weakforced-2.10.2/common/login_tuple.cc000066400000000000000000000166071461473602600200510ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "login_tuple.hh" #include "json11.hpp" static DeviceCache dcache; json11::Json LoginTuple::to_json() const { using namespace json11; Json::object jattrs; Json::object jattrs_dev; for (auto& i : attrs_mv) { jattrs.insert(make_pair(i.first, Json(i.second))); } for (auto& i : attrs) { jattrs.insert(make_pair(i.first, Json(i.second))); } for (auto& i : device_attrs) { jattrs_dev.insert(make_pair(i.first, Json(i.second))); } return Json::object{ {"login", login}, {"success", success}, {"t", (double)t}, {"pwhash", pwhash}, {"remote", remote.toString()}, {"device_id", device_id}, {"device_attrs", jattrs_dev}, {"protocol", protocol}, {"tls", tls}, {"attrs", jattrs}, {"policy_reject", policy_reject}, {"session_id", session_id}, }; } std::string LoginTuple::serialize() const { json11::Json msg=to_json(); return msg.dump(); } void LoginTuple::from_json(const json11::Json& msg, const std::shared_ptr uap) { login=msg["login"].string_value(); pwhash=msg["pwhash"].string_value(); t=msg["t"].number_value(); success=msg["success"].bool_value(); if (msg["remote"].is_string()) { remote=ComboAddress(msg["remote"].string_value()); if (remote.isMappedIPv4()) { remote = remote.mapToIPv4(); } } setLtAttrs(msg); setDeviceAttrs(msg, uap); device_id=msg["device_id"].string_value(); protocol=msg["protocol"].string_value(); tls=msg["tls"].bool_value(); policy_reject=msg["policy_reject"].bool_value(); session_id=msg["session_id"].string_value(); } void LoginTuple::unserialize(const std::string& str) { string err; json11::Json msg=json11::Json::parse(str, err); from_json(msg); } void LoginTuple::setDeviceAttrs(const json11::Json& msg, const std::shared_ptr uap) { std::map cached_attrs; json11::Json jattrs = msg["device_attrs"]; if (jattrs.is_object()) { auto attrs_obj = jattrs.object_items(); for (auto it=attrs_obj.begin(); it!=attrs_obj.end(); ++it) { string attr_name = it->first; if (it->second.is_string()) { device_attrs.insert(std::make_pair(attr_name, it->second.string_value())); } } } else if (dcache.readFromCache(msg["device_id"].string_value(), cached_attrs)) { device_attrs = std::move(cached_attrs); } else if (uap != nullptr) { // client didn't supply, we will parse device_id ourselves std::string my_device_id=msg["device_id"].string_value(); std::string my_protocol=msg["protocol"].string_value(); // parse using the uap-cpp Parser if ((my_protocol.compare("http") == 0) || (my_protocol.compare("https") == 0)) { UserAgent ua = uap->parse(my_device_id); device_attrs.insert(std::make_pair("device.family", ua.device.family)); device_attrs.insert(std::make_pair("device.model", ua.device.model)); device_attrs.insert(std::make_pair("device.brand", ua.device.brand)); device_attrs.insert(std::make_pair("os.family", ua.os.family)); device_attrs.insert(std::make_pair("os.major", ua.os.major)); device_attrs.insert(std::make_pair("os.minor", ua.os.minor)); device_attrs.insert(std::make_pair("browser.family", ua.browser.family)); device_attrs.insert(std::make_pair("browser.major", ua.browser.major)); device_attrs.insert(std::make_pair("browser.minor", ua.browser.minor)); } else if ((my_protocol.compare("imap") == 0) || (my_protocol.compare("imaps") == 0)) { IMAPClientIDParser imap_parser; IMAPClientID ic = imap_parser.parse(my_device_id); device_attrs.insert(std::make_pair("imapc.family", ic.imapc.family)); device_attrs.insert(std::make_pair("imapc.major", ic.imapc.major)); device_attrs.insert(std::make_pair("imapc.minor", ic.imapc.minor)); device_attrs.insert(std::make_pair("os.family", ic.os.family)); device_attrs.insert(std::make_pair("os.major", ic.os.major)); device_attrs.insert(std::make_pair("os.minor", ic.os.minor)); } else if (my_protocol.compare("mobileapi") == 0) { OXMobileAppDeviceParser oxmad_parser; OXMobileAppDevice oxmad = oxmad_parser.parse(my_device_id); device_attrs.insert(std::make_pair("os.family", oxmad.os.family)); device_attrs.insert(std::make_pair("os.major", oxmad.os.major)); device_attrs.insert(std::make_pair("os.minor", oxmad.os.minor)); device_attrs.insert(std::make_pair("app.name", oxmad.app.name)); device_attrs.insert(std::make_pair("app.brand", oxmad.app.brand)); device_attrs.insert(std::make_pair("app.major", oxmad.app.major)); device_attrs.insert(std::make_pair("app.minor", oxmad.app.minor)); device_attrs.insert(std::make_pair("device.family", oxmad.device.family)); } dcache.addToCache(my_device_id, device_attrs); } } void LoginTuple::setLtAttrs(const json11::Json& msg) { json11::Json jattrs = msg["attrs"]; if (jattrs.is_object()) { auto attrs_obj = jattrs.object_items(); for (auto it=attrs_obj.begin(); it!=attrs_obj.end(); ++it) { string attr_name = it->first; if (it->second.is_string()) { attrs.insert(std::make_pair(attr_name, it->second.string_value())); } else if (it->second.is_array()) { auto av_list = it->second.array_items(); std::vector myvec; for (auto avit=av_list.begin(); avit!=av_list.end(); ++avit) { myvec.push_back(avit->string_value()); } attrs_mv.insert(std::make_pair(attr_name, myvec)); } } } } std::string LtAttrsToString(const LoginTuple& lt) { std::ostringstream os; os << "attrs={"; for (auto i= lt.attrs.begin(); i!=lt.attrs.end(); ++i) { os << i->first << "="<< "\"" << i->second << "\""; if (i != --(lt.attrs.end())) os << ", "; } for (auto i = lt.attrs_mv.begin(); i!=lt.attrs_mv.end(); ++i) { if (i == lt.attrs_mv.begin()) os << ", "; os << i->first << "=["; std::vector vec = i->second; for (auto j = vec.begin(); j!=vec.end(); ++j) { os << "\"" << *j << "\""; if (j != --(vec.end())) os << ", "; } os << "]"; if (i != --(lt.attrs_mv.end())) os << ", "; } os << "} "; return os.str(); } std::string DeviceAttrsToString(const LoginTuple& lt) { std::ostringstream os; os << "device_attrs={"; for (auto i= lt.device_attrs.begin(); i!=lt.device_attrs.end(); ++i) { os << i->first << "="<< "\"" << i->second << "\""; if (i != --(lt.device_attrs.end())) os << ", "; } os << "} "; return os.str(); } weakforced-2.10.2/common/login_tuple.hh000066400000000000000000000043531461473602600200560ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "misc.hh" #include "iputils.hh" #include "json11.hpp" #include "device_parser.hh" struct LoginTuple { double t; ComboAddress remote; string login; string pwhash; string device_id; std::map device_attrs; string protocol; bool tls=false; bool success=false; std::map attrs; // additional attributes std::map> attrs_mv; // additional multi-valued attributes bool policy_reject=false; std::string session_id; json11::Json to_json() const; std::string serialize() const; void from_json(const json11::Json& msg, const std::shared_ptr uap=std::shared_ptr()); void unserialize(const std::string& src); void setLtAttrs(const json11::Json& msg); bool operator<(const LoginTuple& r) const { if(std::tie(t, login, pwhash, success) < std::tie(r.t, r.login, r.pwhash, r.success)) return true; ComboAddress cal(remote); ComboAddress car(r.remote); cal.sin4.sin_port=0; car.sin4.sin_port=0; return cal < car; } private: void setDeviceAttrs(const json11::Json& msg, const std::shared_ptr uap); }; std::string LtAttrsToString(const LoginTuple& lt); std::string DeviceAttrsToString(const LoginTuple& lt); weakforced-2.10.2/common/minicurl.cc000066400000000000000000000316071461473602600173470ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "minicurl.hh" #include #include #include #include #include #include #include "wforce_exception.hh" #include "dolog.hh" #ifdef CURL_STRICTER #define getCURLPtr(x) \ x.get() #else #define getCURLPtr(x) \ x #endif bool MiniCurl::initCurlGlobal() { // curl_global_init() is guaranteed to be called only once static const CURLcode init_curl_global = curl_global_init(CURL_GLOBAL_ALL); return init_curl_global == CURLE_OK; } MiniCurl::MiniCurl() : d_id(0) { bool init_cg = initCurlGlobal(); if (init_cg) { #ifdef CURL_STRICTER d_curl = std::unique_ptr(curl_easy_init(), curl_easy_cleanup); #else d_curl = curl_easy_init(); #endif if (d_curl == nullptr) { throw WforceException("Error creating a MiniCurl session"); } else { if (curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_ERRORBUFFER, d_error_buf) != CURLE_OK) throw WforceException("Could not setup curl error buffer"); } } else throw WforceException("Cannot initialize curl library"); } MiniCurl::~MiniCurl() { clearCurlHeaders(); #ifndef CURL_STRICTER if (d_curl) curl_easy_cleanup(d_curl); #endif } size_t MiniCurl::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { if (userdata != nullptr) { MiniCurl* us = static_cast(userdata); us->d_data.append(ptr, size * nmemb); return size * nmemb; } return 0; } void MiniCurl::setCurlOpts() { /* only allow HTTP and HTTPS */ #if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x075500 // 7.85.0 curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_PROTOCOLS_STR, "http,https"); #else curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); #endif #if defined(CURL_AT_LEAST_VERSION) #if CURL_AT_LEAST_VERSION(7, 49, 0) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_TCP_FASTOPEN, 1L); #endif #endif } void MiniCurl::setURLData(const std::string& url, const MiniCurlHeaders& headers) { if (d_curl) { clearCurlHeaders(); setCurlOpts(); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_HTTPGET, 1); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_URL, url.c_str()); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_WRITEFUNCTION, write_callback); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_WRITEDATA, this); setCurlHeaders(headers); d_data.clear(); d_error_buf[0] = '\0'; } } std::string MiniCurl::getURL(const std::string& url, const MiniCurlHeaders& headers) { if (d_curl) { setURLData(url, headers); curl_easy_perform(getCURLPtr(d_curl)); std::string ret=std::move(d_data); d_data.clear(); return ret; } return std::string(); } void MiniCurl::setTimeout(uint64_t timeout_secs) { setCurlOption(CURLOPT_TIMEOUT, static_cast(timeout_secs)); } void MiniCurl::clearCurlHeaders() { if (d_curl) { (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_HTTPHEADER, NULL); if (d_header_list) { #ifdef CURL_STRICTER d_header_list.reset(); #else curl_slist_free_all(d_header_list); d_header_list = nullptr; #endif } } } void MiniCurl::setCurlHeaders(const MiniCurlHeaders& headers) { if (d_curl) { for (auto& header : headers) { std::stringstream header_ss; header_ss << header.first << ": " << header.second; #ifdef CURL_STRICTER struct curl_slist * list = nullptr; if (d_header_list) { list = d_header_list.release(); } d_header_list = std::unique_ptr(curl_slist_append(list, header_ss.str().c_str()), curl_slist_free_all); #else d_header_list = curl_slist_append(d_header_list, header_ss.str().c_str()); #endif } (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_HTTPHEADER, getCURLPtr(d_header_list)); } } size_t MiniCurl::read_callback(char *buffer, size_t size, size_t nitems, void *userdata) { std::stringstream* ss = (std::stringstream*)userdata; ss->read(buffer, size*nitems); auto bytes_read = ss->gcount(); return bytes_read; } CURL* MiniCurl::getCurlHandle() { #ifdef CURL_STRICTER return getCURLPtr(d_curl); #else return d_curl; #endif } // if you want Content-Type other than application/x-www-form-urlencoded you must set Content-Type // to be what you want in the supplied headers, e.g. application/json bool MiniCurl::postURL(const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers, std::string& error_msg) { std::string ignore_res; return postURL(url, post_body, headers, ignore_res, error_msg); } void MiniCurl::setPostData(const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers) { if (d_curl) { d_post_body = std::istringstream(post_body); clearCurlHeaders(); setCurlOpts(); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_URL, url.c_str()); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_POST, 1); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_POSTFIELDSIZE, post_body.length()); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_READFUNCTION, read_callback); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_READDATA, &d_post_body); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_WRITEFUNCTION, write_callback); (void) curl_easy_setopt(getCURLPtr(d_curl), CURLOPT_WRITEDATA, this); setCurlHeaders(headers); d_error_buf[0] = '\0'; d_data.clear(); } } bool MiniCurl::postURL(const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers, std::string& post_res, std::string& error_msg) { bool retval = false; if (d_curl) { setPostData(url, post_body, headers); auto ret = curl_easy_perform(getCURLPtr(d_curl)); post_res = std::move(d_data); d_data.clear(); if (ret != CURLE_OK) { auto eb_len = strlen(d_error_buf); if (eb_len) { error_msg = std::string(d_error_buf); } else { error_msg = std::string(curl_easy_strerror(ret)); } } else { long response_code; curl_easy_getinfo(getCURLPtr(d_curl), CURLINFO_RESPONSE_CODE, &response_code); if (!is2xx(response_code)) { error_msg = std::string("Received non-2XX response from webserver: ") + std::to_string(response_code); } else retval = true; } } return retval; } // MiniCurlMulti MiniCurlMulti::MiniCurlMulti() : d_ccs(numMultiCurlConnections) { #ifdef CURL_STRICTER d_mcurl = std::unique_ptr(curl_multi_init(), curl_multi_cleanup); #else d_mcurl = curl_multi_init(); #endif initMCurl(); } MiniCurlMulti::MiniCurlMulti(size_t num_connections) : d_ccs(num_connections) { #ifdef CURL_STRICTER d_mcurl = std::unique_ptr(curl_multi_init(), curl_multi_cleanup); #else d_mcurl = curl_multi_init(); #endif initMCurl(); } MiniCurlMulti::~MiniCurlMulti() { #ifndef CURL_STRICTER if (d_mcurl != nullptr) { curl_multi_cleanup(d_mcurl); } #endif } void MiniCurlMulti::initMCurl() { #ifdef CURLPIPE_HTTP1 curl_multi_setopt(getCURLPtr(d_mcurl), CURLMOPT_PIPELINING, CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX); #else curl_multi_setopt(getCURLPtr(d_mcurl), CURLMOPT_PIPELINING, 1L); #endif #ifdef CURLMOPT_MAX_HOST_CONNECTIONS curl_multi_setopt(getCURLPtr(d_mcurl), CURLMOPT_MAX_HOST_CONNECTIONS, d_ccs.size()); #endif d_current = d_ccs.begin(); } bool MiniCurlMulti::addPost(unsigned int id, const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers) { if (d_mcurl) { // return false if we don't have any spare connections if (d_current == d_ccs.end()) return false; d_current->setPostData(url, post_body, headers); d_current->setID(id); curl_multi_add_handle(getCURLPtr(d_mcurl), d_current->getCurlHandle()); ++d_current; return true; } return false; } void MiniCurlMulti::setTimeout(uint64_t timeout_secs) { MiniCurl::setTimeout(timeout_secs); for (auto& i : d_ccs) { i.setCurlOption(CURLOPT_TIMEOUT, static_cast(timeout_secs)); } } const std::vector MiniCurlMulti::runPost() { std::vector post_ret; if (d_mcurl) { int still_running; int without_fds = 0; do { CURLMcode mc; int numfds; mc = curl_multi_perform(getCURLPtr(d_mcurl), &still_running); if (mc == CURLM_OK) { auto start_time = std::chrono::steady_clock::now(); mc = curl_multi_wait(getCURLPtr(d_mcurl), NULL, 0, 1000, &numfds); auto wait_time_ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time); if (mc == CURLM_OK) { struct CURLMsg *m; int msgq = 0; while ((m = curl_multi_info_read(getCURLPtr(d_mcurl), &msgq)) != NULL) { if (m->msg == CURLMSG_DONE) { CURL *c = m->easy_handle; auto minic = findMiniCurl(c); if (minic == d_ccs.end()) { throw WforceException("Cannot find curl handle in MiniCurlMulti - something is very wrong"); } struct mcmPostReturn mpr; if (m->data.result != CURLE_OK) { mpr.ret = false; std::string error_msg = minic->getErrorMsg(); if (error_msg.length()) mpr.error_msg = error_msg; else mpr.error_msg = std::string(curl_easy_strerror(m->data.result)); } else { long response_code; curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code); if ((response_code < 200) || (response_code > 299)) { mpr.error_msg = std::string("Received non-2XX response from webserver: ") + std::to_string(response_code); mpr.ret = false; } else { mpr.ret = true; mpr.result_data = minic->getPostResult(); } } mpr.id = minic->getID(); post_ret.emplace_back(std::move(mpr)); } } if (!numfds) { // Avoid busy looping when there is nothing to do // The idea is to busy loop the first couple of times // and then backoff exponentially until a max wait is reached // Inspired by similar code in curl_easy_perform if (wait_time_ms.count() <= 10) { without_fds++; if (without_fds > 2) { int sleep_ms = without_fds < 10 ? (1 << (without_fds - 1)) : 1000; struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = sleep_ms*1000000; nanosleep(&ts, nullptr); } } else { /* it wasn't "instant", restart counter */ without_fds = 0; } } else { /* got file descriptor, restart counter */ without_fds = 0; } } else { // curl_multi_wait failed errlog("curl_multi_wait failed, code %d.n, error=%s", mc, curl_multi_strerror(mc)); break; } } else { // curl_multi_perform failed errlog("curl_multi_perform failed, code %d.n, error=%s", mc, curl_multi_strerror(mc)); break; } } while (still_running); } finishPost(); return post_ret; } // we don't destroy the multi-handle as reusing has benefits like caching, // connection pools etc. void MiniCurlMulti::finishPost() { for (auto i = d_ccs.begin(); i != d_current; ++i) { curl_multi_remove_handle(getCURLPtr(d_mcurl), i->getCurlHandle()); } // Reset the Easy curl handle iterator d_current = d_ccs.begin(); } weakforced-2.10.2/common/minicurl.hh000066400000000000000000000120611461473602600173520ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include #include #include #if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x073200 /* we need this so that 'CURL' is not typedef'd to void, which prevents us from wrapping it in a unique_ptr. Wrapping in a shared_ptr is fine because of type erasure, but it is a bit wasteful. */ #define CURL_STRICTER 1 #endif #include // turns out 'CURL' is currently typedef for void which means we can't easily forward declare it using MiniCurlHeaders = std::map; class MiniCurl { public: MiniCurl(); virtual ~MiniCurl(); MiniCurl& operator=(const MiniCurl&) = delete; MiniCurl& operator=(const MiniCurl&&) = delete; void setURLData(const std::string& url, const MiniCurlHeaders& headers); std::string getURL(const std::string& url, const MiniCurlHeaders& headers); template void setCurlOption(int option, T optval) { if (d_curl) { #ifdef CURL_STRICTER (void) curl_easy_setopt(d_curl.get(), static_cast(option), optval); #else (void) curl_easy_setopt(d_curl, static_cast(option), optval); #endif } } virtual void setTimeout(uint64_t timeout_secs); void setPostData(const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers); std::string getPostResult() { return d_data; } bool postURL(const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers, std::string& error_msg); // This version returns the POST result in post_res bool postURL(const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers, std::string& post_res, std::string& error_msg); CURL* getCurlHandle(); unsigned int getID() { return d_id; } std::string getErrorMsg() { return std::string(d_error_buf); } void setID(unsigned int id) { d_id = id; } protected: void setCurlOpts(); void setCurlHeaders(const MiniCurlHeaders& headers); void clearCurlHeaders(); bool initCurlGlobal(); bool is2xx(const int& code) const { return code/100 == 2; } private: #ifdef CURL_STRICTER std::unique_ptr d_curl{nullptr, curl_easy_cleanup}; std::unique_ptr d_header_list{nullptr, curl_slist_free_all}; #else CURL *d_curl{}; struct curl_slist* d_header_list{}; #endif static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata); static size_t read_callback(char *buffer, size_t size, size_t nitems, void *userdata); std::string d_data; std::istringstream d_post_body; char d_error_buf[CURL_ERROR_SIZE]; unsigned int d_id; }; const unsigned int numMultiCurlConnections=10; struct mcmPostReturn { unsigned int id; bool ret; std::string result_data; std::string error_msg; }; class MiniCurlMulti : public MiniCurl { public: MiniCurlMulti(); MiniCurlMulti(size_t num_connections); ~MiniCurlMulti(); unsigned int getNumConnections() { return d_ccs.size(); } bool addPost(unsigned id, const std::string& url, const std::string& post_body, const MiniCurlHeaders& headers); const std::vector runPost(); void setTimeout(uint64_t timeout_secs); template void setMCurlOption(int option, T optval) { setCurlOption(option, optval); for (auto& i : d_ccs) { i.setCurlOption(option, optval); } } protected: void initMCurl(); void finishPost(); std::vector::iterator findMiniCurl(CURL *curl) { for (auto i = d_ccs.begin(); i != d_ccs.end(); ++i) if (i->getCurlHandle() == curl) return i; return d_ccs.end(); } private: std::vector::iterator d_current; std::vector d_ccs; #ifdef CURL_STRICTER std::unique_ptr d_mcurl{nullptr, curl_multi_cleanup}; #else CURLM* d_mcurl = nullptr; #endif }; struct curlTLSOptions { bool verifyPeer = true; bool verifyHost = true; std::string caCertBundleFile; std::string clientCertFile; std::string clientKeyFile; };weakforced-2.10.2/common/misc.cc000066400000000000000000000464261461473602600164650ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include "misc.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iputils.hh" bool g_singleThreaded; int writen2(int fd, const void *buf, size_t count) { const char *ptr = (char*)buf; const char *eptr = ptr + count; while(ptr != eptr) { int res = ::write(fd, ptr, eptr - ptr); if(res < 0) { if (errno == EAGAIN) throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN"); else unixDie("failed in writen2"); } else if (res == 0) throw std::runtime_error("could not write all bytes, got eof in writen2"); ptr += res; } return count; } int readn2(int fd, void* buffer, unsigned int len) { unsigned int pos=0; for(;;) { int res = read(fd, (char*)buffer + pos, len - pos); if(res == 0) throw runtime_error("EOF while writing message"); if(res < 0) { if (errno == EAGAIN) throw std::runtime_error("used readn2 on non-blocking socket, got EAGAIN"); else unixDie("failed in readn2"); } pos+=res; if(pos == len) break; } return len; } string nowTime() { time_t now=time(0); string t=ctime(&now); boost::trim_right(t); return t; } uint16_t getShort(const unsigned char *p) { return p[0] * 256 + p[1]; } uint16_t getShort(const char *p) { return getShort((const unsigned char *)p); } uint32_t getLong(const unsigned char* p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } uint32_t getLong(const char* p) { return getLong((unsigned char *)p); } bool ciEqual(const string& a, const string& b) { if(a.size()!=b.size()) return false; string::size_type pos=0, epos=a.size(); for(;pos < epos; ++pos) if(dns_tolower(a[pos])!=dns_tolower(b[pos])) return false; return true; } static void parseService4(const string &descr, ServiceTuple &st) { vectorparts; stringtok(parts,descr,":"); if(parts.empty()) throw std::runtime_error("Unable to parse '"+descr+"' as a service"); st.host=parts[0]; if(parts.size()>1) st.port=atoi(parts[1].c_str()); } static void parseService6(const string &descr, ServiceTuple &st) { string::size_type pos=descr.find(']'); if(pos == string::npos) throw std::runtime_error("Unable to parse '"+descr+"' as an IPv6 service"); st.host=descr.substr(1, pos-1); if(pos + 2 < descr.length()) st.port=atoi(descr.c_str() + pos +2); } void parseService(const string &descr, ServiceTuple &st) { if(descr.empty()) throw std::runtime_error("Unable to parse '"+descr+"' as a service"); vector parts; stringtok(parts, descr, ":"); if(descr[0]=='[') { parseService6(descr, st); } else if(descr[0]==':' || parts.size() > 2 || descr.find("::") != string::npos) { st.host=descr; } else { parseService4(descr, st); } } // returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set int waitForData(int fd, int seconds, int useconds) { return waitForRWData(fd, true, seconds, useconds); } int waitForRWData(int fd, bool waitForRead, int seconds, int useconds, bool* error, bool* disconnected) { int ret; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = fd; if(waitForRead) pfd.events=POLLIN; else pfd.events=POLLOUT; ret = poll(&pfd, 1, seconds * 1000 + useconds/1000); if ( ret == -1 ) errno = ETIMEDOUT; // ??? else if (ret > 0) { if (error && (pfd.revents & POLLERR)) { *error = true; } if (disconnected && (pfd.revents & POLLHUP)) { *disconnected = true; } } return ret; } // returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int*fd) { int ret; struct pollfd pfds[2]; memset(&pfds[0], 0, 2*sizeof(struct pollfd)); pfds[0].fd = fd1; pfds[1].fd = fd2; pfds[0].events= pfds[1].events = POLLIN; int nsocks = 1 + (fd2 >= 0); // fd2 can optionally be -1 if(seconds >= 0) ret = poll(pfds, nsocks, seconds * 1000 + useconds/1000); else ret = poll(pfds, nsocks, -1); if(!ret || ret < 0) return ret; if((pfds[0].revents & POLLIN) && !(pfds[1].revents & POLLIN)) *fd = pfds[0].fd; else if((pfds[1].revents & POLLIN) && !(pfds[0].revents & POLLIN)) *fd = pfds[1].fd; else if(ret == 2) { *fd = pfds[random()%2].fd; } else *fd = -1; // should never happen return 1; } string humanTime(time_t t) { char ret[256]; struct tm tm; localtime_r(&t, &tm); strftime(ret, sizeof(ret)-1, "%c", &tm); // %h:%M %Y-%m-%d return ret; } string humanDuration(time_t passed) { ostringstream ret; if(passed<60) ret<> 24)&0xff, (val >> 16)&0xff, (val >> 8)&0xff, (val )&0xff); return tmp; } string makeHexDump(const string& str) { char tmp[5]; string ret; ret.reserve((int)(str.size()*2.2)); for(string::size_type n=0;n 1000000) { ++tv.tv_sec; tv.tv_usec-=1000000; } else if(tv.tv_usec < 0) { --tv.tv_sec; tv.tv_usec+=1000000; } } const struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs) { struct timeval ret; ret.tv_sec=lhs.tv_sec + rhs.tv_sec; ret.tv_usec=lhs.tv_usec + rhs.tv_usec; normalizeTV(ret); return ret; } const struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs) { struct timeval ret; ret.tv_sec=lhs.tv_sec - rhs.tv_sec; ret.tv_usec=lhs.tv_usec - rhs.tv_usec; normalizeTV(ret); return ret; } pair splitField(const string& inp, char sepa) { pair ret; string::size_type cpos=inp.find(sepa); if(cpos==string::npos) ret.first=inp; else { ret.first=inp.substr(0, cpos); ret.second=inp.substr(cpos+1); } return ret; } int logFacilityToLOG(unsigned int facility) { switch(facility) { case 0: return LOG_LOCAL0; case 1: return(LOG_LOCAL1); case 2: return(LOG_LOCAL2); case 3: return(LOG_LOCAL3); case 4: return(LOG_LOCAL4); case 5: return(LOG_LOCAL5); case 6: return(LOG_LOCAL6); case 7: return(LOG_LOCAL7); default: return -1; } } string stripDot(const string& dom) { if(dom.empty()) return dom; if(dom[dom.size()-1]!='.') return dom; return dom.substr(0,dom.size()-1); } string labelReverse(const std::string& qname) { if(qname.empty()) return qname; bool dotName = qname.find('.') != string::npos; vector labels; stringtok(labels, qname, ". "); if(labels.size()==1) return qname; string ret; // vv const_reverse_iter http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11729 for(vector::reverse_iterator iter = labels.rbegin(); iter != labels.rend(); ++iter) { if(iter != labels.rbegin()) ret.append(1, dotName ? ' ' : '.'); ret+=*iter; } return ret; } // do NOT feed trailing dots! // www.powerdns.com, powerdns.com -> www string makeRelative(const std::string& fqdn, const std::string& zone) { if(zone.empty()) return fqdn; if(fqdn != zone) return fqdn.substr(0, fqdn.size() - zone.length() - 1); // strip domain name return ""; } string dotConcat(const std::string& a, const std::string &b) { if(a.empty() || b.empty()) return a+b; else return a+"."+b; } int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret) { if(addr.empty()) return -1; string ourAddr(addr); int port = -1; if(addr[0]=='[') { // [::]:53 style address string::size_type pos = addr.find(']'); if(pos == string::npos || pos + 2 > addr.size() || addr[pos+1]!=':') return -1; ourAddr.assign(addr.c_str() + 1, pos-1); port = atoi(addr.c_str()+pos+2); } ret->sin6_scope_id=0; ret->sin6_family=AF_INET6; if(inet_pton(AF_INET6, ourAddr.c_str(), (void*)&ret->sin6_addr) != 1) { struct addrinfo* res; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(ourAddr.c_str(), 0, &hints, &res)) { // this is correct return -1; } memcpy(ret, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } if(port >= 0) ret->sin6_port = htons(port); return 0; } int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret) { if(str.empty()) { return -1; } struct in_addr inp; string::size_type pos = str.find(':'); if(pos == string::npos) { // no port specified, not touching the port if(inet_aton(str.c_str(), &inp)) { ret->sin_addr.s_addr=inp.s_addr; return 0; } return -1; } if(!*(str.c_str() + pos + 1)) // trailing : return -1; char *eptr = (char*)str.c_str() + str.size(); int port = strtol(str.c_str() + pos + 1, &eptr, 10); if(*eptr) return -1; ret->sin_port = htons(port); if(inet_aton(str.substr(0, pos).c_str(), &inp)) { ret->sin_addr.s_addr=inp.s_addr; return 0; } return -1; } int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret) { if (path.empty()) return -1; memset(ret, 0, sizeof(struct sockaddr_un)); ret->sun_family = AF_UNIX; if (path.length() >= sizeof(ret->sun_path)) return -1; path.copy(ret->sun_path, sizeof(ret->sun_path), 0); return 0; } //! read a line of text from a FILE* to a std::string, returns false on 'no data' bool stringfgets(FILE* fp, std::string& line) { char buffer[1024]; line.clear(); do { if(!fgets(buffer, sizeof(buffer), fp)) return !line.empty(); line.append(buffer); } while(!strchr(buffer, '\n')); return true; } bool readFileIfThere(const char* fname, std::string* line) { line->clear(); FILE* fp = fopen(fname, "r"); if(!fp) return false; stringfgets(fp, *line); fclose(fp); return true; } Regex::Regex(const string &expr) { if(regcomp(&d_preg, expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED)) throw std::runtime_error("Regular expression did not compile"); } void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* source, int itfIndex) { struct cmsghdr *cmsg = NULL; if(source->sin4.sin_family == AF_INET6) { #ifdef IPV6_PKTINFO struct in6_pktinfo *pkt; msgh->msg_control = cmsgbuf; msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt)); cmsg = CMSG_FIRSTHDR(msgh); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt)); pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg); memset(pkt, 0, sizeof(*pkt)); pkt->ipi6_addr = source->sin6.sin6_addr; pkt->ipi6_ifindex = itfIndex; #endif } else { #ifdef IP_PKTINFO struct in_pktinfo *pkt; msgh->msg_control = cmsgbuf; msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt)); cmsg = CMSG_FIRSTHDR(msgh); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt)); pkt = (struct in_pktinfo *) CMSG_DATA(cmsg); memset(pkt, 0, sizeof(*pkt)); pkt->ipi_spec_dst = source->sin4.sin_addr; pkt->ipi_ifindex = itfIndex; #endif #ifdef IP_SENDSRCADDR struct in_addr *in; msgh->msg_control = cmsgbuf; msgh->msg_controllen = CMSG_SPACE(sizeof(*in)); cmsg = CMSG_FIRSTHDR(msgh); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_SENDSRCADDR; cmsg->cmsg_len = CMSG_LEN(sizeof(*in)); in = (struct in_addr *) CMSG_DATA(cmsg); *in = source->sin4.sin_addr; #endif } } unsigned int getFilenumLimit(bool hardOrSoft) { struct rlimit rlim; if(getrlimit(RLIMIT_NOFILE, &rlim) < 0) unixDie("Requesting number of available file descriptors"); return hardOrSoft ? rlim.rlim_max : rlim.rlim_cur; } void setFilenumLimit(unsigned int lim) { struct rlimit rlim; if(getrlimit(RLIMIT_NOFILE, &rlim) < 0) unixDie("Requesting number of available file descriptors"); rlim.rlim_cur=lim; if(setrlimit(RLIMIT_NOFILE, &rlim) < 0) unixDie("Setting number of available file descriptors"); } #define burtlemix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } uint32_t burtle(const unsigned char* k, uint32_t length, uint32_t initval) { uint32_t a,b,c,len; /* Set up the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = initval; /* the previous hash value */ /*---------------------------------------- handle most of the key */ while (len >= 12) { a += (k[0] +((uint32_t)k[1]<<8) +((uint32_t)k[2]<<16) +((uint32_t)k[3]<<24)); b += (k[4] +((uint32_t)k[5]<<8) +((uint32_t)k[6]<<16) +((uint32_t)k[7]<<24)); c += (k[8] +((uint32_t)k[9]<<8) +((uint32_t)k[10]<<16)+((uint32_t)k[11]<<24)); burtlemix(a,b,c); k += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch(len) { /* all the case statements fall through */ case 11: c+=((uint32_t)k[10]<<24); case 10: c+=((uint32_t)k[9]<<16); case 9 : c+=((uint32_t)k[8]<<8); /* the first byte of c is reserved for the length */ case 8 : b+=((uint32_t)k[7]<<24); case 7 : b+=((uint32_t)k[6]<<16); case 6 : b+=((uint32_t)k[5]<<8); case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3]<<24); case 3 : a+=((uint32_t)k[2]<<16); case 2 : a+=((uint32_t)k[1]<<8); case 1 : a+=k[0]; /* case 0: nothing left to add */ } burtlemix(a,b,c); /*-------------------------------------------- report the result */ return c; } void setSocketTimestamps(int fd) { #ifdef SO_TIMESTAMP int on=1; if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (char*)&on, sizeof(on)) < 0 ) ; // L< UINT_MAX) { errno = ERANGE; return UINT_MAX; } return val; #endif } bool setNonBlocking(int sock) { int flags=fcntl(sock,F_GETFL,0); if(flags<0 || fcntl(sock, F_SETFL,flags|O_NONBLOCK) <0) return false; return true; } bool setBlocking(int sock) { int flags=fcntl(sock,F_GETFL,0); if(flags<0 || fcntl(sock, F_SETFL,flags&(~O_NONBLOCK)) <0) return false; return true; } // Closes a socket. int closesocket( int socket ) { int ret=::close(socket); if(ret < 0 && errno == ECONNRESET) // see ticket 192, odd BSD behaviour return 0; if(ret < 0) throw std::runtime_error("Error closing socket: "+stringerror()); return ret; } bool setCloseOnExec(int sock) { int flags=fcntl(sock,F_GETFD,0); if(flags<0 || fcntl(sock, F_SETFD,flags|FD_CLOEXEC) <0) return false; return true; } unsigned int pdns_stou(const std::string& str, size_t * idx, int base) { if (str.empty()) return 0; // compatibility unsigned long result = std::stoul(str, idx, base); if (result > std::numeric_limits::max()) { throw std::out_of_range("stou"); } return static_cast(result); } std::string getDirectoryPath(const std::string& filename) { boost::filesystem::path my_path(filename); boost::filesystem::path branch_path = my_path.parent_path(); return branch_path.empty() ? "." : branch_path.string(); } std::string getFileFromPath(const std::string& filename) { boost::filesystem::path my_path(filename); return my_path.filename().string(); } weakforced-2.10.2/common/misc.hh000066400000000000000000000336071461473602600164740ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef MISC_HH #define MISC_HH #include #include #include #include #include #include #include #include #include #include #include using namespace ::boost::multi_index; #include #include #include #include #include #include #include #include #include #include #include "namespaces.hh" string humanDuration(time_t passed); void stripLine(string &line); string getHostname(); string urlEncode(const string &text); int waitForData(int fd, int seconds, int useconds=0); int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int* fd); int waitForRWData(int fd, bool waitForRead, int seconds, int useconds, bool* error=nullptr, bool* disconnected=nullptr); uint16_t getShort(const unsigned char *p); uint16_t getShort(const char *p); uint32_t getLong(const unsigned char *p); uint32_t getLong(const char *p); uint32_t pdns_strtoui(const char *nptr, char **endptr, int base); struct ServiceTuple { string host; uint16_t port; }; void parseService(const string &descr, ServiceTuple &st); string humanTime(time_t t); template void stringtok (Container &container, string const &in, const char * const delimiters = " \t\n") { const string::size_type len = in.length(); string::size_type i = 0; while (i bool rfc1982LessThan(T a, T b) { return ((signed)(a - b)) < 0; } // fills container with ranges, so {posbegin,posend} template void vstringtok (Container &container, string const &in, const char * const delimiters = " \t\n") { const string::size_type len = in.length(); string::size_type i = 0; while (i='A' && c<='Z') c+='a'-'A'; return c; } inline const string toLower(const string &upper) { string reply(upper); for(unsigned int i = 0; i < reply.length(); i++) { char c = dns_tolower(upper[i]); if (c != upper[i]) reply[i] = c; } return reply; } inline const string toLowerCanonic(const string &upper) { string reply(upper); if(!upper.empty()) { unsigned int i, limit= ( unsigned int ) reply.length(); for(i = 0; i < limit ; i++) { char c = dns_tolower(upper[i]); if (c != upper[i]) reply[i] = c; } if(upper[i-1]=='.') reply.resize(i-1); } return reply; } // Make s uppercase: inline string toUpper( const string& s ) { string r(s); for( unsigned int i = 0; i < s.length(); i++ ) { r[i] = toupper( r[i] ); } return r; } inline double getTime() { struct timeval now; gettimeofday(&now,0); return now.tv_sec+now.tv_usec/1000000.0; } inline void unixDie(const string &why) { throw runtime_error(why+": "+strerror(errno)); } string makeHexDump(const string& str); void normalizeTV(struct timeval& tv); const struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs); const struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs); inline float makeFloat(const struct timeval& tv) { return tv.tv_sec + tv.tv_usec/1000000.0f; } inline bool operator<(const struct timeval& lhs, const struct timeval& rhs) { return make_pair(lhs.tv_sec, lhs.tv_usec) < make_pair(rhs.tv_sec, rhs.tv_usec); } inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b) __attribute__((pure)); inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b) { const unsigned char *aPtr = (const unsigned char*)a.c_str(), *bPtr = (const unsigned char*)b.c_str(); const unsigned char *aEptr = aPtr + a.length(), *bEptr = bPtr + b.length(); while(aPtr != aEptr && bPtr != bEptr) { if ((*aPtr != *bPtr) && (dns_tolower(*aPtr) - dns_tolower(*bPtr))) return (dns_tolower(*aPtr) - dns_tolower(*bPtr)) < 0; aPtr++; bPtr++; } if(aPtr == aEptr && bPtr == bEptr) // strings are equal (in length) return false; return aPtr == aEptr; // true if first string was shorter } inline bool pdns_iequals(const std::string& a, const std::string& b) __attribute__((pure)); inline bool pdns_iequals(const std::string& a, const std::string& b) { if (a.length() != b.length()) return false; const char *aPtr = a.c_str(), *bPtr = b.c_str(); const char *aEptr = aPtr + a.length(); while(aPtr != aEptr) { if((*aPtr != *bPtr) && (dns_tolower(*aPtr) != dns_tolower(*bPtr))) return false; aPtr++; bPtr++; } return true; } inline bool pdns_iequals_ch(const char a, const char b) __attribute__((pure)); inline bool pdns_iequals_ch(const char a, const char b) { if ((a != b) && (dns_tolower(a) != dns_tolower(b))) return false; return true; } // lifted from boost, with thanks class AtomicCounter { public: typedef unsigned long native_t; explicit AtomicCounter( native_t v = 0) : value_( v ) {} native_t operator++() { return atomic_exchange_and_add( &value_, +1 ) + 1; } native_t operator++(int) { return atomic_exchange_and_add( &value_, +1 ); } native_t operator+=(native_t val) { return atomic_exchange_and_add( &value_, val ); } native_t operator-=(native_t val) { return atomic_exchange_and_add( &value_, -val ); } native_t operator--() { return atomic_exchange_and_add( &value_, -1 ) - 1; } operator native_t() const { return atomic_exchange_and_add( &value_, 0); } AtomicCounter(AtomicCounter const &rhs) : value_(rhs) { } private: mutable native_t value_; // the below is necessary because __sync_fetch_and_add is not universally available on i386.. I 3> RHEL5. #if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) static native_t atomic_exchange_and_add( native_t * pw, native_t dv ) { // int r = *pw; // *pw += dv; // return r; native_t r; __asm__ __volatile__ ( "lock\n\t" "xadd %1, %0": "+m"( *pw ), "=r"( r ): // outputs (%0, %1) "1"( dv ): // inputs (%2 == %1) "memory", "cc" // clobbers ); return r; } #else static native_t atomic_exchange_and_add( native_t * pw, native_t dv ) { return __sync_fetch_and_add(pw, dv); } #endif }; // FIXME400 this should probably go? struct CIStringCompare { bool operator()(const string& a, const string& b) const { return pdns_ilexicographical_compare(a, b); } }; struct CIStringComparePOSIX { bool operator() (const std::string& lhs, const std::string& rhs) const { const std::locale &loc = std::locale("POSIX"); auto lhsIter = lhs.begin(); auto rhsIter = rhs.begin(); while (lhsIter != lhs.end()) { if (rhsIter == rhs.end() || std::tolower(*rhsIter,loc) < std::tolower(*lhsIter,loc)) { return false; } if (std::tolower(*lhsIter,loc) < std::tolower(*rhsIter,loc)) { return true; } ++lhsIter;++rhsIter; } return rhsIter != rhs.end(); } }; struct CIStringPairCompare { bool operator()(const pair& a, const pair& b) const { if(pdns_ilexicographical_compare(a.first, b.first)) return true; if(pdns_ilexicographical_compare(b.first, a.first)) return false; return a.second < b.second; } }; inline size_t pdns_ci_find(const string& haystack, const string& needle) { string::const_iterator it = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), pdns_iequals_ch); if (it == haystack.end()) { // not found return string::npos; } else { return it - haystack.begin(); } } pair splitField(const string& inp, char sepa); inline bool isCanonical(const string& dom) { if(dom.empty()) return false; return dom[dom.size()-1]=='.'; } inline string toCanonic(const string& zone, const string& domain) { if(domain.length()==1 && domain[0]=='@') return zone; if(isCanonical(domain)) return domain; string ret=domain; ret.append(1,'.'); if(!zone.empty() && zone[0]!='.') ret.append(zone); return ret; } string stripDot(const string& dom); void seedRandom(const string& source); string makeRelative(const std::string& fqdn, const std::string& zone); string labelReverse(const std::string& qname); std::string dotConcat(const std::string& a, const std::string &b); int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret); int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret); int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret); bool stringfgets(FILE* fp, std::string& line); template std::pair replacing_insert(Index& i,const typename Index::value_type& x) { std::pair res=i.insert(x); if(!res.second)res.second=i.replace(res.first,x); return res; } /** very small regex wrapper */ class Regex { public: /** constructor that accepts the expression to regex */ Regex(const string &expr); ~Regex() { regfree(&d_preg); } /** call this to find out if 'line' matches your expression */ bool match(const string &line) { return regexec(&d_preg,line.c_str(),0,0,0)==0; } private: regex_t d_preg; }; union ComboAddress; /* itfIndex is an interface index, as returned by if_nametoindex(). 0 means default. */ void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* source, int itfIndex); unsigned int getFilenumLimit(bool hardOrSoft=0); void setFilenumLimit(unsigned int lim); bool readFileIfThere(const char* fname, std::string* line); uint32_t burtle(const unsigned char* k, uint32_t length, uint32_t init); void setSocketTimestamps(int fd); //! Sets the socket into blocking mode. bool setBlocking( int sock ); //! Sets the socket into non-blocking mode. bool setNonBlocking( int sock ); int closesocket(int fd); bool setCloseOnExec(int sock); unsigned int pdns_stou(const std::string& str, size_t * idx = 0, int base = 10); std::string getDirectoryPath(const std::string& filename); std::string getFileFromPath(const std::string& filename); typedef std::vector> KeyValVector; #endif weakforced-2.10.2/common/namespaces.hh000066400000000000000000000044561461473602600176600ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PDNS_NAMESPACES_HH #define PDNS_NAMESPACES_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using std::vector; using std::map; using std::pair; using std::make_pair; using std::runtime_error; using std::ostringstream; using std::set; using std::deque; using std::cerr; using std::cout; using std::clog; using std::endl; using std::ifstream; using std::ofstream; using std::ostream; using std::min; // these are a bit scary, everybody uses 'min' using std::max; namespace pdns { typedef std::string string; }; typedef pdns::string string; using boost::lexical_cast; using boost::tie; using std::shared_ptr; using boost::shared_array; using boost::scoped_array; using boost::tuple; using boost::format; using boost::make_tuple; using boost::optional; using boost::any_cast; using boost::any; using boost::function; using boost::trim; using boost::trim_left; using boost::trim_right; using boost::is_any_of; using boost::trim_right_copy_if; using boost::equals; using boost::ends_with; using boost::iends_with; #endif weakforced-2.10.2/common/pdnsexception.hh000066400000000000000000000017511461473602600204170ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "wforce_exception.hh" weakforced-2.10.2/common/perf-stats.cc000066400000000000000000000151131461473602600176070ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include "perf-stats.hh" #include "twmap.hh" #include "dolog.hh" #define STATS_NUM_WINDOWS 20 #define STATS_WINDOW_SIZE 15 using namespace PerfStats; TWStatsDB g_perfStats("perfStats", STATS_WINDOW_SIZE, STATS_NUM_WINDOWS); const static FieldMap fm = { { wtw_0_1_str, "int" }, { wtw_1_10_str, "int" }, { wtw_10_100_str, "int" }, { wtw_100_1000_str, "int" }, { wtw_slow_str, "int" }, { wtr_0_1_str, "int" }, { wtr_1_10_str, "int" }, { wtr_10_100_str, "int" }, { wtr_100_1000_str, "int" }, { wtr_slow_str, "int" }, { command_str, "countmin"}, { custom_str, "countmin"} }; std::map lookupPerfStat = { { WorkerThreadWait_0_1, wtw_0_1_str }, { WorkerThreadWait_1_10, wtw_1_10_str }, { WorkerThreadWait_10_100, wtw_10_100_str }, { WorkerThreadWait_100_1000, wtw_100_1000_str}, { WorkerThreadWait_Slow, wtw_slow_str }, { WorkerThreadRun_0_1, wtr_0_1_str }, { WorkerThreadRun_1_10, wtr_1_10_str }, { WorkerThreadRun_10_100, wtr_10_100_str }, { WorkerThreadRun_100_1000, wtr_100_1000_str }, { WorkerThreadRun_Slow, wtr_slow_str } }; void initPerfStats() { g_perfStats.setFields(fm); } void incStat(PerfStat stat) { auto i = lookupPerfStat.find(stat); if (i != lookupPerfStat.end()) g_perfStats.add(1, i->second, 1); } void addWTWStat(unsigned int num_ms) { if (num_ms <= 1) incStat(WorkerThreadWait_0_1); else if (num_ms <= 10) incStat(WorkerThreadWait_1_10); else if (num_ms <= 100) incStat(WorkerThreadWait_10_100); else if (num_ms <= 1000) incStat(WorkerThreadWait_100_1000); else incStat(WorkerThreadWait_Slow); } void addWTRStat(unsigned int num_ms) { if (num_ms <= 1) incStat(WorkerThreadRun_0_1); else if (num_ms <= 10) incStat(WorkerThreadRun_1_10); else if (num_ms <= 100) incStat(WorkerThreadRun_10_100); else if (num_ms <= 1000) incStat(WorkerThreadRun_100_1000); else incStat(WorkerThreadRun_Slow); } int getStat(PerfStat stat) { auto i = lookupPerfStat.find(stat); if (i != lookupPerfStat.end()) return g_perfStats.get(1, i->second); else return 0; } static std::set command_stats; void addCommandStat(const std::string& command_name) { command_stats.insert(command_name); } void incCommandStat(const std::string& command_name) { g_perfStats.add(1, "Command", command_name, 1); } int getCommandStat(const std::string& command_name) { return g_perfStats.get(1, "Command", command_name); } static std::set custom_stats; void addCustomStat(const std::string& custom_name) { custom_stats.insert(custom_name); } void incCustomStat(const std::string& custom_name) { g_perfStats.add(1, "Custom", custom_name, 1); } int getCustomStat(const std::string& custom_name) { return g_perfStats.get(1, "Custom", custom_name); } void statsReportingThread() { setThreadName("wf/perf-stats"); int interval = STATS_WINDOW_SIZE*STATS_NUM_WINDOWS; for (;;) { std::stringstream ss; sleep(interval); ss << "perf stats last " << interval << " secs: "; for (auto i=lookupPerfStat.begin(); i!=lookupPerfStat.end(); ++i) { ss << i->second << "=" << getStat(i->first) << " "; } noticelog("%s", ss.str()); if (command_stats.size() != 0) { ss.str(std::string()); ss.clear(); ss << "command stats last " << interval << " secs: "; for (const auto& i : command_stats) { ss << i << "=" << getCommandStat(i) << " "; } noticelog("%s", ss.str()); } if (custom_stats.size() != 0) { ss.str(std::string()); ss.clear(); ss << "custom stats last " << interval << " secs: "; for (const auto& i : custom_stats) { ss << i << "=" << getCustomStat(i) << " "; } noticelog("%s", ss.str()); } } } void startStatsThread() { infolog("Starting stats reporting thread"); initPerfStats(); thread t(statsReportingThread); t.detach(); } std::string getPerfStatsString() { std::stringstream ss; for (auto i=lookupPerfStat.begin(); i!=lookupPerfStat.end(); ++i) { ss << i->second << "=" << getStat(i->first) << "\n"; } return ss.str(); } std::string getCommandStatsString() { std::ostringstream ss; for (const auto& i : command_stats) { ss << i << "=" << getCommandStat(i) << endl; } return ss.str(); } std::string getCustomStatsString() { std::ostringstream ss; for (const auto& i : custom_stats) { ss << i << "=" << getCustomStat(i) << endl; } return ss.str(); } using namespace json11; Json perfStatsToJson() { Json::object jattrs; for (auto i=lookupPerfStat.begin(); i!=lookupPerfStat.end(); ++i) { jattrs.insert(std::make_pair(i->second, getStat(i->first))); } return jattrs; } Json commandStatsToJson() { Json::object jattrs; for (const auto& i : command_stats) { jattrs.insert(std::make_pair(i, getCommandStat(i))); } return jattrs; } Json customStatsToJson() { Json::object jattrs; for (const auto& i : custom_stats) { jattrs.insert(std::make_pair(i, getCustomStat(i))); } return jattrs; } weakforced-2.10.2/common/perf-stats.hh000066400000000000000000000051531461473602600176240ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include "json11.hpp" namespace PerfStats { enum PerfStat { WorkerThreadWait_0_1=0, WorkerThreadWait_1_10=2, WorkerThreadWait_10_100=3, WorkerThreadWait_100_1000=4, WorkerThreadWait_Slow=5, WorkerThreadRun_0_1=6, WorkerThreadRun_1_10=7, WorkerThreadRun_10_100=8, WorkerThreadRun_100_1000=9, WorkerThreadRun_Slow=10 }; const std::string wtw_0_1_str = "WTW_0_1"; const std::string wtw_1_10_str = "WTW_1_10"; const std::string wtw_10_100_str = "WTW_10_100"; const std::string wtw_100_1000_str = "WTW_100_1000"; const std::string wtw_slow_str = "WTW_Slow"; const std::string wtr_0_1_str = "WTR_0_1"; const std::string wtr_1_10_str = "WTR_1_10"; const std::string wtr_10_100_str = "WTR_10_100"; const std::string wtr_100_1000_str = "WTR_100_1000"; const std::string wtr_slow_str = "WTR_Slow"; const std::string command_str = "Command"; const std::string custom_str = "Custom"; }; void initPerfStats(); void addWTWStat(unsigned int num_ms); // number of milliseconds void addWTRStat(unsigned int num_ms); // number of milliseconds void startStatsThread(); std::string getPerfStatsString(); // return perf stats in a string json11::Json perfStatsToJson(); // return perf stats as a json object void addCommandStat(const std::string& command_name); void incCommandStat(const std::string& command_name); int getCommandStat(const std::string& command_name); void addCustomStat(const std::string& custom_name); void incCustomStat(const std::string& custom_name); int getCustomStat(const std::string& custom_name); json11::Json commandStatsToJson(); json11::Json customStatsToJson(); std::string getCommandStatsString(); std::string getCustomStatsString(); weakforced-2.10.2/common/prometheus.cc000066400000000000000000000134701461473602600177160ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "prometheus.hh" static std::shared_ptr prom_metrics; void initPrometheusMetrics(std::shared_ptr pmp) { prom_metrics = pmp; } void PrometheusMetrics::addCommandMetric(const std::string& name) { Counter* c = &(commands_family->Add({{"cmd", name}})); commands_metrics.insert(std::make_pair(name, c)); } void PrometheusMetrics::incCommandMetric(const std::string& name) { auto i = commands_metrics.find(name); if (i != commands_metrics.end()) { i->second->Increment(); } } void PrometheusMetrics::addCustomMetric(const std::string& name) { Counter* c = &(custom_family->Add({{"metric", name}})); custom_metrics.insert(std::make_pair(name, c)); } void PrometheusMetrics::incCustomMetric(const std::string& name) { auto i = custom_metrics.find(name); if (i != custom_metrics.end()) { i->second->Increment(); } } void PrometheusMetrics::addDNSResolverMetric(const std::string& name) { Counter* c = &(dns_query_family->Add({{"resolver", name}})); dns_query_metrics.insert(std::make_pair(name, c)); Histogram* h = &(dns_query_latency_family->Add({{"resolver", name}}, d_bb)); dns_query_latency_metrics.insert(std::make_pair(name, h)); } void PrometheusMetrics::incDNSResolverMetric(const std::string& name) { auto i = dns_query_metrics.find(name); if (i != dns_query_metrics.end()) { i->second->Increment(); } } void PrometheusMetrics::observeDNSResolverLatency(const std::string& name, float duration) { auto i = dns_query_latency_metrics.find(name); if (i != dns_query_latency_metrics.end()) { i->second->Observe(duration); } } void PrometheusMetrics::addWebhookMetric(unsigned int id, const std::string& url, bool custom) { std::string success_key = std::to_string(id) + "ok"; std::string error_key = std::to_string(id) + "error"; if (!custom) { Counter* c = &(webhook_family->Add({{"id", std::to_string(id)}, {"url", url}, {"status", "ok"}})); webhook_metrics.insert(std::make_pair(success_key, c)); c = &(webhook_family->Add({{"id", std::to_string(id)}, {"url", url}, {"status", "error"}})); webhook_metrics.insert(std::make_pair(error_key, c)); } else { Counter* c = &(custom_webhook_family->Add({{"id", std::to_string(id)}, {"url", url}, {"status", "ok"}})); custom_webhook_metrics.insert(std::make_pair(success_key, c)); c = &(custom_webhook_family->Add({{"id", std::to_string(id)}, {"url", url}, {"status", "error"}})); custom_webhook_metrics.insert(std::make_pair(error_key, c)); } } void PrometheusMetrics::incWebhookMetric(unsigned int id, bool success, bool custom) { std::string key = std::to_string(id) + (success ? "ok" : "error"); if (!custom) { auto i = webhook_metrics.find(key); if (i != webhook_metrics.end()) { i->second->Increment(); } } else { auto i = custom_webhook_metrics.find(key); if (i != custom_webhook_metrics.end()) { i->second->Increment(); } } } void setPrometheusActiveConns(int value) { if (prom_metrics) prom_metrics->setActiveConns(value); } void addPrometheusCommandMetric(const std::string& name) { if (prom_metrics) prom_metrics->addCommandMetric(name); } void incPrometheusCommandMetric(const std::string& name) { if (prom_metrics) prom_metrics->incCommandMetric(name); } void addPrometheusCustomMetric(const std::string& name) { if (prom_metrics) prom_metrics->addCustomMetric(name); } void incPrometheusCustomMetric(const std::string& name) { if (prom_metrics) prom_metrics->incCustomMetric(name); } void observePrometheusWQD(float duration) { if (prom_metrics) prom_metrics->observeWQD(duration); } void observePrometheusWRD(float duration) { if (prom_metrics) prom_metrics->observeWRD(duration); } void addPrometheusDNSResolverMetric(const std::string& name) { if (prom_metrics) { prom_metrics->addDNSResolverMetric(name); } } void incPrometheusDNSResolverMetric(const std::string& name) { if (prom_metrics) { prom_metrics->incDNSResolverMetric(name); } } void observePrometheusDNSResolverLatency(const std::string& name, float duration) { if (prom_metrics) { prom_metrics->observeDNSResolverLatency(name, duration); } } void addPrometheusWebhookMetric(unsigned int id, const std::string& url, bool custom) { if (prom_metrics) { prom_metrics->addWebhookMetric(id, url, custom); } } void incPrometheusWebhookMetric(unsigned int id, bool success, bool custom) { if (prom_metrics) { prom_metrics->incWebhookMetric(id, success, custom); } } void setPrometheusWebQueueSize(int value) { if (prom_metrics) { prom_metrics->setWebQueueSize(value); } } void setPrometheusWebhookQueueSize(int value) { if (prom_metrics) { prom_metrics->setWebhookQueueSize(value); } } std::string serializePrometheusMetrics() { if (prom_metrics) return prom_metrics->serialize(); else return std::string("Prometheus Metrics not initialized"); } weakforced-2.10.2/common/prometheus.hh000066400000000000000000000160321461473602600177250ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "prometheus/registry.h" #include "prometheus/text_serializer.h" #include "prometheus/counter.h" #include "prometheus/gauge.h" #include "prometheus/histogram.h" #include "wforce_ns.hh" #include "wforce_exception.hh" #include using namespace prometheus; class PrometheusMetrics { public: PrometheusMetrics(const std::string& prefix) : d_registry(wforce::make_unique()), d_bb{0.001,0.01,0.1,1}, d_prefix(prefix) { if (d_registry != nullptr) { auto& wqd_family = BuildHistogram() .Name(d_prefix+"_worker_queue_duration_seconds") .Help("How many seconds do worker threads have to wait to run?") .Register(*d_registry); worker_queue_duration = &(wqd_family.Add({}, d_bb)); auto& wrd_family = BuildHistogram() .Name(d_prefix+"_worker_response_duration_seconds") .Help("How many seconds do worker threads take to run?") .Register(*d_registry); worker_response_duration = &(wrd_family.Add({}, d_bb)); auto& active_conn_family = BuildGauge() .Name(d_prefix+"_active_http_connections") .Help("How many active connections to the REST API?") .Register(*d_registry); active_connections = &(active_conn_family.Add({})); auto& web_queue_family = BuildGauge() .Name(d_prefix+"_web_queue_size") .Help("How full is the REST API worker thread queue?") .Register(*d_registry); web_queue_size = &(web_queue_family.Add({})); auto& webhook_queue_family = BuildGauge() .Name(d_prefix+"_webhook_queue_size") .Help("How full is the webhook worker thread queue?") .Register(*d_registry); webhook_queue_size = &(webhook_queue_family.Add({})); commands_family = &(BuildCounter() .Name(d_prefix+"_commands_total") .Help("How many commands have been received?") .Register(*d_registry)); custom_family = &(BuildCounter() .Name(d_prefix+"_custom_stats_total") .Help("Count of custom stats (by type)") .Register(*d_registry)); dns_query_family = &(BuildCounter() .Name(d_prefix+"_dns_queries_total") .Help("How many DNS queries were performed?") .Register(*d_registry)); dns_query_latency_family = &(BuildHistogram() .Name(d_prefix+"_dns_query_response_seconds") .Help("How long do DNS queries take?") .Register(*d_registry)); webhook_family = &(BuildCounter() .Name(d_prefix+"_webhook_events_total") .Help("How many webhook events occurred?") .Register(*d_registry)); custom_webhook_family = &(BuildCounter() .Name(d_prefix+"_custom_webhook_events_total") .Help("How many custom webhook events occurred?") .Register(*d_registry)); } else { throw WforceException("Could not allocate memory for Prometheus Registry"); } } virtual ~PrometheusMetrics() = default; void addCommandMetric(const std::string& name); void incCommandMetric(const std::string& name); void addCustomMetric(const std::string& name); void incCustomMetric(const std::string& name); void observeWQD(float duration) { worker_queue_duration->Observe(duration); } void observeWRD(float duration) { worker_response_duration->Observe(duration); } void setActiveConns(int value) { active_connections->Set(value); } void addDNSResolverMetric(const std::string& name); void incDNSResolverMetric(const std::string& name); void observeDNSResolverLatency(const std::string& name, float duration); void addWebhookMetric(unsigned int id, const std::string& url, bool custom); void incWebhookMetric(unsigned int id, bool success, bool custom); void setWebQueueSize(int value) { web_queue_size->Set(value); } void setWebhookQueueSize(int value) { webhook_queue_size->Set(value); } virtual std::string serialize() { return d_textSerializer.Serialize(d_registry->Collect()); } private: Histogram* worker_queue_duration; Histogram* worker_response_duration; Gauge* active_connections; Gauge* web_queue_size; Gauge* webhook_queue_size; Family* commands_family; std::map commands_metrics; Family* custom_family; std::map custom_metrics; Family* dns_query_family; std::map dns_query_metrics; Family* dns_query_latency_family; std::map dns_query_latency_metrics; Family* webhook_family; std::map webhook_metrics; Family* custom_webhook_family; std::map custom_webhook_metrics; protected: prometheus::TextSerializer d_textSerializer; std::unique_ptr d_registry; Histogram::BucketBoundaries d_bb; std::string d_prefix; }; void initPrometheusMetrics(std::shared_ptr pmp); void setPrometheusActiveConns(int value); void addPrometheusCommandMetric(const std::string& name); void incPrometheusCommandMetric(const std::string& name); void addPrometheusCustomMetric(const std::string& name); void incPrometheusCustomMetric(const std::string& name); void observePrometheusWQD(float duration); void observePrometheusWRD(float duration); void addPrometheusDNSResolverMetric(const std::string& name); void incPrometheusDNSResolverMetric(const std::string& name); void observePrometheusDNSResolverLatency(const std::string& name, float duration); void addPrometheusWebhookMetric(unsigned int id, const std::string& url, bool custom); void incPrometheusWebhookMetric(unsigned int id, bool success, bool custom); void setPrometheusWebQueueSize(int value); void setPrometheusWebhookQueueSize(int value); std::string serializePrometheusMetrics(); weakforced-2.10.2/common/sholder.hh000066400000000000000000000104121461473602600171660ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include /** This is sort of a light-weight RCU idea. Suitable for when you frequently consult some "readonly" state, which infrequently gets changed. One way of dealing with this is fully locking access to the state, but this is rather wasteful. Instead, in the code below, the frequent users of the state get a "readonly" copy of it, which they can consult. On access, we atomically compare if the local copy is still current with the global one. If it isn't we do the lock thing, and create a new local copy. Meanwhile, to upgrade the global state, methods are offered that do appropriate locking and upgrade the 'generation' counter, signaling to the local copies that they need to be refreshed on the next access. Two ways to change the global copy are available: getCopy(), which delivers a deep copy of the current state, followed by setState() modify(), which accepts a (lambda)function that modifies the state NOTE: The actual destruction of the 'old' state happens when the last local state relinquishes its access to the state. "read-only" Sometimes, a 'state' can contain parts that can safely be modified by multiple users, for example, atomic counters. In such cases, it may be useful to explicitly declare such counters as mutable. */ template class GlobalStateHolder; template class LocalStateHolder { public: explicit LocalStateHolder(const GlobalStateHolder* source) : d_source(source) {} const T* operator->() // fast const-only access, but see "read-only" above { if(d_source->getGeneration() != d_generation) { d_source->getState(&d_state, & d_generation); } return d_state.get(); } const T& operator*() // fast const-only access, but see "read-only" above { return *operator->(); } void reset() { d_generation=0; d_state.reset(); } private: std::shared_ptr d_state; unsigned int d_generation{0}; const GlobalStateHolder* d_source; }; template class GlobalStateHolder { public: GlobalStateHolder() : d_state(std::make_shared()) {} LocalStateHolder getLocal() const { return LocalStateHolder(this); } void setState(T state) //!< Safely & slowly change the global state { std::lock_guard l(d_lock); d_state = std::make_shared(T(state)); d_generation++; } T getCopy() const //!< Safely & slowly get a copy of the global state { std::lock_guard l(d_lock); return *d_state; } //! Safely & slowly modify the global state template void modify(F act) { std::lock_guard l(d_lock); auto state=*d_state; // and yes, these three steps are necessary, can't ever modify state in place, even when locked! act(state); d_state = std::make_shared(T(state)); ++d_generation; } typedef T value_type; private: unsigned int getGeneration() const { return d_generation; } void getState(std::shared_ptr* state, unsigned int* generation) const { std::lock_guard l(d_lock); *state=d_state; *generation = d_generation; } friend class LocalStateHolder; mutable std::mutex d_lock; std::shared_ptr d_state; std::atomic d_generation{1}; }; weakforced-2.10.2/common/sodcrypto.cc000066400000000000000000000305311461473602600175460ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "namespaces.hh" #include "misc.hh" #include "base64.hh" #include "sodcrypto.hh" #ifdef HAVE_LIBSODIUM string newKey() { unsigned char key[crypto_secretbox_KEYBYTES]; randombytes_buf(key, sizeof key); return "\""+Base64Encode(string((char*)key, sizeof key))+"\""; } std::string sodEncryptSym(const std::string& msg, const std::string& key, SodiumNonce& nonce) { unsigned char ciphertext[msg.length() + crypto_secretbox_MACBYTES]; crypto_secretbox_easy(ciphertext, (unsigned char*)msg.c_str(), msg.length(), nonce.value, (unsigned char*)key.c_str()); nonce.increment(); return string((char*)ciphertext, sizeof(ciphertext)); } std::string sodDecryptSym(const std::string& msg, const std::string& key, SodiumNonce& nonce) { // It's fine if there's no data to decrypt if ((msg.length() - crypto_secretbox_MACBYTES) > 0) { unsigned char decrypted[msg.length() - crypto_secretbox_MACBYTES]; if (crypto_secretbox_open_easy(decrypted, (const unsigned char*)msg.c_str(), msg.length(), nonce.value, (const unsigned char*)key.c_str()) != 0) { throw std::runtime_error("Could not decrypt message"); } nonce.increment(); return string((char*)decrypted, sizeof(decrypted)); } else { nonce.increment(); return string(); } } #else #define POLY1305_BLOCK_SIZE 16 string newKey() { unsigned char key[CHACHA20_POLY1305_KEY_SIZE]; if (RAND_priv_bytes(key, sizeof key) != 1) { throw std::runtime_error( "Could not initialize random number generator for cryptographic functions - this is not recoverable"); } return "\"" + Base64Encode(string((char*) key, sizeof key)) + "\""; } std::string sodEncryptSym(const std::string& msg, const std::string& key, SodiumNonce& nonce) { unsigned char ciphertext[msg.length() + POLY1305_BLOCK_SIZE]; int len; int ciphertext_len; // Each thread gets its own cipher context static thread_local auto ctx = std::unique_ptr(nullptr, EVP_CIPHER_CTX_free); if (key.length() != CHACHA20_POLY1305_KEY_SIZE) { ostringstream err; err << "sodEncryptSym: key length is not 32 bytes (actual length=" << key.length() << ")"; throw std::runtime_error(err.str().c_str()); } if (ctx.get() == nullptr) { if (!(ctx = std::unique_ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free))) throw std::runtime_error("sodEncryptSym: EVP_CIPHER_CTX_new() could not initialize cipher context"); /* Initialise the encryption operation. */ if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_chacha20_poly1305(), NULL, NULL, NULL)) throw std::runtime_error("sodEncryptSym: EVP_EncryptInit_ex() could not initialize encryption operation"); } if (1 != EVP_EncryptInit_ex(ctx.get(), NULL, NULL, (unsigned char*) key.c_str(), nonce.value)) throw std::runtime_error("sodEncryptSym: EVP_EncryptInit_ex() could not initialize encryption key and IV"); if (1 != EVP_EncryptUpdate(ctx.get(), ciphertext + POLY1305_BLOCK_SIZE, &len, (unsigned char*) msg.c_str(), msg.length())) throw std::runtime_error("sodEncryptSym: EVP_EncryptUpdate() could not encrypt message"); ciphertext_len = len; if (1 != EVP_EncryptFinal_ex(ctx.get(), ciphertext + len + POLY1305_BLOCK_SIZE, &len)) throw std::runtime_error("sodEncryptSym: EVP_EncryptFinal_ex() could finalize message encryption");; ciphertext_len += len; /* Get the tag */ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, POLY1305_BLOCK_SIZE, ciphertext)) throw std::runtime_error("sodEncryptSym: EVP_CIPHER_CTX_ctrl() could not get tag"); nonce.increment(); return string((char*) ciphertext, sizeof(ciphertext)); } std::string sodDecryptSym(const std::string& msg, const std::string& key, SodiumNonce& nonce) { int len; int plaintext_len; // Each thread gets its own cipher context static thread_local auto ctx = std::unique_ptr(nullptr, EVP_CIPHER_CTX_free); if (key.length() != CHACHA20_POLY1305_KEY_SIZE) { ostringstream err; err << "sodDecryptSym: key length is not 32 bytes (actual length=" << key.length() << ")"; throw std::runtime_error(err.str().c_str()); } if ((msg.length() - POLY1305_BLOCK_SIZE) > 0) { string tag = msg.substr(0, POLY1305_BLOCK_SIZE); char plaintext[msg.length() - POLY1305_BLOCK_SIZE]; if (ctx.get() == nullptr) { if (!(ctx = std::unique_ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free))) throw std::runtime_error("sodDecryptSym: EVP_CIPHER_CTX_new() could not initialize cipher context"); if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_chacha20_poly1305(), NULL, NULL, NULL)) throw std::runtime_error("sodDecryptSym: EVP_DecryptInit_ex() could not initialize decryption operation"); } if (1 != EVP_DecryptInit_ex(ctx.get(), NULL, NULL, (unsigned char*) key.c_str(), nonce.value)) throw std::runtime_error("sodDecryptSym: EVP_DecryptInit_ex() could not initialize decryption key and IV"); if (!EVP_DecryptUpdate(ctx.get(), (unsigned char*) plaintext, &len, (unsigned char*) (msg.c_str() + POLY1305_BLOCK_SIZE), msg.length() - POLY1305_BLOCK_SIZE)) throw std::runtime_error("sodDecryptSym: EVP_DecryptUpdate() could not decrypt message"); plaintext_len = len; /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, POLY1305_BLOCK_SIZE, (void*) tag.c_str())) throw std::runtime_error("sodDecryptSym: EVP_CIPHER_CTX_ctrl() AEAD tag could not be validated"); if (!EVP_DecryptFinal_ex(ctx.get(), (unsigned char*) (plaintext + len), &len)) throw std::runtime_error("sodDecryptSym: EVP_DecryptFinal_ex() failed - plaintext cannot be trusted"); nonce.increment(); return string(plaintext, plaintext_len); } else { nonce.increment(); return string(); } } #endif #include "base64.hh" #include namespace anonpdns { char B64Decode1(char cInChar) { // The incoming character will be A-Z, a-z, 0-9, +, /, or =. // The idea is to quickly determine which grouping the // letter belongs to and return the associated value // without having to search the global encoding string // (the value we're looking for would be the resulting // index into that string). // // To do that, we'll play some tricks... unsigned char iIndex = '\0'; switch (cInChar) { case '+': iIndex = 62; break; case '/': iIndex = 63; break; case '=': iIndex = 0; break; default: // Must be 'A'-'Z', 'a'-'z', '0'-'9', or an error... // // Numerically, small letters are "greater" in value than // capital letters and numerals (ASCII value), and capital // letters are "greater" than numerals (again, ASCII value), // so we check for numerals first, then capital letters, // and finally small letters. iIndex = '9' - cInChar; if (iIndex > 0x3F) { // Not from '0' to '9'... iIndex = 'Z' - cInChar; if (iIndex > 0x3F) { // Not from 'A' to 'Z'... iIndex = 'z' - cInChar; if (iIndex > 0x3F) { // Invalid character...cannot // decode! iIndex = 0x80; // set the high bit } // if else { // From 'a' to 'z' iIndex = (('z' - iIndex) - 'a') + 26; } // else } // if else { // From 'A' to 'Z' iIndex = ('Z' - iIndex) - 'A'; } // else } // if else { // Adjust the index... iIndex = (('9' - iIndex) - '0') + 52; } // else break; } // switch return iIndex; } inline char B64Encode1(unsigned char uc) { if (uc < 26) { return 'A' + uc; } if (uc < 52) { return 'a' + (uc - 26); } if (uc < 62) { return '0' + (uc - 52); } if (uc == 62) { return '+'; } return '/'; }; } using namespace anonpdns; int B64Decode(const std::string& strInput, std::string& strOutput) { // Set up a decoding buffer long cBuf = 0; char* pBuf = (char*) &cBuf; // Decoding management... int iBitGroup = 0, iInNum = 0; // While there are characters to process... // // We'll decode characters in blocks of 4, as // there are 4 groups of 6 bits in 3 bytes. The // incoming Base64 character is first decoded, and // then it is inserted into the decode buffer // (with any relevant shifting, as required). // Later, after all 3 bytes have been reconstituted, // we assign them to the output string, ultimately // to be returned as the original message. int iInSize = strInput.size(); unsigned char cChar = '\0'; uint8_t pad = 0; while (iInNum < iInSize) { // Fill the decode buffer with 4 groups of 6 bits cBuf = 0; // clear pad = 0; for (iBitGroup = 0; iBitGroup < 4; ++iBitGroup) { if (iInNum < iInSize) { // Decode a character if (strInput.at(iInNum) == '=') pad++; while (isspace(strInput.at(iInNum))) iInNum++; cChar = B64Decode1(strInput.at(iInNum++)); } // if else { // Decode a padded zero cChar = '\0'; } // else // Check for valid decode if (cChar > 0x7F) return -1; // Adjust the bits switch (iBitGroup) { case 0: // The first group is copied into // the least significant 6 bits of // the decode buffer...these 6 bits // will eventually shift over to be // the most significant bits of the // third byte. cBuf = cBuf | cChar; break; default: // For groupings 1-3, simply shift // the bits in the decode buffer over // by 6 and insert the 6 from the // current decode character. cBuf = (cBuf << 6) | cChar; break; } // switch } // for // Interpret the resulting 3 bytes...note there // may have been padding, so those padded bytes // are actually ignored. strOutput += pBuf[2]; strOutput += pBuf[1]; strOutput += pBuf[0]; } // while if (pad) strOutput.resize(strOutput.length() - pad); return 1; } /* www.kbcafe.com Copyright 2001-2002 Randy Charles Morin The Encode static method takes an array of 8-bit values and returns a base-64 stream. */ std::string Base64Encode(const std::string& vby) { std::string retval; if (vby.size() == 0) { return retval; }; for (unsigned int i = 0; i < vby.size(); i += 3) { unsigned char by1 = 0, by2 = 0, by3 = 0; by1 = vby[i]; if (i + 1 < vby.size()) { by2 = vby[i + 1]; }; if (i + 2 < vby.size()) { by3 = vby[i + 2]; } unsigned char by4 = 0, by5 = 0, by6 = 0, by7 = 0; by4 = by1 >> 2; by5 = ((by1 & 0x3) << 4) | (by2 >> 4); by6 = ((by2 & 0xf) << 2) | (by3 >> 6); by7 = by3 & 0x3f; retval += B64Encode1(by4); retval += B64Encode1(by5); if (i + 1 < vby.size()) { retval += B64Encode1(by6); } else { retval += "="; }; if (i + 2 < vby.size()) { retval += B64Encode1(by7); } else { retval += "="; }; /* if ((i % (76 / 4 * 3)) == 0) { retval += "\r\n"; }*/ }; return retval; }; weakforced-2.10.2/common/sodcrypto.hh000066400000000000000000000054361461473602600175660ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "config.h" #include #include #include #ifndef HAVE_LIBSODIUM #include #include #include #define CHACHA20_POLY1305_IV_SIZE 12 #define CHACHA20_POLY1305_KEY_SIZE 32 struct SodiumNonce { void init() { if (RAND_priv_bytes(value, sizeof value) != 1) { throw std::runtime_error("Could not initialize random number generator for cryptographic functions - this is not recoverable"); } } void merge(const SodiumNonce& lower, const SodiumNonce& higher) { static const size_t halfSize = (sizeof value) / 2; memcpy(value, lower.value, halfSize); memcpy(value + halfSize, higher.value + halfSize, halfSize); } void increment() { uint32_t* p = (uint32_t*)value; uint32_t count=htonl(*p); ++count; *p=ntohl(count); } string toString() const { return string((const char*)value, sizeof value); } unsigned char value[CHACHA20_POLY1305_IV_SIZE]; }; #else #include struct SodiumNonce { void init() { randombytes_buf(value, sizeof value); } void merge(const SodiumNonce& lower, const SodiumNonce& higher) { static const size_t halfSize = (sizeof value) / 2; memcpy(value, lower.value, halfSize); memcpy(value + halfSize, higher.value + halfSize, halfSize); } void increment() { uint32_t* p = (uint32_t*)value; uint32_t count=htonl(*p); ++count; *p=ntohl(count); } string toString() const { return string((const char*)value, crypto_secretbox_NONCEBYTES); } unsigned char value[crypto_secretbox_NONCEBYTES]; }; #endif std::string newKeypair(); std::string sodEncryptSym(const std::string& msg, const std::string& key, SodiumNonce&); std::string sodDecryptSym(const std::string& msg, const std::string& key, SodiumNonce&); std::string newKey(); weakforced-2.10.2/common/sstuff.hh000066400000000000000000000270461461473602600170530ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef SSTUFF_HH #define SSTUFF_HH #include #include #include #include "iputils.hh" #include #include #include #include #include #include #include #include #include #include #include "dolog.hh" #include #include #include "namespaces.hh" #include "namespaces.hh" class NetworkError : public runtime_error { public: NetworkError(const string& why="Network Error") : runtime_error(why.c_str()) {} NetworkError(const char *why="Network Error") : runtime_error(why) {} }; typedef int ProtocolType; //!< Supported protocol types //! Representation of a Socket and many of the Berkeley functions available class Socket : public boost::noncopyable { public: Socket(int fd) { d_socket = fd; d_buflen=4096; d_buffer=new char[d_buflen]; } //! Construct a socket of specified address family and socket type. Socket(int af, int st, ProtocolType pt=0) { if((d_socket=(int)socket(af,st, pt))<0) throw NetworkError(strerror(errno)); setCloseOnExec(d_socket); d_buflen=4096; d_buffer=new char[d_buflen]; } ~Socket() { try { closesocket(d_socket); } catch (std::runtime_error &e) { // not a lot we can do here except log errlog("Error closing socket: %s", e.what()); } delete[] d_buffer; } //! If the socket is capable of doing so, this function will wait for a connection Socket *accept() { struct sockaddr_in remote; socklen_t remlen=sizeof(remote); memset(&remote, 0, sizeof(remote)); int s=(int)::accept(d_socket,(sockaddr *)&remote, &remlen); if(s<0) { if(errno==EAGAIN) return 0; throw NetworkError("Accepting a connection: "+string(strerror(errno))); } return new Socket(s); } //! Get remote address bool getRemote(ComboAddress &remote) { socklen_t remotelen=sizeof(remote); return (getpeername(d_socket, (struct sockaddr *)&remote, &remotelen) >= 0); } //! Check remote address against netmaskgroup ng bool acl(NetmaskGroup &ng) { ComboAddress remote; if (getRemote(remote)) return ng.match((ComboAddress *) &remote); return false; } //! Set the socket to non-blocking void setNonBlocking() { ::setNonBlocking(d_socket); } //! Set the socket to blocking void setBlocking() { ::setBlocking(d_socket); } void setV6Only() { int tmp = 1; if (setsockopt(d_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&tmp, static_cast(sizeof tmp))<0) throw NetworkError(string("Setsockopt failed: ")+strerror(errno)); } void setReuseAddr() { int tmp = 1; if (setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast(sizeof tmp))<0) throw NetworkError(string("Setsockopt failed: ")+strerror(errno)); } void setKeepAlive() { int tmp = 1; if (setsockopt(d_socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&tmp, static_cast(sizeof tmp))<0) throw NetworkError(string("Setsockopt failed: ")+strerror(errno)); } //! Bind the socket to a specified endpoint void bind(const ComboAddress &local) { int tmp=1; if(setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) throw NetworkError(string("Setsockopt failed: ")+strerror(errno)); if(::bind(d_socket,(struct sockaddr *)&local, local.getSocklen())<0) throw NetworkError(strerror(errno)); } #if 0 //! Bind the socket to a specified endpoint void bind(const ComboAddress &ep) { ComboAddress local; memset(reinterpret_cast(&local),0,sizeof(local)); local.sin_family=d_family; local.sin_addr.s_addr=ep.address.byte; local.sin_port=htons(ep.port); bind(local); } #endif //! Connect the socket to a specified endpoint void connect(const ComboAddress &ep) { if(::connect(d_socket,(struct sockaddr *)&ep, ep.getSocklen()) < 0 && errno != EINPROGRESS) throw NetworkError(strerror(errno)); } //! Connect to the socket to a specific endpoint with a configurable timeout in milliseconds void connectWithTimeout(const ComboAddress &ep, int timeout) { long arg; // Set non-blocking if((arg = fcntl(d_socket, F_GETFL, NULL)) < 0) { throw NetworkError(strerror(errno)); } arg |= O_NONBLOCK; if(fcntl(d_socket, F_SETFL, arg) < 0) { throw NetworkError(strerror(errno)); } if(::connect(d_socket,(struct sockaddr *)&ep, ep.getSocklen()) < 0) { if (errno != EINPROGRESS) { throw NetworkError(strerror(errno)); } else { struct pollfd pf = { d_socket, POLLOUT, 0 }; int poll_ret; poll_ret = poll(&pf, 1, timeout); int saved_errno = errno; if((arg = fcntl(d_socket, F_GETFL, NULL)) < 0) { throw NetworkError(strerror(errno)); } arg &= (~O_NONBLOCK); if(fcntl(d_socket, F_SETFL, arg) < 0) { throw NetworkError(strerror(errno)); } if (poll_ret < 0) { throw NetworkError(strerror(saved_errno)); } else if (poll_ret == 0) { throw NetworkError("Connection timed out"); } } } } //! For datagram sockets, receive a datagram and learn where it came from /** For datagram sockets, receive a datagram and learn where it came from \param dgram Will be filled with the datagram \param ep Will be filled with the origin of the datagram */ void recvFrom(string &dgram, ComboAddress &ep) { socklen_t remlen=sizeof(ep); int bytes; if((bytes=recvfrom(d_socket, d_buffer, d_buflen, 0, (sockaddr *)&ep , &remlen)) <0) throw NetworkError(strerror(errno)); dgram.assign(d_buffer,bytes); } bool recvFromAsync(string &dgram, ComboAddress &ep) { struct sockaddr_in remote; socklen_t remlen=sizeof(remote); int bytes; if((bytes=recvfrom(d_socket, d_buffer, d_buflen, 0, (sockaddr *)&remote, &remlen))<0) { if(errno!=EAGAIN) { throw NetworkError(strerror(errno)); } else { return false; } } dgram.assign(d_buffer,bytes); return true; } //! For datagram sockets, send a datagram to a destination void sendTo(const char* msg, unsigned int len, const ComboAddress &ep) { if(sendto(d_socket, msg, len, 0, (sockaddr *)&ep, ep.getSocklen())<0) throw NetworkError(strerror(errno)); } /** For datagram sockets, send a datagram to a destination \param dgram The datagram \param ep The intended destination of the datagram */ void sendTo(const string &dgram, const ComboAddress &ep) { sendTo(dgram.c_str(), dgram.length(), ep); } //! Write this data to the socket, taking care that all bytes are written out void writen(const string &data) { if(data.empty()) return; int toWrite=(int)data.length(); const char *ptr=data.c_str(); do { int res = ::send(d_socket, ptr, toWrite, 0); if(res<0) throw NetworkError("Writing to a socket: "+string(strerror(errno))); toWrite-=res; ptr+=res; }while(toWrite); } //! tries to write toWrite bytes from ptr to the socket /** tries to write toWrite bytes from ptr to the socket, but does not make sure they al get written out \param ptr Location to write from \param toWrite number of bytes to try */ unsigned int tryWrite(const char *ptr, int toWrite) { int res; res=::send(d_socket,ptr,toWrite,0); if(res==0) throw NetworkError("EOF on writing to a socket"); if(res>0) return res; if(errno==EAGAIN) return 0; throw NetworkError("Writing to a socket: "+string(strerror(errno))); } //! Writes toWrite bytes from ptr to the socket /** Writes toWrite bytes from ptr to the socket. Returns how many bytes were written */ unsigned int write(const char *ptr, int toWrite) { int res; res=::send(d_socket,ptr,toWrite,0); if(res<0) { throw NetworkError("Writing to a socket: "+string(strerror(errno))); } return res; } void writenWithTimeout(const void *buffer, unsigned int n, int timeout) { unsigned int bytes=n; const char *ptr = (char*)buffer; while(bytes) { int ret = ::write(d_socket, ptr, bytes); if(ret < 0) { if(errno==EAGAIN) { ret=waitForRWData(d_socket, false, timeout, 0); if(ret < 0) throw NetworkError("Waiting for data write"); if(!ret) throw NetworkError("Timeout writing data"); continue; } else throw NetworkError("Writing data: "+stringerror()); } if(!ret) { throw NetworkError("Did not fulfill TCP write due to EOF"); } ptr += ret; bytes -= ret; } } //! reads one character from the socket int getChar() { char c; int res=::recv(d_socket,&c,1,0); if(res) return c; return -1; } void getline(string &data) { data=""; int c; while((c=getChar())!=-1) { data+=(char)c; if(c=='\n') break; } } //! Reads a block of data from the socket to a string void read(string &data) { int res=::recv(d_socket,d_buffer,d_buflen,0); if(res<0) throw NetworkError("Reading from a socket: "+string(strerror(errno))); data.assign(d_buffer,res); } //! Reads a block of data from the socket to a block of memory int read(char *buffer, int bytes) { int res=::recv(d_socket,buffer,bytes,0); if(res<0) throw NetworkError("Reading from a socket: "+string(strerror(errno))); return res; } //! Reads a block of data from the socket to a block of memory //! Only returns when all the data requested is available int readAll(char *buffer, int bytes) { int res=::recv(d_socket,buffer,bytes, MSG_WAITALL); if(res<0) throw NetworkError("Reading from a socket: "+string(strerror(errno))); return res; } int readWithTimeout(char* buffer, int n, int timeout) { int err = waitForRWData(d_socket, true, timeout, 0); if(err == 0) throw NetworkError("timeout reading"); if(err < 0) throw NetworkError("nonblocking read failed: "+string(strerror(errno))); return read(buffer, n); } //! Sets the socket to listen with a default listen backlog of 10 bytes void listen(unsigned int length=10) { if(::listen(d_socket,length)<0) throw NetworkError("Setting socket to listen: "+string(strerror(errno))); } //! Returns the internal file descriptor of the socket int getHandle() const { return d_socket; } private: int d_socket; char *d_buffer; int d_buflen; }; #endif weakforced-2.10.2/common/test-minicurl.cc000066400000000000000000000005301461473602600203130ustar00rootroot00000000000000#define BOOST_TEST_DYN_LINK #define BOOST_TEST_NO_MAIN #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "minicurl.hh" BOOST_AUTO_TEST_SUITE(test_minicurl) BOOST_AUTO_TEST_CASE(test_mini_simple) { MiniCurl mc; mc.setID(1234); BOOST_CHECK(mc.getID() == 1234); } BOOST_AUTO_TEST_SUITE_END(); weakforced-2.10.2/common/test-serialize.cc000066400000000000000000000057531461473602600204740ustar00rootroot00000000000000#define BOOST_TEST_DYN_LINK #define BOOST_TEST_NO_MAIN #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "twmap.hh" #include #include "ext/hyperloglog.hpp" #include "ext/count_min_sketch.hpp" #include "misc.hh" #include BOOST_AUTO_TEST_SUITE(test_serialize) BOOST_AUTO_TEST_CASE(test_serialize_hll) { hll::HyperLogLog hll(6); std::string foo="foo"; std::string bar="bar"; hll.add(foo.c_str(), foo.length()); hll.add(bar.c_str(), bar.length()); BOOST_CHECK((int)hll.estimate() == 2); std::stringstream ss(std::stringstream::in|std::stringstream::out|std::stringstream::binary); hll.dump(ss); hll::HyperLogLog hllcopy(6); hllcopy.restore(ss); BOOST_CHECK((int)hllcopy.estimate() == 2); } BOOST_AUTO_TEST_CASE(test_serialize_countmin) { CountMinSketch cm(0.05, 0.2); std::string foo="foo"; std::string bar="bar"; cm.update(foo.c_str(), 2); cm.update(bar.c_str(), 1); BOOST_CHECK((int)cm.estimate(foo.c_str()) == 2); // check we can dump and restore std::stringstream ss(std::stringstream::in|std::stringstream::out|std::stringstream::binary); cm.dump(ss); CountMinSketch cmcopy(0.05, 0.2); cmcopy.restore(ss); BOOST_CHECK((int)cmcopy.estimate(foo.c_str()) == 2); // check we can dump the copy and restore from that std::stringstream ss2(std::stringstream::in|std::stringstream::out|std::stringstream::binary); cmcopy.dump(ss2); CountMinSketch cmcopy2(0.05, 0.2); cmcopy2.restore(ss2); BOOST_CHECK((int)cmcopy2.estimate(foo.c_str()) == 2); } BOOST_AUTO_TEST_CASE(test_serialize_statsdb) { TWStatsDB sdb1("sdb1", 600, 6); TWStatsDB sdb2("sdb2", 600, 6); FieldMap fm; fm.insert(std::make_pair("field1", "int")); fm.insert(std::make_pair("field2", "hll")); fm.insert(std::make_pair("field3", "countmin")); sdb1.setFields(fm); sdb2.setFields(fm); sdb1.add(std::string("bar"), "field1", 10); sdb1.add(std::string("bar"), "field2", "diff1"); sdb1.add(std::string("bar"), "field3", "diff1"); sdb1.add(std::string("foo"), "field1", 10); BOOST_CHECK(sdb1.get(std::string("foo"), "field1") == 10); sdb1.add(std::string("foo"), "field2", "diff1"); sdb1.add(std::string("foo"), "field2", "diff2"); sdb1.add(std::string("foo"), "field2", "diff3"); BOOST_CHECK(sdb1.get(std::string("foo"), "field2") == 3); sdb1.add(std::string("foo"), "field3", "diff1"); sdb1.add(std::string("foo"), "field3", "diff1"); sdb1.add(std::string("foo"), "field3", "diff2"); BOOST_CHECK(sdb1.get(std::string("foo"), "field3", "diff1") == 2); for (auto it = sdb1.startDBDump(); it != sdb1.DBDumpIteratorEnd(); ++it) { TWStatsDBDumpEntry e; std::string key; if (sdb1.DBDumpEntry(it, e, key)) { sdb2.restoreEntry(key, e); } } sdb1.endDBDump(); BOOST_CHECK(sdb2.get(std::string("foo"), "field1") == 10); BOOST_CHECK(sdb2.get(std::string("foo"), "field2") == 3); BOOST_CHECK(sdb2.get(std::string("foo"), "field3", "diff1") == 2); } BOOST_AUTO_TEST_SUITE_END(); weakforced-2.10.2/common/testrunner.cc000066400000000000000000000004211461473602600177240ustar00rootroot00000000000000#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN #define BOOST_TEST_MODULE unit #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "dolog.hh" bool g_console = false; bool g_docker = false; LogLevel g_loglevel{LogLevel::Info}; weakforced-2.10.2/common/twmap-static.cc000066400000000000000000000023451461473602600201370ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "twmap.hh" // C++ forces you to initialise static member variables outside the class // Hence the need for this file unsigned int TWStatsMemberHLL::num_bits = HLL_NUM_REGISTER_BITS; float TWStatsMemberCountMin::eps = COUNTMIN_EPS; float TWStatsMemberCountMin::gamma = COUNTMIN_GAMMA; weakforced-2.10.2/common/twmap.hh000066400000000000000000000776071461473602600167010ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ext/hyperloglog.hpp" #include "ext/count_min_sketch.hpp" #include "iputils.hh" #include "dolog.hh" #include "wforce_ns.hh" #include "ext/threadname.hh" using std::thread; class TWStatsMember; typedef std::unique_ptr TWStatsMemberP; typedef std::vector> TWStatsBuf; // Not all stats member subclasses will implement all of these methods in a useful way (e.g. add string is meaningless to an integer-based class) class TWStatsMember { public: virtual ~TWStatsMember() = default; virtual void add(int a) = 0; // add an integer to the stored value virtual void add(const std::string& s) = 0; // add a string to the stored window (this has different semantics for each subclass) virtual void add(const std::string& s, int a) = 0; // add a string/integer combo to the stored window (only applies to some stats types) virtual void sub(int a) = 0; // subtract an integer from the stored value virtual void sub(const std::string& s) = 0; // remove a string from the stored value virtual int get() = 0; // return the current stat virtual int get(const std::string& s) = 0; // return the current stat virtual void set(int) = 0; // Set the stored value to the supplied integer virtual void set(const std::string& s) = 0; // Set the stored value based on the supplied string virtual void erase() = 0; // Remove all data - notice this isn't necessarily the same as set(0) or set("") virtual int sum(const TWStatsBuf& vec) = 0; // combine an array of stored values virtual int sum(const std::string& s, const TWStatsBuf& vec) = 0; // combine an array of stored values virtual void dump(std::ostream& os) = 0; // serialize a TWStatsMember virtual void restore(std::istream& is) = 0; // unserialize a TWStatsMember }; class TWStatsMemberInt : public TWStatsMember { public: TWStatsMemberInt() {} TWStatsMemberInt(const TWStatsMemberInt&) = delete; TWStatsMemberInt& operator=(const TWStatsMemberInt&) = delete; TWStatsMemberInt(TWStatsMemberInt&&) = delete; // move construct TWStatsMemberInt& operator=(TWStatsMemberInt &&) = delete; // move assign void add(int a) override { i += a; } void add(const std::string& s) override { int a = std::stoi(s); i += a; return; } void add(const std::string& s, int a) override { return; } void sub(int a) override { i -= a; } void sub(const std::string& s) override { int a = std::stoi(s); i -= a; return; } int get() override { return i; } int get(const std::string& s) override { return i; } void set(int a) override { i = a; } void set(const std::string& s) override { return; } void erase() override { i = 0; } int sum(const TWStatsBuf& vec) override { int count = 0; for (auto a=vec.begin(); a!=vec.end(); ++a) { count += a->second->get(); } return count; } int sum(const std::string& s, const TWStatsBuf& vec) override { return 0; } void dump(std::ostream& os) override { uint32_t neti = htonl(i); os.write((char*)&neti, sizeof(neti)); if(os.fail()) { throw std::runtime_error("TWStatsMemberInt: Failed to dump"); } } void restore(std::istream& is) override { uint32_t neti = 0; is.read((char*)&neti, sizeof(neti)); i = ntohl(neti); } private: int i=0; }; #define HLL_NUM_REGISTER_BITS 6 class TWStatsMemberHLL : public TWStatsMember { public: TWStatsMemberHLL() { hllp = wforce::make_unique(num_bits); } TWStatsMemberHLL(const TWStatsMemberHLL&) = delete; TWStatsMemberHLL& operator=(const TWStatsMemberHLL&) = delete; TWStatsMemberHLL(TWStatsMemberHLL&&) = delete; // move construct TWStatsMemberHLL& operator=(TWStatsMemberHLL &&) = delete; // move assign void add(int a) override { std::string str; str = std::to_string(a); hllp->add(str.c_str(), str.length()); return; } void add(const std::string& s) override { hllp->add(s.c_str(), s.length()); } void add(const std::string& s, int a) override { return; } void sub(int a) override { return; } void sub(const std::string& s) override { return; } int get() override { return std::lround(hllp->estimate()); } int get(const std::string& s) override { hllp->add(s.c_str(), s.length()); return std::lround(hllp->estimate()); } // add and return value void set(int a) override { return; } void set(const std::string& s) override { hllp->clear(); hllp->add(s.c_str(), s.length()); } void erase() override { hllp->clear(); } int sum(const TWStatsBuf& vec) override { hll::HyperLogLog hllsum(num_bits); for (auto a = vec.begin(); a != vec.end(); ++a) { // XXX yes not massively pretty hllsum.merge(*((dynamic_cast(*(a->second))).hllp)); } return std::lround(hllsum.estimate()); } int sum(const std::string& s, const TWStatsBuf& vec) override { return 0; } void dump(std::ostream& os) override { hllp->dump(os); } void restore(std::istream& is) override { hllp->restore(is); } static void setNumBits(unsigned int nbits) { if (nbits < 4) nbits = 4; if (num_bits >30) nbits = 30; num_bits = nbits; } private: std::unique_ptr hllp; static unsigned int num_bits; }; #define COUNTMIN_EPS 0.05 #define COUNTMIN_GAMMA 0.2 class TWStatsMemberCountMin : public TWStatsMember { public: TWStatsMemberCountMin() { cm = wforce::make_unique(eps, gamma); } TWStatsMemberCountMin(const TWStatsMemberCountMin&) = delete; TWStatsMemberCountMin& operator=(const TWStatsMemberCountMin&) = delete; TWStatsMemberCountMin(TWStatsMemberCountMin&&) = delete; // move construct TWStatsMemberCountMin& operator=(TWStatsMemberCountMin &&) = delete; // move assign void add(int a) override { return; } void add(const std::string& s) override { cm->update(s.c_str(), 1); } void add(const std::string& s, int a) override { cm->update(s.c_str(), a); } void sub(int a) override { return; } void sub(const std::string& s) override { return; } int get() override { return cm->totalcount(); } int get(const std::string& s) override { return cm->estimate(s.c_str()); } void set(int a) override { return; } void set(const std::string& s) override { return; } void erase() override { cm->erase(); return; } int sum(const TWStatsBuf& vec) override { int count = 0; for (auto a=vec.begin(); a!=vec.end(); ++a) { count += a->second->get(); } return count; } int sum(const std::string&s, const TWStatsBuf& vec) override { int count = 0; for (auto a=vec.begin(); a!=vec.end(); ++a) { count += a->second->get(s); } return count; } void dump(std::ostream& os) override { cm->dump(os); } void restore(std::istream& is) override { cm->restore(is); } static void setGamma(float g) { if (g > 1) g = 1; if (g < 0) g = 0; gamma = g; } static void setEPS(float e) { if (e < 0.01) e = 0.01; if (e > 1) e = 1; eps = e; } private: std::unique_ptr cm; static float eps; static float gamma; }; template TWStatsMemberP createInstance() { return wforce::make_unique(); } typedef std::map map_type; // The idea here is that the type mechanism is extensible, e.g. could add a bloom filter etc. // XXX Should make this dynamically extensible class TWStatsTypeMap { public: map_type type_map; static TWStatsTypeMap& getInstance() { // this is thread-safe in C++11 static TWStatsTypeMap myInstance; return myInstance; } TWStatsTypeMap(const TWStatsTypeMap& src) = delete; // copy construct TWStatsTypeMap(TWStatsTypeMap&&) = delete; // move construct TWStatsTypeMap& operator=(const TWStatsTypeMap& rhs) = delete; // copy assign TWStatsTypeMap& operator=(TWStatsTypeMap &&) = delete; // move assign protected: TWStatsTypeMap() { type_map["int"] = &createInstance; type_map["hll"] = &createInstance; type_map["countmin"] = &createInstance; } }; typedef std::vector> TWStatsBufSerial; // this class is not protected by mutexes because it sits behind TWStatsDB which has a mutex // controlling all access class TWStatsEntry { public: TWStatsEntry(int nw, int ws, std::time_t st, const std::string& field_type) : num_windows(nw), window_size(ws), start_time(st) { auto& field_types = TWStatsTypeMap::getInstance(); auto it = field_types.type_map.find(field_type); if (it != field_types.type_map.end()) { for (int i=0; i< num_windows; i++) { stats_array.push_back(std::pair((std::time_t)0, it->second())); } } } // prevent copies at compile time TWStatsEntry(const TWStatsEntry&) = delete; TWStatsEntry& operator=(const TWStatsEntry&) = delete; void add(int a) { clean_windows(); int cur_window = current_window(); auto& sm = stats_array[cur_window]; sm.second->add(a); update_write_timestamp(cur_window); } void add(const std::string& s) { clean_windows(); int cur_window = current_window(); auto& sm = stats_array[cur_window]; sm.second->add(s); update_write_timestamp(cur_window); } void add(const std::string& s, int a) { clean_windows(); int cur_window = current_window(); auto& sm = stats_array[cur_window]; sm.second->add(s, a); update_write_timestamp(cur_window); } void sub(int a) { clean_windows(); int cur_window = current_window(); auto& sm = stats_array[cur_window]; sm.second->sub(a); update_write_timestamp(cur_window); } void sub(const std::string& s) { clean_windows(); int cur_window = current_window(); auto& sm = stats_array[cur_window]; sm.second->sub(s); update_write_timestamp(cur_window); } int get() { clean_windows(); int cur_window = current_window(); return stats_array[cur_window].second->get(); } int get(const std::string& s) { clean_windows(); int cur_window = current_window(); return stats_array[cur_window].second->get(s); } int get_current() { clean_windows(); int cur_window = current_window(); return stats_array[cur_window].second->get(); } int get_current(const std::string& s) { clean_windows(); int cur_window = current_window(); return stats_array[cur_window].second->get(s); } void get_windows(std::vector& ret_vec) { clean_windows(); int cur_window = current_window(); for (int i=cur_window+num_windows; i>cur_window; i--) { int index = i % num_windows; ret_vec.push_back(stats_array[index].second->get()); } } void get_windows(const std::string& s, std::vector& ret_vec) { clean_windows(); int cur_window = current_window(); for (int i=cur_window+num_windows; i>cur_window; i--) { int index = i % num_windows; ret_vec.push_back(stats_array[index].second->get(s)); } } int sum() { clean_windows(); int cur_window = current_window(); if (sum_cache_valid == true) return sum_cache_value; else { sum_cache_value = stats_array[cur_window].second->sum(stats_array); return sum_cache_value; } } int sum(const std::string& s) { clean_windows(); int cur_window = current_window(); if (ssum_cache_valid == true) return ssum_cache_value; else { ssum_cache_value = stats_array[cur_window].second->sum(s, stats_array); return ssum_cache_value; } } void reset() { for (TWStatsBuf::iterator i = stats_array.begin(); i != stats_array.end(); ++i) { i->second->erase(); i->first = 0; } sum_cache_valid = false; ssum_cache_valid = false; } void dump(TWStatsBufSerial& statsvec, std::time_t& stime) { for (TWStatsBuf::iterator i = stats_array.begin(); i != stats_array.end(); ++i) { std::stringstream ss; i->second->dump(ss); statsvec.emplace_back(std::make_pair(i->first, std::move(ss))); } stime = start_time; } void restore(TWStatsBufSerial& statsvec, std::time_t stime) { start_time = stime; last_cleaned = 0; sum_cache_valid = false; ssum_cache_valid = false; unsigned int j = 0; for (auto i = statsvec.begin(); i != statsvec.end(); ++i, ++j) { stats_array[j].first = i->first; stats_array[j].second->restore(i->second); } } protected: void update_write_timestamp(int cur_window) { std::time_t now, write_time; std::time(&now); // only do this if the window first mod time is 0 if (stats_array[cur_window].first == 0) { write_time = now - ((now - start_time) % window_size); stats_array[cur_window].first = write_time; } sum_cache_valid = false; ssum_cache_valid = false; } int current_window() { std::time_t now, diff; std::time(&now); diff = now - start_time; int cw = (diff/window_size) % num_windows; return cw; } void clean_windows() { // this function checks the time difference since each array member was first written // and expires (zeros) any windows as necessary. This needs to be done before any data is read or written std::time_t now, expire_diff; std::time(&now); expire_diff = window_size * num_windows; // optimization - only clean windows if they need cleaning if ((now-last_cleaned) < window_size) return; for (TWStatsBuf::iterator i = stats_array.begin(); i != stats_array.end(); ++i) { std::time_t last_write = i->first; if ((last_write != 0) && (now - last_write) >= expire_diff) { i->second->erase(); i->first = 0; sum_cache_valid = false; ssum_cache_valid = false; } } last_cleaned = now; } private: TWStatsBuf stats_array; int num_windows; int window_size; std::time_t start_time; std::time_t last_cleaned{0}; bool sum_cache_valid = false; int sum_cache_value = 0; bool ssum_cache_valid = false; int ssum_cache_value = 0; }; typedef std::unique_ptr TWStatsEntryP; // key is field name, value is field type typedef std::map FieldMap; // key is field name, value is a start time and a bunch of time windows typedef std::map> TWStatsDBDumpEntry; // A vector of field name plus the count from each of the windows typedef std::vector>> TWStatsDBEntry; const unsigned int ctwstats_map_size_soft = 524288; template class TWStatsDB { public: typedef std::list TWKeyTrackerType; explicit TWStatsDB(const std::string& name, int w_siz, int num_w) { window_size = w_siz ? w_siz : 1; num_windows = num_w ? num_w : 1; std::time(&start_time); map_size_soft = ctwstats_map_size_soft; db_name = name; } // detect attempts to copy at compile time TWStatsDB(const TWStatsDB&) = delete; TWStatsDB& operator=(const TWStatsDB&) = delete; std::string getDBName() { return db_name; } static void twExpireThread(std::shared_ptr> sdbp) { setThreadName("wf/tw-expire"); sdbp->expireEntries(); } void expireEntries(); bool setFields(const FieldMap& fields); const FieldMap& getFields() { return field_map; } void incr(const T& key, const std::string& field_name); void decr(const T& key, const std::string& field_name); void add(const T& key, const std::string& field_name, int a); void add(const T& key, const std::string& field_name, const std::string& s); void add(const T& key, const std::string& field_name, const std::string& s, int a); void sub(const T& key, const std::string& field_name, int a); void sub(const T& key, const std::string& field_name, const std::string& s); int get(const T& key, const std::string& field_name); // gets all values summed/combined over all windows int get(const T& key, const std::string& field_name, const std::string& s); // gets all values summed/combined over all windows for a particular value int get_current(const T& key, const std::string& field_name); // gets the value just for the current window int get_current(const T& key, const std::string& field_name, const std::string& s); // gets the value just for the current window for a particular value bool get_windows(const T& key, const std::string& field_name, std::vector& ret_vec); // gets each window value returned in a vector bool get_windows(const T& key, const std::string& field_name, const std::string& s, std::vector& ret_vec); // gets each window value returned in a vector for a particular value bool get_all_fields(const T& key, std::vector>& ret_vec); bool get_all_fields_windows(const T& key, TWStatsDBEntry& ret_vec) const; void reset(const T&key); // Reset to zero all fields for a given key void reset_field(const T&key, const std::string& field_name); // Reset to zero a particular field void set_map_size_soft(unsigned int size); unsigned int get_size(); unsigned int get_max_size(); uint8_t getv4Prefix() { return v4_prefix; } uint8_t getv6Prefix() { return v6_prefix; } void setv4Prefix(uint8_t prefix) { v4_prefix = prefix; } void setv6Prefix(uint8_t prefix) { v6_prefix = prefix; } int windowSize() { return window_size; } int numWindows() { return num_windows; } void set_expire_sleep(unsigned int ms) { expire_ms = ms; } // This function is very dangerous since it relies on later calling endDBDump() to unlock the mutex // But it is essential, otherwise the iterator will become garbage as the DB is modified typename TWKeyTrackerType::const_iterator startDBDump() const { mutx.lock(); return key_tracker.cbegin(); } // While the mutex is being held, no modifications can be made to the DB; // this could cause replication packets to get backed up and lost bool DBDumpEntry(typename TWKeyTrackerType::const_iterator& i, TWStatsDBDumpEntry& entry, T& key) const { const auto it = stats_db.find(*i); if (it != stats_db.end()) { key = it->first; for (auto fm = it->second.second.begin(); fm != it->second.second.end(); ++fm) { TWStatsBufSerial sbs; std::time_t stime; fm->second->dump(sbs, stime); entry.emplace(std::make_pair(fm->first, std::make_pair(stime, std::move(sbs)))); } return true; } return false; } // This returns counts for each of the windows rather than the serialized // internal data which DBDumpEntry returns bool DBGetEntry(typename TWKeyTrackerType::const_iterator& i, TWStatsDBEntry& ret_vec, T& key) const { const auto it = stats_db.find(*i); if (it != stats_db.end()) { key = it->first; get_all_fields_windows_unsafe(key, ret_vec); return true; } return false; } const typename TWKeyTrackerType::const_iterator DBDumpIteratorEnd() const { return key_tracker.cend(); } void endDBDump() const { mutx.unlock(); } // Restore StatsDB entries void restoreEntry(const T& key, TWStatsDBDumpEntry& entry) { for (auto fm = entry.begin(); fm != entry.end(); ++fm) { find_create_key_field(key, fm->first, [fm, this](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->restore(fm->second.second, fm->second.first); this->update_write_timestamp(kt); }); } } protected: template bool find_create_key_field(const T& key, const std::string& field_name, Fn fn); template bool find_key_field(const T& key, const std::string& field_name, Fn fn); template bool _find_create_key_field(const T& key, const std::string& field_name, Fn fn, bool create); // This function does not lock the mutex bool get_all_fields_windows_unsafe(const T& key, TWStatsDBEntry& ret_vec) const; void update_write_timestamp(typename TWKeyTrackerType::iterator& kt) { // this is always called from a mutex lock (or should be) // move this key to the end of the key tracker list key_tracker.splice(key_tracker.end(), key_tracker, kt); } private: TWKeyTrackerType key_tracker; typedef std::unordered_map>> TWStatsDBMap; TWStatsDBMap stats_db; FieldMap field_map; int window_size; int num_windows; unsigned int map_size_soft; std::time_t start_time; mutable std::mutex mutx; std::string db_name; uint8_t v4_prefix=32; uint8_t v6_prefix=128; unsigned int expire_ms=250; // expiry thread sleep time }; // Template methods template bool TWStatsDB::setFields(const FieldMap& fields) { auto& field_types = TWStatsTypeMap::getInstance(); for (auto f : fields) { if (field_types.type_map.find(f.second) == field_types.type_map.end()) { return false; } } field_map = fields; return true; } template void TWStatsDB::expireEntries() { struct timespec wait_interval; // spend some time every now and again expiring entries which haven't been updated if (expire_ms >= 1000) { wait_interval.tv_sec = expire_ms / 1000; wait_interval.tv_nsec = (expire_ms % 1000) * 1000000; } else { wait_interval.tv_sec = 0; wait_interval.tv_nsec = expire_ms * 1000000; } for (;;) { nanosleep(&wait_interval, nullptr); { std::lock_guard lock(mutx); // don't bother expiring if the map isn't too big if (stats_db.size() <= map_size_soft) continue; unsigned int num_expire = stats_db.size() - map_size_soft; unsigned int num_expired = num_expire; vinfolog("About to expire %d entries from stats db %s", num_expire, db_name); // this just uses the front of the key tracker list, which always contains the Least Recently Modified keys while (num_expire--) { const typename TWStatsDBMap::iterator it = stats_db.find(key_tracker.front()); if (it != stats_db.end()) { stats_db.erase(it); key_tracker.pop_front(); } } vinfolog("Finished expiring %d entries from stats db %s", num_expired, db_name); } } } template template bool TWStatsDB::find_key_field(const T& key, const std::string& field_name, Fn fn) { std::lock_guard lock(mutx); return _find_create_key_field(key, field_name, fn, false); } template template bool TWStatsDB::find_create_key_field(const T& key, const std::string& field_name, Fn fn) { std::lock_guard lock(mutx); return _find_create_key_field(key, field_name, fn, true); } template template bool TWStatsDB::_find_create_key_field(const T& key, const std::string& field_name, Fn fn, bool create) { bool retval = false; // first check if the field name is in the field map - if not we throw the query out straight away auto myfield = field_map.find(field_name); if (myfield == field_map.end()) return false; // Now we get the field type from the registered field types map auto& field_types = TWStatsTypeMap::getInstance(); auto mytype = field_types.type_map.find(myfield->second); if (mytype == field_types.type_map.end()) return false; // otherwise look to see if the key and field name have been already inserted auto mysdb = stats_db.find(key); if (mysdb != stats_db.end()) { // the key already exists let's look for the field auto myfm = mysdb->second.second.find(field_name); if (myfm != mysdb->second.second.end()) { // awesome this key/field combination has already been created so call the supplied lambda fn(myfm, mysdb->second.first); retval = true; } else { if (create) { // if we get here it means the key exists, but not the field so we need to add it mysdb->second.second.emplace(std::make_pair(field_name, wforce::make_unique(num_windows, window_size, start_time, mytype->first))); retval = _find_create_key_field(key, field_name, fn, false); } } } else { if (create) { // if we get here, we have no key and no field // add the key at the end of the key tracker list typename TWKeyTrackerType::iterator kit = key_tracker.insert(key_tracker.end(), key); std::map myfm; myfm.emplace(std::make_pair(field_name, wforce::make_unique(num_windows, window_size, start_time, mytype->first))); stats_db.emplace(std::make_pair(key, std::make_pair(kit, std::move(myfm)))); retval = _find_create_key_field(key, field_name, fn, false); } } return(retval); } template void TWStatsDB::incr(const T& key, const std::string& field_name) { add(key, field_name, 1); } template void TWStatsDB::decr(const T& key, const std::string& field_name) { sub(key, field_name, 1); } template void TWStatsDB::add(const T& key, const std::string& field_name, int a) { find_create_key_field(key, field_name, [a, this](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->add(a); this->update_write_timestamp(kt); }); } template void TWStatsDB::add(const T& key, const std::string& field_name, const std::string& s) { find_create_key_field(key, field_name, [s, this](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->add(s); this->update_write_timestamp(kt); }); } template void TWStatsDB::add(const T& key, const std::string& field_name, const std::string& s, int a) { find_create_key_field(key, field_name, [&s, a, this](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->add(s, a); this->update_write_timestamp(kt); }); } template void TWStatsDB::sub(const T& key, const std::string& field_name, int a) { find_create_key_field(key, field_name, [a, this](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->sub(a); update_write_timestamp(kt); }); } template void TWStatsDB::sub(const T& key, const std::string& field_name, const std::string& s) { find_create_key_field(key, field_name, [s, this](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->sub(s); update_write_timestamp(kt); }); } template int TWStatsDB::get(const T& key, const std::string& field_name) { int retval=0; find_key_field(key, field_name, [&retval](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { retval = it->second->sum(); }); return retval; } template int TWStatsDB::get(const T& key, const std::string& field_name, const std::string& s) { int retval=0; find_key_field(key, field_name, [&retval, s](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { retval = it->second->sum(s); }); return retval; } template int TWStatsDB::get_current(const T& key, const std::string& field_name) { int retval=0; find_key_field(key, field_name, [&retval](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { retval = it->second->get(); }); return retval; } template int TWStatsDB::get_current(const T& key, const std::string& field_name, const std::string& val) { int retval=0; find_key_field(key, field_name, [&retval, val](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { retval = it->second->get(val); }); return retval; } template bool TWStatsDB::get_windows(const T& key, const std::string& field_name, std::vector& ret_vec) { bool retval=false; retval = find_key_field(key, field_name, [&ret_vec](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->get_windows(ret_vec); }); return(retval); } template bool TWStatsDB::get_windows(const T& key, const std::string& field_name, const std::string& val, std::vector& ret_vec) { bool retval=false; retval = find_key_field(key, field_name, [&val, &ret_vec](std::map::iterator it, typename TWKeyTrackerType::iterator& kt) { it->second->get_windows(val, ret_vec); }); return(retval); } template bool TWStatsDB::get_all_fields(const T& key, std::vector>& ret_vec) { std::lock_guard lock(mutx); auto mysdb = stats_db.find(key); if (mysdb == stats_db.end()) { return false; } else { auto& myfm = mysdb->second.second; // go through all the fields, get them and add to a vector for (auto it = myfm.begin(); it != myfm.end(); ++it) { ret_vec.push_back(make_pair(it->first, it->second->sum())); } } return true; } template bool TWStatsDB::get_all_fields_windows(const T& key, TWStatsDBEntry& ret_vec) const { std::lock_guard lock(mutx); return get_all_fields_windows_unsafe(key, ret_vec); } template bool TWStatsDB::get_all_fields_windows_unsafe(const T& key, TWStatsDBEntry& ret_vec) const { auto mysdb = stats_db.find(key); if (mysdb == stats_db.end()) { return false; } else { auto& myfm = mysdb->second.second; // go through all the fields, get them and add to a vector for (auto fit = myfm.begin(); fit != myfm.end(); ++fit) { std::vector ivec; fit->second->get_windows(ivec); ret_vec.push_back(make_pair(fit->first, ivec)); } } return true; } template void TWStatsDB::reset(const T& key) { std::lock_guard lock(mutx); auto mysdb = stats_db.find(key); if (mysdb != stats_db.end()) { auto& myfm = mysdb->second.second; // go through all the fields and reset them for (auto it = myfm.begin(); it != myfm.end(); ++it) { it->second->reset(); } } } template void TWStatsDB::reset_field(const T& key, const std::string& field_name) { std::lock_guard lock(mutx); auto mysdb = stats_db.find(key); if (mysdb != stats_db.end()) { auto myfm = mysdb->second.second.find(field_name); if (myfm != mysdb->second.second.end()) { // Reset that field myfm->second->reset(); } } } template void TWStatsDB::set_map_size_soft(unsigned int size) { std::lock_guard lock(mutx); map_size_soft = size; stats_db.reserve(size); } template unsigned int TWStatsDB::get_size() { std::lock_guard lock(mutx); return stats_db.size(); } template unsigned int TWStatsDB::get_max_size() { std::lock_guard lock(mutx); return map_size_soft; } weakforced-2.10.2/common/webhook.cc000066400000000000000000000176661461473602600171740ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include "webhook.hh" #include "dolog.hh" #include "hmac.hh" #include "base64.hh" #include "ext/threadname.hh" using namespace boost::posix_time; using namespace boost::gregorian; WebHookRunner::WebHookRunner() { } void WebHookRunner::setNumThreads(unsigned int num_threads) { this->num_threads = num_threads; } void WebHookRunner::startThreads() { for (size_t i=0; i hook, std::string error_msg) { MiniCurlMulti mcm; _addHook("ping", hook, std::string(), mcm); std::vector wqi = { {std::string("ping"), hook, std::make_shared(std::string())} }; return _runHooks(wqi, mcm); } // asynchronously run the hook with the supplied data void WebHookRunner::runHook(const std::string& event_name, std::shared_ptr hook, const std::string& hook_data) { std::string err_msg; vdebuglog("runHook: event %s hook id %d", event_name, hook->getID()); if (!hook->validateConfig(err_msg)) { errlog("runHook: Error validating configuration of webhook id=%d for event (%s) [%s]", hook->getID(), event_name, err_msg); } else { WebHookQueueItem wqi = { event_name, hook, std::make_shared(hook_data) }; { std::lock_guard lock(queue_mutex); if (queue.size() >= max_queue_size) { errlog("runHook: Webhook queue at max size (%d) - dropping webhook id=%d for event (%s)", max_queue_size, hook->getID(), event_name); return; } else { queue.push(wqi); } setPrometheusWebhookQueueSize(queue.size()); } cv.notify_one(); } } void WebHookRunner::runHook(const std::string& event_name, std::shared_ptr hook, const json11::Json& json_data) { if (hook->getConfigKey("kafka") == "true") { json11::Json kobj = json11::Json::object{{"records", json11::Json(json11::Json::array{json11::Json(json11::Json::object{{"value", json_data}})})}}; runHook(event_name, hook, kobj.dump()); } else { runHook(event_name, hook, json_data.dump()); } } void WebHookRunner::_runHookThread(unsigned int num_conns) { setThreadName("wf/wh-runhook"); MiniCurlMulti mcm(num_conns); mcm.setTimeout(timeout_secs); mcm.setMCurlOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L); mcm.setMCurlOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1L : 0L); if (caCertBundleFile.length() != 0) mcm.setMCurlOption(CURLOPT_CAINFO, caCertBundleFile.c_str()); if (clientCertFile.length() != 0) mcm.setMCurlOption(CURLOPT_SSLCERT, clientCertFile.c_str()); if (clientKeyFile.length() != 0) mcm.setMCurlOption(CURLOPT_SSLKEY, clientKeyFile.c_str()); while (true) { std::vector events; { std::unique_lock lock(queue_mutex); while (queue.size() == 0) { cv.wait(lock); } for (unsigned int i=0; ievent_name, i->hook, *(i->hook_data_p), mcm); } _runHooks(events, mcm); } } bool WebHookRunner::_runHooks(const std::vector& events, MiniCurlMulti& mcurl) { bool ret = true; const auto& retvec = mcurl.runPost(); for (auto i = retvec.begin(); i!=retvec.end(); ++i) { std::shared_ptr hook = nullptr; for (auto j = events.begin(); j != events.end(); ++j) { if (i->id == j->hook->getID()) { hook = j->hook; break; } } if (hook != nullptr) { if (i->ret != true) { errlog("Webhook id=%d failed to url (%s): [%s]", i->id, hook->getConfigKey("url"), i->error_msg); hook->incFailed(); ret = false; } else { vinfolog("Webhook id=%d succeeded to url (%s)", i->id, hook->getConfigKey("url")); hook->incSuccess(); } } } return ret; } // This method expands all supported date macros in a url string std::string WebHookRunner::ExpandURL(const std::string&& url) { std::string new_url = url; // This is equivalent to a std::move() thread_local boost::format yf("%1.4d"); thread_local boost::format mf("%1.2d"); thread_local boost::format df("%1.2d"); date::ymd_type ymd = boost::posix_time::second_clock::universal_time().date().year_month_day(); boost::replace_all(new_url, "%{YYYY}", (yf % ymd.year).str()); boost::replace_all(new_url, "%{MM}", (mf % ymd.month.as_number()).str()); boost::replace_all(new_url, "%{dd}", (df % ymd.day.as_number()).str()); return new_url; } // This method (less efficiently) expands all supported date macros in a url string std::string WebHookRunner::ExpandURL(const std::string& url) { return ExpandURL(std::string(url)); } void WebHookRunner::_addHook(const std::string& event_name, std::shared_ptr hook, const std::string& hook_data, MiniCurlMulti& mcurl) { // construct the necessary headers MiniCurlHeaders mch; mch.insert(std::make_pair("X-Wforce-Event", event_name)); if (hook->hasConfigKey("content-type")) { mch.insert(std::make_pair("Content-Type", hook->getConfigKey("content-type"))); } else { mch.insert(std::make_pair("Content-Type", "application/json")); } mch.insert(std::make_pair("X-Wforce-HookID", std::to_string(hook->getID()))); if (hook->hasConfigKey("secret")) mch.insert(std::make_pair("X-Wforce-Signature", Base64Encode(calculateHMAC(hook->getConfigKey("secret"), hook_data, HashAlgo::SHA256)))); ptime t(microsec_clock::universal_time()); std::string b64_hash_id = Base64Encode(calculateHash(to_simple_string(t)+std::to_string(hook->getID())+event_name, HashAlgo::SHA256)); mch.insert(std::make_pair("X-Wforce-Delivery", b64_hash_id)); if (hook->hasConfigKey("basic-auth")) { mch.insert(std::make_pair("Authorization", "Basic " + Base64Encode(hook->getConfigKey("basic-auth")))); } if (hook->hasConfigKey("api-key")) { mch.insert(std::make_pair("X-API-Key", hook->getConfigKey("api-key"))); } vdebuglog("Webhook id=%d starting for event (%s) to url (%s) with delivery id (%s) and hook_data (%s)", hook->getID(), event_name, ExpandURL(hook->getConfigKey("url")), b64_hash_id, hook_data); mcurl.addPost(hook->getID(), ExpandURL(hook->getConfigKey("url")), hook_data, mch); } weakforced-2.10.2/common/webhook.hh000066400000000000000000000353201461473602600171710ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include "json11.hpp" #include "dolog.hh" #include "minicurl.hh" #include "prometheus.hh" #include using WHConfigMap = std::map; using WHEvents = std::vector; // Key = event name, value is a pair of config key vectors - // first is mandatory, second is optional using WHEventConfig = std::pair, std::vector>; using WHEventTypes = std::map; class WebHook { public: WebHook(unsigned int wh_id, const WHEvents& wh_events, bool wh_active, const WHConfigMap& wh_config_keys) : id(wh_id), active(wh_active), config_keys(wh_config_keys) { addEvents(wh_events); } WebHook(unsigned int wh_id, bool wh_active, const WHConfigMap& wh_config_keys, const std::string& wh_name) : id(wh_id), active(wh_active), config_keys(wh_config_keys), name(wh_name) { } virtual ~WebHook() {} virtual bool operator==(const WebHook& rhs) { return id == rhs.getID(); } unsigned int getID() const { return id; } const std::string getName() const { return name; } bool hasName() const { if (name.compare("") == 0) return false; else return true; } WHEvents getEventNames() const { WHEvents ret_events; for (auto& i : event_names) { ret_events.push_back(i.first); } return ret_events; } bool validEventName(const std::string& event_name) const { for (auto& i : event_names) { if (i.first.compare(event_name) == 0) return true; } return false; } const WHEvents& getEvents() const { std::lock_guard lock(mutex); return events; } std::string getEventsStr() const { std::lock_guard lock(mutex); std::stringstream ss; for (auto& i : events) { ss << i << " "; } return ss.str(); } const WHEvents& setEvents(const WHEvents& new_events) { std::lock_guard lock(mutex); events = new_events; return events; } const WHEvents& addEvents(const WHEvents& new_events) { for (auto& i : new_events) { addEvent(i); } return events; } const WHEvents& addEvent(const std::string& event_name) { std::lock_guard lock(mutex); if (validEventName(event_name)) { bool duplicate = false; for (auto& i : events) { if (i.compare(event_name) == 0) { duplicate = true; break; } } if (!duplicate) events.push_back(event_name); } return events; } const WHEvents& deleteEvents(const WHEvents& new_events) { for (auto& i : new_events) { deleteEvent(i); } return events; } const WHEvents& deleteEvent(const std::string& event_name) { std::lock_guard lock(mutex); for (auto i=events.begin(); i!=events.end(); ) { if (i->compare(event_name) == 0) { i = events.erase(i); break; } else ++i; } return events; } // returns true if config is OK virtual bool validateConfig(std::string& error_msg) const { std::lock_guard lock(mutex); if (event_names.begin() == event_names.end()) { error_msg = "No events registered"; return false; } // go through the configured events and ensure we have the right // mandatory config keys for (auto& ev : event_names ) { std::vector man_cfg = ev.second.first; for (auto& cf : man_cfg) { if (config_keys.find(cf) == config_keys.end()) { error_msg = "Missing mandatory configuration key: " + cf; return false; } } } return true; } const WHEventConfig& getConfigForEvent(std::string event_name) const { std::lock_guard lock(mutex); static const WHEventConfig null_config; if (validEventName(event_name)) { for (auto& i : event_names) { if (i.first.compare(event_name) == 0) return i.second; } } return null_config; } const WHConfigMap& getConfigKeys() const { std::lock_guard lock(mutex); return config_keys; } bool hasConfigKey(const std::string& key) const { std::lock_guard lock(mutex); auto i = config_keys.find(key); if (i != config_keys.end()) return true; return false; } std::string getConfigKey(const std::string& key) const { std::lock_guard lock(mutex); auto i = config_keys.find(key); if (i != config_keys.end()) return i->second; else return ""; } const WHConfigMap& setConfigKeys(const WHConfigMap& new_cfg) { std::lock_guard lock(mutex); config_keys = new_cfg; return config_keys; } const WHConfigMap& addConfigKeys(const WHConfigMap& new_cfg) { std::lock_guard lock(mutex); for (const auto& i : new_cfg) { config_keys.insert(std::make_pair(i.first, i.second)); } return config_keys; } const WHConfigMap& addConfigKey(const std::string& key, const std::string& val) { std::lock_guard lock(mutex); config_keys.insert(std::make_pair(key, val)); return config_keys; } const WHConfigMap& deleteConfigKey(const std::string& key) { std::lock_guard lock(mutex); config_keys.erase(key); return config_keys; } bool isActive() const { std::lock_guard lock(mutex); return active; } void setActive(bool isActive) { std::lock_guard lock(mutex); active = isActive; } void toString(std::string& out) const { std::lock_guard lock(mutex); json11::Json my_object = to_json(); my_object.dump(out); } std::string toString() const { std::string out; toString(out); return out; } virtual json11::Json to_json() const { std::lock_guard lock(mutex); json11::Json::array jevents; json11::Json my_object = json11::Json::object { { "id", (int)id }, { "events", events }, { "config", config_keys } }; return my_object; } virtual void incSuccess() const { num_success++; incPrometheusWebhookMetric(id, true, false); } unsigned int getSuccess() const { return num_success; } virtual void incFailed() const { num_failed++; incPrometheusWebhookMetric(id, false, false); } unsigned int getFailed() const { return num_failed; } protected: unsigned int id; std::vector events; bool active; WHConfigMap config_keys; mutable std::atomic num_success{0}; mutable std::atomic num_failed{0}; mutable std::mutex mutex; const WHEventTypes event_names = { { "report", {{ "url" }, {"secret", "basic-auth"}}}, { "allow", {{ "url" }, {"secret", "allow_filter", "basic-auth"}}}, { "reset", {{ "url" }, {"secret", "basic-auth"}}}, { "addbl", {{ "url" }, {"secret", "basic-auth"}}}, { "delbl", {{ "url" }, {"secret", "basic-auth"}}}, { "expirebl", {{ "url" }, {"secret", "basic-auth"}}}, { "addwl", {{ "url" }, {"secret", "basic-auth"}}}, { "delwl", {{ "url" }, {"secret", "basic-auth"}}}, { "expirewl", {{ "url" }, {"secret", "basic-auth"}}} }; const std::string name; }; class CustomWebHook : public WebHook { public: CustomWebHook(unsigned int wh_id, const std::string& wh_name, bool wh_active, const WHConfigMap& wh_config_keys) : WebHook(wh_id, wh_active, wh_config_keys, wh_name) { } ~CustomWebHook() {} bool operator==(const WebHook& rhs) override { return ((id == rhs.getID()) && (name.compare(rhs.getName()) == 0)); } // returns true if config is OK bool validateConfig(std::string& error_msg) const override { std::lock_guard lock(mutex); if (config_keys.find("url") == config_keys.end()) { error_msg = "Missing mandatory configuration key: url"; return false; } return true; } void incSuccess() const override { num_success++; incPrometheusWebhookMetric(id, true, true); } void incFailed() const override { num_failed++; incPrometheusWebhookMetric(id, false, true); } json11::Json to_json() const override { std::lock_guard lock(mutex); json11::Json::array jevents; json11::Json my_object = json11::Json::object { { "id", (int)id }, { "name", name }, { "config", config_keys } }; return my_object; } }; class WebHookDB { public: WebHookDB():id(0) { } unsigned int getNewID() const { return ++id; } bool addWebHook(const WebHook& hook, std::string&error_msg) { std::lock_guard lock(mutex); bool retval = true; if (hook.validateConfig(error_msg)) { for (const auto& i : webhooks) { if (i->getID() == hook.getID()) { error_msg = "Registering webhook failed: id=" + std::to_string(hook.getID()) + " is already registered"; retval = false; } } if (retval == true) { if (hook.hasName()) { infolog("Registering custom webhook id=%d name=%s to url (%s)", hook.getID(), hook.getName(), hook.getConfigKey("url")); webhooks.push_back(std::make_shared(hook.getID(), hook.getName(), hook.isActive(), hook.getConfigKeys())); addPrometheusWebhookMetric(id, hook.getConfigKey("url"), true); } else { infolog("Registering webhook id=%d for events [%s] to url (%s)", hook.getID(), hook.getEventsStr(), hook.getConfigKey("url")); webhooks.push_back(std::make_shared(hook.getID(), hook.getEvents(), hook.isActive(), hook.getConfigKeys())); addPrometheusWebhookMetric(id, hook.getConfigKey("url"), false); } } } return retval; } bool deleteWebHook(unsigned int wh_id) { std::lock_guard lock(mutex); bool retval = false; for (auto i = webhooks.begin(); i!=webhooks.end(); ) { if ((*i)->getID() == wh_id) { i = webhooks.erase(i); retval = true; break; } else ++i; } return retval; } std::weak_ptr getWebHook(unsigned int wh_id) { std::lock_guard lock(mutex); for (auto i = webhooks.begin(); i!=webhooks.end(); ++i) { if ((*i)->getID() == wh_id) return *i; } return std::weak_ptr(); } std::weak_ptr getWebHook(const std::string& wh_name) { std::lock_guard lock(mutex); for (auto i = webhooks.begin(); i!=webhooks.end(); ++i) { if ((*i)->getName().compare(wh_name) == 0) return *i; } return std::weak_ptr(); } const std::vector> getWebHooks() const { std::lock_guard lock(mutex); std::vector> retvec; for (const auto& i : webhooks) retvec.push_back(i); return retvec; } const std::vector> getWebHooksForEvent(const std::string& event_name) const { std::lock_guard lock(mutex); std::vector> retvec; for (const auto& i : webhooks) { for (const auto& ev : i->getEvents()) { if (ev.compare(event_name) == 0) { retvec.push_back(i); } } } return retvec; } void toString(std::string& out) const { std::lock_guard lock(mutex); json11::Json::array jarray; for (auto& i : webhooks) { jarray.push_back(i->to_json()); } json11::Json my_array = json11::Json(jarray); my_array.dump(out); } std::string toString() const { std::string out; toString(out); return out; } private: mutable std::atomic id; std::vector> webhooks; mutable std::mutex mutex; }; struct CurlConnection { CurlConnection() { } std::mutex cmutex; MiniCurlMulti mcurl; }; using CurlConns = std::vector>; #define MAX_HOOK_CONN 10 #define NUM_WEBHOOK_THREADS 5 #define QUEUE_SIZE 50000 #define DEFAULT_TIMEOUT_SECS 2 struct WebHookQueueItem { std::string event_name; std::shared_ptr hook; std::shared_ptr hook_data_p; }; class WebHookRunner { public: WebHookRunner(); void setNumThreads(unsigned int new_num_threads); void setMaxConns(unsigned int max_conns); void setMaxQueueSize(unsigned int max_queue); void setTimeout(uint64_t timeout_seconds); void disablePeerVerification() { verify_peer = false; } void disableHostVerification() { verify_host = false; } void setCACertBundleFile(const std::string& file) { caCertBundleFile = file; } void setClientCertAndKey(const std::string& certfile, const std::string& keyfile) { clientCertFile = certfile; clientKeyFile = keyfile; } // synchronously run the ping command for the hook bool pingHook(std::shared_ptr hook, std::string error_msg); // asynchronously run the hook with the supplied data void runHook(const std::string& event_name, std::shared_ptr hook, const std::string& hook_data); void runHook(const std::string& event_name, std::shared_ptr hook, const json11::Json& json_data); void startThreads(); protected: void _runHookThread(unsigned int num_conns); void _addHook(const std::string& event_name, std::shared_ptr hook, const std::string& hook_data, MiniCurlMulti& mcurl); bool _runHooks(const std::vector& events, MiniCurlMulti& mcurl); std::string ExpandURL(const std::string&& url); std::string ExpandURL(const std::string& url); private: std::queue queue; std::mutex queue_mutex; std::condition_variable cv; unsigned int max_queue_size = QUEUE_SIZE; unsigned int max_hook_conns = MAX_HOOK_CONN; unsigned int num_threads = NUM_WEBHOOK_THREADS; uint64_t timeout_secs = DEFAULT_TIMEOUT_SECS; bool verify_peer = true; bool verify_host = true; std::string caCertBundleFile; std::string clientCertFile; std::string clientKeyFile; }; weakforced-2.10.2/common/wforce-geoip.cc000066400000000000000000000125301461473602600201050ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #ifdef HAVE_GEOIP #include "wforce-geoip.hh" #include "wforce_exception.hh" #include "dolog.hh" WFGeoIPDB g_wfgeodb; void WFGeoIPDB::initGeoIPDB(WFGeoIPDBType db_types) { if (db_types & WFGeoIPDBType::GEOIP_COUNTRY) { if (!gi_v4) gi_v4 = openGeoIPDB(GEOIP_COUNTRY_EDITION, "v4 country"); } if (db_types & WFGeoIPDBType::GEOIP_COUNTRY_V6) { if (!gi_v6) gi_v6 = openGeoIPDB(GEOIP_COUNTRY_EDITION_V6, "v6 country"); } if (db_types & WFGeoIPDBType::GEOIP_CITY) { if (!gi_city_v4) gi_city_v4 = openGeoIPDB(GEOIP_CITY_EDITION_REV1, "v4 city"); } if (db_types & WFGeoIPDBType::GEOIP_CITY_V6) { if (!gi_city_v6) gi_city_v6 = openGeoIPDB(GEOIP_CITY_EDITION_REV1_V6, "v6 city"); } if (db_types & WFGeoIPDBType::GEOIP_ISP) { if (!gi_isp_v4) gi_isp_v4 = openGeoIPDB(GEOIP_ISP_EDITION, "v4 isp"); } if (db_types & WFGeoIPDBType::GEOIP_ISP_V6) { if (!gi_isp_v6) gi_isp_v6 = openGeoIPDB(GEOIP_ISP_EDITION_V6, "v6 isp"); } } GeoIP* WFGeoIPDB::openGeoIPDB(GeoIPDBTypes db_type, const std::string& name) { GeoIP* gip=NULL; if (GeoIP_db_avail(db_type)) { gip = GeoIP_open_type(db_type, GEOIP_MEMORY_CACHE); if (!gip) { std::string myerr = "Unable to open geoip " + name + " db"; errlog(myerr.c_str()); throw WforceException(myerr); } } else { std::string myerr = "No geoip " + name + " db available"; errlog(myerr.c_str()); throw WforceException(myerr); } return gip; } void WFGeoIPDB::reload() { WriteLock wl(&gi_rwlock); if (gi_v4) { GeoIP_delete(gi_v4); gi_v4 = NULL; gi_v4 = openGeoIPDB(GEOIP_COUNTRY_EDITION, "v4 country"); } if (gi_v6) { GeoIP_delete(gi_v6); gi_v6 = NULL; gi_v6 = openGeoIPDB(GEOIP_COUNTRY_EDITION_V6, "v6 country"); } if (gi_city_v4) { GeoIP_delete(gi_city_v4); gi_city_v4 = NULL; gi_city_v4 = openGeoIPDB(GEOIP_CITY_EDITION_REV1, "v4 city"); } if (gi_city_v6) { GeoIP_delete(gi_city_v6); gi_city_v6 = NULL; gi_city_v6 = openGeoIPDB(GEOIP_CITY_EDITION_REV1_V6, "v6 city"); } if (gi_isp_v4) { GeoIP_delete(gi_isp_v4); gi_isp_v4 = NULL; gi_isp_v4 = openGeoIPDB(GEOIP_ISP_EDITION, "v4 isp"); } if (gi_isp_v6) { GeoIP_delete(gi_isp_v6); gi_isp_v6 = NULL; gi_isp_v6 = openGeoIPDB(GEOIP_ISP_EDITION_V6, "v4 isp"); } } std::string WFGeoIPDB::lookupCountry(const ComboAddress& address) const { GeoIPLookup gl; const char* retstr=NULL; std::string ret=""; ReadLock rl(&gi_rwlock); if (address.sin4.sin_family == AF_INET && gi_v4 != NULL) { retstr = GeoIP_country_code_by_ipnum_gl(gi_v4, ntohl(address.sin4.sin_addr.s_addr), &gl); } else if (gi_v6 != NULL) { // it's a v6 address (included mapped v4 of course) retstr = GeoIP_country_code_by_ipnum_v6_gl(gi_v6, address.sin6.sin6_addr, &gl); } if (retstr) ret = retstr; return ret; } std::string WFGeoIPDB::lookupISP(const ComboAddress& address) const { GeoIPLookup gl; const char* retstr=NULL; std::string ret=""; ReadLock rl(&gi_rwlock); if (address.sin4.sin_family == AF_INET && gi_isp_v4 != NULL) { retstr = GeoIP_name_by_ipnum_gl(gi_isp_v4, ntohl(address.sin4.sin_addr.s_addr), &gl); } else if (gi_isp_v6 != NULL) { // v6 address retstr = GeoIP_name_by_ipnum_v6_gl(gi_isp_v6, address.sin6.sin6_addr, &gl); } if (retstr) ret = retstr; return ret; } WFGeoIPRecord WFGeoIPDB::lookupCity(const ComboAddress& address) const { GeoIPRecord* gir=NULL; WFGeoIPRecord ret_wfgir = {}; ReadLock rl(&gi_rwlock); if (address.sin4.sin_family == AF_INET && gi_city_v4 != NULL) { gir = GeoIP_record_by_ipnum(gi_city_v4, ntohl(address.sin4.sin_addr.s_addr)); } else if (gi_city_v6 != NULL) { // v6 address gir = GeoIP_record_by_ipnum_v6(gi_city_v6, address.sin6.sin6_addr); } if (gir) { if (gir->country_code != NULL) ret_wfgir.country_code = gir->country_code; if (gir->country_name != NULL) ret_wfgir.country_name = gir->country_name; if (gir->region != NULL) ret_wfgir.region = gir->region; if (gir->city != NULL) ret_wfgir.city = gir->city; if (gir->postal_code != NULL) ret_wfgir.postal_code = gir->postal_code; if (gir->continent_code != NULL) ret_wfgir.continent_code = gir->continent_code; ret_wfgir.latitude = gir->latitude; ret_wfgir.longitude = gir->longitude; GeoIPRecord_delete(gir); } return ret_wfgir; } #endif // HAVE_GEOIP weakforced-2.10.2/common/wforce-geoip.hh000066400000000000000000000061651461473602600201260ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #ifdef HAVE_GEOIP #include #include #endif #include "lock.hh" #include "iputils.hh" enum class WFGeoIPDBType : uint32_t { GEOIP_NONE=0x00, GEOIP_COUNTRY=0x01, GEOIP_CITY=0x02, GEOIP_ISP=0x04, GEOIP_COUNTRY_V6=0x08, GEOIP_CITY_V6=0x10, GEOIP_ISP_V6=0x20 }; constexpr enum WFGeoIPDBType operator |( const enum WFGeoIPDBType selfValue, const enum WFGeoIPDBType inValue ) { return (enum WFGeoIPDBType)(uint32_t(selfValue) | uint32_t(inValue)); } constexpr bool operator &( const enum WFGeoIPDBType selfValue, const enum WFGeoIPDBType inValue ) { return (uint32_t(selfValue) & uint32_t(inValue)); } struct WFGeoIPRecord { std::string country_code; std::string country_name; std::string region; std::string city; std::string postal_code; std::string continent_code; float latitude; float longitude; }; #ifdef HAVE_GEOIP class WFGeoIPDB { public: WFGeoIPDB() { } ~WFGeoIPDB() { if (gi_v4) { GeoIP_delete(gi_v4); } if (gi_v6) { GeoIP_delete(gi_v6); } if (gi_city_v4) { GeoIP_delete(gi_city_v4); } if (gi_city_v6) { GeoIP_delete(gi_city_v6); } if (gi_isp_v4) { GeoIP_delete(gi_isp_v4); } if (gi_isp_v6) { GeoIP_delete(gi_isp_v6); } } // Only load it if someone wants to use GeoIP, otherwise it's a waste of RAM void initGeoIPDB(WFGeoIPDBType db_types); // pass these as flags // This will lookup in either the v4 or v6 GeoIP DB, depending on what address is std::string lookupCountry(const ComboAddress& address) const; std::string lookupISP(const ComboAddress& address) const; WFGeoIPRecord lookupCity(const ComboAddress& address) const; void reload(); // Reload any opened DBs from original files protected: GeoIP* openGeoIPDB(GeoIPDBTypes db_type, const std::string& name); private: // GeoIPDB seems to have different DBs for v4 and v6 addresses, hence two DBs GeoIP *gi_v4 = NULL; GeoIP *gi_v6 = NULL; GeoIP *gi_city_v4 = NULL; GeoIP *gi_city_v6 = NULL; GeoIP *gi_isp_v4 = NULL; GeoIP *gi_isp_v6 = NULL; mutable pthread_rwlock_t gi_rwlock = PTHREAD_RWLOCK_INITIALIZER; }; extern WFGeoIPDB g_wfgeodb; #endif weakforced-2.10.2/common/wforce-geoip2.cc000066400000000000000000000166571461473602600202050ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #ifdef HAVE_MMDB #include "wforce-geoip2.hh" #include "wforce_exception.hh" #include "dolog.hh" #include std::mutex geoip2_mutx; std::map> geoip2Map; WFGeoIP2DB::WFGeoIP2DB(const std::string& filename) { int res; memset(&d_db, 0, sizeof(d_db)); if ((res = MMDB_open(filename.c_str(), MMDB_MODE_MMAP, &d_db)) != MMDB_SUCCESS) throw WforceException(std::string("Cannot open ") + filename + std::string(": ") + std::string(MMDB_strerror(res))); d_init = true; debuglog("Opened MMDB database %s (type: %s version: %d.%d)", filename, d_db.metadata.database_type, d_db.metadata.binary_format_major_version, d_db.metadata.binary_format_minor_version); } WFGeoIP2DB::WFGeoIP2DB() { memset(&d_db, 0, sizeof(d_db)); } WFGeoIP2DB::~WFGeoIP2DB() { MMDB_close(&d_db); } std::string WFGeoIP2DB::lookupCountry(const ComboAddress& address) { MMDB_entry_data_s data; MMDB_lookup_result_s res; if (!mmdbLookup(address.toString(), res) || (MMDB_get_value(&res.entry, &data, "country", "iso_code", NULL) != MMDB_SUCCESS) || !data.has_data) return {}; return std::string(data.utf8_string, data.data_size); } std::string WFGeoIP2DB::lookupISP(const ComboAddress& address) { MMDB_entry_data_s data; MMDB_lookup_result_s res; if (!mmdbLookup(address.toString(), res) || (MMDB_get_value(&res.entry, &data, "isp", NULL) != MMDB_SUCCESS) || !data.has_data) return {}; return std::string(data.utf8_string, data.data_size); } std::unique_ptr WFGeoIP2DB::lookupCity(const ComboAddress& address) { auto ret_wfgir = std::make_unique(); MMDB_entry_data_s data; MMDB_lookup_result_s res; if (mmdbLookup(address.toString(), res)) { if ((MMDB_get_value(&res.entry, &data, "country", "iso_code", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->country_code = std::string(data.utf8_string, data.data_size); if ((MMDB_get_value(&res.entry, &data, "country", "names", "en", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->country_name = std::string(data.utf8_string, data.data_size); if ((MMDB_get_value(&res.entry, &data, "subdivisions", "0", "iso_code", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->region = std::string(data.utf8_string, data.data_size); if ((MMDB_get_value(&res.entry, &data, "cities", "0", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->city = std::string(data.utf8_string, data.data_size); else if ((MMDB_get_value(&res.entry, &data, "city", "names", "en", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->city = std::string(data.utf8_string, data.data_size); if ((MMDB_get_value(&res.entry, &data, "continent", "code", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->continent_code = std::string(data.utf8_string, data.data_size); if ((MMDB_get_value(&res.entry, &data, "postal", "code", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->postal_code = std::string(data.utf8_string, data.data_size); if ((MMDB_get_value(&res.entry, &data, "location", "latitude", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->latitude = data.double_value; if ((MMDB_get_value(&res.entry, &data, "location", "longitude", NULL) == MMDB_SUCCESS) && (data.has_data)) ret_wfgir->longitude = data.double_value; } return ret_wfgir; } bool WFGeoIP2DB::mmdbLookup(const std::string& ip, MMDB_lookup_result_s& res) { int gai_err; int mmdb_err; if (!d_init) return false; res = MMDB_lookup_string(&d_db, ip.c_str(), &gai_err, &mmdb_err); if (gai_err != 0) { warnlog("MMDB_lookup_string(%s) failed: %s", ip, gai_strerror(gai_err)); } else if (mmdb_err != MMDB_SUCCESS) { warnlog("MMDB_lookup_string(%s) failed: %s", ip, MMDB_strerror(mmdb_err)); } else if (res.found_entry) { return true; } return false; } char* convertToCharStar(const std::pair& pair) { return strdup(pair.second.c_str()); } bool WFGeoIP2DB::lookupDataValue(const ComboAddress& address, const std::vector>& attrs, MMDB_entry_data_s& ret_data) { MMDB_lookup_result_s res; std::vector path; bool retval = false; if (mmdbLookup(address.toString(), res)) { std::transform(attrs.begin(), attrs.end(), std::back_inserter(path), convertToCharStar); path.emplace_back(nullptr); if ((MMDB_aget_value(&res.entry, &ret_data, &path[0]) == MMDB_SUCCESS) && (ret_data.has_data)) { retval = true; } for (auto& i : path) { free(i); } } return retval; } std::string WFGeoIP2DB::lookupStringValue(const ComboAddress& address, const std::vector>& attrs) { MMDB_entry_data_s data; std::string ret_str; if (lookupDataValue(address, attrs, data)) { if (data.type == MMDB_DATA_TYPE_UTF8_STRING) ret_str = std::string(data.utf8_string, data.data_size); } return ret_str; } uint64_t WFGeoIP2DB::lookupUIntValue(const ComboAddress& address, const std::vector>& attrs) { MMDB_entry_data_s data; uint64_t ret_int = 0; if (lookupDataValue(address, attrs, data)) { if (data.type == MMDB_DATA_TYPE_UINT16) { ret_int = static_cast(data.uint16); } else if (data.type == MMDB_DATA_TYPE_UINT32) { ret_int = static_cast(data.uint32); } else if (data.type == MMDB_DATA_TYPE_UINT64) { ret_int = data.uint64; } } return ret_int; } bool WFGeoIP2DB::lookupBoolValue(const ComboAddress& address, const std::vector>& attrs) { MMDB_entry_data_s data; bool ret_bool = false; if (lookupDataValue(address, attrs, data)) { if (data.type == MMDB_DATA_TYPE_BOOLEAN) { ret_bool = data.boolean; } } return ret_bool; } double WFGeoIP2DB::lookupDoubleValue(const ComboAddress& address, const std::vector>& attrs) { MMDB_entry_data_s data; double ret_double = 0; if (lookupDataValue(address, attrs, data)) { if (data.type == MMDB_DATA_TYPE_FLOAT) { ret_double = static_cast(data.float_value); } else if (data.type == MMDB_DATA_TYPE_DOUBLE) { ret_double = data.double_value; } } return ret_double; } std::shared_ptr WFGeoIP2DB::makeWFGeoIP2DB(const std::string& filename) { return std::make_shared(filename); } #endif // HAVE_MMDB weakforced-2.10.2/common/wforce-geoip2.hh000066400000000000000000000051031461473602600201770ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include "lock.hh" #include "iputils.hh" #include "wforce-geoip.hh" #ifdef HAVE_MMDB #include "maxminddb.h" class WFGeoIP2DB { public: WFGeoIP2DB(const std::string& filename); WFGeoIP2DB(); ~WFGeoIP2DB(); WFGeoIP2DB(const WFGeoIP2DB&) = delete; // copy construct WFGeoIP2DB& operator=(const WFGeoIP2DB&) = delete; // copy assign WFGeoIP2DB(WFGeoIP2DB&&) = delete; // move construct WFGeoIP2DB& operator=(WFGeoIP2DB&&) = delete; // move assign std::string lookupCountry(const ComboAddress& address); std::string lookupISP(const ComboAddress& address); std::unique_ptr lookupCity(const ComboAddress& address); std::string lookupStringValue(const ComboAddress& address, const std::vector>& attrs); uint64_t lookupUIntValue(const ComboAddress& address, const std::vector>& attrs); bool lookupBoolValue(const ComboAddress& address, const std::vector>& attrs); double lookupDoubleValue(const ComboAddress& address, const std::vector>& attrs); static std::shared_ptr makeWFGeoIP2DB(const std::string& filename); private: MMDB_s d_db; bool d_init = false; bool lookupDataValue(const ComboAddress& address, const std::vector>& attrs, MMDB_entry_data_s& ret_data); bool mmdbLookup(const std::string& ip, MMDB_lookup_result_s& res); }; extern std::mutex geoip2_mutx; extern std::map> geoip2Map; #endif // HAVE_MMDB weakforced-2.10.2/common/wforce-webserver.cc000066400000000000000000000147061461473602600210150ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "wforce-webserver.hh" #include "sstuff.hh" #include "dolog.hh" #include "perf-stats.hh" #include "base64.hh" #include "prometheus.hh" #include "drogon/drogon.h" #include using std::thread; void WforceWebserver::setNumWorkerThreads(unsigned int num_workers) { d_num_worker_threads = num_workers; } void WforceWebserver::setMaxConns(unsigned int max_conns) { drogon::app().setMaxConnectionNum(max_conns); } void WforceWebserver::setACL(const NetmaskGroup& nmg) { d_ACL.setState(nmg); } void WforceWebserver::addACL(const std::string& ip) { d_ACL.modify([ip](NetmaskGroup& nmg) { nmg.addMask(ip); }); } NetmaskGroup WforceWebserver::getACL() { return d_ACL.getCopy(); } size_t WforceWebserver::getNumConns() { return 0; // XXX - investigate if this can be retrieved from drogon } bool WforceWebserver::registerFunc(const std::string& command, HTTPVerb verb, const WforceWSFunc& wsf) { bool retval = true; drogon::HttpMethod method = drogon::Get; switch (verb) { case HTTPVerb::GET: { method = drogon::Get; auto f = d_get_map.find(command); if (f == d_get_map.end()) { d_get_map.insert(std::make_pair(command, wsf)); retval = true; } break; } case HTTPVerb::POST: { method = drogon::Post; auto f = d_post_map.find(command); if (f == d_post_map.end()) { d_post_map.insert(std::make_pair(command, wsf)); retval = true; } break; } case HTTPVerb::PUT: { method = drogon::Put; auto f = d_put_map.find(command); if (f == d_put_map.end()) { d_put_map.insert(std::make_pair(command, wsf)); retval = true; } break; } case HTTPVerb::DELETE: { method = drogon::Delete; auto f = d_delete_map.find(command); if (f == d_delete_map.end()) { d_delete_map.insert(std::make_pair(command, wsf)); retval = true; } break; } default: retval = false; break; } if (retval) { std::string command_str = "/command/" + command; drogon::app().registerHandlerViaRegex(command_str, [this, wsf, command](const drogon::HttpRequestPtr& req, std::function && callback) { auto start_time = std::chrono::steady_clock::now(); auto resp = drogon::HttpResponse::newHttpResponse(); resp->setContentTypeCode(wsf.d_ret_content_type); wsf.d_func_ptr(req, command, resp); callback(resp); updateWTR(start_time); }, {method, "ACLFilter", "LoginFilter"}); } return retval; } void WforceWebserver::addListener(const std::string& ip, unsigned int port, bool use_ssl, const std::string& cert_file, const std::string& private_key, bool enable_oldtls, std::vector> opts) { infolog("Adding webserver listener on %s:%d with SSL=%d, cert_file=%s, key_file=%s", ip, port, use_ssl, cert_file, private_key); drogon::app().addListener(ip, port, use_ssl, cert_file, private_key, enable_oldtls, opts); } bool LoginFilter::compareAuthorization(const std::string& auth_header, const std::string& expected_password) { // validate password bool auth_ok = false; if (toLower(auth_header).find("basic ") == 0) { string cookie = auth_header.substr(6); string plain; B64Decode(cookie, plain); vector cparts; stringtok(cparts, plain, ":"); // this gets rid of terminating zeros auth_ok = (cparts.size() == 2 && (0 == strcmp(cparts[1].c_str(), expected_password.c_str()))); } return auth_ok; } void LoginFilter::doFilter(const drogon::HttpRequestPtr& req, drogon::FilterCallback&& fcb, drogon::FilterChainCallback&& fccb) { std::string auth_header = req->getHeader("authorization"); if (auth_header != "") { if (compareAuthorization(auth_header, d_password)) { fccb(); return; } } errlog("WforceWebserver: HTTP(S) Request \"%s\" from %s: Web Authentication failed", req->getPath(), req->getPeerAddr().toIpPort()); auto res = drogon::HttpResponse::newHttpResponse(); res->setStatusCode(drogon::k401Unauthorized); std::stringstream ss; ss << "{\"status\":\"failure\", \"reason\":" << "\"Unauthorized\"" << "}"; res->setBody(ss.str()); res->addHeader("WWW-Authenticate", "basic realm=\"wforce\""); fcb(res); } void ACLFilter::doFilter(const drogon::HttpRequestPtr& req, drogon::FilterCallback&& fcb, drogon::FilterChainCallback&& fccb) { auto peer_addr = req->getPeerAddr(); if (d_ACL->match(ComboAddress(peer_addr.getSockAddr(), sizeof(struct sockaddr_in6)))) { fccb(); return; } else { auto res = drogon::HttpResponse::newHttpResponse(); res->setStatusCode(drogon::k401Unauthorized); std::stringstream ss; ss << "{\"status\":\"failure\", \"reason\":" << "\"Source IP Address not in ACL\"" << ", \"ip\":\"" << peer_addr.toIp() << "\"" << "}"; res->setBody(ss.str()); res->setCloseConnection(true); fcb(res); } }weakforced-2.10.2/common/wforce-webserver.hh000066400000000000000000000270151461473602600210240ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include #include #include #include #include #include #include "iputils.hh" #include "sstuff.hh" #include "sholder.hh" #include "drogon/drogon.h" #include "prometheus.hh" #include "perf-stats.hh" using WforceWSFuncPtr = void (*)(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp); struct WforceWSFunc { WforceWSFunc() = delete; explicit WforceWSFunc(WforceWSFuncPtr func) : d_func_ptr(func), d_ret_content_type(drogon::CT_APPLICATION_JSON) {} WforceWSFunc(WforceWSFuncPtr func, drogon::ContentType ct) : d_func_ptr(func), d_ret_content_type(ct) {} WforceWSFuncPtr d_func_ptr; drogon::ContentType d_ret_content_type; }; enum class HTTPVerb { GET, POST, PUT, DELETE }; #define WFORCE_NUM_WORKER_THREADS 4 #define WFORCE_MAX_WS_CONNS 10000 class LoginFilter : public drogon::HttpFilter { public: explicit LoginFilter(const std::string& pass) : d_password(pass) {} virtual void doFilter(const drogon::HttpRequestPtr& req, drogon::FilterCallback&& fcb, drogon::FilterChainCallback&& fccb) override; protected: bool compareAuthorization(const std::string& auth_header, const std::string& expected_password); private: const std::string d_password; }; class ACLFilter : public drogon::HttpFilter { public: explicit ACLFilter(GlobalStateHolder& acl) : d_ACL(acl.getLocal()) {} virtual void doFilter(const drogon::HttpRequestPtr& req, drogon::FilterCallback&& fcb, drogon::FilterChainCallback&& fccb) override; private: LocalStateHolder d_ACL; }; class WforceWebserver { public: WforceWebserver() = default; // detect attempts to copy at compile time WforceWebserver(const WforceWebserver&) = delete; WforceWebserver& operator=(const WforceWebserver&) = delete; // timeout for poll() void setNumWorkerThreads(unsigned int num_workers); void setMaxConns(unsigned int max_conns); // set the ACLs void setACL(const NetmaskGroup& nmg); void addACL(const std::string& ip); NetmaskGroup getACL(); size_t getNumConns(); // set the basic-auth password void setBasicAuthPassword(const std::string& password) { d_password = password; } void setMetricsNoPassword() { d_metricsNoPassword = true; } // Register functions to parse commands bool registerFunc(const std::string& command, HTTPVerb verb, const WforceWSFunc& func); void addSimpleListener(const std::string& ip, unsigned int port) { infolog("Adding webserver listener on %s:%d", ip, port); drogon::app().addListener(ip, port); } void addListener(const std::string& ip, unsigned int port, bool use_ssl, const std::string& cert_file, const std::string& private_key, bool enable_oldtls, std::vector> opts); void setWebLogLevel(LogLevel level) { switch (level) { case LogLevel::Emerg: case LogLevel::Alert: case LogLevel::Crit: d_loglevel = trantor::Logger::kFatal; break; case LogLevel::Err: d_loglevel = trantor::Logger::kError; break; case LogLevel::Warning: d_loglevel = trantor::Logger::kWarn; break; case LogLevel::Notice: case LogLevel::Info: d_loglevel = trantor::Logger::kInfo; break; case LogLevel::Debug: d_loglevel = trantor::Logger::kTrace; break; default: d_loglevel = trantor::Logger::kWarn; break; } } // Initialize the webserver void init() { static std::atomic_flag init = false; if (init.test_and_set() == false) { drogon::app().disableSession(); drogon::app().disableSigtermHandling(); drogon::app().setLogLevel(d_loglevel); // We will never allow uploads, but drogon wants to create a bunch of temp files in uploadPath/tmp/xx drogon::app().setUploadPath("/tmp/wforce"); // Set custom 404 response auto resp = drogon::HttpResponse::newHttpResponse(); resp->setBody(R"({"status":"failure", "reason":"Not found"})"); drogon::app().setCustom404Page(resp, true); // register ACLFilter drogon::app().registerFilter(std::make_shared(d_ACL)); // register LoginFilter drogon::app().registerFilter(std::make_shared(d_password)); // register prometheus metrics handler if (d_metricsNoPassword) { drogon::app().registerHandler("/metrics", [](const drogon::HttpRequestPtr& req, std::function&& callback) { auto res = drogon::HttpResponse::newHttpResponse(); res->setBody(serializePrometheusMetrics()); res->setContentTypeCode(drogon::CT_TEXT_PLAIN); res->setStatusCode(drogon::k200OK); callback(res); }, {drogon::Get, "ACLFilter"}); } else { drogon::app().registerHandler("/metrics", [](const drogon::HttpRequestPtr& req, std::function&& callback) { auto res = drogon::HttpResponse::newHttpResponse(); res->setBody(serializePrometheusMetrics()); res->setContentTypeCode(drogon::CT_TEXT_PLAIN); res->setStatusCode(drogon::k200OK); callback(res); }, {drogon::Get, "ACLFilter", "LoginFilter"}); } drogon::app().setThreadNum(d_num_worker_threads); drogon::app().setMaxConnectionNum(d_max_conns); // register handlers for old-style /?command= paths auto handler_block = [this](const drogon::HttpRequestPtr& req, std::function&& callback, const std::string& command, std::unordered_map& map) { auto start_time = std::chrono::steady_clock::now(); auto resp = drogon::HttpResponse::newHttpResponse(); const auto& f = map.find(command); if (f != map.end()) { resp->setContentTypeCode(f->second.d_ret_content_type); f->second.d_func_ptr(req, command, resp); } else { resp->setStatusCode(drogon::k404NotFound); resp->setBody(R"({"status":"failure", "reason":"Not found"})"); } callback(resp); updateWTR(start_time); }; drogon::app().registerHandler("/?command={command}", [this, handler_block](const drogon::HttpRequestPtr& req, std::function&& callback, const std::string& command) { handler_block(req, std::forward>(callback), command, d_get_map); }, {drogon::Get, "ACLFilter", "LoginFilter"}); drogon::app().registerHandler("/?command={command}", [this, handler_block](const drogon::HttpRequestPtr& req, std::function&& callback, const std::string& command) { handler_block(req, std::forward>(callback), command, d_post_map); }, {drogon::Post, "ACLFilter", "LoginFilter"}); drogon::app().registerHandler("/?command={command}", [this, handler_block](const drogon::HttpRequestPtr& req, std::function&& callback, const std::string& command) { handler_block(req, std::forward>(callback), command, d_put_map); }, {drogon::Put, "ACLFilter", "LoginFilter"}); drogon::app().registerHandler("/?command={command}", [this, handler_block](const drogon::HttpRequestPtr& req, std::function&& callback, const std::string& command) { handler_block(req, std::forward>(callback), command, d_delete_map); }, {drogon::Delete, "ACLFilter", "LoginFilter"}); } } static void start(WforceWebserver* wws) { static std::atomic_flag init = false; if (init.test_and_set() == false) { infolog("Starting webserver"); wws->init(); drogon::app().run(); } } bool isRunning() { return drogon::app().isRunning(); } protected: template void updateWTR(const std::chrono::time_point& start_time) { auto end_time = std::chrono::steady_clock::now(); auto run_time = end_time - start_time; auto i_millis = std::chrono::duration_cast(run_time); addWTRStat(i_millis.count()); observePrometheusWRD(std::chrono::duration(run_time).count()); } private: GlobalStateHolder d_ACL; std::string d_password; std::unordered_map d_get_map; std::unordered_map d_post_map; std::unordered_map d_put_map; std::unordered_map d_delete_map; unsigned int d_num_worker_threads = WFORCE_NUM_WORKER_THREADS; unsigned int d_max_conns = WFORCE_MAX_WS_CONNS; trantor::Logger::LogLevel d_loglevel = trantor::Logger::kWarn; bool d_metricsNoPassword = false; }; weakforced-2.10.2/common/wforce_exception.hh000066400000000000000000000024531461473602600210770ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include using std::string; class WforceException { public: WforceException() {}; WforceException(const string& r): reason(r) {}; string reason{"Unspecified"}; //! Print this to tell the user what went wrong }; // This means we can use PDNS classes that emit PDNSException typedef WforceException PDNSException; weakforced-2.10.2/common/wforce_ns.hh000066400000000000000000000022261461473602600175170ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include namespace wforce { template std::unique_ptr make_unique(Ts&&... params) { return std::unique_ptr(new T(std::forward(params)...)); } } weakforced-2.10.2/configure.ac000066400000000000000000000176421461473602600162170ustar00rootroot00000000000000AC_INIT([wforce], [2.10.2]) AM_INIT_AUTOMAKE([foreign dist-bzip2 parallel-tests 1.11 subdir-objects tar-ustar]) AM_SILENT_RULES([yes]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_HOST : ${CFLAGS="-Wall -g -O3"} : ${CXXFLAGS="-Wall -g -O3"} AC_PROG_CC AC_PROG_CXX AC_LANG([C++]) PDNS_CHECK_OS LT_INIT # Do we want to do a docker-only build? AC_ARG_ENABLE([docker-only], [ --enable-docker-only Build and run docker images only, don't check for compilation dependencies], [case "${enableval}" in yes) docker_only=true ;; no) docker_only=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-docker]) ;; esac],[docker_only=false]) # If docker-only then don't check for all the dependencies AS_IF([test "x$docker_only" = "xfalse"], [ # Check for C++ 2017 support AX_CXX_COMPILE_STDCXX_17(ext,mandatory) # Check dependencies with pkgconfig PKG_PROG_PKG_CONFIG AC_ARG_ENABLE([geoip], AS_HELP_STRING([--disable-geoip], [ Disable geoip feature ])) AS_IF([test "x$enable_geoip" != "xno"], [ AC_DEFINE([WITH_GEOIP], [1], [Geoip is enabled]) PDNS_CHECK_GEOIP AS_IF([test "x$MMDB_LIBS" = "x"], [ AC_MSG_ERROR([libmaxmindb not found, libmaxmind is required"]) ]) ]) AC_CHECK_LIB(dl,dlopen,DLLIBS="-ldl",,) AC_SUBST(DLLIBS, $DLLIBS) # Drogon uses std::filesystem but older versions of g++ need an extra library to use it AC_MSG_NOTICE([Checking for stdc++fs]) SAVED_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -lstdc++fs" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [std::filesystem::path mypath])], [STDCXXFSLIBS="-lstdc++fs"] [HAVE_LIBSTDCXXFS=1], [AC_MSG_NOTICE([libstdc++fs is not installed.])]) LDFLAGS=$SAVED_LDFLAGS AC_SUBST(STDCXXFSLIBS, $STDCXXFSLIBS) PDNS_WITH_LIBSSL AC_ARG_ENABLE([dns], AS_HELP_STRING([--disable-dns], [ Disable getdns lookup feature ])) AS_IF([test "x$enable_dns" != "xno"], [ AC_DEFINE([WITH_GETDNS], [1], [Getdns is enabled]) PKG_CHECK_MODULES([GETDNS], [getdns >= 1.2.0], [AC_DEFINE([HAVE_GETDNS], [1], [Define to 1 if you have getdns])]) ]) AC_ARG_ENABLE([sodium], AS_HELP_STRING([--disable-sodium], [ Disable Libsodium support ])) AS_IF([test "x$enable_sodium" != "xno"], [ PKG_CHECK_MODULES([libsodium], [libsodium >= 1.0.10], [AC_DEFINE([HAVE_LIBSODIUM], [1], [Define to 1 if you have libsodium])]) ]) PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd], [AC_DEFINE([HAVE_LIBSYSTEMD], [1], [Define to 1 if you have libsystemd-dev])], [true]) PKG_CHECK_MODULES([LIBHIREDIS], [libhiredis], [AC_DEFINE([HAVE_LIBHIREDIS], [1], [Define to 1 if you have libhiredis-dev])], [true]) AS_IF([test "x$LIBHIREDIS_LIBS" = "x"], [ PKG_CHECK_MODULES([LIBHIREDIS], [hiredis], [AC_DEFINE([HAVE_LIBHIREDIS], [1], [Define to 1 if you have hiredis])]) ]) AS_IF([test "x$LIBHIREDIS_LIBS" = "x"], [ AC_MSG_ERROR([libhiredis not found, libhiredis support is required]) ]) PDNS_CHECK_YAMLCPP([AC_DEFINE([HAVE_YAMLCPP], [1], [Define to 1 if you have yaml-cpp])], [true]) AS_IF([test "x$YAMLCPP_LIBS" = "x"], [ AC_MSG_ERROR([yaml-cpp not found, yaml-cpp support is required]) ]) AM_CONDITIONAL([LIBSYSTEMD],[test "$HAVE_LIBSYSTEMD" = "1"]) # We need readline WFORCE_CHECK_READLINE # Boost-specific checks BOOST_REQUIRE([1.42]) BOOST_DATE_TIME AS_IF([test -z "$BOOST_DATE_TIME_LIBS"], [AC_MSG_ERROR([Boost date_time library is not installed])]) BOOST_REGEX AS_IF([test -z "$BOOST_REGEX_LIBS"], [ AC_MSG_ERROR([Boost regex library is not installed])]) BOOST_FOREACH BOOST_SYSTEM BOOST_FILESYSTEM AS_IF([test -z "$BOOST_FILESYSTEM_LIBS"], [ AC_MSG_ERROR([Boost filesystem library is not installed])]) AM_CONDITIONAL([HAVE_BOOST_GE_148], [test "$boost_major_version" -ge 148]) PDNS_ENABLE_UNIT_TESTS PDNS_ENABLE_SANITIZERS # Check for dependent programs AC_CHECK_PROG(PERL, perl, perl) AC_CHECK_PROG(HAVE_WGET, wget, wget) AS_IF([test "x$HAVE_WGET" = "x"], [ AC_MSG_ERROR([wget not found, wget needed for downloading regexes.yaml and is required]) ]) AX_PROG_PERL_MODULES( Swagger2::Markdown, , AC_MSG_WARN(Need to install Perl Swagger2::Markdown)) # Look for protobuf PDNS_WITH_PROTOBUF AS_IF([test "x$PROTOBUF_LIBS" = "x" -o "x$PROTOC" = "x"], [ AC_MSG_ERROR([Protobuf not found, protobuf support is required]) ]) # Setup variables for locally built libraries AC_SUBST([YAHTTP_CFLAGS], ['-I$(top_srcdir)/ext/yahttp']) AC_SUBST([YAHTTP_LIBS], ['-L$(abs_top_builddir)/ext/yahttp/yahttp -lyahttp']) AC_SUBST([JSON11_CFLAGS], ['-I$(top_srcdir)/ext/json11']) AC_SUBST([JSON11_LIBS], ['-L$(abs_top_builddir)/ext/json11 -ljson11']) AC_SUBST([EXT_CFLAGS], ['-I$(top_srcdir)/ext']) AC_SUBST([EXT_LIBS], ['$(abs_top_builddir)/ext/ext/libext.la']) AC_SUBST([WFORCE_CFLAGS], ['-I$(top_srcdir)'/common]) AC_SUBST([WFORCE_LIBS], ['$(abs_top_builddir)/common/libweakforce.la']) # We need libcrypto for hash functions PDNS_CHECK_LIBCRYPTO([], [ AC_MSG_ERROR([Libcrypto not found, libcrypto support is required]) ] ) # We need libdrogon for HTTP/HTTPS functions PDNS_CHECK_LIBDROGON([], [ AC_MSG_ERROR([Libdrogon not found, libdrogon support is required]) ] ) # Drogon depends on libjsoncpp, libuuid and libz PDNS_CHECK_LIBJSONCPP([], [ AC_MSG_ERROR([Libjsoncpp not found, libjsoncpp is required as a mandatory dependency of libdrogon]) ] ) PDNS_CHECK_LIBUUID([], [ AC_MSG_ERROR([Libuuid not found, libuuid is required as a mandatory dependency of libdrogon]) ] ) PDNS_CHECK_LIBZ([], [ AC_MSG_ERROR([Libz not found, libz support is required as a mandatory dependency of libdrogon]) ] ) # Check for LuaJIT first then Lua PDNS_WITH_LUAJIT AS_IF([test "x$with_luajit" = "xno"], [ PDNS_WITH_LUA ]) AS_IF([test "x$LUAPC" = "x" -a "x$LUAJITPC" = "x"], [ AC_MSG_ERROR([Neither Lua nor LuaJIT found, Lua support is required]) ]) PDNS_CHECK_LUA_HPP # Need pandoc to build documentation PDNS_CHECK_PANDOC # We need prometheus-cpp PDNS_CHECK_LIBPROMETHEUS([], [ AC_MSG_ERROR([Libprometheus not found, libprometheus-cpp support is required]) ] ) # Check for systemd AX_AVAILABLE_SYSTEMD AM_CONDITIONAL([HAVE_SYSTEMD], [ test x"$systemd" = "xy" ]) # We need a working libcurl package LIBCURL_CHECK_CONFIG AS_IF([test "x$LIBCURL" = "x"], [ AC_MSG_ERROR([Libcurl not found, libcurl support is required]) ]) PTHREAD_SET_NAME # Docker-only ]) AS_IF([ test "x$docker_only" = "xtrue" ], [ AM_CONDITIONAL([HAVE_SYSTEMD], [ test x = x ]) AM_CONDITIONAL([LIBSYSTEMD], [ test x = x ]) AM_CONDITIONAL([HAVE_LIBSSL], [ test x = x ]) AM_CONDITIONAL([HAVE_BOOST_GE_148], [ test x = x ]) AM_CONDITIONAL([UNIT_TESTS], [ test x = x ]) AM_CONDITIONAL([BACKEND_UNIT_TESTS], [ test x = x ]) AM_CONDITIONAL([HAVE_PROTOBUF], [ test x = x ]) AM_CONDITIONAL([HAVE_PROTOC], [ test x = x ]) AM_CONDITIONAL([LUA], [ test x = x ]) AM_CONDITIONAL([HAVE_LUA_HPP], [ test x = x ]) AM_CONDITIONAL([HAVE_PANDOC], [ test x = x ]) AM_CONDITIONAL([HAVE_MANPAGES], [ test x = x ]) AM_CONDITIONAL([BACKEND_UNIT_TESTS], [ test x = x ]) AM_CONDITIONAL([BACKEND_UNIT_TESTS], [ test x = x ]) AM_CONDITIONAL([BACKEND_UNIT_TESTS], [ test x = x ]) AM_CONDITIONAL([BACKEND_UNIT_TESTS], [ test x = x ]) ]) # Do we want the docker subdirectories to be built? AC_ARG_ENABLE([docker], [ --enable-docker Build and run docker images], [case "${enableval}" in yes) docker=true ;; no) docker=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-docker]) ;; esac],[docker=false]) AM_CONDITIONAL([WITH_DOCKER], [test x$docker = xtrue]) AC_CHECK_PROG(DOCKER, docker-compose, docker-compose) AS_IF([test x$docker = xtrue -a "x$DOCKER" = "x"], [ AC_MSG_ERROR([docker-compose not found, and is required]) ]) AC_ARG_ENABLE([trackalert], [ --enable-trackalert Build trackalert], [case "${enableval}" in yes) trackalert=true ;; no) trackalert=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-trackalert]) ;; esac],[trackalert=false]) AM_CONDITIONAL([WITH_TRACKALERT], [test x$trackalert = xtrue]) AC_CONFIG_FILES([Makefile ext/Makefile ext/ext/Makefile ext/json11/Makefile common/Makefile docs/Makefile docker/Makefile docker/wforce_image/Makefile trackalert/Makefile wforce/Makefile]) AC_OUTPUT weakforced-2.10.2/docker/000077500000000000000000000000001461473602600151665ustar00rootroot00000000000000weakforced-2.10.2/docker/.gitignore000066400000000000000000000000371461473602600171560ustar00rootroot00000000000000# Ignore .docker file /.docker weakforced-2.10.2/docker/Makefile.am000066400000000000000000000025611461473602600172260ustar00rootroot00000000000000DCMP = docker-compose COMPOSE_SOURCE = docker-compose.yml logstash/Dockerfile logstash/config/logstash.conf logstash/templates/wforce_template.json regression/Dockerfile wforce_image/Dockerfile wforce_image/docker-entrypoint.sh wforce_image/wforce.conf.j2 wforce_image/create_config.sh COMPOSE_TARGET = .docker REGRESSION_SERVICE = regression SUBDIRS= wforce_image $(COMPOSE_TARGET): $(COMPOSE_SOURCE) $(shell find ../common -type f) $(shell find ../wforce -type f) $(shell find ../trackalert -type f) $(shell find ../ext -type f) $(shell find ../regression-tests -type f) $(shell find regression -type f) $(shell find logstash -type f) $(DCMP) down -v $(DCMP) build touch $(COMPOSE_TARGET) build_image: $(COMPOSE_TARGET) start: $(DCMP) up -d stop: $(DCMP) stop kill: $(DCMP) kill clean_elastic: stop $(DCMP) rm -v docker volume rm docker_esdata rm $(COMPOSE_TARGET) clean_docker: docker-compose down -v clean: clean_docker regression-gcc: build_image start $(DCMP) exec -T $(REGRESSION_SERVICE) docker/regression/regression.sh gcc g++ --enable-sodium /var/tmp/testlog/gcctest.xml regression-clang: build_image start $(DCMP) exec -T $(REGRESSION_SERVICE) docker/regression/regression.sh clang clang++ --disable-sodium /var/tmp/testlog/clangtest.xml regression-none: echo "Regression tests skipped due to compiler 'none'" regression: regression-gcc regression-clang weakforced-2.10.2/docker/docker-compose.yml000066400000000000000000000050311461473602600206220ustar00rootroot00000000000000version: '2' services: logstash: build: context: logstash args: - MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY} ports: - "4501:4501/udp" - "8080:8080/tcp" command: -f /etc/logstash/conf.d/ volumes: - ./logstash/config:/etc/logstash/conf.d - ./logstash/templates:/tmp/templates links: - elasticsearch depends_on: - elasticsearch elasticsearch: image: elasticsearch:7.14.1 environment: - xpack.security.enabled=false - discovery.type=single-node - "ES_JAVA_OPTS=-Xms1000m -Xmx1000m" ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 hard: 65536 cap_add: - IPC_LOCK volumes: - esdata:/usr/share/elasticsearch/data ports: - "9200:9200" - "9300:9300" regression: cap_add: - ALL security_opt: - seccomp=unconfined environment: - SPAMHAUS_KEY=${SPAMHAUS_KEY} build: context: weakforced dockerfile: docker/regression/Dockerfile args: - MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY} volumes: - "/sys/fs/cgroup:/sys/fs/cgroup:ro" - ./tmp:/var/tmp/testlog links: - elasticsearch - logstash depends_on: - elasticsearch - logstash - kafka-rest # The following three images are for kafka regression testing zookeeper: image: confluentinc/cp-zookeeper:latest environment: ZOOKEEPER_CLIENT_PORT: 2181 ZOOKEEPER_TICK_TIME: 2000 kafka: image: confluentinc/cp-kafka:latest depends_on: - zookeeper ports: - 9092:9092 environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 kafka-rest: image: confluentinc/cp-kafka-rest:latest ports: - 8082:8082 environment: KAFKA_REST_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_REST_LISTENERS: http://0.0.0.0:8082 KAFKA_REST_HOST_NAME: localhost KAFKA_REST_BOOTSTRAP_SERVERS: kafka:29092 depends_on: - zookeeper - kafka redis: image: redis:latest ports: - 6380:6379 volumes: - ./redis/conf:/usr/local/etc/redis command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ] volumes: esdata: driver: local weakforced-2.10.2/docker/docker_push.sh000077500000000000000000000011421461473602600200310ustar00rootroot00000000000000#!/bin/bash docker_login() { echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin } # Only push tagged releases matching the versioning scheme check_version() { echo $TAG | egrep "^v[0-9]+\.[0-9]+\.[0-9]+$" } push_tag() { local tag=$1 echo "Docker username is: '"$DOCKER_USERNAME"'" docker_login docker push $IMAGE:$tag } TAG=`git describe --tags` check_version if [ $? = "0" ] then push_tag $TAG fi IMAGE=$1 BRANCH=`git branch --show-current` if [ "$BRANCH" = "master" ] then docker tag $IMAGE:$TAG $IMAGE:unstable push_tag unstable fi exit 0 weakforced-2.10.2/docker/logstash/000077500000000000000000000000001461473602600170125ustar00rootroot00000000000000weakforced-2.10.2/docker/logstash/Dockerfile000066400000000000000000000014611461473602600210060ustar00rootroot00000000000000FROM docker.elastic.co/logstash/logstash:7.14.2 USER root RUN yum install -y wget RUN mkdir -p /usr/share/logstash/geoip RUN chown -R logstash:logstash /usr/share/logstash/geoip ARG MAXMIND_LICENSE_KEY # Don't fail if license key isn't set COPY geoip/GeoIP2-City-Test.mmdb /usr/share/logstash/geoip/GeoLite2-City.mmdb RUN wget -O GeoLite2-City.mmdb.tar.gz "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" || true RUN gunzip GeoLite2-City.mmdb.tar.gz || true RUN tar xvf GeoLite2-City.mmdb.tar || true RUN mv GeoLite2*/GeoLite2-City.mmdb /usr/share/logstash/geoip || true USER logstash RUN logstash-plugin install logstash-input-udp RUN logstash-plugin install logstash-output-elasticsearch RUN logstash-plugin install logstash-filter-geoip weakforced-2.10.2/docker/logstash/config/000077500000000000000000000000001461473602600202575ustar00rootroot00000000000000weakforced-2.10.2/docker/logstash/config/logstash.conf000066400000000000000000000026101461473602600227510ustar00rootroot00000000000000input { udp { port => 4501 codec => json type => wforce_report add_field => { "input" => "udp" } } http { port => 8080 codec => json type => wforce_report add_field => { "input" => "http" } } } filter { if ![type] { mutate { add_field => { "type" => "wforce_report" } } } mutate { remove_field => [ "headers" ] } if [type] == "wforce_report" { mutate { add_field => { "user_confirmation" => "none" } # user_confirmation can have the following values: # "none" - User has not confirmed the login # "good" - User has confirmed the login as good # "bad" - User has confirmed the login as bad # "forget" - User wants us to forget this login (i.e. ignore it) } } if [type] == "wforce_allow" { mutate { add_field => { "remote" => "%{[request][remote]}" } # So that geoip works } } geoip { database => "/usr/share/logstash/geoip/GeoLite2-City.mmdb" source => "remote" } } output { elasticsearch { hosts => "http://elasticsearch:9200" index => "logstash-wforce-%{+YYYY.MM.dd}" template => "/tmp/templates/wforce_template.json" template_name => "wforce" template_overwrite => true user => elastic password => changeme } } weakforced-2.10.2/docker/logstash/geoip/000077500000000000000000000000001461473602600201155ustar00rootroot00000000000000weakforced-2.10.2/docker/logstash/geoip/GeoIP2-City-Test.mmdb000066400000000000000000000510101461473602600236230ustar00rootroot00000000000000¤­Ù­­­­¡ ­ ­ ­ ­ ­­­­­­­­­­­­­­­­­­­ ­!­"­#­$­%­&­'­(­)­*­+­,­-­.­/­0­1­2­3­4­5­6­7­8­9­:­;­<­=­>­?­@­A­B­C­D­E­F­G­H­I­J­K­L­M­N­O­P­Q’R­S­T­U­V­W­X­Y­Z­[­\­]­^­_­`­aìb—c­d­e”f­gh­­i­j­kl­m­n­o­­pqyr­s­t­u­v­w­­x­½z­{­|­}­~­­­€­å­‚­ƒ­„­…†­­‡­ˆ‰­­Š‹­Œ­­Ž­­­­‘’­­“­X•­­–­­˜á™­š­›­­œ­ž­Ÿ­­ ¡­­¢£­­¤­¥­¦§­¨­­©­ª­«¬­°­®Ê¯­°­­±²­³­´­µ­¶­·­­¸¹­º­­»¼­½­¾­­¿À­­Á­ÂÃÈÄ =Å 6­Æ­Ç­ ^É­ =­Ë­Ì­­Í­ÎÏ­­ÐÑ­Ò­Ó­Ô­Õ­Ö­×­Ø­­ÙÚ­­ÛÜ­Ý­Þ †­ß­à­ †âèã­­äå­æ­ç­­­­é­ê­ë­­í­îï­­ðñûò­­ó­ô­õ­ö­÷­ø­ù­ú­­üý­þ­ÿ­­­­­­­­­­­ ­ ­­ ­  ­­­­­­ W‘xYC­­<.­­­­­ ­!­"­#­$­%­&­'-(­)­*­+­,­­­­­/­­0­12­3­4­5­­6­78­9­:­­;­­=­­>?­­@A­B­­­­DE­F­G­HL­IJ­K­­­­MN­O­­P­QR­­S­TU­V­­WX­­­Z­­[\h­]­^_­`­a­­bc­d­­e­f­g\­i­j­k­l­m­n­o­p­q­­r­s­tu­v­w­­­­yz­{­|­­}~­­€­­‚­ƒ­„­…­­†‡­­ˆ‰­Š­­‹­Œ­Ž­­­­S­­­“­”­•­–­—­˜­™­š­›­œ­­ž­Ÿ­ ­`¢­£­¤­¥­¦­§­¨­©­ª­«­¬­­­®­¯­°­±­²­³­´­µ­¶­·­¸­¹­º­»­¼­½­¾­¿­À­Á­Â­Ã­Ä­Å­Æ­Ç­È­É­Ê­Ë­Ì­Í­Î­Ï­Ð­Ñ­Ò­Ó­Ô­Õ­Ö­×­Ø­­­Ú­ÛrÜ­Ý­Þ­ß­à­á­â­ã­ä­åq­æç­è­é­ê­ëfì­íöî­ï­ð­ñ­ò­ó­ô­õ­`­÷­ø*ù úÿ­û­üý­þ­ü­­­­‹­­­‹­ ­ ­e­  ­­­ü­÷÷­÷­÷­­­ü­# ­­ü­!­"­ü­$'%­&­‹­(­)­ü­+J,;-4.1/­0­‹­2­3­e­586­7­‹­9­:­ü­<C=@>­?­ü­A­B­ü­DGE­F­‹­H­I­‹­KWLSMPN­O­ü­Q­R­ü­­TU­V­‹­X_Y\Z­[­‹­]­^­ü­`ca­b­ü­d­e­ü­­gh­­i­jk­­l­m­no­p­­­`­s­­tu­v­w­x­y­z­{­­|}­­~­€˜‘­‚­ƒ­„­…†Š­‡ˆ­‰­­‹ŽŒ­­Í­­­p­’“Ò”³•¤–—š˜­™­©­›­œ­Z­ž¡Ÿ­ ­ˆ­¢­£­+­¥¬¦©§­¨­¿­ª­«­­­°®­¯­t­±­²­®­´Ãµ¼¶¹·­¸­_­º­»­(­½À¾­¿­ù­Á­Â­æ­ÄËÅÈÆ­Ç­­É­Ê­p­ÌÏͭέæ­Ð­Ñ­¡­ÓöÔçÕÜÖÙ×­Ø­Ï­Ú­Û­Z­ÝàÞ­ß­t­áäâ­ã­_­å­æ­t­èïéìê­ë­­í­î­¡­ðóñ­ò­p­ô­õ­¾­÷ øÿùüú­û­t­ý­þ­p­­­¡­­­t­­ ­\­   ­­t­­­©­­­æ­­­p­Y:+$!­ ­ ­"­#­+­%(&­'­+­)­*­­,3-0.­/­Z­1­2­ Z­475­6­!­8­9­!­;J<C=@>­?­t­A­B­¡­DGE­F­!Á­H­I­+­KRLOM­N­t­P­Q­ ­SVT­U­+­W­X­+­Zy[j\c]`^­_­+­a­b­!­dge­f­+­h­i­­krlom­n­"­p­q­¡­svt­u­Z­w­x­+­z‰{‚|}­~­+­€­­Í­ƒ†„­…­+­‡­ˆ­#<­Š‘‹ŽŒ­­æ­­­!­’•“­”­¡­–­—­æ­™š!›Þœ»¬ž¥Ÿ¢ ­¡­p­£­¤­#ä­¦©§­¨­$°­ª­«­©­­´®±¯­°­¡­²­³­¿­µ¸¶­·­¡­¹­º­#䭼ϽȾſÂÀ­Á­!Á­Ã­Ä­%K­Æ­Ç­t­ÉÌÊ­Ë­t­Í­Î­®­Ð×ÑÔÒ­Ó­(­Õ­Ö­­ØÛÙ­Ú­®­Ü­Ý­p­ßþàïáèâåã­ä­Z­æ­ç­t­éìê­ë­+­í­î­#ä­ð÷ñôò­ó­p­õ­ö­&@­øûù­ú­­ü­ý­+­ÿ ­­p­­­+­ ­ ­p­  ­­&è­­­+­­­'À­­­t­­­!­­ ­¿­"^#?$0%,&)'­(­t­*­+­+­-­.­/­+­18253­4­)6­6­7­ ­9<:­;­Í­=­>­Z­@OAHBEC­D­©­F­G­¡­ILJ­K­¿­M­N­¿­PWQTR­S­+­U­V­¡­X[Y­Z­+­\­]­p­_~`oahbec­d­p­f­g­+­ilj­k­­m­n­)û­pwqtr­s­­u­v­!­x{y­z­t­|­}­%K­Ž€‡„‚­ƒ­­…­†­+­ˆ‹‰­Š­#ä­Œ­­Z­–“‘­’­+­”­•­+­—š˜­™­!­›­œ­+­ž%Ÿâ Ã¡´¢©£¦¤­¥­ù­§­¨­ˆ­ª±«®¬­­­­¯­°­¡­²­³­&è­µ¼¶¹·­¸­¡­º­»­t­½À¾­¿­p­Á­Â­­ÄÓÅÌÆÉÇ­È­t­Ê­Ë­æ­ÍÐέϭp­Ñ­Ò­+­ÔÛÕØÖ­×­­Ù­Ú­t­ÜßÝ­Þ­¡­à­á­¿­ãäóåìæéç­è­*Ú­ê­ë­¡­íðî­ï­­ñ­ò­¾­ôÿõøö­÷­+­ùüú­û­¡­ý­þ­+­­­,­­­#ä­   ­ ­#ä­ ­­#ä­­­+­­­+­­­#<­­­¿­" ­!­Z­#­$­¿­&e'F(7)0*-+­,­+­.­/­#ä­142­3­t­5­6­¿­8?9<:­;­æ­=­>­Ï­@CA­B­¡­D­E­¡­GVHOILJ­K­%K­M­N­!­PSQ­R­t­T­U­¿­W^X[Y­Z­t­\­]­¡­_b`­a­®­c­d­¿­f…gvhoilj­k­Z­m­n­,Эpsq­r­­t­u­¡­w~x{y­z­t­|­}­®­‚€­­,Эƒ­„­!Á­†•‡Žˆ‹‰­Š­#ä­Œ­­¡­’­‘­®­“­”­¡­–—š˜­™­®­›­œ­-†­ž¡Ÿ­ ­t­¢­£­.B­­¥­¦­§­¨­©­ª«­­¬­­áDcityâJgeoname_idÃ(ƒEEnamesáBenGBoxfordáIcontinentãDcodeBEU Ã_r, èBdeFEuropaBenFEuropeBes HBfr RBjaOヨーロッパEpt-BR HBruLЕвропаEzh-CNF欧洲ç   ) 3Gcountryä Ã(5ŸTis_in_european_unionHiso_codeBGB èBdeWVereinigtes KönigreichBenNUnited KingdomBesKReino UnidoBfrKRoyaume-UniBjaLイギリス v! Bru\Ð’ÐµÐ»Ð¸ÐºÐ¾Ð±Ñ€Ð¸Ñ‚Ð°Ð½Ð¸Ñ ŽF英国HlocationäOaccuracy_radius¡dHlatitudeh@IàIlongitudeh¿ôItime_zoneMEurope/LondonFpostalá 4COX1Rregistered_countryä Ã. ¦ ³ È ÊBFR èBdeJFrankreichBenFFranceBesGFranciaBfr!ÿBjaUフランス共和国 vGFrançaBruNÐ¤Ñ€Ð°Ð½Ñ†Ð¸Ñ ŽF法国Lsubdivisionsã Ã_¨Ë ÊCENG äBenGEnglandBesJInglaterraBfrJAngleterre v"ã Ã2Üa ÊCWBK ãBenNWest BerkshireBru]Западный Беркшир ŽL西伯克郡å )ã 4BAS Ã_r+ èBdeEAsienBenDAsiaBes#BfrDAsieBjaIアジア vEÃsiaBruHÐÐ·Ð¸Ñ ŽF亚洲 ¤ã à ÊBBT èBdeFBhutanBen#aBesFButánBfr#aBjaRブータン王国 vFButãoBruJБутан ŽFä¸ä¸¹!aä!k¢!}h@;€!h@V !¢LAsia/Thimphu!Èä à /U ³ È ÊBRO èBdeIRumänienBenGRomaniaBesHRumaníaBfrHRoumanieBjaOルーマニア vHRomêniaBruNÐ ÑƒÐ¼Ñ‹Ð½Ð¸Ñ ŽL罗马尼亚FtraitsáRis_anonymous_proxy Èæ â Ã(W çBdeFLondonBen$BesGLondresBfr$ŸBjaLロンドン v$ŸBruLЛондон ) 3 ¤ ¬!aä!k¡d!}h@IÁÑN;Í6!h¿·ÕfÏAò!¢!¬!Èã Ã_eá ÊBUS èBdeCUSABenMUnited StatesBesNEstados UnidosBfrKÉtats-UnisBjaUアメリカåˆè¡†å›½ v%%BruFСШРŽF美国"T"cæ $ƒ ) 3 ¤ ¬!aä!k¡!}$á!$ì!¢!¬!È$û"T%uæ $ƒ ) 3 ¤ ¬!aä!k¡ !}$á!$ì!¢!¬!È$û"T%uæ â Ã)j åBdeJLinköpingBen%ÙBfr%ÙBjaXリンシェーピング ŽI林雪平 ) 3 ¤ä Ã(þ ³ È ÊBSE èBdeHSchwedenBenFSwedenBesFSueciaBfrFSuèdeBjaXスウェーデン王国 vGSuéciaBruLÐ¨Ð²ÐµÑ†Ð¸Ñ ŽF瑞典!aä!k¡L!}h@M5Vlô!!h@/;À6âë!¢PEurope/Stockholm!Èä Ã,’T ³ È ÊBDE èBdeKDeutschlandBenGGermanyBesHAlemaniaBfrIAllemagneBjaXドイツ連邦共和国 vHAlemanhaBruPÐ“ÐµÑ€Ð¼Ð°Ð½Ð¸Ñ ŽF德国"Tã Ã(û« ÊAE âBenUÖstergötland CountyBfrWComté d'Östergötlandæ â ä æBdeKChángchÅ«nBenIChangchunBfr'¹BjaI長春市BruNЧанчунь ŽF长春 )"ö ¤ã Ã±Ï ÊBCN èBdeEChinaBen(Bes(BfrEChineBjaF中国 v(BruJКитай Ž(&!aä!k¡d!}h@Eð£× =q!h@_T¨ÁTɆ!¢KAsia/Harbin!È'ö"Tã à ÊB22 âBenKJilin Sheng ŽF剿ž—æ )"ö ¤ã ÃÙ8 ÊBPH èBdeKPhilippinenBenKPhilippinesBesIFilipinasBfr(ÇBjaXフィリピン共和国 v(ÖBruRФилиппины ŽIè²å¾‹å®¾!aä!k¡y!}h@*!h@^€!¢KAsia/Manila!ºá 4E34021!È(¦Srepresented_countryä Ã_eá ÊBUS % DtypeHmilitaryç â ÃXŽ$ âBenFMiltonBruNМильтон )ã 4BNA Ã_r- èBdeKNordamerikaBenMNorth AmericaBesRAmérica del NorteBfrQAmérique du NordBjaO北アメリカ vQAmérica do NorteBru]Ð¡ÐµÐ²ÐµÑ€Ð½Ð°Ñ Ðмерика ŽI北美洲 ¤$û!aå!k¡!}h@G *™0¾!hÀ^”'RT`ªJmetro_code¢3!¢SAmerica/Los_Angeles!ºá 4E98354!È ¬"Tã ÃX»_ ÊBWA æBenJWashingtonBes(ÜBfrSÉtat de WashingtonBjaRワシントン州BruRВашингтон ŽLåŽç››é¡¿å·žä )"ö ¤ã ÃeÄ ÊBJP èBdeEJapanBen(XBesFJapónBfrEJaponBjaF日本 vFJapãoBruLÐ¯Ð¿Ð¾Ð½Ð¸Ñ Ž(z!aä!k¡d!}h@A×¹à`þH!h@axP3:!¢JAsia/Tokyo!È(Fä )"ö ¤ã ÃA ÊBKR èBdeNRepublik KoreaBenKSouth KoreaBesTCorea, República deBfrMCorée du SudBjaL大韓民国 vVCoréia, República daBruUÐ®Ð¶Ð½Ð°Ñ ÐšÐ¾Ñ€ÐµÑ ŽF韩国!aä!k¡d!}h@B€!h@_à!¢JAsia/Seoul!È(Õä )"ö ¤ã Ãt¼ ÊBTW èBdeFTaiwanBen(ÁBesGTaiwánBfrGTaïwanBjaFå°æ¹¾ v(ÁBruNТайвань Ž(ç!aä!k¡d!}h@8!h@^@!¢KAsia/Taipei!È(¯ä )"ö ¤ã Ã±Ï ÊBCN èBde(BenZPeople's Republic of ChinaBesXRepública Popular ChinaBfr(Bja(& v(Bru(5 Ž(&!aã!k¡d!}h@A€!h@Z@!È(Aä )"ö ¤ã ÃÄR ÊBHK èBdeHHongkongBenIHong KongBes(óBfr(óBjaF香港 v(óBruNГонконг Ž( !aä!k¡d!}h@6@!h@\Šª¸¥Î[!¢NAsia/Hong_Kong!È(Õä ) 3 ¤ã Ã/ù  ÊBNO èBdeHNorwegenBenFNorwayBesGNoruegaBfrHNorvègeBjaUノルウェー王国 v(‘BruPÐÐ¾Ñ€Ð²ÐµÐ³Ð¸Ñ ŽF挪å¨!aä!k¡d!}h@O!h@$!¢KEurope/Oslo!È(iä )"ö ¤ã Ã~ð ÊBIL èBdeFIsraelBen()Bes()BfrGIsraëlBjaRイスラエル国 v()BruNИзраиль ŽI以色列!aä!k¡d!}h@?€!h@A`!¢NAsia/Jerusalem!È(ä ) 3 ¤!Û!aä!k¡d!}h@G!h@!¢LEurope/Paris!È!Ûä ) 3 ¤ã Ã(‚ ÊBCH èBdeGSchweizBenKSwitzerlandBesESuizaBfrFSuisseBjaOスイス連邦 vGSuíçaBruRÐ¨Ð²ÐµÐ¹Ñ†Ð°Ñ€Ð¸Ñ ŽF瑞士!aä!k¡d!}h@G€>-b9!h@ Ne¾ º!¢MEurope/Zurich!È(óä ) 3 ¤&!aä!k¡d!}h@O!h@.!¢&·!È&ä )"ö ¤ã Ãmó ÊBBH èBdeGBahrainBen(äBesHBahréinBfrHBahreïnBjaOãƒãƒ¼ãƒ¬ãƒ¼ãƒ³ v(äBruNБахрейн ŽFå·´æž—!aä!k¡d!}h@:!h@I@!¢LAsia/Bahrain!È(Òä ) 3 ¤ã ÃÈZ ÊBRU èBdeHRusslandBenFRussiaBesERusiaBfrFRussieBjaIロシア vGRússiaBruLРоÑÑÐ¸Ñ ŽIä¿„ç½—æ–¯!aã!k¡d!}h@N!h@Y!È( uä ) 3 ¤ä à /P ³ È ÊBPL èBdeEPolenBenFPolandBesGPoloniaBfrGPologneBjaXãƒãƒ¼ãƒ©ãƒ³ãƒ‰å…±å’Œå›½ vHPolôniaBruLПольша ŽF波兰!aä!k¡d!}h@J!h@4!¢MEurope/Warsaw!È( ä ) 3 ¤&Ê!aä!k¡d!}h@IÀ!h@%!¢MEurope/Berlin!È&Êä ) 3 ¤ä Ã0sã ³ È ÊBIT èBdeGItalienBenEItalyBesFItaliaBfrFItalieBjaUイタリア共和国 vGItáliaBruLÐ˜Ñ‚Ð°Ð»Ð¸Ñ ŽIæ„大利!aä!k¡d!}h@EjªŽ´cI!h@)ªª:Ñ&!¢KEurope/Rome!È( øä ) 3 ¤ä à - ³ È ÊBFI èBdeHFinnlandBenGFinlandBesIFinlandiaBfrHFinlandeBja[フィンランド共和国 vJFinlândiaBruRФинлÑÐ½Ð´Ð¸Ñ ŽF芬兰!aä!k¡d!}h@P!h@:!¢OEurope/Helsinki!È( ©ä ) 3 ¤ã à ž@ ÊBBY èBdeMWeißrusslandBenGBelarusBesKBielorrusiaBfrLBiélorussieBjaXベラルーシ共和国 vMBielo-RússiaBruPБеларуÑÑŒ ŽL白俄罗斯!aä!k¡d!}h@J€!h@<!¢LEurope/Minsk!È( rä ) 3 ¤ä Ã.ô¿ ³ È ÊBCZ èBdeUTschechische RepublikBenNCzech RepublicBesPRepública ChecaBfrITchéquieBjaRãƒã‚§ã‚³å…±å’Œå›½ v( „Bru]ЧешÑÐºÐ°Ñ Ð ÐµÑпублика ŽOæ·å…‹å…±å’Œå›½!aä!k¡d!}h@Hà!h@.!¢MEurope/Prague!È( Cä )"ö ¤ã ÃþÆ ÊBIR èBdeZIran (Islamische Republik)BenDIranBes\Irán (República Islámica)Bfr]Iran (République islamique de)Bja]イラン・イスラム共和国 v\República Islâmica do IrãBruHИран ŽX伊朗伊斯兰共和国!aä!k¡d!}h@@!h@J€!¢KAsia/Tehran!È(0ä ) 3 ¤ã à Šg ÊBUA èBdeGUkraineBen(^BesGUcraniaBfr(^BjaXウクライナ共和国 vHUcrâniaBruNУкраина ŽI乌克兰!aã!k¡d!}h@H€!h@@!È(Lä ) 3 ¤ ¬!aä!k¡d!}h@KaÙý7!hÀþ°t§r!¢!¬!È ¬ä ) 3 ¤ä à ûË ³ È ÊBHU èBdeFUngarnBenGHungaryBesHHungríaBfrGHongrieBjaXãƒãƒ³ã‚¬ãƒªãƒ¼å…±å’Œå›½ vGHungriaBruNÐ’ÐµÐ½Ð³Ñ€Ð¸Ñ ŽI匈牙利!aä!k¡d!}h@G€!h@4!¢OEurope/Budapest!È(â ) 3!aä!k¡d!}h@HXq`•l !h@"GÿX:S¹!¢LEurope/Vaduzä ) 3 ¤ä Ã&O± ³ È ÊBES èBdeGSpanienBenESpainBesGEspañaBfrGEspagneBjaLスペイン vGEspanhaBruNИÑÐ¿Ð°Ð½Ð¸Ñ ŽI西ç­ç‰™!aã!k¡d!}h@D!hÀ!È(ä ) 3 ¤ä à .€ ³ È ÊBBG èBdeIBulgarienBenHBulgariaBes(ÉBfrHBulgarieBjaXブルガリア共和国 vIBulgáriaBruPÐ‘Ð¾Ð»Ð³Ð°Ñ€Ð¸Ñ ŽLä¿åŠ åˆ©äºš!aä!k¡d!}h@E€!h@9!¢LEurope/Sofia!È(¦ä ) 3 ¤#á!aä!k¡d!}h@G!h@9!¢PEurope/Bucharest!È#áä ) 3 ¤ä Ã*¹ ³ È ÊBBE èBdeGBelgienBenGBelgiumBesHBélgicaBfrHBelgiqueBjaRベルギー王国 v(ÐBruNÐ‘ÐµÐ»ÑŒÐ³Ð¸Ñ ŽI比利时!aä!k¡d!}h@IjªŽ´cI!h@!¢OEurope/Brussels!È(¤ä )"ö ¤ã Ã+ ÊBTR èBdeGTürkeiBenFTurkeyBesHTurquíaBfrGTurquieBjaRトルコ共和国 vGTurquiaBruLÐ¢ÑƒÑ€Ñ†Ð¸Ñ ŽI土耳其!aä!k¡d!}h@C‡£Âˆ!h@At­«ŸU›!¢OEurope/Istanbul!È(Yä ) 3 ¤ä Ã*s¡ ³ È ÊBAT èBdeKÖsterreichBenGAustriaBes(0BfrHAutricheBja[オーストリア共和国 vHÃustriaBruNÐвÑÑ‚Ñ€Ð¸Ñ ŽI奥地利!aä!k¡d!}h@GªªŽ´cI!h@*ªª:Ñ&!¢MEurope/Vienna!È( ä ) 3 ¤ã à õŠ ÊBAL èBdeHAlbanienBenGAlbaniaBes(éBfrGAlbanieBjaXアルãƒãƒ‹ã‚¢å…±å’Œå›½ vHAlbâniaBruNÐÐ»Ð±Ð°Ð½Ð¸Ñ ŽO阿尔巴尼亚!aä!k¡d!}h@D€!h@4!¢MEurope/Tirane!È(Ëä )"ö ¤ã Ã&ç ÊBLB èBdeGLibanonBenGLebanonBesGLíbanoBfrELibanBjaUレãƒãƒŽãƒ³å…±å’Œå›½ v(®BruJЛиван ŽI黎巴嫩!aä!k¡d!}h@@ꪎ´cI!h@Aꪎ´cI!¢KAsia/Beirut!È(†ä ) 3 ¤ä Ã)÷Å ³ È ÊBNL èBdeKNiederlandeBenKNetherlandsBesGHolandaBfrHPays-BasBjaRオランダ王国 vNPaíses BaixosBruTÐидерланды ŽFè·å…°!aä!k¡d!}h@J@!h@!¢PEurope/Amsterdam!È(.ä )"ö ¤ã Ã[‚ ÊBKW èBdeFKuwaitBen( Bes( BfrGKoweïtBjaOクウェート v( BruLКувейт ŽIç§‘å¨ç‰¹!aä!k¡d!}h@=€!h@Gà!¢KAsia/Kuwait!È(úä )"ö ¤ã ÃÖ ÊBSA èBdeMSaudi-ArabienBenLSaudi ArabiaBesNArabia SauditaBfrOArabie saouditeBja[サウジアラビア王国 vOArábia SauditaBru]СаудовÑÐºÐ°Ñ ÐÑ€Ð°Ð²Ð¸Ñ ŽO沙特阿拉伯!aä!k¡d!}h@9!h@F€!¢KAsia/Riyadh!È(•ä ) 3 ¤ã Ã_ûL ÊBRS èBdeGSerbienBenFSerbiaBes(§BfrFSerbieBjaLセルビア vGSérviaBruLÐ¡ÐµÑ€Ð±Ð¸Ñ ŽL塞尔维亚!aä!k¡d!}h@FhÒ^Ý)!h@4uÁ?ÐÐh!¢OEurope/Belgrade!È(Šä )"ö ¤ã ÃËð ÊBJO èBdeIJordanienBen[Hashemite Kingdom of JordanBesHJordaniaBfrHJordanieBja]ヨルダン・ãƒã‚·ãƒŸãƒ†çދ国 vIJordâniaBruPÐ˜Ð¾Ñ€Ð´Ð°Ð½Ð¸Ñ ŽF约旦!aä!k¡d!}h@?!h@B!¢JAsia/Amman!È(2ä )ã 4BAF Ã_r* èBdeFAfrikaBenFAfricaBesGÃfricaBfrGAfriqueBjaLアフリカ v(,BruLÐфрика ŽFéžæ´² ¤ã Ã!ÎÔ ÊBLY èBde]Libysch-Arabische DschamahirijaBenELibyaBesXLibia, República ÃrabeBfrELibyeBja] 社会主義人民リビア・アラブ国 vXLíbia Ãrabe JamahiriyaBruJÐ›Ð¸Ð²Ð¸Ñ Ž[阿拉伯利比亚民众国!aä!k¡d!}h@<!h@1!¢NAfrica/Tripoli!È(oä ) 3 ¤ä Ã-8 ³ È ÊBIE èBdeFIrlandBenGIrelandBesURepública de IrlandaBfrGIrlandeBjaRアイルランド vGIrlandaBruPÐ˜Ñ€Ð»Ð°Ð½Ð´Ð¸Ñ ŽI爱尔兰!aä!k¡d!}h@J€!hÀ !¢MEurope/Dublin!È(€ä )"ö ¤ã Ãõl ÊBAZ èBdeMAserbaidschanBenJAzerbaijanBesKAzerbaiyánBfrLAzerbaïdjanBja]アゼルãƒã‚¤ã‚¸ãƒ£ãƒ³å…±å’Œå›½ vKAzerbaijãoBruVÐзербайджан ŽL阿塞拜疆!aä!k¡d!}h@D@!h@GÀ!¢IAsia/Baku!È(Eä )"ö ¤ã Ãný ÊBAE èBde\Vereinigte Arabische EmirateBenTUnited Arab EmiratesBesWEmiratos Ãrabes UnidosBfrTÉmirats Arabes UnisBjaXアラブ首長国連邦 vWEmirados Ãrabes UnidosBru]Объединенные ÐрабÑкие Эмираты ŽX阿拉伯è”åˆé…‹é•¿å›½!aä!k¡d!}h@8!h@K!¢JAsia/Dubai!È($ä )"ö ¤ã ë† ÊBAM èBdeHArmenienBenGArmeniaBes(€BfrHArménieBjaXアルメニア共和国 vHArmêniaBruNÐÑ€Ð¼ÐµÐ½Ð¸Ñ ŽL亚美尼亚!aä!k¡d!}h@D!h@F€!¢LAsia/Yerevan!È(bä ) 3 ¤ä Ã(8 ³ È ÊBDK èBdeIDänemarkBenGDenmarkBesIDinamarcaBfrHDanemarkBjaUデンマーク王国 v(HBruJÐ”Ð°Ð½Ð¸Ñ ŽF丹麦!aä!k¡d!}h@L!h@$!¢QEurope/Copenhagen!È(ä ) 3 ¤ã Ã.k± ÊBIM èBdeIInsel ManBenKIsle of ManBesKIsla de ManBfrKÃŽle de ManBjaIマン島 vKIlha de ManBruMМÑн, о-в ŽF曼岛!aä!k¡d!}h@K !hÀ!¢REurope/Isle_of_Man!È(Ðä ) 3 ¤ã Ã$ÌB ÊBGI çBdeIGibraltarBen( žBes( žBfr( žBjaRジブラルタル v( žBruRГибралтар!aä!k¡d!}h@Bõɰ!hÀffffff!¢PEurope/Gibraltar!È( Œ«ÍïMaxMind.comé[binary_format_major_version¡[binary_format_minor_version Kbuild_epoch_¶.Mdatabase_typeKGeoIP2-CityKdescriptionâBen]*GeoIP2 City Test Database (fake GeoIP2 data, for example purposes only)BzhOå°åž‹æ•°æ®åº“Jip_version¡IlanguagesBenBzhJnode_count­Krecord_size¡weakforced-2.10.2/docker/logstash/templates/000077500000000000000000000000001461473602600210105ustar00rootroot00000000000000weakforced-2.10.2/docker/logstash/templates/wforce_template.json000066400000000000000000000023051461473602600250630ustar00rootroot00000000000000{ "index_patterns" : ["logstash-wforce*"], "settings" : { "index.refresh_interval" : "5s"}, "mappings" : { "dynamic_templates" : [ { "minor_fields" : { "match" : "*minor", "mapping" : { "type" : "integer", "index" : true } } }, { "major_fields" : { "match" : "*major", "mapping" : { "type" : "integer", "index" : true } } }, { "string_fields" : { "match_mapping_type" : "string", "mapping" : { "type" : "keyword", "index" : true } } } ], "properties" : { "geoip" : { "dynamic": true, "properties" : { "ip": { "type": "ip" }, "location" : { "type" : "geo_point" }, "latitude" : { "type" : "half_float" }, "longitude" : { "type" : "half_float" } } }, "policy_reject": { "type": "boolean" }, "success": { "type": "boolean"}, "tls": { "type": "boolean" }, "t": { "type": "float" } } } } weakforced-2.10.2/docker/redis/000077500000000000000000000000001461473602600162745ustar00rootroot00000000000000weakforced-2.10.2/docker/redis/conf/000077500000000000000000000000001461473602600172215ustar00rootroot00000000000000weakforced-2.10.2/docker/redis/conf/redis.conf000066400000000000000000000000771461473602600212020ustar00rootroot00000000000000user neil on +@all ~* >barfoo user default on +@all ~* >barfoo weakforced-2.10.2/docker/regression/000077500000000000000000000000001461473602600173465ustar00rootroot00000000000000weakforced-2.10.2/docker/regression/Dockerfile000066400000000000000000000042711461473602600213440ustar00rootroot00000000000000FROM ubuntu:jammy as wforce_regression ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && \ apt-get dist-upgrade -y && \ apt-get -y -f install \ autoconf \ automake \ build-essential \ libboost-all-dev \ libcurl4-openssl-dev \ libgetdns-dev \ libhiredis-dev \ libmaxminddb-dev \ liblua5.1-0-dev \ libluajit-5.1-dev \ libprotobuf-dev \ libssl-dev \ libsodium-dev \ libsystemd-dev \ libyaml-cpp-dev \ libtool \ libjsoncpp-dev \ libz-dev \ uuid-dev \ pkg-config \ protobuf-compiler \ pandoc \ wget \ nginx \ docker \ docker-compose \ python3-pip \ python3-venv \ prometheus \ geoip-bin \ geoip-database \ geoipupdate \ net-tools \ clang \ cmake RUN pip3 install bottle virtualenv WORKDIR /wforce/ RUN mkdir /sdist RUN git clone https://github.com/jupp0r/prometheus-cpp.git RUN cd prometheus-cpp && git checkout tags/v1.0.1 -b v1.0.1 RUN cd prometheus-cpp && mkdir _build && cd _build && cmake .. -DBUILD_SHARED_LIBS=off -DENABLE_PULL=off -DENABLE_PUSH=off -DENABLE_COMPRESSION=off -DENABLE_TESTING=off && make && make install RUN git clone https://github.com/drogonframework/drogon.git RUN cd drogon && git checkout tags/v1.9.1 -b v1.9.1 RUN cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_REDIS=OFF && make && make install ADD CHANGELOG.md configure.ac ext LICENSE Makefile.am README.md NOTICE /wforce/ COPY m4/ /wforce/m4/ COPY ext/ /wforce/ext/ COPY docs/ /wforce/docs/ COPY regression-tests/ /wforce/regression-tests/ COPY wforce/ /wforce/wforce/ COPY common/ /wforce/common/ COPY trackalert/ /wforce/trackalert/ COPY docker/ /wforce/docker/ COPY elk/ /wforce/elk/ RUN rm -rf regression-tests/.venv CMD ["sleep", "infinity"] weakforced-2.10.2/docker/regression/regression.sh000077500000000000000000000015611461473602600220700ustar00rootroot00000000000000#!/bin/bash set -e if [ $# -ne 4 ] then export MYCC=clang export MYCXX=clang++ export SODIUM= export TESTFILE=pytest.xml else export MYCC=$1 export MYCXX=$2 export SODIUM=$3 export TESTFILE=$4 fi echo "CC=$MYCC" echo "CXX=$MYCXX" echo "TESTFILE=$TESTFILE" autoreconf -v -i -f ./configure --enable-trackalert --enable-systemd --disable-docker --enable-unit-tests --enable-asan --enable-ubsan $SODIUM --disable-silent-rules CC=$MYCC CXX=$MYCXX make clean make make check || (cat common/test-suite.log && false) cd regression-tests ./runtests $TESTFILE cd .. make dist export WF_VERSION=`grep PACKAGE_VERSION Makefile | awk '{ print $3}'` tar xvf wforce-$WF_VERSION.tar.gz cd wforce-$WF_VERSION autoreconf -i cd .. rm -rf build mkdir build cd build ../wforce-$WF_VERSION/configure --enable-trackalert CC=$MYCC CXX=$MYCXX make cd .. make distcheck weakforced-2.10.2/docker/weakforced000077700000000000000000000000001461473602600173542..ustar00rootroot00000000000000weakforced-2.10.2/docker/wforce_image/000077500000000000000000000000001461473602600176155ustar00rootroot00000000000000weakforced-2.10.2/docker/wforce_image/.gitignore000066400000000000000000000000261461473602600216030ustar00rootroot00000000000000/.wforce_image_docker weakforced-2.10.2/docker/wforce_image/Dockerfile000066400000000000000000000113441461473602600216120ustar00rootroot00000000000000FROM debian:bookworm-slim as wforce_build RUN apt-get update && \ apt-get dist-upgrade -y && \ apt-get -y -f install \ autoconf \ automake \ libboost-all-dev \ libcurl4-openssl-dev \ libgetdns-dev \ libhiredis-dev \ libmaxminddb-dev \ liblua5.1-0-dev \ libluajit2-5.1-dev \ libprotobuf-dev \ libssl-dev \ libsystemd-dev \ libyaml-cpp-dev \ libjsoncpp-dev \ libz-dev \ prometheus-cpp-dev \ uuid-dev \ libtool \ pkg-config \ protobuf-compiler \ pandoc \ wget \ docker \ docker-compose \ net-tools \ clang \ cmake WORKDIR /wforce/ RUN mkdir /wforce/install RUN git clone https://github.com/drogonframework/drogon.git RUN cd drogon && git checkout tags/v1.9.1 -b v1.9.1 RUN cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_REDIS=OFF -DCMAKE_BUILD_TYPE=Release -DBUILD_ORM=OFF && make && make install ADD CHANGELOG.md configure.ac ext LICENSE Makefile.am README.md NOTICE /wforce/ COPY m4/ /wforce/m4/ COPY ext/ /wforce/ext/ COPY docs/ /wforce/docs/ COPY wforce/ /wforce/wforce/ COPY common/ /wforce/common/ COPY trackalert/ /wforce/trackalert/ COPY docker/Makefile.am /wforce/docker/ RUN mkdir /wforce/docker/wforce_image COPY docker/wforce_image/Makefile.am /wforce/docker/wforce_image COPY elk/ /wforce/elk/ RUN autoreconf -ivf RUN ./configure --prefix /usr --enable-trackalert --disable-systemd --disable-sodium --disable-docker --with-luajit --sysconfdir=/etc/wforce CC=clang CXX=clang++ RUN make clean RUN make install DESTDIR=/wforce/install FROM debian:bookworm-slim as wforce_image WORKDIR /wforce/ COPY --from=wforce_build /wforce/install/ / # Remove the default wforce.conf and trackalert.conf files RUN rm /etc/wforce/wforce.conf /etc/wforce/trackalert.conf RUN apt-get update && \ apt-get dist-upgrade -y && \ apt-get -y -f install \ libboost-date-time1.74.0 \ libboost-regex1.74.0 \ libboost-system1.74.0 \ libboost-filesystem1.74.0 \ libcurl4 \ libgetdns10 \ libhiredis0.14 \ libluajit2-5.1-2 \ libmaxminddb0 \ libreadline8 \ libprotobuf32 \ libprometheus-cpp-core1.0 \ libssl3 \ libyaml-cpp0.7 \ libjsoncpp-dev \ libz-dev \ uuid-dev \ gnupg \ python3 \ python3-jinja2 \ net-tools \ tini RUN groupadd -g 1000 wforce && \ useradd --uid 1000 -N -M -r --gid 1000 wforce && \ chmod -R 0775 /etc/wforce && \ chgrp -R 1000 /etc/wforce COPY docker/wforce_image/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh COPY docker/wforce_image/create_config.sh /usr/bin/create_config.sh COPY docker/wforce_image/wforce.conf.j2 /etc/wforce COPY docker/wforce_image/trackalert.conf.j2 /etc/wforce RUN chmod 0775 /usr/bin/docker-entrypoint.sh RUN chmod 0775 /usr/bin/create_config.sh RUN rm -rf /wforce/* ARG build_date ARG license ARG git_revision ARG version ENV WFORCE_VERBOSE=0 ENV WFORCE_HTTP_PASSWORD= ENV WFORCE_HTTP_PORT=8084 ENV WFORCE_LOGSTASH_URL= ENV WFORCE_CONFIG_FILE= ENV TRACKALERT= ENV TRACKALERT_HTTP_PORT=8085 ENV TRACKALERT_HTTP_PASSWORD= ENV TRACKALERT_CONFIG_FILE= LABEL org.label-schema.license="${license}" \ org.label-schema.name="Weakforced" \ org.label-schema.schema-version="1.0" \ org.label-schema.url="https://powerdns.github.io/weakforced" \ org.label-schema.usage="https://powerdns.github.io/weakforced" \ org.label-schema.vcs-ref="${git_revision}" \ org.label-schema.vcs-url="https://github.com/PowerDNS/weakforced" \ org.label-schema.vendor="PowerDNS" \ org.label-schema.version="${version}" \ org.opencontainers.image.documentation="https://powerdns.github.io/weakforced" \ org.opencontainers.image.licenses="${license}" \ org.opencontainers.image.revision="${git_revision}" \ org.opencontainers.image.source="https://github.com/PowerDNS/weakforced" \ org.opencontainers.image.title="Weakforced" \ org.opencontainers.image.url="https://powerdns.github.io/weakforced" \ org.opencontainers.image.vendor="PowerDNS" \ org.opencontainers.image.version="${version}" USER wforce:wforce EXPOSE 8084 ENTRYPOINT ["/usr/bin/tini", "-v", "--", "/usr/bin/docker-entrypoint.sh"] # Dummy overridable parameter parsed by entrypoint CMD ["wfwrapper"] weakforced-2.10.2/docker/wforce_image/Dockerfile.minimal000066400000000000000000000104301461473602600232320ustar00rootroot00000000000000FROM alpine:3.19 as wforce_build RUN apk update && \ apk add \ autoconf \ automake \ boost-dev \ curl-dev \ openssl-dev \ hiredis-dev \ readline-dev \ libmaxminddb-dev \ luajit-dev \ protobuf-dev \ yaml-cpp-dev \ jsoncpp-dev \ zlib-dev \ libtool \ pkgconf \ protobuf \ wget \ util-linux-dev \ make \ cmake \ git \ binutils \ g++ WORKDIR /wforce/ RUN mkdir /wforce/install RUN git clone https://github.com/drogonframework/drogon.git RUN cd drogon && git checkout tags/v1.9.1 -b v1.9.1 RUN cd drogon && git submodule init && git submodule update && mkdir _build && cd _build && cmake .. -DBUILD_BROTLI=OFF -DBUILD_C-ARES=OFF -DBUILD_REDIS=OFF -DCMAKE_BUILD_TYPE=Release -DBUILD_ORM=OFF && make && make install RUN git clone https://github.com/jupp0r/prometheus-cpp.git RUN cd prometheus-cpp && git checkout tags/v1.0.1 -b v1.0.1 RUN cd prometheus-cpp && \ mkdir _build && cd _build && cmake .. -DBUILD_SHARED_LIBS=off -DENABLE_PULL=off \ -DENABLE_PUSH=off -DENABLE_COMPRESSION=off -DENABLE_TESTING=off \ -DCMAKE_POSITION_INDEPENDENT_CODE=ON && make && make install RUN wget https://getdnsapi.net/dist/getdns-1.7.3.tar.gz && tar -xvf getdns-1.7.3.tar.gz RUN cd getdns-1.7.3 && cmake . -DCMAKE_BUILD_TYPE=Release -DENABLE_SHARED=off -DENABLE_STUB_ONLY=on -DBUILD_GETDNS_QUERY=off -DBUILD_GETDNS_SERVER_MON=off -DBUILD_TESTING:BOOL=OFF -DUSE_LIBIDN2=off && make install ADD CHANGELOG.md configure.ac ext LICENSE Makefile.am README.md NOTICE /wforce/ COPY m4/ /wforce/m4/ COPY ext/ /wforce/ext/ COPY docs/ /wforce/docs/ COPY wforce/ /wforce/wforce/ COPY common/ /wforce/common/ COPY trackalert/ /wforce/trackalert/ COPY docker/Makefile.am /wforce/docker/ RUN mkdir /wforce/docker/wforce_image COPY docker/wforce_image/Makefile.am /wforce/docker/wforce_image COPY elk/ /wforce/elk/ RUN autoreconf -ivf RUN ./configure --prefix /usr --enable-trackalert --disable-systemd --disable-sodium --disable-docker --with-luajit --sysconfdir=/etc/wforce RUN make clean RUN make install DESTDIR=/wforce/install RUN strip /wforce/install/usr/bin/wforce /wforce/install/usr/bin/trackalert # Remove the default wforce.conf and trackalert.conf files RUN rm /wforce/install/etc/wforce/wforce.conf /wforce/install/etc/wforce/trackalert.conf FROM alpine:3.19 as wforce_image WORKDIR /wforce/ COPY --from=wforce_build /wforce/install/ / RUN apk update && \ apk add \ boost-filesystem \ curl \ openssl \ hiredis \ readline \ libmaxminddb \ luajit \ libprotobuf \ yaml-cpp \ jsoncpp \ zlib \ protobuf \ abseil-cpp \ abseil-cpp-flags-marshalling \ util-linux \ tini RUN addgroup --gid 1000 wforce && \ adduser -u 1000 -S -G wforce wforce && \ chmod -R 0775 /etc/wforce && \ chgrp -R 1000 /etc/wforce ARG license ARG git_revision ARG version LABEL org.label-schema.license="${license}" \ org.label-schema.name="Weakforced" \ org.label-schema.schema-version="1.0" \ org.label-schema.url="https://powerdns.github.io/weakforced" \ org.label-schema.usage="https://powerdns.github.io/weakforced" \ org.label-schema.vcs-ref="${git_revision}" \ org.label-schema.vcs-url="https://github.com/PowerDNS/weakforced" \ org.label-schema.vendor="PowerDNS" \ org.label-schema.version="${version}" \ org.opencontainers.image.documentation="https://powerdns.github.io/weakforced" \ org.opencontainers.image.licenses="${license}" \ org.opencontainers.image.revision="${git_revision}" \ org.opencontainers.image.source="https://github.com/PowerDNS/weakforced" \ org.opencontainers.image.title="Weakforced" \ org.opencontainers.image.url="https://powerdns.github.io/weakforced" \ org.opencontainers.image.vendor="PowerDNS" \ org.opencontainers.image.version="${version}" USER wforce:wforce EXPOSE 8084 ENTRYPOINT ["tini", "--"] CMD ["/usr/bin/wforce", "-D"] weakforced-2.10.2/docker/wforce_image/Makefile.am000066400000000000000000000027211461473602600216530ustar00rootroot00000000000000DCMP = docker-compose WFORCE_IMAGE_COMPOSE_TARGET = .wforce_image_docker WFORCE_IMAGE_SERVICE = wforce_image TRACKALERT_IMAGE_SERVICE = trackalert COMPOSE_SOURCE=Dockerfile create_config.sh docker-compose.yml docker-entrypoint.sh wforce.conf.j2 export BUILD_DATE= $(shell date) export LICENSE= $(shell echo GPLv3) export GIT_REVISION= $(shell git rev-parse --short HEAD) export VERSION = $(shell git describe --tags) export WFORCE_PASSWORD = super export WFORCE_PORT = 18084 export TRACKALERT_PORT = 18085 $(WFORCE_IMAGE_COMPOSE_TARGET): $(shell find weakforced/common -type f) $(shell find weakforced/wforce -type f) $(shell find weakforced/trackalert -type f) $(shell find weakforced/ext -type f) $(COMPOSE_SOURCE) $(DCMP) down -v $(DCMP) build touch $(WFORCE_IMAGE_COMPOSE_TARGET) start: $(DCMP) up -d stop: $(DCMP) stop kill: $(DCMP) kill clean_docker: docker-compose down -v clean: clean_docker build_wforce_image: $(WFORCE_IMAGE_COMPOSE_TARGET) test_wforce_image: build_wforce_image $(DCMP) up -d $(DCMP) exec -T $(WFORCE_IMAGE_SERVICE) test -f /usr/bin/wforce $(DCMP) exec -T $(WFORCE_IMAGE_SERVICE) test -f /usr/bin/trackalert curl -u foo:$(WFORCE_PASSWORD) http://localhost:$(WFORCE_PORT)/metrics $(DCMP) exec -T $(WFORCE_IMAGE_SERVICE) wforce -e 'showStringStatsDB()' | grep "Shards" curl -u foo:$(WFORCE_PASSWORD) http://localhost:$(TRACKALERT_PORT)/metrics $(DCMP) exec -T $(TRACKALERT_IMAGE_SERVICE) trackalert -e 'stats()' | grep reports $(DCMP) stop weakforced-2.10.2/docker/wforce_image/create_config.sh000066400000000000000000000002261461473602600227410ustar00rootroot00000000000000#!/bin/bash python3 -c 'import os import sys import jinja2 sys.stdout.write( jinja2.Template(sys.stdin.read() ).render(env=os.environ))' <$1 >$2 weakforced-2.10.2/docker/wforce_image/docker-compose.yml000066400000000000000000000015441461473602600232560ustar00rootroot00000000000000version: '2' services: # This is the optimized image that will go onto Docker Hub wforce_image: image: powerdns/wforce:${VERSION} build: context: weakforced dockerfile: docker/wforce_image/Dockerfile args: - license=${LICENSE} - git_revision=${GIT_REVISION} - version=${VERSION} - MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY} environment: - WFORCE_VERBOSE - WFORCE_HTTP_PORT=${WFORCE_PORT} - WFORCE_HTTP_PASSWORD=${WFORCE_PASSWORD} - WFORCE_LOGSTASH_URL - WFORCE_CONFIG_FILE ports: - "${WFORCE_PORT}:${WFORCE_PORT}" trackalert: image: powerdns/wforce:${VERSION} environment: - TRACKALERT=1 - TRACKALERT_HTTP_PORT=${TRACKALERT_PORT} - TRACKALERT_HTTP_PASSWORD=${WFORCE_PASSWORD} ports: - "${TRACKALERT_PORT}:${TRACKALERT_PORT}"weakforced-2.10.2/docker/wforce_image/docker-entrypoint.sh000077500000000000000000000054441461473602600236430ustar00rootroot00000000000000#!/bin/bash set -e umask 0002 # Allow user to specify custom CMD if [[ "$1" == "wfwrapper" ]]; then # Rewrite CMD args to remove the explicit command, set -- "${@:2}" else # Run whatever command the user wanted exec "$@" fi if [[ "x$TRACKALERT" == "x" ]]; then WFORCE_CMD="/usr/bin/wforce -D" if [[ "$WFORCE_VERBOSE" -eq "1" ]]; then WFORCE_CMD="$WFORCE_CMD -v" fi # Check if they just supplied their own wforce.conf file if [[ -f /etc/wforce/wforce.conf ]]; then echo "Using supplied wforce config file /etc/wforce/wforce.conf" WFORCE_CONFIG_FILE=/etc/wforce/wforce.conf fi if [[ "x$WFORCE_CONFIG_FILE" == "x" ]]; then if [[ "x$WFORCE_HTTP_PASSWORD" == "x" ]]; then 2>&1 echo "WFORCE_HTTP_PASSWORD environment variable must be set" else echo "Note you are using the default config file - to take full advantage of the capabilities of weakforced, you should specify a custom config file with the WFORCE_CONFIG_FILE environment variable or just replace /etc/wforce/wforce.conf with your own config file." MYKEY=`cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32 | base64` export WFORCE_KEY="setKey(\"$MYKEY\")" create_config.sh /etc/wforce/wforce.conf.j2 /etc/wforce/wforce.conf echo "Starting $WFORCE_CMD" exec $WFORCE_CMD fi else WFORCE_CMD="$WFORCE_CMD -C $WFORCE_CONFIG_FILE" exec $WFORCE_CMD fi else TRACKALERT_CMD="/usr/bin/trackalert -D" if [[ "$WFORCE_VERBOSE" -eq "1" ]]; then TRACKALERT_CMD="$TRACKALERT_CMD -v" fi # Check if they just supplied their own trackalert.conf file if [[ -f /etc/wforce/trackalert.conf ]]; then echo "Using supplied trackalert config file /etc/wforce/trackalert.conf" TRACKALERT_CONFIG_FILE=/etc/wforce/trackalert.conf fi if [[ "x$TRACKALERT_CONFIG_FILE" == "x" ]]; then if [[ "x$TRACKALERT_HTTP_PASSWORD" == "x" ]]; then 2>&1 echo "TRACKALERT_HTTP_PASSWORD environment variable must be set" else echo "Note you are using the default config file - to take full advantage of the capabilities of trackalert, you should specify a custom config file with the TRACKALERT_CONFIG_FILE environment variable or just replace /etc/wforce/trackalert.conf with your own config file." MYKEY=`cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32 | base64` export TRACKALERT_KEY="setKey(\"$MYKEY\")" create_config.sh /etc/wforce/trackalert.conf.j2 /etc/wforce/trackalert.conf echo "Starting $TRACKALERT_CMD" exec $TRACKALERT_CMD fi else TRACKALERT_CMD="$TRACKALERT_CMD -C $TRACKALERT_CONFIG_FILE" exec $TRACKALERT_CMD fi fi exit 1 weakforced-2.10.2/docker/wforce_image/trackalert.conf.j2000066400000000000000000000010371461473602600231330ustar00rootroot00000000000000addListener("0.0.0.0:{{env['TRACKALERT_HTTP_PORT']}}", false, "", "", {}) setWebserverPassword("{{env['TRACKALERT_HTTP_PASSWORD']}}") {{env['TRACKALERT_KEY']}} controlSocket("127.0.0.1:4005") addACL("127.0.0.0/8") addACL("192.168.0.0/16") function report(lt) infoLog("Received report", { login=lt.login, remote=lt.remote:tostring(), timestamp=lt.t }) end setReport(report) function background() infoLog("Ran background thread", {}) end setBackground("background", background) cronScheduleBackgroundFunc("* * * * *", "background") weakforced-2.10.2/docker/wforce_image/weakforced000077700000000000000000000000001461473602600222162../..ustar00rootroot00000000000000weakforced-2.10.2/docker/wforce_image/wforce.conf.j2000066400000000000000000000053051461473602600222660ustar00rootroot00000000000000-- This configuration file is provided as an example -- It only scratches the surface of what weakforced policy can do addListener("0.0.0.0:{{env['WFORCE_HTTP_PORT']}}", false, "", "", {}) setWebserverPassword("{{env['WFORCE_HTTP_PASSWORD']}}") {{env['WFORCE_KEY']}} controlSocket("0.0.0.0:4004") -- This controls access to the webserver -- By default wforce allows access to all private IPv4/6 address ranges -- addACL("127.0.0.0/8") {% if env['WFORCE_LOGSTASH_URL'] != '' %} -- Register a webhook for "report" events local config_keys={} config_keys["url"] = "{{env['WFORCE_LOGSTASH_URL']}}" config_keys["secret"] = "verysecretcode" local events = { "report" } addWebHook(events, config_keys) {% endif %} local bulkRetrievers = newNetmaskGroup() local string_find = string.find local field_map = {} field_map["diffFailedPasswords"] = "hll" newStringStatsDB("OneHourDB", 600, 6, field_map) function twreport(lt) if (not lt.success and not lt.policy_reject) then local sdb = getStringStatsDB("OneHourDB") sdb:twAdd(lt.remote, "diffFailedPasswords", lt.pwhash) addrlogin = lt.remote:tostring() .. ":" .. lt.login sdb:twAdd(addrlogin, "diffFailedPasswords", lt.pwhash) end end setReport(twreport) function allow(lt) local sdb = getStringStatsDB("OneHourDB") if(bulkRetrievers:match(lt.remote)) then -- return , , , return 0, "", "bulkRetrievers match", {} end if(sdb:twGet(lt.remote, "diffFailedPasswords") > 50) then return -1, "", "too many different failed password attempts by IP", { attempts=50 } end local addrlogin = lt.remote:tostring() .. ":" .. lt.login if(sdb:twGet(addrlogin, "diffFailedPasswords") > 3) then return 3, "", "too many different failed password attempts by IP/login", { attempts=3 } end -- you *must* return with 4 arguments like this: , , , return 0, "", "", { defaultReturn=1 } end setAllow(allow) function reset(type, login, ip) local sdb = getStringStatsDB("OneHourDB") if (string_find(type, "ip")) then sdb:twReset(ip) -- if you set a non-default prefix for IP addresses, then reset will not necessarily do what you expect -- for example if v4Prefix==24 and you reset an IP address it will reset the stats for all IPs in that range end if (string_find(type, "login")) then -- we do not actually set any login-only keys sdb:twReset(login) end if (string_find(type, "ip") and string_find(type, "login")) then local iplogin = ip:tostring() .. ":" .. login sdb:twReset(iplogin) end return true end setReset(reset) weakforced-2.10.2/docs/000077500000000000000000000000001461473602600146475ustar00rootroot00000000000000weakforced-2.10.2/docs/.gitignore000066400000000000000000000000471461473602600166400ustar00rootroot00000000000000/*.[0-9] /Makefile /Makefile.in /*.amd weakforced-2.10.2/docs/Makefile.am000066400000000000000000000012651461473602600167070ustar00rootroot00000000000000if HAVE_PANDOC if WITH_TRACKALERT TRACKALERT_MANPAGES_TARGET = trackalert.1 trackalert.conf.5 TRACKALERT_APIDOC = trackalert_api.7 endif MANPAGES_TARGET = wforce.1 wforce.conf.5 wforce_webhook.5 wf_dump_entries.1 $(TRACKALERT_MANPAGES_TARGET) APIDOC = wforce_api.7 report_api.7 $(TRACKALERT_APIDOC) SWAG2MD_PROG = swag2md.pl EXTRA_DIST = manpages swagger $(SWAG2MD_PROG) .PHONY: all-manpages all-manpages: $(MANPAGES_TARGET) api-doc: $(APIDOC) dist_man_MANS = $(MANPAGES_TARGET) $(MANPAGES_TARGET): %: manpages/%.md $(PANDOC) -s -t man $< -o $@ $(APIDOC): %: swagger/%.yml $(PERL) $(SWAG2MD_PROG) $< >$@.amd $(PANDOC) -s -t man $@.amd -o $@ endif clean: rm -rf *.1 *.5 *.7 *.amd weakforced-2.10.2/docs/_config.yml000066400000000000000000000000321461473602600167710ustar00rootroot00000000000000theme: jekyll-theme-hackerweakforced-2.10.2/docs/docker/000077500000000000000000000000001461473602600161165ustar00rootroot00000000000000weakforced-2.10.2/docs/docker/wforce_docker.md000066400000000000000000000136301461473602600212570ustar00rootroot00000000000000# Wforce Docker Image Documentation This page documents the usage of the wforce docker image. ## Obtaining the image The image is hosted on docker hub in the powerdns/wforce repository. The image must be retrieved with a specific version tag, There is no version tagged "latest"; check the [wforce github releases page](https://github.com/PowerDNS/weakforced/releases) for the latest version tags. For example: ```` % docker pull powerdns/wforce:v2.2.2 ```` ## Using the image The image supports three different use cases: 1) (The default) - Running the wforce daemon 2) Running the trackalert daemon 3) Running the report_api webservice ## Running the wforce daemon ### Wforce Quickstart To quickly get the wforce docker image running as a container running wforce, you need to do the following: * Set the WFORCE_HTTP_PASSWORD environment variable (it won't start if that is not set). * Start the container, specifying port 8084 to expose the REST API (that port can be changed, see below). For example: ```` % export WFORCE_HTTP_PASSWORD=secret % docker run --env WFORCE_HTTP_PASSWORD -p 8084:8084 powerdns/wforce:v2.4.0 Note you are using the default config file - to take full advantage of the capabilities of weakforced, you should specify a custom config file with the WFORCE_CONFIG_FILE environment variable. Starting /usr/bin/wforce -D [2020-05-05 10:58:10,645] Read configuration from '/etc/wforce/wforce.conf' [2020-05-05 10:58:10,653] Opened MMDB database /usr/share/GeoIP/GeoLite2-City.mmdb (type: GeoLite2-City version: 2.0) [2020-05-05 10:58:10,659] ACL allowing queries from: 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 10.0.0.0/8, 127.0.0.0/8, fc00::/7, ::1/128, 172.16.0.0/12, fe80::/10 [2020-05-05 10:58:10,661] Starting stats reporting thread [2020-05-05 10:58:10,663] WforceWebserver launched on 0.0.0.0:8084 [2020-05-05 10:58:10,665] Accepting control connections on 0.0.0.0:4004 ```` Note the warning about using the default config file - the docker image ships with a configuration that blocks brute-force attacks using a very simple policy. To use the powerful features of wforce you'll need to provide your own configuration file(s). There are some other features that can be configured using environment variables: * WFORCE_LOGSTASH_URL - If you set this to the URL of a logstash service that is configured with an HTTP input, you can send all report and allow data to the logstash instance. You can make use of the [sample logstash configuration](https://github.com/PowerDNS/weakforced/blob/master/docker/logstash/config/logstash.conf) as well as the [sample kibana reports and dashboards](https://github.com/PowerDNS/weakforced/blob/master/elk/kibana/kibana_saved_objects.json) to be able to store and view the data in elasticsearch. * WFORCE_HTTP_PORT - You can change the port that wforce listens on for REST API requests. ### Advanced Configuration To make use of the powerful features of wforce, you'll need to provide your own configuration file. You can do this in several ways, for example: * Mount your own configuration file to replace /etc/wforce/wforce.conf - You can use a variety of mechanisms to do this, such as docker configs, or in a Kubernetes environment a volume populated with a ConfigMap. * Use the WFORCE_CONFIG_FILE environment variable to specify a different configuration file. Be aware that wforce uses the location of the configuration file to look for the mandatory regexes.yaml file (which lives in /etc/wforce/regexes.yaml), so use a different directory at your own risk. ### Verbose Logging To turn on verbose logging for the wforce daemon, set the WFORCE_VERBOSE environment variable to 1, e.g.: ```` % export WFORCE_VERBOSE=1 % docker run --env WFORCE_HTTP_PASSWORD --env WFORCE_VERBOSE -p 8084:8084 powerdns/wforce:v2.4.0 ```` Warning: this generates a lot of logs - you have been warned! ## Running the trackalert daemon ## Quickstart To run trackalert instead of wforce, set the environment variable TRACKALERT=1. You'll also need to set the following environment variables: * Set the TRACKALERT_HTTP_PASSWORD environment variable (it won't start if that is not set). * Start the container, specifying port 8085 to expose the REST API (that port can be changed, see below). For example: ``` % export TRACKALERT=1 % export TRACKALERT_HTTP_PASSWORD=foo % docker run --env TRACKALERT --env TRACKALERT_HTTP_PASSWORD -p 8085:8085 powerdns/wforce:v2.4.0 Note you are using the default config file - to take full advantage of the capabilities of trackalert, you should specify a custom config file with the TRACKALERT_CONFIG_FILE environment variable or just replace /etc/wforce/trackalert.conf with your own config file. Starting /usr/bin/trackalert -D [2020-06-29 16:43:03,961] Read configuration from '/etc/wforce/trackalert.conf' [2020-06-29 16:43:03,964] ACL allowing queries from: 127.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 192.168.0.0/16, ::1/128, fc00::/7, fe80::/10 [2020-06-29 16:43:03,964] Starting stats reporting thread [2020-06-29 16:43:03,964] Accepting control connections on 127.0.0.1:4005 [2020-06-29 16:43:03,964] WforceWebserver launched on 0.0.0.0:8085 ``` Note the warning about using the default config file - the default trackalert configuration file is only an example. To use the powerful features of trackalert you'll need to provide your own configuration file. There are some other features that can be configured using environment variables: * TRACKALERT_HTTP_PORT - You can change the port that trackalert listens on for REST API requests. ### Verbose Logging To turn on verbose logging for the trackalert daemon, set the WFORCE_VERBOSE environment variable to 1, e.g.: ```` % export WFORCE_VERBOSE=1 % docker run --env TRACKALERT --env TRACKALERT_HTTP_PASSWORD --env WFORCE_VERBOSE -p 8084:8084 powerdns/wforce:v2.4.0 ```` Warning: this generates a lot of logs - you have been warned! ## Running the report_api webservice This is no longer supported in the wforce image (>2.8.0) weakforced-2.10.2/docs/index.md000066400000000000000000000066641461473602600163140ustar00rootroot00000000000000# Weakforced Documentation Welcome to the documentation site for [weakforced](https://github.com/PowerDNS/weakforced). Weakforced is an open-souce anti-abuse engine for authentication systems, acting as a policy decision point for email systems, web portals and any other type of system that is open to authentication abuse on the Internet. Weakforced is in production at sites hosting email for over 20 million customers, so it scales extremely well. Here you will find all the manpages for weakforced, which are the primary documentation source. You'll also find an overview of the main features. These pages are for detailed technical documentation; for details about building, getting started, and testing see the main repo [README](https://github.com/PowerDNS/weakforced/blob/master/README.md). To see documentation on the wforce docker image, go [here](docker/wforce_docker.md) Go [here](https://github.com/PowerDNS/weakforced/releases) for release information. # Wforce Daemon The wforce daemon is the main point of integration with authentication systems, and several products are pre-integrated with it, including [Dovecot](https://wiki.dovecot.org/Authentication/Policy) and [OX AppSuite](https://documentation.open-xchange.com/7.10.3/middleware/security_and_encryption/weakforced_connector.html). * [Wforce Daemon](manpages/wforce.1.md) * [Wforce Config File](manpages/wforce.conf.5.md) * [Wforce Webhook Configuration](manpages/wforce_webhook.5.md) * [REST API](https://petstore.swagger.io/?url=https://raw.githubusercontent.com/PowerDNS/weakforced/master/docs/swagger/wforce_api.7.yml) ## Main Features * Lua Policy Engine - Create policy using Lua; infinitely flexible and very quick. * Statistics DB - A built-in, in-memory statistics DB that stores information about authentication activity using one of the three built-in data types (integer, hyperloglog and countmin), using sliding time windows. You can configure multiple statistics DBs, and each DB can be sharded within each instance for better performance. * Persistent Black/Whitelists - Stored in Redis and modified and retrieved using either Lua or the REST API. * Replication - The statistics databases and black/whitelists can be replicated between wforce instances. * DNS Lookup Support - Built-in support for DNS lookups from Lua, using the GetDNS library for extremely fast and low-latency lookups. Includes support for looking up RBL-formatted zones. * Highly Multithreaded - Scales extremely well on multicore servers. * Webhook Support - Provides built-in support for webhooks on core events, as well as custom webhooks from Lua to send any kind of data anywhere. * REST API - All integration uses the built-in REST API. Custom REST API endpoints can be created in Lua, for endless extensibility. * Prometheus Support - Native Support for prometheus metrics on the /metrics endpoint. * GeoIP Support - Retrieve location information of IP addresses using the GeoIP2 API. # Trackalert Daemon The trackalert daemon provides a mechanism to run arbitrary Lua code based on either a wforce "report" event and/or on a scheduled basis. This is often used to trigger lookups into databases such as Elasticsearch to find anomalous login events. Wforce is pre-integrated with trackalert - you can just create a webhook on "report" events, providing the address and authentication details for the trackalert server. * [Trackalert Daemon](manpages/trackalert.1.md) * [Trackalert Config File](manpages/trackalert.conf.5.md) weakforced-2.10.2/docs/manpages/000077500000000000000000000000001461473602600164425ustar00rootroot00000000000000weakforced-2.10.2/docs/manpages/trackalert.1.md000066400000000000000000000111031461473602600212530ustar00rootroot00000000000000% TRACKALERT(1) % Open-Xchange % 2018 # NAME **trackalert** - daemon to track and alert on long-term abuse trends for logins # SYNOPSIS trackalert [*OPTION*]... # DESCRIPTION **trackalert** implements a simple HTTP server that accepts JSON formatted commands that report successful/unsuccessful logins. **trackalert** can act as both a client and server. As a server it typically runs under systemd control, although it can also be run as a traditional daemon or in 'interactive' mode. As a client, it connects to a trackalert server and provides the same interactive commands. **trackalert** is scriptable in Lua, see the trackalert.conf file for a simple example. In fact, all configuration is done using the Lua language, as trackalert.conf is simply a Lua script. # SCOPE **trackalert** depends on a system to feed it login reports, which will typically be wforce (configured with a webhook that triggers only on "report") integrated with it using the HTTP/JSON API. # OPTIONS -c : Act as a client, connecting to a trackalert instance at the IP/Port specified in the 'controlSocket' function in trackalert.conf. A custom configuration file can be specified. -C,--config *FILE* : Load configuration from *FILE*. Note that trackalert will chdir to the directory where the configuration file is located. -R,--regexes *FILE* : Read device parsing regexes from *FILE* (usually regexes.yaml). -s : Run in foreground, but do not spawn a console. Use this switch to run trackalert inside a supervisor (use with e.g. systemd and daemontools). -d,--daemon : Operate as a daemon. -e,--execute *CMD* : Connect to trackalert and execute *CMD*. -f,--facility *FACILITY NAME* : Log using the specified facility name, e.g. local0 -l,--loglevel <0-7> : Logs sent to stdout will be filtered according to the specified log level, matching the equivalent syslog level (0 - Emerg to 7 - Debug). -h,--help : Display a helpful message and exit. # CONSOLE COMMANDS The following commands can be run from the console when *trackalert* is started with the -c option. * makeKey() - Returns a string to be used in the setKey() function in trackalert.conf to authenticate sibling communications. All siblings must be configured with the same key. > makeKey() setKey("CRK+jKBpzXNLmM2A4C7OpFCBxiwpYlreCWgGEAIKAQI=") * stats() - Returns statistics about the trackalert process. For example: > stats() 40 reports * showACL() - Returns the configured ACLs for the trackalert server. > showACL() 127.0.0.0/8 10.0.0.0/8 100.64.0.0/10 169.254.0.0/16 192.168.0.0/16 172.16.0.0/12 ::1/128 fc00::/7 fe80::/10 * showCustomWebHooks() - Returns information about configured custom webhooks. For example: > showCustomWebHooks() ID Name Successes Failures URL 1 mycustomhook 10 0 http://localhost:8080/webhook/regression * showPerfStats() - Returns information about performance statistics. Stats beginning with WTW refer to the time that worker threads waited in a queue before running. Stats beginning with WTR refer to the time that worker threads took to run. Each stat is in a bucket, where each bucket represents a time range in ms, e.g. 0-1. A server that is not overloaded will have most stats in the 0-1 buckets. For example: > showPerfStats() WTW_0_1=2939287 WTW_1_10=9722 WTW_10_100=4 WTW_100_1000=0 WTW_Slow=0 WTR_0_1=2939229 WTR_1_10=2837 WTR_10_100=131 WTR_100_1000=0 WTR_Slow=0 * showCommandStats() - Returns information about the number of REST API commands that have been called, including custom endpoints. Stats are for the previous 5 mins, and due to the counting method, may be approximate when the numbers get very large. For example: > showCommandStats() addBLEntry=0 allow=23942 delBLEntry=0 getBL=0 getDBStats=0 ping=300 report=19232 reset=24 stats=92 customEndpoint=2821 * showCustomStats() - Returns information about custom stats that are incremented from Lua. Stats are for the previous 5 mins, and due to the counting method, may be approximate when the numbers get very large. For example: > showCustomStats() custom1=0 custom2=8405 * reloadGeoIPDBs() - Reload all GeoIP DBs that have been initialized. For example: > reloadGeoIPDBs reloadGeoIPDBs() successful * showVersion() - Returns the current version of the trackalert server. For example: > showVersion() trackalert 1.2.0 # SEE ALSO trackalert.conf(5) trackalert_api(7) weakforced-2.10.2/docs/manpages/trackalert.conf.5.md000066400000000000000000000506051461473602600222150ustar00rootroot00000000000000% TRACKALERT.CONF(5) % Open-Xchange % 2018 # NAME **trackalert.conf** - configuration file for trackalert daemon # DESCRIPTION This file is read by **trackalert** and is a Lua script containing Lua commands to implement (a) configuration of the trackalert daemon and (b) functions that control the operation of the trackalert daemon in response to HTTP commands, specifically "report". An alternative version of this file can be specified with trackalert -C private_trackalert.conf ... # CONFIGURATION-ONLY FUNCTIONS The following functions are for configuration of trackalert only, and cannot be called inside the report or background functions: * setACL(\) - Set the access control list for the HTTP Server. For example, to allow access from any IP address, specify: setACL({"0.0.0.0/0"}) * addACL(\) - Add a netmask to the access control list for the HTTP server. For example, to allow access from 127.0.0.0/8, specify: addACL("127.0.0.0/8") * addCustomWebHook(\, \) - Add a custom webhook, i.e. one which can be called from Lua (using "runCustomWebHook()" - see below) with arbitrary data, using the specified configuration keys. See *trackalert_webhook(5)* for the supported config keys, and details of the HTTP(S) POST sent to webhook URLs. For example: config_keys={} config_keys["url"] = "http://webhooks.example.com:8080/webhook/" config_keys["secret"] = "verysecretcode" addCustomWebHook("mycustomhook", config_keys) * addCustomStat(\) - Add a custom counter which can be used to track statistics. The stats for custom counters are logged every 5 minutes. The counter is incremented with the "incCustomCounter" command. For example: addCustomStat("custom_stat1") * webserver(\, \) - (*deprecated - see addListener() instead*) Listen for HTTP commands on the specified IP address and port. The password is used to authenticate client connections using basic authentication. For example: webserver("0.0.0.0:8084", "super") * addListener(\, \, \, \, \) - (*replacement for webserver()*) Listen for HTTP commands on the specified IP address and port. If useSSL is true, then HTTPS must be used, and cert_file and key file are used, otherwise they are empty. Options contains a list of key value pairs to configure the TLS connection; these follow the command line option names in https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html. For example, "min_protocol" to set the minimum TLS protocol version. You can add as many listeners as you choose. For example: addListener("0.0.0.0:8084", false, "", "", {}) addListener("1.2.3.4:1234", true, "/etc/wforce/cert.pem", "/etc/wforce/key.pem", {minimum_protocol="TLSv1.2"}) addListener("[::1]:9000", true, "/etc/wforce/cert.pem", "/etc/wforce/key.pem", {minimum_protocol="TLSv1.3"}) * setWebserverPassword(\) - (*replacement for webserver password parameter*) Sets the basic authentication password for access to the webserver. This has been decoupled from the addListener() command because multiple listeners can now be created, which was not previously possible. For example: setWebserverPassword("foobar") * controlSocket(\) - Listen for control connections on the specified IP address and port. If port is not specified it defaults to 5199. For example: controlSocket("0.0.0.0:4004") * setKey(\) - Use the specified key for authenticating connections from siblings. The key must be generated with makeKey() from the console. See *trackalert(1)* for instructions on running a console client. Returns false if the key could not be set (e.g. invalid base64). For example: if not setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") then ... * setNumLuaStates(\) - Set the number of Lua Contexts that will be created to run report commands. Defaults to 10 if not specified. See also setNumReportThreads() and setNumSchedulerThreads(). Should be at least equal to NumReportThreads \+ NumSchedulerThreads. For example: setNumLuaStates(10) * setNumWorkerThreads(\) - Set the number of threads in the pool used to receive webserver commands. Note that the report function itself is run in a separate report thread pool controlled by setNumReportThreads(). Defaults to 4 if not specified. For example: setNumWorkerThreads(4) * setMaxWebserverConns(\) - Set the maximum number of active connections to the webserver. This can be used to limit the effect of too many queries to trackalert. It defaults to 10,000. For example: setMaxWebserverConns(5000) * setNumReportThreads(\) - Set the number of threads in the pool used to run Lua report functions. Each thread uses a separate Lua Context, (see setNumLuaStates()). Defaults to 6 if not specified. For example: setNumReportThreads(6) * setNumSchedulerThreads(\) - Set the number of threads in the pool used to run scheduled background Lua functions. Each thread uses a separate Lua Context, the number of which is set with setNumLuaStates(). Defaults to 4 if not specified. For example: setNumSchedulerThreads(2) * setNumWebHookThreads(\) - Set the number of threads in the pool used to send webhook events. Defaults to 4 if not specified. For example: setNumWebHookThreads(2) * newGeoIP2DB(\, \) - Opens and initializes a GeoIP2 database. A name must be chosen, and the filename of the database to open must also be supplied. To obtain an object allowing lookups to be performed against the database, use the getGeoIP2DB() function. For example: newGeoIP2DB("CityDB", "/usr/share/GeoIP/GeoLite2-City.mmdb") * initGeoIPDB() - (Deprecated - use newGeoIP2DB()). Initializes the country-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and trackalert will not start. For example: initGeoIPDB() * initGeoIPCityDB() - (Deprecated - use newGeoIP2DB()). Initializes the city-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and trackalert will not start. Ensure these databases have the right names if you're using the free/lite DBs - you may need to create symbolic links e.g. GeoIPCityv6.dat -> GeoLiteCityv6.dat. For example: initGeoIPCityDB() * initGeoIPISPDB() - (Deprecated - use newGeoIP2DB()). Initializes the ISP-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and trackalert will not start. For example: initGeoIPISPDB() * setNumWebHookThreads(\) - Set the number of threads in the pool used to send webhook events. Defaults to 5 if not specified. For example: setNumWebHookThreads(2) * setNumWebHookConnsPerThread(\) - Set the maximum number of connections used by each WebHook thread. Defaults to 10 if not specified. This setting replaces the deprecated "num_conns" per-hook configuration setting. For example: setNumWebHookConnsPerThread(50) * setWebHookQueueSize(\) - Set the size of the queue for webhook events. If the queue gets too big, then webhooks will be discarded, and an error will be logged. The default queue size is 50000, which should be appropriate for most use-cases. setWebHookQueueSize(100000) * setWebHookTimeoutSecs(\) - Set the maximum time a request can take for webhooks. For example: setWebHookTimeoutSecs(2) * setReport(\) - Tell trackalert to use the specified Lua function for handling all "report" commands. For example: setReport(report) * setBackground(\, \ - The setBackground function registers a background function with a given name. The name is used when scheduling the function using the xxxScheduleBackgroundFunc() functions. setBackground("mybg", backgroundFunc) * cronScheduleBackgroundFunc(\, \) - Tells trackalert to run the specified function according to the given cron schedule (note the name given in setBackground() is used, *not* the actual function name). Note that cron ranges are not currently supported - if you want to schedule the same function to run for example on two different days of the week, then you would use two different calls to this function to achieve that. For example: cronScheduleBackgroundFunc("0 0 1 * *", "mybg") cronScheduleBackgroundFunc("0 0 6 * *", "mybg") * intervalScheduleBackgroundFunc(\, \) - Tells trackalert to run the specified function at the interval given in duration string. The duration string is of the format h[h][:mm][:ss], so for example "10" would indicate an interval of 10 hours, "00:00:01" would indicate an interval of 1 second, and "5:01" would indicate an interval of 5 hours and 1 minute. For example: intervalScheduleBackgroundFunc("00:30:00", "mybg") intervalScheduleBackgroundFunc("24", "mybg") * setCustomEndpoint(\, \) - Create a new custom REST endpoint with the given name, which when invoked will call the supplied custom lua function. This allows admins to arbitrarily extend the trackalert REST API with new REST endpoints. Admins can create as many custom endpoints as they require. Custom endpoints can only be accessed via a POST method, and all arguments are passed as key-value pairs of a top-level "attrs" json object (these will be split into two tables - one for single-valued attrs, and the other for multi-valued attrs - see CustomFuncArgs below). Return information is passed with a boolean "success" and "r_attrs" json object containing return key-value pairs. For example: function custom(args) for k,v in pairs(args.attrs) do infoLog("custom func argument attrs", { key=k, value=v }); end -- return consists of a boolean, followed by { key-value pairs } return true, { key=value } end setCustomEndpoint("custom", custom) * disableCurlPeerVerification() - Disable checking of peer certificates in all outbound HTTPS connections. This is not recommended except for debugging or development purposes. For example: disableCurlPeerVerification() * disableCurlHostVerification() - Disable checking of the hostname in the peer certificate for all outbound HTTPS connections. This is not recommended except for debugging or development purposes. disableCurlHostVerification() * setCurlCABundleFile(\) - Gives the location of a file containing the certificate authorities to use for checking HTTPS server certificates. Use this if the standard installed root certs do not contain the certs you need. This should be a file containing 1:N certs in PEM format. setCurlCABundleFile("/etc/ca/local_cas.pem") * setCurlClientCertAndKey(\, \) - Gives the location of the certificate and key files to use for mutual TLS authentication (in PEM format). setCurlClientCertAndKey("/etc/certs/clientcert.pem", "/etc/certs/clientkey.pem") * setMetricsNoPassword() - Disable password protection for the /metrics endpoint. # GENERAL FUNCTIONS The following functions are available anywhere; either as part of the configuration or within the allow/report/reset functions: * getGeoIP2DB(\) - Return an object which can be used to perform GeoIP lookups. The database must first be initialised using newGeoIP2DB(). For example: local citydb = getGeoIP2DB("CityDB") * GeoIP2DB:lookupCountry(\) - Returns the two-character country code of the country that the IP address is located in. A ComboAddress object can be created with the newCA() function. For example: my_country = countrydb:lookupCountry(newCA("8.8.8.8")) my_country = countrydb:lookupCountry(lt.remote) * GeoIP2DB:lookupISP(\) - Returns the name of the ISP hosting the IP address. A ComboAddress object can be created with the newCA() function. For example: local my_isp = ispdb:lookupISP(newCA("128.243.16.21")) * GeoIP2DB:lookupCity(\) - Returns a map containing information about the IP address, such as the city name and latitude and longitude. See GeoIPRecord below for the full list of fields. For example: local gip_record = citydb:lookupCity(lt.remote) local my_city = gip_record.city local my_latitude = gip_record.latitude * lookupCountry(\) - (Deprecated - use the new GeoIP2 function). Returns the two-character country code of the country that the IP address is located in. A ComboAddress object can be created with the newCA() function. For example: my_country = lookupCountry(my_ca) * lookupISP(\) - (Deprecated - use the new GeoIP2 function). Returns the name of the ISP hosting the IP address. A ComboAddress object can be created with the newCA() function. For example: local my_isp = lookupISP(newCA("128.243.16.21")) * lookupCity(\) - (Deprecated - use the new GeoIP2 function). Returns a map containing information about the IP address, such as the city name and latitude and longitude. See GeoIPRecord below for the full list of fields. For example: local gip_record = lookupCity(lt.remote) local my_city = gip_record.city local my_latitude = gip_record.latitude * newCA(\) - Create and return an object representing an IP address (v4 or v6) and optional port. The object is called a ComboAddress. For example: my_ca = newCA("192.128.12.82") * ComboAddress:tostring() - Return a string representing the IP address. For example: mystr = my_ca:tostring() * newNetmaskGroup() - Return a NetmaskGroup object, which is a way to efficiently match IPs/subnets against a range. For example: mynm = newNetmaskGroup() * NetmaskGroup:addMask(\) - Add a mask to the NetmaskGroup, in cidr format. For example: mynm:addMask("182.22.0.0/16") * NetmaskGroup:match(\) - Match an IP address against a NetmaskGroup. The IP address must be a ComboAddress object. For example: if (mynm.match(lt.remote)) then print("ip address matched") end * runCustomWebHook(\, \) - Run a previously configured custom webhook, using the supplied webhook data. By default the Content-Type of the data is "application/json", however this can be changed using the "content-type" config key when configuring the custom webhook. Custom webhooks are run using the same thread pool as normal webhooks. For example: runCustomWebHook(mycustomhook", "{ \"foo\":\"bar\" }") * infoLog(\, \) - Log at LOG_INFO level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map. For example: infoLog("Logging is very important", { logging=1, foo=bar }) * vinfoLog(\, \) - Log at LOG_INFO level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map, but only if trackalert was started with the (undocumented) -v flag (for verbose). For example: vinfoLog("Logging is very important", { logging=1, foo=bar }) * warnLog(\, \) - Log at LOG_WARN level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map. For example: warnLog("Logging is very important", { logging=1, foo=bar }) * errorLog(\, \) - Log at LOG_ERR level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map. For example: errorLog("Logging is very important", { logging=1, foo=bar }) * debugLog(\, \) - Log at LOG_DEBUG level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map, but only if trackalert was started with the (undocumented) -v flag (for verbose). For example: debugLog("This will only log if trackalert is started with -v", { logging=1, foo=bar }) * LoginTuple - The only parameter to the report function is a LoginTuple table. This table contains the following fields: * LoginTuple.remote - An IP address of type ComboAddress, representing the system performing login. * LoginTuple.login - The username of the user performing the login. * LoginTuple.pwhash - A hashed representation of the users password * LoginTuple.success - Whether the user login was successful. * LoginTuple.policy_reject - If the login was not successful only because of a policy-based reject from trackalert (i.e. the username and password were correct). * LoginTuple.attrs - Additional array of (single valued) attributes about the login, e.g. information about the user from LDAP. For example: for k, v in pairs(lt.attrs) do if (k == "xxx") then -- do something end end * LoginTuple.attrs_mv - Additional array of (multi-valued) attributes about the login. For example: for k, v in pairs(lt.attrs_mv) do for i, vi in ipairs(v) do if ((k == "xxx") and (vi == "yyy")) then -- do something end end end * LoginTuple.device_id - An optional string that represents the device that the user logged in from. Also see device_attrs. * LoginTuple.device_attrs - Additional array of attributes about the device, which is parsed from the device_id string. The protocol string is used to determine how to parse device_id, so that MUST also be present. If device_attrs is already populated, it will be used as-is. For all protocols, the following keys are set wherever possible: os.family, os.major, os.minor. For http, the following additional keys are set wherever possible: device.family, device.model, device.brand, browser.family, browser.major, browser.minor. For imap, the following additional keys are set wherever possible: imapc.family, imapc.major, imapc.minor. For mobileapi, the following additional keys are set: app.name, app.brand, app.major, app.minor, device.family. For example: if (lt.device_attrs["os.family"] == "Mac OS X") then -- do something special for macOS end * LoginTuple.protocol - A string representing the protocol that was used to access mail, i.e. http, imap, pop3, mobileapi etc. LoginTuple.protocol MUST be set in order to parse device_id into device_attrs, however currently only http, imap and mobileapi are recognized protocols when parsing device_id. For example: if (lt.protocol == "http") then -- do something end * LoginTuple.tls - A boolean representing whether the login session used TLS or not. If the client is using TLS offload proxies then it may be set to false. * GeoIPRecord - The type returned by the lookupCity() function. See below for fields: * GeoIPRecord.country_code - The two-letter country code e.g. "US". * GeoIPRecord.country_name - The country name, e.g. "United States" * GeoIPRecord.region - The region, e.g. "CA" * GeoIPRecord.city - The city name, e.g. "Mountain View" * GeoIPRecord.postal_code - The postal code, e.g. "93102" or "BA216AS" * GeoIPRecord.latitude - The latitude, e.g. 37.386001586914 * GeoIPRecord.longitude - The longitude, e.g. -122.08380126953 * CustomFuncArgs - The only parameter to custom functions is a CustomFuncArgs table. This table contains the following fields: * CustomFuncArgs.attrs - Array of (single valued) attributes supplied by the caller. For example: for k, v in pairs(args.attrs) do if (k == "xxx") then -- do something end end * CustomFuncArgs.attrs_mv - Array of (multi-valued) attributes supplied by the caller. For example: for k, v in pairs(args.attrs_mv) do for i, vi in ipairs(v) do if ((k == "xxx") and (vi == "yyy")) then -- do something end end end * incCustomStat(\) - Increment a custom statistics counter. The value of the counter will be logged every 5 minutes, and then reset. For example: incCustomStat("custom_stat1") # FILES */etc/wforce/trackalert.conf* # SEE ALSO trackalert(1) weakforced-2.10.2/docs/manpages/wf_dump_entries.1.md000066400000000000000000000031571461473602600223230ustar00rootroot00000000000000% WF_DUMP_ENTRIES(1) % Open-Xchange % 2020 # NAME **wf_dump_entries** - Tool to dump the entries from the wforce stats DBs to stdout or a file # SYNOPSIS wf_dump_entries [-u,--uri wforce uri] [-l,--local-addr address] [-p,--local-port port] [-w,--wforce-pwd password] [-f,--filename file] [-h,--help] # DESCRIPTION **wf_dump_entries** connects to the specified wforce server, and asks it to send a dump of all the stats DBs to itself over TCP/IP. It then sends the results to stdout or a file. **wf_dump_entries** firstly acts as a TCP client, connecting to wforce and invoking the 'dumpEntries' command. Then it acts as a server, receiving the dumped entries, and printing them to stdout or to a file. # SCOPE **wf_dump_entries** requires a running wforce daemon to connect to. # OPTIONS -u *URI* : Provides the URI of the wforce daemon, ideally without a path or query. For example: http://127.0.0.1:8084/. The query "?command=dumpEntries" will be appended to the command if the URI does not end with "dumpEntries". -w,--wforce-pwd *PASSWORD* : The password to use for basic authentication to wforce. -l,--local-addr *IP ADDRESS* : The IP address to use to send the entries to. This must be a configured IP address on the localhost. The wf_dump_entries program will listen on this IP address. -p,--local-port *PORT* : The port number to use when sending the entries. The wf_dump_entries program will listen on this port. -f,--filename *FILENAME* : Send the dumped entries to the specified file instead of stdout. -h,--help : Display a helpful message and exit. # SEE ALSO wforce(1) weakforced-2.10.2/docs/manpages/wforce.1.md000066400000000000000000000200511461473602600204060ustar00rootroot00000000000000% WFORCE(1) % Open-Xchange % 2018 # NAME **wforce** - daemon to detect brute-force login attempts and enforce other policies for logins # SYNOPSIS wforce [*OPTION*]... # DESCRIPTION **wforce** implements a simple HTTP server that accepts JSON formatted commands that report successful/unsuccessful logins and query whether a login should be allowed to proceed. **wforce** can act as both a client and server. As a server it typically runs under systemd control, although it can also be run as a traditional daemon or in 'interactive' mode. As a client, it connects to a wforce server and provides the same interactive commands. **wforce** is scriptable in Lua, see the wforce.conf file for a simple example, and wforce.conf.example for a more sophisticated example. In fact all configuration is done using the Lua language, as wforce.conf is simply a Lua script. # SCOPE **wforce** depends on the systems performing login authentication to integrate with it using the HTTP/JSON API. Example clients of the API include Dovecot and OX AppSuite. **wforce** provides a simple clustering mechanism through the process of configuring "siblings" in wforce.conf. All modifications to the blacklists or the string stats DB (either from Lua or the REST API) will be replicated to all configured siblings. Replication uses the UDP protocol, so if siblings are not on a local subnet, you should ensure that any firewalls forward UDP on the configured ports. # OPTIONS -c : Act as a client, connecting to a wforce instance at the IP/Port specified in the 'controlSocket' function in wforce.conf. A custom configuration file can be specified. -C,--config *FILE* : Load configuration from *FILE*. -R,--regexes *FILE* : Read device parsing regexes from *FILE* (usually regexes.yaml). -s : Run in foreground, but do not spawn a console. Use this switch to run wforce inside a supervisor (use with e.g. systemd and daemontools). -d,--daemon : Operate as a daemon. -e,--execute *CMD* : Connect to wforce and execute *CMD*. -f,--facility *FACILITY NAME* : Log using the specified facility name, e.g. local0 -l,--loglevel <0-7> : Logs sent to stdout will be filtered according to the specified log level, matching the equivalent syslog level (0 - Emerg to 7 - Debug). -h,--help : Display a helpful message and exit. # CONSOLE COMMANDS The following commands can be run from the console when *wforce* is started with the -c option. * makeKey() - Returns a string to be used in the setKey() function in wforce.conf to authenticate sibling communications. All siblings must be configured with the same key. > makeKey() setKey("CRK+jKBpzXNLmM2A4C7OpFCBxiwpYlreCWgGEAIKAQI=") * stats() - Returns statistics about the wforce process. For example: > stats() 40 reports, 8 allow-queries (% denies) * siblings() - Returns information about configured siblings. For example: > siblings() Address Send Successes Send Failures Rcv Successes Rcv Failures Note 127.0.0.1:4001 0 0 17 0 127.0.0.1:4002 0 0 0 0 Self * showNamedReportSinks() - Returns information about configured report sinks. For example: > showNamedReportSinks() Name Address Successes Failures trackalert 192.168.1.79:4501 18 0 trackalert 192.168.1.30:4501 19 0 elasticsearch 10.22.2.15:4501 18 0 elasticsearch 10.22.2.16:4501 19 0 * showReportSinks() - Deprecated - use showNamedReportSinks() instead. Returns information about configured report sinks. For example: > showReportSinks() Address Successes Failures 192.168.1.79:4501 18 7 192.168.1.30:4501 25 0 * showStringStatsDB() - Returns information about configured stats databases. This includes the DB Name/number of shards, whether it is configured for replication, the size and number of windows, the maximum size, the current size, and finally all the configured fields and their types. For example: > showStringStatsDB() DB Name/Shards Repl? Win Size/No Max Size Cur Size Field Name Type MyDB1/1 yes 1/15 524288 0 countLogins int diffPasswords hll MyDB2/10 no 600/6 5000 2093 diffIPs hll * showACL() - Returns the configured ACLs for the wforce server. > showACL() 127.0.0.0/8 10.0.0.0/8 100.64.0.0/10 169.254.0.0/16 192.168.0.0/16 172.16.0.0/12 ::1/128 fc00::/7 fe80::/10 * showWebHooks() - Returns information about configured webhooks. For example: > showWebHooks() ID Successes Failures URL Events 1 5 2 http://localhost:8080/webhook/ report allow * showCustomWebHooks() - Returns information about configured custom webhooks. For example: > showCustomWebHooks() ID Name Successes Failures URL 1 mycustomhook 10 0 http://localhost:8080/webhook/regression * showCustomEndpoints() - Returns information about configured custom endpoints. For example: > showCustomEndpoints() Custom Endpoint Send to Report Sink? custom1 true custom2 false * showPerfStats() - (Deprecated in favour of prometheus metrics - will be removed in a future version). Returns information about performance statistics. Stats beginning with WTW refer to the time that worker threads waited in a queue before running. Stats beginning with WTR refer to the time that worker threads took to run. Each stat is in a bucket, where each bucket represents a time range in ms, e.g. 0-1. A server that is not overloaded will have most stats in the 0-1 buckets. Stats are for the previous 5 minutes. For example: > showPerfStats() WTW_0_1=2939287 WTW_1_10=9722 WTW_10_100=4 WTW_100_1000=0 WTW_Slow=0 WTR_0_1=2939229 WTR_1_10=2837 WTR_10_100=131 WTR_100_1000=0 WTR_Slow=0 * showCommandStats() - (Deprecated in favour of prometheus metrics - will be removed in a future version). Returns information about the number of REST API commands that have been called, including custom endpoints. Stats are for the previous 5 mins, and due to the counting method, may be approximate when the numbers get very large. For example: > showCommandStats() addBLEntry=0 allow=23942 delBLEntry=0 getBL=0 getDBStats=0 ping=300 report=19232 reset=24 stats=92 customEndpoint=2821 * showCustomStats() - (Deprecated in favour of prometheus metrics - will be removed in a future version). Returns information about custom stats that are incremented from Lua. Stats are for the previous 5 mins, and due to the counting method, may be approximate when the numbers get very large. For example: > showCustomStats() custom1=0 custom2=8405 * reloadGeoIPDBs() - Reload all GeoIP DBs that have been initialized. For example: > reloadGeoIPDBs reloadGeoIPDBs() successful * showVersion() - Returns the current version of the wforce server. For example: > showVersion() wforce 1.2.0 # BUGS The replication function of clustering means that as more servers are added to a cluster, incremental performance gains may be less each time, eventually possibly leading to performance degradation. This is because each server keeps a full copy of the stats DBs and the blacklists, and changes to those are replicated to all siblings. This can be mitigated by partitioning siblings into smaller clusters that do not share information, at the expense of missing potential abuse activity. # SEE ALSO wforce.conf(5) wforce_webhook(5) wforce_api(7) weakforced-2.10.2/docs/manpages/wforce.conf.5.md000066400000000000000000001335361461473602600213530ustar00rootroot00000000000000% WFORCE.CONF(5) % Open-Xchange % 2018 # NAME **wforce.conf** - configuration file for wforce daemon # DESCRIPTION This file is read by **wforce** and is a Lua script containing Lua commands to implement (a) configuration of the wforce daemon and (b) functions that control the operation of the wforce daemon in response to HTTP commands, specifically "report", "allow" and "reset". An alternative version of this file can be specified with wforce -C private_wforce.conf ... # CONFIGURATION-ONLY FUNCTIONS The following functions are for configuration of wforce only, and cannot be called inside the allow/report/reset functions: * setACL(\) - Set the access control list for the HTTP Server. For example, to allow access from any IP address, specify: setACL({"0.0.0.0/0"}) * addACL(\) - Add a netmask to the access control list for the HTTP server. For example, to allow access from 127.0.0.0/8, specify: addACL("127.0.0.0/8") * addWebHook(\, \) - Add a webhook for the specified events, with the specified configuration keys. See *wforce_webhook(5)* for the list of events, supported configuration keys, and details of the HTTP(S) POST sent to webhook URLs. For example: config_keys={} config_keys["url"] = "http://webhooks.example.com:8080/webhook/" config_keys["secret"] = "verysecretcode" events = { "report", "allow" } addWebHook(events, config_keys) * addCustomWebHook(\, \) - Add a custom webhook, i.e. one which can be called from Lua (using "runCustomWebHook()" - see below) with arbitrary data, using the specified configuration keys. See *wforce_webhook(5)* for the supported config keys, and details of the HTTP(S) POST sent to webhook URLs. For example: config_keys={} config_keys["url"] = "http://webhooks.example.com:8080/webhook/" config_keys["secret"] = "verysecretcode" addCustomWebHook("mycustomhook", config_keys) * addCustomStat(\) - Add a custom counter which can be used to track statistics. The stats for custom counters are logged every 5 minutes. The counter is incremented with the "incCustomStat" command. For example: addCustomStat("custom_stat1") * setSiblings(\) - Set the list of siblings to which stats db and blacklist/whitelist data should be replicated. If port is not specified it defaults to 4001. If protocol is not specified it defaults to udp. This function is safe to call while wforce is running. For example: setSiblings({"127.0.1.2", "sibling1.example.com:4004", "[::1]:4004:tcp"}) * setSiblingsWithKey(\) - Identical to setSiblings() except that it allows an encryption key to be specified for each sibling. For example: setSiblingsWithKey({{"127.0.1.2", "Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE="}, {"127.0.1.3:4004:tcp", "KaiQkCHloe2ysXv2HbxBAFqHI4N8+ahmwYwsbYlDdF0="}}) * addSibling(\) - Add a sibling to the list to which all stats db and blacklist/whitelist data should be replicated. Use [] to enclose IPv6 addresses. If port is not specified it defaults to 4001. If protocol is not specified it defaults to udp. This function is safe to call while wforce is running. For example: addSibling("192.168.1.23") addSibling("192.168.1.23:4001:udp") addSibling("192.168.1.23:4003:tcp") addSibling("[::1]:4003:tcp") addSibling("sibling1.example.com") * addSiblingWithKey(\, \) - Identical to addSibling(), except that an encryption key is specified to enable per-sibling encryption.For example: addSiblingWithKey("192.168.1.23", "Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") * removeSibling(\) - Remove a sibling to the list to which all stats db and blacklist/whitelist data should be replicated. Use [] to enclose IPv6 addresses. If port is not specified it defaults to 4001. If protocol is not specified it defaults to udp. This function is safe to call while wforce is running. For example: removeSibling("192.168.1.23") removeSibling("sibling1.example.com:4001:udp") removeSibling("[::1]:4003:tcp") * siblingListener(\) - Listen for reports from siblings on the specified IP address and port. If port is not specified it defaults to 4001. Wforce will always listen on both UDP and TCP ports. For example: siblingListener("0.0.0.0:4001") siblingListener("[::1]:4001") * setSiblingConnectTimeout(\) - Sets a timeout in milliseconds for new connections to siblings. Defaults to 5000 ms (5 seconds). For example: setSiblingConnectTimeout(1000) * setMaxSiblingQueueSize(\) - Sets the maximum size of the send and receive queues for replication events waiting to be processed. Defaults to 5000. This is only to handle short-term spikes in load/latency - if error messages relating to the recv queue max size being reached are seen, then you should consider using sharded string stats dbs (newShardedStringStatsDB), and/or tuning the stats db expiry sleep time (twSetExpireSleep). setMaxSiblingQueueSize(10000) * setNamedReportSinks(\, \) - Set a named list of report sinks to which all received reports should be forwarded over UDP. Reports will be sent to the configured report sinks for a given name in a round-robin fashion if more than one is specified. Reports are sent separately to each named report sink. If port is not specified it defaults to 4501. Replaces the deprecated "setReportSinks()". For example: setNamedReportSinks("logstash", {"127.0.1.2", "127.0.1.3:4501"}) * addNamedReportSink(\, \) - Add a report sink to the named list to which all received reports should be forwarded over UDP. Reports will be sent to the configured report sinks for a given name in a round-robin fashion if more than one is specified. Reports are sent separately to each named report sink. If port is not specified it defaults to 4501. Replaces the deprecated "addReportSink()". For example: addNamedReportSink("logstash", "192.168.1.23") * webserver(\, \) - (*deprecated - see addListener() instead*) Listen for HTTP commands on the specified IP address and port. The password is used to authenticate client connections using basic authentication. For example: webserver("0.0.0.0:8084", "super") * addListener(\, \, \, \, \) - (*replacement for webserver()*) Listen for HTTP commands on the specified IP address and port. If useSSL is true, then HTTPS must be used, and cert_file and key file are used, otherwise they are empty. Options contains a list of key value pairs to configure the TLS connection; these follow the command line option names in https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html. For example, "min_protocol" to set the minimum TLS protocol version. You can add as many listeners as you choose. For example: addListener("0.0.0.0:8084", false, "", "", {}) addListener("1.2.3.4:1234", true, "/etc/wforce/cert.pem", "/etc/wforce/key.pem", {minimum_protocol="TLSv1.2"}) addListener("[::1]:9000", true, "/etc/wforce/cert.pem", "/etc/wforce/key.pem", {minimum_protocol="TLSv1.3"}) * setWebserverPassword(\) - (*replacement for webserver password parameter*) Sets the basic authentication password for access to the webserver. This has been decoupled from the addListener() command because multiple listeners can now be created, which was not previously possible. For example: setWebserverPassword("foobar") * controlSocket(\) - Listen for control connections on the specified IP address and port. If port is not specified it defaults to 5199. For example: controlSocket("0.0.0.0:4004") * setKey(\) - Use the specified key for authenticating connections from siblings. The key can be generated with makeKey() from the console. See *wforce(1)* for instructions on running a console client. Returns false if the key could not be set (e.g. invalid base64). For example: if not setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") then ... * setNumLuaStates(\) - Set the number of Lua Contexts that will be created to run allow/report/reset commands. Defaults to 10 if not specified. See also setNumWorkerThreads() and setNumSiblingThreads(). Should be at least equal to NumWorkerThreads \+ NumSiblingThreads. For example: setNumLuaStates(10) * setNumWorkerThreads(\) - Set the number of threads in the pool used to run allow/report/reset commands. Each thread uses a separate Lua Context, (see setNumLuaStates()). Defaults to 4 if not specified. For example: setNumWorkerThreads(4) * setNumSiblingThreads(\) - Set the number of threads in the pool used to process reports received from siblings. Each thread uses a separate Lua Context, the number of which is set with setNumLuaStates(). Defaults to 2 if not specified. For example: setNumSiblingThreads(2) * setNumWebHookThreads(\) - Set the number of threads in the pool used to send webhook events. Defaults to 5 if not specified. For example: setNumWebHookThreads(2) * setMaxWebserverConns(\) - Set the maximum number of active connections to the webserver. This can be used to limit the effect of too many queries to wforce. It defaults to 10,000. For example: setMaxWebserverConns(5000) * setNumWebHookConnsPerThread(\) - Set the maximum number of connections used by each WebHook thread. Defaults to 10 if not specified. This setting replaces the deprecated "num_conns" per-hook configuration setting. For example: setNumWebHookConnsPerThread(50) * setWebHookQueueSize(\) - Set the size of the queue for webhook events. If the queue gets too big, then webhooks will be discarded, and an error will be logged. The default queue size is 50000, which should be appropriate for most use-cases. setWebHookQueueSize(100000) * setWebHookTimeoutSecs(\) - Set the maximum time a request can take for webhooks. For example: setWebHookTimeoutSecs(2) * newGeoIP2DB(\, \) - Opens and initializes a GeoIP2 database. A name must be chosen, and the filename of the database to open must also be supplied. To obtain an object allowing lookups to be performed against the database, use the getGeoIP2DB() function. For example: newGeoIP2DB("CityDB", "/usr/share/GeoIP/GeoLite2-City.mmdb") * initGeoIPDB() - (Deprecated - use newGeoIP2DB()) Initializes the country-level IPv4 and IPv6 GeoIP Legacy databases. If either of these databases is not installed, this command will fail and wforce will not start. For example: initGeoIPDB() * initGeoIPCityDB() - (Deprecated - use newGeoIP2DB()) Initializes the city-level IPv4 and IPv6 GeoIP Legacy databases. If either of these databases is not installed, this command will fail and wforce will not start. Ensure these databases have the right names if you're using the free/lite DBs - you may need to create symbolic links e.g. GeoIPCityv6.dat -> GeoLiteCityv6.dat. For example: initGeoIPCityDB() * initGeoIPISPDB() - (Deprecated - use newGeoIP2DB()) Initializes the ISP-level IPv4 and IPv6 GeoIP Legacy databases. If either of these databases is not installed, this command will fail and wforce will not start. For example: initGeoIPISPDB() * newDNSResolver(\) - Create a new DNS resolver object with the specified name. Note this does not return the resolver object - that is achieved with the getDNSResolver() function. For example: newDNSResolver("MyResolver") * setHLLBits(\) - Set the accuracy of the HyperLogLog algorithm. The value can be between 4-30, with a high number being more accurate at the expense of using (potentially a lot) more memory. The default value is 6. If you require more accuracy, consider increasing slightly, but check your memory usage carefully. * setCountMinBits(\, \) - Set the accuracy of the CountMin algorithm. The value of eps can be between 0.01 and 1, with a lower number giving more accuracy at the expense of memory. The default is 0.05. The value of gamma is between 0 and 1, with a higher number giving higher accuracy. The default for gamma is 0.2. If you require more accuracy, consider changing these values slightly, but check your memory usage carefully. * newStringStatsDB(\, \, \, \) - Create a new StatsDB object with the specified name. Note this does not return the object - that is achieved with the getStringStatsDB() function. For example: field_map = {} field_map["countLogins"] = "int" field_map["diffPasswords"] = "hll" field_map["countCountries"] = "countmin" newStringStatsDB("OneHourDB", 900, 4, field_map) * newShardedStringStatsDB(\, \, \, \, \) - Identical to "newStringStatsDB" except that it creates a sharded DB, which is more scalable at higher query loads. A good starting value for the number of shards might be 10. * StringStatsDB:twSetv4Prefix(\) - Set the prefix to use for any IPv4 ComboAddress keys stored in the db. For example, specify 24 to group statistics for class C networks. For example: statsdb:twSetv4Prefix(24) * StringStatsDB:twSetv6Prefix(\) - Set the prefix to use for any IPv6 ComboAddress keys stored in the db. For example: statsdb:twSetv4Prefix(64) * StringStatsDB:twSetMaxSize(\) - Set the maximum number of keys to be stored in the db. When the db reaches that size, keys will be expired on a LRU basis. The default size is 500K keys. For example: statsdb:twSetMaxSize(1000000) * StringStatsDB:twEnableReplication() - Enable replication for this db; only makes a difference if siblings have been configured. For example: statsdb:twEnableReplication() * disableBuiltinBlacklists() - Disable the built-in blacklisting checks, enabling them to be checked from Lua instead. For example: disableBuiltinBlacklists() * blacklistPersistDB(\, \) - Make the blacklist persistent by storing entries in the specified redis DB. The IP address is a string, and port should be 6379 unless you are running redis on a non-standard port. If this option is specified, wforce will read all the blacklist entries from the redis DB on startup. For example: blacklistPersistDB("127.0.0.1", 6379) * blacklistPersistReplicated() - Store blacklist entries that have been replicated in the redis DB. By default, replicated blacklist entries will not be persisted. If you use a local redis DB for each wforce server, then use this config option. If you use a single redis instance for all wforce servers, then you should not specify this option, as it will cause unnecessary writes to the redis DB. For example: blacklistPersistReplicated() * blacklistRedisUsername() - Set the Redis username for authentication to the redis DB. For example: blacklistRedisUsername("foobar") * blacklistRedisPassword() - Set the Redis password for authentication to the redis DB. For example: blacklistRedisPassword("secret") * blacklistPersistConnectTimeout() - Set the connect timeout for connecting to the persistent redis DB. If the timeout is exceeded during connection at startup then wforce will exit, otherwise during normal operation if the timeout is exceeded, an error will be logged. For example: blacklistPersistConnectTimeout(2) * blacklistPersistRWTimeout(, ) - Set the timeout for reading from/writing to the Redis DB. For example: blacklistPersistRWTimeout(0, 50000) * disableBuiltinWhitelists() - Disable the built-in whitelisting checks, enabling them to be checked from Lua instead. For example: disableBuiltinWhitelists() * whitelistPersistDB(\, \) - Make the whitelist persistent by storing entries in the specified redis DB. The IP address is a string, and port should be 6379 unless you are running redis on a non-standard port. If this option is specified, wforce will read all the whitelist entries from the redis DB on startup. For example: whitelistPersistDB("127.0.0.1", 6379) * whitelistRedisUsername() - Set the Redis username for authentication to the redis DB. For example: whitelistRedisUsername("foobar") * whitelistRedisPassword() - Set the Redis password for authentication to the redis DB. For example: whitelistRedisPassword("secret") * whitelistPersistReplicated() - Store whitelist entries that have been replicated in the redis DB. By default, replicated whitelist entries will not be persisted. If you use a local redis DB for each wforce server, then use this config option. If you use a single redis instance for all wforce servers, then you should not specify this option, as it will cause unnecessary writes to the redis DB. For example: whitelistPersistReplicated() * whitelistPersistConnectTimeout() - Set the connect timeout for connecting to the persistent redis DB. If the timeout is exceeded during connection at startup then wforce will exit, otherwise during normal operation if the timeout is exceeded, an error will be logged. For example: whitelistPersistConnectTimeout(2) * whitelistPersistRWTimeout(, ) - Set the timeout for reading from/writing to the Redis DB. For example: whitelistPersistRWTimeout(0, 50000) * setBlacklistIPRetMsg() - Set the message to be returned to clients whose IP address is blacklisted. The strings "{ip}" and "{login}" will be substituted for the actual IP address and login name. For example: setBlacklistIPRetMsg("Go away your IP {ip} is blacklisted") * setBlacklistLoginRetMsg() - Set the message to be returned to clients whose login is blacklisted. The strings "{ip}" and "{login}" will be substituted for the actual IP address and login name. For example: setBlacklistLoginRetMsg("Go away your login {login} is blacklisted") * setBlacklistIPLoginRetMsg() - Set the message to be returned to clients whose IP address/login is blacklisted. The strings "{ip}" and "{login}" will be substituted for the actual IP address and login name. For example: setBlacklistIPLoginRetMsg("Go away your IP {ip}/Login {login} is blacklisted") * setAllow(\) - Tell wforce to use the specified Lua function for handling all "allow" commands. For example: setAllow(allow) * setReport(\) - Tell wforce to use the specified Lua function for handling all "report" commands. For example: setReport(report) * setReset(\) - Tell wforce to use the specified Lua function for handling all "reset" commands. For example: setReset(reset) * setCanonicalize(\) - Tell wforce to use the specified Lua function for handling all "canonicalisation" functionality. This function is responsible for mapping multiple aliases for a user to a single login string, e.g. ncook, neil.cook and neil.cook@foobar.com would all map to neil.cook@foobar.com. Use Lua modules such as LuaSQL-MySQL (luarocks install luasql-mysql) or LuaLDAP (luarocks install lualdap) to perform lookups in user databases. For example: setCanonicalize(canonicalize) * setCustomEndpoint(\, \, \) - Create a new custom REST endpoint with the given name, which when invoked will call the supplied custom lua function. This allows admins to arbitrarily extend the wforce REST API with new REST endpoints. Admins can create as many custom endpoints as they require. Custom endpoints can only be accessed via a POST method, and all arguments as passed as key-value pairs of a top-level "attrs" json object (these will be split into two tables - one for single-valued attrs, and the other for multi-valued attrs - see CustomFuncArgs below). If the boolean argument is true, then all arguments to the custom function will be sent to all configured named report sinks. Return information is passed with a boolean "success" and "r_attrs" json object containing return key-value pairs. See wforce.conf.example for an example. For example: function custom(args) for k,v in pairs(args.attrs) do infoLog("custom func argument attrs", { key=k, value=v }); end -- return consists of a boolean, followed by { key-value pairs } return true, { key=value } end setCustomEndpoint("custom", false, custom) -- Set boolean to true to enable arguments to be sent to all -- report sinks -- setCustomEndpoint("custom", true, custom) * setCustomGetEndpoint(\, \) - Create a new custom REST endpoint accessible via a GET command (setCustomEndpoint() only works with POST). The return value of the function is a string, which will be passed to the HTTP client as a text/plain response body. For example: function textIPBlacklist() local ipbl = getIPBlacklist() local ret_table = {} for i,j in pairs(ipbl) do for k,v in pairs(j) do if k == "ip" then table.insert(ret_table, v) end end end local s = table.concat(ret_table, "\n") .. "\n" return s end setCustomGetEndpoint("textIPBlacklist", textIPBlacklist) * setVerboseAllowLog() - When logging allow requests, for performance reaons, allow requests returning 0 will not be logged by default. In order to log allow requests returning 0, use this function. For example: setVerboseAllowLog() * addSyncHost(\, \, \, \) - If you wish wforce to synchronize the contents of its StatsDBs with the rest of a cluster, then use this configuration command. If any sync hosts are added, wforce will attempt to contact them in turn and find a host which has been up longer than the number of seconds specified in setMinSyncHostUptime(). The sync host address should include a port (it defaults to 8084). If successful, the sync host will send the contents of its StatsDBs to this wforce instance on the replication address specified (this address should have the same port as configured in siblingListener() and defaults to 4001), and when it is finished it will notify this instance of wforce using the callback address (this address should have the same port as configured in webserver and defaults to 8084). N.B. It should be noted that the encryption key of the local instance is sent to the sync host, so to protect the confidentiality of the key, you may want to consider running an HTTPS reverse proxy in front of the sync host. For example: -- Add 10.2.3.1:8084 as a sync host, -- and use the password "super" -- Send the DB dump to 10.2.1.1:4001 -- and let me know on 10.2.1.1:8084 when the dump is finished addSyncHost("10.2.3.1:8084", "super", "10.2.1.1:4001", "10.2.1.1:8084") -- Add https://10.2.3.1:8084 as a sync host, -- and use the password "super" -- Send the DB dump to https://10.2.1.1:4001 addSyncHost("https://host1.example.com:8084", "super", "10.2.1.1:4001", "https://host2.example.com:8084") * setMinSyncHostUptime(\) - The minimum time that any sync host must have been up for it to be able to send me the contents of its DBs. Defaults to 3600. For example: setMinSyncHostUptime(1800) * disableCurlPeerVerification() - Disable checking of peer certificates in all outbound HTTPS connections. This is not recommended except for debugging or development purposes. For example: disableCurlPeerVerification() * disableCurlHostVerification() - Disable checking of the hostname in the peer certificate for all outbound HTTPS connections. This is not recommended except for debugging or development purposes. disableCurlHostVerification() * setCurlCABundleFile(\) - Gives the location of a file containing the certificate authorities to use for checking HTTPS server certificates. Use this if the standard installed root certs do not contain the certs you need. This should be a file containing 1:N certs in PEM format. setCurlCABundleFile("/etc/ca/local_cas.pem") * setCurlClientCertAndKey(\, \) - Gives the location of the certificate and key files to use for mutual TLS authentication (in PEM format). setCurlClientCertAndKey("/etc/certs/clientcert.pem", "/etc/certs/clientkey.pem") * setMetricsNoPassword() - Disable password protection for the /metrics endpoint. # GENERAL FUNCTIONS The following functions are available anywhere; either as part of the configuration or within the allow/report/reset functions: * getGeoIP2DB(\) - Return an object which can be used to perform GeoIP lookups. The database must first be initialised using newGeoIP2DB(). For example: local citydb = getGeoIP2DB("CityDB") * GeoIP2DB:lookupCountry(\) - Returns the two-character country code of the country that the IP address is located in. A ComboAddress object can be created with the newCA() function. For example: my_country = countrydb:lookupCountry(newCA("8.8.8.8")) my_country = countrydb:lookupCountry(lt.remote) * GeoIP2DB:lookupISP(\) - Returns the name of the ISP hosting the IP address. A ComboAddress object can be created with the newCA() function. For example: local my_isp = ispdb:lookupISP(newCA("128.243.16.21")) * GeoIP2DB:lookupCity(\) - Returns a map containing information about the IP address, such as the city name and latitude and longitude. See GeoIPRecord below for the full list of fields. For example: local gip_record = citydb:lookupCity(lt.remote) local my_city = gip_record.city local my_latitude = gip_record.latitude * GeoIP2DB:lookupStringValue(\, \) - Returns a string corresponding to the value of the field specified by the array of string values. For example: local city_name = citydb:lookupStringValue(newCA(ip_address), {"city", "names", "en"}) * GeoIP2DB:lookupUIntValue(\, \) - Returns an integer corresponding to the value of the field specified by the array of string values. For example: local accuracy = citydb:lookupUIntValue(newCA(ip_address), {"location", "accuracy_radius"}) * GeoIP2DB:lookupBoolValue(\, \) - Returns a boolean corresponding to the value of the field specified by the array of string values. For example: local eu = citydb:lookupBoolValue(newCA(ip_address), {"country", "is_in_european_union"}) * GeoIP2DB:lookupDoubleValue(\, \) - Returns the value corresponding to the value of the field specified by the array of string values (which can be either double or float in the MMDB specification). For example: local city_lat = citydb:lookupDoubleValue(newCA(ip_address), {"location", "latitude"}) local city_long = citydb:lookupDoubleValue(newCA(ip_address), {"location", "longitude"}) * lookupCountry(\) - (Deprecated - use the new GeoIP2 function). Returns the two-character country code of the country that the IP address is located in. A ComboAddress object can be created with the newCA() function. For example: my_country = lookupCountry(my_ca) * lookupISP(\) - (Deprecated - use the new GeoIP2 function). Returns the name of the ISP hosting the IP address. A ComboAddress object can be created with the newCA() function. For example: local my_isp = lookupISP(newCA("128.243.16.21")) * lookupCity(\) - (Deprecated - use the new GeoIP2 function). Returns a map containing information about the IP address, such as the city name and latitude and longitude. See GeoIPRecord below for the full list of fields. For example: local gip_record = lookupCity(lt.remote) local my_city = gip_record.city local my_latitude = gip_record.latitude * newCA(\) - Create and return an object representing an IP address (v4 or v6) and optional port. The object is called a ComboAddress. For example: my_ca = newCA("192.128.12.82") * ComboAddress:tostring() - Return a string representing the IP address. For example: mystr = my_ca:tostring() * newNetmask(\) - Create and return an object representing a Netmask. For example: my_nm = newNetmask("8.0.0.0/8") * newNetmaskGroup() - Return a NetmaskGroup object, which is a way to efficiently match IPs/subnets against a range. For example: mynm = newNetmaskGroup() * NetmaskGroup:addMask(\) - Add a mask to the NetmaskGroup, in cidr format. For example: mynm:addMask("182.22.0.0/16") * NetmaskGroup:match(\) - Match an IP address against a NetmaskGroup. The IP address must be a ComboAddress object. For example: if (mynm.match(lt.remote)) then print("ip address matched") end * getDNSResolver(\) - Return a DNS resolver object corresponding to the name specified. For example: resolv = getDNSResolver("MyResolver") * Resolver:addResolver(\, \) - Adds the specified IP address and port as a recursive server to use when resolving DNS queries. For example: resolv = getDNSResolver("MyResolver") resolv:addResolver("8.8.8.8", 53) * Resolver:setRequestTimeout(\) - Sets the timeout in milliseconds. For example resolv = getDNSResolver("MyResolver") resolv:setRequestTimeout(100) * Resolver:setNumContexts(\) - Sets the number of DNS contexts to use to perform DNS queries. Defaults to 12, but you may want to increase for performance, although it should not need to be higher than NumWorkerThreads. For example: resolv = getDNSResolver("MyResolver") resolv:setNumContexts(20) * Resolver:lookupAddrByName(\, [\]) - Performs DNS A record resolution for the specified name, returning an array of IP address strings. Optionally the number of retries can be specified. For example: resolv = getDNSResolver("MyResolver") resolv:lookupAddrByName("google.com") for i, addr in ipairs(dnames) do -- addr contains an IPv4 or IPv6 address end * Resolver:lookupNameByAddr(\, [num retries]) - Performs DNS PTR record resolution for the specified address, returning an array of names. Optionally the number of retries can be specified. For example: resolv = getDNSResolver("MyResolver") resolv:lookupNameByAddr(lt.remote) for i, dname in ipairs(dnames) do -- dname is the resolved DNS name end * Resolver:lookupRBL(\, \, [num retries]) - Lookup an IP address in a RBL-formatted zone. Returns an array of IP address strings. Optionally the number of retries can be specified. For example: dnames = rblresolv:lookupRBL(lt.remote, "sbl.spamhaus.org") for i, dname in ipairs(dnames) do if (string.match(dname, "127.0.0.2")) then -- the RBL matched end end * runCustomWebHook(\, \) - Run a previously configured custom webhook, using the supplied webhook data. By default the Content-Type of the data is "application/json", however this can be changed using the "content-type" config key when configuring the custom webhook. Custom webhooks are run using the same thread pool as normal webhooks. For example: runCustomWebHook(mycustomhook", "{ \"foo\":\"bar\" }") * getStringStatsDB(\) - Retrieve the StatsDB object specified by the name. For example: statsdb = getStringStatsDB("OneHourDB") * StringStatsDB:twAdd(\, \, \) - For the specified key, add the specified value to the specified field. Keys can be ComboAddress, strings or integers. Values can be ComboAddress, strings or integers. Some values only make sense for certain field types (e.g. adding a string to an int field type makes no sense). For example: ca = newCA("192.168.1.2") statsdb:twAdd("luser", "diffIPs", ca) statsdb:twAdd(ca, "countLogins", 1) * StringStatsDB:twSub(\, \, \) - For the specified key, subtract the specified value from the specified field. Keys can be ComboAddress, strings or integers. Values can only be integers. For example: statsdb:twSub(ca, "countLogins", 1) * StringStatsDB:twGet(\, \, [\]) - For the specified key, retrieve the value for the specified field. For fields of type "countmin", specify the value you wish to retrieve. Return value is summed over all the windows in the db. For example: numLogins = statsdb:twGet(lt.login, "countLogins") numUSLogins = statsdb:twGet(lt.login, "countCountries", "US") * StringStatsDB:twGetCurrent(\, \, [\]) - For the specified key, retrieve the value for the specified field for the current (latest) time window. For fields of type "countmin", specify the value you wish to retrieve. For example: numLogins = statsdb:twGetCurrent(lt.login, "countLogins") * StringStatsDB:twGetWindows(\, \, [\]) - For the specified key, retrieve an array containing all the values for all the windows for the specified field. For fields of type "countmin", specify the value you wish to retrieve. For example: myarray = statsdb:twGetWindows(lt.login, "countCountries", "AU") * StringStatsDB:twGetSize() - Returns the number of keys stored in the db. For example dbsize = statsdb:twGetSize() * StringStatsDB:twReset(\) - Reset all stats for all windows in the db for the specified key. For example: statsdb:twReset(lt.login) * StringStatsDB:twResetField(\, \) - Reset all stats for all windows in the db for the specified key and field name. For example: statsdb:twResetField(lt.login, "countLogins") * StringStatsDB:twSetExpireSleep(\) - Set the sleep interval between checks to expire/expunge entries. Defaults to 250ms. For example: statsdb:twSetExpireSleep(200) * infoLog(\, \) - Log at LOG_INFO level t, \) - Log at LOG_INFO level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map, but only if wforce was started with the (undocumented) -v flag (for verbose). For example: vinfoLog("Logging is very important", { logging=1, foo=bar }) * warnLog(\, \) - Log at LOG_WARN level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map. For example: warnLog("Logging is very important", { logging=1, foo=bar }) * errorLog(\, \) - Log at LOG_ERR level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map. For example: errorLog("Logging is very important", { logging=1, foo=bar }) * debugLog(\, \) - Log at LOG_DEBUG level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map, but only if wforce was started with the (undocumented) -v flag (for verbose). For example: debugLog("This will only log if wforce is started with -v", { logging=1, foo=bar }) * getBlacklistIPRetMsg() - Get the message to be returned to clients whose IP address is blacklisted. For example: local retmsg = getBlacklistIPRetMsg() * getBlacklistLoginRetMsg() - Get the message to be returned to clients whose login is blacklisted. For example: local retmsg = getBlacklistLoginRetMsg() * getBlacklistIPLoginRetMsg() - Get the message to be returned to clients whose IP address/login is blacklisted. For example: local retmsg = getBlacklistIPLoginRetMsg() * blacklistNetmask(\, \, \) - Blacklist the specified netmask for expiry seconds, with the specified reason. Netmask address must be a Netmask object, e.g. created with newNetmask(). For example: blacklistNetmask(newNetmask("12.32.0.0/16"), 300, "Attempted password brute forcing") * blacklistIP(\, \, \) - Blacklist the specified IP for expiry seconds, with the specified reason. IP address must be a ComboAddress. For example: blacklistIP(lt.remote, 300, "Attempted password brute forcing") * blacklistLogin(\, \ \) - Blacklist the specified login for expiry seconds, with the specified reason. For example: blacklistLogin(lt.login, 300, "Potentially compromised account") * blacklistIPLogin(\, \, \, \) - Blacklist the specified IP-Login tuple for expiry seconds, with the specified reason. Only when that IP and login are received in the same login tuple will the request be blacklisted. IP address must be a ComboAddress. For example: blacklistIPLogin(lt.remote, lt.login, 300, "Account and IP are suspicious") * unblacklistNetmask(\) Remove the blacklist for the specified netmask. Netmask address must be a Netmask object, e.g. created with newNetmask(). For example: unblacklistNetmask(newNetmask("12.32.0.0/16")) * unblacklistIP(\) - Remove the blacklist for the specified IP. IP address must be a ComboAddress. For example: unblacklistIP(lt.remote) * unblacklistLogin(\) - Remove the blacklist for the specified login. For example: unblacklistLogin(lt.login) * unblacklistIPLogin(\, \) - Remove the blacklist for the specified IP-Login tuple. IP address must be a ComboAddress. For example: unblacklistIPLogin(lt.remote, lt.login) * checkBlacklistIP(\) - Check if an IP is blacklisted. Return true if the IP is blacklisted. IP must be a ComboAddress. For example: checkBlacklistIP(newCA("192.1.2.3")) * checkBlacklistLogin(\) - Check if a login is blacklisted. Return true if the login is blacklisted. For example: checkBlacklistLogin(lt.login) * checkBlacklistIPLogin(\, \) - Check if a IP/login is blacklisted. Return true if the ip/login tuple is blacklisted. For example: checkBlacklistIPLogin(lt.remote, lt.login) * whitelistNetmask(\, \, \) - Whitelist the specified netmask for expiry seconds, with the specified reason. Netmask address must be a Netmask object, e.g. created with newNetmask(). For example: whitelistNetmask(newNetmask("12.32.0.0/16"), 300, "Attempted password brute forcing") * whitelistIP(\, \, \) - Whitelist the specified IP for expiry seconds, with the specified reason. IP address must be a ComboAddress. For example: whitelistIP(lt.remote, 300, "Attempted password brute forcing") * whitelistLogin(\, \ \) - Whitelist the specified login for expiry seconds, with the specified reason. For example: whitelistLogin(lt.login, 300, "Potentially compromised account") * whitelistIPLogin(\, \, \, \) - Whitelist the specified IP-Login tuple for expiry seconds, with the specified reason. Only when that IP and login are received in the same login tuple will the request be whitelisted. IP address must be a ComboAddress. For example: whitelistIPLogin(lt.remote, lt.login, 300, "Account and IP are suspicious") * unwhitelistNetmask(\) Remove the whitelist for the specified netmask. Netmask address must be a Netmask object, e.g. created with newNetmask(). For example: unwhitelistNetmask(newNetmask("12.32.0.0/16")) * unwhitelistIP(\) - Remove the whitelist for the specified IP. IP address must be a ComboAddress. For example: unwhitelistIP(lt.remote) * unwhitelistLogin(\) - Remove the whitelist for the specified login. For example: unwhitelistLogin(lt.login) * unwhitelistIPLogin(\, \) - Remove the whitelist for the specified IP-Login tuple. IP address must be a ComboAddress. For example: unwhitelistIPLogin(lt.remote, lt.login) * checkWhitelistIP(\) - Check if an IP is whitelisted. Return true if the IP is whitelisted. IP must be a ComboAddress. For example: checkWhitelistIP(newCA("192.1.2.3")) * checkWhitelistLogin(\) - Check if a login is whitelisted. Return true if the login is whitelisted. For example: checkWhitelistLogin(lt.login) * checkWhitelistIPLogin(\, \) - Check if a IP/login is whitelisted. Return true if the ip/login tuple is whitelisted. For example: checkWhitelistIPLogin(lt.remote, lt.login) * LoginTuple - The only parameter to both the allow and report functions is a LoginTuple table. This table contains the following fields: * LoginTuple.remote - An IP address of type ComboAddress, representing the system performing login. * LoginTuple.login - The username of the user performing the login. * LoginTuple.pwhash - A hashed representation of the users password * LoginTuple.success - Whether the user login was successful. * LoginTuple.policy_reject - If the login was not successful only because of a policy-based reject from wforce (i.e. the username and password were correct). * LoginTuple.attrs - Additional array of (single valued) attributes about the login, e.g. information about the user from LDAP. For example: for k, v in pairs(lt.attrs) do if (k == "xxx") then -- do something end end * LoginTuple.attrs_mv - Additional array of (multi-valued) attributes about the login. For example: for k, v in pairs(lt.attrs_mv) do for i, vi in ipairs(v) do if ((k == "xxx") and (vi == "yyy")) then -- do something end end end * LoginTuple.device_id - An optional string that represents the device that the user logged in from. Also see device_attrs. * LoginTuple.device_attrs - Additional array of attributes about the device, which is parsed from the device_attrs string. The protocol string is used to determine how to parse device_id, so that MUST also be present. For all protocols, the following keys are set wherever possible: os.family, os.major, os.minor. For http, the following additional keys are set wherever possible: device.family, device.model, device.brand, browser.family, browser.major, browser.minor. For imap, the following additional keys are set wherever possible: imapc.family, imapc.major, imapc.minor. For mobileapi, the following additional keys are set: app.name, app.brand, app.major, app.minor, device.family. For example: if (lt.device_attrs["os.family"] == "Mac OS X") then -- do something special for macOS end * LoginTuple.protocol - A string representing the protocol that was used to access mail, i.e. http, imap, pop3, mobileapi etc. LoginTuple.protocol MUST be set in order to parse device_id into device_attrs, however currently only http, imap and mobileapi are recognized protocols when parsing device_id. For example: if (lt.protocol == "http") then -- do something end * LoginTuple.tls - A boolean representing whether the login session used TLS or not. If the client is using TLS offload proxies then it may be set to false. * LoginTuple.session_id - An optional string representing the particular session that a user used to login. If this is not supplied by the wforce client, it will be an empty string. * GeoIPRecord - The type returned by the lookupCity() function. See below for fields: * GeoIPRecord.country_code - The two-letter country code e.g. "US". * GeoIPRecord.country_name - The country name, e.g. "United States" * GeoIPRecord.region - The region, e.g. "CA" * GeoIPRecord.city - The city name, e.g. "Mountain View" * GeoIPRecord.postal_code - The postal code, e.g. "93102" or "BA216AS" * GeoIPRecord.latitude - The latitude, e.g. 37.386001586914 * GeoIPRecord.longitude - The longitude, e.g. -122.08380126953 * CustomFuncArgs - The only parameter to custom functions is a CustomFuncArgs table. This table contains the following fields: * CustomFuncArgs.attrs - Array of (single valued) attributes supplied by the caller. For example: for k, v in pairs(args.attrs) do if (k == "xxx") then -- do something end end * CustomFuncArgs.attrs_mv - Array of (multi-valued) attributes supplied by the caller. For example: for k, v in pairs(args.attrs_mv) do for i, vi in ipairs(v) do if ((k == "xxx") and (vi == "yyy")) then -- do something end end end * incCustomStat(\) - Increment a custom statistics counter. The value of the counter will be logged every 5 minutes, and then reset. For example: incCustomStat("custom_stat1") # FILES */etc/wforce/wforce.conf* # SEE ALSO wforce(1) wforce_webhook(5) weakforced-2.10.2/docs/manpages/wforce_webhook.5.md000066400000000000000000000150521461473602600221350ustar00rootroot00000000000000% WFORCE_WEBHOOK(5) % Open-Xchange % 2018 # NAME **wforce_webhook** - Documentation for wforce webhook functionality # DESCRIPTION The **wforce** daemon supports generating webhooks which run for specific events. The list of supported events, example payload for those events, and the supported configuration keys for those events are documented here. A summary of the custom HTTP(S) POST headers is also included. # WEBHOOK EVENTS See **wforce.conf(5)** for details of the "addWebHook()" configuration setting. The following events are available for generating webhooks: * report - Runs each time a valid report command is received. The webhook includes the report request payload but not the report response. An example payload for the HTTP post is shown below: {"remote": "1.4.3.1", "success": false, "policy_reject": false, "attrs": {"cos": "basic"}, "login": "webhooktest@foobar.com", "pwhash": "1234", "t": 1472718468.412865} * allow - Runs each time a valid allow command is received. The webhook includes the request payload and the response payload. This hook can be filtered on the return value using the "allow_filter" configuration key. An example payload for the HTTP post is shown below: {"request": {"remote": "1.4.3.2", "success": false, "policy_reject": true, "attrs": {}, "login": "webhooktest@foobar.com", "pwhash": "1234", "t": 1472718469.827362}, "response": {"msg": "", "status": 0}} * reset - Runs each time a valid reset command is received. The webhook includes the request payload. An example payload for the HTTP post is shown below: {"ip": "1.4.3.3", "login": "webhooktest@foobar.com"} * addbl - Runs each time a blacklist entry is added (from Lua or the REST API). The webhook includes the request payload. An example payload for the HTTP post is shown below: {"key": "webhooktest@foobar.com", "reason": "Too many different bad password attempts", "expire_secs": 10, "bl_type": "login_bl"} * delbl - Runs each time a blacklist entry is deleted (from Lua or the REST API). The webhook includes the request payload. An example payload for the HTTP post is shown below: {"key": "1.4.3.3:webhooktest@foobar.com", "bl_type": "ip_login_bl"} * addwl - Runs each time a whitelist entry is added (from Lua or the REST API). The webhook includes the request payload. An example payload for the HTTP post is shown below: {"key": "webhooktest@foobar.com", "reason": "This user is an admin", "expire_secs": 10, "wl_type": "login_wl"} * delwl - Runs each time a whitelist entry is deleted (from Lua or the REST API). The webhook includes the request payload. An example payload for the HTTP post is shown below: {"key": "1.4.3.3:webhooktest@foobar.com", "wl_type": "ip_login_wl"} * expirebl - Runs each time a blacklist entry is expired. An example payload for the HTTP post is shown below: {"key": "webhooktest@foobar.com", "bl_type": "login_bl"} * expirewl - Runs each time a whitelist entry is expired. An example payload for the HTTP post is shown below: {"key": "webhooktest@foobar.com", "wl_type": "login_wl"} # CUSTOM WEBHOOKS See **wforce.conf(5)** for details of the "addCustomWebHook()" configuration setting. This enables custom webhooks to be generated from Lua. # WEBHOOK CONFIGURATION KEYS See **wforce.conf(5)** for details of the "addWebHook()" configuration setting. The following configuration keys can be used for all events: * url - The URL that the webhook should POST to. You can add date macros into the URL that will be substituted when the hook is run. The following macros are supported: %{YYYY}, %{MM}, %{dd}. Supports http and https, for example: config_key["url"] = "https://example.com/foo" config_key["url"] = "https://example.com/foo/logstash-%{YYYY}-${MM}-%{dd}" * secret - A string that is used to generate the Hmac digest for the X-Wforce-Signature header. Should be unique for each webhook, so that the remote web server can verify the digest as being authentic. For example: config_key["secret"] = "12345" * num_conns - This configuration key is now deprecated - setting it will have no effect. Use "setNumWebHookConnsPerThread()" configuration setting instead (see **wforce.conf(5)**). * basic-auth - Adds an Authorization header to Webhooks, for servers which expect Basic Authentication. The username and password are provided as "user:pass". For example config_key["basic-auth"] = "wforce:super" * api-key - Adds an X-API-Key header to Webhooks, for servers that expect such a header for Authorization. For example: config_key["api-key"] = "myapikeysecret" * kafka - When this is set to "true" then the webhook will be sent with a Content-Type of "application/vnd.kafka.json.v2+json" and the json will be wrapped according to the Kafka REST Proxy requirements (https://docs.confluent.io/current/kafka-rest/api.html). The webhook "url" config key should be set to the correct topic, e.g. "http://kafka-rest:8082/topics/foo" for the topic named "foo". The following configuration keys are custom to specific events: * allow_filter - Filters allow webhooks based on the allow response status. If not specified, the webhook runs for all allow events. Possible values are: "reject", "allow" and "tarpit". The values are OR'd together, so specifying all values will have the same effect as not specifying allow_filter. For example: config_key["allow_filter"] = "allow reject" config_key["allow_filter"] = "tarpit allow" config_key["allow_filter"] = "reject" * content-type - Only custom webhooks can set a custom content-type. Doing so will set the HTTP Content-Type header to the value specified in this key, so ensure it is a valid MIME Content-Type. For example: config_key["content-type"] = "text/plain" # WEBHOOK CUSTOM HTTP HEADERS The following custom headers will be added to the POST request for each event: * X-Wforce-Event - The name of the event, e.g. "report" * X-Wforce-HookID - The ID of the WebHook (use "showWebHooks()" in the console to see the list of IDs. * X-Wforce-Signature - If the "secret" configuration key is specified, this header will contain a base64-encoded HMAC-SHA256 digest of the POST body. * X-Wforce-Delivery - A unique ID for this webhook. # WEBHOOK HTTP Content-Type HEADER The HTTP Content-Type header defaults to application/json, and cannot be changed for normal webhooks. Custom webhooks however can change it using the "content-type" configuration key. # FILES */etc/wforce.conf* # SEE ALSO wforce(1) wforce_webhook(5) wforce_api(7) weakforced-2.10.2/docs/release_notes/000077500000000000000000000000001461473602600174775ustar00rootroot00000000000000weakforced-2.10.2/docs/release_notes/ReleaseNotes-1.4.0.md000066400000000000000000000162241461473602600230550ustar00rootroot00000000000000# Release Notes for Dovecot Anti-Abuse Shield 1.4 rc1 New Features/Bug Fixes ------------ * Support for additional GeoIP DBs: City and ISP * Support for reloading GeoIP databases * Blacklists now support IP netmasks as well as individual IP addresses * Configurable timeout for Redis connections * New Lua logging function: debugLog * Wforce daemon changes working directory to config file directory * Webhook support for Basic Authentication * Support for parsing device_id from http, imap and OX mobile clients * New tls parameter to report/allow commands * Use Content-Length instead of chunked encoding for Webhooks * New console commands Additional GeoIP DB Support ------ The new config commands are described in full in "man wforce.conf", the following is an extract: * initGeoIPCityDB() - Initializes the city-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and wforce will not start. Ensure these databases have the right names if you're using the free/lite DBs - you may need to create symbolic links e.g. GeoIPCityv6.dat -> GeoLiteCityv6.dat. For example: initGeoIPCityDB() * initGeoIPISPDB() - Initializes the ISP-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and wforce will not start. For example: initGeoIPISPDB() Support for reloading GeoIP DBs --------- The following new console command is available: * reloadGeoIPDBs() - Reload all GeoIP DBs that have been initialized. For example: > reloadGeoIPDBs() reloadGeoIPDBs() successful Netmask support in Blacklists ----------- The following new Lua commands are available: * newNetmask(\) - Create and return an object representing a Netmask. For example: my_nm = newNetmask("8.0.0.0/8") * blacklistNetmask(\, \, \) - Blacklist the specified netmask for expiry seconds, with the specified reason. Netmask address must be a Netmask object, e.g. created with newNetmask(). For example: blacklistIP(newNetmask("12.32.0.0/16"), 300, "Attempted password brute forcing") There is also a new "netmask" parameter to the HTTP REST API addBLEntry and delBLEntry commands. The netmask parameter is mutually exclusive with the existing ip parameter. For example: curl -H "Content-Type: application/json" -X POST --data '{ "netmask":"2001:503:ba3e::2:30/64", "expire_secs":100, "reason":"foo" }' http://localhost:8084/?command=addBLEntry -u user:password Configurable Timeout for Redis Connections ------- Previously wforce would hang on startup or during operation if configured to use Redis and the Redis server was unavailable. Now wforce will timeout if the Redis server cannot be reached, and the timeout is configurable in Lua: * blacklistPersistConnectTimeout() - Set the connect timeout for connecting to the persistent redis DB. If the timeout is exceeded during connection at startup then wforce will exit, otherwise during normal operation if the timeout is exceeded, an error will be logged. For example: blacklistPersistConnectTimeout(2) New Lua Logging Functions ---------- There is a new Lua logging function, which can be useful for diagnosing issues: * debugLog(\, \) - Log at LOG_DEBUG level the specified string, adding "key=value" strings to the log for all the kvs specified in the key-value map, but only if wforce was started with the (undocumented) -v flag (for verbose). For example: debugLog("This will only log if wforce is started with -v", { logging=1, foo=bar }) Wforce changes cwd to config directory ----------- Wforce will now change working directory to the directory containing the wforce.conf file. This enables Lua modules to be used, with paths relative to the configuration directory (e.g. /etc/wforce). Webhooks support Basic Authentication ----------- A new configuration key has been added to Webhooks, to support basic authentication: * basic-auth - Adds an Authorization header to Webhooks, for servers which expect Basic Authentication. The username and password are provided as "user:pass". For example config_key["basic-auth"] = "wforce:super" Support for Parsing device_id --------- The device_id parameter to the allow and report commands existed in 1.2.x, however it was not parsed by wforce. Now the parameter is parsed if the protocol parameter is one of "http", "imap" or "mobileapi". Note that if the protocol parameter is nonexistent or does not match the above list, then device_id will not be parsed. The device_id is parsed into key value pairs of the "device_attrs" object, as follows: * LoginTuple.device_attrs - Additional array of attributes about the device, which is parsed from the device_attrs string. The protocol string is used to determine how to parse device_id, so that MUST also be present. For all protocols, the following keys are set wherever possible: os.family, os.major, os.minor. For http, the following additional keys are set wherever possible: device.family, device.model, device.brand, browser.family, browser.major, browser.minor. For imap, the following additional keys are set wherever possible: imapc.family, imapc.major, imapc.minor. For mobileapi, the following additional keys are set: app.name, app.brand, app.major, app.minor, device.family. For example: if (lt.device_attrs["os.family"] == "Mac OS X") then -- do something special for macOS end * LoginTuple.protocol - A string representing the protocol that was used to access mail, i.e. http, imap, pop3, mobileapi etc. LoginTuple.protocol MUST be set in order to parse device_id into device_attrs, however currently only http, imap and mobileapi are recognized protocols when parsing device_id. For example: if (lt.protocol == "http") then -- do something end New tls Parameter ----- The new "tls" parameter is used to indicate whether the client session used TLS or not. It may not be completely reliable, e.g. if TLS offload is done on load balancers. * LoginTuple.tls - A boolean representing whether the login session used TLS or not. If the client is using TLS offload proxies then it may be set to false. Content-Length instead of Chunked Encoding for Webhooks ------- Webhooks previously used Chunked encoding, however that is not currently supported by Logstash, so Content-Length is used instead. New Console Commands ----------- The following new console commands are available: * showPerfStats() - Returns information about performance statistics. Stats beginning with WTW refer to the time that worker threads waited in a queue before running. Stats beginning with WTR refer to the time that worker threads took to run. Each stat is in a bucket, where each bucket represents a time range in ms, e.g. 0-1. A server that is not overloaded will have most stats in the 0-1 buckets. For example: > showPerfStats() WTW_0_1=2939287 WTW_1_10=9722 WTW_10_100=4 WTW_100_1000=0 WTW_Slow=0 WTR_0_1=2939229 WTR_1_10=2837 WTR_10_100=131 WTR_100_1000=0 WTR_Slow=0 * reloadGeoIPDBs() - Reload all GeoIP DBs that have been initialized. For example: > reloadGeoIPDBs() reloadGeoIPDBs() successful weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.0.0.md000066400000000000000000000262231461473602600230520ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.0.0 New Features/Bug Fixes ------------ * Fix extra : at the end of custom log lines when kv table is empty * Return additional info about blacklist in allow and getDBStats RESTAPI functions * New Lua functions to remove blacklist entries * Add configuration setting "setNumWebHookConnsPerThread" * Add configuration setting "setWebHookTimeoutSecs" * Add support for querying replication status in showStringStatsDB() * Add sibling received success/fail stats to siblings() command * New custom stats framework * New stats for all commands, including custom commands * GeoIP2 support (MMDB-style DBs) * New twResetField() function for statsDBs * Configurable accuracy for HLL and CountMin types * DB Synchronization for newly started wforce instances * Support for replication over TCP * Customizable log facility via a command line option * New trackalert daemon * Logstash Configuration and Elasticsearch Templates * Kibana Reports and Dashboards * Report API Fix Extra : at end of Custom Log Lines ----- The Lua infoLog, errorLog etc. functions would previously, when called as 'errorLog("foo", {})' log: foo : Now the same call will log only: foo Return additional info about blacklist in allow and getDBStats REST API functions --- The allow command will now return additional information when an IP/Login is blacklisted. The 'r_attrs' object will contain four new fields: * expiration - A string showing the date/time when the blacklist will expire * reason - A string stating why the blacklist was created * key - What was blacklisted, i.e. either ip, login or iplogin * blacklisted - This will be set to 1 The getDBStats command will return additional information about blacklisted objects: * bl_expire - A string showing the date/time when the blacklist will expire * lb_reason - A string stating why the blacklist was created New Lua functions to remove blacklist entries ---- The following new Lua functions are available: * unblacklistNetmask * unblacklistIP * unblacklistLogin * unblacklistIPLogin See the wforce.conf manpage for more details. New Configuration Setting setNumWebHookConnsPerThread ------ The webhook support has been completely refactored in order to achieve much higher performance with fewer resources. Previously a very high number of webhook threads was required to achieve good performance, whereas now a much smaller number of threads can achieve the same performance. The previous per-webhook configuration key "num_conns" is no longer supported. Instead the global configuration setting "setNumWebHookConnsPerThread" is used. For example: setNumWebHookConnsPerThread(10) The default is 10 connections per webhook thread. Add configuration setting "setWebHookTimeoutSecs" ------- The function setWebHookTimeoutSecs() is used to control the time for webhook requests, e.g.: setWebHookTimeoutSecs(2) Support for querying replication status in showStringStatsDB() ----- The showStringStatsDB() command now shows whether each StatsDB is configured for replication or not. For example: > showStringStatsDB() DB Name Repl? Win Size/No Max Size Cur Size Field Name Type MyDB1 yes 1/15 524288 0 countLogins int diffPasswords hll MyDB2 no 600/6 5000 2093 diffIPs hll Add Sibling received success/fail stats to siblings() command -------- The siblings() command now shows success and failure stats about received messages as well as sent messages. For example: > siblings() Address Send Successes Send Failures Rcv Successes Rcv Failures Note 127.0.0.1:4001 0 0 17 0 127.0.0.1:4002 0 0 0 0 Self New Custom Stats Framework --------- Two new functions, "addCustomStat" and "incCustomStat" can be used to keep track of custom statistics. A new custom counter is created with "addCustomStat", e.g. addCustomStat("custom_stat1") Custom statistics are counters which track statistics over a 5 minute period. Every 5 minutes, the current values of all the custom stats counters are logged to the wforce log file, before the counters are reset. Stats can be incremented with the "incCustomStat" command: incCustomStat("custom_stat1") New stats for all commands -------- Previously there were no statistics logged for all the REST API commands; only allow and report commands. Now all REST API commands are tracked and statistics are reported, including for custom endpoints created from Lua. For example: command stats last 300 secs: addBLEntry=42 allow=393827 allow_allowed=299221 allow_blacklisted=3224 allow_denied=9884 allow_tarpitted=8373 delBLEntry=3 getBL=3949 getDBStats=3229 ping=83764 report=38473 reset=0 stats=0 syncDBs=0 syncDone=0 custom stats last 300 secs: customFunc1=3401 GeoIP2 support (MMDB-style DBs) -------- Maxmind are in the process of deprecating the GeoIP Legacy DB support, therefore this release supports GeoIP2 format databases, i.e. the MMDB format. This release therefore deprecates the GeoIP legacy functions, which will be removed in a later release. The following functions are deprecated: * initGeoIPDB() * initGeoIPCityDB() * initGeoIPISPDB() * lookupCountry() * lookupISP() * lookupCity() Due to differences in the way that the GeoIP2 API works, GeoIP Databases must be opened by specifying the filename of the database to be used. For example: newGeoIP2DB("CityDB", "/usr/share/GeoIP/GeoLite2-City.mmdb") To retrieve a GeoIP DB to conduct queries against, use the following command: local citydb = getGeoIP2DB("CityDB") Once a database has been assigned to a local variable, it can be queried, for example: my_country = countrydb:lookupCountry(newCA("8.8.8.8")) my_country = countrydb:lookupCountry(lt.remote) local my_isp = ispdb:lookupISP(newCA("128.243.16.21")) local gip_record = citydb:lookupCity(lt.remote) local my_city = gip_record.city local my_latitude = gip_record.latitude For full details see "man wforce.conf". New twResetField() function for statsDBs --------- The twReset() function can be used to reset all the fields for a given key, but previously there was no way to reset an individual field. Now the function "twResetField()" can be used to achieve this, e.g.: statsdb:twResetField(lt.login, "countLogins") Configurable accuracy for HLL and CountMin types ----------- The HLL and CountMin types of StatsDB entries are probabilistic data structures, which trade accuracy for memory usage. Previously the accuracy (and thus memory usage) was hardcoded, however now their accuracy can be tuned. Increasing accuracy however means a (potentially very large) increase in memory usage, so extreme care must be taken before modifying these parameters. The function setHLLBits() can be used to change the accuracy of the HLL type. The value supplied can be between 4 and 30, with the default value being 6. The function setCountMinBits() can be used to set the accuracy of the CountMin type. See the wforce.conf manpage for full details. DB Synchronization for newly started wforce instances -------- Normally when a wforce instance starts, it has a "fresh" set of Stats DBs, and therefore can take a reasonable period of time (an hour or more depending on the policy) before it starts giving the same answers as other wforce servers in a cluster which have been running for some time. This issue is now addressed with the ability to tell a wforce server to find another server which has been running for longer than a configurable period of time, from which it can download the entire set of Stats DBs. While a server is in the process of downloading the Stats DBs from another server, it is in a "warmup" state; this fact is reflected in a new return value from the "ping" REST API endpoint. In order to enable this feature, the "addSyncHosts()" function must be used, once for each host that will be contacted on startup, for example: -- Add 10.2.3.1:8084 as a sync host, -- and use the password "super" -- Send the DB dump to 10.2.1.1:4001 -- and let me know on 10.2.1.1:8084 when the dump is finished addSyncHost("10.2.3.1:8084", "super", "10.2.1.1:4001", "10.2.1.1:8084") The default time that the sync hosts must have been "up" for is 3600 seconds, however that can be configured using "setMinSyncHostUptime()", e.g.: setMinSyncHostUptime(1800) The replication of data between the sync host and the wforce instance that is starting up is always performed over TCP. See wforce.conf for full details. Support for replication over TCP ------------ The addSiblings() and setSiblings() functions now take an extra (optional) parameter that specifies whether the replication should use UDP or TCP. The default is UDP. If the protocol is specified, the port must also be specified. For example: setSiblings({"127.0.1.2", "127.0.1.3:4004", "127.0.2.23:4004:tcp"}) addSibling("192.168.1.23") addSibling("192.168.1.23:4001:udp") addSibling("192.168.1.23:4003:tcp") Customizable log facility via a command line option ------- The new "-f" or "--facility" command line option can be used to set the syslog facility used for wforce logging. For example: wforce -f "local0" New trackalert daemon ---------- A new daemon "trackalert" is part of the product. This daemon shares a lot of functionality with wforce, particularly in terms of Lua support. However the REST API for trackalert is much simpler, consisting only of "report" and "stats" endpoints. The trackalert daemon is designed to process login reports sent to it by wforce, use those reports to determine whether the login is suspicious. It is also designed to run Lua functions on a periodic basis using a configurable scheduler, in order to run tasks such as finding suspicious IPs or compromised accounts. The trackalert daemon works best with the Lua policy delivered in the separate wforce-policy package. That policy implements suspicious login alerts using historical report data stored in Elasticsearch, as well as periodic searches of Elasticsearch to find suspicious IPs and compromised accounts. For the trackalert daemon to be effective, wforce must be configured to send reports to both trackalert and Elasticsearch using webhooks. Logstash Configuration and Elasticsearch Templates -------- This release ships with sample logstash configuration and Elasticsearch mapping template to ensure that the report data is stored in a consistent form by Elasticsearch. The minimum version of ELK (Elasticsearch, Logstash, Kibana) that is required is version 6. Kibana Reports and Dashboards ------ This release ships with a sample set of reports and dashboards for Kibana (version 6+). Report API ------ A REST API to handle querying and modification of the data stored in Elasticsearch. Currently this ships as an informational feature for experimentation; a future release will ship this API as a package shipping a deployable and supported webapp. The API is documented using OpenAPI (Swagger); consult the documentation on documentation.open-xchange.com. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.10.0.md000066400000000000000000000025441461473602600231330ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.10.0 ## New Features * Add Enterprise Linux 9 Build Target * Option to use OpenSSL instead of Libsodium for encryption ## Removed Features - Remove Legacy GeoIP from Packages and Dockerfiles/Images - Remove the Report API from weakforced entirely ## Add Enterprise Linux 9 Build Target Enterprise Linux 9-based systems are now supported as a build target. Oracle Linux 9 is used as the build environment, but the package should work on any EL-9 environment. Additionally, el-7, el-8 and el-9 aliases are available as build targets. ## Option to use OpenSSL instead of Libsodium for encryption When libsodium is not available, weakforced will now use openssl crypto functions instead for encryption, including encryption between the client and the server, and replication encryption. OpenSSL encryption is used for the docker image, but the default for built packages is still libsodium. ## Remove Legacy GeoIP from Packages and Dockerfiles/Images The legacy GeoIP Library is no longer included in the packages or Dockerfiles/images for weakforced. ## Remove the report_api from weakforced entirely The Report API has been removed from weakforced. This feature was never used (to my knowledge), and was creating a significant burden in terms of the maintenance of the python dependencies. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.10.1.md000066400000000000000000000006651461473602600231360ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.10.1 ## Bug Fixes * Fixed bug in GeoIP2 lookups where return values were not populated ## Fixed bug in GeoIP2 lookups where return values were not populated The GeoIP2 LookupCity Lua function was never correctly implemented, so results were not exposed to Lua correctly. This fix exposes the results using the correct method to ensure future operation. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.10.2.md000066400000000000000000000005771461473602600231410ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.10.2 ## Improvements * Fixed LuaState selection algorithm to use a free pool, which should lead to faster/more consistent selection of lua states by threads. * `powerdns/wforce-minimal` image is now available, using alpine for more secure and much smaller image than the existing debian-based image. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.2.0.md000066400000000000000000000156311461473602600230550ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.0.0 New Features ------------ * Lookup individual GeoIP2 values (Unsigned Integer, String, Double/Float and Boolean) * Add session_id to wforce LoginTuple * The Report API is now deployable via proper packaging, systemd and gunicorn * Custom GET Endpoints supporting non-JSON return values * New Kibana Reports and Dashboard to view effectiveness of wforce policy * New "type" field added to built-in webhooks * Built-in whitelists added in addition to built-in blacklists * New checkBlacklist and checkWhitelist functions in Lua * Built-in black/whitelisting can be disabled and checked from Lua instead * Thread names support * Built-In Blacklist and Whitelist return messages are configurable * Support TCP Keepalive Bug Fixes/Changes ----------------- * Fix typo in wforce.conf.example blackistLogin->blacklistLogin * Python3 support throughout wforce (regression tests and deployment) * Uses "bytes" instead of "string" in replication, which avoids protobuf errors for non-UTF-8 StatsDB keys or values. * Remove ctpl from wforce * Sibling threads now use explicit std::queue instead of ctpl * Only connect to TCP siblings when necessary (not on startup) to prevent delays on startup. * Updated logstash.conf file for ELK integration Lookup Individual GeoIP2 Values ---------- Previously the GeoIP2 support was limited to retrieving City and County DB information, with a fixed set of fields returned. However this meant that other DBs (e.g. ISPs, Anonymizers etc.) could not be queried, as well as additional fields in City or Country DBs. Now there are four new Lua functions to query arbitrary fields in the Maxmind DBs: * lookupStringValue * lookupDoubleValue * lookupUIntValue * lookupBoolValue For example: local citydb = getGeoIP2DB("City") local city_name = citydb:lookupStringValue(newCA(ip_address), {"city", "names", "en"}) local city_lat = citydb:lookupDoubleValue(newCA(ip_address), {"location", "latitude"}) local city_long = citydb:lookupDoubleValue(newCA(ip_address), {"location", "longitude"}) local accuracy = citydb:lookupUIntValue(newCA(ip_address), {"location", "accuracy_radius"}) local eu = citydb:lookupBoolValue(newCA(ip_address), {"country", "is_in_european_union"}) Add session_id to wforce LoginTuple ---------- It can be useful for wforce to recognise when multiple logins belong to the same session. Therefore a new field "session_id" is added to the LoginTuple table, which allows a session id to be passed to wforce. This field is optional, so if empty it should be ignored. For example: local session_id = lt.session_id Deployable Report API ------------ Previously the Report API was supplied as an experimental feature, which was supplied as a Flask application, but without support for running in production, packaging etc. Now the Report API is fully deployable; it is packaged in a new package "wforce-report-api", and can be started/stopped via systemd. For example: systemctl enable wforce-report-api systemctl start wforce-report-api The Report API is still a Flash application, which runs under gunicorn, and the configuration and deployment settings are configurable. Report API configuration is via: /etc/wforce-report-api/wforce-report-api-instance.conf Gunicorn configuration is via: /etc/wforce-report-api/wforce-report-api-web.conf It is recommended to run Gunicorn behind an nginx proxy in a production deployment. Custom GET Endpoints ------- The existing Custom Endpoint functionality is based only on POST commands, and requires return values to be json-encoded. This presents problems with more limited clients such as firewalls or other network equipment, which only support HTTP GET, and cannot parse json-encoded return values. The Custom GET endpoints solve this issue by allowing endpoints to be created which are based on HTTP GET, and which return data using the text/plain content type. Custom GET endpoints do not take any parameters, so there is no way to pass data to the endpoints. For example the following function uses the new getIPBlacklist() functionality to return a text version of the IP blacklist: function returnTextBlacklist() local ipbl = getIPBlacklist() local ret_table = {} for i,j in pairs(ipbl) do for k,v in pairs(j) do if k == "ip" then table.insert(ret_table, v) end end end local s = table.concat(ret_table, "\n") .. "\n" unblacklistIP(newCA("1.2.3.4")) return s end setCustomGetEndpoint("textBlacklist", returnTextBlacklist) New Kibana Reports and Dashboard ---------- New Kibana Reports and Dashboard have been added to the kibana_saved_objects.json file. These reports are based on "allow" webhooks sent to elasticsearch. New "type" field added to built-in webhooks ------ The built-in webhooks add a "type" field to the webhook json to make it easier to search for specific webhook types in elasticsearch. The type field can have the following values: * wforce_report * wforce_allow * wforce_reset * wforce_expireblwl * wforce_addblwl * wforce_delblwl Built-in Whitelists ------ In addition to the built-in blacklists there are now built-in whitelists. Entries can be added and deleted from the built-in whitelists using either the REST API or using Lua commands. For example using the REST API: curl -XPOST --data '{ "ip":"1.2.3.4" }' -u user:pass http://localhost:8084/?command=addWLEntry For example using Lua: whitelistIP(lt.remote, 3600, "This is the reason why it is blacklisted") New checkBlacklist and checkWhitelist functions in Lua ------------- New functions to check the built-in black and whitelists are available from Lua: * checkBlacklistIP * checkBlacklistLogin * checkBlacklistIPLogin * checkWhitelistIP * checkWhitelistLogin * checkWhitelistIPLogin See the wforce.conf man page for more details. Built-in Black/Whitelisting can be disabled ---------- The built-in black/whitelisting functionality can be disabled so that the checks can instead be performed from Lua. This is achieved with the following Lua commands: disableBuiltinBlacklists() disableBuiltinWhitelists() Thread Names Support -------- Every thread type in wforce is now named, so that top -H for example will show individual thread names. This can be very useful for diagnosing when particular threads are CPU-bound and could benefit from increasing the size of the thread pool for example. Built-In Blacklist return messages are configurable --------------- The following new functions enable the return messages for built-in blacklists to be configured: * setBlacklistIPRetMsg * setBlacklistLoginRetMsg * setBlacklistIPLoginRetMsg See the wforce.conf man page for more details. Support TCP Keepalive ---------- The wforce daemon previously did not enable TCP keepalive on accepted sockets. The TCP keepalive socketoption is now enabled for all sockets. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.2.1.md000066400000000000000000000002311461473602600230440ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.2.1 Bug Fixes/Changes ----------------- * Fix wforce crash in Sibling send thread triggered by syncDB operation weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.4.0.md000066400000000000000000000055431461473602600230600ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.4.0 ## New Features * New wf_dump_entries tool to dump stats DBs to file * Support for new "forwarding" type in replication messages * Support for Prometheus via the new /metrics REST endpoint * Add Date, Last-Modified and Cache-Control headers to all responses * Session-ID now logged for allow/report commands * Improved logging to show line numbers for Lua errors * Enable TCP keepalive for redis connections * Configurable timeout for R/W on redis connections ## Bug Fixes/Changes * Fix duplicate command stats under some circumstances ## New wf_dump_entries Tool New tool to dump the contents of Stats DBs for debugging purposes. `man wforce_dump_entries` for more information. ## Forwarding Type in Replication Messages Replication messages now have a 'forwarding' flag, which is used to indicate when a message has been forwarded. This can be used to prevent forwarding loops. ## Support for Prometheus Metrics Both the wforce and trackalert daemons support native Prometheus metrics via the new /metrics REST API endpoint. This endpoint follows the format described [here](https://prometheus.io/docs/instrumenting/exposition_formats/). The prometheus metrics deprecate the existing metrics functionality including the following console commands: * showPerfStats() * showCommandStats() * showCustomStats() as well as the following REST API endpoints: * /?command=stats The prometheus metrics include metrics for many components that were not previously instrumented, including: * Redis statistics * DNS Queries * Whitelists and blacklists ## New HTTP Response Headers All HTTP responses now include the following headers: * Last-Modified * Date * Cache-Control Last-Modified and Date headers will always reflect the current date/time as seen by the wforce server. ## Session-ID Logging The allow and report logs will now contain session_id information. ## Improved Logging for Lua Errors The Lua wrapper code has been updated to provide better traceback information, including line numbers, for Lua errors. This helps when writing Lua policy that triggers a Lua exception. ## Enable TCP keepalive for redis connections Redis connections could be timed-out by middleboxes, which would not be detected because keepalive was not enabled for Redis connections. Now it is enabled (always). ## Configurable timeout for R/W on redis connections Previously reads from and writes to Redis were subject to the underlying socket timeout defaults. Now the timeout defaults to 100000 microseconds, and is configurable with new Lua functions: blacklistPersistRWTimeout() and whitelistPersistRWTimeout(). See wforce.conf for more details. ## Fix Duplicate Command Stats Under certain circumstances, relating to EOF handling when sockets are closed, REST API command statistics would be double counted. This has been fixed by refactoring the EOF handling code. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.4.1.md000066400000000000000000000033311461473602600230520ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.4.1 ## New Features * Dynamic management of siblings via Lua functions and REST API * Optional per-sibling encryption keys * Packaging for Amazon Linux in pdns-builder ## Bug Fixes/Changes * Fix issue where replication length bytes can be truncated causing syncDB problems ## Dynamic Management of Siblings via Lua functions Before this release, siblings could only be defined as part of the startup configuration; there was no way to add or remove siblings dynamically while wforce was running. With this release all sibling management functions in Lua can be used from the console to add/remove siblings at runtime. In addition, per-sibling encryption keys can optionally be specified. The complete set of sibling management functions is as follows: * setSiblings() * setSiblingsWithKey() (New) * addSibling() * addSiblingWithKey() (New) * removeSibling() (New) For full details, see the wforce.conf man page. ## Dynamic Management of Siblings via REST API New REST API endpoints enable siblings to be managed dynamically. The new REST API endpoints are as follows: * /?command=addSibling * /?command=removeSibling * /?command=setSibling For more details see the wforce OpenAPI specification, which is available at https://powerdns.github.io/weakforced/ Note that the REST API does not currently support TLS natively, so use of a HTTPS reverse proxy on localhost is strongly recommended when specifying per-sibling encryption keys. # Optional Per-Sibling Encryption Keys All the methods of managing siblings (Lua or REST API) enable per-sibling encryption keys to be set. Encryption keys are 32-byte strings that are Base-64 encoded before passing to the sibling management functions or REST API. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.6.0.md000066400000000000000000000036701461473602600230610ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.6.0 ## New Features * REST API supports TLS/HTTPS natively * Multiple REST API listeners can be configured * Outbound HTTPS connection TLS behaviour is configurable * Build on Debian Bullseye * Remove support for building on Debian Stretch ## Bug Fixes/Changes * Fix issue where building of geoip2 functionality was dependent on legacy geoip library being installed ## REST API Supports TLS/HTTPS natively The `webserver()` configuration command is now deprecated, and is replaced with `addListener()`, which enables both TLS and non-TLS listeners to be created, as well as enabling multiple listeners to be created concurrently. The new command `setWebserverPassword()` is used to set the password for the REST API (previously this was set as part of the `webserver()` command). An example listener without TLS: * `addListener("0.0.0.0:8084", false, "", "", {})` An example listener with TLS: * `addListener("1.2.3.4:1234", true, "/etc/wforce/cert.pem", "/etc/wforce/key.pem", {minimum_protocol="TLSv1.2"})` ` For more details, see the man page for wforce.conf. ## Outbound HTTPS connection TLS behaviour is configurable Various options for the configuration of outbound HTTPS connections are now supported, specifically: * Mutual TLS Authentication - `setCurlClientCertAndKey()` is used to specify the location of a client certificate and key for mTLS. * Using a different CA for checking server certificates - `setCurlCABundleFile()` is used to specify the location of a file containing certs to use for this purposes. * Disable checking peer certificates - `disableCurlPeerVerification()` disables checking of peer certificates (not recommended except for debugging). * Disable peer certificate hostname checking - `disableCurlHostVerification()` disables checking of the hostname in peer certificates (not recommended except for debugging). ## Build on Debian Bullseye Support for building on debian bullseye.weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.6.1.md000066400000000000000000000036751461473602600230670ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.6.1 ## Bug Fixes/Changes * Fix issue where wforce was complaining about not being able to create tmp file on startup * Fix timing issue whereby the webserver was not started before syncDB leading to syncDone failures * Use debian bullseye-slim in wforce docker image to save over 100MB in image size * Fix issue in wforce docker image where the default config file was overridden with a volume mount but not used ## Fix Wforce complaint about not being able to create temporary files on startup Wforce 2.6.x uses an HTTP library which creates temporary directories for file upload on startup, by default in the current working directory, which for wforce is the config directory. For packaged installation of wforce, this is /etc/wforce, which is typically not writable by wforce itself, leading to errors. This fix changes the directory for those temporary files to /tmp/wforce. ## Fix timing issue with webserver and syncDB In rare cases when starting up, the syncDB command may start, replicate from another wdforce instance, and complete, before the webserver had finished initializing. This would cause the syncDone command from the other wforce instance to fail. This fix forces wforce to wait until the webserver is ready before starting the syncDB checks. ## Fix issue in wforce docker image where the default config file was overridden by a volume mount by not used The wforce docker image documentation states that a volume mount can be used to specify a custom config file in /etc/wforce/wforce.conf, however this was not actually the case. The file was only used if the environment variable WFORCE_CONFIG_FILE was also set, which is incorrect, because that variable is only supposed to be used to specify a *new* location for the config file. This fix ensures that whenever a volume mount correctly mounts a custom /etc/wforce/wforce.conf file, it is both used, and a log message is output stating that it is being used. weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.6.2.md000066400000000000000000000033421461473602600230570ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.6.2 ## Bug Fixes/Changes * Better error checking in blacklist loading to prevent deadlock * Fix missing stub for setBlacklistIPRetMsg() Lua function * Fix trackalert crash when schedules are used before global Lua state is initialised * Return 401 with appropriate JSON instead of 404 when webserver ACL is used * New --loglevel flag to control the log level of stdout logging ## Better error checking in blacklist loading to prevent deadlock Under certain conditions, i.e. when Redis was available but non-responsive, the blacklist loading function would not return, causing deadlock. This has been fixed. ## Fix missing stub for setBlacklistIPRetMsg() Lua function The setBlacklistIPRetMsg() Lua function was missing a stub, which meant that it could not be used. This has now been corrected. ## Fix trackalert crash when schedules are used before global Lua state is initialised Fixed an issue where trackalert would crash when a schedule was created which ran immediately, before the global Lua state was initialised. ## Return 401 with appropriate JSON instead of 404 when webserver ACL is used Fixed an issue where the webserver ACL was causing 404 errors instead of 401 errors. Now a 401 and an appropriate JSON message are returned. ## New --loglevel flag to control the log level of stdout logging Previously there was no way to control the loglevel of the stdout logging, which meant that even debug logging would be logged. Now there is a -l or --loglevel flag, which takes the value 0-7 (matching the syslog levels), and which defaults to 6 (infolog). This fix also applies to the built-in webserver, which only logs to stdout, and which previously only logged errors, but which now obeys this flag.weakforced-2.10.2/docs/release_notes/ReleaseNotes-2.8.0.md000066400000000000000000000044141461473602600230600ustar00rootroot00000000000000# Release Notes for OX Abuse Shield 2.8.0 ## New Features * Support ELK 7.x Stack * Support Date Expansion in WebHook URLs * Enable IP and Login substitution in blocklist return messages * Add config option to disable password for /metrics endpoint * Support redis usernames and passwords for redis authentication * Support hostnames for redis configuration in addition to IP addresses ## Bug Fixes/Changes * Fix an issue where IPv6 ComboAddress returned zero port number (which caused v6 HTTP listen addresses to not work) * Set V6ONLY socket option to stop v6 sockets from managing v4 addresses for replication * Return the IP address of the client in JSON of ACL denied response ## Support ELK 7.x Stack Support Elasticsearch, Logstash and Kibana 7.x stack: * Continuous Integration now tests against ELK 7.x * Logstash Templates now work with 7.x * Kibana Dashboards are now in ndjson format ## Support Date Expansion in WebHook URLs WebHook URLs can be specified with fields representing years, months and days that are expanded at runtime, for example: config_key["url"] = "https://example.com/foo/index-%{YYYY}-${MM}-{%dd}" See the wforce_webhook man page for more details. ## Enable IP and Login Substitution in blocklist return messages For example: setBlacklistIPRetMsg("Go away your IP {ip} is blacklisted") setBlacklistLoginRetMsg("Go away your login {login} is blacklisted") See the wforce.conf man page for more details. ## Add config option to disable password for /metrics endpoint Adding the following to wforce.conf or trackalert.conf: setMetricsNoPassword() will disable the password for the metrics endpoint. See wforce.conf and trackalert.conf manpages for more details. ## Support redis usernames and passwords for redis authentication Redis authentication is supported with the following configuration in wforce.conf: blacklistRedisUsername() blacklistRedisPassword() whitelistRedisUsername() whitelistRedisPassword() The username is optional, depending on whether a username is set in redis. See wforce.conf manpage for more details. ## Support hostnames for redis configuration in addition to IP addresses The blacklistPersistDB() and whitelistPersistDB() configuration commands now accept hostnames as well as IP addresses. weakforced-2.10.2/docs/swag2md.pl000077500000000000000000000007511461473602600165560ustar00rootroot00000000000000use strict; use warnings; use Swagger2; use Swagger2::Markdown; my %pod_markdown_opts; my $s2md = Swagger2::Markdown->new( swagger2 => Swagger2->new->load( $ARGV[0] ) ); my $api_bp_str = $s2md->api_blueprint; my $markdown_str = $s2md->markdown( %pod_markdown_opts ); my $progname = $ARGV[0]; $progname =~ /.*\/(.*)\.([1-9]).*/; $progname = uc $1; my $man_version = $2; print "% $progname($man_version)\n"; print "% Open-Xchange\n"; print "% 2018\n\n"; print $markdown_str; weakforced-2.10.2/docs/swagger/000077500000000000000000000000001461473602600163065ustar00rootroot00000000000000weakforced-2.10.2/docs/swagger/report_api.7.yml000066400000000000000000000173061461473602600213510ustar00rootroot00000000000000swagger: "2.0" info: version: "2.10.2" title: "OX Abuse Shield Report API" description: "An API to retrieve and modify report information" contact: name: "Open-Xchange" license: name: "GPL3" url: http://github.com/PowerDNS/weakforced/blob/master/LICENSE host: "doesnotexist.open-xchange.com" basePath: "/v1" schemes: - "http" consumes: - "application/json" produces: - "application/json" paths: /logins: post: description: "Retrieve information about logins matching the supplied criteria" operationId: "getLogins" produces: - "application/json" parameters: - name: "QueryParams" in: body description: "Query Parameters for retrieval commands" required: true schema: $ref: "#/definitions/QueryParams" responses: "200": description: "logins response" schema: $ref: "#/definitions/LoginsResponse" default: description: "Unexpected error" schema: $ref: "#/definitions/ErrorResponse" /devices: post: description: "Retrieve information about devices matching the supplied criteria" operationId: "getDevices" produces: - "application/json" parameters: - name: "QueryParams" in: body description: "Query Parameters for retrieval commands" required: true schema: $ref: "#/definitions/QueryParams" responses: "200": description: "devices response" schema: $ref: "#/definitions/DevicesResponse" default: description: "Unexpected error" schema: $ref: "#/definitions/ErrorResponse" /logins/confirm: post: description: "Confirm that a login is valid or invalid" operationId: "confirmLogin" produces: - "application/json" parameters: - name: "ConfirmLoginParams" in: body description: "Confirm Login parameters" required: true schema: $ref: "#/definitions/ConfirmLoginParams" responses: "200": description: login confirmed default: description: "Unexpected error" schema: $ref: "#/definitions/ErrorResponse" /devices/forget: post: description: "Forget a particular device" operationId: "forgetDevice" produces: - "application/json" parameters: - name: "ForgetDeviceParams" in: body description: "Forget Device Parameters" required: true schema: $ref: "#/definitions/QueryParams" responses: "200": description: device forgotten default: description: "Unexpected error" schema: $ref: "#/definitions/ErrorResponse" definitions: ConfirmLoginParams: type: object required: - id - confirm properties: id: description: "The ID of the login to confirm" type: string confirm: description: "Boolean: true to confirm valid login, false to confirm invalid login" type: boolean QueryParams: type: object properties: login: description: "Return information matching this login name (ANDed with IP and device if supplied)" type: string ip: description: "Return information matching this IP address (v4 or v6). (ANDed with login and device if supplied)" type: string device: $ref: "#/definitions/DeviceObject" max_num: description: "Specify the maximum number of results to return (defaults to 100)" type: integer default: 100 max_age: description: "Specify the maximum age of results to return (based on the login time). Using elasticsearch date math, e.g. 1d, 3M, or 12h. Defaults to 1w (one week)" type: string default: 1w example: ip: 243.22.12.98 login: fred@foobar.com max_num: 20 max_age: 1w ErrorResponse: type: object required: - error_msg properties: error_msg: type: string example: error_msg: "Could not parse max_age (-3dMwY)" LoginsResponse: type: object properties: query: $ref: "#/definitions/QueryParams" response: type: array items: $ref: "#/definitions/LoginObject" LoginObject: type: object properties: login: description: "The login name used to login" type: string ip: description: "The IP address used to login" type: string country_name: description: "The name of the country associated with the IP address" type: string country_code: description: "The 2-digit country code associated with the IP address" type: string city_name: description: "The name of the city associated with the IP address" type: string region_name: description: "The name of the region associated with the IP address" type: string location: description: "The longitude and latitude of the IP address [long, lat]" type: string login_datetime: description: "The date, time and timezone of the login in ISO-8601 format" type: string device_id: description: "The string that the login device used to identify itself (could be empty)" type: string device_attrs: $ref: "#/definitions/DeviceObject" protocol: description: "The protocol used to login" type: string id: description: "A unique ID for the login" type: string required: - login_datetime - login - ip - id DevicesResponse: type: object properties: query: $ref: "#/definitions/QueryParams" response: type: array items: $ref: "#/definitions/LoginObject" DeviceObject: type: object properties: app.name: description: "The name of the (mobile) app used to login" type: string app.brand: description: "The brand of the (mobile) app used to login" type: string app.major: description: "The major version of the (mobile) app used to login" type: string app.minor: description: "The minor version of the (mobile) app used to login" type: string imapc.family: description: "The family of IMAP client used to login" type: string imapc.major: description: "The major version of the IMAP client used to login" type: string imapc.minor: description: "The minor version of the IMAP client used to login" type: string browser.family: description: "The browser family of the device used to login" type: string browser.major: description: "The major version of the browser used to login" type: string browser.minor: description: "The minor version of the browser of the device used to login" type: string os.family: description: "The OS family of the device used to login" type: string os.major: description: "The major version of the OS of the device used to login" type: string os.minor: description: "The minor version of the OS of the device used to login" type: string device.family: description: "The family of the device used to login" type: string device.model: description: "The model of the device used to login" type: string device.brand: description: "The brand of device used to login" type: string weakforced-2.10.2/docs/swagger/set-version000077500000000000000000000005311461473602600205110ustar00rootroot00000000000000#!/usr/bin/env bash VERSION=$1 [ -z "$VERSION" ] && exit 1 OS=`uname` SED_ARG='-r' if [ $OS == "Darwin" ] then SED_ARG='-E' fi for i in wforce_api.7.yml trackalert_api.7.yml report_api.7.yml do sed $SED_ARG "s/version:(.*)/version: \"$VERSION\"/" $i >$i.tmp if [ $? != 0 ] then exit 1 fi mv $i.tmp $i done exit 0 weakforced-2.10.2/docs/swagger/trackalert_api.7.yml000066400000000000000000000113061461473602600221640ustar00rootroot00000000000000swagger: "2.0" info: version: "2.10.2" title: "OX Abuse Shield (Trackalert)" description: "An API to the trackalert daemon to prevent brute-force and abuse of mail systems" contact: name: "Open-Xchange" license: name: "GPL3" url: http://github.com/PowerDNS/weakforced/blob/master/LICENSE host: "doesnotexist.open-xchange.com" basePath: "/" schemes: - "http" - "https" consumes: - "application/json" produces: - "application/json" paths: /?command=report: post: description: "Creates a new report about a login (successful or unsuccessful)" operationId: "report" produces: - "application/json" parameters: - name: "LoginTupleReport" in: body description: "Login Tuple for report command" required: true schema: $ref: "#/definitions/LoginTupleReport" responses: "200": description: "report response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=stats: get: description: "Get server stats" operationId: "stats" produces: - "application/json" responses: "200": description: "stats response" schema: $ref: "#/definitions/statsResponse" default: description: unexpected error schema: $ref: "#/definitions/Error" definitions: LoginTupleReport: required: - login - remote - pwhash - success example: login: joe.bloggs remote: 192.168.1.2 pwhash: cc04 success: true properties: login: type: string remote: type: string pwhash: type: string success: type: boolean policy_reject: type: boolean protocol: type: string enum: - http - imap - pop3 - smtp - mobileapi tls: type: boolean device_id: type: string device_attrs: $ref: "#/definitions/LTAttrs" attrs: $ref: "#/definitions/LTAttrs" LTAttrs: type: object properties: attr_name_single_value: type: string attr_name multi_value: type: array items: type: string example: attr1: value1 attr2: value2 attr3: [ value3, value4 ] Error: required: - status - reason properties: status: type: string reason: type: string example: status: failure reason: Unauthorized statsResponse: type: object properties: reports: type: integer sys-msec: type: integer user-msec: type: integer perfstats: type: object description: Latency buckets for worker thread wait (WTW) and run (WTR) properties: WTW_0_1: type: integer WTW_1_10: type: integer WTW_10_100: type: integer WTW_100_1000: type: integer WTW_Slow: type: integer WTR_0_1: type: integer WTR_1_10: type: integer WTR_10_100: type: integer WTR_100_1000: type: integer WTR_Slow: type: integer commandstats: type: object description: Count of REST API commands run in last 5 minutes properties: addBLEntry: type: integer delBLEntry: type: integer getBL: type: integer getDBStats: type: integer stats: type: integer allow: type: integer report: type: integer reset: type: integer ping: type: integer customstats: type: object description: Count of custom statistics in last 5 minutes properties: custom_stat: type: integer example: reports: 28291 sys-msec: 97211221 user-msec: 292910108 perfstats: {"WTR_0_1": 0, "WTR_100_1000": 0, "WTR_10_100": 0, "WTR_1_10": 0, "WTR_Slow": 0, "WTW_0_1": 1, "WTW_100_1000": 0, "WTW_10_100": 0, "WTW_1_10": 0, "WTW_Slow": 0} commandstats: {"addBLEntry": 0, "allow": 0, "delBLEntry": 0, "getBL": 0, "getDBStats": 0, "ping": 0, "report": 0, "reset": 0, "stats": 0} customstats: {"custom1": 0, "custom2": 0} weakforced-2.10.2/docs/swagger/wforce_api.7.yml000066400000000000000000000561701461473602600213250ustar00rootroot00000000000000swagger: "2.0" info: version: "2.10.2" title: "OX Abuse Shield (Wforce)" description: "An API to the wforce daemon to prevent brute-force and abuse of mail systems" contact: name: "Open-Xchange" license: name: "GPL3" url: http://github.com/PowerDNS/weakforced/blob/master/LICENSE host: "doesnotexist.open-xchange.com" basePath: "/" schemes: - "http" - "https" consumes: - "application/json" produces: - "application/json" paths: /?command=report: post: description: "Creates a new report about a login (successful or unsuccessful)" operationId: "report" produces: - "application/json" parameters: - name: "LoginTupleReport" in: body description: "Login Tuple for report command" required: true schema: $ref: "#/definitions/LoginTupleReport" responses: "200": description: "report response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=allow: post: description: "Query whether a login should be allowed" operationId: "allow" produces: - "application/json" parameters: - name: "LoginTupleAllow" in: body description: "Login Tuple for allow command" required: true schema: $ref: "#/definitions/LoginTupleAllow" responses: "200": description: "allow response" schema: type: object required: - status - msg - r_attrs properties: status: type: integer msg: type: string r_attrs: type: object properties: attr_name: type: string example: two_factor_required: true default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=reset: post: description: "Reset the stats and any blacklist entry for an IP and/or login" operationId: "reset" produces: - "application/json" parameters: - name: "Reset" in: body description: "Provide IP and/or login" required: true schema: $ref: "#/definitions/ResetStatsParams" responses: "200": description: "reset response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=delBLEntry: post: description: "Delete a blacklist entry for an IP and/or login. Note that netmask and ip parameters are mutually exclusive." operationId: "delBLEntry" produces: - "application/json" parameters: - name: "delBLEntry" in: body description: "Provide IP and/or login" required: true schema: $ref: "#/definitions/DelBLWLEntryParams" responses: "200": description: "delBLEntry response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=addBLEntry: post: description: "Add a blacklist entry for an IP and/or login. Note that netmask and ip parameters are mutually exclusive." operationId: "addBLEntry" produces: - "application/json" parameters: - name: "addBLEntry" in: body description: "Provide IP and/or login" required: true schema: $ref: "#/definitions/AddBLWLEntryParams" responses: "200": description: "addBLEntry response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=delWLEntry: post: description: "Delete a whitelist entry for an IP and/or login. Note that netmask and ip parameters are mutually exclusive." operationId: "delWLEntry" produces: - "application/json" parameters: - name: "delWLEntry" in: body description: "Provide IP and/or login" required: true schema: $ref: "#/definitions/DelBLWLEntryParams" responses: "200": description: "delWLEntry response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=addWLEntry: post: description: "Add a whitelist entry for an IP and/or login. Note that netmask and ip parameters are mutually exclusive." operationId: "addWLEntry" produces: - "application/json" parameters: - name: "addWLEntry" in: body description: "Provide IP and/or login" required: true schema: $ref: "#/definitions/AddBLWLEntryParams" responses: "200": description: "addWLEntry response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=ping: get: description: "Ping the server to ensure it is operational" operationId: "ping" produces: - "application/json" responses: "200": description: "ping response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=syncDone: get: description: "Tell the server that DB syncing is done" operationId: "syncDone" produces: - "application/json" responses: "200": description: "syncDone response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=stats: get: description: "Get server stats (deprecated - will be removed in future version)" operationId: "stats" produces: - "application/json" responses: "200": description: "stats response" schema: $ref: "#/definitions/statsResponse" default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=getBL: get: description: "Get the list of all blacklisted IPs and/or Logins" operationId: "getBL" produces: - "application/json" responses: "200": description: "getBL response" schema: $ref: "#/definitions/BLWLResponse" default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=getWL: get: description: "Get the list of all whitelisted IPs and/or Logins" operationId: "getWL" produces: - "application/json" responses: "200": description: "getWL response" schema: $ref: "#/definitions/BLWLResponse" default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=getDBStats: post: description: "Get the db stats for an IP and/or login" operationId: "getDBStats" produces: - "application/json" parameters: - name: "GetDBStats" in: body description: "Provide IP and/or login" required: true schema: $ref: "#/definitions/ResetStatsParams" responses: "200": description: "getDBStats response" schema: $ref: "#/definitions/DBStatsResponse" default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=syncDBs: post: description: This is a request to synchronize StatsDBs. operationId: syncDBs produces: - application/json parameters: - name: "syncDBs" in: body description: "The ip and address to sync to" required: true schema: $ref: "#/definitions/syncDBsParams" responses: "200": description: "syncDBs response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=dumpEntries: post: description: This is a request to dump StatsDB entries (consisting of the values of each of the time windows for each field) to a specified IP address and port over TCP. operationId: dumpEntries produces: - application/json parameters: - name: "dumpEntries" in: body description: "The ip and port to dump to" required: true schema: $ref: "#/definitions/dumpEntriesParams" responses: "200": description: "dumpEntries response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=customEndpoint: post: description: Extensible mechanism allows custom REST endpoints to be defined - this is an example operationId: customEndpoint produces: - "application/json" parameters: - name: CustomFuncArgs in: body description: Arguments to custom REST endpoint required: true schema: $ref: "#/definitions/CustomFuncArgs" responses: "200": description: "custom endpoint response" schema: type: object required: - success - r_attrs properties: success: type: boolean r_attrs: type: object properties: attr_name: type: string example: two_factor_required: true default: description: unexpected error schema: $ref: "#/definitions/CustomError" /?command=addSibling: post: description: This is a request to add a new Sibling for replication purposes. operationId: addSibling produces: - application/json parameters: - name: "addSibling" in: body description: "Details about the Sibling to add" required: true schema: $ref: "#/definitions/addSiblingParams" responses: "200": description: "addSibling response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=removeSibling: post: description: This is a request to add remove a Sibling for replication purposes. operationId: removeSibling produces: - application/json parameters: - name: "removeSibling" in: body description: "Details about the Sibling to remove" required: true schema: $ref: "#/definitions/removeSiblingParams" responses: "200": description: "removeSibling response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /?command=setSiblings: post: description: This is a request to set the Siblings for replication purposes. operationId: setSiblings produces: - application/json parameters: - name: "setSiblings" in: body description: "Details about the Sibling to add" required: true schema: $ref: "#/definitions/setSiblingsParams" responses: "200": description: "setSiblings response" schema: type: object required: - status properties: status: type: string example: status: ok default: description: unexpected error schema: $ref: "#/definitions/Error" /metrics: get: description: "Retrieve prometheus metrics" operationId: "metrics" produces: - "text/plain" responses: "200": description: "Prometheus metrics in the format described at https://prometheus.io/docs/instrumenting/exposition_formats/" default: description: unexpected error schema: $ref: "#/definitions/Error" definitions: CustomFuncArgs: required: - attrs example: attrs: { "key":"value" } properties: attrs: $ref: "#/definitions/LTAttrs" LoginTupleReport: required: - login - remote - pwhash - success example: login: joe.bloggs remote: 192.168.1.2 pwhash: cc04 success: true properties: login: type: string remote: type: string pwhash: type: string success: type: boolean policy_reject: type: boolean protocol: type: string enum: - http - imap - pop3 - smtp - mobileapi tls: type: boolean device_id: type: string attrs: $ref: "#/definitions/LTAttrs" LTAttrs: type: object properties: attr_name_single_value: type: string attr_name multi_value: type: array items: type: string example: attr1: value1 attr2: value2 attr3: [ value3, value4 ] LoginTupleAllow: required: - login - remote - pwhash properties: login: type: string remote: type: string pwhash: type: string protocol: type: string enum: - http - imap - pop - smtp - mobileapi tls: type: boolean device_id: type: string attrs: $ref: "#/definitions/LTAttrs" example: login: joe.bloggs remote: 192.168.1.2 pwhash: cc04 success: true protocol: http tls: true attrs: { "foo":"bar" } ResetStatsParams: properties: ip: type: string login: type: string example: ip: 127.0.0.1 DelBLWLEntryParams: properties: ip: type: string netmask: type: string login: type: string example: ip: 127.0.0.1 netmask: 2001:503:ba3e/64 AddBLWLEntryParams: properties: ip: type: string netmask: type: string login: type: string expire_secs: type: integer reason: type: string example: ip: 127.0.0.1 netmask: 2001:503:ba3e::/64 expire_secs: 3600 reason: "Is there ever a good reason" CustomError: required: - success - reason properties: success: type: boolean reason: type: string example: success: false reason: Unauthorized Error: required: - status - reason properties: status: type: string reason: type: string example: status: failure reason: Unauthorized BLWLResponse: type: object properties: bl_entries: type: array items: $ref: "#/definitions/BLWLArray" BLWLArray: type: object properties: ip: $ref: "#/definitions/BLWLEntry" login: $ref: "#/definitions/BLWLEntry" iplogin: $ref: "#/definitions/BLWLEntry" BLWLEntry: type: array items: type: object properties: key_name: type: string expiration: type: string reason: type: string example: login: joe.bloggs expiration: 2002-Jan-01 10:00:01 reason: Too many invalid login attempts from greylisted countries DBStatsResponse: type: object required: - key_name - blacklisted - stats properties: key_name: type: string blacklisted: type: boolean bl_expire: type: string bl_reason: type: string whitelisted: type: boolean wl_expire: type: string wl_reason: type: string stats: $ref: "#/definitions/DBStatsEntry" DBStatsEntry: type: object properties: db_name: $ref: "#/definitions/DBStatsFields" DBStatsFields: type: object properties: field_name: type: integer example: countLogins: 239 diffPassword: 10 statsResponse: type: object properties: reports: type: integer allows: type: integer denieds: type: integer sys-msec: type: integer user-msec: type: integer perfstats: type: object description: Latency buckets for worker thread wait (WTW) and run (WTR)for last 5 minutes properties: WTW_0_1: type: integer WTW_1_10: type: integer WTW_10_100: type: integer WTW_100_1000: type: integer WTW_Slow: type: integer WTR_0_1: type: integer WTR_1_10: type: integer WTR_10_100: type: integer WTR_100_1000: type: integer WTR_Slow: type: integer commandstats: type: object description: Count of REST API commands run in last 5 minutes properties: addBLEntry: type: integer delBLEntry: type: integer getBL: type: integer addWLEntry: type: integer delWLEntry: type: integer getWL: type: integer getDBStats: type: integer stats: type: integer allow: type: integer allow_allowed: type: integer allow_blacklisted: type: integer allow_whitelisted: type: integer allow_denied: type: integer allow_tarpitted: type: integer report: type: integer reset: type: integer ping: type: integer syncDBs: type: integer syncDone: type: integer customstats: type: object description: Count of custom statistics in last 5 minutes properties: custom_stat: type: integer example: reports: 28291 allows: 120321 denieds: 20201 sys-msec: 97211221 user-msec: 292910108 perfstats: {"WTR_0_1": 0, "WTR_100_1000": 0, "WTR_10_100": 0, "WTR_1_10": 0, "WTR_Slow": 0, "WTW_0_1": 1, "WTW_100_1000": 0, "WTW_10_100": 0, "WTW_1_10": 0, "WTW_Slow": 0} commandstats: {"addBLEntry": 0, "addWLEntry": 1, "allow": 8, "allow_allowed": 4, "allow_blacklisted": 0, "allow_denied": 0, "allow_tarpitted": 0, "allow_whitelisted": 4, "delBLEntry": 0, "delWLEntry": 0, "getBL": 0, "getDBStats": 0, "getWL": 0, "ping": 0, "report": 0, "reset": 0, "stats": 3, "syncDBs": 0, "syncDone": 0} customstats: {"custom1": 0, "custom2": 0} syncDBsParams: type: object required: - replication_host - replication_port - callback_url - callback_auth_pwd properties: replication_host: type: string replication_port: type: integer callback_url: type: string callback_auth_pw: type: string encryption_key: type: string dumpEntriesParams: type: object required: - dump_host - dump_port properties: dump_host: type: string dump_port: type: integer addSiblingParams: type: object required: - sibling_host - sibling_port properties: sibling_host: type: string sibling_port: type: integer sibling_protocol: type: string enum: [tcp, udp] encryption_key: type: string removeSiblingParams: type: object required: - sibling_host - sibling_port properties: sibling_host: type: string sibling_port: type: integer setSiblingsParams: type: object properties: siblings: type: array items: type: object required: - sibling_host - sibling_port properties: sibling_host: type: string sibling_port: type: integer sibling_protocol: type: string enum: [ tcp, udp ] encryption_key: type: string weakforced-2.10.2/elk/000077500000000000000000000000001461473602600144725ustar00rootroot00000000000000weakforced-2.10.2/elk/kibana/000077500000000000000000000000001461473602600157175ustar00rootroot00000000000000weakforced-2.10.2/elk/kibana/kibana_saved_objects.json000066400000000000000000003010371461473602600227360ustar00rootroot00000000000000[ { "_id": "Per-User-Login-and-Abuse-Statistics", "_type": "dashboard", "_source": { "title": "Per-User Login and Abuse Statistics", "hits": 0, "description": "", "panelsJSON": "[{\"id\":\"Successful-slash-Unsuccessful-Logins\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":10,\"w\":24,\"h\":15,\"i\":\"1\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Passwords\",\"panelIndex\":\"2\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":10,\"w\":24,\"h\":15,\"i\":\"2\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-IPs-for-Successful-Logins\",\"panelIndex\":\"3\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":25,\"w\":24,\"h\":15,\"i\":\"3\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-IPs-for-Unsuccessful-Logins\",\"panelIndex\":\"4\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":25,\"w\":24,\"h\":15,\"i\":\"4\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Devices-for-Successful-Logins\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":40,\"w\":24,\"h\":15,\"i\":\"5\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Devices-for-Unsuccessful-Logins\",\"panelIndex\":\"6\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":40,\"w\":24,\"h\":15,\"i\":\"6\"},\"version\":\"6.4.2\"},{\"id\":\"Login-Country-over-Time-(Successful-Logins)\",\"panelIndex\":\"7\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":55,\"w\":24,\"h\":15,\"i\":\"7\"},\"version\":\"6.4.2\"},{\"id\":\"Login-Country-over-Time-(Unsuccessful-Logins)\",\"panelIndex\":\"8\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":55,\"w\":24,\"h\":15,\"i\":\"8\"},\"version\":\"6.4.2\"},{\"id\":\"Per-User-Dashboard-Instructions\",\"panelIndex\":\"9\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":10,\"i\":\"9\"},\"version\":\"6.4.2\"},{\"id\":\"Suspicious-IPs\",\"panelIndex\":\"10\",\"type\":\"visualization\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"x\":0,\"y\":70,\"w\":24,\"h\":25,\"i\":\"10\"},\"version\":\"6.4.2\"},{\"id\":\"Suspicious-Device-Types\",\"panelIndex\":\"11\",\"type\":\"visualization\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"x\":24,\"y\":70,\"w\":24,\"h\":25,\"i\":\"11\"},\"version\":\"6.4.2\"},{\"id\":\"Unsuccessful-Login-Map\",\"panelIndex\":\"12\",\"type\":\"visualization\",\"embeddableConfig\":{\"mapCenter\":[14.944784875088372,5.09765625]},\"gridData\":{\"x\":0,\"y\":95,\"w\":48,\"h\":30,\"i\":\"12\"},\"version\":\"6.4.2\"}]", "optionsJSON": "{\"darkTheme\":true,\"useMargins\":true}", "version": 1, "timeRestore": false, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"}}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Per-IP-Login-and-Abuse-Statistics", "_type": "dashboard", "_source": { "title": "Per-IP Login and Abuse Statistics", "hits": 0, "description": "", "panelsJSON": "[{\"id\":\"Successful-slash-Unsuccessful-Logins\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"1\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Passwords\",\"panelIndex\":\"2\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"2\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Devices-for-Successful-Logins\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Devices-for-Unsuccessful-Logins\",\"panelIndex\":\"6\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"6\"},\"version\":\"6.4.2\"},{\"id\":\"Suspicious-Device-Types\",\"panelIndex\":\"11\",\"type\":\"visualization\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":25,\"i\":\"11\"},\"version\":\"6.4.2\"},{\"id\":\"Per-IP-Dashboard-Instructions\",\"panelIndex\":\"13\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":10,\"i\":\"13\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Successful-User-Logins-(Per-IP)\",\"panelIndex\":\"14\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"14\"},\"version\":\"6.4.2\"},{\"id\":\"Distinct-Count-of-Unsuccessful-User-Logins-(Per-IP)\",\"panelIndex\":\"15\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"15\"},\"version\":\"6.4.2\"},{\"id\":\"GeoIP-City-Name\",\"panelIndex\":\"16\",\"type\":\"visualization\",\"gridData\":{\"x\":32,\"y\":10,\"w\":16,\"h\":5,\"i\":\"16\"},\"version\":\"6.4.2\"},{\"id\":\"GeoIP-Country-Name\",\"panelIndex\":\"17\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":10,\"w\":16,\"h\":5,\"i\":\"17\"},\"version\":\"6.4.2\"},{\"id\":\"GeoIP-Region-Name\",\"panelIndex\":\"18\",\"type\":\"visualization\",\"gridData\":{\"x\":16,\"y\":10,\"w\":16,\"h\":5,\"i\":\"18\"},\"version\":\"6.4.2\"}]", "optionsJSON": "{\"darkTheme\":true,\"useMargins\":true}", "version": 1, "timeRestore": false, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"}}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "System-Wide-Login-Abuse", "_type": "dashboard", "_source": { "title": "System-Wide Login Abuse", "hits": 0, "description": "", "panelsJSON": "[{\"id\":\"Unsuccessful-Login-Map\",\"panelIndex\":\"2\",\"type\":\"visualization\",\"embeddableConfig\":{\"mapCenter\":[14.944784875088372,6.328125]},\"gridData\":{\"x\":0,\"y\":50,\"w\":44,\"h\":25,\"i\":\"2\"},\"version\":\"6.4.2\"},{\"id\":\"Suspicious-Device-Types\",\"panelIndex\":\"3\",\"type\":\"visualization\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"x\":0,\"y\":25,\"w\":24,\"h\":25,\"i\":\"3\"},\"version\":\"6.4.2\"},{\"id\":\"Suspicious-IPs\",\"panelIndex\":\"4\",\"type\":\"visualization\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":25,\"i\":\"4\"},\"version\":\"6.4.2\"},{\"id\":\"Suspicious-slash-Compromised-Users\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}},\"gridData\":{\"x\":24,\"y\":0,\"w\":20,\"h\":25,\"i\":\"5\"},\"version\":\"6.4.2\"},{\"id\":\"Successful-slash-Unsuccessful-Logins\",\"type\":\"visualization\",\"panelIndex\":\"6\",\"gridData\":{\"x\":24,\"y\":25,\"w\":20,\"h\":25,\"i\":\"6\"},\"version\":\"6.4.2\"}]", "optionsJSON": "{\"darkTheme\":true,\"useMargins\":true}", "version": 1, "timeRestore": false, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"}}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Per-User-Dashboard-Instructions", "_type": "visualization", "_source": { "title": "Per-User Dashboard Instructions", "visState": "{\"title\":\"Per-User Dashboard Instructions\",\"type\":\"markdown\",\"params\":{\"markdown\":\"Type a username into the search box above to view stats for that user, for example:\\n* frank.black@domain,com\\n* frank.black\\n* login: frank.black@domain.com\\n\\nIf you do not type in a username, you will see system-wide stats, which are not very useful.\"},\"aggs\":[],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Per-IP-Dashboard-Instructions", "_type": "visualization", "_source": { "title": "Per-IP Dashboard Instructions", "visState": "{\"title\":\"Per-IP Dashboard Instructions\",\"type\":\"markdown\",\"params\":{\"markdown\":\"Type an IP address into the search box above to view stats for that IP, for example:\\n* 212.33.12.81\\n* 2001:0db8:85a3:0000:0000:8a2e:0370:7334\\n* remote: 212.33.12.81\\n\\nIf you do not type in an IP address, you will see system-wide stats, which are not very useful.\"},\"aggs\":[],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Successful-slash-Unsuccessful-Logins", "_type": "visualization", "_source": { "title": "Successful/Unsuccessful Logins", "visState": "{\"title\":\"Successful/Unsuccessful Logins\",\"type\":\"area\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"interpolate\":\"linear\",\"legendPosition\":\"top\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"smoothLines\":false,\"times\":[],\"yAxis\":{},\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"success: true\"}},{\"input\":{\"query\":\"success: false\"}}]}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"default_field\":\"*\",\"query\":\"*\"}}},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Distinct-Count-of-IPs-for-Successful-Logins", "_type": "visualization", "_source": { "title": "Distinct Count of IPs for Successful Logins", "visState": "{\"title\":\"Distinct Count of IPs for Successful Logins\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of IPs\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of IPs\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of IPs\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"remote\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Distinct-Count-of-Devices-for-Unsuccessful-Logins", "_type": "visualization", "_source": { "title": "Distinct Count of Devices for Unsuccessful Logins", "visState": "{\"title\":\"Distinct Count of Devices for Unsuccessful Logins\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Devices\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Devices\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Devices\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"device_id\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"success: false\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Distinct-Count-of-IPs-for-Unsuccessful-Logins", "_type": "visualization", "_source": { "title": "Distinct Count of IPs for Unsuccessful Logins", "visState": "{\"title\":\"Distinct Count of IPs for Unsuccessful Logins\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of IPs\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of IPs\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of IPs\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"remote\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Suspicious-IPs", "_type": "visualization", "_source": { "title": "Suspicious IPs", "visState": "{\"title\":\"Suspicious IPs\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Count of Significant Terms\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"remote\",\"size\":100,\"customLabel\":\"Suspicious IPs\"}}],\"listeners\":{}}", "uiStateJSON": "{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"constant_score\":{\"filter\":{\"term\":{\"success\":false}}}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Protocol-Used-over-Time-(Successful-Logins)", "_type": "visualization", "_source": { "title": "Protocol Used over Time (Successful Logins)", "visState": "{\"title\":\"Protocol Used over Time (Successful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"bottom\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Total Logins\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"protocol\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Protocol\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Unsuccessful-Login-Map", "_type": "visualization", "_source": { "title": "Unsuccessful Login Map", "visState": "{\"title\":\"Unsuccessful Login Map\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[15,5],\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.location\",\"autoPrecision\":true,\"customLabel\":\"Unsuccessful Logins\"}}],\"listeners\":{}}", "uiStateJSON": "{\"mapCenter\":[14.944784875088372,5.09765625]}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Suspicious-slash-Compromised-Users", "_type": "visualization", "_source": { "title": "Suspicious/Compromised Users", "visState": "{\"title\":\"Suspicious/Compromised Users\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Significant Terms\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"login\",\"size\":100,\"customLabel\":\"Suspicious/Compromised Users\"}}],\"listeners\":{}}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"constant_score\":{\"filter\":{\"term\":{\"success\":false}}}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Suspicious-Device-Types", "_type": "visualization", "_source": { "title": "Suspicious Device Types", "visState": "{\"title\":\"Suspicious Device Types\",\"type\":\"table\",\"params\":{\"perPage\":6,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Significant Terms\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"device_id\",\"size\":10,\"customLabel\":\"Suspicious Device Types\"}}],\"listeners\":{}}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"constant_score\":{\"filter\":{\"term\":{\"success\":false}}}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Protocol-Used-over-Time", "_type": "visualization", "_source": { "title": "Protocol Used over Time", "visState": "{\"title\":\"Protocol Used over Time\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"bottom\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Total Logins\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"protocol\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Protocol\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Login-Country-over-Time-(Successful-Logins)", "_type": "visualization", "_source": { "title": "Login Country over Time (Successful Logins)", "visState": "{\"title\":\"Login Country over Time (Successful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Login Country (GeoIP)\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geoip.country_code2\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Country (GeoIP)\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Distinct-Count-of-Passwords", "_type": "visualization", "_source": { "title": "Distinct Count of Passwords", "visState": "{\"title\":\"Distinct Count of Passwords\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"top\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{\"min\":1,\"max\":4096}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"pwhash\",\"customLabel\":\"Distinct Count of Passwords\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "98888d10-7329-11e9-8970-ebaa4ad88862", "_type": "index-pattern", "_source": { "title": "logstash-wforce*", "timeFieldName": "@timestamp", "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"device_attrs\",\"type\":\"unknown\",\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false,\"count\":4},{\"name\":\"device_attrs.app\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"device_attrs.app.brand\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.app.major\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.app.minor\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.app.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.browser\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"device_attrs.browser.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.browser.major\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.browser.minor\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.device\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"device_attrs.device.brand\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.device.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.device.model\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.imapc\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"device_attrs.imapc.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.imapc.major\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.imapc.minor\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.os\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"device_attrs.os.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.os.major\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_attrs.os.minor\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"device_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"geoip.city_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.continent_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.country_code2\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.country_code3\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.country_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.latitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.longitude\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.postal_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.region_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.region_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.timezone\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"input\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"login\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"policy_reject\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pwhash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"remote\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.device_attrs\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.device_attrs.imapc\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.device_attrs.imapc.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.device_attrs.imapc.major\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.device_attrs.imapc.minor\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.device_attrs.os\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.device_attrs.os.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.device_attrs.os.major\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.device_attrs.os.minor\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.device_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.login\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.policy_reject\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.pwhash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.remote\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.success\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.t\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request.tls\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.msg\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs\",\"type\":\"unknown\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.r_attrs.action\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.bl_expiry\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.blacklisted\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.defaultReturn\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.expiration\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.graylisted\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.key\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.policy\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.policy_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.reason\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.suspiciousLogin\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.tarpit\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.test_mode\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.threshold\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.whitelisted\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.r_attrs.wl_key\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response.status\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"success\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"t\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_confirmation\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "98914830-7340-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "IPs Rejected by Policy Map", "visState": "{\"title\":\"IPs Rejected by Policy Map\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[15,5],\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"},\"baseLayersAreLoaded\":{},\"tmsLayers\":[{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.4.2\",\"minZoom\":0,\"maxZoom\":10,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|MapTiler|Elastic Maps Service

\",\"subdomains\":[]}],\"selectedTmsLayer\":{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.4.2\",\"minZoom\":0,\"maxZoom\":10,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|MapTiler|Elastic Maps Service

\",\"subdomains\":[]}},\"colorSchema\":\"Yellow to Red\",\"heatClusterSize\":1.5},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.location\",\"autoPrecision\":true,\"isFilteredByCollar\":true,\"useGeocentroid\":true,\"mapZoom\":2,\"mapCenter\":[0,0],\"precision\":2,\"customLabel\":\"Rejected IPs\"}}]}", "uiStateJSON": "{\"mapCenter\":[14.944784875088372,5.09765625]}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"type\",\"value\":\"wforce_allow\",\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"response.status\",\"value\":\"-1\",\"params\":{\"query\":-1,\"type\":\"phrase\"}},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "b7fad100-7340-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "IPs Tarpitted by Policy Map", "visState": "{\"title\":\"IPs Tarpitted by Policy Map\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[15,5],\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"},\"baseLayersAreLoaded\":{},\"tmsLayers\":[{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.4.2\",\"minZoom\":0,\"maxZoom\":10,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|MapTiler|Elastic Maps Service

\",\"subdomains\":[]}],\"selectedTmsLayer\":{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.4.2\",\"minZoom\":0,\"maxZoom\":10,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|MapTiler|Elastic Maps Service

\",\"subdomains\":[]}},\"colorSchema\":\"Yellow to Red\",\"heatClusterSize\":1.5},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.location\",\"autoPrecision\":true,\"isFilteredByCollar\":true,\"useGeocentroid\":true,\"mapZoom\":2,\"mapCenter\":[0,0],\"precision\":2,\"customLabel\":\"Tarpitted IPs\"}}]}", "uiStateJSON": "{\"mapCenter\":[14.944784875088372,5.09765625]}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"type\",\"value\":\"wforce_allow\",\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.tarpit\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.tarpit\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "c115ff20-7346-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Top 25 Blacklisted Users", "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Login Name\",\"field\":\"request.login\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":25},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"aggregate\":\"concat\",\"customLabel\":\"Last 5 Policy Hits\",\"field\":\"response.r_attrs.policy\",\"size\":5,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"},\"schema\":\"metric\",\"type\":\"top_hits\"}],\"params\":{\"perPage\":25,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"title\":\"Top 25 Blacklisted Users\",\"type\":\"table\"}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"response.status\",\"negate\":false,\"params\":{\"query\":-1,\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"-1\"},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.blacklisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "80dec9a0-7346-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Top 25 Rejected Users (Not Blacklisted)", "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Login Name\",\"field\":\"request.login\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":25},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"aggregate\":\"concat\",\"customLabel\":\"Last 5 Policy Hits\",\"field\":\"response.r_attrs.policy\",\"size\":5,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"},\"schema\":\"metric\",\"type\":\"top_hits\"}],\"params\":{\"perPage\":25,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"title\":\"Top 25 Rejected Users (Not Blacklisted)\",\"type\":\"table\"}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"response.status\",\"negate\":false,\"params\":{\"query\":-1,\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"-1\"},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.blacklisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Distinct-Count-of-Devices-for-Successful-Logins", "_type": "visualization", "_source": { "title": "Distinct Count of Devices for Successful Logins", "visState": "{\"title\":\"Distinct Count of Devices for Successful Logins\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Devices\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Devices\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Devices\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"device_id\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Login-Country-over-Time-(Unsuccessful-Logins)", "_type": "visualization", "_source": { "title": "Login Country over Time (Unsuccessful Logins)", "visState": "{\"title\":\"Login Country over Time (Unsuccessful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Login Country (GeoIP)\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geoip.country_code2\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Country (GeoIP)\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Distinct-Count-of-Successful-User-Logins-(Per-IP)", "_type": "visualization", "_source": { "title": "Distinct Count of Successful User Logins (Per-IP)", "visState": "{\"title\":\"Distinct Count of Successful User Logins (Per-IP)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Usernames\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Usernames\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Usernames\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"login\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Distinct-Count-of-Unsuccessful-User-Logins-(Per-IP)", "_type": "visualization", "_source": { "title": "Distinct Count of Unsuccessful User Logins (Per-IP)", "visState": "{\"title\":\"Distinct Count of Unsuccessful User Logins (Per-IP)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Usernames\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Usernames\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Usernames\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"login\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "Protocol-Used-over-Time-(Unsuccessful-Logins)", "_type": "visualization", "_source": { "title": "Protocol Used over Time (Unsuccessful Logins)", "visState": "{\"title\":\"Protocol Used over Time (Unsuccessful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"bottom\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Total Logins\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"protocol\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Protocol\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "171cb3a0-7347-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Policy Hits (Blacklisted)", "visState": "{\"title\":\"Policy Hits (Blacklisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.reason\",\"size\":30,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Blacklist Reason\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"response.r_attrs.blacklisted\",\"negate\":false,\"type\":\"exists\",\"value\":\"exists\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "9d6e6e90-7332-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "IPs Graylisted by Policy", "visState": "{\"title\":\"IPs Graylisted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"No Policy\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Policy Name\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.graylisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.graylisted\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "92f3d020-732f-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Policy Hits (Graylisted)", "visState": "{\"title\":\"Policy Hits (Graylisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.action\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Action\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.status\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Status Code\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.whitelisted\"},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"response.r_attrs.graylisted\",\"value\":\"1\",\"params\":{\"query\":\"1\",\"type\":\"phrase\"}},\"query\":{\"match\":{\"response.r_attrs.graylisted\":{\"query\":\"1\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "d8f3c380-732c-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Policy Hits (Not Graylisted or Whitelisted)", "visState": "{\"title\":\"Policy Hits (Not Graylisted or Whitelisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.action\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Action\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.status\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Status Code\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.graylisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.graylisted\"},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.whitelisted\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "GeoIP-Region-Name", "_type": "visualization", "_source": { "title": "GeoIP Region Name", "visState": "{\"title\":\"GeoIP Region Name\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":12,\"maxFontSize\":24,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.region_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Region Name\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "GeoIP-City-Name", "_type": "visualization", "_source": { "title": "GeoIP City Name", "visState": "{\"title\":\"GeoIP City Name\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":12,\"maxFontSize\":24,\"hideLabel\":false,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.city_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"City Name\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "8e9e7660-7348-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "IPs Blacklisted by Policy", "visState": "{\"title\":\"IPs Blacklisted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.reason\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Blacklist Reason\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"response.status\",\"negate\":false,\"params\":{\"query\":-1,\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"-1\"},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"response.r_attrs.blacklisted\",\"negate\":false,\"type\":\"exists\",\"value\":\"exists\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "37e3d2a0-7331-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "IPs Rejected by Policy (not Blacklisted)", "visState": "{\"title\":\"IPs Rejected by Policy (not Blacklisted)\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"response.status\",\"value\":\"-1\",\"params\":{\"query\":-1,\"type\":\"phrase\"}},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.blacklisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "e9e1c570-7331-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "IPs Tarpitted by Policy", "visState": "{\"title\":\"IPs Tarpitted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}},{\"id\":\"4\",\"enabled\":false,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.status\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Response Code\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.tarpit\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.tarpit\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "a914a370-732f-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Policy Hits (Whitelisted)", "visState": "{\"title\":\"Policy Hits (Whitelisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Policy Name\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"1\",\"params\":{\"query\":\"1\",\"type\":\"phrase\"}},\"query\":{\"match\":{\"response.r_attrs.whitelisted\":{\"query\":\"1\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "12d181f0-7332-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "IPs Whitelisted by Policy", "visState": "{\"title\":\"IPs Whitelisted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"No Policy\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Policy Name\"}}]}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"exists\"},\"exists\":{\"field\":\"response.r_attrs.whitelisted\"},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "GeoIP-Country-Name", "_type": "visualization", "_source": { "title": "GeoIP Country Name", "visState": "{\"title\":\"GeoIP Country Name\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":12,\"maxFontSize\":24,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.country_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Country Name\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "f8e2ec80-732b-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Policy Hits over Time", "visState": "{\"title\":\"Policy Hits over Time\",\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"line\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", "uiStateJSON": "{\"vis\":{\"legendOpen\":true}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"language\":\"lucene\",\"query\":\"type: wforce_allow\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "9c9beb10-732c-11e9-8970-ebaa4ad88862", "_type": "visualization", "_source": { "title": "Policy Hits Breakdown", "visState": "{\"title\":\"Policy Hits Breakdown\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}}]}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"index\":\"98888d10-7329-11e9-8970-ebaa4ad88862\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"type\",\"value\":\"wforce_allow\",\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}" } }, "_meta": { "savedObjectVersion": 2 } }, { "_id": "89cd5660-7334-11e9-8970-ebaa4ad88862", "_type": "dashboard", "_source": { "title": "Abuse Shield Policy Dashboard", "hits": 0, "description": "", "panelsJSON": "[{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"id\":\"f8e2ec80-732b-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":0,\"y\":15},\"id\":\"d8f3c380-732c-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"2\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":24,\"y\":15},\"id\":\"92f3d020-732f-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"3\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":11,\"x\":13,\"y\":30},\"id\":\"a914a370-732f-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"4\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":24,\"y\":30},\"id\":\"37e3d2a0-7331-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"5\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":45},\"id\":\"e9e1c570-7331-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"6\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":24,\"y\":75},\"id\":\"12d181f0-7332-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"7\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":60},\"id\":\"9d6e6e90-7332-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"8\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":24,\"y\":0},\"id\":\"9c9beb10-732c-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"9\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":13,\"x\":0,\"y\":30},\"id\":\"171cb3a0-7347-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"10\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":60},\"id\":\"80dec9a0-7346-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"11\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":90},\"id\":\"c115ff20-7346-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"12\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":75},\"id\":\"98914830-7340-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"13\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":105},\"id\":\"b7fad100-7340-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"14\",\"type\":\"visualization\",\"version\":\"6.4.2\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":45},\"id\":\"8e9e7660-7348-11e9-8970-ebaa4ad88862\",\"panelIndex\":\"15\",\"type\":\"visualization\",\"version\":\"6.4.2\"}]", "optionsJSON": "{\"darkTheme\":true,\"hidePanelTitles\":false,\"useMargins\":true}", "version": 1, "timeRestore": false, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}" } }, "_meta": { "savedObjectVersion": 2 } } ]weakforced-2.10.2/elk/kibana/kibana_saved_objects.ndjson000066400000000000000000003272501461473602600232650ustar00rootroot00000000000000{"attributes":{"fieldAttrs":"{\"device_attrs\":{\"count\":4}}","fields":"[]","runtimeFieldMap":"{}","timeFieldName":"@timestamp","title":"logstash-wforce*","typeMeta":"{}"},"coreMigrationVersion":"7.14.1","id":"98888d10-7329-11e9-8970-ebaa4ad88862","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1632748505152,23],"type":"index-pattern","updated_at":"2021-09-27T13:15:05.152Z","version":"WzEyMDYsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.whitelisted\"},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"IPs Whitelisted by Policy","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}","version":1,"visState":"{\"title\":\"IPs Whitelisted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"No Policy\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Policy Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"12d181f0-7332-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"}],"sort":[1632748542306,136],"type":"visualization","updated_at":"2021-09-27T13:15:42.306Z","version":"WzEyNDMsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"response.r_attrs.blacklisted\",\"negate\":false,\"type\":\"exists\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Policy Hits (Blacklisted)","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Policy Hits (Blacklisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.reason\",\"size\":30,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Blacklist Reason\"}}]}"},"coreMigrationVersion":"7.14.1","id":"171cb3a0-7347-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"}],"sort":[1632748532292,96],"type":"visualization","updated_at":"2021-09-27T13:15:32.292Z","version":"WzEyMzMsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"response.status\",\"value\":\"-1\",\"params\":{\"query\":-1,\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.blacklisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"IPs Rejected by Policy (not Blacklisted)","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}","version":1,"visState":"{\"title\":\"IPs Rejected by Policy (not Blacklisted)\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"37e3d2a0-7331-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index","type":"index-pattern"}],"sort":[1632748539311,124],"type":"visualization","updated_at":"2021-09-27T13:15:39.311Z","version":"WzEyNDAsOV0="} {"attributes":{"description":"","layerListJSON":"[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"0903ff4e-a751-4330-93f7-5ffdf60c08a3\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\"},\"includeInFitToBounds\":true,\"type\":\"VECTOR_TILE\"},{\"alpha\":0.75,\"id\":\"107484f8-e1b8-446a-8b88-3dae0a8872b4\",\"includeInFitToBounds\":true,\"joins\":[],\"label\":\"IPs Rejected by Policy Map\",\"maxZoom\":24,\"minZoom\":0,\"sourceDescriptor\":{\"applyGlobalQuery\":true,\"applyGlobalTime\":true,\"geoField\":\"geoip.location\",\"id\":\"b8ca25bb-67e8-4d3f-aa8d-cb0c136d4c1f\",\"metrics\":[{\"type\":\"count\"}],\"requestType\":\"point\",\"resolution\":\"MOST_FINE\",\"type\":\"ES_GEO_GRID\",\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"style\":{\"isTimeAware\":true,\"properties\":{\"fillColor\":{\"options\":{\"color\":\"Yellow to Red\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"type\":\"ORDINAL\"},\"type\":\"DYNAMIC\"},\"icon\":{\"options\":{\"value\":\"marker\"},\"type\":\"STATIC\"},\"iconOrientation\":{\"options\":{\"orientation\":0},\"type\":\"STATIC\"},\"iconSize\":{\"options\":{\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"maxSize\":18,\"minSize\":7},\"type\":\"DYNAMIC\"},\"labelBorderColor\":{\"options\":{\"color\":\"#FFFFFF\"},\"type\":\"STATIC\"},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}},\"labelColor\":{\"options\":{\"color\":\"#000000\"},\"type\":\"STATIC\"},\"labelSize\":{\"options\":{\"size\":14},\"type\":\"STATIC\"},\"labelText\":{\"options\":{\"value\":\"\"},\"type\":\"STATIC\"},\"lineColor\":{\"options\":{\"color\":\"#3d3d3d\"},\"type\":\"STATIC\"},\"lineWidth\":{\"options\":{\"size\":1},\"type\":\"STATIC\"},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}}},\"type\":\"VECTOR\"},\"type\":\"VECTOR\",\"visible\":true,\"query\":{\"query\":\"type: wforce_allow and response.status: -1 and request.success: false\",\"language\":\"kuery\"}}]","mapStateJSON":"{\"zoom\":2.87,\"center\":{\"lon\":-33.7801,\"lat\":41.38101},\"timeFilters\":{\"from\":\"now/w\",\"to\":\"now/w\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":true,\"backgroundColor\":\"#ffffff\",\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"AUTO_FIT_TO_BOUNDS\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"IPs Rejected by Policy Map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"7.14.1","id":"41464410-1fad-11ec-aabe-ef01f1c3095b","migrationVersion":{"map":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"layer_1_source_index_pattern","type":"index-pattern"}],"sort":[1632910217581,275],"type":"map","updated_at":"2021-09-29T10:10:17.581Z","version":"WzIyODAsOV0="} {"attributes":{"description":"","layerListJSON":"[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"f0fa4c5a-4549-4613-a9b7-d5dd40bac71a\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\"},\"includeInFitToBounds\":true,\"type\":\"VECTOR_TILE\"},{\"alpha\":0.75,\"id\":\"a761af36-1017-4826-96d9-6a2614a84966\",\"includeInFitToBounds\":true,\"joins\":[],\"label\":\"IPs Tarpitted by Policy Map\",\"maxZoom\":24,\"minZoom\":0,\"sourceDescriptor\":{\"applyGlobalQuery\":true,\"applyGlobalTime\":true,\"geoField\":\"geoip.location\",\"id\":\"e121796e-cde9-4584-b780-9b4216608a47\",\"metrics\":[{\"type\":\"count\"}],\"requestType\":\"point\",\"resolution\":\"MOST_FINE\",\"type\":\"ES_GEO_GRID\",\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"style\":{\"isTimeAware\":true,\"properties\":{\"fillColor\":{\"options\":{\"color\":\"Yellow to Red\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"type\":\"ORDINAL\"},\"type\":\"DYNAMIC\"},\"icon\":{\"options\":{\"value\":\"marker\"},\"type\":\"STATIC\"},\"iconOrientation\":{\"options\":{\"orientation\":0},\"type\":\"STATIC\"},\"iconSize\":{\"options\":{\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"maxSize\":18,\"minSize\":7},\"type\":\"DYNAMIC\"},\"labelBorderColor\":{\"options\":{\"color\":\"#FFFFFF\"},\"type\":\"STATIC\"},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}},\"labelColor\":{\"options\":{\"color\":\"#000000\"},\"type\":\"STATIC\"},\"labelSize\":{\"options\":{\"size\":14},\"type\":\"STATIC\"},\"labelText\":{\"options\":{\"value\":\"\"},\"type\":\"STATIC\"},\"lineColor\":{\"options\":{\"color\":\"#3d3d3d\"},\"type\":\"STATIC\"},\"lineWidth\":{\"options\":{\"size\":1},\"type\":\"STATIC\"},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}}},\"type\":\"VECTOR\"},\"type\":\"VECTOR\",\"visible\":true,\"query\":{\"query\":\"type: wforce_allow and response.r_attrs.tarpit: exists\",\"language\":\"kuery\"}}]","mapStateJSON":"{\"zoom\":1.75,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now/w\",\"to\":\"now/w\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":true,\"backgroundColor\":\"#ffffff\",\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"AUTO_FIT_TO_BOUNDS\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"IPs Tarpitted by Policy Map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"7.14.1","id":"684d2f30-2064-11ec-aabe-ef01f1c3095b","migrationVersion":{"map":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"layer_1_source_index_pattern","type":"index-pattern"}],"sort":[1632910596297,345],"type":"map","updated_at":"2021-09-29T10:16:36.297Z","version":"WzIzMzcsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"response.status\",\"negate\":false,\"params\":{\"query\":-1,\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"-1\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.blacklisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Top 25 Rejected Users (Not Blacklisted)","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Login Name\",\"field\":\"request.login\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":25},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"aggregate\":\"concat\",\"customLabel\":\"Last 5 Policy Hits\",\"field\":\"response.r_attrs.policy\",\"size\":5,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"},\"schema\":\"metric\",\"type\":\"top_hits\"}],\"params\":{\"perPage\":25,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Top 25 Rejected Users (Not Blacklisted)\",\"type\":\"table\"}"},"coreMigrationVersion":"7.14.1","id":"80dec9a0-7346-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index","type":"index-pattern"}],"sort":[1632748526240,84],"type":"visualization","updated_at":"2021-09-27T13:15:26.240Z","version":"WzEyMjcsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"type: wforce_allow\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Policy Hits over Time","uiStateJSON":"{\"vis\":{\"legendOpen\":true}}","version":1,"visState":"{\"title\":\"Policy Hits over Time\",\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"line\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.14.1","id":"f8e2ec80-732b-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748544364,140],"type":"visualization","updated_at":"2021-09-27T13:15:44.364Z","version":"WzEyNDUsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.graylisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.graylisted\"},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.whitelisted\"},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Policy Hits (Not Graylisted or Whitelisted)","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Policy Hits (Not Graylisted or Whitelisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.action\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Action\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.status\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Status Code\"}}]}"},"coreMigrationVersion":"7.14.1","id":"d8f3c380-732c-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index","type":"index-pattern"}],"sort":[1632748535326,110],"type":"visualization","updated_at":"2021-09-27T13:15:35.326Z","version":"WzEyMzYsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.whitelisted\"},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"response.r_attrs.graylisted\",\"value\":\"1\",\"params\":{\"query\":\"1\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"},\"query\":{\"match\":{\"response.r_attrs.graylisted\":{\"query\":\"1\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Policy Hits (Graylisted)","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Policy Hits (Graylisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.action\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Action\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.status\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Status Code\"}}]}"},"coreMigrationVersion":"7.14.1","id":"92f3d020-732f-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index","type":"index-pattern"}],"sort":[1632748534273,105],"type":"visualization","updated_at":"2021-09-27T13:15:34.273Z","version":"WzEyMzUsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"response.r_attrs.whitelisted\",\"value\":\"1\",\"params\":{\"query\":\"1\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match\":{\"response.r_attrs.whitelisted\":{\"query\":\"1\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Policy Hits (Whitelisted)","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Policy Hits (Whitelisted)\",\"type\":\"table\",\"params\":{\"perPage\":20,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Policy Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"a914a370-732f-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"}],"sort":[1632748541337,132],"type":"visualization","updated_at":"2021-09-27T13:15:41.337Z","version":"WzEyNDIsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.tarpit\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.tarpit\"},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"IPs Tarpitted by Policy","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}","version":1,"visState":"{\"title\":\"IPs Tarpitted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}},{\"id\":\"4\",\"enabled\":false,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.status\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Response Code\"}}]}"},"coreMigrationVersion":"7.14.1","id":"e9e1c570-7331-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"}],"sort":[1632748540319,128],"type":"visualization","updated_at":"2021-09-27T13:15:40.319Z","version":"WzEyNDEsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.graylisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.graylisted\"},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"IPs Graylisted by Policy","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}","version":1,"visState":"{\"title\":\"IPs Graylisted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"No Policy\",\"missingBucket\":true,\"missingBucketLabel\":\"No Policy\",\"customLabel\":\"Policy Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"9d6e6e90-7332-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"}],"sort":[1632748533263,100],"type":"visualization","updated_at":"2021-09-27T13:15:33.263Z","version":"WzEyMzQsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"type\",\"value\":\"wforce_allow\",\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Policy Hits Breakdown","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Policy Hits Breakdown\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"response.r_attrs.policy\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Policy Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"9c9beb10-732c-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1632748545329,143],"type":"visualization","updated_at":"2021-09-27T13:15:45.329Z","version":"WzEyNDYsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"response.status\",\"negate\":false,\"params\":{\"query\":-1,\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"-1\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}}},{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"exists\",\"key\":\"response.r_attrs.blacklisted\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Top 25 Blacklisted Users","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Login Name\",\"field\":\"request.login\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":25},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"aggregate\":\"concat\",\"customLabel\":\"Last 5 Policy Hits\",\"field\":\"response.r_attrs.policy\",\"size\":5,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"},\"schema\":\"metric\",\"type\":\"top_hits\"}],\"params\":{\"perPage\":25,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Top 25 Blacklisted Users\",\"type\":\"table\"}"},"coreMigrationVersion":"7.14.1","id":"c115ff20-7346-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index","type":"index-pattern"}],"sort":[1632748525187,79],"type":"visualization","updated_at":"2021-09-27T13:15:25.187Z","version":"WzEyMjYsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"type\",\"negate\":false,\"params\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"wforce_allow\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"type\":{\"query\":\"wforce_allow\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"response.status\",\"negate\":false,\"params\":{\"query\":-1,\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"-1\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match\":{\"response.status\":{\"query\":-1,\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"exists\":{\"field\":\"response.r_attrs.blacklisted\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"response.r_attrs.blacklisted\",\"negate\":false,\"type\":\"exists\",\"value\":\"exists\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"IPs Blacklisted by Policy","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}","version":1,"visState":"{\"title\":\"IPs Blacklisted by Policy\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"},\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"request.remote\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Remote IP Address\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.r_attrs.reason\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Blacklist Reason\"}}]}"},"coreMigrationVersion":"7.14.1","id":"8e9e7660-7348-11e9-8970-ebaa4ad88862","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index","type":"index-pattern"}],"sort":[1632748538344,119],"type":"visualization","updated_at":"2021-09-27T13:15:38.344Z","version":"WzEyMzksOV0="} {"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":true,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":13,\"y\":30,\"w\":11,\"h\":15,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"8\"},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":{\"legendOpen\":true}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":13,\"h\":15,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"12\"},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"15\"},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.14.1\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"8cf314d5-c87c-4f97-8a7e-e06e4c66356b\"},\"panelIndex\":\"8cf314d5-c87c-4f97-8a7e-e06e4c66356b\",\"embeddableConfig\":{\"attributes\":{\"title\":\"IPs Rejected by Policy Map\",\"description\":\"\",\"layerListJSON\":\"[{\\\"sourceDescriptor\\\":{\\\"type\\\":\\\"EMS_TMS\\\",\\\"isAutoSelect\\\":true},\\\"id\\\":\\\"0903ff4e-a751-4330-93f7-5ffdf60c08a3\\\",\\\"label\\\":null,\\\"minZoom\\\":0,\\\"maxZoom\\\":24,\\\"alpha\\\":1,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"TILE\\\"},\\\"includeInFitToBounds\\\":true,\\\"type\\\":\\\"VECTOR_TILE\\\"},{\\\"alpha\\\":0.75,\\\"id\\\":\\\"107484f8-e1b8-446a-8b88-3dae0a8872b4\\\",\\\"includeInFitToBounds\\\":true,\\\"joins\\\":[],\\\"label\\\":\\\"IPs Rejected by Policy Map\\\",\\\"maxZoom\\\":24,\\\"minZoom\\\":0,\\\"sourceDescriptor\\\":{\\\"applyGlobalQuery\\\":true,\\\"applyGlobalTime\\\":true,\\\"geoField\\\":\\\"geoip.location\\\",\\\"id\\\":\\\"b8ca25bb-67e8-4d3f-aa8d-cb0c136d4c1f\\\",\\\"indexPatternId\\\":\\\"98888d10-7329-11e9-8970-ebaa4ad88862\\\",\\\"metrics\\\":[{\\\"type\\\":\\\"count\\\"}],\\\"requestType\\\":\\\"point\\\",\\\"resolution\\\":\\\"MOST_FINE\\\",\\\"type\\\":\\\"ES_GEO_GRID\\\"},\\\"style\\\":{\\\"isTimeAware\\\":true,\\\"properties\\\":{\\\"fillColor\\\":{\\\"options\\\":{\\\"color\\\":\\\"Yellow to Red\\\",\\\"colorCategory\\\":\\\"palette_0\\\",\\\"field\\\":{\\\"name\\\":\\\"doc_count\\\",\\\"origin\\\":\\\"source\\\"},\\\"fieldMetaOptions\\\":{\\\"isEnabled\\\":false,\\\"sigma\\\":3},\\\"type\\\":\\\"ORDINAL\\\"},\\\"type\\\":\\\"DYNAMIC\\\"},\\\"icon\\\":{\\\"options\\\":{\\\"value\\\":\\\"marker\\\"},\\\"type\\\":\\\"STATIC\\\"},\\\"iconOrientation\\\":{\\\"options\\\":{\\\"orientation\\\":0},\\\"type\\\":\\\"STATIC\\\"},\\\"iconSize\\\":{\\\"options\\\":{\\\"field\\\":{\\\"name\\\":\\\"doc_count\\\",\\\"origin\\\":\\\"source\\\"},\\\"fieldMetaOptions\\\":{\\\"isEnabled\\\":false,\\\"sigma\\\":3},\\\"maxSize\\\":18,\\\"minSize\\\":7},\\\"type\\\":\\\"DYNAMIC\\\"},\\\"labelBorderColor\\\":{\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"},\\\"type\\\":\\\"STATIC\\\"},\\\"labelBorderSize\\\":{\\\"options\\\":{\\\"size\\\":\\\"SMALL\\\"}},\\\"labelColor\\\":{\\\"options\\\":{\\\"color\\\":\\\"#000000\\\"},\\\"type\\\":\\\"STATIC\\\"},\\\"labelSize\\\":{\\\"options\\\":{\\\"size\\\":14},\\\"type\\\":\\\"STATIC\\\"},\\\"labelText\\\":{\\\"options\\\":{\\\"value\\\":\\\"\\\"},\\\"type\\\":\\\"STATIC\\\"},\\\"lineColor\\\":{\\\"options\\\":{\\\"color\\\":\\\"#3d3d3d\\\"},\\\"type\\\":\\\"STATIC\\\"},\\\"lineWidth\\\":{\\\"options\\\":{\\\"size\\\":1},\\\"type\\\":\\\"STATIC\\\"},\\\"symbolizeAs\\\":{\\\"options\\\":{\\\"value\\\":\\\"circle\\\"}}},\\\"type\\\":\\\"VECTOR\\\"},\\\"type\\\":\\\"VECTOR\\\",\\\"visible\\\":true}]\",\"mapStateJSON\":\"{\\\"zoom\\\":1.75,\\\"center\\\":{\\\"lon\\\":0,\\\"lat\\\":19.94277},\\\"timeFilters\\\":{\\\"from\\\":\\\"now/d\\\",\\\"to\\\":\\\"now/d\\\"},\\\"refreshConfig\\\":{\\\"isPaused\\\":true,\\\"interval\\\":0},\\\"query\\\":{\\\"query\\\":\\\"request.success: false\\\",\\\"language\\\":\\\"lucene\\\"},\\\"filters\\\":[{\\\"$state\\\":{\\\"store\\\":\\\"appState\\\"},\\\"meta\\\":{\\\"alias\\\":null,\\\"disabled\\\":false,\\\"key\\\":\\\"type\\\",\\\"negate\\\":false,\\\"params\\\":{\\\"query\\\":\\\"wforce_allow\\\"},\\\"type\\\":\\\"phrase\\\",\\\"index\\\":\\\"98888d10-7329-11e9-8970-ebaa4ad88862\\\"},\\\"query\\\":{\\\"match\\\":{\\\"type\\\":{\\\"query\\\":\\\"wforce_allow\\\",\\\"type\\\":\\\"phrase\\\"}}}},{\\\"$state\\\":{\\\"store\\\":\\\"appState\\\"},\\\"meta\\\":{\\\"alias\\\":null,\\\"disabled\\\":false,\\\"key\\\":\\\"response.status\\\",\\\"negate\\\":false,\\\"params\\\":{\\\"query\\\":-1},\\\"type\\\":\\\"phrase\\\",\\\"index\\\":\\\"98888d10-7329-11e9-8970-ebaa4ad88862\\\"},\\\"query\\\":{\\\"match\\\":{\\\"response.status\\\":{\\\"query\\\":-1,\\\"type\\\":\\\"phrase\\\"}}}}],\\\"settings\\\":{\\\"autoFitToDataBounds\\\":false,\\\"backgroundColor\\\":\\\"#ffffff\\\",\\\"disableInteractive\\\":false,\\\"disableTooltipControl\\\":false,\\\"hideToolbarOverlay\\\":false,\\\"hideLayerControl\\\":false,\\\"hideViewControl\\\":false,\\\"initialLocation\\\":\\\"LAST_SAVED_LOCATION\\\",\\\"fixedLocation\\\":{\\\"lat\\\":0,\\\"lon\\\":0,\\\"zoom\\\":2},\\\"browserLocation\\\":{\\\"zoom\\\":2},\\\"maxZoom\\\":24,\\\"minZoom\\\":0,\\\"showScaleControl\\\":false,\\\"showSpatialFilters\\\":true,\\\"showTimesliderToggleButton\\\":true,\\\"spatialFiltersAlpa\\\":0.3,\\\"spatialFiltersFillColor\\\":\\\"#DA8B45\\\",\\\"spatialFiltersLineColor\\\":\\\"#DA8B45\\\"}}\",\"uiStateJSON\":\"{\\\"isLayerTOCOpen\\\":true,\\\"openTOCDetails\\\":[]}\",\"references\":[]},\"mapCenter\":{\"lat\":41.38101,\"lon\":-33.7801,\"zoom\":1.62},\"mapBuffer\":{\"minLon\":-180,\"minLat\":0,\"maxLon\":90,\"maxLat\":66.51326},\"isLayerTOCOpen\":false,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_8cf314d5-c87c-4f97-8a7e-e06e4c66356b\"},{\"version\":\"7.14.1\",\"type\":\"map\",\"gridData\":{\"x\":24,\"y\":90,\"w\":24,\"h\":15,\"i\":\"7a2fda92-2b6f-4f80-8080-7ed3d3deceea\"},\"panelIndex\":\"7a2fda92-2b6f-4f80-8080-7ed3d3deceea\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":0.5},\"mapBuffer\":{\"minLon\":-360,\"minLat\":-85.05113,\"maxLon\":360,\"maxLat\":85.05113},\"isLayerTOCOpen\":false,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_7a2fda92-2b6f-4f80-8080-7ed3d3deceea\"}]","timeRestore":false,"title":"Abuse Shield Policy Dashboard","version":1},"coreMigrationVersion":"7.14.1","id":"89cd5660-7334-11e9-8970-ebaa4ad88862","migrationVersion":{"dashboard":"7.14.0"},"references":[{"id":"f8e2ec80-732b-11e9-8970-ebaa4ad88862","name":"1:panel_1","type":"visualization"},{"id":"d8f3c380-732c-11e9-8970-ebaa4ad88862","name":"2:panel_2","type":"visualization"},{"id":"92f3d020-732f-11e9-8970-ebaa4ad88862","name":"3:panel_3","type":"visualization"},{"id":"a914a370-732f-11e9-8970-ebaa4ad88862","name":"4:panel_4","type":"visualization"},{"id":"37e3d2a0-7331-11e9-8970-ebaa4ad88862","name":"5:panel_5","type":"visualization"},{"id":"e9e1c570-7331-11e9-8970-ebaa4ad88862","name":"6:panel_6","type":"visualization"},{"id":"12d181f0-7332-11e9-8970-ebaa4ad88862","name":"7:panel_7","type":"visualization"},{"id":"9d6e6e90-7332-11e9-8970-ebaa4ad88862","name":"8:panel_8","type":"visualization"},{"id":"9c9beb10-732c-11e9-8970-ebaa4ad88862","name":"9:panel_9","type":"visualization"},{"id":"171cb3a0-7347-11e9-8970-ebaa4ad88862","name":"10:panel_10","type":"visualization"},{"id":"80dec9a0-7346-11e9-8970-ebaa4ad88862","name":"11:panel_11","type":"visualization"},{"id":"c115ff20-7346-11e9-8970-ebaa4ad88862","name":"12:panel_12","type":"visualization"},{"id":"8e9e7660-7348-11e9-8970-ebaa4ad88862","name":"15:panel_15","type":"visualization"},{"id":"41464410-1fad-11ec-aabe-ef01f1c3095b","name":"8cf314d5-c87c-4f97-8a7e-e06e4c66356b:panel_8cf314d5-c87c-4f97-8a7e-e06e4c66356b","type":"map"},{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"8cf314d5-c87c-4f97-8a7e-e06e4c66356b:layer_1_source_index_pattern","type":"index-pattern"},{"id":"684d2f30-2064-11ec-aabe-ef01f1c3095b","name":"7a2fda92-2b6f-4f80-8080-7ed3d3deceea:panel_7a2fda92-2b6f-4f80-8080-7ed3d3deceea","type":"map"}],"sort":[1632910499788,328],"type":"dashboard","updated_at":"2021-09-29T10:14:59.788Z","version":"WzIzMTcsOV0="} {"attributes":{"description":"","layerListJSON":"[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"363d81ab-a0d4-403d-a599-7304d183365f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\"},\"includeInFitToBounds\":true,\"type\":\"VECTOR_TILE\"},{\"alpha\":0.75,\"id\":\"92ac6a6c-ffab-4be8-809e-69123876b472\",\"includeInFitToBounds\":true,\"joins\":[],\"label\":\"Unsuccessful Login Map\",\"maxZoom\":24,\"minZoom\":0,\"sourceDescriptor\":{\"applyGlobalQuery\":true,\"applyGlobalTime\":true,\"geoField\":\"geoip.location\",\"id\":\"e87b5a09-4805-4d66-8270-346e0cfca249\",\"metrics\":[{\"type\":\"count\"}],\"requestType\":\"point\",\"resolution\":\"MOST_FINE\",\"type\":\"ES_GEO_GRID\",\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"style\":{\"isTimeAware\":true,\"properties\":{\"fillColor\":{\"options\":{\"color\":\"Yellow to Red\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"type\":\"ORDINAL\"},\"type\":\"DYNAMIC\"},\"icon\":{\"options\":{\"value\":\"marker\"},\"type\":\"STATIC\"},\"iconOrientation\":{\"options\":{\"orientation\":0},\"type\":\"STATIC\"},\"iconSize\":{\"options\":{\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"maxSize\":18,\"minSize\":7},\"type\":\"DYNAMIC\"},\"labelBorderColor\":{\"options\":{\"color\":\"#FFFFFF\"},\"type\":\"STATIC\"},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}},\"labelColor\":{\"options\":{\"color\":\"#000000\"},\"type\":\"STATIC\"},\"labelSize\":{\"options\":{\"size\":14},\"type\":\"STATIC\"},\"labelText\":{\"options\":{\"value\":\"\"},\"type\":\"STATIC\"},\"lineColor\":{\"options\":{\"color\":\"#3d3d3d\"},\"type\":\"STATIC\"},\"lineWidth\":{\"options\":{\"size\":1},\"type\":\"STATIC\"},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}}},\"type\":\"VECTOR\"},\"type\":\"VECTOR\",\"visible\":true}]","mapStateJSON":"{\"zoom\":1.75,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now/d\",\"to\":\"now/d\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true,\"time_zone\":\"Europe/London\"}},\"language\":\"lucene\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"showTimesliderToggleButton\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Unsuccessful Login Map","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"7.14.1","id":"8fb0ef60-1fb2-11ec-aabe-ef01f1c3095b","migrationVersion":{"map":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"layer_1_source_index_pattern","type":"index-pattern"}],"sort":[1632761231702,180],"type":"map","updated_at":"2021-09-27T16:47:11.702Z","version":"WzE2NTAsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"success: true\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Distinct Count of Devices for Successful Logins","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Distinct Count of Devices for Successful Logins\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Devices\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"now/d\",\"to\":\"now/d\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"used_interval\":\"30m\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"device_id\",\"orderBy\":\"_key\",\"order\":\"desc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\"},\"schema\":\"group\"}],\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Devices\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Devices\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"radiusRatio\":0,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}"},"coreMigrationVersion":"7.14.1","id":"Distinct-Count-of-Devices-for-Successful-Logins","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632757942211,166],"type":"visualization","updated_at":"2021-09-27T15:52:22.211Z","version":"WzEzODQsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"success: false\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Distinct Count of Devices for Unsuccessful Logins","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Distinct Count of Devices for Unsuccessful Logins\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Devices\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Devices\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Devices\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"device_id\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":25,\"order\":\"desc\",\"orderBy\":\"_key\"}}]}"},"coreMigrationVersion":"7.14.1","id":"Distinct-Count-of-Devices-for-Unsuccessful-Logins","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748513057,56],"type":"visualization","updated_at":"2021-09-27T13:15:13.057Z","version":"WzEyMTQsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Distinct Count of IPs for Successful Logins","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Distinct Count of IPs for Successful Logins\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of IPs\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of IPs\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of IPs\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"remote\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_key\"}}]}"},"coreMigrationVersion":"7.14.1","id":"Distinct-Count-of-IPs-for-Successful-Logins","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748512045,54],"type":"visualization","updated_at":"2021-09-27T13:15:12.045Z","version":"WzEyMTMsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Distinct Count of IPs for Unsuccessful Logins","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Distinct Count of IPs for Unsuccessful Logins\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of IPs\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of IPs\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of IPs\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"remote\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_key\"}}]}"},"coreMigrationVersion":"7.14.1","id":"Distinct-Count-of-IPs-for-Unsuccessful-Logins","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748514106,58],"type":"visualization","updated_at":"2021-09-27T13:15:14.106Z","version":"WzEyMTUsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Distinct Count of Passwords","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Distinct Count of Passwords\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"top\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{\"min\":1,\"max\":4096}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"pwhash\",\"customLabel\":\"Distinct Count of Passwords\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Distinct-Count-of-Passwords","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748522158,74],"type":"visualization","updated_at":"2021-09-27T13:15:22.158Z","version":"WzEyMjMsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Distinct Count of Successful User Logins (Per-IP)","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Distinct Count of Successful User Logins (Per-IP)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Usernames\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Usernames\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Usernames\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"login\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_key\"}}]}"},"coreMigrationVersion":"7.14.1","id":"Distinct-Count-of-Successful-User-Logins-(Per-IP)","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748529265,88],"type":"visualization","updated_at":"2021-09-27T13:15:29.265Z","version":"WzEyMzAsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Distinct Count of Unsuccessful User Logins (Per-IP)","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Distinct Count of Unsuccessful User Logins (Per-IP)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Distinct Count of Usernames\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Distinct Count of Usernames\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"remote\",\"customLabel\":\"Distinct Count of Usernames\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"login\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"include\":\"\",\"size\":100,\"order\":\"desc\",\"orderBy\":\"_key\"}}]}"},"coreMigrationVersion":"7.14.1","id":"Distinct-Count-of-Unsuccessful-User-Logins-(Per-IP)","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748530234,90],"type":"visualization","updated_at":"2021-09-27T13:15:30.234Z","version":"WzEyMzEsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"GeoIP City Name","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"GeoIP City Name\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":12,\"maxFontSize\":24,\"hideLabel\":false,\"showLabel\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.city_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"City Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"GeoIP-City-Name","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748537293,114],"type":"visualization","updated_at":"2021-09-27T13:15:37.293Z","version":"WzEyMzgsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"GeoIP Country Name","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"GeoIP Country Name\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":12,\"maxFontSize\":24,\"showLabel\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.country_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Country Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"GeoIP-Country-Name","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748543313,138],"type":"visualization","updated_at":"2021-09-27T13:15:43.313Z","version":"WzEyNDQsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"GeoIP Region Name","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"GeoIP Region Name\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":12,\"maxFontSize\":24,\"showLabel\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.region_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"_key\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Region Name\"}}]}"},"coreMigrationVersion":"7.14.1","id":"GeoIP-Region-Name","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748536288,112],"type":"visualization","updated_at":"2021-09-27T13:15:36.288Z","version":"WzEyMzcsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Login Country over Time (Successful Logins)","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Login Country over Time (Successful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Login Country (GeoIP)\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geoip.country_code2\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Country (GeoIP)\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Login-Country-over-Time-(Successful-Logins)","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748521132,72],"type":"visualization","updated_at":"2021-09-27T13:15:21.132Z","version":"WzEyMjIsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Login Country over Time (Unsuccessful Logins)","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Login Country over Time (Unsuccessful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Login Country (GeoIP)\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geoip.country_code2\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Country (GeoIP)\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Login-Country-over-Time-(Unsuccessful-Logins)","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748528214,86],"type":"visualization","updated_at":"2021-09-27T13:15:28.214Z","version":"WzEyMjksOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}"},"title":"Per-IP Dashboard Instructions","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Per-IP Dashboard Instructions\",\"type\":\"markdown\",\"params\":{\"markdown\":\"Type an IP address into the search box above to view stats for that IP, for example:\\n* 212.33.12.81\\n* 2001:0db8:85a3:0000:0000:8a2e:0370:7334\\n* remote: 212.33.12.81\\n\\nIf you do not type in an IP address, you will see system-wide stats, which are not very useful.\"},\"aggs\":[],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Per-IP-Dashboard-Instructions","migrationVersion":{"visualization":"7.14.0"},"references":[],"sort":[1632748510060,50],"type":"visualization","updated_at":"2021-09-27T13:15:10.060Z","version":"WzEyMTEsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"default_field\":\"*\",\"query\":\"*\"}}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Successful/Unsuccessful Logins","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Successful/Unsuccessful Logins\",\"type\":\"area\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"interpolate\":\"linear\",\"legendPosition\":\"top\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"smoothLines\":false,\"times\":[],\"yAxis\":{},\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"setYExtents\":false,\"defaultYExtents\":false},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"success: true\",\"language\":\"lucene\"}},{\"input\":{\"query\":\"success: false\",\"language\":\"lucene\"}}]}}]}"},"coreMigrationVersion":"7.14.1","id":"Successful-slash-Unsuccessful-Logins","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748511201,52],"type":"visualization","updated_at":"2021-09-27T13:15:11.201Z","version":"WzEyMTIsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"constant_score\":{\"filter\":{\"term\":{\"success\":false}}}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Suspicious Device Types","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Suspicious Device Types\",\"type\":\"table\",\"params\":{\"perPage\":6,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false,\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Significant Terms\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"device_id\",\"size\":10,\"customLabel\":\"Suspicious Device Types\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Suspicious-Device-Types","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748519112,68],"type":"visualization","updated_at":"2021-09-27T13:15:19.112Z","version":"WzEyMjAsOV0="} {"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":true,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":25,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":10,\"i\":\"13\"},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"14\"},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"15\"},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":32,\"y\":10,\"w\":16,\"h\":5,\"i\":\"16\"},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":10,\"w\":16,\"h\":5,\"i\":\"17\"},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":16,\"y\":10,\"w\":16,\"h\":5,\"i\":\"18\"},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"}]","timeRestore":false,"title":"Per-IP Login and Abuse Statistics","version":1},"coreMigrationVersion":"7.14.1","id":"Per-IP-Login-and-Abuse-Statistics","migrationVersion":{"dashboard":"7.14.0"},"references":[{"id":"Successful-slash-Unsuccessful-Logins","name":"1:panel_1","type":"visualization"},{"id":"Distinct-Count-of-Passwords","name":"2:panel_2","type":"visualization"},{"id":"Distinct-Count-of-Devices-for-Successful-Logins","name":"5:panel_5","type":"visualization"},{"id":"Distinct-Count-of-Devices-for-Unsuccessful-Logins","name":"6:panel_6","type":"visualization"},{"id":"Suspicious-Device-Types","name":"11:panel_11","type":"visualization"},{"id":"Per-IP-Dashboard-Instructions","name":"13:panel_13","type":"visualization"},{"id":"Distinct-Count-of-Successful-User-Logins-(Per-IP)","name":"14:panel_14","type":"visualization"},{"id":"Distinct-Count-of-Unsuccessful-User-Logins-(Per-IP)","name":"15:panel_15","type":"visualization"},{"id":"GeoIP-City-Name","name":"16:panel_16","type":"visualization"},{"id":"GeoIP-Country-Name","name":"17:panel_17","type":"visualization"},{"id":"GeoIP-Region-Name","name":"18:panel_18","type":"visualization"}],"sort":[1632748507032,48],"type":"dashboard","updated_at":"2021-09-27T13:15:07.032Z","version":"WzEyMDgsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}"},"title":"Per-User Dashboard Instructions","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Per-User Dashboard Instructions\",\"type\":\"markdown\",\"params\":{\"markdown\":\"Type a username into the search box above to view stats for that user, for example:\\n* frank.black@domain,com\\n* frank.black\\n* login: frank.black@domain.com\\n\\nIf you do not type in a username, you will see system-wide stats, which are not very useful.\"},\"aggs\":[],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Per-User-Dashboard-Instructions","migrationVersion":{"visualization":"7.14.0"},"references":[],"sort":[1632748509097,49],"type":"visualization","updated_at":"2021-09-27T13:15:09.097Z","version":"WzEyMTAsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"constant_score\":{\"filter\":{\"term\":{\"success\":false}}}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Suspicious IPs","uiStateJSON":"{\n \"vis\": {\n \"params\": {\n \"sort\": {\n \"columnIndex\": null,\n \"direction\": null\n }\n }\n }\n}","version":1,"visState":"{\"title\":\"Suspicious IPs\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false,\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Count of Significant Terms\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"remote\",\"size\":100,\"customLabel\":\"Suspicious IPs\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Suspicious-IPs","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748515076,60],"type":"visualization","updated_at":"2021-09-27T13:15:15.076Z","version":"WzEyMTYsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Unsuccessful Login Map","uiStateJSON":"{\"mapCenter\":[14.944784875088372,5.09765625]}","version":1,"visState":"{\"title\":\"Unsuccessful Login Map\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[15,5],\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geoip.location\",\"autoPrecision\":true,\"customLabel\":\"Unsuccessful Logins\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Unsuccessful-Login-Map","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748517135,64],"type":"visualization","updated_at":"2021-09-27T13:15:17.135Z","version":"WzEyMTgsOV0="} {"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true,\"default_field\":\"*\"}},\"language\":\"lucene\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":true,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":10,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":10,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":25,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":25,\"w\":24,\"h\":15,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":40,\"w\":24,\"h\":15,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":40,\"w\":24,\"h\":15,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":55,\"w\":24,\"h\":15,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":55,\"w\":24,\"h\":15,\"i\":\"8\"},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":10,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":70,\"w\":24,\"h\":25,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":70,\"w\":24,\"h\":25,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":95,\"w\":48,\"h\":30,\"i\":\"12\"},\"panelIndex\":\"12\",\"embeddableConfig\":{\"mapCenter\":[14.944784875088372,5.09765625],\"enhancements\":{}},\"panelRefName\":\"panel_12\"}]","timeRestore":false,"title":"Per-User Login and Abuse Statistics","version":1},"coreMigrationVersion":"7.14.1","id":"Per-User-Login-and-Abuse-Statistics","migrationVersion":{"dashboard":"7.14.0"},"references":[{"id":"Successful-slash-Unsuccessful-Logins","name":"1:panel_1","type":"visualization"},{"id":"Distinct-Count-of-Passwords","name":"2:panel_2","type":"visualization"},{"id":"Distinct-Count-of-IPs-for-Successful-Logins","name":"3:panel_3","type":"visualization"},{"id":"Distinct-Count-of-IPs-for-Unsuccessful-Logins","name":"4:panel_4","type":"visualization"},{"id":"Distinct-Count-of-Devices-for-Successful-Logins","name":"5:panel_5","type":"visualization"},{"id":"Distinct-Count-of-Devices-for-Unsuccessful-Logins","name":"6:panel_6","type":"visualization"},{"id":"Login-Country-over-Time-(Successful-Logins)","name":"7:panel_7","type":"visualization"},{"id":"Login-Country-over-Time-(Unsuccessful-Logins)","name":"8:panel_8","type":"visualization"},{"id":"Per-User-Dashboard-Instructions","name":"9:panel_9","type":"visualization"},{"id":"Suspicious-IPs","name":"10:panel_10","type":"visualization"},{"id":"Suspicious-Device-Types","name":"11:panel_11","type":"visualization"},{"id":"Unsuccessful-Login-Map","name":"12:panel_12","type":"visualization"}],"sort":[1632748506085,36],"type":"dashboard","updated_at":"2021-09-27T13:15:06.085Z","version":"WzEyMDcsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Protocol Used over Time","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Protocol Used over Time\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"bottom\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Total Logins\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"protocol\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Protocol\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Protocol-Used-over-Time","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748520166,70],"type":"visualization","updated_at":"2021-09-27T13:15:20.166Z","version":"WzEyMjEsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: true\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Protocol Used over Time (Successful Logins)","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Protocol Used over Time (Successful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"bottom\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Total Logins\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"protocol\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Protocol\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Protocol-Used-over-Time-(Successful-Logins)","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748516085,62],"type":"visualization","updated_at":"2021-09-27T13:15:16.085Z","version":"WzEyMTcsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"query\":\"success: false\",\"analyze_wildcard\":true}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Protocol Used over Time (Unsuccessful Logins)","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Protocol Used over Time (Unsuccessful Logins)\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"bottom\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Total Logins\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"protocol\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Login Protocol\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Protocol-Used-over-Time-(Unsuccessful-Logins)","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748531242,92],"type":"visualization","updated_at":"2021-09-27T13:15:31.242Z","version":"WzEyMzIsOV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"constant_score\":{\"filter\":{\"term\":{\"success\":false}}}},\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Suspicious/Compromised Users","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Suspicious/Compromised Users\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showMetricsAtAllLevels\":false,\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"Significant Terms\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"login\",\"size\":100,\"customLabel\":\"Suspicious/Compromised Users\"}}],\"listeners\":{}}"},"coreMigrationVersion":"7.14.1","id":"Suspicious-slash-Compromised-Users","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"98888d10-7329-11e9-8970-ebaa4ad88862","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1632748518102,66],"type":"visualization","updated_at":"2021-09-27T13:15:18.102Z","version":"WzEyMTksOV0="} {"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\",\"default_field\":\"*\",\"time_zone\":\"Europe/London\"}},\"language\":\"lucene\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":true,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":25,\"w\":24,\"h\":25,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":25,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":20,\"h\":25,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.14.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":25,\"w\":20,\"h\":25,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.14.1\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":50,\"w\":44,\"h\":29,\"i\":\"996a8e81-533c-4111-8fa1-2f021e57f5e9\"},\"panelIndex\":\"996a8e81-533c-4111-8fa1-2f021e57f5e9\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.75},\"mapBuffer\":{\"minLon\":-90,\"minLat\":-66.51326,\"maxLon\":90,\"maxLat\":66.51326},\"isLayerTOCOpen\":false,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_996a8e81-533c-4111-8fa1-2f021e57f5e9\"}]","timeRestore":false,"title":"System-Wide Login Abuse","version":1},"coreMigrationVersion":"7.14.1","id":"System-Wide-Login-Abuse","migrationVersion":{"dashboard":"7.14.0"},"references":[{"id":"Suspicious-Device-Types","name":"3:panel_3","type":"visualization"},{"id":"Suspicious-IPs","name":"4:panel_4","type":"visualization"},{"id":"Suspicious-slash-Compromised-Users","name":"5:panel_5","type":"visualization"},{"id":"Successful-slash-Unsuccessful-Logins","name":"6:panel_6","type":"visualization"},{"id":"8fb0ef60-1fb2-11ec-aabe-ef01f1c3095b","name":"996a8e81-533c-4111-8fa1-2f021e57f5e9:panel_996a8e81-533c-4111-8fa1-2f021e57f5e9","type":"map"}],"sort":[1632761261897,187],"type":"dashboard","updated_at":"2021-09-27T16:47:41.897Z","version":"WzE2NjMsOV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":43,"missingRefCount":0,"missingReferences":[]}weakforced-2.10.2/elk/logstash/000077500000000000000000000000001461473602600163165ustar00rootroot00000000000000weakforced-2.10.2/elk/logstash/config/000077500000000000000000000000001461473602600175635ustar00rootroot00000000000000weakforced-2.10.2/elk/logstash/config/logstash.conf000077700000000000000000000000001461473602600321452../../../docker/logstash/config/logstash.confustar00rootroot00000000000000weakforced-2.10.2/elk/logstash/templates/000077500000000000000000000000001461473602600203145ustar00rootroot00000000000000weakforced-2.10.2/elk/logstash/templates/wforce_template.json000077700000000000000000000000001461473602600363672../../../docker/logstash/templates/wforce_template.jsonustar00rootroot00000000000000weakforced-2.10.2/elk/sample_elasticqueries/000077500000000000000000000000001461473602600210555ustar00rootroot00000000000000weakforced-2.10.2/elk/sample_elasticqueries/all_query.json000066400000000000000000000005561461473602600237530ustar00rootroot00000000000000{ "size": 0, "aggs": { "browser": { "terms": { "field": "device_attrs.browser.family.keyword" }, "aggs": { "os": { "terms": { "field": "device_attrs.os.family.keyword" }, "aggs": { "device": { "terms": { "field": "device_attrs.device.family.keyword" } } } } } } } } weakforced-2.10.2/elk/sample_elasticqueries/login_fail_query.json000066400000000000000000000027131461473602600253030ustar00rootroot00000000000000{ "size" : 0, "query": { "constant_score": { "filter": { "term": { "success": false } } } }, "aggs": { "most_sig_ips": { "significant_terms": { "field": "remote", "size": 6 } }, "most_sig_logins": { "significant_terms": { "field": "login", "size": 6 } }, "most_sig_cc": { "significant_terms": { "field": "geoip.country_code2", "size": 6 } }, "most_sig_browser": { "significant_terms": { "field": "device_attrs.browser.family", "size": 6 }, "aggs": { "os": { "terms": { "field": "device_attrs.os.family" }, "aggs": { "device": { "terms": { "field": "device_attrs.device.family" } } } } } }, "most_sig_imapc": { "significant_terms": { "field": "device_attrs.imapc.family", "size": 6 }, "aggs": { "os": { "terms": { "field": "device_attrs.os.family" } } } }, "most_sig_mobileapp": { "significant_terms": { "field": "device_attrs.app.name", "size": 6 }, "aggs": { "brand": { "terms": { "field": "device_attrs.app.brand" }, "aggs": { "os": { "terms": { "field": "device_attrs.os.family" }, "aggs": { "device": { "terms": { "field": "device_attrs.device.family" } } } } } } } } } } weakforced-2.10.2/elk/sample_elasticqueries/login_success_query.json000066400000000000000000000032311461473602600260340ustar00rootroot00000000000000{ "size": 0, "query": { "constant_score": { "filter": { "bool" : { "must": [ { "term": { "login" : "user900" } }, { "term": { "success" : true } }, { "range": { "@timestamp": { "gt" : "now-6M" } } } ] } } } }, "aggs": { "browser": { "terms": { "field": "device_attrs.browser.family" }, "aggs": { "os": { "terms": { "field": "device_attrs.os.family" }, "aggs": { "device": { "terms": { "field": "device_attrs.device.family" } } } } } }, "imapc": { "terms": { "field": "device_attrs.imapc.family" }, "aggs": { "os": { "terms": { "field": "device_attrs.os.family" } } } }, "mobileapp": { "terms": { "field": "device_attrs.app.name" }, "aggs": { "brand": { "terms": { "field": "device_attrs.app.brand" }, "aggs": { "os": { "terms": { "field": "device_attrs.os.family" }, "aggs": { "device": { "terms": { "field": "device_attrs.device.family" } } } } } } } }, "location": { "terms": { "field": "geoip.country_code2" } }, "protocol": { "terms": { "field": "protocol" }, "aggs": { "tls": { "terms": { "field": "tls" } } } }, "password": { "cardinality": { "field": "pwhash" } }, "range": { "date_range": { "field": "@timestamp", "ranges": [ { "to": "now-1w" } ] } } } } weakforced-2.10.2/elk/sample_elasticqueries/query.json000066400000000000000000000004131461473602600231130ustar00rootroot00000000000000{ "query": { "constant_score": { "filter": { "bool" : { "must": [ { "term": { "type": "wforce_report" } }, { "range": { "@timestamp": { "gt" : "now-30m" } } } ] } } } } } weakforced-2.10.2/ext/000077500000000000000000000000001461473602600145175ustar00rootroot00000000000000weakforced-2.10.2/ext/Makefile.am000066400000000000000000000000251461473602600165500ustar00rootroot00000000000000SUBDIRS = json11 ext weakforced-2.10.2/ext/ext/000077500000000000000000000000001461473602600153175ustar00rootroot00000000000000weakforced-2.10.2/ext/ext/Makefile.am000066400000000000000000000010371461473602600173540ustar00rootroot00000000000000noinst_LTLIBRARIES=libext.la noinst_HEADERS=count_min_sketch.hpp murmur3.h ctpl.h hyperloglog.hpp threadname.hh uap-cpp/UaParser.h scheduler/Scheduler.h scheduler/Cron.h scheduler/InterruptableSleep.h incbin/incbin.h luawrapper/include/LuaContext.hpp libext_la_SOURCES=count_min_sketch.cpp count_min_sketch.hpp murmur3.cc murmur3.h threadname.cc threadname.hh uap-cpp/UaParser.cpp uap-cpp/UaParser.h ctpl.h hyperloglog.hpp scheduler/Scheduler.h scheduler/Cron.h scheduler/InterruptableSleep.h incbin/incbin.h luawrapper/include/LuaContext.hpp weakforced-2.10.2/ext/ext/count_min_sketch.cpp000066400000000000000000000120241461473602600213560ustar00rootroot00000000000000/** Author: Daniel Alabi http://alabidan.me GitHub: alabid/countminsketch Count-Min Sketch Implementation based on paper by Muthukrishnan and Cormode, 2004 **/ # include # include # include # include # include # include # include # include "count_min_sketch.hpp" using namespace std; /** Class definition for CountMinSketch. public operations: // overloaded updates void update(int item, int c); void update(char *item, int c); // overloaded estimates unsigned int estimate(int item); unsigned int estimate(char *item); **/ // CountMinSketch constructor // ep -> error 0.01 < ep < 1 (the smaller the better) // gamma -> probability for error (the smaller the better) 0 < gamm < 1 CountMinSketch::CountMinSketch(float ep, float gamm) { if (!(0.009 <= ep && ep < 1)) { cout << "eps must be in this range: [0.01, 1)" << endl; exit(EXIT_FAILURE); } else if (!(0 < gamm && gamm < 1)) { cout << "gamma must be in this range: (0,1)" << endl; exit(EXIT_FAILURE); } eps = ep; gamma = gamm; w = ceil(exp(1)/eps); d = ceil(log(1/gamma)); total = 0; // initialize counter array of arrays, C C = new int *[d]; unsigned int i, j; for (i = 0; i < d; i++) { C[i] = new int[w]; for (j = 0; j < w; j++) { C[i][j] = 0; } } // initialize d pairwise independent hashes srand(time(NULL)); hashes = new int* [d]; for (i = 0; i < d; i++) { hashes[i] = new int[2]; genajbj(hashes, i); } } // CountMinSkectch destructor CountMinSketch::~CountMinSketch() { // free array of counters, C unsigned int i; for (i = 0; i < d; i++) { delete[] C[i]; } delete[] C; // free array of hash values for (i = 0; i < d; i++) { delete[] hashes[i]; } delete[] hashes; } // CountMinSketch totalcount returns the // total count of all items in the sketch unsigned int CountMinSketch::totalcount() { return total; } // countMinSketch update item count (int) void CountMinSketch::update(int item, int c) { total = total + c; unsigned int hashval = 0; for (unsigned int j = 0; j < d; j++) { hashval = (hashes[j][0]*item+hashes[j][1])%w; C[j][hashval] = C[j][hashval] + c; } } // countMinSketch update item count (string) void CountMinSketch::update(const char *str, int c) { int hashval = hashstr(str); update(hashval, c); } // CountMinSketch estimate item count (int) unsigned int CountMinSketch::estimate(int item) { int minval = numeric_limits::max(); unsigned int hashval = 0; for (unsigned int j = 0; j < d; j++) { hashval = (hashes[j][0]*item+hashes[j][1])%w; minval = MIN(minval, C[j][hashval]); } return minval; } // CountMinSketch estimate item count (string) unsigned int CountMinSketch::estimate(const char *str) { int hashval = hashstr(str); return estimate(hashval); } // erase counts void CountMinSketch::erase() { unsigned int i, j; for (i = 0; i < d; i++) { for (j = 0; j < w; j++) { C[i][j] = 0; } } total = 0; // zero the running total } // generates aj,bj from field Z_p for use in hashing void CountMinSketch::genajbj(int** hashes, int i) { hashes[i][0] = int(float(rand())*float(LONG_PRIME)/float(RAND_MAX) + 1); hashes[i][1] = int(float(rand())*float(LONG_PRIME)/float(RAND_MAX) + 1); } // generates a hash value for a sting // same as djb2 hash function unsigned int CountMinSketch::hashstr(const char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash; } void CountMinSketch::swap(CountMinSketch& rhs) { std::swap(eps, rhs.eps); std::swap(this->gamma, rhs.gamma); std::swap(total, rhs.total); std::swap(w, rhs.w); std::swap(d, rhs.d); std::swap(C, rhs.C); std::swap(hashes, rhs.hashes); } // XXX - this function does not currently convert everything to network byte order void CountMinSketch::dump(std::ostream& os) const { unsigned int i=0; os.write((char*)&eps, sizeof(eps)); os.write((char*)&gamma, sizeof(gamma)); os.write((char*)&total, sizeof(total)); for (i = 0; i < d; i++) { os.write((char*)&(C[i][0]), sizeof(C[i][0])*w); } for (i = 0; i < d; i++) { os.write((char*)&(hashes[i][0]), sizeof(hashes[i][0])*2); } if(os.fail()){ throw std::runtime_error("CountMinSketch: Failed to dump"); } } // XXX - this function does not currently convert everything from network byte order void CountMinSketch::restore(std::istream& is) { float myeps=0; float mygamma=0; unsigned int i=0; is.read((char*)&myeps, sizeof(myeps)); is.read((char*)&mygamma, sizeof(mygamma)); CountMinSketch tempCMS(myeps, mygamma); is.read((char*)&tempCMS.total, sizeof(tempCMS.total)); for (i = 0; i < tempCMS.d; i++) { is.read((char*)&(tempCMS.C[i][0]), sizeof(tempCMS.C[i][0])*tempCMS.w); } for (i = 0; i < tempCMS.d; i++) { is.read((char*)&(tempCMS.hashes[i][0]), sizeof(tempCMS.hashes[i][0])*2); } if(is.fail()){ throw std::runtime_error("CountMinSketch: Failed to restore"); } swap(tempCMS); } weakforced-2.10.2/ext/ext/count_min_sketch.hpp000066400000000000000000000033101461473602600213610ustar00rootroot00000000000000/** Author: Daniel Alabi http://alabidan.me GitHub: alabid/countminsketch Count-Min Sketch Implementation based on paper by Muthukrishnan and Cormode, 2004 **/ #pragma once // define some constants # define LONG_PRIME 32993 #ifndef MIN # define MIN(a,b) (a < b ? a : b) #endif /** CountMinSketch class definition here **/ class CountMinSketch { protected: // width, depth unsigned int w,d; // eps (for error), 0.01 < eps < 1 // the smaller the better float eps; // gamma (probability for accuracy), 0 < gamma < 1 // the bigger the better float gamma; // total count so far unsigned int total; // array of arrays of counters int **C; // array of hash values for a particular item // contains two element arrays {aj,bj} int **hashes; // generate "new" aj,bj void genajbj(int **hashes, int i); public: // constructor CountMinSketch(float eps, float gamma); // update item (int) by count c void update(int item, int c); // update item (string) by count c void update(const char *item, int c); // estimate count of item i and return count unsigned int estimate(int item); unsigned int estimate(const char *item); // return total count unsigned int totalcount(); // generates a hash value for a string // same as djb2 hash function unsigned int hashstr(const char *str); // erase counts void erase(); // Exchange the contents of an instance void swap(CountMinSketch& rhs); // Dump to a stream void dump(std::ostream& os) const; // Restore from a stream void restore(std::istream& is); // destructor ~CountMinSketch(); }; weakforced-2.10.2/ext/ext/ctpl.h000066400000000000000000000223551461473602600164410ustar00rootroot00000000000000 /********************************************************* * * Copyright (C) 2014 by Vitaliy Vitsentiy * * GitHub: https://github.com/vit-vit/ctpl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************/ #ifndef __ctpl_thread_pool_H__ #define __ctpl_thread_pool_H__ #include #include #include #include #include #include #include #include #include #ifndef _ctplThreadPoolLength_ #define _ctplThreadPoolLength_ 100 #endif // thread pool to run user's functors with signature // ret func(int id, other_params) // where id is the index of the thread that runs the functor // ret is some return type namespace ctpl { class thread_pool { public: thread_pool() : q(_ctplThreadPoolLength_) { this->init(); } thread_pool(int nThreads, int queueSize = _ctplThreadPoolLength_) : q(queueSize) { this->init(); this->resize(nThreads); } // the destructor waits for all the functions in the queue to be finished ~thread_pool() { this->stop(true); } // get the number of running threads in the pool int size() { return static_cast(this->threads.size()); } // number of idle threads int n_idle() { return this->nWaiting; } std::thread & get_thread(int i) { return *this->threads[i]; } // change the number of threads in the pool // should be called from one thread, otherwise be careful to not interleave, also with this->stop() // nThreads must be >= 0 void resize(int nThreads) { if (!this->isStop && !this->isDone) { int oldNThreads = static_cast(this->threads.size()); if (oldNThreads <= nThreads) { // if the number of threads is increased this->threads.resize(nThreads); this->flags.resize(nThreads); for (int i = oldNThreads; i < nThreads; ++i) { this->flags[i] = std::make_shared>(false); this->set_thread(i); } } else { // the number of threads is decreased for (int i = oldNThreads - 1; i >= nThreads; --i) { *this->flags[i] = true; // this thread will finish this->threads[i]->detach(); } { // stop the detached threads that were waiting std::unique_lock lock(this->mutex); this->cv.notify_all(); } this->threads.resize(nThreads); // safe to delete because the threads are detached this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals } } } // empty the queue void clear_queue() { std::function * _f; while (this->q.pop(_f)) delete _f; // empty the queue } // pops a functional wrapper to the original function std::function pop() { std::function * _f = nullptr; this->q.pop(_f); std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred std::function f; if (_f) f = *_f; return f; } // wait for all computing threads to finish and stop all threads // may be called asynchronously to not pause the calling thread while waiting // if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions void stop(bool isWait = false) { if (!isWait) { if (this->isStop) return; this->isStop = true; for (int i = 0, n = this->size(); i < n; ++i) { *this->flags[i] = true; // command the threads to stop } this->clear_queue(); // empty the queue } else { if (this->isDone || this->isStop) return; this->isDone = true; // give the waiting threads a command to finish } { std::unique_lock lock(this->mutex); this->cv.notify_all(); // stop all waiting threads } for (int i = 0; i < static_cast(this->threads.size()); ++i) { // wait for the computing threads to finish if (this->threads[i]->joinable()) this->threads[i]->join(); } // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads // therefore delete them here this->clear_queue(); this->threads.clear(); this->flags.clear(); } template auto push(F && f, Rest&&... rest) ->std::future { auto pck = std::make_shared>( std::bind(std::forward(f), std::placeholders::_1, std::forward(rest)...) ); auto _f = new std::function([pck](int id) { (*pck)(id); }); this->q.push(_f); std::unique_lock lock(this->mutex); this->cv.notify_one(); return pck->get_future(); } // run the user's function that excepts argument int - id of the running thread. returned value is templatized // operator returns std::future, where the user can get the result and rethrow the caught exceptions template auto push(F && f) ->std::future { auto pck = std::make_shared>(std::forward(f)); auto _f = new std::function([pck](int id) { (*pck)(id); }); this->q.push(_f); std::unique_lock lock(this->mutex); this->cv.notify_one(); return pck->get_future(); } private: // deleted thread_pool(const thread_pool &);// = delete; thread_pool(thread_pool &&);// = delete; thread_pool & operator=(const thread_pool &);// = delete; thread_pool & operator=(thread_pool &&);// = delete; void set_thread(int i) { std::shared_ptr> flag(this->flags[i]); // a copy of the shared ptr to the flag auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() { std::atomic & _flag = *flag; std::function * _f; bool isPop = this->q.pop(_f); while (true) { while (isPop) { // if there is anything in the queue std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred (*_f)(i); if (_flag) return; // the thread is wanted to stop, return even if the queue is not empty yet else isPop = this->q.pop(_f); } // the queue is empty here, wait for the next command std::unique_lock lock(this->mutex); ++this->nWaiting; this->cv.wait(lock, [this, &_f, &isPop, &_flag](){ isPop = this->q.pop(_f); return isPop || this->isDone || _flag; }); --this->nWaiting; if (!isPop) return; // if the queue is empty and this->isDone == true or *flag then return } }; this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique() } void init() { this->nWaiting = 0; this->isStop = false; this->isDone = false; } std::vector> threads; std::vector>> flags; mutable boost::lockfree::queue *> q; std::atomic isDone; std::atomic isStop; std::atomic nWaiting; // how many threads are waiting std::mutex mutex; std::condition_variable cv; }; } #endif // __ctpl_thread_pool_H__ weakforced-2.10.2/ext/ext/hyperloglog.hpp000077500000000000000000000245551461473602600204010ustar00rootroot00000000000000#pragma once #if !defined(HYPERLOGLOG_HPP) #define HYPERLOGLOG_HPP /** * @file hyperloglog.hpp * @brief HyperLogLog cardinality estimator * @date Created 2013/3/20 * @author Hideaki Ohno * * (The MIT License) Copyright (c) 2013 Hideaki Ohno Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #include #include #include #include #include #include "murmur3.h" #define HLL_HASH_SEED 313 #if defined(__has_builtin) && (defined(__GNUC__) || defined(__clang__)) #define _GET_CLZ(x, b) (uint8_t)std::min(b, ::__builtin_clz(x)) + 1 #else inline uint8_t _get_leading_zero_count(uint32_t x, uint8_t b) { #if defined (_MSC_VER) uint32_t leading_zero_len = 32; ::_BitScanReverse(&leading_zero_len, x); --leading_zero_len; return std::min(b, (uint8_t)leading_zero_len); #else uint8_t v = 1; while (v <= b && !(x & 0x80000000)) { v++; x <<= 1; } return v; #endif } #define _GET_CLZ(x, b) _get_leading_zero_count(x, b) #endif /* defined(__GNUC__) */ namespace hll { static const double pow_2_32 = 4294967296.0; ///< 2^32 static const double neg_pow_2_32 = -4294967296.0; ///< -(2^32) /** @class HyperLogLog * @brief Implement of 'HyperLogLog' estimate cardinality algorithm */ class HyperLogLog { public: /** * Constructor * * @param[in] b bit width (register size will be 2 to the b power). * This value must be in the range[4,30].Default value is 4. * * @exception std::invalid_argument the argument is out of range. */ HyperLogLog(uint8_t b = 4) : b_(b), m_(1 << b), M_(m_, 0) { if (b < 4 || 30 < b) { throw std::invalid_argument("bit width must be in the range [4,30]"); } double alpha; switch (m_) { case 16: alpha = 0.673; break; case 32: alpha = 0.697; break; case 64: alpha = 0.709; break; default: alpha = 0.7213 / (1.0 + 1.079 / m_); break; } alphaMM_ = alpha * m_ * m_; } /** * Adds element to the estimator * * @param[in] str string to add * @param[in] len length of string */ void add(const char* str, uint32_t len) { uint32_t hash; MurmurHash3_x86_32(str, len, HLL_HASH_SEED, (void*) &hash); uint32_t index = hash & ((1 << b_) - 1); uint8_t rank = _GET_CLZ((hash << b_), 32 - b_); if (rank > M_[index]) { M_[index] = rank; } } /** * Estimates cardinality value. * * @return Estimated cardinality value. */ double estimate() const { double estimate; double sum = 0.0; for (uint32_t i = 0; i < m_; i++) { sum += 1.0 / (1 << M_[i]); } estimate = alphaMM_ / sum; // E in the original paper if (estimate <= 2.5 * m_) { uint32_t zeros = 0; for (uint32_t i = 0; i < m_; i++) { if (M_[i] == 0) { zeros++; } } if (zeros != 0) { estimate = m_ * std::log(static_cast(m_)/ zeros); } } else if (estimate > (1.0 / 30.0) * pow_2_32) { estimate = neg_pow_2_32 * log(1.0 - (estimate / pow_2_32)); } return estimate; } /** * Merges the estimate from 'other' into this object, returning the estimate of their union. * The number of registers in each must be the same. * * @param[in] other HyperLogLog instance to be merged * * @exception std::invalid_argument number of registers doesn't match. */ void merge(const HyperLogLog& other) { if (m_ != other.m_) { std::stringstream ss; ss << "number of registers doesn't match: " << m_ << " != " << other.m_; throw std::invalid_argument(ss.str().c_str()); } for (uint32_t r = 0; r < m_; ++r) { if (M_[r] < other.M_[r]) { M_[r] |= other.M_[r]; } } } /** * Clears all internal registers. */ void clear() { std::fill(M_.begin(), M_.end(), 0); } /** * Returns size of register. * * @return Register size */ uint32_t registerSize() const { return m_; } /** * Exchanges the content of the instance * * @param[in,out] rhs Another HyperLogLog instance */ void swap(HyperLogLog& rhs) { std::swap(b_, rhs.b_); std::swap(m_, rhs.m_); std::swap(alphaMM_, rhs.alphaMM_); M_.swap(rhs.M_); } /** * Dump the current status to a stream * * @param[out] os The output stream where the data is saved * * @exception std::runtime_error When failed to dump. */ void dump(std::ostream& os) const { os.write((char*)&b_, sizeof(b_)); os.write((char*)&M_[0], sizeof(M_[0]) * M_.size()); if(os.fail()){ throw std::runtime_error("HLL: Failed to dump"); } } /** * Restore the status from a stream * * @param[in] is The input stream where the status is saved * * @exception std::runtime_error When failed to restore. */ void restore(std::istream& is) { uint8_t b = 0; is.read((char*)&b, sizeof(b)); HyperLogLog tempHLL(b); is.read((char*)&(tempHLL.M_[0]), sizeof(M_[0]) * tempHLL.m_); if(is.fail()){ throw std::runtime_error("HLL: Failed to restore"); } swap(tempHLL); } protected: uint8_t b_; ///< register bit width uint32_t m_; ///< register size double alphaMM_; ///< alpha * m^2 std::vector M_; ///< registers }; /** * @brief HIP estimator on HyperLogLog counter. */ class HyperLogLogHIP : public HyperLogLog { public: /** * Constructor * * @param[in] b bit width (register size will be 2 to the b power). * This value must be in the range[4,30].Default value is 4. * * @exception std::invalid_argument the argument is out of range. */ HyperLogLogHIP(uint8_t b = 4) : HyperLogLog(b), register_limit_((1 << 5) - 1), c_(0.0), p_(1 << b) { } /** * Adds element to the estimator * * @param[in] str string to add * @param[in] len length of string */ void add(const char* str, uint32_t len) { uint32_t hash; MurmurHash3_x86_32(str, len, HLL_HASH_SEED, (void*) &hash); uint32_t index = hash >> (32 - b_); uint8_t rank = _GET_CLZ((hash << b_), 32 - b_); rank = rank == 0 ? register_limit_ : std::min(register_limit_, rank); const uint8_t old = M_[index]; if (rank > old) { c_ += 1.0 / (p_/m_); p_ -= 1.0/(1 << old); M_[index] = rank; if(rank < 31){ p_ += 1.0/(uint32_t(1) << rank); } } } /** * Estimates cardinality value. * * @return Estimated cardinality value. */ double estimate() const { return c_; } /** * Merges the estimate from 'other' into this object, returning the estimate of their union. * The number of registers in each must be the same. * * @param[in] other HyperLogLog instance to be merged * * @exception std::invalid_argument number of registers doesn't match. */ void merge(const HyperLogLogHIP& other) { if (m_ != other.m_) { std::stringstream ss; ss << "number of registers doesn't match: " << m_ << " != " << other.m_; throw std::invalid_argument(ss.str().c_str()); } for (uint32_t r = 0; r < m_; ++r) { const uint8_t b = M_[r]; const uint8_t b_other = other.M_[r]; if (b < b_other) { c_ += 1.0 / (p_/m_); p_ -= 1.0/(1 << b); M_[r] |= b_other; if(b_other < register_limit_){ p_ += 1.0/(1 << b_other); } } } } /** * Clears all internal registers. */ void clear() { std::fill(M_.begin(), M_.end(), 0); c_ = 0.0; p_ = 1 << b_; } /** * Returns size of register. * * @return Register size */ uint32_t registerSize() const { return m_; } /** * Exchanges the content of the instance * * @param[in,out] rhs Another HyperLogLog instance */ void swap(HyperLogLogHIP& rhs) { std::swap(b_, rhs.b_); std::swap(m_, rhs.m_); std::swap(c_, rhs.c_); M_.swap(rhs.M_); } /** * Dump the current status to a stream * * @param[out] os The output stream where the data is saved * * @exception std::runtime_error When failed to dump. */ void dump(std::ostream& os) const { os.write((char*)&b_, sizeof(b_)); os.write((char*)&M_[0], sizeof(M_[0]) * M_.size()); os.write((char*)&c_, sizeof(c_)); os.write((char*)&p_, sizeof(p_)); if(os.fail()){ throw std::runtime_error("Failed to dump"); } } /** * Restore the status from a stream * * @param[in] is The input stream where the status is saved * * @exception std::runtime_error When failed to restore. */ void restore(std::istream& is) { uint8_t b = 0; is.read((char*)&b, sizeof(b)); HyperLogLogHIP tempHLL(b); is.read((char*)&(tempHLL.M_[0]), sizeof(M_[0]) * tempHLL.m_); is.read((char*)&(tempHLL.c_), sizeof(double)); is.read((char*)&(tempHLL.p_), sizeof(double)); if(is.fail()){ throw std::runtime_error("Failed to restore"); } swap(tempHLL); } private: const uint8_t register_limit_; double c_; double p_; }; } // namespace hll #endif // !defined(HYPERLOGLOG_HPP) weakforced-2.10.2/ext/ext/incbin/000077500000000000000000000000001461473602600165615ustar00rootroot00000000000000weakforced-2.10.2/ext/ext/incbin/incbin.h000066400000000000000000000071711461473602600202020ustar00rootroot00000000000000/** * @file incbin.h * @author Dale Weiler * @brief Utility for including binary files * * Facilities for including binary files into the current translation unit and * making use from them externally in other translation units. */ #ifndef INCBIN_HDR #define INCBIN_HDR #include #if defined(__SSE__) || defined(__neon__) # define INCBIN_ALIGNMENT 16 #else # if ULONG_MAX == 0xffffffffu # define INCBIN_ALIGNMENT 4 # else # define INCBIN_ALIGNMENT 8 # endif #endif #define INCBIN_ALIGN __attribute__((aligned(INCBIN_ALIGNMENT))) #ifdef __cplusplus # define INCBIN_EXTERNAL extern "C" #else # define INCBIN_EXTERNAL extern #endif #ifdef __APPLE__ # define INCBIN_SECTION ".const_data\n" # define INCBIN_GLOBAL(NAME) ".globl " #NAME "\n" # define INCBIN_INT ".long " # define INCBIN_MANGLE "_" # define INCBIN_TYPE(...) #else # define INCBIN_SECTION ".section .rodata\n" # define INCBIN_GLOBAL(NAME) ".global " #NAME "\n" # define INCBIN_INT ".int " # define INCBIN_MANGLE # define INCBIN_TYPE(NAME) ".type " #NAME ", @object\n" #endif #define INCBIN_STR(X) #X #define INCBIN_STRINGIZE(X) INCBIN_STR(X) /** * @brief Externally reference binary data included in another translation unit. * * Produces two external symbols that reference the binary data included in * another translation unit. * * The symbol names are a concatenation of "g" before *NAME*; with "Data", as well * as "Size" after. An example is provided below. * * @param NAME The name given for the binary data * * @code * INCBIN_EXTERN(Foo); * * // Now you have the following symbols: * // extern unsigned char gFooData[]; * // extern const unsigned char gFooEnd; * // extern unsigned int gFooSize; * @endcode */ #define INCBIN_EXTERN(NAME) \ INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char g ## NAME ## Data[]; \ INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char g ## NAME ## End; \ INCBIN_EXTERNAL const unsigned int g ## NAME ## Size /** * @brief Include a binary file into the current translation unit. * * Includes a binary file into the current translation unit, producing two symbols * for objects that encode the data and size respectively. * * The symbol names are a concatenation of "g" before *NAME*; with "Data", as well * as "Size" after. An example is provided below. * * @param NAME The name to associate with this binary data (as an identifier.) * @param FILENAME The file to include (as a string literal.) * * @code * INCBIN(Icon, "icon.png"); * * // Now you have the following symbols: * // unsigned char gIconData[]; * // unsigned int gIconSize; * @endcode * * @warning This must be used in global scope * * To externally reference the data included by this in another translation unit * please @see INCBIN_EXTERN. */ #define INCBIN(NAME, FILENAME) \ __asm__(INCBIN_SECTION \ INCBIN_GLOBAL(g ## NAME ## Data) \ INCBIN_TYPE(g ## NAME ## Data) \ ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" \ INCBIN_MANGLE "g" #NAME "Data:\n" \ ".incbin \"" FILENAME "\"\n" \ INCBIN_GLOBAL(g ## NAME ## End) \ INCBIN_TYPE(g ## NAME ## End) \ ".align 1\n" \ INCBIN_MANGLE "g" #NAME "End:\n" \ INCBIN_INT "1\n"\ INCBIN_GLOBAL(g ## NAME ## Size) \ INCBIN_TYPE(g ## NAME ## Size) \ ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" \ INCBIN_MANGLE "g" #NAME "Size:\n" \ INCBIN_INT INCBIN_MANGLE "g" #NAME "End - " INCBIN_MANGLE "g" #NAME "Data\n" \ ); \ INCBIN_EXTERN(NAME) #endif weakforced-2.10.2/ext/ext/luawrapper/000077500000000000000000000000001461473602600175015ustar00rootroot00000000000000weakforced-2.10.2/ext/ext/luawrapper/include/000077500000000000000000000000001461473602600211245ustar00rootroot00000000000000weakforced-2.10.2/ext/ext/luawrapper/include/LuaContext.hpp000066400000000000000000003647431461473602600237440ustar00rootroot00000000000000/* Copyright (c) 2013, Pierre KRIEGER All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef INCLUDE_LUACONTEXT_HPP #define INCLUDE_LUACONTEXT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1900 # include "misc/exception.hpp" #endif #ifdef __GNUC__ # define ATTR_UNUSED __attribute__((unused)) #else # define ATTR_UNUSED #endif #define LUACONTEXT_GLOBAL_EQ "e5ddced079fc405aa4937b386ca387d2" #define EQ_FUNCTION_NAME "__eq" #define TOSTRING_FUNCTION_NAME "__tostring" /** * Defines a Lua context * A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions), * we only provide few functions like readVariable and writeVariable. * * You can also write variables with C++ functions so that they are callable by Lua. Note however that you HAVE TO convert * your function to std::function (not directly std::bind or a lambda function) so the class can detect which argument types * it wants. These arguments may only be of basic types (int, float, etc.) or std::string. */ #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif class LuaContext { struct ValueInRegistry; template struct Binder; template struct IsOptional; enum Globals_t { Globals }; // tag for "global variables" public: /** * @param openDefaultLibs True if luaL_openlibs should be called */ explicit LuaContext(bool openDefaultLibs = true) { // luaL_newstate can return null if allocation failed mState = luaL_newstate(); if (mState == nullptr) throw std::bad_alloc(); // setting the panic function lua_atpanic(mState, [](lua_State* state) -> int { const std::string str = lua_tostring(state, -1); lua_pop(state, 1); assert(false && "lua_atpanic triggered"); exit(0); }); // opening default library if required to do so if (openDefaultLibs) luaL_openlibs(mState); writeGlobalEq(); } void writeGlobalEq() { const auto eqFunction = [](lua_State* lua) -> int { try { lua_pushstring(lua, "__eq"); lua_gettable(lua, -2); /* if not found, return false */ if (lua_isnil(lua, -1)) { lua_pop(lua, -2); lua_pushboolean(lua, false); return 1; } lua_insert(lua, lua_gettop(lua)-2); return callRaw(lua, PushedObject{lua, 3}, 1).release(); } catch(...) { Pusher::push(lua, std::current_exception()).release(); luaError(lua); } }; lua_pushcfunction(mState, eqFunction); lua_setglobal(mState, LUACONTEXT_GLOBAL_EQ); }; /** * Move constructor */ LuaContext(LuaContext&& s) : mState(s.mState) { s.mState = luaL_newstate(); } /** * Move operator */ LuaContext& operator=(LuaContext&& s) noexcept { std::swap(mState, s.mState); return *this; } /** * Copy is forbidden */ LuaContext(const LuaContext&) = delete; /** * Copy is forbidden */ LuaContext& operator=(const LuaContext&) = delete; /** * Destructor */ ~LuaContext() noexcept { assert(mState); lua_close(mState); } /** * Thrown when an error happens during execution of lua code (like not enough parameters for a function) */ class ExecutionErrorException : public std::runtime_error { public: ExecutionErrorException(const std::string& msg) : std::runtime_error(msg) { } }; /** * Thrown when a syntax error happens in a lua script */ class SyntaxErrorException : public std::runtime_error { public: SyntaxErrorException(const std::string& msg) : std::runtime_error(msg) { } }; /** * Thrown when trying to cast a Lua variable to an unvalid type, eg. trying to read a number when the variable is a string */ class WrongTypeException : public std::runtime_error { public: WrongTypeException(const std::string& luaType_, const std::type_info& destination_) : std::runtime_error("Trying to cast a lua variable from \"" + luaType_ + "\" to \"" + destination_.name() + "\""), luaType(luaType_), destination(destination_) { } std::string luaType; const std::type_info& destination; }; /** * Function object that can call a function stored by Lua * This type is copyable and movable, but not constructible. It can only be created through readVariable. * @tparam TFunctionType Function type (eg. "int (int, bool)") */ template class LuaFunctionCaller; /** * Opaque type that identifies a Lua object */ struct LuaObject { LuaObject() = default; LuaObject(lua_State* state, int index=-1) { this->objectInRegistry = std::make_shared(state, index); } std::shared_ptr objectInRegistry; }; /** * Opaque type that identifies a Lua thread */ struct ThreadID { ThreadID() = default; ThreadID(ThreadID&& o) : state(o.state), threadInRegistry(std::move(o.threadInRegistry)) { } ThreadID& operator=(ThreadID&& o) { std::swap(state, o.state); std::swap(threadInRegistry, o.threadInRegistry); return *this; } public: friend LuaContext; lua_State* state; std::unique_ptr threadInRegistry; }; /** * Type that is considered as an empty array */ enum EmptyArray_t { EmptyArray }; /** * Type for a metatable */ enum Metatable_t { Metatable }; /** * Executes lua code from the stream * @param code A stream that Lua will read its code from */ void executeCode(std::istream& code) { auto toCall = load(mState, code); call>(mState, std::move(toCall)); } /** * Executes lua code from the stream and returns a value * @param code A stream that Lua will read its code from * @tparam TType The type that the executing code should return */ template auto executeCode(std::istream& code) -> TType { auto toCall = load(mState, code); return call(mState, std::move(toCall)); } /** * Executes lua code given as parameter * @param code A string containing code that will be executed by Lua */ void executeCode(const std::string& code) { executeCode(code.c_str()); } /* * Executes Lua code from the stream and returns a value * @param code A string containing code that will be executed by Lua * @tparam TType The type that the executing code should return */ template auto executeCode(const std::string& code) -> TType { return executeCode(code.c_str()); } /** * Executes Lua code * @param code A string containing code that will be executed by Lua */ void executeCode(const char* code) { auto toCall = load(mState, code); call>(mState, std::move(toCall)); } /* * Executes Lua code from the stream and returns a value * @param code A string containing code that will be executed by Lua * @tparam TType The type that the executing code should return */ template auto executeCode(const char* code) -> TType { auto toCall = load(mState, code); return call(mState, std::move(toCall)); } /** * Executes lua code from the stream * @param code A stream that Lua will read its code from */ void executeCode(const ThreadID& thread, std::istream& code) { auto toCall = load(thread.state, code); call>(thread.state, std::move(toCall)); } /** * Executes lua code from the stream and returns a value * @param code A stream that Lua will read its code from * @tparam TType The type that the executing code should return */ template auto executeCode(const ThreadID& thread, std::istream& code) -> TType { auto toCall = load(thread.state, code); return call(thread.state, std::move(toCall)); } /** * Executes lua code given as parameter * @param code A string containing code that will be executed by Lua */ void executeCode(const ThreadID& thread, const std::string& code) { executeCode(thread, code.c_str()); } /* * Executes Lua code from the stream and returns a value * @param code A string containing code that will be executed by Lua * @tparam TType The type that the executing code should return */ template auto executeCode(const ThreadID& thread, const std::string& code) -> TType { return executeCode(thread, code.c_str()); } /** * Executes Lua code * @param code A string containing code that will be executed by Lua */ void executeCode(const ThreadID& thread, const char* code) { auto toCall = load(thread.state, code); call>(thread.state, std::move(toCall)); } /* * Executes Lua code from the stream and returns a value * @param code A string containing code that will be executed by Lua * @tparam TType The type that the executing code should return */ template auto executeCode(const ThreadID& thread, const char* code) -> TType { auto toCall = load(thread.state, code); return call(thread.state, std::move(toCall)); } /** * Tells that Lua will be allowed to access an object's function * This is the version "registerFunction(name, &Foo::function)" */ template auto registerFunction(const std::string& name, TPointerToMemberFunction pointer) -> typename std::enable_if::value>::type { registerFunctionImpl(name, std::mem_fn(pointer), tag{}); } /** * Tells that Lua will be allowed to access an object's function * This is the version with an explicit template parameter: "registerFunction(name, [](Foo&) { })" * @param fn Function object which takes as first parameter a reference to the object * @tparam TFunctionType Pointer-to-member function type */ template void registerFunction(const std::string& functionName, TType fn) { static_assert(std::is_member_function_pointer::value, "registerFunction must take a member function pointer type as template parameter"); registerFunctionImpl(functionName, std::move(fn), tag{}); } /** * Tells that Lua will be allowed to access an object's function * This is the alternative version with an explicit template parameter: "registerFunction(name, [](Foo&) { })" * @param fn Function object which takes as first parameter a reference to the object * @tparam TObject Object to register this function to * @tparam TFunctionType Function type */ template void registerFunction(const std::string& functionName, TType fn) { static_assert(std::is_function::value, "registerFunction must take a function type as template parameter"); registerFunctionImpl(functionName, std::move(fn), tag{}, tag{}); } /** * Wrappers for registering "__eq" function in case we want to change this to something else some day */ template auto registerEqFunction(TPointerToMemberFunction pointer) -> typename std::enable_if::value>::type { registerFunctionImpl(EQ_FUNCTION_NAME, std::mem_fn(pointer), tag{}); } template void registerEqFunction(TType fn) { static_assert(std::is_member_function_pointer::value, "registerFunction must take a member function pointer type as template parameter"); registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag{}); } template void registerEqFunction(TType fn) { static_assert(std::is_function::value, "registerFunction must take a function type as template parameter"); registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag{}, tag{}); } /** * Wrappers for registering "__tostring" function in case we want to change this to something else some day */ template auto registerToStringFunction(TPointerToMemberFunction pointer) -> typename std::enable_if::value>::type { registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::mem_fn(pointer), tag{}); } template void registerToStringFunction(TType fn) { static_assert(std::is_member_function_pointer::value, "registerFunction must take a member function pointer type as template parameter"); registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag{}); } template void registerToStringFunction(TType fn) { static_assert(std::is_function::value, "registerFunction must take a function type as template parameter"); registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag{}, tag{}); } /** * Inverse operation of registerFunction * @tparam TType Type whose function belongs to */ template void unregisterFunction(const std::string& /*functionName*/) { lua_pushlightuserdata(mState, const_cast(&typeid(TType))); lua_pushnil(mState); lua_settable(mState, LUA_REGISTRYINDEX); checkTypeRegistration(mState, &typeid(TType)); lua_pushlightuserdata(mState, const_cast(&typeid(TType*))); lua_pushnil(mState); lua_settable(mState, LUA_REGISTRYINDEX); checkTypeRegistration(mState, &typeid(TType*)); lua_pushlightuserdata(mState, const_cast(&typeid(std::shared_ptr))); lua_pushnil(mState); lua_settable(mState, LUA_REGISTRYINDEX); checkTypeRegistration(mState, &typeid(std::shared_ptr)); } /** * Registers a member variable * This is the version "registerMember(name, &Foo::member)" */ template void registerMember(const std::string& name, TVarType TObject::*member) { // implementation simply calls the custom member with getter and setter const auto getter = [=](const TObject& obj) -> TVarType { return obj.*member; }; const auto setter = [=](TObject& obj, const TVarType& value) { obj.*member = value; }; registerMember(name, getter, setter); } /** * Registers a member variable * This is the version "registerMember(name, getter, setter)" * @tparam TObject Type to register the member to * @tparam TVarType Type of the member * @param name Name of the member to register * @param readFunction Function of type "TVarType (const TObject&)" * @param writeFunction_ Function of type "void (TObject&, const TVarType&)" */ template void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_) { registerMemberImpl(name, std::move(readFunction), std::move(writeFunction_)); } /** * Registers a member variable * This is the version "registerMember(name, getter, setter)" * @tparam TMemberType Pointer to member object representing the type * @param name Name of the member to register * @param readFunction Function of type "TVarType (const TObject&)" * @param writeFunction_ Function of type "void (TObject&, const TVarType&)" */ template void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_) { static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); registerMemberImpl(tag{}, name, std::move(readFunction), std::move(writeFunction_)); } /** * Registers a non-modifiable member variable * This is the version "registerMember(name, getter)" * @tparam TObject Type to register the member to * @tparam TVarType Type of the member * @param name Name of the member to register * @param readFunction Function of type "TVarType (const TObject&)" */ template void registerMember(const std::string& name, TReadFunction readFunction) { registerMemberImpl(name, std::move(readFunction)); } /** * Registers a non-modifiable member variable * This is the version "registerMember(name, getter)" * @tparam TMemberType Pointer to member object representing the type * @param name Name of the member to register * @param readFunction Function of type "TVarType (const TObject&)" */ template void registerMember(const std::string& name, TReadFunction readFunction) { static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); registerMemberImpl(tag{}, name, std::move(readFunction)); } /** * Registers a dynamic member variable * This is the version "registerMember(getter, setter)" * @tparam TObject Type to register the member to * @tparam TVarType Type of the member * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)" */ template void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_) { registerMemberImpl(std::move(readFunction), std::move(writeFunction_)); } /** * Registers a dynamic member variable * This is the version "registerMember(getter, setter)" * @tparam TMemberType Pointer to member object representing the type * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" * @param writeFunction_ Function of type "void (TObject&, const std::string&, const TVarType&)" */ template void registerMember(TReadFunction readFunction, TWriteFunction writeFunction_) { static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); registerMemberImpl(tag{}, std::move(readFunction), std::move(writeFunction_)); } /** * Registers a dynamic non-modifiable member variable * This is the version "registerMember(getter)" * @tparam TObject Type to register the member to * @tparam TVarType Type of the member * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" */ template void registerMember(TReadFunction readFunction) { registerMemberImpl(std::move(readFunction)); } /** * Registers a dynamic non-modifiable member variable * This is the version "registerMember(getter)" * @tparam TMemberType Pointer to member object representing the type * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" */ template void registerMember(TReadFunction readFunction) { static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); registerMemberImpl(tag{}, std::move(readFunction)); } /** * Creates a new thread * A Lua thread is not really a thread, but rather an "execution stack". * You can destroy the thread by calling destroyThread * @sa destroyThread */ auto createThread() -> ThreadID { ThreadID result; result.state = lua_newthread(mState); result.threadInRegistry = std::unique_ptr(new ValueInRegistry(mState)); lua_pop(mState, 1); return result; } /** * Destroys a thread created with createThread * @sa createThread */ void destroyThread(ThreadID& id) { id.threadInRegistry.reset(); } /** * Reads the content of a Lua variable * * @tparam TType Type requested for the read * @throw WrongTypeException When the variable is not convertible to the requested type * @sa writeVariable * * Readable types are all types accepted by writeVariable except nullptr, std::unique_ptr and function pointers * Additionally supported: * - LuaFunctionCaller, which is an alternative to std::function * - references to custom objects, in which case it will return the object in-place * * After the variable name, you can add other parameters. * If the variable is an array, it will instead get the element of that array whose offset is the second parameter. * Same applies for third, fourth, etc. parameters. */ template TType readVariable(const std::string& name, TTypes&&... elements) const { lua_getglobal(mState, name.c_str()); lookIntoStackTop(mState, std::forward(elements)...); return readTopAndPop(mState, PushedObject{mState, 1}); } /** * @sa readVariable */ template TType readVariable(const char* name, TTypes&&... elements) const { lua_getglobal(mState, name); lookIntoStackTop(mState, std::forward(elements)...); return readTopAndPop(mState, PushedObject{mState, 1}); } /** * @sa readVariable */ template TType readVariable(const ThreadID& thread, const std::string& name, TTypes&&... elements) const { lua_getglobal(thread.state, name.c_str()); lookIntoStackTop(thread.state, std::forward(elements)...); return readTopAndPop(thread.state, PushedObject{thread.state, 1}); } /** * @sa readVariable */ template TType readVariable(const ThreadID& thread, const char* name, TTypes&&... elements) const { lua_getglobal(thread.state, name); lookIntoStackTop(thread.state, std::forward(elements)...); return readTopAndPop(thread.state, PushedObject{thread.state, 1}); } /** * Changes the content of a Lua variable * * Accepted values are: * - all base types (char, short, int, float, double, bool) * - std::string * - enums * - std::vector<> * - std::vector>, std::map<> and std::unordered_map<> (the key and value must also be accepted values) * - std::function<> (all parameters must be accepted values, and return type must be either an accepted value for readVariable or a tuple) * - std::shared_ptr<> (std::unique_ptr<> are converted to std::shared_ptr<>) * - nullptr (writes nil) * - any object * * All objects are passed by copy and destroyed by the garbage collector if necessary. */ template void writeVariable(TData&&... data) noexcept { static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeVariable"); typedef typename std::decay>::type>::type RealDataType; static_assert(!std::is_same::type,RealDataType>::value, "Error: you can't use LuaContext::writeVariable with a tuple"); setTable(mState, Globals, std::forward(data)...); } /** * Equivalent to writeVariable(varName, ..., std::function(data)); * This version is more efficient than writeVariable if you want to write functions */ template void writeFunction(TData&&... data) noexcept { static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction"); setTable(mState, Globals, std::forward(data)...); } /** * Same as the other writeFunction, except that the template parameter is automatically detected * This only works if the data is either a native function pointer, or contains one operator() (this is the case for lambdas) */ template void writeFunction(TData&&... data) noexcept { static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction"); typedef typename std::decay>::type>::type RealDataType; typedef typename FunctionTypeDetector::type DetectedFunctionType; return writeFunction(std::forward(data)...); } private: // the state is the most important variable in the class since it is our interface with Lua // - registered members and functions are stored in tables at offset &typeid(type) of the registry // each table has its getter functions at offset 0, getter members at offset 1, default getter at offset 2 // offset 3 is unused, setter members at offset 4, default setter at offset 5 lua_State* mState; /**************************************************/ /* PUSH OBJECT */ /**************************************************/ struct PushedObject { PushedObject(lua_State* state_, int num_ = 1) : state(state_), num(num_) {} ~PushedObject() { assert(lua_gettop(state) >= num); if (num >= 1) lua_pop(state, num); } PushedObject& operator=(const PushedObject&) = delete; PushedObject(const PushedObject&) = delete; PushedObject& operator=(PushedObject&& other) { std::swap(state, other.state); std::swap(num, other.num); return *this; } PushedObject(PushedObject&& other) : state(other.state), num(other.num) { other.num = 0; } PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return obj; } void operator+=(PushedObject&& other) { assert(state == other.state); num += other.num; other.num = 0; } auto getState() const -> lua_State* { return state; } auto getNum() const -> int { return num; } int release() { const auto n = num; num = 0; return n; } void pop() { if (num >= 1) lua_pop(state, num); num = 0; } void pop(int n) { assert(num >= n); lua_pop(state, n); num -= n; } private: lua_State* state; int num = 0; }; /**************************************************/ /* MISC */ /**************************************************/ // type used as a tag template struct tag {}; // tag for "the registry" enum RegistryTag { Registry }; // this function takes a value representing the offset to look into // it will look into the top element of the stack and replace the element by its content at the given index template static void lookIntoStackTop(lua_State* state, OffsetType1&& offset1, OffsetTypeOthers&&... offsetOthers) { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); auto p1 = Pusher::type>::push(state, offset1); lua_gettable(state, -2); lua_remove(state, -2); p1.release(); lookIntoStackTop(state, std::forward(offsetOthers)...); } template static void lookIntoStackTop(lua_State* state, Metatable_t, OffsetTypeOthers&&... offsetOthers) { lua_getmetatable(state, -1); lua_remove(state, -2); lookIntoStackTop(state, std::forward(offsetOthers)...); } static void lookIntoStackTop(lua_State*) { } // equivalent of lua_settable with t[k]=n, where t is the value at the index in the template parameter, k is the second parameter, n is the last parameter, and n is pushed by the function in the first parameter // if there are more than 3 parameters, parameters 3 to n-1 are considered as sub-indices into the array // the dataPusher MUST push only one thing on the stack // TTableIndex must be either LUA_REGISTRYINDEX, LUA_GLOBALSINDEX, LUA_ENVINDEX, or the position of the element on the stack template static void setTable(lua_State* state, const PushedObject&, TIndex&& index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, index); auto p2 = Pusher::type>::push(state, std::forward(data)); lua_settable(state, -3); p1.release(); p2.release(); } template static void setTable(lua_State* state, const PushedObject&, const std::string& index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, std::forward(data)); lua_setfield(state, -2, index.c_str()); p1.release(); } template static void setTable(lua_State* state, const PushedObject&, const char* index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, std::forward(data)); lua_setfield(state, -2, index); p1.release(); } template static void setTable(lua_State* state, const PushedObject&, Metatable_t, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, std::forward(data)); lua_setmetatable(state, -2); p1.release(); } template static auto setTable(lua_State* state, PushedObject&, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept -> typename std::enable_if::type, Metatable_t>::value>::type { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); auto p1 = Pusher::type>::push(state, std::forward(index1)); lua_gettable(state, -2); setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); } template static auto setTable(lua_State* state, PushedObject&& pushedTable, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept -> typename std::enable_if::type, Metatable_t>::value>::type { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); auto p1 = Pusher::type>::push(state, std::forward(index1)) + std::move(pushedTable); lua_gettable(state, -2); setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); } template static void setTable(lua_State* state, PushedObject& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept { if (lua_getmetatable(state, -1) == 0) { lua_newtable(state); PushedObject p1{state, 1}; setTable(state, p1, std::forward(index2), std::forward(index3), std::forward(indices)...); lua_setmetatable(state, -2); p1.release(); } else { setTable(state, pushedObject, std::forward(index2), std::forward(index3), std::forward(indices)...); } } template static void setTable(lua_State* state, PushedObject&& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept { if (lua_getmetatable(state, -1) == 0) { lua_newtable(state); PushedObject p1{state, 1}; setTable(state, p1, std::forward(index2), std::forward(index3), std::forward(indices)...); lua_setmetatable(state, -2); p1.release(); } else { setTable(state, std::move(pushedObject), std::forward(index2), std::forward(index3), std::forward(indices)...); } } template static void setTable(lua_State* state, RegistryTag, TIndex&& index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, index); auto p2 = Pusher::type>::push(state, std::forward(data)); lua_settable(state, LUA_REGISTRYINDEX); p1.release(); p2.release(); } template static void setTable(lua_State* state, RegistryTag, const std::string& index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, std::forward(data)); lua_setfield(state, LUA_REGISTRYINDEX, index.c_str()); p1.release(); } template static void setTable(lua_State* state, RegistryTag, const char* index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, std::forward(data)); lua_setfield(state, LUA_REGISTRYINDEX, index); p1.release(); } template static void setTable(lua_State* state, RegistryTag, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); auto p1 = Pusher::type>::push(state, std::forward(index1)); lua_gettable(state, LUA_REGISTRYINDEX); setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); } template static void setTable(lua_State* state, Globals_t, TIndex&& index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); # if LUA_VERSION_NUM >= 502 lua_pushglobaltable(state); PushedObject p3{state, 1}; auto p1 = Pusher::type>::push(state, index); auto p2 = Pusher::type>::push(state, std::forward(data)); lua_settable(state, -3); # else auto p1 = Pusher::type>::push(state, index); auto p2 = Pusher::type>::push(state, std::forward(data)); lua_settable(state, LUA_GLOBALSINDEX); # endif p1.release(); p2.release(); } template static void setTable(lua_State* state, Globals_t, const std::string& index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, std::forward(data)); lua_setglobal(state, index.c_str()); p1.release(); } template static void setTable(lua_State* state, Globals_t, const char* index, TData&& data) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); auto p1 = Pusher::type>::push(state, std::forward(data)); lua_setglobal(state, index); p1.release(); } template static void setTable(lua_State* state, Globals_t, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); # if LUA_VERSION_NUM >= 502 lua_pushglobaltable(state); auto p1 = Pusher::type>::push(state, std::forward(index1)) + PushedObject{state, 1}; lua_gettable(state, -2); # else auto p1 = Pusher::type>::push(state, std::forward(index1)); lua_gettable(state, LUA_GLOBALSINDEX); # endif setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); } // TODO: g++ reports "ambiguous overload" /*template static void setTable(lua_State* state, Globals_t, const char* index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept { lua_getglobal(state, index); PushedObject p1{state, 1}; setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); } template static void setTable(lua_State* state, Globals_t, const std::string& index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept { lua_getglobal(state, index.c_str()); PushedObject p1{state, 1}; setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); }*/ // simple function that reads the "nb" first top elements of the stack, pops them, and returns the value // warning: first parameter is the number of parameters, not the parameter index // if read generates an exception, stack is popped anyway template static auto readTopAndPop(lua_State* state, PushedObject object) -> TReturnType { auto val = Reader::type>::read(state, -object.getNum()); if (!val.is_initialized()) throw WrongTypeException{lua_typename(state, lua_type(state, -object.getNum())), typeid(TReturnType)}; return val.get(); } // checks that the offsets for a type's registrations are set in the registry static void checkTypeRegistration(lua_State* state, const std::type_info* type) { lua_pushlightuserdata(state, const_cast(type)); lua_gettable(state, LUA_REGISTRYINDEX); if (!lua_isnil(state, -1)) { lua_pop(state, 1); return; } lua_pop(state, 1); lua_pushlightuserdata(state, const_cast(type)); lua_newtable(state); lua_pushinteger(state, 0); lua_newtable(state); lua_settable(state, -3); lua_pushinteger(state, 1); lua_newtable(state); lua_settable(state, -3); lua_pushinteger(state, 3); lua_newtable(state); lua_settable(state, -3); lua_pushinteger(state, 4); lua_newtable(state); lua_settable(state, -3); lua_settable(state, LUA_REGISTRYINDEX); } // # ifdef _MSC_VER __declspec(noreturn) # else [[noreturn]] # endif static void luaError(lua_State* state) { lua_error(state); assert(false); std::terminate(); // removes compilation warning } /**************************************************/ /* FUNCTIONS REGISTRATION */ /**************************************************/ // the "registerFunction" public functions call this one template void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag, tag) { static_assert(std::is_class::value || std::is_pointer::value || std::is_union::value , "registerFunction can only be used for a class a union or a pointer"); checkTypeRegistration(mState, &typeid(TObject)); setTable(mState, Registry, &typeid(TObject), 0, functionName, function); checkTypeRegistration(mState, &typeid(TObject*)); setTable(mState, Registry, &typeid(TObject*), 0, functionName, [=](TObject* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); checkTypeRegistration(mState, &typeid(std::shared_ptr)); setTable, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr), 0, functionName, [=](const std::shared_ptr& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); } template void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag, tag fTypeTag) { registerFunctionImpl(functionName, function, tag{}, fTypeTag); checkTypeRegistration(mState, &typeid(TObject const*)); setTable(mState, Registry, &typeid(TObject const*), 0, functionName, [=](TObject const* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); checkTypeRegistration(mState, &typeid(std::shared_ptr)); setTable, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr), 0, functionName, [=](const std::shared_ptr& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); } template void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) { registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); } template void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) { registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); } template void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) { registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); } template void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) { registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); } // the "registerMember" public functions call this one template void registerMemberImpl(const std::string& name, TReadFunction readFunction) { static_assert(std::is_class::value || std::is_pointer::value, "registerMember can only be called on a class or a pointer"); checkTypeRegistration(mState, &typeid(TObject)); setTable(mState, Registry, &typeid(TObject), 1, name, [readFunction](TObject const& object) { return readFunction(object); }); checkTypeRegistration(mState, &typeid(TObject*)); setTable(mState, Registry, &typeid(TObject*), 1, name, [readFunction](TObject const* object) { assert(object); return readFunction(*object); }); checkTypeRegistration(mState, &typeid(TObject const*)); setTable(mState, Registry, &typeid(TObject const*), 1, name, [readFunction](TObject const* object) { assert(object); return readFunction(*object); }); checkTypeRegistration(mState, &typeid(std::shared_ptr)); setTable)>(mState, Registry, &typeid(std::shared_ptr), 1, name, [readFunction](const std::shared_ptr& object) { assert(object); return readFunction(*object); }); checkTypeRegistration(mState, &typeid(std::shared_ptr)); setTable)>(mState, Registry, &typeid(std::shared_ptr), 1, name, [readFunction](const std::shared_ptr& object) { assert(object); return readFunction(*object); }); } template void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_) { registerMemberImpl(name, readFunction); setTable(mState, Registry, &typeid(TObject), 4, name, [writeFunction_](TObject& object, const TVarType& value) { writeFunction_(object, value); }); setTable(mState, Registry, &typeid(TObject*), 4, name, [writeFunction_](TObject* object, const TVarType& value) { assert(object); writeFunction_(*object, value); }); setTable, TVarType)>(mState, Registry, &typeid(std::shared_ptr), 4, name, [writeFunction_](std::shared_ptr object, const TVarType& value) { assert(object); writeFunction_(*object, value); }); } template void registerMemberImpl(tag, const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction_) { registerMemberImpl(name, std::move(readFunction), std::move(writeFunction_)); } template void registerMemberImpl(tag, const std::string& name, TReadFunction readFunction) { registerMemberImpl(name, std::move(readFunction)); } // the "registerMember" public functions call this one template void registerMemberImpl(TReadFunction readFunction) { checkTypeRegistration(mState, &typeid(TObject)); setTable(mState, Registry, &typeid(TObject), 2, [readFunction](TObject const& object, const std::string& name) { return readFunction(object, name); }); checkTypeRegistration(mState, &typeid(TObject*)); setTable(mState, Registry, &typeid(TObject*), 2, [readFunction](TObject const* object, const std::string& name) { assert(object); return readFunction(*object, name); }); checkTypeRegistration(mState, &typeid(TObject const*)); setTable(mState, Registry, &typeid(TObject const*), 2, [readFunction](TObject const* object, const std::string& name) { assert(object); return readFunction(*object, name); }); checkTypeRegistration(mState, &typeid(std::shared_ptr)); setTable, std::string)>(mState, Registry, &typeid(std::shared_ptr), 2, [readFunction](const std::shared_ptr& object, const std::string& name) { assert(object); return readFunction(*object, name); }); checkTypeRegistration(mState, &typeid(std::shared_ptr)); setTable, std::string)>(mState, Registry, &typeid(std::shared_ptr), 2, [readFunction](const std::shared_ptr& object, const std::string& name) { assert(object); return readFunction(*object, name); }); } template void registerMemberImpl(TReadFunction readFunction, TWriteFunction writeFunction_) { registerMemberImpl(readFunction); setTable(mState, Registry, &typeid(TObject), 5, [writeFunction_](TObject& object, const std::string& name, const TVarType& value) { writeFunction_(object, name, value); }); setTable(mState, Registry, &typeid(TObject*), 2, [writeFunction_](TObject* object, const std::string& name, const TVarType& value) { assert(object); writeFunction_(*object, name, value); }); setTable, std::string, TVarType)>(mState, Registry, &typeid(std::shared_ptr), 2, [writeFunction_](const std::shared_ptr& object, const std::string& name, const TVarType& value) { assert(object); writeFunction_(*object, name, value); }); } template void registerMemberImpl(tag, TReadFunction readFunction, TWriteFunction writeFunction_) { registerMemberImpl(std::move(readFunction), std::move(writeFunction_)); } template void registerMemberImpl(tag, TReadFunction readFunction) { registerMemberImpl(std::move(readFunction)); } /**************************************************/ /* LOADING AND CALLING */ /**************************************************/ // this function loads data from the stream and pushes a function at the top of the stack // throws in case of syntax error static PushedObject load(lua_State* state, std::istream& code) { // since the lua_load function requires a static function, we use this structure // the Reader structure is at the same time an object storing an istream and a buffer, // and a static function provider struct Reader { Reader(std::istream& str) : stream(str) {} std::istream& stream; std::array buffer; // read function ; "data" must be an instance of Reader static const char* read(lua_State* /*l*/, void* data, size_t* size) { assert(size != nullptr); assert(data != nullptr); Reader& me = *static_cast(data); if (me.stream.eof()) { *size = 0; return nullptr; } me.stream.read(me.buffer.data(), me.buffer.size()); *size = static_cast(me.stream.gcount()); // gcount could return a value larger than a size_t, but its maximum is me.buffer.size() so there's no problem return me.buffer.data(); } }; // we create an instance of Reader, and we call lua_load Reader reader{code}; const auto loadReturnValue = lua_load(state, &Reader::read, &reader, "chunk" # if LUA_VERSION_NUM >= 502 , nullptr # endif ); // now we have to check return value if (loadReturnValue != 0) { // there was an error during loading, an error message was pushed on the stack const std::string errorMsg = lua_tostring(state, -1); lua_pop(state, 1); if (loadReturnValue == LUA_ERRMEM) throw std::bad_alloc(); else if (loadReturnValue == LUA_ERRSYNTAX) throw SyntaxErrorException{errorMsg}; throw std::runtime_error("Error while calling lua_load: " + errorMsg); } return PushedObject{state, 1}; } // this function loads data and pushes a function at the top of the stack // throws in case of syntax error static PushedObject load(lua_State* state, const char* code) { auto loadReturnValue = luaL_loadstring(state, code); // now we have to check return value if (loadReturnValue != 0) { // there was an error during loading, an error message was pushed on the stack const std::string errorMsg = lua_tostring(state, -1); lua_pop(state, 1); if (loadReturnValue == LUA_ERRMEM) throw std::bad_alloc(); else if (loadReturnValue == LUA_ERRSYNTAX) throw SyntaxErrorException{errorMsg}; throw std::runtime_error("Error while calling lua_load: " + errorMsg); } return PushedObject{state, 1}; } // this function calls what is on the top of the stack and removes it (just like lua_call) // if an exception is triggered, the top of the stack will be removed anyway template static auto call(lua_State* state, PushedObject toCall, TParameters&&... input) -> TReturnType { typedef typename Tupleizer::type RealReturnType; // we push the parameters on the stack auto inArguments = Pusher>::push(state, std::forward_as_tuple(std::forward(input)...)); // const int outArgumentsCount = std::tuple_size::value; auto outArguments = callRaw(state, std::move(toCall) + std::move(inArguments), outArgumentsCount); // pcall succeeded, we pop the returned values and return them return readTopAndPop(state, std::move(outArguments)); } static int gettraceback(lua_State* L) { lua_getglobal(L, "debug"); // stack: error, debug library lua_getfield(L, -1, "traceback"); // stack: error, debug library, debug.traceback function lua_remove(L, -2); // stack: error, debug.traceback function lua_pushstring(L, ""); // stack: error, debug.traceback, "" lua_pushinteger(L, 2); // stack: error, debug.traceback, "", 2 lua_call(L, 2, 1); // stack: error, traceback lua_createtable(L, 2, 0); // stack: error, traceback, {} lua_insert(L, 1); // stack: {}, error, traceback lua_rawseti(L, 1, 2); // stack: {[2]=traceback}, error lua_rawseti(L, 1, 1); // stack: {[1]=error,[2]=traceback} return 1; // return the table } // this function just calls lua_pcall and checks for errors static PushedObject callRaw(lua_State* state, PushedObject functionAndArguments, const int outArguments) { // provide traceback handler int tbindex = lua_gettop(state) - (functionAndArguments.getNum() - 1); lua_pushcfunction(state, gettraceback); // move it back up, before our function and arguments lua_insert(state, tbindex); // calling pcall automatically pops the parameters and pushes output const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, tbindex); functionAndArguments.release(); lua_remove(state, tbindex); // remove traceback function // if pcall failed, analyzing the problem and throwing if (pcallReturnValue != 0) { // stack top: {error, traceback} lua_rawgeti(state, -1, 1); // stack top: {error, traceback}, error lua_rawgeti(state, -2, 2); // stack top: {error, traceback}, error, traceback lua_remove(state, -3); // stack top: error, traceback PushedObject traceBackRef{state, 1}; const auto traceBack = readTopAndPop(state, std::move(traceBackRef)); // stack top: error PushedObject errorCode{state, 1}; // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack if (pcallReturnValue == LUA_ERRMEM) { throw std::bad_alloc{}; } else if (pcallReturnValue == LUA_ERRRUN) { if (lua_isstring(state, 1)) { // the error is a string const auto str = readTopAndPop(state, std::move(errorCode)); throw ExecutionErrorException{str+traceBack}; } else { // an exception_ptr was pushed on the stack // rethrowing it with an additional ExecutionErrorException try { if (const auto exp = readTopAndPop(state, std::move(errorCode))) { std::rethrow_exception(exp); } } catch(const std::exception& e) { std::throw_with_nested(ExecutionErrorException{std::string{"Exception thrown by a callback function: "} + e.what()}); } catch(...) { std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua. "+traceBack}); } throw ExecutionErrorException{"Unknown Lua error"}; } } } return PushedObject{state, outArguments}; } /**************************************************/ /* PUSH FUNCTIONS */ /**************************************************/ template static PushedObject push(lua_State* state, T&& value) { return Pusher::type>::push(state, std::forward(value)); } // the Pusher structures allow you to push a value on the stack // - static const int minSize : minimum size on the stack that the value can have // - static const int maxSize : maximum size on the stack that the value can have // - static int push(const LuaContext&, ValueType) : pushes the value on the stack and returns the size on the stack // implementation for custom objects template struct Pusher { static const int minSize = 1; static const int maxSize = 1; template static PushedObject push(lua_State* state, TType2&& value) noexcept { // this function is called when lua's garbage collector wants to destroy our object // we simply call its destructor const auto garbageCallbackFunction = [](lua_State* lua) -> int { assert(lua_gettop(lua) == 1); TType* ptr = static_cast(lua_touserdata(lua, 1)); assert(ptr); ptr->~TType(); return 0; }; // this function will be stored in __index in the metatable const auto indexFunction = [](lua_State* lua) -> int { try { assert(lua_gettop(lua) == 2); assert(lua_isuserdata(lua, 1)); // searching for a handler lua_pushlightuserdata(lua, const_cast(&typeid(TType))); lua_gettable(lua, LUA_REGISTRYINDEX); assert(!lua_isnil(lua, -1)); // looking into getter functions lua_pushinteger(lua, 0); lua_gettable(lua, -2); lua_pushvalue(lua, 2); lua_gettable(lua, -2); if (!lua_isnil(lua, -1)) return 1; lua_pop(lua, 2); // looking into getter members lua_pushinteger(lua, 1); lua_gettable(lua, -2); lua_pushvalue(lua, 2); lua_gettable(lua, -2); if (!lua_isnil(lua, -1)) { lua_pushvalue(lua, 1); return callRaw(lua, PushedObject{lua, 2}, 1).release(); } lua_pop(lua, 2); // looking into default getter lua_pushinteger(lua, 2); lua_gettable(lua, -2); if (lua_isnil(lua, -1)) return 1; lua_pushvalue(lua, 1); lua_pushvalue(lua, 2); return callRaw(lua, PushedObject{lua, 3}, 1).release(); } catch (...) { Pusher::push(lua, std::current_exception()).release(); luaError(lua); } }; // this function will be stored in __newindex in the metatable const auto newIndexFunction = [](lua_State* lua) -> int { try { assert(lua_gettop(lua) == 3); assert(lua_isuserdata(lua, 1)); // searching for a handler lua_pushlightuserdata(lua, const_cast(&typeid(TType))); lua_rawget(lua, LUA_REGISTRYINDEX); assert(!lua_isnil(lua, -1)); // looking into setter members lua_pushinteger(lua, 4); lua_rawget(lua, -2); lua_pushvalue(lua, 2); lua_rawget(lua, -2); if (!lua_isnil(lua, -1)) { lua_pushvalue(lua, 1); lua_pushvalue(lua, 3); callRaw(lua, PushedObject{lua, 3}, 0); lua_pop(lua, 2); return 0; } lua_pop(lua, 2); // looking into default setter lua_pushinteger(lua, 5); lua_rawget(lua, -2); if (lua_isnil(lua, -1)) { lua_pop(lua, 2); lua_pushstring(lua, "No setter found"); luaError(lua); } lua_pushvalue(lua, 1); lua_pushvalue(lua, 2); lua_pushvalue(lua, 3); callRaw(lua, PushedObject{lua, 4}, 0); lua_pop(lua, 1); return 0; } catch (...) { Pusher::push(lua, std::current_exception()).release(); luaError(lua); } }; const auto toStringFunction = [](lua_State* lua) -> int { try { assert(lua_gettop(lua) == 1); assert(lua_isuserdata(lua, 1)); lua_pushstring(lua, "__tostring"); lua_gettable(lua, 1); if (lua_isnil(lua, -1)) { const void *ptr = lua_topointer(lua, -2); lua_pop(lua, 1); lua_pushstring(lua, (boost::format("userdata 0x%08x") % reinterpret_cast(ptr)).str().c_str()); return 1; } lua_pushvalue(lua, 1); return callRaw(lua, PushedObject{lua, 2}, 1).release(); } catch (...) { Pusher::push(lua, std::current_exception()).release(); luaError(lua); } }; // writing structure for this type into the registry checkTypeRegistration(state, &typeid(TType)); // creating the object // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it // and that's what we do with placement-new const auto pointerLocation = static_cast(lua_newuserdata(state, sizeof(TType))); new (pointerLocation) TType(std::forward(value)); PushedObject obj{state, 1}; // creating the metatable (over the object on the stack) // lua_settable pops the key and value we just pushed, so stack management is easy // all that remains on the stack after these function calls is the metatable lua_newtable(state); PushedObject pushedTable{state, 1}; // using the garbage collecting function we created above if (!boost::has_trivial_destructor::value) { lua_pushstring(state, "__gc"); lua_pushcfunction(state, garbageCallbackFunction); lua_settable(state, -3); } // the _typeid index of the metatable will store the type_info* lua_pushstring(state, "_typeid"); lua_pushlightuserdata(state, const_cast(&typeid(TType))); lua_settable(state, -3); // using the index function we created above lua_pushstring(state, "__index"); lua_pushcfunction(state, indexFunction); lua_settable(state, -3); // using the newindex function we created above lua_pushstring(state, "__newindex"); lua_pushcfunction(state, newIndexFunction); lua_settable(state, -3); lua_pushstring(state, "__tostring"); lua_pushcfunction(state, toStringFunction); lua_settable(state, -3); lua_pushstring(state, "__eq"); lua_getglobal(state, LUACONTEXT_GLOBAL_EQ); lua_settable(state, -3); // at this point, the stack contains the object at offset -2 and the metatable at offset -1 // lua_setmetatable will bind the two together and pop the metatable // our custom type remains on the stack (and that's what we want since this is a push function) lua_setmetatable(state, -2); pushedTable.release(); return obj; } }; // this structure has a "size" int static member which is equal to the total of the push min size of all the types template struct PusherTotalMinSize; // this structure has a "size" int static member which is equal to the total of the push max size of all the types template struct PusherTotalMaxSize; // this structure has a "size" int static member which is equal to the maximum size of the push of all the types template struct PusherMinSize; // this structure has a "size" int static member which is equal to the maximum size of the push of all the types template struct PusherMaxSize; /**************************************************/ /* READ FUNCTIONS */ /**************************************************/ // the "Reader" structures allow to read data from the stack // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness) // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type template struct Reader { typedef typename std::conditional::value, TType, TType&>::type ReturnType; static auto read(lua_State* state, int index) -> boost::optional { if (!test(state, index)) return boost::none; return boost::optional(*static_cast(lua_touserdata(state, index))); } private: static bool test(lua_State* state, int index) { if (!lua_isuserdata(state, index)) return false; if (!lua_getmetatable(state, index)) return false; // now we have our metatable on the top of the stack // retrieving its _typeid member lua_pushstring(state, "_typeid"); lua_gettable(state, -2); const auto storedTypeID = static_cast(lua_touserdata(state, -1)); const auto typeIDToCompare = &typeid(TType); // if wrong typeid, returning false lua_pop(state, 2); if (storedTypeID != typeIDToCompare) return false; return true; } }; /** * This functions reads multiple values starting at "index" and passes them to the callback */ template static auto readIntoFunction(lua_State* /*state*/, tag, TCallback&& callback, int /*index*/) -> TRetValue { return callback(); } template static auto readIntoFunction(lua_State* state, tag retValueTag, TCallback&& callback, int index, tag, tag... othersTags) -> typename std::enable_if::value, TRetValue>::type { if (index >= 0) { Binder binder{ callback, {} }; return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...); } const auto& firstElem = Reader::type>::read(state, index); if (!firstElem) throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType)); Binder binder{ callback, *firstElem }; return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...); } template static auto readIntoFunction(lua_State* state, tag retValueTag, TCallback&& callback, int index, tag, tag... othersTags) -> typename std::enable_if::value, TRetValue>::type { if (index >= 0) throw std::logic_error("Wrong number of parameters"); const auto& firstElem = Reader::type>::read(state, index); if (!firstElem) throw WrongTypeException(lua_typename(state, lua_type(state, index)), typeid(TFirstType)); Binder binder{ callback, *firstElem }; return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...); } /**************************************************/ /* UTILITIES */ /**************************************************/ // structure that will ensure that a certain value is stored somewhere in the registry struct ValueInRegistry { // this constructor will clone and hold the value at the specified index (or by default at the top of the stack) in the registry ValueInRegistry(lua_State* lua_, int index=-1) : lua{lua_} { lua_pushlightuserdata(lua, this); lua_pushvalue(lua, -1 + index); lua_settable(lua, LUA_REGISTRYINDEX); } // removing the function from the registry ~ValueInRegistry() { lua_pushlightuserdata(lua, this); lua_pushnil(lua); lua_settable(lua, LUA_REGISTRYINDEX); } // loads the value and puts it at the top of the stack PushedObject pop() { lua_pushlightuserdata(lua, this); lua_gettable(lua, LUA_REGISTRYINDEX); return PushedObject{lua, 1}; } ValueInRegistry(const ValueInRegistry&) = delete; ValueInRegistry& operator=(const ValueInRegistry&) = delete; private: lua_State* lua; }; // binds the first parameter of a function object template struct Binder { TFunctionObject function; TFirstParamType param; template auto operator()(TParams&&... params) -> decltype(function(param, std::forward(params)...)) { return function(param, std::forward(params)...); } }; // turns a type into a tuple // void is turned into std::tuple<> // existing tuples are untouched template struct Tupleizer; // this structure takes a pointer to a member function type and returns the base function type template struct RemoveMemberPointerFunction { typedef void type; }; // required because of a compiler bug // this structure takes any object and detects its function type template struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction::type::operator())>::type type; }; // this structure takes a function arguments list and has the "min" and the "max" static const member variables, whose value equal to the min and max number of parameters for the function // the only case where "min != max" is with boost::optional at the end of the list template struct FunctionArgumentsCounter {}; // true is the template parameter is a boost::optional template struct IsOptional : public std::false_type {}; }; /// @deprecated static LuaContext::EmptyArray_t ATTR_UNUSED LuaEmptyArray {}; /// @deprecated static LuaContext::Metatable_t ATTR_UNUSED LuaMetatable {}; /**************************************************/ /* PARTIAL IMPLEMENTATIONS */ /**************************************************/ template<> inline auto LuaContext::readTopAndPop(lua_State* /*state*/, PushedObject /*obj*/) -> void { } // this structure takes a template parameter T // if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple // we have to use this structure because std::tuple> triggers a bug in both MSVC++ and GCC template struct LuaContext::Tupleizer { typedef std::tuple type; }; template struct LuaContext::Tupleizer> { typedef std::tuple type; }; template<> struct LuaContext::Tupleizer { typedef std::tuple<> type; }; // this structure takes any object and detects its function type template struct LuaContext::FunctionTypeDetector { typedef TRetValue type(TParameters...); }; template struct LuaContext::FunctionTypeDetector { typedef typename FunctionTypeDetector::type type; }; // this structure takes a pointer to a member function type and returns the base function type template struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; template struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; template struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; template struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; // implementation of PusherTotalMinSize template struct LuaContext::PusherTotalMinSize { static const int size = Pusher::type>::minSize + PusherTotalMinSize::size; }; template<> struct LuaContext::PusherTotalMinSize<> { static const int size = 0; }; // implementation of PusherTotalMaxSize template struct LuaContext::PusherTotalMaxSize { static const int size = Pusher::type>::maxSize + PusherTotalMaxSize::size; }; template<> struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; }; // implementation of PusherMinSize template struct LuaContext::PusherMinSize { static const int size = Pusher::type>::minSize < Pusher::type>::minSize ? PusherMinSize::type, TTypes...>::size : PusherMinSize::type, TTypes...>::size; }; template struct LuaContext::PusherMinSize { static const int size = Pusher::type>::minSize; }; // implementation of PusherMaxSize template struct LuaContext::PusherMaxSize { static const int size = Pusher::type>::maxSize > PusherTotalMaxSize::size ? Pusher::type>::maxSize : PusherMaxSize::size; }; template<> struct LuaContext::PusherMaxSize<> { static const int size = 0; }; // implementation of FunctionArgumentsCounter template struct LuaContext::FunctionArgumentsCounter { typedef FunctionArgumentsCounter SubType; static const int min = (IsOptional::value && SubType::min == 0) ? 0 : 1 + SubType::min; static const int max = 1 + SubType::max; }; template<> struct LuaContext::FunctionArgumentsCounter<> { static const int min = 0; static const int max = 0; }; // implementation of IsOptional template struct LuaContext::IsOptional> : public std::true_type {}; // implementation of LuaFunctionCaller template class LuaContext::LuaFunctionCaller { static_assert(std::is_function::value, "Template parameter of LuaFunctionCaller must be a function type"); }; template class LuaContext::LuaFunctionCaller { public: TRetValue operator()(TParams&&... params) const { auto obj = valueHolder->pop(); return call(state, std::move(obj), std::forward(params)...); } private: std::shared_ptr valueHolder; lua_State* state; private: friend LuaContext; explicit LuaFunctionCaller(lua_State* state_, int index) : valueHolder(std::make_shared(state_, index)), state(state_) {} }; /**************************************************/ /* PUSH FUNCTIONS */ /**************************************************/ // specializations of the Pusher structure // opaque Lua references template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const LuaContext::LuaObject& value) noexcept { if (value.objectInRegistry.get()) { PushedObject obj = value.objectInRegistry->pop(); return obj; } else { lua_pushnil(state); return PushedObject{state, 1}; } } }; // boolean template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, bool value) noexcept { lua_pushboolean(state, value); return PushedObject{state, 1}; } }; // string template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const std::string& value) noexcept { lua_pushlstring(state, value.c_str(), value.length()); return PushedObject{state, 1}; } }; // const char* template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const char* value) noexcept { lua_pushstring(state, value); return PushedObject{state, 1}; } }; // const char[N] template struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const char* value) noexcept { lua_pushstring(state, value); return PushedObject{state, 1}; } }; // floating numbers template struct LuaContext::Pusher::value>::type> { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, T value) noexcept { lua_pushnumber(state, value); return PushedObject{state, 1}; } }; // integers template struct LuaContext::Pusher::value>::type> { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, T value) noexcept { lua_pushinteger(state, value); return PushedObject{state, 1}; } }; // nil template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, std::nullptr_t) noexcept { lua_pushnil(state); return PushedObject{state, 1}; } }; // empty arrays template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, EmptyArray_t) noexcept { lua_newtable(state); return PushedObject{state, 1}; } }; // std::type_info* is a lightuserdata template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept { lua_pushlightuserdata(state, const_cast(ptr)); return PushedObject{state, 1}; } }; // thread template<> struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept { lua_pushthread(value.state); return PushedObject{state, 1}; } }; // maps template struct LuaContext::Pusher> { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const std::map& value) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table key"); static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); auto obj = Pusher::push(state, EmptyArray); for (auto i = value.begin(), e = value.end(); i != e; ++i) setTable(state, obj, i->first, i->second); return obj; } }; // unordered_maps template struct LuaContext::Pusher> { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const std::unordered_map& value) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table key"); static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); auto obj = Pusher::push(state, EmptyArray); for (auto i = value.begin(), e = value.end(); i != e; ++i) setTable(state, obj, i->first, i->second); return obj; } }; // vectors of pairs template struct LuaContext::Pusher>> { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const std::vector>& value) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table key"); static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); auto obj = Pusher::push(state, EmptyArray); for (auto i = value.begin(), e = value.end(); i != e; ++i) setTable(state, obj, i->first, i->second); return obj; } }; // vectors template struct LuaContext::Pusher> { static const int minSize = 1; static const int maxSize = 1; static PushedObject push(lua_State* state, const std::vector& value) noexcept { static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); auto obj = Pusher::push(state, EmptyArray); for (unsigned int i = 0; i < value.size(); ++i) setTable(state, obj, i + 1, value[i]); return obj; } }; // unique_ptr template struct LuaContext::Pusher> { static const int minSize = Pusher>::minSize; static const int maxSize = Pusher>::maxSize; static PushedObject push(lua_State* state, std::unique_ptr value) noexcept { return Pusher>::push(state, std::move(value)); } }; // enum template struct LuaContext::Pusher::value>::type> { #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3) typedef typename std::underlying_type::type RealType; #else // implementation when std::underlying_type is not supported typedef unsigned long RealType; #endif static const int minSize = Pusher::minSize; static const int maxSize = Pusher::maxSize; static PushedObject push(lua_State* state, TEnum value) noexcept { return Pusher::push(state, static_cast(value)); } }; // any function // this specialization is not directly called, but is called by other specializations template struct LuaContext::Pusher { static const int minSize = 1; static const int maxSize = 1; // counts the number of arguments typedef FunctionArgumentsCounter LocalFunctionArgumentsCounter; // this is the version of "push" for non-trivially destructible function objects template static auto push(lua_State* state, TFunctionObject fn) noexcept -> typename std::enable_if::value, PushedObject>::type { // TODO: is_move_constructible not supported by some compilers //static_assert(std::is_move_constructible::value, "The function object must be move-constructible"); // when the lua script calls the thing we will push on the stack, we want "fn" to be executed // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems // so we use userdata instead // this function is called when the lua script tries to call our custom data type // we transfer execution to the "callback" function below const auto callCallback = [](lua_State* lua) -> int { assert(lua_gettop(lua) >= 1); assert(lua_isuserdata(lua, 1)); auto function = static_cast(lua_touserdata(lua, 1)); assert(function); return callback(lua, function, lua_gettop(lua) - 1).release(); }; // this one is called when lua's garbage collector no longer needs our custom data type // we call the function object's destructor const auto garbageCallback = [](lua_State* lua) -> int { assert(lua_gettop(lua) == 1); auto function = static_cast(lua_touserdata(lua, 1)); assert(function); function->~TFunctionObject(); return 0; }; // creating the object // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it // and that's what we do with placement-new const auto functionLocation = static_cast(lua_newuserdata(state, sizeof(TFunctionObject))); new (functionLocation) TFunctionObject(std::move(fn)); // creating the metatable (over the object on the stack) // lua_settable pops the key and value we just pushed, so stack management is easy // all that remains on the stack after these function calls is the metatable lua_newtable(state); lua_pushstring(state, "__call"); lua_pushcfunction(state, callCallback); lua_settable(state, -3); lua_pushstring(state, "__gc"); lua_pushcfunction(state, garbageCallback); lua_settable(state, -3); // at this point, the stack contains the object at offset -2 and the metatable at offset -1 // lua_setmetatable will bind the two together and pop the metatable // our custom function remains on the stack (and that's what we want) lua_setmetatable(state, -2); return PushedObject{state, 1}; } // this is the version of "push" for trivially destructible objects template static auto push(lua_State* state, TFunctionObject fn) noexcept -> typename std::enable_if::value, PushedObject>::type { // TODO: is_move_constructible not supported by some compilers //static_assert(std::is_move_constructible::value, "The function object must be move-constructible"); // when the lua script calls the thing we will push on the stack, we want "fn" to be executed // since "fn" doesn't need to be destroyed, we simply push it on the stack // this is the cfunction that is the callback const auto function = [](lua_State* state_) -> int { // the function object is an upvalue const auto toCall = static_cast(lua_touserdata(state_, lua_upvalueindex(1))); return callback(state_, toCall, lua_gettop(state_)).release(); }; // we copy the function object onto the stack const auto functionObjectLocation = static_cast(lua_newuserdata(state, sizeof(TFunctionObject))); new (functionObjectLocation) TFunctionObject(std::move(fn)); // pushing the function with the function object as upvalue lua_pushcclosure(state, function, 1); return PushedObject{state, 1}; } // this is the version of "push" for pointer to functions static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept -> PushedObject { // when the lua script calls the thing we will push on the stack, we want "fn" to be executed // since "fn" doesn't need to be destroyed, we simply push it on the stack // this is the cfunction that is the callback const auto function = [](lua_State* state_) -> int { // the function object is an upvalue const auto toCall = reinterpret_cast(lua_touserdata(state_, lua_upvalueindex(1))); return callback(state_, toCall, lua_gettop(state_)).release(); }; // we copy the function object onto the stack lua_pushlightuserdata(state, reinterpret_cast(fn)); // pushing the function with the function object as upvalue lua_pushcclosure(state, function, 1); return PushedObject{state, 1}; } // this is the version of "push" for references to functions static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept -> PushedObject { return push(state, &fn); } private: // callback that calls the function object // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back template static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount) -> PushedObject { // checking if number of parameters is correct if (argumentsCount < LocalFunctionArgumentsCounter::min) { // if not, using lua_error to return an error luaL_where(state, 1); lua_pushstring(state, "This function requires at least "); lua_pushnumber(state, LocalFunctionArgumentsCounter::min); lua_pushstring(state, " parameter(s)"); lua_concat(state, 4); luaError(state); } else if (argumentsCount > LocalFunctionArgumentsCounter::max) { // if not, using lua_error to return an error luaL_where(state, 1); lua_pushstring(state, "This function requires at most "); lua_pushnumber(state, LocalFunctionArgumentsCounter::max); lua_pushstring(state, " parameter(s)"); lua_concat(state, 4); luaError(state); } // calling the function try { return callback2(state, *toCall, argumentsCount); } catch (const WrongTypeException& ex) { // wrong parameter type, using lua_error to return an error luaL_where(state, 1); lua_pushstring(state, "Unable to convert parameter from "); lua_pushstring(state, ex.luaType.c_str()); lua_pushstring(state, " to "); lua_pushstring(state, ex.destination.name()); lua_concat(state, 5); luaError(state); } catch (const std::exception& e) { luaL_where(state, 1); lua_pushstring(state, "Caught exception: "); lua_pushstring(state, e.what()); lua_concat(state, 3); luaError(state); } catch (...) { Pusher::push(state, std::current_exception()).release(); luaError(state); } } template static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount) -> typename std::enable_if::value && !std::is_void::value, PushedObject>::type { // pushing the result on the stack and returning number of pushed elements typedef Pusher::type> P; return P::push(state, readIntoFunction(state, tag{}, toCall, -argumentsCount, tag{}...)); } template static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount) -> typename std::enable_if::value && !std::is_void::value, PushedObject>::type { readIntoFunction(state, tag{}, toCall, -argumentsCount, tag{}...); return PushedObject{state, 0}; } }; // C function pointers template struct LuaContext::Pusher { // using the function-pushing implementation typedef Pusher SubPusher; static const int minSize = SubPusher::minSize; static const int maxSize = SubPusher::maxSize; template static PushedObject push(lua_State* state, TType value) noexcept { return SubPusher::push(state, value); } }; // C function references template struct LuaContext::Pusher { // using the function-pushing implementation typedef Pusher SubPusher; static const int minSize = SubPusher::minSize; static const int maxSize = SubPusher::maxSize; template static PushedObject push(lua_State* state, TType value) noexcept { return SubPusher::push(state, value); } }; // std::function template struct LuaContext::Pusher> { // using the function-pushing implementation typedef Pusher SubPusher; static const int minSize = SubPusher::minSize; static const int maxSize = SubPusher::maxSize; static PushedObject push(lua_State* state, const std::function& value) noexcept { return SubPusher::push(state, value); } }; // boost::variant template struct LuaContext::Pusher> { static const int minSize = PusherMinSize::size; static const int maxSize = PusherMaxSize::size; static PushedObject push(lua_State* state, const boost::variant& value) noexcept { PushedObject obj{state, 0}; VariantWriter writer{state, obj}; value.apply_visitor(writer); return obj; } private: struct VariantWriter : public boost::static_visitor<> { template void operator()(TType value) noexcept { obj = Pusher::type>::push(state, std::move(value)); } VariantWriter(lua_State* state_, PushedObject& obj_) : state(state_), obj(obj_) {} lua_State* state; PushedObject& obj; }; }; // boost::optional template struct LuaContext::Pusher> { typedef Pusher::type> UnderlyingPusher; static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1; static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1; static PushedObject push(lua_State* state, const boost::optional& value) noexcept { if (value) { return UnderlyingPusher::push(state, value.get()); } else { lua_pushnil(state); return PushedObject{state, 1}; } } }; // tuple template struct LuaContext::Pusher> { // TODO: NOT EXCEPTION SAFE /!\ // static const int minSize = PusherTotalMinSize::size; static const int maxSize = PusherTotalMaxSize::size; static PushedObject push(lua_State* state, const std::tuple& value) noexcept { return PushedObject{state, push2(state, value, std::integral_constant{})}; } static PushedObject push(lua_State* state, std::tuple&& value) noexcept { return PushedObject{state, push2(state, std::move(value), std::integral_constant{})}; } private: template static int push2(lua_State* state, const std::tuple& value, std::integral_constant) noexcept { typedef typename std::tuple_element>::type ElemType; return Pusher::type>::push(state, std::get(value)).release() + push2(state, value, std::integral_constant{}); } template static int push2(lua_State* state, std::tuple&& value, std::integral_constant) noexcept { typedef typename std::tuple_element>::type ElemType; return Pusher::type>::push(state, std::move(std::get(value))).release() + push2(state, std::move(value), std::integral_constant{}); } static int push2(lua_State* /*state*/, const std::tuple&, std::integral_constant) noexcept { return 0; } static int push2(lua_State* /*state*/, std::tuple&&, std::integral_constant) noexcept { return 0; } }; /**************************************************/ /* READ FUNCTIONS */ /**************************************************/ // specializations of the Reader structures // opaque Lua references template<> struct LuaContext::Reader { static auto read(lua_State* state, int index) -> boost::optional { LuaContext::LuaObject obj(state, index); return obj; } }; // reading null template<> struct LuaContext::Reader { static auto read(lua_State* state, int index) -> boost::optional { if (!lua_isnil(state, index)) return boost::none; return nullptr; } }; // integrals template struct LuaContext::Reader< TType, typename std::enable_if::value>::type > { static auto read(lua_State* state, int index) -> boost::optional { # if LUA_VERSION_NUM >= 502 int success; auto value = lua_tointegerx(state, index, &success); if (success == 0) return boost::none; return static_cast(value); # else if (!lua_isnumber(state, index)) return boost::none; return static_cast(lua_tointeger(state, index)); # endif } }; // floating points template struct LuaContext::Reader< TType, typename std::enable_if::value>::type > { static auto read(lua_State* state, int index) -> boost::optional { # if LUA_VERSION_NUM >= 502 int success; auto value = lua_tonumberx(state, index, &success); if (success == 0) return boost::none; return static_cast(value); # else if (!lua_isnumber(state, index)) return boost::none; return static_cast(lua_tonumber(state, index)); # endif } }; // boolean template<> struct LuaContext::Reader { static auto read(lua_State* state, int index) -> boost::optional { if (!lua_isboolean(state, index)) return boost::none; return lua_toboolean(state, index) != 0; } }; // string // lua_tostring returns a temporary pointer, but that's not a problem since we copy // the data into a std::string template<> struct LuaContext::Reader { static auto read(lua_State* state, int index) -> boost::optional { std::string result; // lua_tolstring might convert the variable that would confuse lua_next, so we // make a copy of the variable. lua_pushvalue(state, index); size_t len; const auto val = lua_tolstring(state, -1, &len); if (val != nullptr) result.assign(val, len); lua_pop(state, 1); return val != nullptr ? boost::optional{ std::move(result) } : boost::none; } }; // enums template struct LuaContext::Reader< TType, typename std::enable_if::value>::type > { static auto read(lua_State* state, int index) -> boost::optional { if (!lua_isnumber(state, index) || fmod(lua_tonumber(state, index), 1.) != 0) return boost::none; return static_cast(lua_tointeger(state, index)); } }; // LuaFunctionCaller template struct LuaContext::Reader> { typedef LuaFunctionCaller ReturnType; static auto read(lua_State* state, int index) -> boost::optional { if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0) return boost::none; return ReturnType(state, index); } }; // function template struct LuaContext::Reader> { static auto read(lua_State* state, int index) -> boost::optional> { if (auto val = Reader>::read(state, index)) { std::function f{*val}; return boost::optional>{std::move(f)}; } return boost::none; } }; // vector of pairs template struct LuaContext::Reader>> { static auto read(lua_State* state, int index) -> boost::optional>> { if (!lua_istable(state, index)) return boost::none; std::vector> result; // we traverse the table at the top of the stack lua_pushnil(state); // first key while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) { // now a key and its value are pushed on the stack try { auto val1 = Reader::read(state, -2); auto val2 = Reader::read(state, -1); if (!val1.is_initialized() || !val2.is_initialized()) { lua_pop(state, 2); // we remove the value and the key return {}; } result.push_back({ val1.get(), val2.get() }); lua_pop(state, 1); // we remove the value but keep the key for the next iteration } catch(...) { lua_pop(state, 2); // we remove the value and the key return {}; } } return { std::move(result) }; } }; // map template struct LuaContext::Reader> { static auto read(lua_State* state, int index) -> boost::optional> { if (!lua_istable(state, index)) return boost::none; std::map result; // we traverse the table at the top of the stack lua_pushnil(state); // first key while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) { // now a key and its value are pushed on the stack try { auto key = Reader::read(state, -2); auto value = Reader::read(state, -1); if (!key.is_initialized() || !value.is_initialized()) { lua_pop(state, 2); // we remove the value and the key return {}; } result.insert({ key.get(), value.get() }); lua_pop(state, 1); // we remove the value but keep the key for the next iteration } catch(...) { lua_pop(state, 2); // we remove the value and the key return {}; } } return { std::move(result) }; } }; // unordered_map template struct LuaContext::Reader> { static auto read(lua_State* state, int index) -> boost::optional> { if (!lua_istable(state, index)) return boost::none; std::unordered_map result; // we traverse the table at the top of the stack lua_pushnil(state); // first key while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) { // now a key and its value are pushed on the stack try { auto key = Reader::read(state, -2); auto value = Reader::read(state, -1); if (!key.is_initialized() || !value.is_initialized()) { lua_pop(state, 2); // we remove the value and the key return {}; } result.insert({ key.get(), value.get() }); lua_pop(state, 1); // we remove the value but keep the key for the next iteration } catch(...) { lua_pop(state, 2); // we remove the value and the key return {}; } } return { std::move(result) }; } }; // optional // IMPORTANT: optional means "either nil or the value of the right type" // * if the value is nil, then an optional containing an empty optional is returned // * if the value is of the right type, then an optional containing an optional containing the value is returned // * if the value is of the wrong type, then an empty optional is returned template struct LuaContext::Reader> { static auto read(lua_State* state, int index) -> boost::optional> { if (lua_isnil(state, index)) return boost::optional{boost::none}; if (auto&& other = Reader::read(state, index)) return std::move(other); return boost::none; } }; // variant template struct LuaContext::Reader> { typedef boost::variant ReturnType; private: // class doing operations for a range of types from TIterBegin to TIterEnd template struct VariantReader { using SubReader = Reader::type>::type>; static auto read(lua_State* state, int index) -> boost::optional { // note: using SubReader::read triggers a compilation error when used with a reference if (const auto val = SubReader::read(state, index)) return boost::variant{*val}; return VariantReader::type, TIterEnd>::read(state, index); } }; // specialization of class above being called when list of remaining types is empty template struct VariantReader::type::value == 0>::type> { static auto read(lua_State* /*state*/, int /*index*/) -> boost::optional { return boost::none; } }; // this is the main type typedef VariantReader::type, typename boost::mpl::end::type> MainVariantReader; public: static auto read(lua_State* state, int index) -> boost::optional { return MainVariantReader::read(state, index); } }; // reading a tuple // tuple have an additional argument for their functions, that is the maximum size to read // if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value template<> struct LuaContext::Reader> { static auto read(lua_State* /*state*/, int /*index*/, int /*maxSize*/ = 0) -> boost::optional> { return std::tuple<>{}; } }; template struct LuaContext::Reader, typename std::enable_if::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler > { // this is the "TFirst is NOT default constructible" version typedef std::tuple ReturnType; static auto read(lua_State* state, int index, int maxSize = std::tuple_size::value) -> boost::optional { if (maxSize <= 0) return boost::none; auto firstVal = Reader::read(state, index); auto othersVal = Reader>::read(state, index + 1, maxSize - 1); if (!firstVal || !othersVal) return boost::none; return std::tuple_cat(std::tuple(*firstVal), std::move(*othersVal)); } }; template struct LuaContext::Reader, typename std::enable_if::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler > { // this is the "TFirst is default-constructible" version typedef std::tuple ReturnType; static auto read(lua_State* state, int index, int maxSize = std::tuple_size::value) -> boost::optional { auto othersVal = Reader>::read(state, index + 1, maxSize - 1); if (!othersVal) return boost::none; if (maxSize <= 0) return std::tuple_cat(std::tuple(), std::move(*othersVal)); auto firstVal = Reader::read(state, index); if (!firstVal) return boost::none; return std::tuple_cat(std::tuple(*firstVal), std::move(*othersVal)); } }; #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif #endif weakforced-2.10.2/ext/ext/murmur3.cc000066400000000000000000000035211461473602600172410ustar00rootroot00000000000000//----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. // Note - The x86 and x64 versions do _not_ produce the same results, as the // algorithms are optimized for their respective platforms. You can still // compile and run any of them on any platform, but your performance with the // non-native version will be less than optimal. #include "murmur3.h" #include #include //----------------------------------------------------------------------------- // Finalization mix - force all bits of a hash block to avalanche uint32_t fmix32( uint32_t h ) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } //----------------------------------------------------------------------------- void MurmurHash3_x86_32( const void * key, int len, uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 4; int i; uint32_t h1 = seed; uint32_t c1 = 0xcc9e2d51; uint32_t c2 = 0x1b873593; //---------- // body const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); for(i = -nblocks; i; i++) { uint32_t k1 = getblock(blocks,i); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,13); h1 = h1*5+0xe6546b64; } //---------- // tail { const uint8_t * tail = (const uint8_t*)(data + nblocks*4); uint32_t k1 = 0; switch(len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; } //---------- // finalization h1 ^= len; h1 = fmix32(h1); *(uint32_t*)out = h1; } weakforced-2.10.2/ext/ext/murmur3.h000077500000000000000000000051551461473602600171130ustar00rootroot00000000000000#pragma once #ifndef MURMUR3_H #define MURMUR3_H //----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. // Note - The x86 and x64 versions do _not_ produce the same results, as the // algorithms are optimized for their respective platforms. You can still // compile and run any of them on any platform, but your performance with the // non-native version will be less than optimal. //----------------------------------------------------------------------------- // Platform-specific functions and macros // Microsoft Visual Studio #if defined(_MSC_VER) typedef unsigned char uint8_t; typedef unsigned long uint32_t; typedef unsigned __int64 uint64_t; // Other compilers #else // defined(_MSC_VER) #include #endif // !defined(_MSC_VER) #define FORCE_INLINE __attribute__((always_inline)) inline uint32_t rotl32 ( uint32_t x, uint8_t r ) { return (x << r) | (x >> (32 - r)); } #define ROTL32(x,y) rotl32(x,y) #define BIG_CONSTANT(x) (x##LLU) /* NO-OP for little-endian platforms */ #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define BYTESWAP(x) (x) # endif /* if __BYTE_ORDER__ is not predefined (like FreeBSD), use arch */ #elif defined(__i386) || defined(__x86_64) \ || defined(__alpha) || defined(__vax) # define BYTESWAP(x) (x) /* use __builtin_bswap32 if available */ #elif defined(__GNUC__) || defined(__clang__) #ifdef __has_builtin #if __has_builtin(__builtin_bswap32) #define BYTESWAP(x) __builtin_bswap32(x) #endif // __has_builtin(__builtin_bswap32) #endif // __has_builtin #endif // defined(__GNUC__) || defined(__clang__) /* last resort (big-endian w/o __builtin_bswap) */ #ifndef BYTESWAP # define BYTESWAP(x) ((((x)&0xFF)<<24) \ |(((x)>>24)&0xFF) \ |(((x)&0x0000FF00)<<8) \ |(((x)&0x00FF0000)>>8) ) #endif //----------------------------------------------------------------------------- // Block read - if your platform needs to do endian-swapping or can only // handle aligned reads, do the conversion here #define getblock(p, i) BYTESWAP(p[i]) //----------------------------------------------------------------------------- // Finalization mix - force all bits of a hash block to avalanche uint32_t fmix32( uint32_t h ); //----------------------------------------------------------------------------- #ifdef __cplusplus extern "C" #else extern #endif void MurmurHash3_x86_32( const void * key, int len, uint32_t seed, void * out ); #endif weakforced-2.10.2/ext/ext/scheduler/000077500000000000000000000000001461473602600172755ustar00rootroot00000000000000weakforced-2.10.2/ext/ext/scheduler/Cron.h000066400000000000000000000102471461473602600203530ustar00rootroot00000000000000/* Author: Bosma MIT License Copyright (c) 2017 Bosma Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include namespace Bosma { using Clock = std::chrono::system_clock; inline void add(std::tm &tm, Clock::duration time) { auto tp = Clock::from_time_t(std::mktime(&tm)); auto tp_adjusted = tp + time; auto tm_adjusted = Clock::to_time_t(tp_adjusted); tm = *std::localtime(&tm_adjusted); } inline void verify_and_set(const std::string& token, const std::string &expression, int &field, const int lower_bound, const int upper_bound, const bool adjust = false) { if (token == "*") field = -1; else { field = std::stoi(token); if (field < lower_bound || field > upper_bound) throw std::runtime_error("cron out of range: " + expression); if (adjust) field--; } } class Cron { public: explicit Cron(const std::string &expression) { std::istringstream iss(expression); std::vector tokens{std::istream_iterator{iss}, std::istream_iterator{}}; if (tokens.size() != 5) throw std::runtime_error("malformed cron string: " + expression); verify_and_set(tokens[0], expression, minute, 0, 59); verify_and_set(tokens[1], expression, hour, 0, 23); verify_and_set(tokens[2], expression, day, 1, 31); verify_and_set(tokens[3], expression, month, 1, 12, true); verify_and_set(tokens[4], expression, day_of_week, 0, 6); } // http://stackoverflow.com/a/322058/1284550 Clock::time_point cron_to_next() const { // get current time as a tm object auto now = Clock::to_time_t(Clock::now()); std::tm next(*std::localtime(&now)); // it will always at least run the next minute next.tm_sec = 0; add(next, std::chrono::minutes(1)); while (true) { if (month != -1 && next.tm_mon != month) { // add a month // if this will bring us over a year, increment the year instead and reset the month if (next.tm_mon + 1 > 11) { next.tm_mon = 0; next.tm_year++; } else next.tm_mon++; next.tm_mday = 1; next.tm_hour = 0; next.tm_min = 0; continue; } if (day != -1 && next.tm_mday != day) { add(next, std::chrono::hours(24)); next.tm_hour = 0; next.tm_min = 0; continue; } if (day_of_week != -1 && next.tm_wday != day_of_week) { add(next, std::chrono::hours(24)); next.tm_hour = 0; next.tm_min = 0; continue; } if (hour != -1 && next.tm_hour != hour) { add(next, std::chrono::hours(1)); next.tm_min = 0; continue; } if (minute != -1 && next.tm_min != minute) { add(next, std::chrono::minutes(1)); continue; } break; } // telling mktime to figure out dst next.tm_isdst = -1; return Clock::from_time_t(std::mktime(&next)); } int minute, hour, day, month, day_of_week; }; } weakforced-2.10.2/ext/ext/scheduler/InterruptableSleep.h000066400000000000000000000054341461473602600232650ustar00rootroot00000000000000/* Author: Bosma MIT License Copyright (c) 2017 Bosma Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include namespace Bosma { class InterruptableSleep { using Clock = std::chrono::system_clock; // InterruptableSleep offers a sleep that can be interrupted by any thread. // It can be interrupted multiple times // and be interrupted before any sleep is called (the sleep will immediately complete) // Has same interface as condition_variables and futures, except with sleep instead of wait. // For a given object, sleep can be called on multiple threads safely, but is not recommended as behaviour is undefined. public: InterruptableSleep() : interrupted(false) { } InterruptableSleep(const InterruptableSleep &) = delete; InterruptableSleep(InterruptableSleep &&) noexcept = delete; ~InterruptableSleep() noexcept = default; InterruptableSleep& operator=(const InterruptableSleep &) noexcept = delete; InterruptableSleep& operator=(InterruptableSleep &&) noexcept = delete; void sleep_for(Clock::duration duration) { std::unique_lock ul(m); cv.wait_for(ul, duration, [this] { return interrupted; }); interrupted = false; } void sleep_until(Clock::time_point time) { std::unique_lock ul(m); cv.wait_until(ul, time, [this] { return interrupted; }); interrupted = false; } void sleep() { std::unique_lock ul(m); cv.wait(ul, [this] { return interrupted; }); interrupted = false; } void interrupt() { std::lock_guard lg(m); interrupted = true; cv.notify_one(); } private: bool interrupted; std::mutex m; std::condition_variable cv; }; } weakforced-2.10.2/ext/ext/scheduler/Scheduler.h000066400000000000000000000205371461473602600213730ustar00rootroot00000000000000/* Author: Bosma MIT License Copyright (c) 2017 Bosma Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "ext/ctpl.h" #include "ext/threadname.hh" #include "InterruptableSleep.h" #include "Cron.h" namespace Bosma { using Clock = std::chrono::system_clock; class Task { public: explicit Task(std::function &&f, bool recur = false, bool interval = false) : f(std::move(f)), recur(recur), interval(interval) {} virtual ~Task() {} virtual Clock::time_point get_new_time() const = 0; std::function f; bool recur; bool interval; }; class InTask : public Task { public: explicit InTask(std::function &&f) : Task(std::move(f)) {} // dummy time_point because it's not used Clock::time_point get_new_time() const override { return Clock::time_point(Clock::duration(0)); } }; class EveryTask : public Task { public: EveryTask(Clock::duration time, std::function &&f, bool interval = false) : Task(std::move(f), true, interval), time(time) {} Clock::time_point get_new_time() const override { return Clock::now() + time; }; Clock::duration time; }; class CronTask : public Task { public: CronTask(const std::string &expression, std::function &&f) : Task(std::move(f), true), cron(expression) {} Clock::time_point get_new_time() const override { return cron.cron_to_next(); }; Cron cron; }; inline bool try_parse(std::tm &tm, const std::string& expression, const std::string& format) { std::stringstream ss(expression); return !(ss >> std::get_time(&tm, format.c_str())).fail(); } class Scheduler { public: explicit Scheduler(unsigned int max_n_tasks = 4) : done(false), threads(max_n_tasks + 1) { threads.push([this](int) { thread_local bool init=false; if (!init) { setThreadName("wf/scheduler"); init = true; } while (!done) { if (tasks.empty()) { sleeper.sleep(); } else { auto time_of_first_task = (*tasks.begin()).first; sleeper.sleep_until(time_of_first_task); } std::lock_guard l(lock); manage_tasks(); } }); } Scheduler(const Scheduler &) = delete; Scheduler(Scheduler &&) noexcept = delete; Scheduler &operator=(const Scheduler &) = delete; Scheduler &operator=(Scheduler &&) noexcept = delete; ~Scheduler() { done = true; sleeper.interrupt(); } void setNumThreads(int numThreads) { threads.resize(numThreads); } template void in(const Clock::time_point time, _Callable &&f, _Args &&... args) { std::shared_ptr t = std::make_shared(std::bind(std::forward<_Callable>(f), std::forward<_Args>(args)...)); add_task(time, std::move(t)); } template void in(const Clock::duration time, _Callable &&f, _Args &&... args) { in(Clock::now() + time, std::forward<_Callable>(f), std::forward<_Args>(args)...); } template void at(const std::string &time, _Callable &&f, _Args &&... args) { // get current time as a tm object auto time_now = Clock::to_time_t(Clock::now()); std::tm tm = *std::localtime(&time_now); // our final time as a time_point Clock::time_point tp; if (try_parse(tm, time, "%H:%M:%S")) { // convert tm back to time_t, then to a time_point and assign to final tp = Clock::from_time_t(std::mktime(&tm)); // if we've already passed this time, the user will mean next day, so add a day. if (Clock::now() >= tp) tp += std::chrono::hours(24); } else if (try_parse(tm, time, "%Y-%m-%d %H:%M:%S")) { tp = Clock::from_time_t(std::mktime(&tm)); } else if (try_parse(tm, time, "%Y/%m/%d %H:%M:%S")) { tp = Clock::from_time_t(std::mktime(&tm)); } else { // could not parse time throw std::runtime_error("Cannot parse time string: " + time); } in(tp, std::forward<_Callable>(f), std::forward<_Args>(args)...); } template void every(const Clock::duration time, _Callable &&f, _Args &&... args) { std::shared_ptr t = std::make_shared(time, std::bind(std::forward<_Callable>(f), std::forward<_Args>(args)...)); auto next_time = t->get_new_time(); add_task(next_time, std::move(t)); } // expression format: // from https://en.wikipedia.org/wiki/Cron#Overview // ┌───────────── minute (0 - 59) // │ ┌───────────── hour (0 - 23) // │ │ ┌───────────── day of month (1 - 31) // │ │ │ ┌───────────── month (1 - 12) // │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday) // │ │ │ │ │ // │ │ │ │ │ // * * * * * template void cron(const std::string &expression, _Callable &&f, _Args &&... args) { std::shared_ptr t = std::make_shared(expression, std::bind(std::forward<_Callable>(f), std::forward<_Args>(args)...)); auto next_time = t->get_new_time(); add_task(next_time, std::move(t)); } template void interval(const Clock::duration time, _Callable &&f, _Args &&... args) { std::shared_ptr t = std::make_shared(time, std::bind(std::forward<_Callable>(f), std::forward<_Args>(args)...), true); auto next_time = t->get_new_time(); add_task(next_time, std::move(t)); } private: std::atomic done; Bosma::InterruptableSleep sleeper; ctpl::thread_pool threads; std::multimap> tasks; std::mutex lock; void add_task(const Clock::time_point time, std::shared_ptr t) { std::lock_guard l(lock); tasks.emplace(time, std::move(t)); sleeper.interrupt(); } void manage_tasks() { auto end_of_tasks_to_run = tasks.upper_bound(Clock::now()); // if there are any tasks to be run and removed if (end_of_tasks_to_run != tasks.begin()) { decltype(tasks) recurred_tasks; for (auto i = tasks.begin(); i != end_of_tasks_to_run; ++i) { auto &task = (*i).second; if (task->interval) { // if it's an interval task, add the task back after f() is completed threads.push([this, task](int) { task->f(); add_task(task->get_new_time(), task); }); } else { threads.push([task](int) { task->f(); }); // calculate time of next run and add the new task to the tasks to be recurred if (task->recur) recurred_tasks.emplace(task->get_new_time(), std::move(task)); } } // remove the completed tasks tasks.erase(tasks.begin(), end_of_tasks_to_run); // re-add the tasks that are recurring for (auto &task : recurred_tasks) tasks.emplace(task.first, std::move(task.second)); } } }; } weakforced-2.10.2/ext/ext/threadname.cc000066400000000000000000000033111461473602600177340ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #if HAVE_PTHREAD_NP_H #include #endif #include "threadname.hh" void setThreadName(const std::string& threadName) { #ifdef HAVE_PTHREAD_SETNAME_NP_2 pthread_setname_np(pthread_self(), threadName.c_str()); #endif #ifdef HAVE_PTHREAD_SET_NAME_NP_2 pthread_set_name_np(pthread_self(), threadName.c_str()); #endif #ifdef HAVE_PTHREAD_SET_NAME_NP_2_VOID pthread_set_name_np(pthread_self(), threadName.c_str()); #endif #ifdef HAVE_PTHREAD_SETNAME_NP_1 pthread_setname_np(threadName.c_str()); #endif #ifdef HAVE_PTHREAD_SETNAME_NP_3 pthread_setname_np(pthread_self(), threadName.c_str(), nullptr); #endif } weakforced-2.10.2/ext/ext/threadname.hh000066400000000000000000000021151461473602600177470ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include void setThreadName(const std::string& threadName); weakforced-2.10.2/ext/ext/uap-cpp/000077500000000000000000000000001461473602600166645ustar00rootroot00000000000000weakforced-2.10.2/ext/ext/uap-cpp/UaParser.cpp000066400000000000000000000211031461473602600211070ustar00rootroot00000000000000#include "UaParser.h" #include #include #include #include #include #include #include #include #include namespace { typedef std::map i2tuple; struct GenericStore { std::string replacement; i2tuple replacementMap; boost::regex regExpr; }; struct DeviceStore : GenericStore { std::string brandReplacement; std::string modelReplacement; i2tuple brandReplacementMap; i2tuple modelReplacementMap; }; struct AgentStore : GenericStore { std::string majorVersionReplacement; std::string minorVersionReplacement; std::string patchVersionReplacement; std::string patchMinorVersionReplacement; }; void mark_placeholders(i2tuple& replacement_map, const std::string& device_property) { auto loc = device_property.rfind("$"); while (loc != std::string::npos) { const auto replacement = device_property.substr(loc + 1, 1); replacement_map[loc] = strtol(replacement.c_str(), nullptr, 10); if (loc < 2) break; loc = device_property.rfind("$", loc - 2); } return; } DeviceStore fill_device_store(const YAML::Node& device_parser) { DeviceStore device; bool regex_flag = false; for (auto it = device_parser.begin(); it != device_parser.end(); ++it) { const auto key = it->first.as(); const auto value = it->second.as(); if (key == "regex") { device.regExpr.assign(value, boost::regex::optimize | boost::regex::normal); } else if (key == "regex_flag" && value == "i") { regex_flag = true; } else if (key == "device_replacement") { device.replacement = value; mark_placeholders(device.replacementMap, device.replacement); } else if (key == "model_replacement") { device.modelReplacement = value; mark_placeholders(device.modelReplacementMap, device.modelReplacement); } else if (key == "brand_replacement") { device.brandReplacement = value; mark_placeholders(device.brandReplacementMap, device.brandReplacement); } else { // Ignore, don't barf. We shouldn't let unexpected keys stop us from starting // assert(false); } } if (regex_flag == true) { device.regExpr.assign(device.regExpr.str(), boost::regex::optimize | boost::regex::icase | boost::regex::normal); } return device; } AgentStore fill_agent_store(const YAML::Node& node, const std::string& repl, const std::string& major_repl, const std::string& minor_repl, const std::string& patch_repl) { AgentStore agent_store; assert(node.Type() == YAML::NodeType::Map); for (auto it = node.begin(); it != node.end(); ++it) { const auto key = it->first.as(); const auto value = it->second.as(); if (key == "regex") { agent_store.regExpr.assign(value, boost::regex::optimize | boost::regex::normal); } else if (key == repl) { agent_store.replacement = value; mark_placeholders(agent_store.replacementMap, agent_store.replacement); } else if (key == major_repl && !value.empty()) { agent_store.majorVersionReplacement = value; } else if (key == minor_repl && !value.empty()) { agent_store.minorVersionReplacement = value; } else if (key == patch_repl && !value.empty()) { agent_store.patchVersionReplacement = value; } else { // Ignore, don't barf. We shouldn't let unexpected keys stop us from starting // assert(false); } } return agent_store; } struct UAStore { explicit UAStore(const std::string& regexes_file_path) { auto regexes = YAML::LoadFile(regexes_file_path); const auto& user_agent_parsers = regexes["user_agent_parsers"]; for (const auto& user_agent : user_agent_parsers) { const auto browser = fill_agent_store(user_agent, "family_replacement", "v1_replacement", "v2_replacement", "v3_replacement"); browserStore.push_back(browser); } const auto& os_parsers = regexes["os_parsers"]; for (const auto& o : os_parsers) { const auto os = fill_agent_store(o, "os_replacement", "os_v1_replacement", "os_v2_replacement", "os_v3_replacement"); osStore.push_back(os); } const auto& device_parsers = regexes["device_parsers"]; for (const auto& device_parser : device_parsers) { deviceStore.push_back(fill_device_store(device_parser)); } } std::vector deviceStore; std::vector osStore; std::vector browserStore; }; ///////////// // HELPERS // ///////////// void replace_all_placeholders(std::string& ua_property, const boost::smatch& result, const i2tuple& replacement_map) { for (auto iter = replacement_map.rbegin(); iter != replacement_map.rend(); ++iter) { ua_property.replace(iter->first, 2, result[iter->second].str()); } boost::algorithm::trim(ua_property); return; } Device parse_device_impl(const std::string& ua, const UAStore* ua_store) { Device device; for (const auto& d : ua_store->deviceStore) { boost::smatch m; if (boost::regex_search(ua, m, d.regExpr)) { if (d.replacement.empty() && m.size() > 1) { device.family = m[1].str(); } else { device.family = d.replacement; if (!d.replacementMap.empty()) { replace_all_placeholders(device.family, m, d.replacementMap); } } if (!d.brandReplacement.empty()) { device.brand = d.brandReplacement; if (!d.brandReplacementMap.empty()) { replace_all_placeholders(device.brand, m, d.brandReplacementMap); } } if (d.modelReplacement.empty() && m.size() > 1) { device.model = m[1].str(); } else { device.model = d.modelReplacement; if (!d.modelReplacementMap.empty()) { replace_all_placeholders(device.model, m, d.modelReplacementMap); } } break; } else { device.family = "Other"; } } return device; } template void fill_agent(AGENT& agent, const AGENT_STORE& store, const boost::smatch& m, const bool os) { if (m.size() > 1) { agent.family = !store.replacement.empty() ? boost::regex_replace(store.replacement, boost::regex("\\$1"), m[1].str()) : m[1]; } else { agent.family = !store.replacement.empty() ? boost::regex_replace(store.replacement, boost::regex("\\$1"), m[0].str()) : m[0]; } boost::algorithm::trim(agent.family); // The chunk above is slightly faster than the one below. // if ( store.replacement.empty() && m.size() > 1) { // agent.family = m[1].str(); // } else { // agent.family = store.replacement; // if ( ! store.replacementMap.empty()) { // replace_all_placeholders(agent.family,m,store.replacementMap); // } // } if (!store.majorVersionReplacement.empty()) { agent.major = store.majorVersionReplacement; } else if (m.size() > 2) { agent.major = m[2].str(); } if (!store.minorVersionReplacement.empty()) { agent.minor = store.minorVersionReplacement; } else if (m.size() > 3) { agent.minor = m[3].str(); } if (!store.patchVersionReplacement.empty()) { agent.patch = store.patchVersionReplacement; } else if (m.size() > 4) { agent.patch = m[4].str(); } if (os && m.size() > 5) { agent.patch_minor = m[5].str(); } } Agent parse_browser_impl(const std::string& ua, const UAStore* ua_store) { Agent browser; for (const auto& b : ua_store->browserStore) { boost::smatch m; if (boost::regex_search(ua, m, b.regExpr)) { fill_agent(browser, b, m, false); break; } else { browser.family = "Other"; } } return browser; } Agent parse_os_impl(const std::string& ua, const UAStore* ua_store) { Agent os; for (const auto& o : ua_store->osStore) { boost::smatch m; if (boost::regex_search(ua, m, o.regExpr)) { fill_agent(os, o, m, true); break; } else { os.family = "Other"; } } return os; } } // namespace UserAgentParser::UserAgentParser(const std::string& regexes_file_path) : regexes_file_path_{regexes_file_path} { ua_store_ = new UAStore(regexes_file_path); } UserAgentParser::~UserAgentParser() { delete static_cast(ua_store_); } UserAgent UserAgentParser::parse(const std::string& ua) const { const auto ua_store = static_cast(ua_store_); const auto device = parse_device_impl(ua, ua_store); const auto os = parse_os_impl(ua, ua_store); const auto browser = parse_browser_impl(ua, ua_store); return {device, os, browser}; } weakforced-2.10.2/ext/ext/uap-cpp/UaParser.h000066400000000000000000000017201461473602600205570ustar00rootroot00000000000000#pragma once #include struct Generic { std::string family; }; struct Device : Generic { std::string model; std::string brand; }; struct Agent : Generic { std::string major; std::string minor; std::string patch; std::string patch_minor; std::string toString() const { return family + " " + toVersionString(); } std::string toVersionString() const { return (major.empty() ? "0" : major) + "." + (minor.empty() ? "0" : minor) + "." + (patch.empty() ? "0" : patch); } }; struct UserAgent { Device device; Agent os; Agent browser; std::string toFullString() const { return browser.toString() + "/" + os.toString(); } bool isSpider() const { return device.family == "Spider"; } }; class UserAgentParser { public: explicit UserAgentParser(const std::string& regexes_file_path); UserAgent parse(const std::string&) const; ~UserAgentParser(); private: const std::string regexes_file_path_; const void* ua_store_; }; weakforced-2.10.2/ext/json11/000077500000000000000000000000001461473602600156325ustar00rootroot00000000000000weakforced-2.10.2/ext/json11/Makefile.am000066400000000000000000000002641461473602600176700ustar00rootroot00000000000000noinst_LTLIBRARIES=libjson11.la noinst_HEADERS=json11.hpp libjson11_la_CXXFLAGS=-W -Wall $(RELRO_CFLAGS) $(PIE_CFLAGS) -D__STRICT_ANSI__ libjson11_la_SOURCES=json11.hpp json11.cpp weakforced-2.10.2/ext/json11/json11.cpp000066400000000000000000000606611461473602600174620ustar00rootroot00000000000000/* Copyright (c) 2013 Dropbox, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "json11.hpp" #include #include #include #include #include #include namespace json11 { static const int max_depth = 200; using std::string; using std::vector; using std::map; using std::make_shared; using std::initializer_list; using std::move; /* Helper for representing null - just a do-nothing struct, plus comparison * operators so the helpers in JsonValue work. We can't use nullptr_t because * it may not be orderable. */ struct NullStruct { bool operator==(NullStruct) const { return true; } bool operator<(NullStruct) const { return false; } }; /* * * * * * * * * * * * * * * * * * * * * Serialization */ static void dump(NullStruct, string &out) { out += "null"; } static void dump(double value, string &out) { if (std::isfinite(value)) { char buf[32]; snprintf(buf, sizeof buf, "%.17g", value); out += buf; } else { out += "null"; } } static void dump(int value, string &out) { char buf[32]; snprintf(buf, sizeof buf, "%d", value); out += buf; } static void dump(bool value, string &out) { out += value ? "true" : "false"; } static void dump(const string &value, string &out) { out += '"'; for (size_t i = 0; i < value.length(); i++) { const char ch = value[i]; if (ch == '\\') { out += "\\\\"; } else if (ch == '"') { out += "\\\""; } else if (ch == '\b') { out += "\\b"; } else if (ch == '\f') { out += "\\f"; } else if (ch == '\n') { out += "\\n"; } else if (ch == '\r') { out += "\\r"; } else if (ch == '\t') { out += "\\t"; } else if (static_cast(ch) <= 0x1f) { char buf[8]; snprintf(buf, sizeof buf, "\\u%04x", ch); out += buf; } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 && static_cast(value[i+2]) == 0xa8) { out += "\\u2028"; i += 2; } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 && static_cast(value[i+2]) == 0xa9) { out += "\\u2029"; i += 2; } else { out += ch; } } out += '"'; } static void dump(const Json::array &values, string &out) { bool first = true; out += "["; for (const auto &value : values) { if (!first) out += ", "; value.dump(out); first = false; } out += "]"; } static void dump(const Json::object &values, string &out) { bool first = true; out += "{"; for (const auto &kv : values) { if (!first) out += ", "; dump(kv.first, out); out += ": "; kv.second.dump(out); first = false; } out += "}"; } void Json::dump(string &out) const { m_ptr->dump(out); } /* * * * * * * * * * * * * * * * * * * * * Value wrappers */ template class Value : public JsonValue { protected: // Constructors explicit Value(const T &value) : m_value(value) {} explicit Value(T &&value) : m_value(std::move(value)) {} // Get type tag Json::Type type() const override { return tag; } // Comparisons bool equals(const JsonValue * other) const override { return m_value == static_cast *>(other)->m_value; } bool less(const JsonValue * other) const override { return m_value < static_cast *>(other)->m_value; } const T m_value; void dump(string &out) const override { json11::dump(m_value, out); } }; class JsonDouble final : public Value { double number_value() const override { return m_value; } int int_value() const override { return static_cast(m_value); } bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } bool less(const JsonValue * other) const override { return m_value < other->number_value(); } public: explicit JsonDouble(double value) : Value(value) {} }; class JsonInt final : public Value { double number_value() const override { return m_value; } int int_value() const override { return m_value; } bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } bool less(const JsonValue * other) const override { return m_value < other->number_value(); } public: explicit JsonInt(int value) : Value(value) {} }; class JsonBoolean final : public Value { bool bool_value() const override { return m_value; } public: explicit JsonBoolean(bool value) : Value(value) {} }; class JsonString final : public Value { const string &string_value() const override { return m_value; } public: explicit JsonString(const string &value) : Value(value) {} explicit JsonString(string &&value) : Value(std::move(value)) {} }; class JsonArray final : public Value { const Json::array &array_items() const override { return m_value; } const Json & operator[](size_t i) const override; public: explicit JsonArray(const Json::array &value) : Value(value) {} explicit JsonArray(Json::array &&value) : Value(std::move(value)) {} }; class JsonObject final : public Value { const Json::object &object_items() const override { return m_value; } const Json & operator[](const string &key) const override; public: explicit JsonObject(const Json::object &value) : Value(value) {} explicit JsonObject(Json::object &&value) : Value(std::move(value)) {} }; class JsonNull final : public Value { public: JsonNull() : Value({}) {} }; /* * * * * * * * * * * * * * * * * * * * * Static globals - static-init-safe */ struct Statics { const std::shared_ptr null = make_shared(); const std::shared_ptr t = make_shared(true); const std::shared_ptr f = make_shared(false); const string empty_string; const vector empty_vector; const map empty_map; Statics() {} }; static const Statics & statics() { static const Statics s {}; return s; } static const Json & static_null() { // This has to be separate, not in Statics, because Json() accesses statics().null. static const Json json_null; return json_null; } /* * * * * * * * * * * * * * * * * * * * * Constructors */ Json::Json() noexcept : m_ptr(statics().null) {} Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} Json::Json(double value) : m_ptr(make_shared(value)) {} Json::Json(int value) : m_ptr(make_shared(value)) {} Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} Json::Json(const string &value) : m_ptr(make_shared(value)) {} Json::Json(string &&value) : m_ptr(make_shared(std::move(value))) {} Json::Json(const char * value) : m_ptr(make_shared(value)) {} Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} Json::Json(Json::array &&values) : m_ptr(make_shared(std::move(values))) {} Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} Json::Json(Json::object &&values) : m_ptr(make_shared(std::move(values))) {} /* * * * * * * * * * * * * * * * * * * * * Accessors */ Json::Type Json::type() const { return m_ptr->type(); } double Json::number_value() const { return m_ptr->number_value(); } int Json::int_value() const { return m_ptr->int_value(); } bool Json::bool_value() const { return m_ptr->bool_value(); } const string & Json::string_value() const { return m_ptr->string_value(); } const vector & Json::array_items() const { return m_ptr->array_items(); } const map & Json::object_items() const { return m_ptr->object_items(); } const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } double JsonValue::number_value() const { return 0; } int JsonValue::int_value() const { return 0; } bool JsonValue::bool_value() const { return false; } const string & JsonValue::string_value() const { return statics().empty_string; } const vector & JsonValue::array_items() const { return statics().empty_vector; } const map & JsonValue::object_items() const { return statics().empty_map; } const Json & JsonValue::operator[] (size_t) const { return static_null(); } const Json & JsonValue::operator[] (const string &) const { return static_null(); } const Json & JsonObject::operator[] (const string &key) const { auto iter = m_value.find(key); return (iter == m_value.end()) ? static_null() : iter->second; } const Json & JsonArray::operator[] (size_t i) const { if (i >= m_value.size()) return static_null(); else return m_value[i]; } /* * * * * * * * * * * * * * * * * * * * * Comparison */ bool Json::operator== (const Json &other) const { if (m_ptr->type() != other.m_ptr->type()) return false; return m_ptr->equals(other.m_ptr.get()); } bool Json::operator< (const Json &other) const { if (m_ptr->type() != other.m_ptr->type()) return m_ptr->type() < other.m_ptr->type(); return m_ptr->less(other.m_ptr.get()); } /* * * * * * * * * * * * * * * * * * * * * Parsing */ /* esc(c) * * Format char c suitable for printing in an error message. */ static inline string esc(char c) { char buf[12]; if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { snprintf(buf, sizeof buf, "'%c' (%d)", c, c); } else { snprintf(buf, sizeof buf, "(%d)", c); } return string(buf); } static inline bool in_range(long x, long lower, long upper) { return (x >= lower && x <= upper); } namespace { /* JsonParser * * Object that tracks all state of an in-progress parse. */ struct JsonParser final { /* State */ const string &str; size_t i; string &err; bool failed; const JsonParse strategy; /* fail(msg, err_ret = Json()) * * Mark this parse as failed. */ Json fail(string &&msg) { return fail(std::move(msg), Json()); } template T fail(string &&msg, const T err_ret) { if (!failed) err = std::move(msg); failed = true; return err_ret; } /* consume_whitespace() * * Advance until the current character is non-whitespace. */ void consume_whitespace() { while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') i++; } /* consume_comment() * * Advance comments (c-style inline and multiline). */ bool consume_comment() { bool comment_found = false; if (str[i] == '/') { i++; if (i == str.size()) return fail("unexpected end of input inside comment", false); if (str[i] == '/') { // inline comment i++; if (i == str.size()) return fail("unexpected end of input inside inline comment", false); // advance until next line while (str[i] != '\n') { i++; if (i == str.size()) return fail("unexpected end of input inside inline comment", false); } comment_found = true; } else if (str[i] == '*') { // multiline comment i++; if (i > str.size()-2) return fail("unexpected end of input inside multi-line comment", false); // advance until closing tokens while (!(str[i] == '*' && str[i+1] == '/')) { i++; if (i > str.size()-2) return fail( "unexpected end of input inside multi-line comment", false); } i += 2; if (i == str.size()) return fail( "unexpected end of input inside multi-line comment", false); comment_found = true; } else return fail("malformed comment", false); } return comment_found; } /* consume_garbage() * * Advance until the current character is non-whitespace and non-comment. */ void consume_garbage() { consume_whitespace(); if(strategy == JsonParse::COMMENTS) { bool comment_found = false; do { comment_found = consume_comment(); consume_whitespace(); } while(comment_found); } } /* get_next_token() * * Return the next non-whitespace character. If the end of the input is reached, * flag an error and return 0. */ char get_next_token() { consume_garbage(); if (i == str.size()) return fail("unexpected end of input", (char)0); return str[i++]; } /* encode_utf8(pt, out) * * Encode pt as UTF-8 and add it to out. */ void encode_utf8(long pt, string & out) { if (pt < 0) return; if (pt < 0x80) { out += static_cast(pt); } else if (pt < 0x800) { out += static_cast((pt >> 6) | 0xC0); out += static_cast((pt & 0x3F) | 0x80); } else if (pt < 0x10000) { out += static_cast((pt >> 12) | 0xE0); out += static_cast(((pt >> 6) & 0x3F) | 0x80); out += static_cast((pt & 0x3F) | 0x80); } else { out += static_cast((pt >> 18) | 0xF0); out += static_cast(((pt >> 12) & 0x3F) | 0x80); out += static_cast(((pt >> 6) & 0x3F) | 0x80); out += static_cast((pt & 0x3F) | 0x80); } } /* parse_string() * * Parse a string, starting at the current position. */ string parse_string() { string out; long last_escaped_codepoint = -1; while (true) { if (i == str.size()) return fail("unexpected end of input in string", ""); char ch = str[i++]; if (ch == '"') { encode_utf8(last_escaped_codepoint, out); return out; } if (in_range(ch, 0, 0x1f)) return fail("unescaped " + esc(ch) + " in string", ""); // The usual case: non-escaped characters if (ch != '\\') { encode_utf8(last_escaped_codepoint, out); last_escaped_codepoint = -1; out += ch; continue; } // Handle escapes if (i == str.size()) return fail("unexpected end of input in string", ""); ch = str[i++]; if (ch == 'u') { // Extract 4-byte escape sequence string esc = str.substr(i, 4); // Explicitly check length of the substring. The following loop // relies on std::string returning the terminating NUL when // accessing str[length]. Checking here reduces brittleness. if (esc.length() < 4) { return fail("bad \\u escape: " + esc, ""); } for (size_t j = 0; j < 4; j++) { if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') && !in_range(esc[j], '0', '9')) return fail("bad \\u escape: " + esc, ""); } long codepoint = strtol(esc.data(), nullptr, 16); // JSON specifies that characters outside the BMP shall be encoded as a pair // of 4-hex-digit \u escapes encoding their surrogate pair components. Check // whether we're in the middle of such a beast: the previous codepoint was an // escaped lead (high) surrogate, and this is a trail (low) surrogate. if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) && in_range(codepoint, 0xDC00, 0xDFFF)) { // Reassemble the two surrogate pairs into one astral-plane character, per // the UTF-16 algorithm. encode_utf8((((last_escaped_codepoint - 0xD800) << 10) | (codepoint - 0xDC00)) + 0x10000, out); last_escaped_codepoint = -1; } else { encode_utf8(last_escaped_codepoint, out); last_escaped_codepoint = codepoint; } i += 4; continue; } encode_utf8(last_escaped_codepoint, out); last_escaped_codepoint = -1; if (ch == 'b') { out += '\b'; } else if (ch == 'f') { out += '\f'; } else if (ch == 'n') { out += '\n'; } else if (ch == 'r') { out += '\r'; } else if (ch == 't') { out += '\t'; } else if (ch == '"' || ch == '\\' || ch == '/') { out += ch; } else { return fail("invalid escape character " + esc(ch), ""); } } } /* parse_number() * * Parse a double. */ Json parse_number() { size_t start_pos = i; if (str[i] == '-') i++; // Integer part if (str[i] == '0') { i++; if (in_range(str[i], '0', '9')) return fail("leading 0s not permitted in numbers"); } else if (in_range(str[i], '1', '9')) { i++; while (in_range(str[i], '0', '9')) i++; } else { return fail("invalid " + esc(str[i]) + " in number"); } if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { return std::atoi(str.c_str() + start_pos); } // Decimal part if (str[i] == '.') { i++; if (!in_range(str[i], '0', '9')) return fail("at least one digit required in fractional part"); while (in_range(str[i], '0', '9')) i++; } // Exponent part if (str[i] == 'e' || str[i] == 'E') { i++; if (str[i] == '+' || str[i] == '-') i++; if (!in_range(str[i], '0', '9')) return fail("at least one digit required in exponent"); while (in_range(str[i], '0', '9')) i++; } return std::strtod(str.c_str() + start_pos, nullptr); } /* expect(str, res) * * Expect that 'str' starts at the character that was just read. If it does, advance * the input and return res. If not, flag an error. */ Json expect(const string &expected, Json res) { assert(i != 0); i--; if (str.compare(i, expected.length(), expected) == 0) { i += expected.length(); return res; } else { return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); } } /* parse_json() * * Parse a JSON object. */ Json parse_json(int depth) { if (depth > max_depth) { return fail("exceeded maximum nesting depth"); } char ch = get_next_token(); if (failed) return Json(); if (ch == '-' || (ch >= '0' && ch <= '9')) { i--; return parse_number(); } if (ch == 't') return expect("true", true); if (ch == 'f') return expect("false", false); if (ch == 'n') return expect("null", Json()); if (ch == '"') return parse_string(); if (ch == '{') { map data; ch = get_next_token(); if (ch == '}') return data; while (1) { if (ch != '"') return fail("expected '\"' in object, got " + esc(ch)); string key = parse_string(); if (failed) return Json(); ch = get_next_token(); if (ch != ':') return fail("expected ':' in object, got " + esc(ch)); data[std::move(key)] = parse_json(depth + 1); if (failed) return Json(); ch = get_next_token(); if (ch == '}') break; if (ch != ',') return fail("expected ',' in object, got " + esc(ch)); ch = get_next_token(); } return data; } if (ch == '[') { vector data; ch = get_next_token(); if (ch == ']') return data; while (1) { i--; data.push_back(parse_json(depth + 1)); if (failed) return Json(); ch = get_next_token(); if (ch == ']') break; if (ch != ',') return fail("expected ',' in list, got " + esc(ch)); ch = get_next_token(); (void)ch; } return data; } return fail("expected value, got " + esc(ch)); } }; }//namespace { Json Json::parse(const string &in, string &err, JsonParse strategy) { JsonParser parser { in, 0, err, false, strategy }; Json result = parser.parse_json(0); // Check for any trailing garbage parser.consume_garbage(); if (parser.i != in.size()) return parser.fail("unexpected trailing " + esc(in[parser.i])); return result; } // Documented in json11.hpp vector Json::parse_multi(const string &in, std::string::size_type &parser_stop_pos, string &err, JsonParse strategy) { JsonParser parser { in, 0, err, false, strategy }; parser_stop_pos = 0; vector json_vec; while (parser.i != in.size() && !parser.failed) { json_vec.push_back(parser.parse_json(0)); // Check for another object parser.consume_garbage(); if (!parser.failed) parser_stop_pos = parser.i; } return json_vec; } /* * * * * * * * * * * * * * * * * * * * * Shape-checking */ bool Json::has_shape(const shape & types, string & err) const { if (!is_object()) { err = "expected JSON object, got " + dump(); return false; } for (auto & item : types) { if ((*this)[item.first].type() != item.second) { err = "bad type for " + item.first + " in " + dump(); return false; } } return true; } } // namespace json11 weakforced-2.10.2/ext/json11/json11.hpp000066400000000000000000000217741461473602600174710ustar00rootroot00000000000000/* json11 * * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. * * The core object provided by the library is json11::Json. A Json object represents any JSON * value: null, bool, number (int or double), string (std::string), array (std::vector), or * object (std::map). * * Json objects act like values: they can be assigned, copied, moved, compared for equality or * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and * Json::parse (static) to parse a std::string as a Json object. * * Internally, the various types of Json object are represented by the JsonValue class * hierarchy. * * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, * so some JSON implementations distinguish between integers and floating-point numbers, while * some don't. In json11, we choose the latter. Because some JSON implementations (namely * JavaScript itself) treat all numbers as the same type, distinguishing the two leads * to JSON that will be *silently* changed by a round-trip through those implementations. * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also * provides integer helpers. * * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch * will be exact for +/- 275 years.) */ /* Copyright (c) 2013 Dropbox, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include #include #include #include #ifdef _MSC_VER #if _MSC_VER <= 1800 // VS 2013 #ifndef noexcept #define noexcept throw() #endif #ifndef snprintf #define snprintf _snprintf_s #endif #endif #endif namespace json11 { enum JsonParse { STANDARD, COMMENTS }; class JsonValue; class Json final { public: // Types enum Type { NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT }; // Array and object typedefs typedef std::vector array; typedef std::map object; // Constructors for the various types of JSON value. Json() noexcept; // NUL Json(std::nullptr_t) noexcept; // NUL Json(double value); // NUMBER Json(int value); // NUMBER Json(bool value); // BOOL Json(const std::string &value); // STRING Json(std::string &&value); // STRING Json(const char * value); // STRING Json(const array &values); // ARRAY Json(array &&values); // ARRAY Json(const object &values); // OBJECT Json(object &&values); // OBJECT // Implicit constructor: anything with a to_json() function. template Json(const T & t) : Json(t.to_json()) {} // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) template ::value && std::is_constructible::value, int>::type = 0> Json(const M & m) : Json(object(m.begin(), m.end())) {} // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) template ::value, int>::type = 0> Json(const V & v) : Json(array(v.begin(), v.end())) {} // This prevents Json(some_pointer) from accidentally producing a bool. Use // Json(bool(some_pointer)) if that behavior is desired. Json(void *) = delete; // Accessors Type type() const; bool is_null() const { return type() == NUL; } bool is_number() const { return type() == NUMBER; } bool is_bool() const { return type() == BOOL; } bool is_string() const { return type() == STRING; } bool is_array() const { return type() == ARRAY; } bool is_object() const { return type() == OBJECT; } // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not // distinguish between integer and non-integer numbers - number_value() and int_value() // can both be applied to a NUMBER-typed object. double number_value() const; int int_value() const; // Return the enclosed value if this is a boolean, false otherwise. bool bool_value() const; // Return the enclosed string if this is a string, "" otherwise. const std::string &string_value() const; // Return the enclosed std::vector if this is an array, or an empty vector otherwise. const array &array_items() const; // Return the enclosed std::map if this is an object, or an empty map otherwise. const object &object_items() const; // Return a reference to arr[i] if this is an array, Json() otherwise. const Json & operator[](size_t i) const; // Return a reference to obj[key] if this is an object, Json() otherwise. const Json & operator[](const std::string &key) const; // Serialize. void dump(std::string &out) const; std::string dump() const { std::string out; dump(out); return out; } // Parse. If parse fails, return Json() and assign an error message to err. static Json parse(const std::string & in, std::string & err, JsonParse strategy = JsonParse::STANDARD); static Json parse(const char * in, std::string & err, JsonParse strategy = JsonParse::STANDARD) { if (in) { return parse(std::string(in), err, strategy); } else { err = "null input"; return nullptr; } } // Parse multiple objects, concatenated or separated by whitespace static std::vector parse_multi( const std::string & in, std::string::size_type & parser_stop_pos, std::string & err, JsonParse strategy = JsonParse::STANDARD); static inline std::vector parse_multi( const std::string & in, std::string & err, JsonParse strategy = JsonParse::STANDARD) { std::string::size_type parser_stop_pos; return parse_multi(in, parser_stop_pos, err, strategy); } bool operator== (const Json &rhs) const; bool operator< (const Json &rhs) const; bool operator!= (const Json &rhs) const { return !(*this == rhs); } bool operator<= (const Json &rhs) const { return !(rhs < *this); } bool operator> (const Json &rhs) const { return (rhs < *this); } bool operator>= (const Json &rhs) const { return !(*this < rhs); } /* has_shape(types, err) * * Return true if this is a JSON object and, for each item in types, has a field of * the given type. If not, return false and set err to a descriptive message. */ typedef std::initializer_list> shape; bool has_shape(const shape & types, std::string & err) const; private: std::shared_ptr m_ptr; }; // Internal class hierarchy - JsonValue objects are not exposed to users of this API. class JsonValue { protected: friend class Json; friend class JsonInt; friend class JsonDouble; virtual Json::Type type() const = 0; virtual bool equals(const JsonValue * other) const = 0; virtual bool less(const JsonValue * other) const = 0; virtual void dump(std::string &out) const = 0; virtual double number_value() const; virtual int int_value() const; virtual bool bool_value() const; virtual const std::string &string_value() const; virtual const Json::array &array_items() const; virtual const Json &operator[](size_t i) const; virtual const Json::object &object_items() const; virtual const Json &operator[](const std::string &key) const; virtual ~JsonValue() {} }; } // namespace json11 weakforced-2.10.2/gendata/000077500000000000000000000000001461473602600153225ustar00rootroot00000000000000weakforced-2.10.2/gendata/README.md000066400000000000000000000021241461473602600166000ustar00rootroot00000000000000The files in this directory are used to generate data to feed into wforce, primarily to create data to populate named report sinks (typically logstash/elasticsearch) with report data. The following files are used: * gen_success_reports.lua - This is a file to be used by wrk2 in order to construct "random" data about successful login attempts to send to wforce as reports * gen_fail_reports.lua - This is a file to be used by wrk2 in order to construct "random" data about failed login attempts to send to wforce as reports * wforce_elastic.conf - This is a very simple (no policy) wforce config file that just sends reports to a logstash instance on localhost:14501. This is not the standard logstash port because I run logstash and elasticsearch in a Docker container. * send_reports.py - This is a python script that start wforce, and send reports to it using wrk2, which must be installed and in the path. Wrk2 can be found at https://github.com/giltene/wrk2. It is also a very good tool for loadtesting wforce, and the gen_xxxx_reports.lua scripts are a good place to start if you wish to do so. weakforced-2.10.2/gendata/gen_fail_reports.lua000066400000000000000000000043171461473602600213540ustar00rootroot00000000000000# This lua script generates random reports for wforce logins counter = 0 one_counter = 0 wrk.headers["Content-Type"] = "application/json" wrk.headers["Authorization"] = "Basic d2ZvcmNlOnN1cGVy" wrk.method = 'POST' wrk.path = "/?command=report" dp_access = { {protocol="http", device="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A"}, {protocol="http", device="Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"}, {protocol="http", device="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"}, {protocol="http", device="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36"}, {protocol="http", device="Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko"}, {protocol="http", device="Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 7.0; InfoPath.3; .NET CLR 3.1.40767; Trident/6.0; en-IN)"}, {protocol="http", device="Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"}, {protocol="imap", device='\\"name\\" \\"Bad Guy Mailer\\" \\"version\\" \\"10.0 (3226)\\" \\"os\\" \\"Mac OS X\\" \\"os-version\\" \\"10.12 (16A323)\\" \\"vendor\\" \\"Bad Guy Inc.\\"'} } ip_access = { "2.2.2.2", "3.3.3.3", "4.4.4.4", "5.5.5.5", "6.6.6.6", "7.7.7.7" } pwd_access = { "ijfds", "sokee", "sdefw", "93ked", "0kwepw", "ow3002", "2303wp", "2020ew", "309oka" } request = function() ip_mod = one_counter % table.getn(ip_access) + 1 dp_mod = one_counter % table.getn(dp_access) + 1 pwd_mod = counter % table.getn(pwd_access) + 1 mybody = '{"login":"user' .. counter .. '", "remote":"' .. ip_access[ip_mod] .. '", "pwhash":"' .. pwd_access[pwd_mod] .. '", "success": false, "policy_reject":false, "protocol":"' .. dp_access[dp_mod].protocol .. '", "device_id":"' .. dp_access[dp_mod].device .. '" }' counter = counter + 10 one_counter = one_counter + 1 if (counter>1000) then counter = 0 end return wrk.format(nil, nil, nil, mybody) end weakforced-2.10.2/gendata/gen_success_reports.lua000066400000000000000000000115401461473602600221050ustar00rootroot00000000000000# This lua script generates random reports for wforce logins counter = 0 wrk.headers["Content-Type"] = "application/json" wrk.headers["Authorization"] = "Basic d2ZvcmNlOnN1cGVy" wrk.method = 'POST' wrk.path = "/?command=report" dp_access = { {protocol="http", device="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A"}, {protocol="http", device="Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"}, {protocol="http", device="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"}, {protocol="http", device="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36"}, {protocol="http", device="Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko"}, {protocol="http", device="Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 7.0; InfoPath.3; .NET CLR 3.1.40767; Trident/6.0; en-IN)"}, {protocol="http", device="Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"}, {protocol="imap", device='\\"name\\" \\"iPhone Mail\\" \\"version\\" \\"14D27\\" \\"os\\" \\"iOS\\" \\"os-version\\" \\"10.2.1 (14D27)\\"'}, {protocol="imap", device='\\"name\\" \\"iPad Mail\\" \\"version\\" \\"14D27\\" \\"os\\" \\"iOS\\" \\"os-version\\" \\"10.2.1 (14D27)\\"'}, {protocol="imap", device='\\"name\\" \\"Mac OS X Mail\\" \\"version\\" \\"10.2 (3259)\\" \\"os\\" \\"Mac OS X\\" \\"os-version\\" \\"10.12.3 (16D32)\\" \\"vendor\\" \\"Apple Inc.\\"'}, {protocol="imap", device='\\"name\\" \\"Mac OS X Notes\\" \\"version\\" \\"4.3.1 (698.50)\\" \\"os\\" \\"Mac OS X\\" \\"os-version\\" \\"10.12.3 (16D32)\\" \\"vendor\\" \\"Apple Inc.\\"'}, {protocol="imap", device='\\"name\\" \\"Mac OS X accountsd\\" \\"version\\" \\"113 (113)\\" \\"os\\" \\"Mac OS X\\" \\"os-version\\" \\"10.12.3 (16D32)\\" \\"vendor\\" \\"Apple Inc.\\"'}, {protocol="imap", device='\\"name\\" \\"Microsoft Outlook\\" \\"version\\" \\"14.0.7172.5000\\"'}, {protocol="imap", device='\\"name\\" \\"Thunderbird\\" \\"version\\" \\"45.7.1\\"'}, {protocol="imap", device='\\"name\\" \\"Icedove\\" \\"version\\" \\"38.5.0\\"'}, {protocol="imap", device='vendor\\" \\"Microsoft\\" \\"os\\" \\"Windows Mobile\\" \\"os-version\\" \\"10.0\\" \\"guid\\" \\"31414643323441464239373433384242374438463641383146423942364639383931324435323232\\"'}, {protocol="imap", device='\\"name\\" \\"com.google.android.gm\\" \\"os\\" \\"android\\" \\"os-version\\" \\"7.1.1; N4F26O\\" \\"vendor\\" \\"LGE\\" \\"x-android-device-model\\" \\"Nexus 5X\\" \\"x-android-mobile-net-operator\\" \\"Sonera AGUID\\" \\"XwzK66ekvB2DkNmD8EyAuwr8M4s\\"'}, {protocol="imap", device='\\"name\\" \\"com.android.email\\" \\"os\\" \\"android\\" \\"os-version\\" \\"6.0; 32.1.F.1.67\\" \\"vendor\\" \\"Sony\\" \\"x-android-device-model\\" \\"SO-01H\\" \\"x-android-mobile-net-operator\\" \\"NTT DOCOMO\\" \\"AGUID\\" \\"QHaFdAshRmaLYCPM0KVo7lwajhE\\"'}, {protocol="imap", device='\\"name\\" \\"com.samsung.android.email.provider\\" \\"os\\" \\"android\\" \\"os-version\\" \\"6.0.1; MMB29K\\" \\"vendor\\" \\"samsung\\" \\"x-android-device-model\\" \\"SM-G925F\\" \\"x-android-mobile-net-operator\\" \\"Sonera\\" \\"AGUID\\" \\"X9I/elUUlMNosGZjexmq6mp1zuA\\"'}, {protocol="imap", device='\\"name\\" \\"com.sonymobile.email\\" \\"os\\" \\"android\\" \\"os-version\\" \\"6.0.1; 32.2.A.5.11\\" \\"vendor\\" \\"Sony\\" \\"x-android-device-model\\" \\"SGP771\\" \\"AGUID\\" \\"cJwASIogV2kpgCa3kPG+77TgCZk\\"'}, {protocol="imap", device='\\"name\\" \\"eM Client for OX App Suite\\" \\"version\\" \\"6.0.28376.0\\" \\"GUID\\" \\"1\\"'}, {protocol="mobileapi", device='OpenXchange.iOS.Mail/1.0.3 (OS: 10.0.3; device: iPhone 7 Plus)'}, {protocol="mobileapi", device='OpenXchange.Android.Mail/1.0+1234 (OS: 7.0; device: Samsung/GT9700)'} } ip_access = { "23.96.52.53", "86.128.243.146", "213.20.82.16", "212.223.44.11", "8.8.8.8", "2a03:b0c0:2:d0::4ab:8001", "2a00:1450:4009:80a::200e", "2a00:1450:4009:806::2005", "180.22.47.32", "52.48.64.3" } pwd_access = { "ijfds", "sokee", "sdefw", "93ked", "0kwepw", "ow3002", "2303wp", "2020ew", "309oka" } request = function() ip_mod = counter % table.getn(ip_access) + 1 dp_mod = counter % table.getn(dp_access) + 1 pwd_mod = counter % table.getn(pwd_access) + 1 mybody = '{"login":"user' .. counter .. '", "remote":"' .. ip_access[ip_mod] .. '", "pwhash":"' .. pwd_access[pwd_mod] .. '", "success": true, "policy_reject":false, "protocol":"' .. dp_access[dp_mod].protocol .. '", "device_id":"' .. dp_access[dp_mod].device .. '" }' counter = counter + 1 if (counter==1000) then counter = 0 end return wrk.format(nil, nil, nil, mybody) end weakforced-2.10.2/gendata/send_reports.py000077500000000000000000000024361461473602600204130ustar00rootroot00000000000000#!/usr/bin/env python # # Shell-script style. import os import requests import shutil import subprocess import sys import tempfile import time WEBPORT = '8084' APIKEY = 'super' cmd1 = ("../wforce/wforce -C ./wforce_elastic.conf -R ../wforce/regexes.yaml").split() wrksuccesscmd = ("wrk -c 2 -d 60 -t 2 -s ./gen_success_reports.lua -R 30 http://127.0.0.1:8084").split() wrkfailcmd = ("wrk -c 2 -d 60 -t 2 -s ./gen_fail_reports.lua -R 20 http://127.0.0.1:8084").split() # Now run wforce and the tests. print "Launching wforce..." print ' '.join(cmd1) proc1 = subprocess.Popen(cmd1, close_fds=True) wforcepid = proc1.pid print "Waiting for webserver port to become available..." available = False for try_number in range(0, 10): try: res = requests.get('http://127.0.0.1:%s/' % WEBPORT) available = True break except: time.sleep(0.5) if not available: print "Webserver port not reachable after 10 tries, giving up." proc1.terminate() proc1.wait() sys.exit(2) print "Sending Reports..." wrkproc = subprocess.Popen(wrksuccesscmd, close_fds=True) wrkproc.wait() time.sleep(10) wrkproc = subprocess.Popen(wrkfailcmd, close_fds=True) wrkproc.wait() time.sleep(10) print "Done sending reports..." proc1.terminate() proc1.wait() print "Exiting" sys.exit(0) weakforced-2.10.2/gendata/wforce_elastic.conf000066400000000000000000000020731461473602600211640ustar00rootroot00000000000000webserver("0.0.0.0:8084", "super") setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") controlSocket("0.0.0.0:4004") addACL("127.0.0.0/8") addACL("192.168.0.0/16") --addNamedReportSink("logstash", "127.0.0.1:14501") config_keys = {} config_keys["url"] = "http://127.0.0.1:8080" config_keys["secret"] = "verysecretcode" addWebHook({"report"}, config_keys) function report(lt) end function allow(lt) -- return must have these 4 arguments return 0, "allowed", "allowed", {} end -- Use this function to reset stats if needed for particular IPs, logins or both function reset(type, login, ip) end setReport(report) setAllow(allow) setReset(reset) function custom(args) for k,v in pairs(args.attrs) do infoLog("custom func argument attrs", { key=k, value=v }); end runCustomWebHook("mycustomhook", "{ \"foo\":\"bar\" }") -- return consists of a boolean, followed by { key-value pairs } return true, { key=value } end -- Register a custom endpoint -- Parameters: name, send arguments to report sink?, function) setCustomEndpoint("custom", false, custom) weakforced-2.10.2/m4/000077500000000000000000000000001461473602600142375ustar00rootroot00000000000000weakforced-2.10.2/m4/ac_pthread_set_name.m4000066400000000000000000000046011461473602600204470ustar00rootroot00000000000000# PTHREAD_SET_NAME(); # Check which variant (if any) of pthread_set_name_np we have. # ----------------------------------------------------------------------------- AC_DEFUN([PTHREAD_SET_NAME], [ stored_LIBS="$LIBS" LIBS="-lpthread" # pthread setname (4 non-portable variants...) AC_CHECK_HEADERS([pthread_np.h], [], [], [#include ]) define(pthread_np_preamble,[ #include #if HAVE_PTHREAD_NP_H # include #endif ]) # 2-arg setname (e.g. Linux/glibc, QNX, IBM) AC_MSG_CHECKING([for 2-arg pthread_setname_np]) AC_LINK_IFELSE([AC_LANG_PROGRAM(pthread_np_preamble, [ pthread_setname_np(pthread_self(), "foo") ])], [ AC_DEFINE(HAVE_PTHREAD_SETNAME_NP_2, 1, [2-arg pthread_setname_np]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) # 2-arg set_name (e.g. FreeBSD, OpenBSD) AC_MSG_CHECKING([for 2-arg pthread_set_name_np]) AC_LINK_IFELSE([AC_LANG_PROGRAM(pthread_np_preamble, [ return pthread_set_name_np(pthread_self(), "foo"); ])], [ AC_DEFINE(HAVE_PTHREAD_SET_NAME_NP_2, 1, [2-arg pthread_set_name_np]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) # 2-arg void set_name (e.g. FreeBSD, OpenBSD) AC_MSG_CHECKING([for 2-arg void pthread_set_name_np]) AC_LINK_IFELSE([AC_LANG_PROGRAM(pthread_np_preamble, [ pthread_set_name_np(pthread_self(), "foo"); ])], [ AC_DEFINE(HAVE_PTHREAD_SET_NAME_NP_2_VOID, 1, [2-arg void pthread_set_name_np]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) # 1-arg setname (e.g. Darwin) AC_MSG_CHECKING([for 1-arg pthread_setname_np]) AC_LINK_IFELSE([AC_LANG_PROGRAM(pthread_np_preamble, [ return pthread_setname_np("foo"); ])], [ AC_DEFINE(HAVE_PTHREAD_SETNAME_NP_1, 1, [1-arg pthread_setname_np]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) # 3-arg setname (e.g. NetBSD) AC_MSG_CHECKING([for 3-arg pthread_setname_np]) AC_LINK_IFELSE([AC_LANG_PROGRAM(pthread_np_preamble, [ return pthread_setname_np(pthread_self(), "foo", NULL); ])], [ AC_DEFINE(HAVE_PTHREAD_SETNAME_NP_3, 1, [3-arg pthread_setname_np]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) ]) ]) ]) ]) LIBS=$stored_LIBS ]) weakforced-2.10.2/m4/ax_arg_default_enable_disable.m4000066400000000000000000000010631461473602600224370ustar00rootroot00000000000000AC_DEFUN([AX_ARG_DEFAULT_ENABLE], [ AC_ARG_ENABLE([$1], AS_HELP_STRING([--disable-$1], [$2 (default is ENABLED)])) AX_PARSE_VALUE([$1], [y]) ]) AC_DEFUN([AX_ARG_DEFAULT_DISABLE], [ AC_ARG_ENABLE([$1], AS_HELP_STRING([--enable-$1], [$2 (default is DISABLED)])) AX_PARSE_VALUE([$1], [n]) ]) dnl This function should not be called outside of this file AC_DEFUN([AX_PARSE_VALUE], [ AS_IF([test "x$enable_$1" = "xno"], [ ax_cv_$1="n" ], [test "x$enable_$1" = "xyes"], [ ax_cv_$1="y" ], [test -z $ax_cv_$1], [ ax_cv_$1="$2" ]) $1=$ax_cv_$1 AC_SUBST($1)]) weakforced-2.10.2/m4/ax_cxx_compile_stdcxx.m4000066400000000000000000000465501461473602600211120ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # or '14' (for the C++14 standard). # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for no added switch, and then for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016, 2018 Krzesimir Nowak # Copyright (c) 2019 Enji Cooper # Copyright (c) 2020 Jason Merrill # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 12 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no m4_if([$2], [], [dnl AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi]) m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L ]]) weakforced-2.10.2/m4/ax_cxx_compile_stdcxx_17.m4000066400000000000000000000026051461473602600214120ustar00rootroot00000000000000# ============================================================================= # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_17.html # ============================================================================= # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX_17([ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++17 # standard; if necessary, add switches to CXX and CXXCPP to enable # support. # # This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX # macro with the version set to C++17. The two optional arguments are # forwarded literally as the second and third argument respectively. # Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for # more information. If you want to use this macro, you also need to # download the ax_cxx_compile_stdcxx.m4 file. # # LICENSE # # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016 Krzesimir Nowak # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 2 AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) AC_DEFUN([AX_CXX_COMPILE_STDCXX_17], [AX_CXX_COMPILE_STDCXX([17], [$1], [$2])]) weakforced-2.10.2/m4/boost.m4000066400000000000000000001752131461473602600156400ustar00rootroot00000000000000# boost.m4: Locate Boost headers and libraries for autoconf-based projects. # Copyright (C) 2007-2011, 2014 Benoit Sigoure # # 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. # # Additional permission under section 7 of the GNU General Public # License, version 3 ("GPLv3"): # # If you convey this file as part of a work that contains a # configuration script generated by Autoconf, you may do so under # terms of your choice. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . m4_define([_BOOST_SERIAL], [m4_translit([ # serial 34 ], [# ], [])]) # Original sources can be found at http://github.com/tsuna/boost.m4 # You can fetch the latest version of the script by doing: # wget http://github.com/tsuna/boost.m4/raw/master/build-aux/boost.m4 # ------ # # README # # ------ # # This file provides several macros to use the various Boost libraries. # The first macro is BOOST_REQUIRE. It will simply check if it's possible to # find the Boost headers of a given (optional) minimum version and it will # define BOOST_CPPFLAGS accordingly. It will add an option --with-boost to # your configure so that users can specify non standard locations. # If the user's environment contains BOOST_ROOT and --with-boost was not # specified, --with-boost=$BOOST_ROOT is implicitly used. # For more README and documentation, go to http://github.com/tsuna/boost.m4 # Note: THESE MACROS ASSUME THAT YOU USE LIBTOOL. If you don't, don't worry, # simply read the README, it will show you what to do step by step. m4_pattern_forbid([^_?(BOOST|Boost)_]) # _BOOST_SED_CPP(SED-PROGRAM, PROGRAM, # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # -------------------------------------------------------- # Same as AC_EGREP_CPP, but leave the result in conftest.i. # # SED-PROGRAM is *not* overquoted, as in AC_EGREP_CPP. It is expanded # in double-quotes, so escape your double quotes. # # It could be useful to turn this into a macro which extracts the # value of any macro. m4_define([_BOOST_SED_CPP], [AC_LANG_PUSH([C++])dnl AC_LANG_PREPROC_REQUIRE()dnl AC_REQUIRE([AC_PROG_SED])dnl AC_LANG_CONFTEST([AC_LANG_SOURCE([[$2]])]) AS_IF([dnl eval is necessary to expand ac_cpp. dnl Ultrix and Pyramid sh refuse to redirect output of eval, so use subshell. dnl Beware of Windows end-of-lines, for instance if we are running dnl some Windows programs under Wine. In that case, boost/version.hpp dnl is certainly using "\r\n", but the regular Unix shell will only dnl strip `\n' with backquotes, not the `\r'. This results in dnl boost_cv_lib_version='1_37\r' for instance, which breaks dnl everything else. dnl Cannot use 'dnl' after [$4] because a trailing dnl may break AC_CACHE_CHECK dnl dnl Beware that GCC 5, when expanding macros, may embed # line directives dnl a within single line: dnl dnl # 1 "conftest.cc" dnl # 1 "" dnl # 1 "" dnl # 1 "conftest.cc" dnl # 1 "/opt/local/include/boost/version.hpp" 1 3 dnl # 2 "conftest.cc" 2 dnl boost-lib-version = dnl # 2 "conftest.cc" 3 dnl "1_56" dnl dnl So get rid of the # and empty lines, and glue the remaining ones together. (eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | grep -v '#' | grep -v '^[[[:space:]]]*$' | tr -d '\r' | tr -s '\n' ' ' | $SED -n -e "$1" >conftest.i 2>&1], [$3], [$4]) rm -rf conftest* AC_LANG_POP([C++])dnl ])# _BOOST_SED_CPP # BOOST_REQUIRE([VERSION], [ACTION-IF-NOT-FOUND]) # ----------------------------------------------- # Look for Boost. If version is given, it must either be a literal of the form # "X.Y.Z" where X, Y and Z are integers (the ".Z" part being optional) or a # variable "$var". # Defines the value BOOST_CPPFLAGS. This macro only checks for headers with # the required version, it does not check for any of the Boost libraries. # On # success, defines HAVE_BOOST. On failure, calls the optional # ACTION-IF-NOT-FOUND action if one was supplied. # Otherwise aborts with an error message. AC_DEFUN_ONCE([BOOST_REQUIRE], [AC_REQUIRE([AC_PROG_CXX])dnl AC_REQUIRE([AC_PROG_GREP])dnl echo "$as_me: this is boost.m4[]_BOOST_SERIAL" >&AS_MESSAGE_LOG_FD boost_save_IFS=$IFS boost_version_req=$1 IFS=. set x $boost_version_req 0 0 0 IFS=$boost_save_IFS shift boost_version_req=`expr "$[1]" '*' 100000 + "$[2]" '*' 100 + "$[3]"` boost_version_req_string=$[1].$[2].$[3] AC_ARG_WITH([boost], [AS_HELP_STRING([--with-boost=DIR], [prefix of Boost $1 @<:@guess@:>@])])dnl AC_ARG_VAR([BOOST_ROOT],[Location of Boost installation])dnl # If BOOST_ROOT is set and the user has not provided a value to # --with-boost, then treat BOOST_ROOT as if it the user supplied it. if test x"$BOOST_ROOT" != x; then if test x"$with_boost" = x; then AC_MSG_NOTICE([Detected BOOST_ROOT; continuing with --with-boost=$BOOST_ROOT]) with_boost=$BOOST_ROOT else AC_MSG_NOTICE([Detected BOOST_ROOT=$BOOST_ROOT, but overridden by --with-boost=$with_boost]) fi fi AC_SUBST([DISTCHECK_CONFIGURE_FLAGS], ["$DISTCHECK_CONFIGURE_FLAGS '--with-boost=$with_boost'"])dnl boost_save_CPPFLAGS=$CPPFLAGS AC_CACHE_CHECK([for Boost headers version >= $boost_version_req_string], [boost_cv_inc_path], [boost_cv_inc_path=no AC_LANG_PUSH([C++])dnl m4_pattern_allow([^BOOST_VERSION$])dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM([[#include #if !defined BOOST_VERSION # error BOOST_VERSION is not defined #elif BOOST_VERSION < $boost_version_req # error Boost headers version < $boost_version_req #endif ]])]) # If the user provided a value to --with-boost, use it and only it. case $with_boost in #( ''|yes) set x '' /opt/local/include /usr/local/include /opt/include \ /usr/include C:/Boost/include;; #( *) set x "$with_boost/include" "$with_boost";; esac shift for boost_dir do # Without --layout=system, Boost (or at least some versions) installs # itself in /include/boost-. This inner loop helps to # find headers in such directories. # # Any ${boost_dir}/boost-x_xx directories are searched in reverse version # order followed by ${boost_dir}. The final '.' is a sentinel for # searching $boost_dir" itself. Entries are whitespace separated. # # I didn't indent this loop on purpose (to avoid over-indented code) boost_layout_system_search_list=`cd "$boost_dir" 2>/dev/null \ && ls -1 | "${GREP}" '^boost-' | sort -rn -t- -k2 \ && echo .` for boost_inc in $boost_layout_system_search_list do if test x"$boost_inc" != x.; then boost_inc="$boost_dir/$boost_inc" else boost_inc="$boost_dir" # Uses sentinel in boost_layout_system_search_list fi if test x"$boost_inc" != x; then # We are going to check whether the version of Boost installed # in $boost_inc is usable by running a compilation that # #includes it. But if we pass a -I/some/path in which Boost # is not installed, the compiler will just skip this -I and # use other locations (either from CPPFLAGS, or from its list # of system include directories). As a result we would use # header installed on the machine instead of the /some/path # specified by the user. So in that precise case (trying # $boost_inc), make sure the version.hpp exists. # # Use test -e as there can be symlinks. test -e "$boost_inc/boost/version.hpp" || continue CPPFLAGS="$CPPFLAGS -I$boost_inc" fi AC_COMPILE_IFELSE([], [boost_cv_inc_path=yes], [boost_cv_version=no]) if test x"$boost_cv_inc_path" = xyes; then if test x"$boost_inc" != x; then boost_cv_inc_path=$boost_inc fi break 2 fi done done AC_LANG_POP([C++])dnl ]) case $boost_cv_inc_path in #( no) boost_errmsg="cannot find Boost headers version >= $boost_version_req_string" m4_if([$2], [], [AC_MSG_ERROR([$boost_errmsg])], [AC_MSG_NOTICE([$boost_errmsg])]) $2 ;;#( yes) BOOST_CPPFLAGS= ;;#( *) AC_SUBST([BOOST_CPPFLAGS], ["-I$boost_cv_inc_path"])dnl ;; esac if test x"$boost_cv_inc_path" != xno; then AC_DEFINE([HAVE_BOOST], [1], [Defined if the requested minimum BOOST version is satisfied]) AC_CACHE_CHECK([for Boost's header version], [boost_cv_lib_version], [m4_pattern_allow([^BOOST_LIB_VERSION$])dnl _BOOST_SED_CPP([[/^boost-lib-version = /{s///;s/[\" ]//g;p;q;}]], [#include boost-lib-version = BOOST_LIB_VERSION], [boost_cv_lib_version=`cat conftest.i`])]) # e.g. "134" for 1_34_1 or "135" for 1_35 boost_major_version=`echo "$boost_cv_lib_version" | sed 's/_//;s/_.*//'` case $boost_major_version in #( '' | *[[!0-9]]*) AC_MSG_ERROR([invalid value: boost_major_version='$boost_major_version']) ;; esac fi CPPFLAGS=$boost_save_CPPFLAGS ])# BOOST_REQUIRE # BOOST_STATIC() # -------------- # Add the "--enable-static-boost" configure argument. If this argument is given # on the command line, static versions of the libraries will be looked up. AC_DEFUN([BOOST_STATIC], [AC_ARG_ENABLE([static-boost], [AS_HELP_STRING([--enable-static-boost], [Prefer the static boost libraries over the shared ones [no]])], [enable_static_boost=yes], [enable_static_boost=no])])# BOOST_STATIC # BOOST_FIND_HEADER([HEADER-NAME], [ACTION-IF-NOT-FOUND], [ACTION-IF-FOUND]) # -------------------------------------------------------------------------- # Wrapper around AC_CHECK_HEADER for Boost headers. Useful to check for # some parts of the Boost library which are only made of headers and don't # require linking (such as Boost.Foreach). # # Default ACTION-IF-NOT-FOUND: Fail with a fatal error unless Boost couldn't be # found in the first place, in which case by default a notice is issued to the # user. Presumably if we haven't died already it's because it's OK to not have # Boost, which is why only a notice is issued instead of a hard error. # # Default ACTION-IF-FOUND: define the preprocessor symbol HAVE_ in # case of success # (where HEADER-NAME is written LIKE_THIS, e.g., # HAVE_BOOST_FOREACH_HPP). AC_DEFUN([BOOST_FIND_HEADER], [AC_REQUIRE([BOOST_REQUIRE])dnl if test x"$boost_cv_inc_path" = xno; then m4_default([$2], [AC_MSG_NOTICE([Boost not available, not searching for $1])]) else AC_LANG_PUSH([C++])dnl boost_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_CHECK_HEADER([$1], [m4_default([$3], [AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], [Define to 1 if you have <$1>])])], [m4_default([$2], [AC_MSG_ERROR([cannot find $1])])]) CPPFLAGS=$boost_save_CPPFLAGS AC_LANG_POP([C++])dnl fi ])# BOOST_FIND_HEADER # BOOST_FIND_LIBS([COMPONENT-NAME], [CANDIDATE-LIB-NAMES], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE], # [ERROR_ON_UNUSABLE]) # -------------------------------------------------------------- # Look for the Boost library COMPONENT-NAME (e.g., `thread', for # libboost_thread) under the possible CANDIDATE-LIB-NAMES (e.g., # "thread_win32 thread"). Check that HEADER-NAME works and check that # libboost_LIB-NAME can link with the code CXX-TEST. The optional # argument CXX-PROLOGUE can be used to include some C++ code before # the `main' function. The CXX-POST-INCLUDE-PROLOGUE can be used to # include some code before the `main' function, but after the # `#include '. # # Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above). # # Boost libraries typically come compiled with several flavors (with different # runtime options) so PREFERRED-RT-OPT is the preferred suffix. A suffix is one # or more of the following letters: sgdpn (in that order). s = static # runtime, d = debug build, g = debug/diagnostic runtime, p = STLPort build, # n = (unsure) STLPort build without iostreams from STLPort (it looks like `n' # must always be used along with `p'). Additionally, PREFERRED-RT-OPT can # start with `mt-' to indicate that there is a preference for multi-thread # builds. Some sample values for PREFERRED-RT-OPT: (nothing), mt, d, mt-d, gdp # ... If you want to make sure you have a specific version of Boost # (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro. # # ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their # configure to fail AC_DEFUN([BOOST_FIND_LIBS], [AC_REQUIRE([BOOST_REQUIRE])dnl AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])dnl AC_REQUIRE([BOOST_STATIC])dnl AC_REQUIRE([_BOOST_GUESS_WHETHER_TO_USE_MT])dnl if test x"$boost_cv_inc_path" = xno; then AC_MSG_NOTICE([Boost not available, not searching for the Boost $1 library]) else dnl The else branch is huge and wasn't indented on purpose. AC_LANG_PUSH([C++])dnl AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])dnl AS_VAR_PUSHDEF([Boost_lib_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl AS_VAR_PUSHDEF([Boost_lib_LDPATH], [boost_cv_lib_$1_LDPATH])dnl AS_VAR_PUSHDEF([Boost_lib_LIBS], [boost_cv_lib_$1_LIBS])dnl AS_IF([test x"$8" = "xno"], [not_found_header='true']) BOOST_FIND_HEADER([$4], [$not_found_header]) boost_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib], [_BOOST_FIND_LIBS($@)]) case $Boost_lib in #( (yes) _AC_MSG_LOG_CONFTEST AC_DEFINE(AS_TR_CPP([HAVE_BOOST_$1]), [1], [Defined if the Boost $1 library is available])dnl AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])dnl AC_SUBST(AS_TR_CPP([BOOST_$1_LDPATH]), [$Boost_lib_LDPATH])dnl AC_SUBST([BOOST_LDPATH], [$Boost_lib_LDPATH])dnl AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])dnl ;; (no) _AC_MSG_LOG_CONFTEST AS_IF([test x"$8" != "xno"], [ AC_MSG_ERROR([cannot find flags to link with the Boost $1 library (libboost-$1)]) ]) ;; esac CPPFLAGS=$boost_save_CPPFLAGS AS_VAR_POPDEF([Boost_lib])dnl AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl AS_VAR_POPDEF([Boost_lib_LDPATH])dnl AS_VAR_POPDEF([Boost_lib_LIBS])dnl AC_LANG_POP([C++])dnl fi ]) # BOOST_FIND_LIB([LIB-NAME], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE], # [ERROR_ON_UNUSABLE]) # -------------------------------------------------------------- # Backward compatibility wrapper for BOOST_FIND_LIBS. # ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their # configure to fail AC_DEFUN([BOOST_FIND_LIB], [BOOST_FIND_LIBS([$1], $@)]) # _BOOST_FIND_LIBS([LIB-NAME], [CANDIDATE-LIB-NAMES], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE], # [ERROR_ON_UNUSABLE]) # -------------------------------------------------------------- # Real implementation of BOOST_FIND_LIBS: rely on these local macros: # Boost_lib, Boost_lib_LDFLAGS, Boost_lib_LDPATH, Boost_lib_LIBS # # The algorithm is as follows: first look for a given library name # according to the user's PREFERRED-RT-OPT. For each library name, we # prefer to use the ones that carry the tag (toolset name). Each # library is searched through the various standard paths were Boost is # usually installed. If we can't find the standard variants, we try # to enforce -mt (for instance on MacOSX, libboost_thread.dylib # doesn't exist but there's -obviously- libboost_thread-mt.dylib). # # ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their # configure to fail AC_DEFUN([_BOOST_FIND_LIBS], [Boost_lib=no case "$3" in #( (mt | mt-) boost_mt=-mt; boost_rtopt=;; #( (mt* | mt-*) boost_mt=-mt; boost_rtopt=`expr "X$3" : 'Xmt-*\(.*\)'`;; #( (*) boost_mt=; boost_rtopt=$3;; esac if test $enable_static_boost = yes; then boost_rtopt="s$boost_rtopt" fi # Find the proper debug variant depending on what we've been asked to find. case $boost_rtopt in #( (*d*) boost_rt_d=$boost_rtopt;; #( (*[[sgpn]]*) # Insert the `d' at the right place (in between `sg' and `pn') boost_rt_d=`echo "$boost_rtopt" | sed 's/\(s*g*\)\(p*n*\)/\1\2/'`;; #( (*) boost_rt_d='-d';; esac # If the PREFERRED-RT-OPT are not empty, prepend a `-'. test -n "$boost_rtopt" && boost_rtopt="-$boost_rtopt" $boost_guess_use_mt && boost_mt=-mt # Look for the abs path the static archive. # $libext is computed by Libtool but let's make sure it's non empty. test -z "$libext" && AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?]) boost_save_ac_objext=$ac_objext # Generate the test file. AC_LANG_CONFTEST([AC_LANG_PROGRAM([$7 #include <$4> $6], [$5])]) dnl Optimization hacks: compiling C++ is slow, especially with Boost. What dnl we're trying to do here is guess the right combination of link flags dnl (LIBS / LDFLAGS) to use a given library. This can take several dnl iterations before it succeeds and is thus *very* slow. So what we do dnl instead is that we compile the code first (and thus get an object file, dnl typically conftest.o). Then we try various combinations of link flags dnl until we succeed to link conftest.o in an executable. The problem is dnl that the various TRY_LINK / COMPILE_IFELSE macros of Autoconf always dnl remove all the temporary files including conftest.o. So the trick here dnl is to temporarily change the value of ac_objext so that conftest.o is dnl preserved across tests. This is obviously fragile and I will burn in dnl hell for not respecting Autoconf's documented interfaces, but in the dnl mean time, it optimizes the macro by a factor of 5 to 30. dnl Another small optimization: the first argument of AC_COMPILE_IFELSE left dnl empty because the test file is generated only once above (before we dnl start the for loops). AC_COMPILE_IFELSE([], [ac_objext=do_not_rm_me_plz], [AS_IF([test x"$8" != x"no"], [ AC_MSG_ERROR([cannot compile a test that uses Boost $1]) ]) ]) ac_objext=$boost_save_ac_objext boost_failed_libs= # Don't bother to ident the following nested for loops, only the 2 # innermost ones matter. for boost_lib_ in $2; do for boost_tag_ in -$boost_cv_lib_tag ''; do for boost_ver_ in -$boost_cv_lib_version ''; do for boost_mt_ in $boost_mt -mt ''; do for boost_rtopt_ in $boost_rtopt '' -d; do for boost_full_suffix in \ $boost_last_suffix \ x$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \ x$boost_tag_$boost_rtopt_$boost_ver_ \ x$boost_tag_$boost_mt_$boost_ver_ \ x$boost_tag_$boost_ver_ do boost_real_suffix=`echo "$boost_full_suffix" | sed 's/^x//'` boost_lib="boost_$boost_lib_$boost_real_suffix" # Avoid testing twice the same lib case $boost_failed_libs in #( (*@$boost_lib@*) continue;; esac # If with_boost is empty, we'll search in /lib first, which is not quite # right so instead we'll try to a location based on where the headers are. boost_tmp_lib=$with_boost test x"$with_boost" = x && boost_tmp_lib=${boost_cv_inc_path%/include} for boost_ldpath in "$boost_tmp_lib/lib" '' \ /opt/local/lib* /usr/local/lib* /opt/lib* /usr/lib* \ "$with_boost" C:/Boost/lib /lib* do # Don't waste time with directories that don't exist. if test x"$boost_ldpath" != x && test ! -e "$boost_ldpath"; then continue fi boost_save_LDFLAGS=$LDFLAGS # Are we looking for a static library? case $boost_ldpath:$boost_rtopt_ in #( (*?*:*s*) # Yes (Non empty boost_ldpath + s in rt opt) Boost_lib_LIBS="$boost_ldpath/lib$boost_lib.$libext" test -e "$Boost_lib_LIBS" || continue;; #( (*) # No: use -lboost_foo to find the shared library. Boost_lib_LIBS="-l$boost_lib";; esac boost_save_LIBS=$LIBS LIBS="$Boost_lib_LIBS $LIBS" test x"$boost_ldpath" != x && LDFLAGS="$LDFLAGS -L$boost_ldpath" dnl First argument of AC_LINK_IFELSE left empty because the test file is dnl generated only once above (before we start the for loops). _BOOST_AC_LINK_IFELSE([], [Boost_lib=yes], [Boost_lib=no]) ac_objext=$boost_save_ac_objext LDFLAGS=$boost_save_LDFLAGS LIBS=$boost_save_LIBS if test x"$Boost_lib" = xyes; then # Check or used cached result of whether or not using -R or # -rpath makes sense. Some implementations of ld, such as for # Mac OS X, require -rpath but -R is the flag known to work on # other systems. https://github.com/tsuna/boost.m4/issues/19 AC_CACHE_VAL([boost_cv_rpath_link_ldflag], [case $boost_ldpath in '') # Nothing to do. boost_cv_rpath_link_ldflag= boost_rpath_link_ldflag_found=yes;; *) for boost_cv_rpath_link_ldflag in -Wl,-R, -Wl,-rpath,; do LDFLAGS="$boost_save_LDFLAGS -L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" LIBS="$Boost_lib_LIBS $boost_save_LIBS" _BOOST_AC_LINK_IFELSE([], [boost_rpath_link_ldflag_found=yes break], [boost_rpath_link_ldflag_found=no]) done ;; esac AS_IF([test "x$boost_rpath_link_ldflag_found" != "xyes"], [AC_MSG_ERROR([Unable to determine whether to use -R or -rpath])]) LDFLAGS=$boost_save_LDFLAGS LIBS=$boost_save_LIBS ]) test x"$boost_ldpath" != x && Boost_lib_LDFLAGS="-L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" Boost_lib_LDPATH="$boost_ldpath" boost_last_suffix="$boost_full_suffix" break 7 else boost_failed_libs="$boost_failed_libs@$boost_lib@" fi done done done done done done done # boost_lib_ rm -f conftest.$ac_objext ]) # --------------------------------------- # # Checks for the various Boost libraries. # # --------------------------------------- # # List of boost libraries: http://www.boost.org/libs/libraries.htm # The page http://beta.boost.org/doc/libs is useful: it gives the first release # version of each library (among other things). # BOOST_DEFUN(LIBRARY, CODE) # -------------------------- # Define BOOST_ as a macro that runs CODE. # # Use indir to avoid the warning on underquoted macro name given to AC_DEFUN. m4_define([BOOST_DEFUN], [m4_indir([AC_DEFUN], m4_toupper([BOOST_$1]), [m4_pushdef([BOOST_Library], [$1])dnl $2 m4_popdef([BOOST_Library])dnl ]) ]) # BOOST_ANY() # ------------ # Look for Boost.Any BOOST_DEFUN([Any], [BOOST_FIND_HEADER([boost/any.hpp])]) # BOOST_ARRAY() # ------------- # Look for Boost.Array BOOST_DEFUN([Array], [BOOST_FIND_HEADER([boost/array.hpp])]) # BOOST_ASIO() # ------------ # Look for Boost.Asio (new in Boost 1.35). BOOST_DEFUN([Asio], [AC_REQUIRE([BOOST_SYSTEM])dnl BOOST_FIND_HEADER([boost/asio.hpp])]) # BOOST_BIMAP() # ------------ # Look for Boost.Bimap BOOST_DEFUN([Bimap], [BOOST_FIND_HEADER([boost/bimap.hpp])]) # BOOST_ASSIGN() # ------------- # Look for Boost.Assign BOOST_DEFUN([Assign], [BOOST_FIND_HEADER([boost/assign.hpp])]) # BOOST_ATOMIC([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------- # Look for Boost.Atomic. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Atomic], [BOOST_FIND_LIB([atomic], [$1], [boost/atomic.hpp], [boost::atomic a;], [ ], [#ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDINT_H #include #endif], [$2]) ])# BOOST_ATOMIC # BOOST_BIND() # ------------ # Look for Boost.Bind. BOOST_DEFUN([Bind], [BOOST_FIND_HEADER([boost/bind.hpp])]) # BOOST_CAST() # ------------ # Look for Boost.Cast BOOST_DEFUN([Cast], [BOOST_FIND_HEADER([boost/cast.hpp])]) # BOOST_CHRONO([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------- # Look for Boost.Chrono. BOOST_DEFUN([Chrono], [# Do we have to check for Boost.System? This link-time dependency was # added as of 1.35.0. If we have a version <1.35, we must not attempt to # find Boost.System as it didn't exist by then. if test $boost_major_version -ge 135; then BOOST_SYSTEM([$1], [$2]) fi # end of the Boost.System check. boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LDFLAGS=$LDFLAGS m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([chrono], [$1], [boost/chrono.hpp], [boost::chrono::thread_clock d;], [], [], [$2]) if test $enable_static_boost = yes && test $boost_major_version -ge 135; then BOOST_CHRONO_LIBS="$BOOST_CHRONO_LIBS $BOOST_SYSTEM_LIBS" fi LIBS=$boost_filesystem_save_LIBS LDFLAGS=$boost_filesystem_save_LDFLAGS ])# BOOST_CHRONO # BOOST_CONTEXT([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Context. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. # # * This library was introduced in Boost 1.51.0 # * The signatures of make_fcontext() and jump_fcontext were changed in 1.56.0 # * A dependency on boost_thread appears in 1.57.0 # * The implementation details were moved to boost::context::detail in 1.61.0 # * 1.61 also introduces execution_context_v2, which is the "lowest common # denominator" for boost::context presence since then. # * boost::context::fiber was introduced in 1.69 and execution_context_v2 was # removed in 1.72 BOOST_DEFUN([Context], [boost_context_save_LIBS=$LIBS boost_context_save_LDFLAGS=$LDFLAGS if test $boost_major_version -ge 157; then BOOST_THREAD([$1], [$2]) m4_pattern_allow([^BOOST_THREAD_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_THREAD_LIBS" LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS" fi if test $boost_major_version -ge 169; then BOOST_FIND_LIB([context], [$1], [boost/context/fiber.hpp], [[ namespace ctx=boost::context; int a; ctx::fiber source{[&a](ctx::fiber&& sink){ a=0; int b=1; for(;;){ sink=std::move(sink).resume(); int next=a+b; a=b; b=next; } return std::move(sink); }}; for (int j=0;j<10;++j) { source=std::move(source).resume(); } return a == 34; ]], [], [], [$2]) elif test $boost_major_version -ge 161; then BOOST_FIND_LIB([context], [$1], [boost/context/execution_context_v2.hpp], [[ namespace ctx=boost::context; int res=0; int n=35; ctx::execution_context source( [n, &res](ctx::execution_context sink, int) mutable { int a=0; int b=1; while(n-->0){ auto result=sink(a); sink=std::move(std::get<0>(result)); auto next=a+b; a=b; b=next; } return sink; }); for(int i=0;i<10;++i){ auto result=source(i); source=std::move(std::get<0>(result)); res = std::get<1>(result); } return res == 34; ]], [], [], [$2]) else BOOST_FIND_LIB([context], [$1], [boost/context/fcontext.hpp],[[ // creates a stack void * stack_pointer = new void*[4096]; std::size_t const size = sizeof(void*[4096]); #if BOOST_VERSION <= 105100 ctx::make_fcontext(&fc, f); return ctx::jump_fcontext(&fcm, &fc, 3) == 6; #else fc = ctx::make_fcontext(stack_pointer, size, f); return ctx::jump_fcontext(&fcm, fc, 3) == 6; #endif ]],[dnl #include #if BOOST_VERSION <= 105100 namespace ctx = boost::ctx; static ctx::fcontext_t fcm, fc; static void f(intptr_t i) { ctx::jump_fcontext(&fc, &fcm, i * 2); } #elif BOOST_VERSION <= 105500 namespace ctx = boost::context; // context static ctx::fcontext_t fcm, *fc; // context-function static void f(intptr_t i) { ctx::jump_fcontext(fc, &fcm, i * 2); } #else namespace ctx = boost::context; // context static ctx::fcontext_t fcm, fc; // context-function static void f(intptr_t i) { ctx::jump_fcontext(&fc, fcm, i * 2); } #endif ], [], [], [$2]) fi LIBS=$boost_context_save_LIBS LDFLAGS=$boost_context_save_LDFLAGS ])# BOOST_CONTEXT # BOOST_CONVERSION() # ------------------ # Look for Boost.Conversion (cast / lexical_cast) BOOST_DEFUN([Conversion], [BOOST_FIND_HEADER([boost/cast.hpp]) BOOST_FIND_HEADER([boost/lexical_cast.hpp]) ])# BOOST_CONVERSION # BOOST_COROUTINE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Coroutine. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. This library was introduced in Boost # 1.53.0 BOOST_DEFUN([Coroutine], [ boost_coroutine_save_LIBS=$LIBS boost_coroutine_save_LDFLAGS=$LDFLAGS # Link-time dependency from coroutine to context BOOST_CONTEXT([$1], [$2]) # Starting from Boost 1.55 a dependency on Boost.System is added if test $boost_major_version -ge 155; then BOOST_SYSTEM([$1], [$2]) fi m4_pattern_allow([^BOOST_(CONTEXT|SYSTEM)_(LIBS|LDFLAGS)]) LIBS="$LIBS $BOOST_CONTEXT_LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_CONTEXT_LDFLAGS" # in 1.53 coroutine was a header only library if test $boost_major_version -eq 153; then AS_IF([test x"$2" = "xno"], [not_found_header='true']) BOOST_FIND_HEADER([boost/coroutine/coroutine.hpp], [$not_found_header]) else BOOST_FIND_LIB([coroutine], [$1], [boost/coroutine/coroutine.hpp], [ #include #if BOOST_VERSION <= 105500 boost::coroutines::coroutine coro; coro.get(); #else boost::coroutines::asymmetric_coroutine::pull_type coro; coro.get(); #endif ], [], [], [$2]) fi # Link-time dependency from coroutine to context, existed only in 1.53, in 1.54 # coroutine doesn't use context from its headers but from its library. if test $boost_major_version -eq 153 || test $enable_static_boost = yes && test $boost_major_version -ge 154; then BOOST_COROUTINE_LIBS="$BOOST_COROUTINE_LIBS $BOOST_CONTEXT_LIBS" BOOST_COROUTINE_LDFLAGS="$BOOST_COROUTINE_LDFLAGS $BOOST_CONTEXT_LDFLAGS" fi if test $enable_static_boost = yes && test $boost_major_version -ge 155; then BOOST_COROUTINE_LIBS="$BOOST_COROUTINE_LIBS $BOOST_SYSTEM_LIBS" BOOST_COROUTINE_LDFLAGS="$BOOST_COROUTINE_LDFLAGS $BOOST_SYSTEM_LDFLAGS" fi LIBS=$boost_coroutine_save_LIBS LDFLAGS=$boost_coroutine_save_LDFLAGS ])# BOOST_COROUTINE # BOOST_CRC() # ----------- # Look for Boost.CRC BOOST_DEFUN([CRC], [BOOST_FIND_HEADER([boost/crc.hpp]) ])# BOOST_CRC # BOOST_DATE_TIME([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Date_Time. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Date_Time], [BOOST_FIND_LIB([date_time], [$1], [boost/date_time/posix_time/posix_time.hpp], [boost::posix_time::ptime t;], [], [], [$2]) ])# BOOST_DATE_TIME # BOOST_EXCEPTION() # ------------ # Look for Boost.Exception BOOST_DEFUN([Exception], [BOOST_FIND_HEADER([boost/exception/all.hpp])]) # BOOST_FILESYSTEM([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------------ # Look for Boost.Filesystem. For the documentation of PREFERRED-RT-OPT, see # the documentation of BOOST_FIND_LIB above. # Do not check for boost/filesystem.hpp because this file was introduced in # 1.34. BOOST_DEFUN([Filesystem], [# Do we have to check for Boost.System? This link-time dependency was # added as of 1.35.0. If we have a version <1.35, we must not attempt to # find Boost.System as it didn't exist by then. if test $boost_major_version -ge 135; then BOOST_SYSTEM([$1], [$2]) fi # end of the Boost.System check. boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LDFLAGS=$LDFLAGS m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([filesystem], [$1], [boost/filesystem/path.hpp], [boost::filesystem::path p;], [], [], [$2]) if test $enable_static_boost = yes && test $boost_major_version -ge 135; then BOOST_FILESYSTEM_LIBS="$BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" fi LIBS=$boost_filesystem_save_LIBS LDFLAGS=$boost_filesystem_save_LDFLAGS ])# BOOST_FILESYSTEM # BOOST_FLYWEIGHT() # ----------------- # Look for Boost.Flyweight. BOOST_DEFUN([Flyweight], [dnl There's a hidden dependency on pthreads. AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl BOOST_FIND_HEADER([boost/flyweight.hpp]) AC_SUBST([BOOST_FLYWEIGHT_LIBS], [$boost_cv_pthread_flag]) ]) # BOOST_FOREACH() # --------------- # Look for Boost.Foreach. BOOST_DEFUN([Foreach], [BOOST_FIND_HEADER([boost/foreach.hpp])]) # BOOST_FORMAT() # -------------- # Look for Boost.Format. # Note: we can't check for boost/format/format_fwd.hpp because the header isn't # standalone. It can't be compiled because it triggers the following error: # boost/format/detail/config_macros.hpp:88: error: 'locale' in namespace 'std' # does not name a type BOOST_DEFUN([Format], [BOOST_FIND_HEADER([boost/format.hpp])]) # BOOST_FUNCTION() # ---------------- # Look for Boost.Function BOOST_DEFUN([Function], [BOOST_FIND_HEADER([boost/function.hpp])]) # BOOST_FUSION() # ----------------- # Look for Boost.Fusion BOOST_DEFUN([Fusion], [BOOST_FIND_HEADER([boost/fusion/sequence.hpp])]) # BOOST_GEOMETRY() # ---------------- # Look for Boost.Geometry (new since 1.47.0). BOOST_DEFUN([Geometry], [BOOST_FIND_HEADER([boost/geometry.hpp]) ])# BOOST_GEOMETRY # BOOST_GRAPH([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------- # Look for Boost.Graphs. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Graph], [boost_graph_save_LIBS=$LIBS boost_graph_save_LDFLAGS=$LDFLAGS # Link-time dependency from graph to regex was added as of 1.40.0. if test $boost_major_version -ge 140; then BOOST_REGEX([$1], [$2]) m4_pattern_allow([^BOOST_REGEX_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_REGEX_LIBS" LDFLAGS="$LDFLAGS $BOOST_REGEX_LDFLAGS" fi BOOST_FIND_LIB([graph], [$1], [boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;], [], [], [$2]) LIBS=$boost_graph_save_LIBS LDFLAGS=$boost_graph_save_LDFLAGS ])# BOOST_GRAPH # BOOST_HASH() # ------------ # Look for Boost.Functional/Hash BOOST_DEFUN([Hash], [BOOST_FIND_HEADER([boost/functional/hash.hpp])]) # BOOST_IOSTREAMS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.IOStreams. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([IOStreams], [BOOST_FIND_LIB([iostreams], [$1], [boost/iostreams/device/file_descriptor.hpp], [boost::iostreams::file_descriptor fd; fd.close();], [], [], [$2]) ])# BOOST_IOSTREAMS # BOOST_ITERATOR() # ------------ # Look for Boost.Iterator BOOST_DEFUN([Iterator], [BOOST_FIND_HEADER([boost/iterator/iterator_adaptor.hpp])]) # BOOST_LAMBDA() # -------------- # Look for Boost.Lambda BOOST_DEFUN([Lambda], [BOOST_FIND_HEADER([boost/lambda/lambda.hpp])]) # BOOST_LOCALE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------- # Look for Boost.Locale BOOST_DEFUN([Locale], [ boost_locale_save_LIBS=$LIBS boost_locale_save_LDFLAGS=$LDFLAGS # require SYSTEM for boost-1.50.0 and up if test $boost_major_version -ge 150; then BOOST_SYSTEM([$1], [$2]) m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" fi # end of the Boost.System check. BOOST_FIND_LIB([locale], [$1], [boost/locale.hpp], [[boost::locale::generator gen; std::locale::global(gen(""));]], [], [], [$2]) LIBS=$boost_locale_save_LIBS LDFLAGS=$boost_locale_save_LDFLAGS ])# BOOST_LOCALE # BOOST_LOG([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------- # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Log], [boost_log_save_LIBS=$LIBS boost_log_save_LDFLAGS=$LDFLAGS BOOST_SYSTEM([$1], [$2]) BOOST_FILESYSTEM([$1], [$2]) BOOST_DATE_TIME([$1], [$2]) m4_pattern_allow([^BOOST_(SYSTEM|FILESYSTEM|DATE_TIME)_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_DATE_TIME_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_DATE_TIME_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([log], [$1], [boost/log/core/core.hpp], [boost::log::attribute a; a.get_value();], [], [], [$2]) LIBS=$boost_log_save_LIBS LDFLAGS=$boost_log_save_LDFLAGS ])# BOOST_LOG # BOOST_LOG_SETUP([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Log_Setup], [boost_log_setup_save_LIBS=$LIBS boost_log_setup_save_LDFLAGS=$LDFLAGS BOOST_LOG([$1]) m4_pattern_allow([^BOOST_LOG_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_LOG_LIBS" LDFLAGS="$LDFLAGS $BOOST_LOG_LDFLAGS" BOOST_FIND_LIB([log_setup], [$1], [boost/log/utility/setup/from_settings.hpp], [boost::log::basic_settings bs; bs.empty();], [], [], [$2]) LIBS=$boost_log_setup_save_LIBS LDFLAGS=$boost_log_setup_save_LDFLAGS ])# BOOST_LOG_SETUP # BOOST_MATH() # ------------ # Look for Boost.Math # TODO: This library isn't header-only but it comes in multiple different # flavors that don't play well with BOOST_FIND_LIB (e.g, libboost_math_c99, # libboost_math_c99f, libboost_math_c99l, libboost_math_tr1, # libboost_math_tr1f, libboost_math_tr1l). This macro must be fixed to do the # right thing anyway. BOOST_DEFUN([Math], [BOOST_FIND_HEADER([boost/math/special_functions.hpp])]) # BOOST_MPI([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------- # Look for Boost MPI. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. Uses MPICXX variable if it is # set, otherwise tries CXX # BOOST_DEFUN([MPI], [boost_save_CXX=${CXX} boost_save_CXXCPP=${CXXCPP} if test x"${MPICXX}" != x; then CXX=${MPICXX} CXXCPP="${MPICXX} -E" fi BOOST_FIND_LIB([mpi], [$1], [boost/mpi.hpp], [int argc = 0; char **argv = 0; boost::mpi::environment env(argc,argv);], [], [], [$2]) CXX=${boost_save_CXX} CXXCPP=${boost_save_CXXCPP} ])# BOOST_MPI # BOOST_MPL() # ------------------ # Look for Boost.MPL BOOST_DEFUN([MPL], [BOOST_FIND_HEADER([boost/mpl/for_each.hpp])]) # BOOST_MULTIARRAY() # ------------------ # Look for Boost.MultiArray BOOST_DEFUN([MultiArray], [BOOST_FIND_HEADER([boost/multi_array.hpp])]) # BOOST_MULTIINDEXCCONTAINER() # ------------------ # Look for Boost.MultiIndexContainer BOOST_DEFUN([MultiIndexContainer], [BOOST_FIND_HEADER([boost/multi_index_container.hpp])]) # BOOST_NUMERIC_UBLAS() # -------------------------- # Look for Boost.NumericUblas (Basic Linear Algebra) BOOST_DEFUN([Numeric_Ublas], [BOOST_FIND_HEADER([boost/numeric/ublas/vector.hpp]) ])# BOOST_NUMERIC_UBLAS # BOOST_NUMERIC_CONVERSION() # -------------------------- # Look for Boost.NumericConversion (policy-based numeric conversion) BOOST_DEFUN([Numeric_Conversion], [BOOST_FIND_HEADER([boost/numeric/conversion/converter.hpp]) ])# BOOST_NUMERIC_CONVERSION # BOOST_OPTIONAL() # ---------------- # Look for Boost.Optional BOOST_DEFUN([Optional], [BOOST_FIND_HEADER([boost/optional.hpp])]) # BOOST_PREPROCESSOR() # -------------------- # Look for Boost.Preprocessor BOOST_DEFUN([Preprocessor], [BOOST_FIND_HEADER([boost/preprocessor/repeat.hpp])]) # BOOST_PROPERTY_TREE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------------- # Look for Boost.Property_Tree. For the documentation of PREFERRED-RT-OPT, # see the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Property_Tree], [BOOST_FIND_LIB([property_tree], [$1], [boost/property_tree/ptree.hpp], [boost::property_tree::ptree pt; boost::property_tree::read_xml d("test", pt);], [], [], [$2]) ])# BOOST_PROPERTY_TREE # BOOST_RANDOM() # -------------------- # Look for Boost.Random BOOST_DEFUN([Random], [BOOST_FIND_HEADER([boost/random/random_number_generator.hpp])]) # BOOST_RANGE() # -------------------- # Look for Boost.Range BOOST_DEFUN([Range], [BOOST_FIND_HEADER([boost/range/adaptors.hpp])]) # BOOST_UNORDERED() # ----------------- # Look for Boost.Unordered BOOST_DEFUN([Unordered], [BOOST_FIND_HEADER([boost/unordered_map.hpp])]) # BOOST_UUID() # ------------ # Look for Boost.Uuid BOOST_DEFUN([Uuid], [BOOST_FIND_HEADER([boost/uuid/uuid.hpp])]) # BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------------- # Look for Boost.Program_options. For the documentation of PREFERRED-RT-OPT, # see the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Program_Options], [BOOST_FIND_LIB([program_options], [$1], [boost/program_options.hpp], [boost::program_options::options_description d("test");], [], [], [$2]) ])# BOOST_PROGRAM_OPTIONS # _BOOST_PYTHON_CONFIG(VARIABLE, FLAG) # ------------------------------------ # Save VARIABLE, and define it via `python-config --FLAG`. # Substitute BOOST_PYTHON_VARIABLE. m4_define([_BOOST_PYTHON_CONFIG], [AC_SUBST([BOOST_PYTHON_$1], [`python-config --$2 2>/dev/null`])dnl boost_python_save_$1=$$1 $1="$$1 $BOOST_PYTHON_$1"]) # BOOST_PYTHON([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------------------------- # Look for Boost.Python. For the documentation of PREFERRED-RT-OPT, # see the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Python], [_BOOST_PYTHON_CONFIG([CPPFLAGS], [includes]) _BOOST_PYTHON_CONFIG([LDFLAGS], [ldflags]) _BOOST_PYTHON_CONFIG([LIBS], [libs]) m4_pattern_allow([^BOOST_PYTHON_MODULE$])dnl BOOST_FIND_LIBS([python], [python python3], [$1], [boost/python.hpp], [], [BOOST_PYTHON_MODULE(empty) {}], [], [$2]) CPPFLAGS=$boost_python_save_CPPFLAGS LDFLAGS=$boost_python_save_LDFLAGS LIBS=$boost_python_save_LIBS ])# BOOST_PYTHON # BOOST_REF() # ----------- # Look for Boost.Ref BOOST_DEFUN([Ref], [BOOST_FIND_HEADER([boost/ref.hpp])]) # BOOST_REGEX([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------- # Look for Boost.Regex. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Regex], [BOOST_FIND_LIB([regex], [$1], [boost/regex.hpp], [boost::regex exp("*"); boost::regex_match("foo", exp);], [], [], [$2]) ])# BOOST_REGEX # BOOST_SCOPE_EXIT() # ------------ # Look for Boost.ScopeExit. BOOST_DEFUN([SCOPE_EXIT], [BOOST_FIND_HEADER([boost/scope_exit.hpp])]) # BOOST_SERIALIZATION([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # --------------------------------------- # Look for Boost.Serialization. For the documentation of PREFERRED-RT-OPT, see # the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Serialization], [BOOST_FIND_LIB([serialization], [$1], [boost/archive/text_oarchive.hpp], [std::ostream* o = 0; // Cheap way to get an ostream... boost::archive::text_oarchive t(*o);], [], [], [$2]) ])# BOOST_SERIALIZATION # BOOST_SIGNALS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # --------------------------------- # Look for Boost.Signals. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Signals], [BOOST_FIND_LIB([signals], [$1], [boost/signal.hpp], [boost::signal s;], [], [], [$2]) ])# BOOST_SIGNALS # BOOST_SIGNALS2() # ---------------- # Look for Boost.Signals2 (new since 1.39.0). BOOST_DEFUN([Signals2], [BOOST_FIND_HEADER([boost/signals2.hpp]) ])# BOOST_SIGNALS2 # BOOST_SMART_PTR() # ----------------- # Look for Boost.SmartPtr BOOST_DEFUN([Smart_Ptr], [BOOST_FIND_HEADER([boost/scoped_ptr.hpp]) BOOST_FIND_HEADER([boost/shared_ptr.hpp]) ]) # BOOST_STATICASSERT() # -------------------- # Look for Boost.StaticAssert BOOST_DEFUN([StaticAssert], [BOOST_FIND_HEADER([boost/static_assert.hpp])]) # BOOST_STRING_ALGO() # ------------------- # Look for Boost.StringAlgo BOOST_DEFUN([String_Algo], [BOOST_FIND_HEADER([boost/algorithm/string.hpp]) ]) # BOOST_SYSTEM([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------------------------- # Look for Boost.System. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. This library was introduced in Boost # 1.35.0. BOOST_DEFUN([System], [BOOST_FIND_LIB([system], [$1], [boost/system/error_code.hpp], [boost::system::error_code e; e.clear();], [], [], [$2]) ])# BOOST_SYSTEM # BOOST_TEST([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------ # Look for Boost.Test. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Test], [m4_pattern_allow([^BOOST_CHECK$])dnl BOOST_FIND_LIB([unit_test_framework], [$1], [boost/test/unit_test.hpp], [BOOST_CHECK(2 == 2);], [using boost::unit_test::test_suite; test_suite* init_unit_test_suite(int argc, char ** argv) { return NULL; }], [], [$2]) ])# BOOST_TEST # BOOST_THREAD([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # --------------------------------- # Look for Boost.Thread. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Thread], [dnl Having the pthread flag is required at least on GCC3 where dnl boost/thread.hpp would complain if we try to compile without dnl -pthread on GNU/Linux. AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl boost_thread_save_LIBS=$LIBS boost_thread_save_LDFLAGS=$LDFLAGS boost_thread_save_CPPFLAGS=$CPPFLAGS # Link-time dependency from thread to system was added as of 1.49.0. if test $boost_major_version -ge 149; then BOOST_SYSTEM([$1], [$2]) fi # end of the Boost.System check. m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" CPPFLAGS="$CPPFLAGS $boost_cv_pthread_flag" # When compiling for the Windows platform, the threads library is named # differently. This suffix doesn't exist in new versions of Boost, or # possibly new versions of GCC on mingw I am assuming it's Boost's change for # now and I am setting version to 1.48, for lack of knowledge as to when this # change occurred. if test $boost_major_version -lt 148; then case $host_os in (*mingw*) boost_thread_lib_ext=_win32;; esac fi BOOST_FIND_LIBS([thread], [thread$boost_thread_lib_ext], [$1], [boost/thread.hpp], [boost::thread t; boost::mutex m;], [], [], [$2]) case $host_os in (*mingw*) boost_thread_w32_socket_link=-lws2_32;; esac BOOST_THREAD_LIBS="$BOOST_THREAD_LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag $boost_thread_w32_socket_link" BOOST_THREAD_LDFLAGS="$BOOST_SYSTEM_LDFLAGS" BOOST_CPPFLAGS="$BOOST_CPPFLAGS $boost_cv_pthread_flag" LIBS=$boost_thread_save_LIBS LDFLAGS=$boost_thread_save_LDFLAGS CPPFLAGS=$boost_thread_save_CPPFLAGS ])# BOOST_THREAD AU_ALIAS([BOOST_THREADS], [BOOST_THREAD]) # BOOST_TOKENIZER() # ----------------- # Look for Boost.Tokenizer BOOST_DEFUN([Tokenizer], [BOOST_FIND_HEADER([boost/tokenizer.hpp])]) # BOOST_TRIBOOL() # --------------- # Look for Boost.Tribool BOOST_DEFUN([Tribool], [BOOST_FIND_HEADER([boost/logic/tribool_fwd.hpp]) BOOST_FIND_HEADER([boost/logic/tribool.hpp]) ]) # BOOST_TUPLE() # ------------- # Look for Boost.Tuple BOOST_DEFUN([Tuple], [BOOST_FIND_HEADER([boost/tuple/tuple.hpp])]) # BOOST_TYPETRAITS() # -------------------- # Look for Boost.TypeTraits BOOST_DEFUN([TypeTraits], [BOOST_FIND_HEADER([boost/type_traits.hpp])]) # BOOST_UTILITY() # --------------- # Look for Boost.Utility (noncopyable, result_of, base-from-member idiom, # etc.) BOOST_DEFUN([Utility], [BOOST_FIND_HEADER([boost/utility.hpp])]) # BOOST_VARIANT() # --------------- # Look for Boost.Variant. BOOST_DEFUN([Variant], [BOOST_FIND_HEADER([boost/variant/variant_fwd.hpp]) BOOST_FIND_HEADER([boost/variant.hpp])]) # BOOST_POINTER_CONTAINER() # ------------------------ # Look for Boost.PointerContainer BOOST_DEFUN([Pointer_Container], [BOOST_FIND_HEADER([boost/ptr_container/ptr_deque.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_list.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_vector.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_array.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_set.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_map.hpp]) ])# BOOST_POINTER_CONTAINER # BOOST_WAVE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------ # NOTE: If you intend to use Wave/Spirit with thread support, make sure you # call BOOST_THREAD first. # Look for Boost.Wave. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Wave], [AC_REQUIRE([BOOST_FILESYSTEM])dnl AC_REQUIRE([BOOST_DATE_TIME])dnl boost_wave_save_LIBS=$LIBS boost_wave_save_LDFLAGS=$LDFLAGS m4_pattern_allow([^BOOST_((FILE)?SYSTEM|DATE_TIME|THREAD)_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_DATE_TIME_LIBS \ $BOOST_THREAD_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS \ $BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS" BOOST_FIND_LIB([wave], [$1], [boost/wave.hpp], [boost::wave::token_id id; get_token_name(id);], [], [], [$2]) LIBS=$boost_wave_save_LIBS LDFLAGS=$boost_wave_save_LDFLAGS ])# BOOST_WAVE # BOOST_XPRESSIVE() # ----------------- # Look for Boost.Xpressive (new since 1.36.0). BOOST_DEFUN([Xpressive], [BOOST_FIND_HEADER([boost/xpressive/xpressive.hpp])]) # ----------------- # # Internal helpers. # # ----------------- # # _BOOST_PTHREAD_FLAG() # --------------------- # Internal helper for BOOST_THREAD. Computes boost_cv_pthread_flag # which must be used in CPPFLAGS and LIBS. # # Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3, # boost/thread.hpp will trigger a #error if -pthread isn't used: # boost/config/requires_threads.hpp:47:5: #error "Compiler threading support # is not turned on. Please set the correct command line options for # threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)" # # Based on ACX_PTHREAD: http://autoconf-archive.cryp.to/acx_pthread.html AC_DEFUN([_BOOST_PTHREAD_FLAG], [AC_REQUIRE([AC_PROG_CXX])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_LANG_PUSH([C++])dnl AC_CACHE_CHECK([for the flags needed to use pthreads], [boost_cv_pthread_flag], [ boost_cv_pthread_flag= # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # (none): in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -lpthreads: AIX (must check this before -lpthread) # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads) # -pthreads: Solaris/GCC # -mthreads: MinGW32/GCC, Lynx/GCC # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # -lpthread: GNU Linux, etc. # --thread-safe: KAI C++ case $host_os in #( *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: boost_pthread_flags="-pthreads -lpthread -mt -pthread";; #( *) boost_pthread_flags="-lpthreads -Kthread -kthread -llthread -pthread \ -pthreads -mthreads -lpthread --thread-safe -mt";; esac # Generate the test file. AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include void *f(void*){ return 0; }], [pthread_t th; pthread_create(&th,0,f,0); pthread_join(th,0); pthread_attr_t attr; pthread_attr_init(&attr); pthread_cleanup_push(0, 0); pthread_cleanup_pop(0);])]) for boost_pthread_flag in '' $boost_pthread_flags; do boost_pthread_ok=false dnl Re-use the test file already generated. boost_pthreads__save_LIBS=$LIBS LIBS="$LIBS $boost_pthread_flag" AC_LINK_IFELSE([], [if grep ".*$boost_pthread_flag" conftest.err; then echo "This flag seems to have triggered warnings" >&AS_MESSAGE_LOG_FD else boost_pthread_ok=:; boost_cv_pthread_flag=$boost_pthread_flag fi]) LIBS=$boost_pthreads__save_LIBS $boost_pthread_ok && break done ]) AC_LANG_POP([C++])dnl ])# _BOOST_PTHREAD_FLAG # _BOOST_gcc_test(MAJOR, MINOR) # ----------------------------- # Internal helper for _BOOST_FIND_COMPILER_TAG. m4_define([_BOOST_gcc_test], ["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC @ gcc$1$2"])dnl # _BOOST_mingw_test(MAJOR, MINOR) # ----------------------------- # Internal helper for _BOOST_FIND_COMPILER_TAG. m4_define([_BOOST_mingw_test], ["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC && \ (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \ || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw$1$2"])dnl # _BOOST_FIND_COMPILER_TAG() # -------------------------- # Internal. When Boost is installed without --layout=system, each library # filename will hold a suffix that encodes the compiler used during the # build. The Boost build system seems to call this a `tag'. AC_DEFUN([_BOOST_FIND_COMPILER_TAG], [AC_REQUIRE([AC_PROG_CXX])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_CACHE_CHECK([for the toolset name used by Boost for $CXX], [boost_cv_lib_tag], [boost_cv_lib_tag=unknown if test x$boost_cv_inc_path != xno; then AC_LANG_PUSH([C++])dnl # The following tests are mostly inspired by boost/config/auto_link.hpp # The list is sorted to most recent/common to oldest compiler (in order # to increase the likelihood of finding the right compiler with the # least number of compilation attempt). # Beware that some tests are sensible to the order (for instance, we must # look for MinGW before looking for GCC3). # I used one compilation test per compiler with a #error to recognize # each compiler so that it works even when cross-compiling (let me know # if you know a better approach). # Known missing tags (known from Boost's tools/build/v2/tools/common.jam): # como, edg, kcc, bck, mp, sw, tru, xlc # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines # the same defines as GCC's). for i in \ "defined __clang__ && __clang_major__ == 12 && __clang_minor__ == 0 @ clang120" \ "defined __clang__ && __clang_major__ == 11 && __clang_minor__ == 0 @ clang110" \ "defined __clang__ && __clang_major__ == 10 && __clang_minor__ == 0 @ clang100" \ "defined __clang__ && __clang_major__ == 9 && __clang_minor__ == 0 @ clang90" \ "defined __clang__ && __clang_major__ == 8 && __clang_minor__ == 0 @ clang80" \ "defined __clang__ && __clang_major__ == 7 && __clang_minor__ == 0 @ clang70" \ "defined __clang__ && __clang_major__ == 6 && __clang_minor__ == 0 @ clang60" \ "defined __clang__ && __clang_major__ == 5 && __clang_minor__ == 0 @ clang50" \ "defined __clang__ && __clang_major__ == 4 && __clang_minor__ == 0 @ clang40" \ "defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 9 @ clang39" \ "defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 8 @ clang38" \ "defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 7 @ clang37" \ _BOOST_mingw_test(10, 2) \ _BOOST_gcc_test(10, 2) \ _BOOST_mingw_test(10, 1) \ _BOOST_gcc_test(10, 1) \ _BOOST_mingw_test(9, 3) \ _BOOST_gcc_test(9, 3) \ _BOOST_mingw_test(9, 2) \ _BOOST_gcc_test(9, 2) \ _BOOST_mingw_test(9, 1) \ _BOOST_gcc_test(9, 1) \ _BOOST_mingw_test(9, 0) \ _BOOST_gcc_test(9, 0) \ _BOOST_mingw_test(8, 4) \ _BOOST_gcc_test(8, 4) \ _BOOST_mingw_test(8, 3) \ _BOOST_gcc_test(8, 3) \ _BOOST_mingw_test(8, 2) \ _BOOST_gcc_test(8, 2) \ _BOOST_mingw_test(8, 1) \ _BOOST_gcc_test(8, 1) \ _BOOST_mingw_test(8, 0) \ _BOOST_gcc_test(8, 0) \ _BOOST_mingw_test(7, 4) \ _BOOST_gcc_test(7, 4) \ _BOOST_mingw_test(7, 3) \ _BOOST_gcc_test(7, 3) \ _BOOST_mingw_test(7, 2) \ _BOOST_gcc_test(7, 2) \ _BOOST_mingw_test(7, 1) \ _BOOST_gcc_test(7, 1) \ _BOOST_mingw_test(7, 0) \ _BOOST_gcc_test(7, 0) \ _BOOST_mingw_test(6, 5) \ _BOOST_gcc_test(6, 5) \ _BOOST_mingw_test(6, 4) \ _BOOST_gcc_test(6, 4) \ _BOOST_mingw_test(6, 3) \ _BOOST_gcc_test(6, 3) \ _BOOST_mingw_test(6, 2) \ _BOOST_gcc_test(6, 2) \ _BOOST_mingw_test(6, 1) \ _BOOST_gcc_test(6, 1) \ _BOOST_mingw_test(6, 0) \ _BOOST_gcc_test(6, 0) \ _BOOST_mingw_test(5, 5) \ _BOOST_gcc_test(5, 5) \ _BOOST_mingw_test(5, 4) \ _BOOST_gcc_test(5, 4) \ _BOOST_mingw_test(5, 3) \ _BOOST_gcc_test(5, 3) \ _BOOST_mingw_test(5, 2) \ _BOOST_gcc_test(5, 2) \ _BOOST_mingw_test(5, 1) \ _BOOST_gcc_test(5, 1) \ _BOOST_mingw_test(5, 0) \ _BOOST_gcc_test(5, 0) \ _BOOST_mingw_test(4, 10) \ _BOOST_gcc_test(4, 10) \ _BOOST_mingw_test(4, 9) \ _BOOST_gcc_test(4, 9) \ _BOOST_mingw_test(4, 8) \ _BOOST_gcc_test(4, 8) \ _BOOST_mingw_test(4, 7) \ _BOOST_gcc_test(4, 7) \ _BOOST_mingw_test(4, 6) \ _BOOST_gcc_test(4, 6) \ _BOOST_mingw_test(4, 5) \ _BOOST_gcc_test(4, 5) \ _BOOST_mingw_test(4, 4) \ _BOOST_gcc_test(4, 4) \ _BOOST_mingw_test(4, 3) \ _BOOST_gcc_test(4, 3) \ _BOOST_mingw_test(4, 2) \ _BOOST_gcc_test(4, 2) \ _BOOST_mingw_test(4, 1) \ _BOOST_gcc_test(4, 1) \ _BOOST_mingw_test(4, 0) \ _BOOST_gcc_test(4, 0) \ "defined __GNUC__ && __GNUC__ == 3 && !defined __ICC \ && (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \ || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw" \ _BOOST_gcc_test(3, 4) \ _BOOST_gcc_test(3, 3) \ "defined _MSC_VER && _MSC_VER >= 1500 @ vc90" \ "defined _MSC_VER && _MSC_VER == 1400 @ vc80" \ _BOOST_gcc_test(3, 2) \ "defined _MSC_VER && _MSC_VER == 1310 @ vc71" \ _BOOST_gcc_test(3, 1) \ _BOOST_gcc_test(3, 0) \ "defined __BORLANDC__ @ bcb" \ "defined __ICC && (defined __unix || defined __unix__) @ il" \ "defined __ICL @ iw" \ "defined _MSC_VER && _MSC_VER == 1300 @ vc7" \ _BOOST_gcc_test(2, 95) \ "defined __MWERKS__ && __MWERKS__ <= 0x32FF @ cw9" \ "defined _MSC_VER && _MSC_VER < 1300 && !defined UNDER_CE @ vc6" \ "defined _MSC_VER && _MSC_VER < 1300 && defined UNDER_CE @ evc4" \ "defined __MWERKS__ && __MWERKS__ <= 0x31FF @ cw8" do boost_tag_test=`expr "X$i" : 'X\([[^@]]*\) @ '` boost_tag=`expr "X$i" : 'X[[^@]]* @ \(.*\)'` AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if $boost_tag_test /* OK */ #else # error $boost_tag_test #endif ]])], [boost_cv_lib_tag=$boost_tag; break], []) done AC_LANG_POP([C++])dnl case $boost_cv_lib_tag in #( # Some newer (>= 1.35?) versions of Boost seem to only use "gcc" as opposed # to "gcc41" for instance. *-gcc | *'-gcc ') :;; #( Don't re-add -gcc: it's already in there. gcc*) boost_tag_x= case $host_os in #( darwin*) if test $boost_major_version -ge 136; then # The `x' added in r46793 of Boost. boost_tag_x=x fi;; esac # We can specify multiple tags in this variable because it's used by # BOOST_FIND_LIB that does a `for tag in -$boost_cv_lib_tag' ... boost_cv_lib_tag="$boost_tag_x$boost_cv_lib_tag -${boost_tag_x}gcc" ;; #( unknown) AC_MSG_WARN([[could not figure out which toolset name to use for $CXX]]) boost_cv_lib_tag= ;; esac fi])dnl end of AC_CACHE_CHECK ])# _BOOST_FIND_COMPILER_TAG # _BOOST_GUESS_WHETHER_TO_USE_MT() # -------------------------------- # Compile a small test to try to guess whether we should favor MT (Multi # Thread) flavors of Boost. Sets boost_guess_use_mt accordingly. AC_DEFUN([_BOOST_GUESS_WHETHER_TO_USE_MT], [# Check whether we do better use `mt' even though we weren't ask to. AC_LANG_PUSH([C++])dnl AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if defined _REENTRANT || defined _MT || defined __MT__ /* use -mt */ #else # error MT not needed #endif ]])], [boost_guess_use_mt=:], [boost_guess_use_mt=false]) AC_LANG_POP([C++])dnl ]) # _BOOST_AC_LINK_IFELSE(PROGRAM, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # ------------------------------------------------------------------- # Fork of _AC_LINK_IFELSE that preserves conftest.o across calls. Fragile, # will break when Autoconf changes its internals. Requires that you manually # rm -f conftest.$ac_objext in between to really different tests, otherwise # you will try to link a conftest.o left behind by a previous test. # Used to aggressively optimize BOOST_FIND_LIB (see the big comment in this # macro). # # Don't use "break" in the actions, as it would short-circuit some code # this macro runs after the actions. m4_define([_BOOST_AC_LINK_IFELSE], [m4_ifvaln([$1], [AC_LANG_CONFTEST([$1])])dnl rm -f conftest$ac_exeext boost_save_ac_ext=$ac_ext boost_use_source=: # If we already have a .o, re-use it. We change $ac_ext so that $ac_link # tries to link the existing object file instead of compiling from source. test -f conftest.$ac_objext && ac_ext=$ac_objext && boost_use_source=false && _AS_ECHO_LOG([re-using the existing conftest.$ac_objext]) AS_IF([_AC_DO_STDERR($ac_link) && { test -z "$ac_[]_AC_LANG_ABBREV[]_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_executable_p conftest$ac_exeext dnl FIXME: use AS_TEST_X instead when 2.61 is widespread enough. }], [$2], [if $boost_use_source; then _AC_MSG_LOG_CONFTEST fi $3]) ac_objext=$boost_save_ac_objext ac_ext=$boost_save_ac_ext dnl Delete also the IPA/IPO (Inter Procedural Analysis/Optimization) dnl information created by the PGI compiler (conftest_ipa8_conftest.oo), dnl as it would interfere with the next link command. rm -f core conftest.err conftest_ipa8_conftest.oo \ conftest$ac_exeext m4_ifval([$1], [conftest.$ac_ext])[]dnl ])# _BOOST_AC_LINK_IFELSE # Local Variables: # mode: autoconf # End: weakforced-2.10.2/m4/libcurl.m4000066400000000000000000000256371461473602600161520ustar00rootroot00000000000000#*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # # Copyright (C) 2006, David Shaw # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at https://curl.haxx.se/docs/copyright.html. # # You may opt to use, copy, modify, merge, publish, distribute and/or sell # copies of the Software, and permit persons to whom the Software is # furnished to do so, under the terms of the COPYING file. # # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY # KIND, either express or implied. # ########################################################################### # LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION], # [ACTION-IF-YES], [ACTION-IF-NO]) # ---------------------------------------------------------- # David Shaw May-09-2006 # # Checks for libcurl. DEFAULT-ACTION is the string yes or no to # specify whether to default to --with-libcurl or --without-libcurl. # If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the # minimum version of libcurl to accept. Pass the version as a regular # version number like 7.10.1. If not supplied, any version is # accepted. ACTION-IF-YES is a list of shell commands to run if # libcurl was successfully found and passed the various tests. # ACTION-IF-NO is a list of shell commands that are run otherwise. # Note that using --without-libcurl does run ACTION-IF-NO. # # This macro #defines HAVE_LIBCURL if a working libcurl setup is # found, and sets @LIBCURL@ and @LIBCURL_CPPFLAGS@ to the necessary # values. Other useful defines are LIBCURL_FEATURE_xxx where xxx are # the various features supported by libcurl, and LIBCURL_PROTOCOL_yyy # where yyy are the various protocols supported by libcurl. Both xxx # and yyy are capitalized. See the list of AH_TEMPLATEs at the top of # the macro for the complete list of possible defines. Shell # variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also # defined to 'yes' for those features and protocols that were found. # Note that xxx and yyy keep the same capitalization as in the # curl-config list (e.g. it's "HTTP" and not "http"). # # Users may override the detected values by doing something like: # LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure # # For the sake of sanity, this macro assumes that any libcurl that is # found is after version 7.7.2, the first version that included the # curl-config script. Note that it is very important for people # packaging binary versions of libcurl to include this script! # Without curl-config, we can only guess what protocols are available, # or use curl_version_info to figure it out at runtime. AC_DEFUN([LIBCURL_CHECK_CONFIG], [ AH_TEMPLATE([LIBCURL_FEATURE_SSL],[Defined if libcurl supports SSL]) AH_TEMPLATE([LIBCURL_FEATURE_KRB4],[Defined if libcurl supports KRB4]) AH_TEMPLATE([LIBCURL_FEATURE_IPV6],[Defined if libcurl supports IPv6]) AH_TEMPLATE([LIBCURL_FEATURE_LIBZ],[Defined if libcurl supports libz]) AH_TEMPLATE([LIBCURL_FEATURE_ASYNCHDNS],[Defined if libcurl supports AsynchDNS]) AH_TEMPLATE([LIBCURL_FEATURE_IDN],[Defined if libcurl supports IDN]) AH_TEMPLATE([LIBCURL_FEATURE_SSPI],[Defined if libcurl supports SSPI]) AH_TEMPLATE([LIBCURL_FEATURE_NTLM],[Defined if libcurl supports NTLM]) AH_TEMPLATE([LIBCURL_PROTOCOL_HTTP],[Defined if libcurl supports HTTP]) AH_TEMPLATE([LIBCURL_PROTOCOL_HTTPS],[Defined if libcurl supports HTTPS]) AH_TEMPLATE([LIBCURL_PROTOCOL_FTP],[Defined if libcurl supports FTP]) AH_TEMPLATE([LIBCURL_PROTOCOL_FTPS],[Defined if libcurl supports FTPS]) AH_TEMPLATE([LIBCURL_PROTOCOL_FILE],[Defined if libcurl supports FILE]) AH_TEMPLATE([LIBCURL_PROTOCOL_TELNET],[Defined if libcurl supports TELNET]) AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP]) AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT]) AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP]) AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP]) AH_TEMPLATE([LIBCURL_PROTOCOL_POP3],[Defined if libcurl supports POP3]) AH_TEMPLATE([LIBCURL_PROTOCOL_IMAP],[Defined if libcurl supports IMAP]) AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP]) AC_ARG_WITH(libcurl, AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]), [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])]) if test "$_libcurl_with" != "no" ; then AC_PROG_AWK _libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'" _libcurl_try_link=yes if test -d "$_libcurl_with" ; then LIBCURL_CPPFLAGS="-I$withval/include" _libcurl_ldflags="-L$withval/lib" AC_PATH_PROG([_libcurl_config],[curl-config],[], ["$withval/bin"]) else AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH]) fi if test x$_libcurl_config != "x" ; then AC_CACHE_CHECK([for the version of libcurl], [libcurl_cv_lib_curl_version], [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`]) _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse` _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse` if test $_libcurl_wanted -gt 0 ; then AC_CACHE_CHECK([for libcurl >= version $2], [libcurl_cv_lib_version_ok], [ if test $_libcurl_version -ge $_libcurl_wanted ; then libcurl_cv_lib_version_ok=yes else libcurl_cv_lib_version_ok=no fi ]) fi if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then if test x"$LIBCURL_CPPFLAGS" = "x" ; then LIBCURL_CPPFLAGS=`$_libcurl_config --cflags` fi if test x"$LIBCURL" = "x" ; then LIBCURL=`$_libcurl_config --libs` # This is so silly, but Apple actually has a bug in their # curl-config script. Fixed in Tiger, but there are still # lots of Panther installs around. case "${host}" in powerpc-apple-darwin7*) LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'` ;; esac fi # All curl-config scripts support --feature _libcurl_features=`$_libcurl_config --feature` # Is it modern enough to have --protocols? (7.12.4) if test $_libcurl_version -ge 461828 ; then _libcurl_protocols=`$_libcurl_config --protocols` fi else _libcurl_try_link=no fi unset _libcurl_wanted fi if test $_libcurl_try_link = yes ; then # we didn't find curl-config, so let's see if the user-supplied # link line (or failing that, "-lcurl") is enough. LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"} AC_CACHE_CHECK([whether libcurl is usable], [libcurl_cv_lib_curl_usable], [ _libcurl_save_cppflags=$CPPFLAGS CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS" _libcurl_save_libs=$LIBS LIBS="$LIBCURL $LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ /* Try and use a few common options to force a failure if we are missing symbols or can't link. */ int x; curl_easy_setopt(NULL,CURLOPT_URL,NULL); x=CURL_ERROR_SIZE; x=CURLOPT_WRITEFUNCTION; x=CURLOPT_WRITEDATA; x=CURLOPT_ERRORBUFFER; x=CURLOPT_STDERR; x=CURLOPT_VERBOSE; if (x) {;} ]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no) CPPFLAGS=$_libcurl_save_cppflags LIBS=$_libcurl_save_libs unset _libcurl_save_cppflags unset _libcurl_save_libs ]) if test $libcurl_cv_lib_curl_usable = yes ; then # Does curl_free() exist in this version of libcurl? # If not, fake it with free() _libcurl_save_cppflags=$CPPFLAGS CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS" _libcurl_save_libs=$LIBS LIBS="$LIBS $LIBCURL" AC_CHECK_FUNC(curl_free,, AC_DEFINE(curl_free,free, [Define curl_free() as free() if our version of curl lacks curl_free.])) CPPFLAGS=$_libcurl_save_cppflags LIBS=$_libcurl_save_libs unset _libcurl_save_cppflags unset _libcurl_save_libs AC_DEFINE(HAVE_LIBCURL,1, [Define to 1 if you have a functional curl library.]) AC_SUBST(LIBCURL_CPPFLAGS) AC_SUBST(LIBCURL) for _libcurl_feature in $_libcurl_features ; do AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1]) eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes done if test "x$_libcurl_protocols" = "x" ; then # We don't have --protocols, so just assume that all # protocols are available _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP" if test x$libcurl_feature_SSL = xyes ; then _libcurl_protocols="$_libcurl_protocols HTTPS" # FTPS wasn't standards-compliant until version # 7.11.0 (0x070b00 == 461568) if test $_libcurl_version -ge 461568; then _libcurl_protocols="$_libcurl_protocols FTPS" fi fi # RTSP, IMAP, POP3 and SMTP were added in # 7.20.0 (0x071400 == 463872) if test $_libcurl_version -ge 463872; then _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP" fi fi for _libcurl_protocol in $_libcurl_protocols ; do AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1]) eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes done else unset LIBCURL unset LIBCURL_CPPFLAGS fi fi unset _libcurl_try_link unset _libcurl_version_parse unset _libcurl_config unset _libcurl_feature unset _libcurl_features unset _libcurl_protocol unset _libcurl_protocols unset _libcurl_version unset _libcurl_ldflags fi if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes ; then # This is the IF-NO path ifelse([$4],,:,[$4]) else # This is the IF-YES path ifelse([$3],,:,[$3]) fi unset _libcurl_with ])dnl weakforced-2.10.2/m4/pdns_check_libcrypto.m4000066400000000000000000000100301461473602600206630ustar00rootroot00000000000000# SYNOPSIS # # PDNS_CHECK_LIBCRYPTO([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for OpenSSL's libcrypto in a number of default spots, or in a # user-selected spot (via --with-libcrypto). Sets # # LIBCRYPTO_INCLUDES to the include directives required # LIBCRYPTO_LIBS to the -l directives required # LIBCRYPTO_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets LIBCRYPTO_INCLUDES such that source files should use the # openssl/ directory in include directives: # # #include # # LICENSE # # Taken and modified from AX_CHECK_OPENSSL by: # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AU_ALIAS([CHECK_LIBCRYPTO], [PDNS_CHECK_LIBCRYPTO]) AC_DEFUN([PDNS_CHECK_LIBCRYPTO], [ found=false AC_ARG_WITH([libcrypto], [AS_HELP_STRING([--with-libcrypto=DIR], [root of the OpenSSL directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-libcrypto value]) ;; *) ssldirs="$withval" ;; esac ], [ # if pkg-config is installed and openssl has installed a .pc file, # then use that information and don't search ssldirs AC_CHECK_TOOL([PKG_CONFIG], [pkg-config]) if test x"$PKG_CONFIG" != x""; then LIBCRYPTO_LDFLAGS=`$PKG_CONFIG libcrypto --libs-only-L 2>/dev/null` if test $? = 0; then LIBCRYPTO_LIBS=`$PKG_CONFIG libcrypto --libs-only-l 2>/dev/null` LIBCRYPTO_INCLUDES=`$PKG_CONFIG libcrypto --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default ssldirs if ! $found; then ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" fi ] ) # note that we #include , so the OpenSSL headers have to be in # an 'openssl' subdirectory if ! $found; then LIBCRYPTO_INCLUDES= for ssldir in $ssldirs; do AC_MSG_CHECKING([for openssl/crypto.h in $ssldir]) if test -f "$ssldir/include/openssl/crypto.h"; then LIBCRYPTO_INCLUDES="-I$ssldir/include" LIBCRYPTO_LDFLAGS="-L$ssldir/lib" LIBCRYPTO_LIBS="-lcrypto" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS AC_MSG_CHECKING([whether compiling and linking against OpenSSL's libcrypto works]) echo "Trying link with LIBCRYPTO_LDFLAGS=$LIBCRYPTO_LDFLAGS;" \ "LIBCRYPTO_LIBS=$LIBCRYPTO_LIBS; LIBCRYPTO_INCLUDES=$LIBCRYPTO_INCLUDES" >&AS_MESSAGE_LOG_FD save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $LIBCRYPTO_LDFLAGS" LIBS="$LIBCRYPTO_LIBS $LIBS" CPPFLAGS="$LIBCRYPTO_INCLUDES $CPPFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [ERR_load_CRYPTO_strings()])], [ AC_MSG_RESULT([yes]) $1 ], [ AC_MSG_RESULT([no]) $2 ]) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" AC_SUBST([LIBCRYPTO_INCLUDES]) AC_SUBST([LIBCRYPTO_LIBS]) AC_SUBST([LIBCRYPTO_LDFLAGS]) ]) weakforced-2.10.2/m4/pdns_check_libdrogon.m4000066400000000000000000000063051461473602600206450ustar00rootroot00000000000000# SYNOPSIS # # PDNS_CHECK_LIBDROGON([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for libdrogon in a number of default spots, or in a # user-selected spot (via --with-libdrogon). Sets # # LIBDROGON_INCLUDES to the include directives required # LIBDROGON_LIBS to the -l directives required # LIBDROGON_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets LIBDROGON_INCLUDES such that source files should use the # drogon/ directory in include directives: # # #include # # LICENSE # # Taken and modified from AX_CHECK_OPENSSL by: # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AU_ALIAS([CHECK_LIBDROGON], [PDNS_CHECK_LIBDROGON]) AC_DEFUN([PDNS_CHECK_LIBDROGON], [ found=false AC_ARG_WITH([libdrogon], [AS_HELP_STRING([--with-libdrogon=DIR], [root of the drogon directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-libdrogon value]) ;; *) drogondirs="$withval" ;; esac ], [ # if pkg-config is installed and drogon has installed a .pc file, # then use that information and don't search drogondirs AC_CHECK_TOOL([PKG_CONFIG], [libdrogon pkg-config]) if test x"$PKG_CONFIG" != x""; then LIBDROGON_LDFLAGS=`$PKG_CONFIG libdrogon --libs-only-L 2>/dev/null` if test $? = 0; then LIBDROGON_LIBS=`$PKG_CONFIG libdrogon --libs-only-l 2>/dev/null` LIBDROGON_INCLUDES=`$PKG_CONFIG libdrogon --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default dirs if ! $found; then drogondirs="/usr/local /usr/local/opt /usr/pkg /usr" fi ] ) # note that we #include , so the drogon headers have to be in # a 'drogon' subdirectory if ! $found; then LIBDROGON_INCLUDES= for drogondir in $drogondirs; do AC_MSG_CHECKING([for drogon/drogon.h in $drogondir]) if test -f "$drogondir/include/drogon/drogon.h"; then LIBDROGON_INCLUDES="-I$drogondir/include" LIBDROGON_LDFLAGS="-L$drogondir/lib" LIBDROGON_LIBS="-ldrogon -ltrantor -ljsoncpp -lz" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done fi if ! $found; then AC_MSG_NOTICE([Did not find libdrogon]) $2 else AC_MSG_NOTICE([Found libdrogon]) $1 fi AC_SUBST([LIBDROGON_INCLUDES]) AC_SUBST([LIBDROGON_LIBS]) AC_SUBST([LIBDROGON_LDFLAGS]) ]) weakforced-2.10.2/m4/pdns_check_libjsoncpp.m4000066400000000000000000000062671461473602600210400ustar00rootroot00000000000000# SYNOPSIS # # PDNS_CHECK_LIBJSONCPP([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for libjsoncpp in a number of default spots, or in a # user-selected spot (via --with-libjsoncpp). Sets # # LIBJSONCPP_INCLUDES to the include directives required # LIBJSONCPP_LIBS to the -l directives required # LIBJSONCPP_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets LIBJSONCPP_INCLUDES such that source files should use the # json/ directory in include directives: # # #include # # LICENSE # # Taken and modified from AX_CHECK_OPENSSL by: # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AU_ALIAS([CHECK_LIBJSONCPP], [PDNS_CHECK_LIBJSONCPP]) AC_DEFUN([PDNS_CHECK_LIBJSONCPP], [ found=false AC_ARG_WITH([libjsoncpp], [AS_HELP_STRING([--with-libjsoncpp=DIR], [root of the jsoncpp directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-libjsoncpp value]) ;; *) jsoncppdirs="$withval" ;; esac ], [ # if pkg-config is installed and jsoncpp has installed a .pc file, # then use that information and don't search jsoncppdirs AC_CHECK_TOOL([PKG_CONFIG], [jsoncpp pkg-config]) if test x"$PKG_CONFIG" != x""; then LIBJSONCPP_LDFLAGS=`$PKG_CONFIG jsoncpp --libs-only-L 2>/dev/null` if test $? = 0; then LIBJSONCPP_LIBS=`$PKG_CONFIG jsoncpp --libs-only-l 2>/dev/null` LIBJSONCPP_INCLUDES=`$PKG_CONFIG jsoncpp --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default dirs if ! $found; then jsoncppdirs="/usr/local /usr/local/opt /usr/pkg /usr" fi ] ) # note that we #include , so the jsoncpp headers have to be in # a 'json' subdirectory if ! $found; then LIBJSONCPP_INCLUDES= for jsoncppdir in $jsoncppdirs; do AC_MSG_CHECKING([for json/json.h in $jsoncppdir]) if test -f "$jsoncppdir/include/json/json.h"; then LIBJSONCPP_INCLUDES="-I$jsoncppdir/include" LIBJSONCPP_LDFLAGS="-L$jsoncppdir/lib" LIBJSONCPP_LIBS="-ljsoncpp" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done fi if ! $found; then AC_MSG_NOTICE([Did not find libjsoncpp]) $2 else AC_MSG_NOTICE([Found libjsoncpp]) $1 fi AC_SUBST([LIBJSONCPP_INCLUDES]) AC_SUBST([LIBJSONCPP_LIBS]) AC_SUBST([LIBJSONCPP_LDFLAGS]) ]) weakforced-2.10.2/m4/pdns_check_libuuid.m4000066400000000000000000000060711461473602600203230ustar00rootroot00000000000000# SYNOPSIS # # PDNS_CHECK_LIBUUID([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for libuuid in a number of default spots, or in a # user-selected spot (via --with-libuuid). Sets # # LIBUUID_INCLUDES to the include directives required # LIBUUID_LIBS to the -l directives required # LIBUUID_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets LIBUUID_INCLUDES such that source files should use the # uuid/ directory in include directives: # # #include # # LICENSE # # Taken and modified from AX_CHECK_OPENSSL by: # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AU_ALIAS([CHECK_LIBUUID], [PDNS_CHECK_LIBUUID]) AC_DEFUN([PDNS_CHECK_LIBUUID], [ found=false AC_ARG_WITH([libuuid], [AS_HELP_STRING([--with-libuuid=DIR], [root of the uuid directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-libuuid value]) ;; *) uuiddirs="$withval" ;; esac ], [ # if pkg-config is installed and uuid has installed a .pc file, # then use that information and don't search uuiddirs AC_CHECK_TOOL([PKG_CONFIG], [uuid pkg-config]) if test x"$PKG_CONFIG" != x""; then LIBUUID_LDFLAGS=`$PKG_CONFIG uuid --libs-only-L 2>/dev/null` if test $? = 0; then LIBUUID_LIBS=`$PKG_CONFIG uuid --libs-only-l 2>/dev/null` LIBUUID_INCLUDES=`$PKG_CONFIG uuid --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default dirs if ! $found; then uuiddirs="/usr/local /usr/local/opt /usr/pkg /usr" fi ] ) # note that we #include , so the uuid headers have to be in # a 'uuid' subdirectory if ! $found; then LIBUUID_INCLUDES= for uuiddir in $uuiddirs; do AC_MSG_CHECKING([for uuid/uuid.h in $uuiddir]) if test -f "$uuiddir/include/uuid/uuid.h"; then LIBUUID_INCLUDES="-I$uuiddir/include" LIBUUID_LDFLAGS="-L$uuiddir/lib" LIBUUID_LIBS="-luuid" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done fi if ! $found; then AC_MSG_NOTICE([Did not find libuuid]) $2 else AC_MSG_NOTICE([Found libuuid]) $1 fi AC_SUBST([LIBUUID_INCLUDES]) AC_SUBST([LIBUUID_LIBS]) AC_SUBST([LIBUUID_LDFLAGS]) ]) weakforced-2.10.2/m4/pdns_check_libz.m4000066400000000000000000000053451461473602600176310ustar00rootroot00000000000000# SYNOPSIS # # PDNS_CHECK_LIBZ([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for libz in a number of default spots, or in a # user-selected spot (via --with-libz). Sets # # LIBZ_INCLUDES to the include directives required # LIBZ_LIBS to the -l directives required # LIBZ_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # # LICENSE # # Taken and modified from AX_CHECK_OPENSSL by: # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AU_ALIAS([CHECK_LIBZ], [PDNS_CHECK_LIBZ]) AC_DEFUN([PDNS_CHECK_LIBZ], [ found=false AC_ARG_WITH([libz], [AS_HELP_STRING([--with-libz=DIR], [root of the zlib directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-libz value]) ;; *) libzdirs="$withval" ;; esac ], [ # if pkg-config is installed and libz has installed a .pc file, # then use that information and don't search libzdirs AC_CHECK_TOOL([PKG_CONFIG], [zlib pkg-config]) if test x"$PKG_CONFIG" != x""; then LIBZ_LDFLAGS=`$PKG_CONFIG zlib --libs-only-L 2>/dev/null` if test $? = 0; then LIBZ_LIBS=`$PKG_CONFIG zlib --libs-only-l 2>/dev/null` LIBZ_INCLUDES=`$PKG_CONFIG zlib --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default dirs if ! $found; then libzdirs="/usr/local /usr/local/opt /usr/pkg /usr" fi ] ) if ! $found; then LIBZ_INCLUDES= for libzdir in $libzdirs; do AC_MSG_CHECKING([for zlib.h in $libzdir]) if test -f "$libzdir/include/zlib.h"; then LIBZ_INCLUDES="-I$libzdir/include" LIBZ_LDFLAGS="-L$libzdir/lib" LIBZ_LIBS="-lz" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done fi if ! $found; then AC_MSG_NOTICE([Did not find zlib]) $2 else AC_MSG_NOTICE([Found zlib]) $1 fi AC_SUBST([LIBZ_INCLUDES]) AC_SUBST([LIBZ_LIBS]) AC_SUBST([LIBZ_LDFLAGS]) ]) weakforced-2.10.2/m4/pdns_check_lua_hpp.m4000066400000000000000000000005671461473602600203220ustar00rootroot00000000000000AC_DEFUN([PDNS_CHECK_LUA_HPP],[ AC_REQUIRE([PDNS_WITH_LUA]) AC_REQUIRE([PDNS_WITH_LUAJIT]) AS_IF([test "x$LUAPC" != "x" -o "x$LUAJITPC" != "x" ], [ OLD_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $LUA_CFLAGS" AC_CHECK_HEADER([lua.hpp], [ have_lua_hpp=y ]) CPPFLAGS="$OLD_CPPFLAGS" ]) AM_CONDITIONAL([HAVE_LUA_HPP], [ test x"$have_lua_hpp" = "xy" ]) ]) weakforced-2.10.2/m4/pdns_check_os.m4000066400000000000000000000025421461473602600173060ustar00rootroot00000000000000AC_DEFUN([PDNS_CHECK_OS],[ THREADFLAGS="" case "$host_os" in solaris2.1*) LIBS="-lposix4 -lpthread $LIBS" CXXFLAGS="-D_REENTRANT $CXXFLAGS" have_solaris="yes" ;; solaris2.8 | solaris2.9 ) AC_DEFINE(NEED_POSIX_TYPEDEF,,[If POSIX typedefs need to be defined]) AC_DEFINE(NEED_INET_NTOP_PROTO,,[If your OS is so broken that it needs an additional prototype]) LIBS="-lposix4 -lpthread $LIBS" CXXFLAGS="-D_REENTRANT $CXXFLAGS" have_solaris="yes" ;; linux*) THREADFLAGS="-pthread" have_linux="yes" ;; darwin*) CXXFLAGS="-D__APPLE_USE_RFC_3542 -D_XOPEN_SOURCE $CXXFLAGS" ;; freebsd*) THREADFLAGS="-pthread" have_freebsd="yes" ;; *) LDFLAGS="-pthread $LDFLAGS" CXXFLAGS="-pthread $CXXFLAGS" ;; esac AM_CONDITIONAL([HAVE_FREEBSD], [test "x$have_freebsd" = "xyes"]) AM_CONDITIONAL([HAVE_LINUX], [test "x$have_linux" = "xyes"]) AM_CONDITIONAL([HAVE_SOLARIS], [test "x$have_solaris" = "xyes"]) case "$host" in mips* | powerpc-* ) AC_MSG_CHECKING([whether the linker accepts -latomic]) LDFLAGS="-latomic $LDFLAGS" AC_LINK_IFELSE([m4_default([],[AC_LANG_PROGRAM()])], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([Unable to link against libatomic, cannot continue])] ) ;; esac AC_SUBST(THREADFLAGS) AC_SUBST([DYNLINKFLAGS], [-export-dynamic]) ]) weakforced-2.10.2/m4/pdns_check_pandoc.m4000066400000000000000000000006641461473602600201340ustar00rootroot00000000000000AC_DEFUN([PDNS_CHECK_PANDOC], [ AC_CHECK_PROG([PANDOC], [pandoc], [pandoc], [no]) AS_IF([test "x$PANDOC" = "xno"], [ AS_IF([test ! -d "$srcdir/docs/html" -o ! -f "$srcdir/docs/pdns_server.1"], [AC_MSG_WARN([pandoc is missing, unable to build documentation and manpages.])] ) ]) AM_CONDITIONAL([HAVE_PANDOC], [test "x$PANDOC" != "xno"]) AM_CONDITIONAL([HAVE_MANPAGES], [test -e "$srcdir/docs/pdns_server.1"]) ]) weakforced-2.10.2/m4/pdns_check_prometheus_cpp.m4000066400000000000000000000102541461473602600217210ustar00rootroot00000000000000# SYNOPSIS # # PDNS_CHECK_LIBPROMETHEUS([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for prometheus-cpp library in standard places, or in a # user-selected spot (via --with-libprometheus). Sets # # LIBPROMETHEUS_INCLUDES to the include directives required # LIBPROMETHEUS_LIBS to the -l directives required # LIBPROMETHEUS_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets LIBPROMETHEUS_INCLUDES such that source files should use the # prometheus/ directory in include directives: # # #include # # LICENSE # # Taken and modified from AX_CHECK_OPENSSL by: # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AU_ALIAS([CHECK_LIBPROMETHEUS], [PDNS_CHECK_LIBPROMETHEUS]) AC_DEFUN([PDNS_CHECK_LIBPROMETHEUS], [ found=false AC_ARG_WITH([libprometheus], [AS_HELP_STRING([--with-libprometheus=DIR], [root of the OpenSSL directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-libprometheus value]) ;; *) promdirs="$withval" ;; esac ], [ # if pkg-config is installed and there is a .pc file, # then use that information and don't search promdirs AC_CHECK_TOOL([PKG_CONFIG], [pkg-config]) if test x"$PKG_CONFIG" != x""; then LIBPROMETHEUS_LDFLAGS=`$PKG_CONFIG prometheus-cpp-core --libs-only-L 2>/dev/null` if test $? = 0; then LIBPROMETHEUS_LIBS=`$PKG_CONFIG prometheus-cpp-core --libs-only-l 2>/dev/null` LIBPROMETHEUS_INCLUDES=`$PKG_CONFIG prometheus-cpp-core --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default promdirs if ! $found; then promdirs="/usr/local /usr" fi ] ) # note that we #include , so the prometheus headers have to be in # an 'prometheus' subdirectory if ! $found; then LIBPROMETHEUS_INCLUDES= for promdir in $promdirs; do AC_MSG_CHECKING([for prometheus/registry.h in $promdir]) if test -f "$promdir/include/prometheus/registry.h"; then LIBPROMETHEUS_INCLUDES="-I$promdir/include" LIBPROMETHEUS_LDFLAGS="-L$promdir/lib" LIBPROMETHEUS_LIBS="-lprometheus-cpp-core" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS AC_MSG_CHECKING([whether compiling and linking against libprometheus-cpp-core works]) echo "Trying link with LIBPROMETHEUS_LDFLAGS=$LIBPROMETHEUS_LDFLAGS;" \ "LIBPROMETHEUS_LIBS=$LIBPROMETHEUS_LIBS; LIBPROMETHEUS_INCLUDES=$LIBPROMETHEUS_INCLUDES" >&AS_MESSAGE_LOG_FD save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $LIBPROMETHEUS_LDFLAGS" LIBS="$LIBPROMETHEUS_LIBS $LIBS" CPPFLAGS="$LIBPROMETHEUS_INCLUDES $CPPFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [prometheus::Registry reg;])], [ AC_MSG_RESULT([yes]) $1 ], [ AC_MSG_RESULT([no]) $2 ]) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" AC_SUBST([LIBPROMETHEUS_INCLUDES]) AC_SUBST([LIBPROMETHEUS_LIBS]) AC_SUBST([LIBPROMETHEUS_LDFLAGS]) ]) weakforced-2.10.2/m4/pdns_check_yamlcpp.m4000066400000000000000000000077211461473602600203360ustar00rootroot00000000000000# SYNOPSIS # # PDNS_CHECK_YAMLCPP([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for yaml-cpp in a number of default spots, or in a # user-selected spot (via --with-yamlcpp). Sets # # YAMLCPP_INCLUDES to the include directives required # YAMLCPP_LIBS to the -l directives required # YAMLCPP_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets YAMLCPP_INCLUDES such that source files should use the # yaml-cpp/ directory in include directives: # # #include # # LICENSE # # Taken and modified from PDNS_CHECK_OPENSSL by: # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AU_ALIAS([CHECK_YAMLCPP], [PDNS_CHECK_YAMLCPP]) AC_DEFUN([PDNS_CHECK_YAMLCPP], [ found=false AC_ARG_WITH([yamlcpp], [AS_HELP_STRING([--with-yamlcpp=DIR], [root of the yaml-cpp directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-yamlcpp value]) ;; *) yamlcppdirs="$withval" ;; esac ], [ # if pkg-config is installed and yaml-cpp has installed a .pc file, # then use that information and don't search yamlcppdirs AC_CHECK_TOOL([PKG_CONFIG], [pkg-config]) if test x"$PKG_CONFIG" != x""; then YAMLCPP_LDFLAGS=`$PKG_CONFIG yaml-cpp --libs-only-L 2>/dev/null` if test $? = 0; then YAMLCPP_LIBS=`$PKG_CONFIG yaml-cpp --libs-only-l 2>/dev/null` YAMLCPP_INCLUDES=`$PKG_CONFIG yaml-cpp --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default yamlcppdirs if ! $found; then yamlcppdirs="/usr /usr/local /usr/lib /usr/pkg /usr" fi ] ) # note that we #include , so the YAML-CPP headers have to be in # an 'yaml-cpp' subdirectory if ! $found; then YAMLCPP_INCLUDES= for yamlcppdir in $yamlcppdirs; do AC_MSG_CHECKING([for yaml-cpp/yaml.h in $yamlcppdir]) if test -f "$yamlcppdir/include/yaml-cpp/yaml.h"; then YAMLCPP_INCLUDES="-I$yamlcppdir/include" YAMLCPP_LDFLAGS="-L$yamlcppdir/lib" YAMLCPP_LIBS="-lyaml-cpp" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS AC_MSG_CHECKING([whether compiling and linking against yaml-cpp works]) echo "Trying link with YAMLCPP_LDFLAGS=$YAMLCPP_LDFLAGS;" \ "YAMLCPP_LIBS=$YAMLCPP_LIBS; YAMLCPP_INCLUDES=$YAMLCPP_INCLUDES" >&AS_MESSAGE_LOG_FD save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $YAMLCPP_LDFLAGS" LIBS="$YAMLCPP_LIBS $LIBS" CPPFLAGS="$YAMLCPP_INCLUDES $CPPFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [YAML::Node node;])], [ AC_MSG_RESULT([yes]) $1 ], [ AC_MSG_RESULT([no]) $2 ]) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" AC_SUBST([YAMLCPP_INCLUDES]) AC_SUBST([YAMLCPP_LIBS]) AC_SUBST([YAMLCPP_LDFLAGS]) ]) weakforced-2.10.2/m4/pdns_enable_sanitizers.m4000066400000000000000000000133041461473602600212270ustar00rootroot00000000000000AC_DEFUN([PDNS_ENABLE_SANITIZERS], [ PDNS_ENABLE_ASAN PDNS_ENABLE_MSAN PDNS_ENABLE_TSAN PDNS_ENABLE_LSAN PDNS_ENABLE_UBSAN AS_IF([test "x$enable_asan" != "xno" -a "x$enable_tsan" != "xno"],[ AC_MSG_ERROR([Address Sanitizer is not compatible with Thread Sanitizer]) ]) AS_IF([test "x$enable_msan" != "xno" -a "x$enable_asan" != "xno"],[ AC_MSG_ERROR([Memory Sanitizer is not compatible with Address Sanitizer]) ]) AS_IF([test "x$enable_msan" != "xno" -a "x$enable_lsan" != "xno"],[ AC_MSG_ERROR([Memory Sanitizer is not compatible with Leak Sanitizer]) ]) AS_IF([test "x$enable_msan" != "xno" -a "x$enable_tsan" != "xno"],[ AC_MSG_ERROR([Memory Sanitizer is not compatible with Thread Sanitizer]) ]) AS_IF([test "x$enable_asan" != "xno" -o "x$enable_tsan" != "xno" -o "x$enable_lsan" != "xno" -o "x$enable_ubsan" != "xno" -o "x$enable_msan" != "xno"], [ gl_WARN_ADD([-fno-omit-frame-pointer]) ]) ]) AC_DEFUN([PDNS_ENABLE_ASAN], [ AC_REQUIRE([gl_UNKNOWN_WARNINGS_ARE_ERRORS]) AC_MSG_CHECKING([whether to enable AddressSanitizer]) AC_ARG_ENABLE([asan], AS_HELP_STRING([--enable-asan], [enable AddressSanitizer @<:@default=no@:>@]), [enable_asan=$enableval], [enable_asan=no] ) AC_MSG_RESULT([$enable_asan]) AS_IF([test "x$enable_asan" != "xno"], [ gl_COMPILER_OPTION_IF([-fsanitize=address], [ [SANITIZER_FLAGS="-fsanitize=address $SANITIZER_FLAGS"] AC_CHECK_HEADERS([sanitizer/common_interface_defs.h], asan_headers=yes, asan_headers=no) AS_IF([test x"$asan_headers" = "xyes" ], [AC_CHECK_DECL(__sanitizer_start_switch_fiber, [ AC_MSG_CHECKING([for the exact signature of __sanitizer_finish_switch_fiber]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [#include ], [ __sanitizer_finish_switch_fiber(nullptr); ]) ], [ AC_MSG_RESULT([a single pointer]) AC_DEFINE([HAVE_FIBER_SANITIZER], [1], [Define if ASAN fiber annotation interface is available.]) AC_DEFINE(HAVE_SANITIZER_FINISH_SWITCH_FIBER_SINGLE_PTR, [1], [Define to 1 if __sanitizer_finish_switch_fiber takes only a pointer]) ], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [#include ], [ __sanitizer_finish_switch_fiber(nullptr, nullptr, nullptr); ]) ], [ AC_MSG_RESULT([three pointers]) AC_DEFINE([HAVE_FIBER_SANITIZER], [1], [Define if ASAN fiber annotation interface is available.]) AC_DEFINE(HAVE_SANITIZER_FINISH_SWITCH_FIBER_THREE_PTRS, [1], [Define to 1 if __sanitizer_finish_switch_fiber takes three pointers]) ], [ AC_MSG_RESULT([unknown]) AC_MSG_NOTICE([ASAN fiber switching is not available due to an unknown API version]) ]) ]) ], [ AC_MSG_NOTICE([ASAN fiber switching is not available]) ], [#include ] )] ) ], [AC_MSG_ERROR([Cannot enable AddressSanitizer])] ) ]) AC_SUBST([SANITIZER_FLAGS]) ]) AC_DEFUN([PDNS_ENABLE_TSAN], [ AC_REQUIRE([gl_UNKNOWN_WARNINGS_ARE_ERRORS]) AC_MSG_CHECKING([whether to enable ThreadSanitizer]) AC_ARG_ENABLE([tsan], AS_HELP_STRING([--enable-tsan], [enable ThreadSanitizer @<:@default=no@:>@]), [enable_tsan=$enableval], [enable_tsan=no] ) AC_MSG_RESULT([$enable_tsan]) AS_IF([test "x$enable_tsan" != "xno"], [ gl_COMPILER_OPTION_IF([-fsanitize=thread], [SANITIZER_FLAGS="-fsanitize=thread $SANITIZER_FLAGS"], [AC_MSG_ERROR([Cannot enable ThreadSanitizer])] ) ]) AC_SUBST([SANITIZER_FLAGS]) ]) AC_DEFUN([PDNS_ENABLE_LSAN], [ AC_REQUIRE([gl_UNKNOWN_WARNINGS_ARE_ERRORS]) AC_MSG_CHECKING([whether to enable LeakSanitizer]) AC_ARG_ENABLE([lsan], AS_HELP_STRING([--enable-lsan], [enable LeakSanitizer @<:@default=no@:>@]), [enable_lsan=$enableval], [enable_lsan=no] ) AC_MSG_RESULT([$enable_lsan]) AS_IF([test "x$enable_lsan" != "xno"], [ gl_COMPILER_OPTION_IF([-fsanitize=leak], [SANITIZER_FLAGS="-fsanitize=leak $SANITIZER_FLAGS"], [AC_MSG_ERROR([Cannot enable LeakSanitizer])] ) ]) AC_SUBST([SANITIZER_FLAGS]) ]) AC_DEFUN([PDNS_ENABLE_UBSAN], [ AC_REQUIRE([gl_UNKNOWN_WARNINGS_ARE_ERRORS]) AC_MSG_CHECKING([whether to enable Undefined Behaviour Sanitizer]) AC_ARG_ENABLE([ubsan], AS_HELP_STRING([--enable-ubsan], [enable Undefined Behaviour Sanitizer @<:@default=no@:>@]), [enable_ubsan=$enableval], [enable_ubsan=no] ) AC_MSG_RESULT([$enable_ubsan]) AS_IF([test "x$enable_ubsan" != "xno"], [ gl_COMPILER_OPTION_IF([-fsanitize=undefined], [SANITIZER_FLAGS="-fsanitize=undefined $SANITIZER_FLAGS"], [AC_MSG_ERROR([Cannot enable Undefined Behaviour Sanitizer])] ) ]) AC_SUBST([SANITIZER_FLAGS]) ]) AC_DEFUN([PDNS_ENABLE_MSAN], [ AC_REQUIRE([gl_UNKNOWN_WARNINGS_ARE_ERRORS]) AC_MSG_CHECKING([whether to enable MemorySanitizer]) AC_ARG_ENABLE([msan], AS_HELP_STRING([--enable-msan], [enable MemorySanitizer @<:@default=no@:>@]), [enable_msan=$enableval], [enable_msan=no] ) AC_MSG_RESULT([$enable_msan]) AS_IF([test "x$enable_msan" != "xno"], [ gl_COMPILER_OPTION_IF([-fsanitize=memory], [SANITIZER_FLAGS="-fsanitize=memory $SANITIZER_FLAGS"], [AC_MSG_ERROR([Cannot enable MemorySanitizer])] ) ]) AC_SUBST([SANITIZER_FLAGS]) ]) weakforced-2.10.2/m4/pdns_enable_unit_tests.m4000066400000000000000000000020771461473602600212420ustar00rootroot00000000000000AC_DEFUN([PDNS_ENABLE_UNIT_TESTS], [ AC_MSG_CHECKING([whether to enable unit test building]) AC_ARG_ENABLE([unit-tests], AS_HELP_STRING([--enable-unit-tests], [enable unit test building @<:@default=no@:>@]), [enable_unit_tests=$enableval], [enable_unit_tests=no] ) AC_MSG_RESULT([$enable_unit_tests]) AM_CONDITIONAL([UNIT_TESTS], [test "x$enable_unit_tests" != "xno"]) AC_MSG_CHECKING([whether to enable backend unit test building]) AC_ARG_ENABLE([backend-unit-tests], AS_HELP_STRING([--enable-backend-unit-tests], [enable backend unit test building @<:@default=no@:>@]), [enable_backend_unit_tests=$enableval], [enable_backend_unit_tests=no] ) AC_MSG_RESULT([$enable_backend_unit_tests]) AM_CONDITIONAL([BACKEND_UNIT_TESTS], [test "x$enable_backend_unit_tests" != "xno"]) AS_IF([test "x$enable_unit_tests" != "xno" || test "x$enable_backend_unit_tests" != "xno"], [ BOOST_TEST([mt]) AS_IF([test "$boost_cv_lib_unit_test_framework" = "no"], [ AC_MSG_ERROR([Boost Unit Test library not found]) ]) ]) ]) weakforced-2.10.2/m4/pdns_with_geo.m4000066400000000000000000000031551461473602600173360ustar00rootroot00000000000000AC_DEFUN([PDNS_CHECK_GEOIP], [ PKG_CHECK_MODULES([GEOIP], [geoip], AC_DEFINE([HAVE_GEOIP], [1], [Define this if you have GeoIP]), [have_geoip=0]) AC_ARG_WITH([maxminddb_incdir], AS_HELP_STRING([--with-maxminddb-includedir],[path to maxminddb include directory @<:@default=auto@:>@]), [with_maxminddb_incdir=$withval], [with_maxminddb_incdir=auto] ) AC_ARG_WITH([maxminddb_libdir], AS_HELP_STRING([--with-maxminddb-libdir],[path to maxminddb library directory @<:@default=auto@:>@]), [with_maxminddb_libdir=$withval], [with_maxminddb_libdir=auto], ) PKG_CHECK_MODULES([MMDB], [libmaxminddb], [ AC_DEFINE([HAVE_MMDB], [1], [Define this if you have Maxmind DB]) ], [ AS_IF([test "x$with_maxminddb_incdir" = "xauto"], [ AC_CHECK_HEADER([maxminddb.h], [have_mmdb=1], [have_mmdb=0]) ], [ OLD_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -I$with_maxminddb_incdir" AC_CHECK_HEADER([maxminddb.h], [have_mmdb=1], [have_mmdb=0]) CFLAGS="$OLD_CFLAGS" ]) AS_IF([test "$have_mmdb" = "1"], [ AS_IF([test "x$with_maxminddb_libdir" = "xauto"], [ AC_CHECK_LIB([maxminddb], [MMDB_open], [ AC_DEFINE([HAVE_MMDB], [1], [Define this if you have Maxmind DB]) MMDB_LIBS="-lmaxminddb" ]) ], [ OLD_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -L$with_maxminddb_libdir" AC_CHECK_LIB([maxminddb], [MMDB_open], [ AC_DEFINE([HAVE_MMDB], [1], [Define this if you have Maxmind DB]) MMDB_CFLAGS="-I$with_maxminddb_incdir" MMDB_LIBS="-L$with_maxminddb_libdir -lmaxminddb" ]) ]) ]) ]) ]) weakforced-2.10.2/m4/pdns_with_libssl.m4000066400000000000000000000023021461473602600200450ustar00rootroot00000000000000AC_DEFUN([PDNS_WITH_LIBSSL], [ AC_MSG_CHECKING([whether we will be linking in OpenSSL libssl]) HAVE_LIBSSL=0 AC_ARG_WITH([libssl], AS_HELP_STRING([--with-libssl],[use OpenSSL libssl @<:@default=auto@:>@]), [with_libssl=$withval], [with_libssl=auto], ) AC_MSG_RESULT([$with_libssl]) AS_IF([test "x$with_libssl" != "xno"], [ AS_IF([test "x$with_libssl" = "xyes" -o "x$with_libssl" = "xauto"], [ PKG_CHECK_MODULES([LIBSSL], [libssl], [ [HAVE_LIBSSL=1] AC_DEFINE([HAVE_LIBSSL], [1], [Define to 1 if you have OpenSSL libssl]) save_CFLAGS=$CFLAGS save_LIBS=$LIBS CFLAGS="$LIBSSL_CFLAGS $CFLAGS" LIBS="$LIBSSL_LIBS -lcrypto $LIBS" AC_CHECK_FUNCS([SSL_CTX_set_ciphersuites OCSP_basic_sign SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback SSL_CTX_get0_privatekey SSL_CTX_set_min_proto_version SSL_set_hostflags]) CFLAGS=$save_CFLAGS LIBS=$save_LIBS ], [ : ]) ]) ]) AM_CONDITIONAL([HAVE_LIBSSL], [test "x$LIBSSL_LIBS" != "x"]) AS_IF([test "x$with_libssl" = "xyes"], [ AS_IF([test x"$LIBSSL_LIBS" = "x"], [ AC_MSG_ERROR([OpenSSL libssl requested but libraries were not found]) ]) ]) ]) weakforced-2.10.2/m4/pdns_with_lua.m4000066400000000000000000000035501461473602600173440ustar00rootroot00000000000000AC_DEFUN([PDNS_WITH_LUA],[ AC_PROG_GREP()dnl Ensure we have grep AC_MSG_CHECKING([which Lua implementation to use]) AC_ARG_WITH([lua], [AS_HELP_STRING([--with-lua], [select Lua implementation @<:@default=auto@:>@]) ], [ with_lua=$withval ], [ with_lua=auto ]) AS_IF([test "x$with_lua" = "xyes"], [ dnl --with-lua was passed, make it auto with_lua=auto ]) AC_MSG_RESULT([$with_lua]) AS_IF([test "x$with_lua" = "xno" -a "$1" = "mandatory"], [ AC_MSG_ERROR([--without-lua specified, but Lua is not optional]) ]) LUAPC="" luajit_min_version='2.0.2' lua_min_version='5.1' AS_IF([test "x$with_lua" != "xno"], [ AS_IF([test "x$with_lua" != "xauto"], [ with_lua_version=${lua_min_version} AS_IF([echo "x$with_lua" | ${GREP} 'jit' >/dev/null 2>&1], [with_lua_version=${luajit_min_version}]) PKG_CHECK_MODULES([LUA], $with_lua >= $with_lua_version, [ AC_DEFINE([HAVE_LUA], [1], [Define to 1 if you have Lua]) LUAPC=$with_lua ], [ AC_MSG_ERROR([Selected Lua ($with_lua) not found]) ]) ], [ PKG_CHECK_MODULES([LUA], [luajit >= ${luajit_min_version}], [ LUAPC=luajit AC_DEFINE([HAVE_LUA], [1], [Define to 1 if you have Lua]) ], [ : ]) AS_IF([test -z "$LUAPC"], [ found_lua=n m4_foreach_w([luapc], [lua5.3 lua-5.3 lua53 lua5.2 lua-5.2 lua52 lua5.1 lua-5.1 lua51 lua], [ AS_IF([test "$found_lua" != "y"], [ PKG_CHECK_MODULES([LUA], [luapc >= ${lua_min_version}], [ AC_DEFINE([HAVE_LUA], [1], [Define to 1 if you have lua]) found_lua=y LUAPC=luapc ], [ : ]) ]) ]) ]) ]) ]) AS_IF([test -z "$LUAPC" -a "$1" = "mandatory"], [ AC_MSG_ERROR([No Lua not found, but is mandatory]) ]) AM_CONDITIONAL([LUA], [test -n "x$LUAPC"]) ]) weakforced-2.10.2/m4/pdns_with_luajit.m4000066400000000000000000000012001461473602600200410ustar00rootroot00000000000000AC_DEFUN([PDNS_WITH_LUAJIT],[ AC_MSG_CHECKING([whether we will be linking in LuaJIT]) AC_ARG_WITH([luajit], [AS_HELP_STRING([--with-luajit], [build LuaJIT bindings @<:@default=auto@:>@])], [with_luajit=$withval], [with_luajit=no] ) AC_MSG_RESULT([$with_luajit]) AS_IF([test "x$with_luajit" = "xyes"], [ LUAJITPC="$with_luajit" PKG_CHECK_MODULES([LUA], [luajit], [AC_DEFINE([HAVE_LUA], [1], [Define to 1 if you have LuaJIT])], [LUAJITPC=""] ) AS_IF([test "x$LUAJITPC" = "x"], [ AC_MSG_ERROR([LuaJIT not found])] ) ]) AM_CONDITIONAL([LUA], [test "x$with_luajit" = "xyes"]) ]) weakforced-2.10.2/m4/pdns_with_protobuf.m4000066400000000000000000000020541461473602600204210ustar00rootroot00000000000000AC_DEFUN([PDNS_WITH_PROTOBUF], [ AC_MSG_CHECKING([if we need to link in protobuf]) AC_ARG_WITH([protobuf], AS_HELP_STRING([--with-protobuf],[enable protobuf support @<:@default=auto@:>@]), [with_protobuf=$withval], [with_protobuf=auto], ) AC_MSG_RESULT([$with_protobuf]) AS_IF([test "x$with_protobuf" != "xno"], [ AS_IF([test "x$with_protobuf" = "xyes" -o "x$with_protobuf" = "xauto"], [ PKG_CHECK_MODULES([PROTOBUF], [protobuf], [ : ], [ : ]) AC_CHECK_PROG([PROTOC], [protoc], [protoc]) ]) ]) AS_IF([test "x$with_protobuf" = "xyes"], [ AS_IF([test x"$PROTOBUF_LIBS" = "x"], [ AC_MSG_ERROR([Protobuf requested but libraries were not found]) ]) AS_IF([test x"$PROTOC" = "x"], [ AC_MSG_ERROR([Protobuf requested but the protobuf compiler was not found]) ]) ]) AM_CONDITIONAL([HAVE_PROTOBUF], [test x"$PROTOBUF_LIBS" != "x"]) AM_CONDITIONAL([HAVE_PROTOC], [test x"$PROTOC" != "x"]) AS_IF([test x"$PROTOBUF_LIBS" != "x"], [AC_DEFINE([HAVE_PROTOBUF], [1], [Define if using protobuf.])]) ]) weakforced-2.10.2/m4/perl_modules.m4000066400000000000000000000043131461473602600171740ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_prog_perl_modules.html # =========================================================================== # # SYNOPSIS # # AX_PROG_PERL_MODULES([MODULES], [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # # DESCRIPTION # # Checks to see if the given perl modules are available. If true the shell # commands in ACTION-IF-TRUE are executed. If not the shell commands in # ACTION-IF-FALSE are run. Note if $PERL is not set (for example by # calling AC_CHECK_PROG, or AC_PATH_PROG), AC_CHECK_PROG(PERL, perl, perl) # will be run. # # MODULES is a space separated list of module names. To check for a # minimum version of a module, append the version number to the module # name, separated by an equals sign. # # Example: # # AX_PROG_PERL_MODULES( Text::Wrap Net::LDAP=1.0.3, , # AC_MSG_WARN(Need some Perl modules) # # LICENSE # # Copyright (c) 2009 Dean Povey # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 7 AU_ALIAS([AC_PROG_PERL_MODULES], [AX_PROG_PERL_MODULES]) AC_DEFUN([AX_PROG_PERL_MODULES],[dnl m4_define([ax_perl_modules]) m4_foreach([ax_perl_module], m4_split(m4_normalize([$1])), [ m4_append([ax_perl_modules], [']m4_bpatsubst(ax_perl_module,=,[ ])[' ]) ]) # Make sure we have perl if test -z "$PERL"; then AC_CHECK_PROG(PERL,perl,perl) fi if test "x$PERL" != x; then ax_perl_modules_failed=0 for ax_perl_module in ax_perl_modules; do AC_MSG_CHECKING(for perl module $ax_perl_module) # Would be nice to log result here, but can't rely on autoconf internals $PERL -e "use $ax_perl_module; exit" > /dev/null 2>&1 if test $? -ne 0; then AC_MSG_RESULT(no); ax_perl_modules_failed=1 else AC_MSG_RESULT(ok); fi done # Run optional shell commands if test "$ax_perl_modules_failed" = 0; then : $2 else : $3 fi else AC_MSG_WARN(could not find perl) fi])dnl weakforced-2.10.2/m4/systemd.m4000066400000000000000000000111271461473602600161730ustar00rootroot00000000000000# systemd.m4 - Macros to check for and enable systemd -*- Autoconf -*- # # Copyright (C) 2014 Luis R. Rodriguez # Copyright (C) 2016 Pieter Lexis # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #serial 2 dnl Some optional path options AC_DEFUN([AX_SYSTEMD_OPTIONS], [ AC_ARG_WITH(systemd, [ --with-systemd set directory for systemd service files], SYSTEMD_DIR="$withval", SYSTEMD_DIR="") AC_SUBST(SYSTEMD_DIR) AC_ARG_WITH(systemd, [ --with-systemd-modules-load set directory for systemd modules load files], SYSTEMD_MODULES_LOAD="$withval", SYSTEMD_MODULES_LOAD="") AC_SUBST(SYSTEMD_MODULES_LOAD) ]) AC_DEFUN([AX_ENABLE_SYSTEMD_OPTS], [ AX_ARG_DEFAULT_ENABLE([systemd], [Disable systemd support]) AX_SYSTEMD_OPTIONS() ]) AC_DEFUN([AX_ALLOW_SYSTEMD_OPTS], [ AX_ARG_DEFAULT_DISABLE([systemd], [Enable systemd support]) AX_SYSTEMD_OPTIONS() ]) AC_DEFUN([AX_CHECK_SYSTEMD_LIBS], [ AC_REQUIRE([AX_CHECK_SYSTEMD_DETECT_AND_ENABLE]) AS_IF([test "x$libsystemd" = x], [ AC_MSG_ERROR([Unable to find a suitable libsystemd library]) ]) PKG_CHECK_MODULES([SYSTEMD], [$libsystemd_daemon]) dnl pkg-config older than 0.24 does not set these for dnl PKG_CHECK_MODULES() worth also noting is that as of version 208 dnl of systemd pkg-config --cflags currently yields no extra flags yet. AC_SUBST([SYSTEMD_CFLAGS]) AC_SUBST([SYSTEMD_LIBS]) AS_IF([test "x$SYSTEMD_DIR" = x], [ dnl In order to use the line below we need to fix upstream systemd dnl to properly ${prefix} for child variables in dnl src/core/systemd.pc.in but this is a bit complex at the dnl moment as they depend on another rootprefix, which can vary dnl from prefix in practice. We provide our own definition as we dnl *know* where systemd will dump this to, but this does limit dnl us to stick to a non custom systemdsystemunitdir, dnl to work dnl around this we provide the additional configure option dnl --with-systemd where you can specify the directory for the unit dnl files. It would also be best to just extend the upstream dnl pkg-config pkg.m4 with an AC_DEFUN() to do this neatly. dnl SYSTEMD_DIR="`$PKG_CONFIG --define-variable=prefix=$PREFIX --variable=systemdsystemunitdir systemd`" SYSTEMD_DIR="\$(prefix)/lib/systemd/system/" ], []) AS_IF([test "x$SYSTEMD_DIR" = x], [ AC_MSG_ERROR([SYSTEMD_DIR is unset]) ], []) dnl There is no variable for this yet for some reason AS_IF([test "x$SYSTEMD_MODULES_LOAD" = x], [ SYSTEMD_MODULES_LOAD="\$(prefix)/lib/modules-load.d/" ], []) AS_IF([test "x$SYSTEMD_MODULES_LOAD" = x], [ AC_MSG_ERROR([SYSTEMD_MODULES_LOAD is unset]) ], []) ]) AC_DEFUN([AX_CHECK_SYSTEMD], [ dnl Respect user override to disable AS_IF([test "x$enable_systemd" != "xno"], [ AS_IF([test "x$systemd" = "xy" ], [ AC_DEFINE([HAVE_SYSTEMD], [1], [Systemd available and enabled]) systemd=y AX_CHECK_SYSTEMD_LIBS() ],[systemd=n]) ],[systemd=n]) ]) AC_DEFUN([AX_CHECK_SYSTEMD_DETECT_AND_ENABLE], [ AC_CHECK_HEADER([systemd/sd-daemon.h], [ for libname in systemd-daemon systemd; do AC_CHECK_LIB([$libname], [sd_listen_fds], [ libsystemd_daemon="lib$libname" systemd=y libsystemd=y ]) done ]) ]) dnl Enables systemd by default and requires a --disable-systemd option flag dnl to configure if you want to disable. AC_DEFUN([AX_ENABLE_SYSTEMD], [ AX_ENABLE_SYSTEMD_OPTS() AX_CHECK_SYSTEMD() ]) dnl Systemd will be disabled by default and requires you to run configure with dnl --enable-systemd to look for and enable systemd. AC_DEFUN([AX_ALLOW_SYSTEMD], [ AX_ALLOW_SYSTEMD_OPTS() AX_CHECK_SYSTEMD() ]) dnl Systemd will be disabled by default but if your build system is detected dnl to have systemd build libraries it will be enabled. You can always force dnl disable with --disable-systemd AC_DEFUN([AX_AVAILABLE_SYSTEMD], [ AX_ALLOW_SYSTEMD_OPTS() AX_CHECK_SYSTEMD_DETECT_AND_ENABLE() AX_CHECK_SYSTEMD() ]) weakforced-2.10.2/m4/warnings.m4000066400000000000000000000056031461473602600163350ustar00rootroot00000000000000# warnings.m4 serial 11 dnl Copyright (C) 2008-2015 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Simon Josefsson # gl_AS_VAR_APPEND(VAR, VALUE) # ---------------------------- # Provide the functionality of AS_VAR_APPEND if Autoconf does not have it. m4_ifdef([AS_VAR_APPEND], [m4_copy([AS_VAR_APPEND], [gl_AS_VAR_APPEND])], [m4_define([gl_AS_VAR_APPEND], [AS_VAR_SET([$1], [AS_VAR_GET([$1])$2])])]) # gl_COMPILER_OPTION_IF(OPTION, [IF-SUPPORTED], [IF-NOT-SUPPORTED], # [PROGRAM = AC_LANG_PROGRAM()]) # ----------------------------------------------------------------- # Check if the compiler supports OPTION when compiling PROGRAM. # # FIXME: gl_Warn must be used unquoted until we can assume Autoconf # 2.64 or newer. AC_DEFUN([gl_COMPILER_OPTION_IF], [AS_VAR_PUSHDEF([gl_Warn], [gl_cv_warn_[]_AC_LANG_ABBREV[]_$1])dnl AS_VAR_PUSHDEF([gl_Flags], [_AC_LANG_PREFIX[]FLAGS])dnl AS_LITERAL_IF([$1], [m4_pushdef([gl_Positive], m4_bpatsubst([$1], [^-Wno-], [-W]))], [gl_positive="$1" case $gl_positive in -Wno-*) gl_positive=-W`expr "X$gl_positive" : 'X-Wno-\(.*\)'` ;; esac m4_pushdef([gl_Positive], [$gl_positive])])dnl AC_CACHE_CHECK([whether _AC_LANG compiler handles $1], m4_defn([gl_Warn]), [ gl_save_compiler_FLAGS="$gl_Flags" gl_AS_VAR_APPEND(m4_defn([gl_Flags]), [" $gl_unknown_warnings_are_errors ]m4_defn([gl_Positive])["]) AC_LINK_IFELSE([m4_default([$4], [AC_LANG_PROGRAM([])])], [AS_VAR_SET(gl_Warn, [yes])], [AS_VAR_SET(gl_Warn, [no])]) gl_Flags="$gl_save_compiler_FLAGS" ]) AS_VAR_IF(gl_Warn, [yes], [$2], [$3]) m4_popdef([gl_Positive])dnl AS_VAR_POPDEF([gl_Flags])dnl AS_VAR_POPDEF([gl_Warn])dnl ]) # gl_UNKNOWN_WARNINGS_ARE_ERRORS # ------------------------------ # Clang doesn't complain about unknown warning options unless one also # specifies -Wunknown-warning-option -Werror. Detect this. AC_DEFUN([gl_UNKNOWN_WARNINGS_ARE_ERRORS], [gl_COMPILER_OPTION_IF([-Werror -Wunknown-warning-option], [gl_unknown_warnings_are_errors='-Wunknown-warning-option -Werror'], [gl_unknown_warnings_are_errors=])]) # gl_WARN_ADD(OPTION, [VARIABLE = WARN_CFLAGS], # [PROGRAM = AC_LANG_PROGRAM()]) # --------------------------------------------- # Adds parameter to WARN_CFLAGS if the compiler supports it when # compiling PROGRAM. For example, gl_WARN_ADD([-Wparentheses]). # # If VARIABLE is a variable name, AC_SUBST it. AC_DEFUN([gl_WARN_ADD], [AC_REQUIRE([gl_UNKNOWN_WARNINGS_ARE_ERRORS]) gl_COMPILER_OPTION_IF([$1], [gl_AS_VAR_APPEND(m4_if([$2], [], [[WARN_CFLAGS]], [[$2]]), [" $1"])], [], [$3]) m4_ifval([$2], [AS_LITERAL_IF([$2], [AC_SUBST([$2])])], [AC_SUBST([WARN_CFLAGS])])dnl ]) # Local Variables: # mode: autoconf # End: weakforced-2.10.2/m4/wforce_check_readline.m4000066400000000000000000000002021461473602600207600ustar00rootroot00000000000000AC_DEFUN([WFORCE_CHECK_READLINE], [ AC_CHECK_HEADER([readline/readline.h], [ : ], [ AC_MSG_ERROR([readline.h not found]) ]) ]) weakforced-2.10.2/regression-tests/000077500000000000000000000000001461473602600172375ustar00rootroot00000000000000weakforced-2.10.2/regression-tests/.gitignore000066400000000000000000000000411461473602600212220ustar00rootroot00000000000000/.venv /*.xml /*.pyc __pycache__ weakforced-2.10.2/regression-tests/README.md000066400000000000000000000017321461473602600205210ustar00rootroot00000000000000README for Weakforced Regression Tests ------ Regression Tests are now designed to be run inside the regression docker container. This has necessitated some changes in the server names and port numbers. Attempting to run the regression tests manually with ./runtests will fail. The correct way to run the regression tests is with "make regression" in the docker directory, which will also run various other tests including distchecks. Running a specific test ------ Run the following in docker directory to start the test environment: ``` make build_image start ``` Start bash in regression environment: ``` docker-compose exec regression bash ``` In the bash session, run: ``` autoreconf -v -i -f ./configure --enable-trackalert --enable-systemd --disable-docker \ --enable-unit-tests --enable-asan --enable-ubsan \ --disable-silent-rules CC=clang CXX=clang++ make -j ``` Now you can run only `test_Basics.py` with: ``` cd regression-tests ./runtests test_Basics.py ``` weakforced-2.10.2/regression-tests/RegressionGeoIP.mmdb000066400000000000000000000034141461473602600231060ustar00rootroot00000000000000´´–´´´´´ ´ ´ ´ ´ ´´´´´´´´´´´´´´´´´´´ ´!´"´#´$´%´&´'´(´)´*´+´,´-´.´/´0´1´2´3´4´5´6´7´8´9´:´;´<´=´>´?´@´A´B´C´D´E´F´G´H´I´J´K´L´M´N´O´P´Q‡R´S´T´U´V´W´X´Y´Z´[´\´]´^´_´`´ax´b´c´de´f´g´h´i´´jk´l´´m´n´op´q´´r´s´tu´v´w´Ä´y´z´{´|´}´~´´´€´´‚´ƒ„´…´´†´u´ˆ´‰´Š´‹´Œ´´Ž´´´‘´’´“´”´•´`—´˜´™´š´›´œ´´ž´Ÿ´ ´¡´¢³´£¤´¥´¦´§´¨´©´ª´«´¬´­´®´¯´°´±´²´`´`´äDcityâJgeoname_idÃjEnamesáBenETokyoGcountryã ÃÈZHiso_codeBJP á EJapanHlocationäOaccuracy_radiusÁ2Hlatitudeh@AÓÿ¼ä!Ilongitudeh@azÝ÷vÅItime_zoneJAsia/TokyoLsubdivisionsä â Ã2Ü: á JNottingham &ã Ã(5Ÿ 5BGB á NUnited Kingdom Lä VÁ2 hh@JsÃa@O zh¿ó¸ºÇË) MEurope/London ¢«ÍïMaxMind.comé[binary_format_major_version¡[binary_format_minor_version Kbuild_epoche¡UjMdatabase_type@KdescriptionáBen]$Compiled with mmdb-editor () https://github.com/iglov/mmdb-editorJip_version¡IlanguagesJnode_countÁ´Krecord_size¡weakforced-2.10.2/regression-tests/create-certs.sh000077500000000000000000000025621461473602600221640ustar00rootroot00000000000000#!/usr/bin/env bash set -eux CERTS_DIR=certs mkdir -p $CERTS_DIR # Create root key openssl genrsa -out $CERTS_DIR/root.key 4096 # Create root CA openssl req -x509 -new -nodes -key $CERTS_DIR/root.key -sha256 -days 100 \ -subj '/CN=localhost/O=OX Abuse Shield Root/C=US' -out $CERTS_DIR/root.pem # Create client key openssl genrsa -out $CERTS_DIR/client.key 4096 # Create Certificate Signing Request for client.key openssl req -new -key $CERTS_DIR/client.key -out $CERTS_DIR/client.csr \ -subj '/CN=localhost/O=OX Abuse Shield Client/C=US' # Create server key openssl genrsa -out $CERTS_DIR/server.key 4096 # Create Certificate Signing Request for server.key openssl req -new -key $CERTS_DIR/server.key -out $CERTS_DIR/server.csr \ -subj '/CN=localhost/O=OX Abuse Shield Server/C=US' # Sign client.key and server.key cat >$CERTS_DIR/csr.ext <=7.0.0 Jinja2==3.1.3 MarkupSafe==2.1.3 pytz==2017.2 urllib3==2.0.7 bottle==0.12.20 prometheus_client==0.7.1 pyOpenSSL==20.0.1 weakforced-2.10.2/regression-tests/runtests000077500000000000000000000003431461473602600210540ustar00rootroot00000000000000#!/usr/bin/env bash set -e if [ ! -d .venv ]; then python3 -m venv .venv fi . .venv/bin/activate python3 -V pip3 install --upgrade pip pip3 install -r requirements.txt set -e set -x ./create-certs.sh exec ./runtests.py "$@" weakforced-2.10.2/regression-tests/runtests.py000077500000000000000000000064761461473602600215200ustar00rootroot00000000000000#!/usr/bin/env python # # Shell-script style. import os import requests import subprocess import sys import time import signal WEBPORT = '8084' APIKEY = 'super' ACL_LIST_TPL = """ # Generated by runtests.py # local host 127.0.0.1 ::1 """ wait = ('--wait' in sys.argv) if wait: sys.argv.remove('--wait') cmd1 = ("../wforce/wforce -D -C ./wforce1.conf -R ../wforce/regexes.yaml").split() cmd2 = ("../wforce/wforce -D -C ./wforce2.conf -R ../wforce/regexes.yaml").split() cmd4 = ("../wforce/wforce -D -C ./wforce4.conf -R ../wforce/regexes.yaml").split() webcmd = (".venv/bin/python ./webhook_server.py").split() nginx_cmd = ("nginx -c /wforce/regression-tests/nginx/nginx.conf").split() udpsinkcmd = (".venv/bin/python ./udp_sink.py").split() ta_cmd = ("../trackalert/trackalert -D -C ./trackalert.conf").split() # Now run wforce and the tests. print("Launching wforce (1 and 2 and 4)...") print(' '.join(cmd1)) print(' '.join(cmd2)) print(' '.join(cmd4)) proc1 = subprocess.Popen(cmd1, close_fds=True) proc2 = subprocess.Popen(cmd2, close_fds=True) proc4 = subprocess.Popen(cmd4, close_fds=True) webproc = subprocess.Popen(webcmd, close_fds=True) webpid = webproc.pid nginx_proc = subprocess.Popen(nginx_cmd, close_fds=True) udpproc = subprocess.Popen(udpsinkcmd, close_fds=True) udppid = udpproc.pid taproc = subprocess.Popen(ta_cmd, close_fds=True) tapid = taproc.pid def sighandler(signum, frame): proc1.terminate() proc1.wait() proc2.terminate() proc2.wait() proc4.terminate() proc4.wait() webproc.terminate() webproc.wait() nginx_proc.terminate() nginx_proc.wait() udpproc.terminate() udpproc.wait() taproc.terminate() taproc.wait() subprocess.call(["/bin/stty", "sane"]) signal.signal(signal.SIGINT, sighandler) print("Waiting for webserver port to become available...") available = False for try_number in range(0, 10): try: res = requests.get('http://127.0.0.1:%s/' % WEBPORT) available = True break except: time.sleep(0.5) if not available: print("Webserver port not reachable after 10 tries, giving up.") proc1.terminate() proc1.wait() proc2.terminate() proc2.wait() proc4.terminate() proc4.wait() webproc.terminate() webproc.wait() nginx_proc.terminate() nginx_proc.wait() udpproc.terminate() udpproc.wait() taproc.terminate() taproc.wait() subprocess.call(["/bin/stty", "sane"]) sys.exit(2) print("Running tests...") rc = 0 test_env = {} test_env.update(os.environ) test_env.update({'WEBPORT': WEBPORT, 'APIKEY': APIKEY, 'WEBPID': str(webpid)}) try: print("") if len(sys.argv) > 1: p = subprocess.check_call(["pytest", "--junitxml", sys.argv[1]], env=test_env) else: p = subprocess.check_call(["pytest", "--junitxml", "pytest.xml"], env=test_env) except subprocess.CalledProcessError as ex: rc = ex.returncode finally: if wait: print("Waiting as requested, press ENTER to stop.") raw_input() proc1.terminate() proc1.wait() proc2.terminate() proc2.wait() proc4.terminate() proc4.wait() webproc.terminate() webproc.wait() nginx_proc.terminate() nginx_proc.wait() udpproc.terminate() udpproc.wait() taproc.terminate() taproc.wait() subprocess.call(["/bin/stty", "sane"]) sys.exit(rc) weakforced-2.10.2/regression-tests/test_8bit.py000066400000000000000000000010631461473602600215160ustar00rootroot00000000000000import requests import socket import time import json from test_helper import ApiTestCase class TestEightBit(ApiTestCase): def setUp(self): time.sleep(16) super(TestEightBit, self).setUp() def test8Bit(self): r = self.customFuncWithName("EightBitKey", {}) j = r.json() print(json.dumps(j)) time.sleep(4) r = self.customFuncReplicaWithName("EightBitKey", {}) j = r.json() print(json.dumps(j)) count = int(j["r_attrs"]["count"]) self.assertEqual(count, 2) weakforced-2.10.2/regression-tests/test_Attrs.py000066400000000000000000000017601461473602600217510ustar00rootroot00000000000000import requests import socket import time import json from test_helper import ApiTestCase class TestAttrs(ApiTestCase): def test_Attrs(self): attrs = dict() attrs['accountStatus'] = "normal" r = self.allowFuncAttrs('fredbloggs', '127.0.0.1', "1234", attrs) j = r.json() self.assertEqual(j['status'], 0) r.close() attrs['accountStatus'] = "blocked" r = self.allowFuncAttrs('fredbloggs', '127.0.0.1', "1234", attrs) j = r.json() self.assertEqual(j['status'], -1) attrs['accountStatus'] = "normal" attrs['countryList'] = [ "UK", "US" ] r = self.allowFuncAttrs('fredbloggs', '127.0.0.1', "1234", attrs) j = r.json() self.assertEqual(j['status'], 0) r.close() attrs['countryList'] = [ "UK", "US", "Blockmestan" ] r = self.allowFuncAttrs('fredbloggs', '127.0.0.1', "1234", attrs) j = r.json() self.assertEqual(j['status'], -1) weakforced-2.10.2/regression-tests/test_Basics.py000066400000000000000000000062401461473602600220560ustar00rootroot00000000000000import requests import socket import time import json import mmap from test_helper import ApiTestCase class TestBasics(ApiTestCase): def test_unauth(self): r = requests.get(self.url("/?command=stats")) self.assertEqual(r.status_code, requests.codes.unauthorized) def test_auth_stats(self): r = self.session.get(self.url("/?command=stats")) self.assertEqual(r.status_code, requests.codes.ok) def test_ping(self): for _ in range(10): r = self.pingFunc() j = r.json() if j['status'] == 'ok': break time.sleep(1) self.assertEqual(j['status'], 'ok') def test_ping_acl_deny(self): self.writeCmdToConsole('setACL({})') r = self.pingFunc() self.assertEqual(r.status_code, 401) j = r.json() self.assertEqual(j['status'], 'failure') self.assertEqual(j['reason'], 'Source IP Address not in ACL') self.writeCmdToConsole('setACL({"0.0.0.0/0"})') def test_getBL(self): r = self.getBLFunc() j = r.json() self.assertEqual(j['bl_entries'], []) def test_customFunc(self): r = self.customFunc("custom1") j = r.json() self.assertEqual(j['r_attrs']['login'], 'custom1') def test_customGetFunc(self): r = self.customGetFunc("testCustomGet") t = r.text self.assertEqual(t, '1.2.3.4/32\n') def test_deviceParsing(self): r = self.allowFuncDeviceProtocol('foobar', '127.0.0.1', "12432", '"name" "Mac OS X Mail" "version" "10.0 (3226)" "os" "Mac OS X" "os-version" "10.12 (16A323)" "vendor" "Apple Inc."', "imap") j = r.json() self.assertRegex(json.dumps(j), "Mac OS X") r = self.allowFuncDeviceProtocol('foobar', '127.0.0.1', "12432", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A', "http") j = r.json() self.assertRegex(json.dumps(j), "Mac OS X") r = self.allowFuncDeviceProtocol('foobar', '127.0.0.1', "12432", 'OpenXchange.Android.Mail/1.0+1234 (OS: 7.0; device: Samsung/GT9700)', "mobileapi") j = r.json() self.assertRegex(json.dumps(j), "Android") def test_getDBStats(self): self.reportFunc('dbstats', '1.4.3.2', '1234', False); r = self.getDBStatsLogin('dbstats') j = r.json(); self.assertRegex(json.dumps(j), "countLogins") r = self.getDBStatsIP('1.4.3.2') j = r.json(); self.assertRegex(json.dumps(j), "countLogins") self.assertRegex(json.dumps(j), "bl_reason") self.assertRegex(json.dumps(j), '"blacklisted": false') def chunkGen(self): payload = dict() payload['login'] = "chunky" payload['remote'] = "127.0.0.1" payload['pwhash'] = "1234" payload['success'] = True payload['attrs'] = {} yield json.dumps(payload).encode() def testChunked(self): r = self.session.post( self.url("/?command=report"), data=self.chunkGen(), headers={'Content-Type': 'application/json'}) j = r.json() self.assertEqual(j['status'], 'ok') weakforced-2.10.2/regression-tests/test_Blacklist.py000066400000000000000000000115131461473602600225610ustar00rootroot00000000000000import requests import socket import subprocess import sys import time import json from test_helper import ApiTestCase class TestBlacklist(ApiTestCase): def test_NetmaskBlacklist(self): r = self.allowFunc('goodie', '193.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2002:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryNetmask("193.168.0.0/16", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addBLEntryNetmask("2002:503:ba3e::/64", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '193.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], -1) r.close() r = self.allowFunc('goodie', '2002:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], -1) r.close() time.sleep(11); r = self.allowFunc('goodie', '193.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2002:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_IPBlacklist(self): r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2001:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryIP("192.168.72.14", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addBLEntryIP("2001:503:ba3e::2:30", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], -1) r.close() r = self.allowFunc('goodie', '2001:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], -1) r.close() time.sleep(11); r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2001:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_LoginBlacklist(self): r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryLogin("goodie", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], -1) r.close() time.sleep(11); r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_IPLoginBlacklist(self): r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryIPLogin("192.168.72.14", "goodie", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], -1) r.close() r = self.allowFunc('goody', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '192.168.72.15', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() time.sleep(11); r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_PersistBlacklist(self): cmd3 = ("../wforce/wforce -D -C ./wforce3.conf -R ../wforce/regexes.yaml").split() proc3 = subprocess.Popen(cmd3, close_fds=True) time.sleep(1) r = self.addBLEntryIPPersist("99.99.99.99", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') print("Killing process") proc3.terminate() print("Waiting for process") proc3.wait() proc3 = subprocess.Popen(cmd3, close_fds=True) time.sleep(1) r = self.getBLFuncPersist() j = r.json() self.assertNotEqual(str.find(json.dumps(j),'99.99.99.99'), -1) proc3.terminate() proc3.wait() weakforced-2.10.2/regression-tests/test_Block.py000066400000000000000000000010631461473602600217020ustar00rootroot00000000000000import requests import socket import time import json from test_helper import ApiTestCase class TestBlock(ApiTestCase): def test_block(self): r = self.allowFunc("baddie", "127.0.0.1", "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(100): r = self.reportFunc('baddie', '127.0.0.1', "1234'%s" % i, False) r.json() r = self.allowFunc('baddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r.close() weakforced-2.10.2/regression-tests/test_DNSLookups.py000066400000000000000000000057771461473602600226710ustar00rootroot00000000000000import requests import socket import time import json from test_helper import ApiTestCase class TestDNSLookups(ApiTestCase): def test_DNSLookups(self): self.writeCmdToConsole("rootca4 = newCA(\"198.41.0.4\")") self.writeCmdToConsole("rootca6 = newCA(\"2001:503:ba3e::2:30\")") self.writeCmdToConsole("rblca = newCA(\"127.0.0.2\")") self.writeCmdToConsole("newDNSResolver(\"DNSTestResolv\")") self.writeCmdToConsole("resolv = getDNSResolver(\"DNSTestResolv\")") self.writeCmdToConsole("resolv:addResolver(\"208.67.222.222\", 53)") res = self.writeCmdToConsole("showAddrByName(resolv, \"a.root-servers.net.\")") res = res.decode() if ((str.find(res, '198.41.0.4') == -1) and (str.find(res, "a.root-servers.net") != -1)): # this means there is a middlebox messing with us - replace 192.168.1.254 with your middlebox IP self.writeCmdToConsole("newDNSResolver(\"MiddleBoxResolv\")") self.writeCmdToConsole("resolv = getDNSResolver(\"MiddleBoxResolv\")") self.writeCmdToConsole("resolv:addResolver(\"192.168.1.254\", 53)") res = self.writeCmdToConsole("showAddrByName(resolv, \"a.root-servers.net.\")") self.assertNotEqual(str.find(res,'198.41.0.4'), -1) self.assertNotEqual(str.find(res,'2001:503:ba3e::2:30'), -1) res = self.writeCmdToConsole("showNameByAddr(resolv, rootca4)") self.assertNotEqual(str.find(res.decode(),'a.root-servers.net.'), -1) res = self.writeCmdToConsole("showNameByAddr(resolv, rootca6)") self.assertNotEqual(str.find(res.decode(),'a.root-servers.net.'), -1) res = self.writeCmdToConsole("showRBL(resolv, rblca, \"sbl.spamhaus.org.\")") self.assertNotEqual(str.find(res.decode(),'127.0.0.2'), -1) def test_DNSRetries(self): self.writeCmdToConsole("newDNSResolver(\"RetryResolv\")") self.writeCmdToConsole("resolv = getDNSResolver(\"RetryResolv\")") self.writeCmdToConsole("resolv:addResolver(\"208.67.222.222\", 5353)") self.writeCmdToConsole("resolv:addResolver(\"208.67.222.222\", 53)") # the DNS show... functions are hardcoded to retry once if encountering a timeout res = self.writeCmdToConsole("showAddrByName(resolv, \"a.root-servers.net.\")") if ((str.find(res.decode(), '198.41.0.4') == -1) and (str.find(res.decode(), "a.root-servers.net") != -1)): # this means there is a middlebox messing with us - replace 192.168.1.254 with your middlebox IP self.writeCmdToConsole("resolv = newDNSResolver(\"MiddleBoxRetryResolv\")") self.writeCmdToConsole("resolv = getDNSResolver(\"MiddleBoxRetryResolv\")") self.writeCmdToConsole("resolv:addResolver(\"192.168.1.254\", 5353)") self.writeCmdToConsole("resolv:addResolver(\"192.168.1.254\", 53)") res = self.writeCmdToConsole("showAddrByName(resolv, \"a.root-servers.net.\")") self.assertNotEqual(str.find(res.decode(),'198.41.0.4'), -1) weakforced-2.10.2/regression-tests/test_DumpEntries.py000066400000000000000000000013751461473602600231150ustar00rootroot00000000000000import requests import socket import time import json import os from subprocess import call from test_helper import ApiTestCase class TestDumpEntries(ApiTestCase): def test_dumpentries(self): for i in range(100): r = self.reportFunc('dumpentries', '199.99.99.99', "1234'%s" % i, False) dumpfile = "/tmp/wfdb.txt" result = call(["../wforce/wf_dump_entries", "-f", dumpfile, "-u", "http://127.0.0.1:8084", "-l", "127.0.0.1", "-p", "9999", "-w", os.environ.get('APIKEY', 'super')]) if result: f = open(dumpfile, "r") found = False for x in f: if x.find("199.99.99.99"): found = True break self.assertTrue(found) weakforced-2.10.2/regression-tests/test_GeoIP.py000066400000000000000000000020721461473602600216140ustar00rootroot00000000000000import requests import socket import time import json import os from test_helper import ApiTestCase class TestGeoIP(ApiTestCase): def setUp(self): super(TestGeoIP, self).setUp() def test_geoIP(self): # don't allow IPs from Japan (arbitrary) r = self.allowFunc('baddie', '112.78.112.20', "1234") j = r.json() self.assertEqual(j['status'], -1) self.assertRegex(json.dumps(j), "Japan") r.close() def test_geoIP2City(self): attrs = dict() attrs['ip'] = '128.243.1.1' r = self.customFuncWithName("geoip2", attrs) j = r.json() self.assertRegex(json.dumps(j), "Nottingham") r.close() def test_geoIP2LookupVals(self): attrs = dict() attrs['ip'] = '128.243.21.1' r = self.customFuncWithName("geoip2_lookupValue", attrs) j = r.json() print(json.dumps(j)) self.assertRegex(json.dumps(j['r_attrs']['city']), "Nottingham") self.assertRegex(json.dumps(j['r_attrs']['latitude']), "52.9044") r.close() weakforced-2.10.2/regression-tests/test_LogLevel.py000066400000000000000000000027771461473602600223760ustar00rootroot00000000000000import subprocess import time from test_helper import ApiTestCase ERROR = 3 WARNING = 4 INFO = 6 DEBUG = 7 class TestBasics(ApiTestCase): def check_loglevel(self, loglevel, content): cmd = ('../wforce/wforce -v -D -C ./wforce3.conf -R ' '../wforce/regexes.yaml --loglevel %d' % loglevel).split() try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) self.generate_log_entries() finally: proc.terminate() proc.wait() out, err = proc.communicate() if out: out = out.decode() if err: err = err.decode() assert content in out, out def trigger_log_entry(self, cmd): for i in range(20): out = self.writeCmdToConsole3(cmd).decode() if "Connection refused" not in out: break time.sleep(1) assert "Connection refused" not in out def generate_log_entries(self): self.trigger_log_entry('debugLog("Debug Test", {})') self.trigger_log_entry('infoLog("Info Test", {})') self.trigger_log_entry('warnLog("Warning Test", {})') self.trigger_log_entry('errorLog("Error Test", {})') def test_debug(self): self.check_loglevel(DEBUG, "Debug Test") def test_info(self): self.check_loglevel(INFO, "Info Test") def test_warning(self): self.check_loglevel(WARNING, "Warning Test") def test_error(self): self.check_loglevel(ERROR, "Error Test") weakforced-2.10.2/regression-tests/test_Prometheus.py000066400000000000000000000114411461473602600230040ustar00rootroot00000000000000import requests import time import os import subprocess from urllib.parse import urlparse from urllib.parse import urljoin from prometheus_client.parser import text_string_to_metric_families from prometheus_client.samples import Sample from test_helper import ApiTestCase PROMETHEUS_PORT="9090" PROMETHEUS_URL="http://localhost:%s" % PROMETHEUS_PORT PROMETHEUS_CONF="./prometheus-wforce.yml" class TestPrometheus(ApiTestCase): def parsePrometheusResponse(self, response): # parse the text feed and iterate over samples so we make sure # the content is well formated for family in text_string_to_metric_families(response): for sample in family.samples: self.assertTrue(isinstance(sample, Sample)) result = {} for line in response.split("\n"): if not line or line.startswith('#'): continue values = line.split(" ") self.assertGreaterEqual(len(values), 2) result[" ".join(values[0:-1])] = values[-1] return result def getWforcePrometheusValues(self): r = self.getWforceMetrics() self.assertTrue(r) self.assertEqual(r.status_code, 200) return self.parsePrometheusResponse(r.text) def getTrackalertPrometheusValues(self): r = self.getTrackalertMetrics() self.assertTrue(r) self.assertEqual(r.status_code, 200) return self.parsePrometheusResponse(r.text) def checkWforceValues(self, values): for key in ['wforce_worker_queue_duration_seconds_bucket{le="0.001"}', 'wforce_worker_response_duration_seconds_bucket{le="0.001"}', 'wforce_commands_total{cmd="ping"}', 'wforce_custom_stats_total{metric="customStat"}', 'wforce_allow_status_total{status="denied"}', 'wforce_replication_sent_total{sibling="127.0.0.1:4001",status="ok"}', 'wforce_replication_rcvd_total{sibling="127.0.0.1",status="ok"}', 'wforce_replication_tcp_connfailed_total{sibling="127.0.0.1:4001"}', 'wforce_redis_wlbl_updates_total{type="bl"}', 'wforce_redis_wlbl_connfailed_total{type="wl"}', 'wforce_bl_entries{type="iplogin"}', 'wforce_wl_entries{type="ip"}', 'wforce_web_queue_size', 'wforce_webhook_queue_size']: self.assertIn(key, values) self.assertGreaterEqual(float(values[key]), 0) def checkTrackalertValues(self, values): for key in ['trackalert_worker_queue_duration_seconds_bucket{le="0.001"}', 'trackalert_worker_response_duration_seconds_bucket{le="0.001"}', 'trackalert_active_http_connections', 'trackalert_commands_total{cmd="report"}']: self.assertIn(key, values) self.assertGreaterEqual(float(values[key]), 0) def test_WforceMetrics(self): values = self.getWforcePrometheusValues() self.checkWforceValues(values) allow_count = float(values['wforce_commands_total{cmd="allow"}']) repl_rcvd_count = float(values['wforce_replication_rcvd_total{sibling="127.0.0.1",status="ok"}']) # add one message before checking stats again self.allowFunc("foobar", "99.22.33.11", "1234") self.reportFuncReplica("foobar", "99.22.33.11", "1234", False) time.sleep(1) values = self.getWforcePrometheusValues() self.assertGreater(float(values['wforce_commands_total{cmd="allow"}']), allow_count) self.assertGreater(float(values['wforce_worker_response_duration_seconds_bucket{le="+Inf"}']), 0) self.assertGreater(float(values['wforce_replication_rcvd_total{sibling="127.0.0.1",status="ok"}']), repl_rcvd_count) def test_TrackalertMetrics(self): values = self.getTrackalertPrometheusValues() self.checkTrackalertValues(values) custom_count = float(values['trackalert_commands_total{cmd="custom"}']) # add one message before checking stats again self.trackalertCustomFunc("foobar") values = self.getTrackalertPrometheusValues() self.assertGreater(float(values['trackalert_commands_total{cmd="custom"}']), custom_count) self.assertGreater(float(values['trackalert_worker_response_duration_seconds_bucket{le="+Inf"}']), 0) def test_RealPrometheus(self): cmd = ["prometheus", "--config.file=%s" % PROMETHEUS_CONF] prometheus = subprocess.Popen(cmd, close_fds=True) prometheus_tries = 20 try: for i in range(prometheus_tries+1): try: r = requests.get('%s/api/v1/label/__name__/values' % PROMETHEUS_URL) assert 'wforce_commands_total' in r.text, r.text break except: if i == prometheus_tries: raise time.sleep(4) finally: prometheus.terminate() prometheus.wait() weakforced-2.10.2/regression-tests/test_Replication.py000066400000000000000000000307051461473602600231260ustar00rootroot00000000000000import requests import socket import time import json from test_helper import ApiTestCase class TestTimeWindowsReplication(ApiTestCase): def setUp(self): time.sleep(16) super(TestTimeWindowsReplication, self).setUp() def test_invalidPasswords(self): r = self.allowFunc('ivbaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(22): r = self.reportFunc('ivbaddiereplication', '127.0.0.1', "1234'%s" % i, True) r.json() time.sleep(1) r = self.allowFuncReplica('ivbaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('ivbaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(16) r = self.allowFuncReplica('ivbaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('ivbaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_FailedLogins(self): r = self.allowFunc('flbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(32): r = self.reportFunc('flbaddiereplication', '128.0.0.1', "1234", False) r.json() time.sleep(1) r = self.allowFuncReplica('flbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('flbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(16) r = self.allowFuncReplica('flbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('flbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_diffIPs(self): r = self.allowFunc('ipbaddiereplication', '227.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(12): r = self.reportFunc('ipbaddiereplication', "227.0.0.%s" % i, "1234", True) r.json() time.sleep(1) r = self.allowFuncReplica('ipbaddiereplication', '227.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('ipbaddiereplication', '227.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(16) r = self.allowFuncReplica('ipbaddiereplication', '227.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('ipbaddiereplication', '227.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_subTest(self): r = self.allowFunc('subbaddiereplication', '228.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(42): r = self.reportFunc('subbaddiereplication', '228.0.0.1', "1234", False) r.json() r = self.allowFuncReplica('subbaddiereplication', '228.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('subbaddiereplication', '228.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(15) r = self.allowFuncReplica('subbaddiereplication', '227.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('subbaddiereplication', '227.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_Prefixv4(self): r = self.allowFunc('ipv4baddiereplication', '114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(50): r = self.reportFunc('ipv4baddiereplication%s' % i, "114.31.193.%s" % i, "1234", True) r.json() time.sleep(1) r = self.allowFuncReplica('ipv4baddiereplication', '114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('ipv4baddiereplication', '114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(16) r = self.allowFuncReplica('ipv4baddiereplication', '114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('ipv4baddiereplication', '114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_PrefixMappedv4(self): r = self.allowFunc('mappedipv4baddiereplication', '::ffff:114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(50): r = self.reportFunc('mappedipv4baddiereplication%s' % i, "::ffff:%s.31.193.200" % i, "1234", True) r.json() time.sleep(1) r = self.allowFuncReplica('mappedipv4baddiereplication', '::ffff:114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('mappedipv4baddiereplication', '::ffff:114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) # Wait for the time windows to clear and then check again time.sleep(16) r = self.allowFuncReplica('mappedipv4baddiereplication', '::ffff:114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('mappedipv4baddiereplication', '::ffff:114.31.193.200', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_Prefixv6(self): r = self.allowFunc('ipv6baddiereplication', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(50): r = self.reportFunc('ipv6baddiereplication%s' % i, "2001:c78::%s" % i, "1234", True) r.json() time.sleep(1) r = self.allowFuncReplica('ipv6baddiereplication', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('ipv6baddiereplication', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(16) r = self.allowFuncReplica('ipv6baddiereplication', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('ipv6baddiereplication', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_expiry(self): r = self.allowFunc('expirebaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(20): r = self.reportFunc('expirebaddiereplication%s' % i, "127.0.0.1", "1234", True) r.json() for i in range(20): r = self.reportFunc('expirebaddiereplication%s' % i, "127.0.0.1", "1234", True) r.json() r = self.allowFuncReplica('expirebaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('expirebaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the expiry thread to delete everything bigger than size (10) and then check again time.sleep(30) r = self.allowFuncReplica('expirebaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('expirebaddiereplication', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_Reset(self): r = self.allowFunc('resetbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(32): r = self.reportFunc('resetbaddiereplication', '128.0.0.1', "1234", False) r.json() time.sleep(1) r = self.allowFuncReplica('resetbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.allowFuncReplica2('resetbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.resetFunc('resetbaddiereplication', '128.0.0.1') j = r.json() self.assertEqual(j['status'], "ok") time.sleep(1) r = self.allowFuncReplica('resetbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r = self.allowFuncReplica2('resetbaddiereplication', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_resetField(self): r = self.resetLogins("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '0') r = self.incLogins("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '1') time.sleep(1) r = self.countLoginsReplica("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '1') r = self.countLoginsReplica2("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '1') r = self.resetLogins("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '0') time.sleep(1) r = self.countLoginsReplica("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '0') r = self.countLoginsReplica2("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '0') def test_BlackWhitelist(self): self.addBLEntryIP('12.12.12.12', 60, "replication bl test") self.addWLEntryIP('13.13.13.13', 60, "replication wl test") r = self.getBLFunc(); j = r.json() self.assertNotEqual(str.find(json.dumps(j), '12.12.12.12'), -1) r = self.getWLFunc(); j = r.json() self.assertNotEqual(str.find(json.dumps(j), '13.13.13.13'), -1) time.sleep(1) r = self.getBLFuncReplica(); j = r.json() self.assertNotEqual(str.find(json.dumps(j), '12.12.12.12'), -1) r = self.getWLFuncReplica(); j = r.json() self.assertNotEqual(str.find(json.dumps(j), '13.13.13.13'), -1) def test_AddRemoveSiblings(self): self.assertEqual(self.removeSibling("127.0.0.1", 4002).ok, True) self.assertEqual(self.addSibling("127.0.0.1", 4002, "udp", "KaiQkCHloe2ysXv2HbxBAFqHI4N8+ahmwYwsbYlDdF0=").ok, True) self.assertEqual(self.addBLEntryIP('9.2.4.6', 60, "remove and add sibling test").ok, True) time.sleep(1) r = self.getBLFuncReplica() j = r.json() self.assertNotEqual(str.find(json.dumps(j), '9.2.4.6'), -1) def test_SetSiblings(self): self.assertEqual(self.setSiblings({"siblings": [ {"sibling_host": "127.0.0.1", "sibling_port": 4001, "sibling_protocol": "udp"}, {"sibling_host": "127.0.0.1", "sibling_port": 4002, "sibling_protocol": "udp", "encryption_key": "KaiQkCHloe2ysXv2HbxBAFqHI4N8+ahmwYwsbYlDdF0="}, {"sibling_host": "127.0.0.1", "sibling_port": 4004, "sibling_protocol": "tcp", "encryption_key": "lykfkV/07VPMK80nLNOTWtlMsLz9y7X0r6t9zcFNTmE="} ]}).ok, True) self.assertEqual(self.addBLEntryIP('10.2.4.6', 60, "remove and add sibling test").ok, True) time.sleep(1) r = self.getBLFuncReplica(); j = r.json() self.assertNotEqual(str.find(json.dumps(j), '10.2.4.6'), -1) weakforced-2.10.2/regression-tests/test_SyncDBs.py000066400000000000000000000015341461473602600221600ustar00rootroot00000000000000import requests import socket import subprocess import sys import time import json from test_helper import ApiTestCase class TestSyncDBs(ApiTestCase): def test_SyncDBs(self): for i in range(42): r = self.reportFunc('subbaddie%s' % i, '128.0.0.%s' % i, "1234", False) r.json() res1 = self.writeCmdToConsole("showStringStatsDB()"); res1_ss = res1.decode().split("DB Name", 1)[1] time.sleep(11); cmd3 = ("../wforce/wforce -D -C ./wforce3.conf -R ../wforce/regexes.yaml").split() proc3 = subprocess.Popen(cmd3, close_fds=True) time.sleep(5) res2 = self.writeCmdToConsole3("showStringStatsDB()"); res2_ss = res2.decode().split("DB Name", 1)[1] self.assertEqual(res1_ss == res2_ss, True) proc3.terminate() proc3.wait() weakforced-2.10.2/regression-tests/test_TimeWindows.py000066400000000000000000000151021461473602600231200ustar00rootroot00000000000000import requests import socket import time import json from test_helper import ApiTestCase class TestTimeWindows(ApiTestCase): def setUp(self): time.sleep(16) super(TestTimeWindows, self).setUp() def test_invalidPasswords(self): r = self.allowFunc('ivbaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(25): r = self.reportFunc('ivbaddie', '127.0.0.1', "1234'%s" % i, True) r.json() r = self.allowFunc('ivbaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(20) r = self.allowFunc('ivbaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_FailedLogins(self): r = self.allowFunc('flbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(32): r = self.reportFunc('flbaddie', '128.0.0.1', "1234", False) r.json() r = self.allowFunc('flbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(20) r = self.allowFunc('flbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_diffIPs(self): r = self.allowFunc('ipbaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(12): r = self.reportFunc('ipbaddie', "127.0.0.%s" % i, "1234", True) r.json() r = self.allowFunc('ipbaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(20) r = self.allowFunc('ipbaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_subTest(self): r = self.allowFunc('subbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(42): r = self.reportFunc('subbaddie', '128.0.0.1', "1234", False) r.json() r = self.allowFunc('subbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(20) r = self.allowFunc('subbaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_Prefixv4(self): r = self.allowFunc('ipv4baddie', '114.31.192.200', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(50): r = self.reportFunc('ipv4baddie%s' % i, "114.31.192.%s" % i, "1234", True) r.json() r = self.allowFunc('ipv4baddie', '114.31.192.200', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(20) r = self.allowFunc('ipv4baddie', '114.31.192.200', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_PrefixMappedv4(self): r = self.allowFunc('mappedipv4baddie', '::ffff:114.31.192.200', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(50): r = self.reportFunc('mappedipv4baddie%s' % i, "::ffff:%s.31.192.200" % i, "1234", True) r.json() r = self.allowFunc('mappedipv4baddie', '::ffff:114.31.192.200', "1234") j = r.json() self.assertEqual(j['status'], 0) # Wait for the time windows to clear and then check again time.sleep(20) r = self.allowFunc('mappedipv4baddie', '::ffff:114.31.192.200', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_Prefixv6(self): r = self.allowFunc('ipv6baddie', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(50): r = self.reportFunc('ipv6baddie%s' % i, "2001:c78::%s" % i, "1234", True) r.json() r = self.allowFunc('ipv6baddie', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the time windows to clear and then check again time.sleep(20) r = self.allowFunc('ipv6baddie', '2001:c78::1000', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_expiry(self): r = self.allowFunc('expirebaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(50): r = self.reportFunc('expirebaddie%s' % i, "127.0.0.1", "1234", True) r.json() r = self.allowFunc('expirebaddie', '127.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) # Wait for the expiry thread to delete everything bigger than size (10) and then check again time.sleep(35) r = self.allowFunc('expirebaddie', '127.0.01', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_Reset(self): time.sleep(16) r = self.allowFunc('resetbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() for i in range(32): r = self.reportFunc('resetbaddie', '128.0.0.1', "1234", False) r.json() r = self.allowFunc('resetbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], -1) r = self.resetFunc('resetbaddie', '128.0.0.1') j = r.json() self.assertEqual(j['status'], "ok") r = self.allowFunc('resetbaddie', '128.0.0.1', "1234") j = r.json() self.assertEqual(j['status'], 0) def test_ResetField(self): r = self.resetLogins("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '0') r = self.incLogins("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '1') r = self.resetLogins("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '0') r = self.countLogins("resetFieldTest") j = r.json() self.assertEqual(j['r_attrs']['countLogins'], '0') weakforced-2.10.2/regression-tests/test_Trackalert.py000066400000000000000000000043571461473602600227550ustar00rootroot00000000000000import requests import socket import re import os import time import json import mmap from test_helper import ApiTestCase class TestTrackalert(ApiTestCase): def test_auth_stats(self): r = self.session.get(self.ta_url("/?command=stats")) self.assertEqual(r.status_code, requests.codes.ok) def test_wforceToTrackalert(self): self.writeCmdToConsole("addWebHook(ta_events, tack)") r = self.reportFunc('wforce2trackalert', '1.4.3.2', '1234', False); j = r.json() self.assertEqual(j['status'], 'ok') time.sleep(5) logfile = open('/tmp/trackalert.log', 'r') s = mmap.mmap(logfile.fileno(), 0, access=mmap.ACCESS_READ) search_str = s.read(s.size()).decode() for mystring in [ 'login=wforce2trackalert', 'remote=1.4.3.2' ]: regex = re.escape(mystring) result = re.search(regex, search_str); self.assertNotEqual(result, None) s.close() logfile.close() def test_trackalertReport(self): attrs = dict() attrs['accountStatus'] = "normal" r = self.taReportFuncAttrs('tareportuser', '127.0.0.1', 'foo', True, attrs) j = r.json() time.sleep(5) logfile = open('/tmp/trackalert.log', 'r') s = mmap.mmap(logfile.fileno(), 0, access=mmap.ACCESS_READ) search_str = s.read(s.size()).decode() for mystring in [ 'login=tareportuser', 'remote=127.0.0.1' ]: regex = re.escape(mystring) result = re.search(regex, search_str); self.assertNotEqual(result, None) s.close() logfile.close() def test_trackalertBackgroundFuncs(self): time.sleep(61) logfile = open('/tmp/trackalert.log', 'r') s = mmap.mmap(logfile.fileno(), 0, access=mmap.ACCESS_READ) search_str = s.read(s.size()).decode() for mystring in [ 'background1', 'background2' ]: regex = re.escape(mystring) result = re.search(regex, search_str); self.assertNotEqual(result, None) s.close() logfile.close() def test_trackalertCustomFunc(self): r = self.trackalertCustomFunc("custom1") j = r.json() self.assertEqual(j['r_attrs']['login'], 'custom1') weakforced-2.10.2/regression-tests/test_WebHooks.py000066400000000000000000000070771461473602600224040ustar00rootroot00000000000000import requests import mmap import re import time import os import json from test_helper import ApiTestCase class TestWebHooks(ApiTestCase): def test_webhooks(self): self.writeCmdToConsole("addWebHook(events, ck)") self.reportFunc('webhooktest', '1.4.3.1', '1234', False) self.allowFunc('webhooktest', '1.4.3.2', '1234') self.resetFunc('webhooktest', '1.4.3.3'); self.addBLEntryLogin('webhooktest', 10, 'This is not a reason') time.sleep(11) self.addBLEntryLogin('webhooktest', 10, 'This is not a reason') self.delBLEntryLogin('webhooktest') time.sleep(5) logfile = open('/tmp/webhook-server.log', 'r') s = mmap.mmap(logfile.fileno(), 0, access=mmap.ACCESS_READ) search_str = s.read(s.size()).decode() for event in [ 'report', 'allow', 'reset', 'addbl', 'delbl', 'expirebl' ]: regex = r"digest_match=True, event=" + re.escape(event) result = re.search(regex, search_str); self.assertNotEqual(result, None) s.close() logfile.close() def test_mtls_webhook(self): self.writeCmdToConsole("addWebHook(events, mtls_ck)") self.reportFunc('mtls_webhook_test', '1.4.3.1', '1234', False) self.allowFunc('mtls_webhook_test2', '1.4.3.2', '1234') self.resetFunc('mtls_webhook_test2', '1.4.3.3'); self.addBLEntryLogin('mtls_webhook_test2', 10, 'This is not a reason') self.addBLEntryLogin('mtls_webhook_test2', 10, 'This is not a reason') self.delBLEntryLogin('mtls_webhook_test2') time.sleep(4) logfile = open('/var/log/nginx/access.log', 'r') s = mmap.mmap(logfile.fileno(), 0, access=mmap.ACCESS_READ) search_str = s.read(s.size()).decode() self.assertIn('"POST / HTTP/1.1" 200', search_str) s.close() logfile.close() def test_kafka_webhooks(self): self.writeCmdToConsole("addWebHook(events, kafka_ck)") self.reportFunc('kafkawebhooktest', '4.4.3.1', '1234', False) self.reportFunc('kafkawebhooktest', '4.4.3.1', '1234', False) time.sleep(5) r = self.kafkaProducer() j = r.json() print(json.dumps(j)) self.assertGreater(j['offsets'][0]['offset'], 0) def test_customwebhooks(self): self.writeCmdToConsole("addCustomWebHook(\"customwebhook\", ck)") r = self.customFunc("custom1") j = r.json() self.assertEqual(j['r_attrs']['login'], 'custom1') time.sleep(5) logfile = open('/tmp/webhook-server.log', 'r') s = mmap.mmap(logfile.fileno(), 0, access=mmap.ACCESS_READ) search_str = s.read(s.size()).decode() for event in [ 'customwebhook' ]: regex = r"digest_match=True, event=" + re.escape(event) result = re.search(regex, search_str); self.assertNotEqual(result, None) s.close() logfile.close() def test_namedreportsinks(self): self.writeCmdToConsole("addNamedReportSink(\"trackalert\", \"127.0.0.1:4502\")") r = self.reportFunc('namedreportsink', '1.2.3.4', '1234', False) r = self.customFunc("customargs") time.sleep(1) logfile = open('/tmp/udp-sink.log', 'r') s = mmap.mmap(logfile.fileno(), 0, access=mmap.ACCESS_READ) search_str = s.read(s.size()).decode() for event in [ 'namedreportsink', 'customargs' ]: regex = r"\"login\": \"" + re.escape(event) result = re.search(regex, search_str); self.assertNotEqual(result, None) s.close() logfile.close() weakforced-2.10.2/regression-tests/test_Whitelist.py000066400000000000000000000130021461473602600226200ustar00rootroot00000000000000import requests import socket import subprocess import sys import time import json from test_helper import ApiTestCase class TestWhitelist(ApiTestCase): def test_NetmaskWhitelist(self): r = self.allowFunc('goodie', '193.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2002:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryNetmask("193.168.0.0/16", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addBLEntryNetmask("2002:503:ba3e::/64", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addWLEntryNetmask("193.168.0.0/16", 10, "test whitelist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addWLEntryNetmask("2002:503:ba3e::/64", 10, "test whitelist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '193.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2002:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() time.sleep(11); r = self.allowFunc('goodie', '193.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2002:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_IPWhitelist(self): r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2001:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryIP("192.168.72.14", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addBLEntryIP("2001:503:ba3e::2:30", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addWLEntryIP("192.168.72.14", 10, "test whitelist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addWLEntryIP("2001:503:ba3e::2:30", 10, "test whitelist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2001:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() time.sleep(11); r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '2001:503:ba3e::2:30', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_LoginWhitelist(self): r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryLogin("goodie", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addWLEntryLogin("goodie", 10, "test whitelist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() time.sleep(11); r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_IPLoginBlacklist(self): r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.addBLEntryIPLogin("192.168.72.14", "goodie", 10, "test blacklist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.addWLEntryIPLogin("192.168.72.14", "goodie", 10, "test whitelist") j = r.json() self.assertEqual(j['status'], 'ok') r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() r = self.allowFunc('goodie', '192.168.72.15', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() time.sleep(11); r = self.allowFunc('goodie', '192.168.72.14', "1234") j = r.json() self.assertEqual(j['status'], 0) r.close() def test_PersistWhitelist(self): cmd3 = ("../wforce/wforce -D -C ./wforce3.conf -R ../wforce/regexes.yaml").split() proc3 = subprocess.Popen(cmd3, close_fds=True) time.sleep(1) r = self.addWLEntryIPPersist("99.99.99.99", 10, "test whitelist") j = r.json() self.assertEqual(j['status'], 'ok') print("Killing process") proc3.terminate() print("Waiting for process") proc3.wait() proc3 = subprocess.Popen(cmd3, close_fds=True) time.sleep(1) r = self.getWLFuncPersist() j = r.json() self.assertNotEqual(str.find(json.dumps(j),'99.99.99.99'), -1) proc3.terminate() proc3.wait() weakforced-2.10.2/regression-tests/test_helper.py000066400000000000000000000513611461473602600221350ustar00rootroot00000000000000from datetime import datetime import os import requests from urllib.parse import urlparse from urllib.parse import urljoin import unittest import json from subprocess import call, check_output DAEMON = os.environ.get('DAEMON', 'authoritative') class ApiTestCase(unittest.TestCase): @classmethod def setUpClass(cls): """On inherited classes, run our `setUp` method""" if cls is not ApiTestCase and cls.setUp is not ApiTestCase.setUp: orig_setUp = cls.setUp def setUpOverride(self, *args, **kwargs): ApiTestCase.setUp(self) return orig_setUp(self, *args, **kwargs) cls.setUp = setUpOverride def setUp(self): # TODO: config self.server_address = '127.0.0.1' self.server1_port = int(os.environ.get('WEBPORT', '8184')) self.server1_url = 'http://%s:%s/' % (self.server_address, self.server1_port) self.server2_port = 8085 self.server2_url = 'http://%s:%s/' % (self.server_address, self.server2_port) self.server3_port = 8086 self.server3_url = 'http://%s:%s/' % (self.server_address, self.server3_port) self.server4_port = 8087 self.server4_url = 'http://%s:%s/' % (self.server_address, self.server4_port) self.ta_server_port = 8090 self.ta_server_url = 'http://%s:%s/' % (self.server_address, self.ta_server_port) self.docker_image_server_url = 'http://%s:%s/' % ("wforce_image", 18084) self.report_server_port = 5000 self.report_server_url = 'http://%s:%s' % (self.server_address, self.report_server_port) self.session = requests.Session() self.session.auth = ('foo', os.environ.get('APIKEY', 'super')) self.session.verify = 'certs/root.pem' # self.session.keep_alive = False # self.session.headers = {'X-API-Key': os.environ.get('APIKEY', 'changeme-key'), 'Origin': 'http://%s:%s' % (self.server_address, self.server_port)} def writeFileToConsole(self, file): fp = open(file) cmds_nl = fp.read() # Lua doesn't need newlines and the console gets confused by them e.g. # function definitions cmds = cmds_nl.replace("\n", " ") return call(["../wforce/wforce", "-c", "./wforce1.conf", "-R", "../wforce/regexes.yaml", "-e", cmds]) def writeCmdToConsole(self, cmd): return check_output(["../wforce/wforce", "-c", "./wforce1.conf", "-R", "../wforce/regexes.yaml", "-e", cmd]) def writeCmdToConsole3(self, cmd): return check_output(["../wforce/wforce", "-c", "./wforce3.conf", "-R", "../wforce/regexes.yaml", "-e", cmd]) def writeFileToConsoleReplica(self, file): fp = open(file) cmds_nl = fp.read() # Lua doesn't need newlines and the console gets confused by them e.g. # function definitions cmds = cmds_nl.replace("\n", " ") return call(["../wforce/wforce", "-c", "./wforce2.conf", "-R", "../wforce/regexes.yaml", "-e", cmds]) def writeCmdToConsoleReplica(self, cmd): return check_output(["../wforce/wforce", "-c", "./wforce2.conf", "-R", "../wforce/regexes.yaml", "-e", cmd]) def allowFunc(self, login, remote, pwhash): return self.allowFuncAttrsInternal(login, remote, pwhash, {}, "", "", False) def allowFuncAttrs(self, login, remote, pwhash, attrs): return self.allowFuncAttrsInternal(login, remote, pwhash, attrs, "", "", False) def allowFuncReplica(self, login, remote, pwhash): return self.allowFuncAttrsInternal(login, remote, pwhash, {}, "", "", True) def allowFuncAttrsReplica(self, login, remote, pwhash, attrs): return self.allowFuncAttrsInternal(login, remote, pwhash, attrs, "", "", True) def allowFuncReplica2(self, login, remote, pwhash): return self.allowFuncAttrsInternal(login, remote, pwhash, {}, "", "", True, True) def allowFuncAttrsReplica2(self, login, remote, pwhash, attrs): return self.allowFuncAttrsInternal(login, remote, pwhash, attrs, "", "", True, True) def allowFuncDeviceProtocol(self, login, remote, pwhash, device_id, protocol): return self.allowFuncAttrsInternal(login, remote, pwhash, {}, device_id, protocol, False) def allowFuncAttrsInternal(self, login, remote, pwhash, attrs, device_id, protocol, replica, replica2=False): payload = dict() payload['login'] = login payload['remote'] = remote payload['pwhash'] = pwhash payload['attrs'] = attrs payload['device_id'] = device_id payload['protocol'] = protocol payload['tls'] = False if not replica: return self.session.post( self.url("/?command=allow"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) elif replica and not replica2: return self.session.post( self.url2("/?command=allow"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) else: return self.session.post( self.url4("/?command=allow"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def reportFunc(self, login, remote, pwhash, success): return self.reportFuncAttrsInternal(login, remote, pwhash, success, {}, "", "", False) def reportFuncReplica(self, login, remote, pwhash, success): return self.reportFuncAttrsInternal(login, remote, pwhash, success, {}, "", "", True) def reportFuncAttrs(self, login, remote, pwhash, success, attrs): return self.reportFuncAttrsInternal(login, remote, pwhash, success, attrs, "", "", False) def reportFuncDeviceProtocol(self, login, remote, pwhash, success, device_id, protocol): return self.reportFuncAttrsInternal(login, remote, pwhash, success, {}, device_id, protocol, False) def reportFuncAttrsReplica(self, login, remote, pwhash, success, attrs): return self.reportFuncAttrsInternal(login, remote, pwhash, success, attrs, "", "", True) def reportFuncAttrsInternal(self, login, remote, pwhash, success, attrs, device_id, protocol, replica): payload = dict() payload['login'] = login payload['remote'] = remote payload['pwhash'] = pwhash payload['success'] = success payload['attrs'] = attrs payload['device_id'] = device_id payload['protocol'] = protocol if not replica: return self.session.post( self.url("/?command=report"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) else: return self.session.post( self.url2("/?command=report"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def taReportFuncAttrs(self, login, remote, pwhash, success, attrs): payload = dict() payload['login'] = login payload['remote'] = remote payload['pwhash'] = pwhash payload['success'] = success payload['attrs'] = attrs return self.session.post( self.ta_url("/?command=report"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def dockerImageReportFunc(self, login, remote, pwhash, success): payload = dict() payload['login'] = login payload['remote'] = remote payload['pwhash'] = pwhash payload['success'] = success return self.session.post( self.docker_image_url("/?command=report"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addSibling(self, host, port, protocol, key): payload = dict() payload['sibling_host'] = host payload['sibling_port'] = port payload['sibling_proto'] = protocol payload['encryption_key'] = key return self.session.post( self.url("/?command=addSibling"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def removeSibling(self, host, port): payload = dict() payload['sibling_host'] = host payload['sibling_port'] = port return self.session.post( self.url("/?command=removeSibling"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def setSiblings(self, payload): return self.session.post( self.url("/?command=setSiblings"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def resetFunc(self, login, ip): return self.resetFuncInternal(login, ip, False) def resetFuncReplica(self, login, ip): return self.resetFuncInternal(login, ip, True) def resetFuncInternal(self, login, ip, replica): payload = dict() payload['login'] = login payload['ip'] = ip if not replica: return self.session.post( self.url("/?command=reset"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) else: return self.session.post( self.url2("/?command=reset"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def incLogins(self, login): attrs = dict() attrs['login'] = login payload = dict() payload['attrs'] = attrs return self.session.post( self.url("/?command=incLogins"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def countLogins(self, login): return self.countLoginsInternal(login, False) def countLoginsReplica(self, login): return self.countLoginsInternal(login, True) def countLoginsReplica2(self, login): return self.countLoginsInternal(login, True, True) def countLoginsInternal(self, login, replica, replica2=False): attrs = dict() attrs['login'] = login payload = dict() payload['attrs'] = attrs if not replica: return self.session.post( self.url("/?command=countLogins"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) elif replica and not replica2: return self.session.post( self.url2("/?command=countLogins"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) else: return self.session.post( self.url4("/?command=countLogins"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def resetLogins(self, login): attrs = dict() attrs['login'] = login payload = dict() payload['attrs'] = attrs return self.session.post( self.url("/?command=resetLogins"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def customFunc(self, login): attrs = dict() attrs['login'] = login payload = dict() payload['attrs'] = attrs return self.session.post( self.url("/?command=custom"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def customGetFunc(self, func): return self.session.get( self.url("/?command=" + func)) def customFuncWithName(self, custom_func_name, attrs): payload = dict() payload['attrs'] = attrs return self.session.post( self.url("/?command=" + custom_func_name), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def customFuncReplicaWithName(self, custom_func_name, attrs): payload = dict() payload['attrs'] = attrs return self.session.post( self.url2("/?command=" + custom_func_name), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def trackalertCustomFunc(self, login): attrs = dict() attrs['login'] = login payload = dict() payload['attrs'] = attrs return self.session.post( self.ta_url("/?command=custom"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def pingFunc(self): return self.session.get(self.url("/?command=ping")) def getBLFunc(self): return self.session.get(self.url("/?command=getBL")) def getWLFunc(self): return self.session.get(self.url("/?command=getWL")) def getBLFuncReplica(self): return self.session.get(self.url2("/?command=getBL")) def getWLFuncReplica(self): return self.session.get(self.url2("/?command=getWL")) def getBLFuncPersist(self): return self.session.get(self.url3("/?command=getBL")) def getWLFuncPersist(self): return self.session.get(self.url3("/?command=getWL")) def addBLEntryIPLogin(self, ip, login, expire_secs, reason): payload = dict() payload['login'] = login payload['ip'] = ip payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addBLEntryIP(self, ip, expire_secs, reason): payload = dict() payload['ip'] = ip payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addBLEntryNetmask(self, netmask, expire_secs, reason): payload = dict() payload['netmask'] = netmask payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addBLEntryIPPersist(self, ip, expire_secs, reason): payload = dict() payload['ip'] = ip payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url3("/?command=addBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addBLEntryLogin(self, login, expire_secs, reason): payload = dict() payload['login'] = login payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def delBLEntryIPLogin(self, ip, login): payload = dict() payload['login'] = login payload['ip'] = ip return self.session.post( self.url("/?command=delBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def delBLEntryIP(self, ip): payload = dict() payload['ip'] = ip return self.session.post( self.url("/?command=delBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def delBLEntryLogin(self, login): payload = dict() payload['login'] = login return self.session.post( self.url("/?command=delBLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addWLEntryIPLogin(self, ip, login, expire_secs, reason): payload = dict() payload['login'] = login payload['ip'] = ip payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addWLEntryIP(self, ip, expire_secs, reason): payload = dict() payload['ip'] = ip payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addWLEntryNetmask(self, netmask, expire_secs, reason): payload = dict() payload['netmask'] = netmask payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addWLEntryIPPersist(self, ip, expire_secs, reason): payload = dict() payload['ip'] = ip payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url3("/?command=addWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def addWLEntryLogin(self, login, expire_secs, reason): payload = dict() payload['login'] = login payload['expire_secs'] = expire_secs payload['reason'] = reason return self.session.post( self.url("/?command=addWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def delWLEntryIPLogin(self, ip, login): payload = dict() payload['login'] = login payload['ip'] = ip return self.session.post( self.url("/?command=delWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def delWLEntryIP(self, ip): payload = dict() payload['ip'] = ip return self.session.post( self.url("/?command=delWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def delWLEntryLogin(self, login): payload = dict() payload['login'] = login return self.session.post( self.url("/?command=delWLEntry"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def getDBStatsIPLogin(self, ip, login): payload = dict() payload['login'] = login payload['ip'] = ip return self.session.post( self.url("/?command=getDBStats"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def getDBStatsIP(self, ip): payload = dict() payload['ip'] = ip return self.session.post( self.url("/?command=getDBStats"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def getDBStatsLogin(self, login): payload = dict() payload['login'] = login return self.session.post( self.url("/?command=getDBStats"), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) def kafkaProducer(self): payload = {"records": [{"value": {"foo": "bar"}}]} return self.session.post( "http://kafka-rest:8082/topics/wforce", data=json.dumps(payload), headers={'Content-Type': 'application/vnd.kafka.json.v2+json'}) def getWforceMetrics(self): return self.session.get( self.url("/metrics")) def getTrackalertMetrics(self): return self.session.get( self.ta_url("/metrics")) def reportAPI(self, path, attrs): return self.session.post( self.report_url(path), data=json.dumps(attrs), headers={'Content-Type': 'application/json'}, auth=('foo', 'secret')) def url(self, relative_url): return urljoin(self.server1_url, relative_url) def url2(self, relative_url): return urljoin(self.server2_url, relative_url) def url3(self, relative_url): return urljoin(self.server3_url, relative_url) def url4(self, relative_url): return urljoin(self.server4_url, relative_url) def ta_url(self, relative_url): return urljoin(self.ta_server_url, relative_url) def docker_image_url(self, relative_url): return urljoin(self.docker_image_server_url, relative_url) def report_url(self, relative_url): return urljoin(self.report_server_url, relative_url) def assert_success_json(self, result): try: result.raise_for_status() except: print(result.content) raise self.assertEqual(result.headers['Content-Type'], 'application/json') weakforced-2.10.2/regression-tests/trackalert.conf000066400000000000000000000027251461473602600222500ustar00rootroot00000000000000webserver("0.0.0.0:8090", "super") setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") controlSocket("127.0.0.1:4010") addACL("127.0.0.0/8") addACL("192.168.0.0/16") local f = assert(io.open("/tmp/trackalert.log", "w")) f:write("trackalert debug log initialized\n") f:close() function report(lt) local f = assert(io.open("/tmp/trackalert.log", "a")) f:write(string.format("Received report for login=%s and remote=%s\n", lt.login, lt.remote:tostring())) f:close() end setReport(report) function oneMinuteBackground1() local f = assert(io.open("/tmp/trackalert.log", "a")) f:write(string.format("Ran one minute background1 function\n")) f:close() end setBackground("oneminutebackground1", oneMinuteBackground1) cronScheduleBackgroundFunc("* * * * *", "oneminutebackground1") function oneMinuteBackground2() local f = assert(io.open("/tmp/trackalert.log", "a")) f:write(string.format("Ran one minute background2 function\n")) f:close() end setBackground("oneminutebackground2", oneMinuteBackground2) cronScheduleBackgroundFunc("* * * * *", "oneminutebackground2") function custom(args) login_name = "unknown" for k,v in pairs(args.attrs) do if (k == "login") then login_name = v end infoLog("custom func argument attrs", { key=k, value=v }); end -- return consists of a boolean, followed by { key-value pairs } return true, { login=login_name } end -- Register a custom endpoint setCustomEndpoint("custom", custom) weakforced-2.10.2/regression-tests/udp_sink.py000066400000000000000000000010151461473602600214220ustar00rootroot00000000000000import socket import os UDP_IP = "127.0.0.1" UDP_PORT = 4502 sock = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP sock.bind((UDP_IP, UDP_PORT)) logfile = open('/tmp/udp-sink.log', 'w') logfile.write("This is to ensure the file isn't empty\n") logfile.flush() mypid = os.getpid() while True: data, addr = sock.recvfrom(2048) # buffer size is 2048 bytes log_msg = "[%s] Received report=%s\n" % (mypid, data) logfile.write(log_msg) logfile.flush() print(log_msg) weakforced-2.10.2/regression-tests/webhook_server.py000077500000000000000000000020661461473602600226440ustar00rootroot00000000000000from bottle import route, run, template, request, response import hmac import hashlib import base64 import json import os logfile = open('/tmp/webhook-server.log', 'w') logfile.write("This is to ensure the file isn't empty\n") logfile.flush() mypid = os.getpid() @route('/webhook/', method='POST') def webhook(event): digest_match = False event = request.headers.get('X-Wforce-Event') secret = request.headers.get('X-Wforce-Signature') delivery_id = request.headers.get('X-Wforce-Delivery') hook_id = request.headers.get('X-Wforce-HookID') body_json = request.json shmac = hmac.new(b"secret", b"", hashlib.sha256) for myline in request.body.readlines(): shmac.update(myline) sdigest = base64.b64encode(shmac.digest()) if sdigest.decode() == secret: digest_match = True log_msg = "[%s] Received webhook id=%s, digest_match=%s, event=%s, body=%s\n" % (mypid, hook_id, digest_match, event, json.dumps(body_json)) logfile.write(log_msg) logfile.flush() return "ok\n" run(host='localhost', port=9080) weakforced-2.10.2/regression-tests/wforce-tw.conf000066400000000000000000000217151461473602600220310ustar00rootroot00000000000000setHLLBits(6) setCountMinBits(0.05, 0.2) field_map = {} field_map["countLogins"] = "int" field_map["failedLogins"] = "int" field_map["subTest"] = "int" field_map["diffPasswords"] = "hll" field_map["diffIPs"] = "hll" field_map["countryCount"] = "countmin" newShardedStringStatsDB("15SecondsFirstDB",1,15,field_map, 10) sdb = getStringStatsDB("15SecondsFirstDB") sdb:twEnableReplication() sdb:twSetExpireSleep(250) newShardedStringStatsDB("15SecondsSecondDB",1,15,field_map, 10) sdb_prefix = getStringStatsDB("15SecondsSecondDB") sdb_prefix:twSetv4Prefix(24) sdb_prefix:twSetv6Prefix(64) sdb_prefix:twEnableReplication() sdb_prefix:twSetExpireSleep(250) newShardedStringStatsDB("15SecondsSmallDB",1,15,field_map, 10) sdb_small = getStringStatsDB("15SecondsSmallDB") sdb_small:twSetMaxSize(10) sdb_small:twEnableReplication() addCustomStat("customStat") ck={} ck["url"] = "http://localhost:9080/webhook/regression" ck["secret"] = "secret" events = { "report", "allow", "reset", "addbl", "delbl", "expirebl" } mtls_ck={} mtls_ck["url"] = "https://localhost:9081" tack={} tack["url"] = "http://localhost:8090/?command=report" tack["secret"] = "secret" tack["basic-auth"] = "wforce:super" ta_events = { "report" } ls_ck = {} ls_ck["url"] = "http://logstash:8080" ls_ck["secret"] = "verysecretcode" kafka_ck = {} kafka_ck["url"] = "http://kafka-rest:8082/topics/wforce" kafka_ck["kafka"] = "true" kafka_events = { "report" } function twreport(lt) local tls = lt.tls local session_id = lt.session_id sdb = getStringStatsDB("15SecondsFirstDB") sdb_prefix = getStringStatsDB("15SecondsSecondDB") sdb_small = getStringStatsDB("15SecondsSmallDB") local countrydb = getGeoIP2DB("Country") cur_ct = countrydb:lookupCountry(lt.remote) sdb:twAdd(lt.login, "countLogins", 1) if (not lt.success) then sdb:twAdd(lt.remote, "failedLogins", 1) end sdb:twSub(lt.login, "subTest", 1) sdb:twAdd(lt.login, "diffPasswords", lt.pwhash) sdb:twAdd(lt.login, "diffIPs", lt.remote) sdb_prefix:twAdd(lt.remote, "countLogins", 1) if (string.find(lt.login, "expirebaddie")) then sdb_small:twAdd(lt.login, "countLogins", 1) end end function twallow(lt) local tls = lt.tls sdb = getStringStatsDB("15SecondsFirstDB") sdb_prefix = getStringStatsDB("15SecondsSecondDB") sdb_small = getStringStatsDB("15SecondsSmallDB") -- check the getBLRetMsg functions work getBlacklistIPRetMsg() getBlacklistLoginRetMsg() getBlacklistIPLoginRetMsg() for k, v in pairs(lt.attrs) do if ((k == "accountStatus") and (v == "blocked")) then return -1, "accountStatus blocked", "accountStatus blocked", {} end end for k, v in pairs(lt.attrs_mv) do for i, vi in ipairs(v) do if ((k == "countryList") and (vi == "Blockmestan")) then return -1, "blocked country", "blocked country", {} end end end local countrydb = getGeoIP2DB("Country") cur_ct = countrydb:lookupCountry(lt.remote) if (cur_ct == "JP") then return -1, "Japan is blocked", "Japan is blocked", {} end if (sdb:twGet(lt.login, "diffPasswords") > 20) then return -1, "diffPasswords for login", "diffPasswords for login", {} end if (sdb:twGet(lt.remote, "failedLogins") > 30) then return -1, "failed logins for IP", "failed logins for IP", {} end if (sdb:twGet(lt.login, "diffIPs") > 10) then return -1, "different IPs for login", "different IPs for login", {} end if (sdb:twGet(lt.login, "subTest") < -40) then return -1, "subTest", "subTest", {} end if (sdb_prefix:twGet(lt.remote, "countLogins") > 45) then return -1, "Too many logins (prefix)", "Too many logins (prefix)", {} end if (sdb_small:twGetSize() > 10) then return -1, "Small DB Size too big", "Small DB Size too big", {} end return 0, "", "", { os_family=lt.device_attrs["os.family"] } end function reset(type, login, ip) sdb = getStringStatsDB("15SecondsFirstDB") sdb_prefix = getStringStatsDB("15SecondsSecondDB") sdb_small = getStringStatsDB("15SecondsSmallDB") if (string.find(type, "ip")) then sdb:twReset(ip) sdb_prefix:twReset(ip) sdb_small:twReset(ip) end if (string.find(type, "login")) then sdb:twReset(login) sdb_prefix:twReset(login) sdb_small:twReset(login) end if (string.find(type, "ip") and string.find(type, "login")) then end return true end function canonicalize(login) newlogin = login .. "@foobar.com" infoLog("canonicalize", { login_str=newlogin }) return newlogin end setAllow(twallow) setReport(twreport) setReset(reset) setCanonicalize(canonicalize) function custom(args) local login_name = "unknown" for k,v in pairs(args.attrs) do if (k == "login") then login_name = v end infoLog("custom func argument attrs", { key=k, value=v }); end -- this will fail until regression tests add a custom webhook runCustomWebHook("customwebhook", "{ \"loginname\":\"bar\"}") -- return consists of a boolean, followed by { key-value pairs } incCustomStat("customStat") return true, { login=login_name } end -- Register a custom endpoint setCustomEndpoint("custom", true, custom) function EightBitKey(args) local key = "\000\001\002\003" local stats_db = getStringStatsDB("15SecondsFirstDB") stats_db:twAdd(key, "countLogins", 1) local count = stats_db:twGet(key, "countLogins") return true, { count=count } end setCustomEndpoint("EightBitKey", true, EightBitKey) function testCustomGetFunc() blacklistIP(newCA("1.2.3.4"), 30, "Why not?") local ipbl = getIPBlacklist() local ret_table = {} for i,j in pairs(ipbl) do for k,v in pairs(j) do if k == "ip" then table.insert(ret_table, v) end end end local s = table.concat(ret_table, "\n") .. "\n" unblacklistIP(newCA("1.2.3.4")) return s end setCustomGetEndpoint("testCustomGet", testCustomGetFunc) newGeoIP2DB("City", "RegressionGeoIP.mmdb") newGeoIP2DB("Country", "RegressionGeoIP.mmdb") function geoip2(args) local ip_address for k,v in pairs(args.attrs) do if (k == "ip") then ip_address = v end infoLog("custom func argument attrs", { key=k, value=v }); end local citydb = getGeoIP2DB("City") local geodata = citydb:lookupCity(newCA(ip_address)) return true, {city=geodata.city} end setCustomEndpoint("geoip2", false, geoip2) function geoip2_lookupValue(args) local ip_address for k,v in pairs(args.attrs) do if (k == "ip") then ip_address = v end infoLog("custom func argument attrs", { key=k, value=v }); end local citydb = getGeoIP2DB("City") local city_name = citydb:lookupStringValue(newCA(ip_address), {"city", "names", "en"}) local city_lat = citydb:lookupDoubleValue(newCA(ip_address), {"location", "latitude"}) local city_long = citydb:lookupDoubleValue(newCA(ip_address), {"location", "longitude"}) local accuracy = citydb:lookupUIntValue(newCA(ip_address), {"location", "accuracy_radius"}) local eu = citydb:lookupBoolValue(newCA(ip_address), {"country", "is_in_european_union"}) if eu == true then in_eu = 1 else in_eu = 0 end return true, {city=city_name, latitude=city_lat, longitude=city_long, accuracy=accuracy, is_in_eu=in_eu} end setCustomEndpoint("geoip2_lookupValue", false, geoip2_lookupValue) function incLogins(args) local sdb = getStringStatsDB("15SecondsFirstDB") login_name = "unknown" for k,v in pairs(args.attrs) do if (k == "login") then login_name = v end infoLog("incLogins argument attrs", { key=k, value=v }); end sdb:twAdd(login_name, "countLogins",1) local login_count = sdb:twGet(login_name, "countLogins") -- return consists of a boolean, followed by { key-value pairs } return true, { login=login_name, countLogins=login_count } end function countLogins(args) local sdb = getStringStatsDB("15SecondsFirstDB") login_name = "unknown" for k,v in pairs(args.attrs) do if (k == "login") then login_name = v end infoLog("countLogins argument attrs", { key=k, value=v }); end local login_count = sdb:twGet(login_name, "countLogins") -- return consists of a boolean, followed by { key-value pairs } return true, { login=login_name, countLogins=login_count } end function resetLogins(args) local sdb = getStringStatsDB("15SecondsFirstDB") login_name = "unknown" for k,v in pairs(args.attrs) do if (k == "login") then login_name = v end infoLog("resetLogins argument attrs", { key=k, value=v }); end sdb:twResetField(login_name, "countLogins") local login_count = sdb:twGet(login_name, "countLogins") -- return consists of a boolean, followed by { key-value pairs } return true, { login=login_name, countLogins=login_count } end setCustomEndpoint("incLogins", false, incLogins) setCustomEndpoint("countLogins", false, countLogins) setCustomEndpoint("resetLogins", false, resetLogins) weakforced-2.10.2/regression-tests/wforce1.conf000066400000000000000000000011371461473602600214560ustar00rootroot00000000000000addListener("0.0.0.0:8084", false, "", "", {}) setWebserverPassword("super") setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") controlSocket("0.0.0.0:4104") addListener("0.0.0.0:8184", true, "certs/server.crt", "certs/server.key", {}) setCurlClientCertAndKey("certs/client.crt", "certs/client.key") setCurlCABundleFile("certs/root.pem") addSibling("127.0.0.1:4001"); addSiblingWithKey("127.0.0.1:4002", "KaiQkCHloe2ysXv2HbxBAFqHI4N8+ahmwYwsbYlDdF0="); addSiblingWithKey("127.0.0.1:4004:tcp", "lykfkV/07VPMK80nLNOTWtlMsLz9y7X0r6t9zcFNTmE="); siblingListener("0.0.0.0:4001") dofile("./wforce-tw.conf")weakforced-2.10.2/regression-tests/wforce2.conf000066400000000000000000000010061461473602600214520ustar00rootroot00000000000000addListener("0.0.0.0:8085", false, "", "", {}) setWebserverPassword("super") setKey("KaiQkCHloe2ysXv2HbxBAFqHI4N8+ahmwYwsbYlDdF0=") controlSocket("0.0.0.0:4105") addListener("0.0.0.0:8185", true, "certs/server.crt", "certs/server.key", {}) setCurlClientCertAndKey("certs/client.crt", "certs/client.key") setCurlCABundleFile("certs/root.pem") addSiblingWithKey("127.0.0.1:4001", "Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE="); addSibling("127.0.0.1:4002"); siblingListener("0.0.0.0:4002") dofile("./wforce-tw.conf")weakforced-2.10.2/regression-tests/wforce3.conf000066400000000000000000000017111461473602600214560ustar00rootroot00000000000000addListener("0.0.0.0:8086", false, "", "", {}) setWebserverPassword("super") setKey("MzfImn71m3bYrlPApe+PR7tY/RXoZ4IdpWvUc4o5OnQ=") controlSocket("0.0.0.0:4106") addListener("0.0.0.0:8186", true, "certs/server.crt", "certs/server.key", {}) setCurlClientCertAndKey("certs/client.crt", "certs/client.key") setCurlCABundleFile("certs/root.pem") blacklistPersistDB("redis", 6379) blacklistRedisUsername("neil") blacklistRedisPassword("barfoo") blacklistPersistRWTimeout(0, 50000) whitelistPersistDB("redis", 6379) whitelistPersistRWTimeout(0, 50000) whitelistRedisUsername("neil") whitelistRedisPassword("barfoo") setSiblingsWithKey({{"127.0.0.1:4001", "Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE="}}) addSiblingWithKey("127.0.0.1:4004:tcp", "lykfkV/07VPMK80nLNOTWtlMsLz9y7X0r6t9zcFNTmE="); siblingListener("0.0.0.0:4003") addSyncHost("https://localhost:8184", "super", "127.0.0.1:4003", "https://localhost:8186") setMinSyncHostUptime(10) dofile("./wforce-tw.conf") weakforced-2.10.2/regression-tests/wforce4.conf000066400000000000000000000012531461473602600214600ustar00rootroot00000000000000addListener("0.0.0.0:8087", false, "", "", {}) setWebserverPassword("super") setKey("lykfkV/07VPMK80nLNOTWtlMsLz9y7X0r6t9zcFNTmE=") controlSocket("0.0.0.0:4107") addListener("0.0.0.0:8187", true, "certs/server.crt", "certs/server.key", {}) setCurlClientCertAndKey("certs/client.crt", "certs/client.key") setCurlCABundleFile("certs/root.pem") blacklistPersistDB("redis", 6379) blacklistRedisPassword("barfoo") setSiblingsWithKey({{"127.0.0.1:4001", "Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE="}, {"127.0.0.1:4002", "KaiQkCHloe2ysXv2HbxBAFqHI4N8+ahmwYwsbYlDdF0="}}); addSibling("127.0.0.1:4004:tcp"); siblingListener("0.0.0.0:4004") dofile("./wforce-tw.conf") weakforced-2.10.2/set-version000077500000000000000000000016331461473602600161260ustar00rootroot00000000000000#!/usr/bin/env bash VERSION=$1 [ -z "$VERSION" ] && exit 1 OS=`uname` SED_ARG='-r' if [ $OS == "Darwin" ] then SED_ARG='-E' fi cd docs/swagger && ./set-version $VERSION && cd ../.. if [ $? == 0 ] then sed $SED_ARG "s/AC_INIT\(\[wforce\],(.*)/AC_INIT([wforce], [$VERSION])/" configure.ac >configure.tmp if [ $? == 0 ] then mv configure.tmp configure.ac read -r -p "Do you want to tag this release in git? [y/N] " response if [[ $response =~ ^([yY])$ ]] then SIGN="" read -r -p "Do you want to sign the tag? [y/N] " response if [[ $response =~ ^([yY])$ ]] then SIGN="--sign" fi git add -u git commit -m "Set version $VERSION and tagged as v$VERSION" git tag -a v$VERSION -m "v$VERSION" $SIGN echo "Tagged this commit as v$VERSION" fi fi fi weakforced-2.10.2/trackalert/000077500000000000000000000000001461473602600160535ustar00rootroot00000000000000weakforced-2.10.2/trackalert/.gitignore000066400000000000000000000012561461473602600200470ustar00rootroot00000000000000.deps .dirstamp .history /.tags* /aclocal.m4 /autom4te.cache /compile /config.guess /config.h /config.h.in /config.log /config.status /config.sub /configure /depcomp /install-sh /libtool /ltmain.sh Makefile Makefile.in /missing /stamp-h1 /trackalert /*.service /regexes.yaml *.libs/ *.DSYM/ *~ libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4 # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # Compressed files *.gz *.bz2 # Other .DS_Store # Protobuf files *.pb.h *.pb.cc weakforced-2.10.2/trackalert/Makefile.am000066400000000000000000000034641461473602600201160ustar00rootroot00000000000000AM_CPPFLAGS = $(LUA_CFLAGS) \ $(EXT_CFLAGS) \ $(WFORCE_CFLAGS) \ $(libsodium_CFLAGS) \ $(JSON11_CFLAGS) \ $(LIBCURL_CFLAGS) \ $(LIBCRYPTO_INCLUDES) \ $(LIBDROGON_INCLUDES) \ $(LIBJSONCPP_INCLUDES) \ $(LIBZ_INCLUDES) \ $(LIBUUID_INCLUDES) \ $(LIBPROMETHEUS_INCLUDES) \ $(YAMLCPP_FLAGS) \ $(MMDB_CFLAGS) \ -DSYSCONFDIR='"$(sysconfdir)"' \ -I$(top_srcdir)/wforce AM_LDFLAGS = \ $(PROGRAM_LDFLAGS) \ $(THREADFLAGS) \ $(SANITIZER_FLAGS) EXTRA_DIST= trackalert.conf README.md trackalert.service.in CLEANFILES = trackalert.service distclean-local: -rm -rf *.dSYM sysconf_DATA= trackalert.conf bin_PROGRAMS = trackalert trackalert_SOURCES = \ trackalert.cc trackalert.hh \ trackalert-lua.cc \ wforce-common-lua.cc wforce-common-lua.hh \ trackalert-web.cc trackalert-web.hh\ trackalert-luastate.hh trackalert_LDFLAGS = \ $(AM_LDFLAGS) \ $(LIBPROMETHEUS_LDFLAGS) \ $(LIBDROGON_LDFLAGS) \ $(LIBJSONCPP_LDFLAGS) \ $(LIBCRYPTO_LDFLAGS) \ $(LIBZ_LDFLAGS) \ $(LIBUUID_LDFLAGS) trackalert_LDADD = \ -lreadline \ $(LUA_LIBS) $(EXT_LIBS) $(WFORCE_LIBS) \ $(libsodium_LIBS) \ $(LIBSYSTEMD_LIBS) $(JSON11_LIBS) $(BOOST_DATE_TIME_LIBS) \ $(BOOST_REGEX_LIBS) $(LIBCURL) $(LIBCRYPTO_LIBS) \ $(LIBDROGON_LIBS) $(LIBJSONCPP_LIBS) $(LIBZ_LIBS) $(LIBUUID_LIBS) $(LIBSSL_LIBS) \ $(YAMLCPP_LIBS) $(GEOIP_LIBS) $(MMDB_LIBS) $(LIBPROMETHEUS_LIBS) $(DLLIBS) $(STDCXXFSLIBS) EXTRA_trackalert_DEPENDENCIES = \ $(EXT_LIBS) $(WFORCE_LIBS) noinst_HEADERS = \ trackalert.hh \ trackalert-luastate.hh \ trackalert-web.hh if HAVE_SYSTEMD SYSTEMD_TYPE = notify systemdsystemunitdir = $(SYSTEMD_DIR) systemdsystemunit_DATA = trackalert.service else SYSTEMD_TYPE = simple endif trackalert.service: trackalert.service.in $(AM_V_GEN)sed -e 's![@]bindir[@]!$(bindir)!' -e 's![@]type[@]!$(SYSTEMD_TYPE)!' < $< > $@ weakforced-2.10.2/trackalert/README.md000066400000000000000000000074001461473602600173330ustar00rootroot00000000000000Trackalert ------ Trackalert is designed to be an optional service to complement wforce. Whereas wforce provides a toolkit to combat abuse of logins such as password brute forcing in realtime, trackalert is designed to look at abuse asynchronously, using long-term report data stored in an external DB such as elasticsearch, and to send alerts on potential login abuse. Rationale ------ Trackalert is much simpler than wforce - the REST API provides access to a single Lua hook "report", which is designed to ingest report data, typically received from wforce via a webhook. Various functions from wforce are available to the report function such as GeoIP lookups and the ability to send custom Webhooks, however the report function is mainly designed to allow the current login data received in the report to be compared to aggregated data found in a DB such as elasticsearch. The [elasticqueries directory](elasticqueries) contains sample elasticsearch queries that could be used to achieve this. Because trackalert is decoupled from the login flow, the action that can be taken if a report is deemed to be suspicious is not "block", rather it is intended that the report function is used to send alerts to users via email or other methods, to alert them about potentially suspicious logins. It might be asked, "Why not implement this in wforce?", however if you consider that wforce is designed to run as fast as possible, without delaying logins, combined with the fact that querying long-term datasets can be quite slow, it makes sense to create a separate daemon to enable this behaviour. Additionally trackalert allows for scheduled "background" functions to be run periodically. Unlike the report function, which is intended to be used to find out if a particular login was suspicious, the background functions are intended to be used to find "whole system" abuse. For example, by searching the login-term report DB to discover IPs which are consistently generating unsuccessful login attempts, or to discover users whose accounts are being attacker or are maybe already compromised. Again the action here may be to send an email or a webhook, however this time the target would be an administrator or operations team. Theoretically trackalert background functions could call the wforce API and blacklist individual IPs or users, thus leading to realtime blocking. In summary, trackalert has very different goals to wforce, and attempts to provide a simple framework to fulfil them: * The ability to create Lua policy that responds to individual login reports and takes appropriate action. It is suggested that policies query a long-term DB such as elasticsearch for previously stored reports, and then alert based on comparing the long-term data with the current login information. Currently no default policy is provided to support that behaviour. * The ability to run scheduled (background) Lua functions that can be used to creates policies such as looking for long-term abuse trends of IP addresses and users, and alert based on that data. Again, no default policy is provided to implement that behaviour. Console ----- The console is very similar to wforce. Read about it in the [trackalert documentation](../docs/manpages/trackalert.1.md), or "man trackalert". REST API --- The REST API is very similar to wforce, although it consists of just two commands: "report" (using POST) and "stats" (using GET). Read more using the [trackalert swagger documentation](../docs/swagger/trackalert_api.7.yml). Configuration ---------- Configuration is very similar to wforce, although there are less configuration options and less functions available via Lua. Read about configuration in the [trackalert configuration documentation](../docs/manpages/trackalert.conf.5.md) or "man trackalert.conf". weakforced-2.10.2/trackalert/trackalert-lua.cc000066400000000000000000000251471461473602600213060ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include "trackalert.hh" #include "wforce-common-lua.hh" #include #include "dolog.hh" #include "sodcrypto.hh" #include "base64.hh" #include "trackalert-luastate.hh" #include "perf-stats.hh" #include "wforce-webserver.hh" #include "trackalert-web.hh" #include #include #include #include "common-lua.hh" #include "prometheus.hh" using std::thread; static vector>* launchWork; // lua functions are split into three groups: // 1) Those which are only applicable as "config/setup" (single global lua state) (they are defined as blank/empty functions otherwise) // 2) Those which are only applicable inside the "report" and "background" functions (multiple lua states running in different threads), which are defined as blank/empty otherwise // 3) Those which are applicable to both states // Functions that are in 2) or 3) MUST be thread-safe. The rest not as they are called at startup. // We have a single lua config file for historical reasons, hence the somewhat complex structure of this function // The Lua state and type is passed via "multi_lua" (true means it's one of the multiple states, false means it's the global lua config state) vector> setupLua(bool client, bool multi_lua, LuaContext& c_lua, report_t& report_func, bg_func_map_t* bg_func_map, CustomFuncMap& custom_func_map, const std::string& config) { launchWork= new vector>(); setupCommonLua(client, multi_lua, c_lua, config); setupWforceCommonLua(client, multi_lua, c_lua, launchWork); if (!multi_lua) { c_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password) { if (client) return; warnlog("Warning - webserver() configuration command is deprecated in favour of addListener() and will be removed in a future version"); ComboAddress local; try { local = ComboAddress(address); } catch (const WforceException& e) { errlog("webserver() error parsing address/port [%s]. Make sure to use IP addresses not hostnames", address); return; } g_webserver.setBasicAuthPassword(password); g_webserver.addSimpleListener(local.toString(), local.getPort()); auto launch =[]() { thread t(WforceWebserver::start, &g_webserver); t.detach(); }; if (launchWork) launchWork->push_back(launch); else launch(); }); } else { c_lua.writeFunction("webserver", [](const std::string& address, const std::string& password) { }); } if (!multi_lua) { c_lua.writeFunction("setWebserverPassword", [](const std::string& password) { g_webserver.setBasicAuthPassword(password); }); } else { c_lua.writeFunction("setWebserverPassword", [](const std::string& password) { }); } if (!multi_lua) { c_lua.writeFunction("stats", []() { boost::format fmt("%d reports\n"); g_outputBuffer = (fmt % g_stats.reports).str(); }); } else { c_lua.writeFunction("stats", []() { }); } if (!multi_lua) { c_lua.writeFunction("setNumReportThreads", [](int numThreads) { // XXX this function is deprecated - use setNumWorkerThreads instead infolog("setNumReportThreads is deprecated - use setNumWorkerThreads() instead"); }); } else { c_lua.writeFunction("setNumReportThreads", [](int numThreads) { }); } if (multi_lua) { c_lua.writeFunction("setReport", [&report_func](report_t func) { report_func=func;}); } else { c_lua.writeFunction("setReport", [](report_t func) { }); } if (multi_lua) { c_lua.writeFunction("setBackground", [bg_func_map](const std::string& func_name, background_t func) { bg_func_map->insert(std::make_pair(func_name, func)); }); } else { c_lua.writeFunction("setBackground", [](const std::string& func_name, background_t func) { }); } if (!multi_lua) { c_lua.writeFunction("setNumSchedulerThreads", [](int numThreads) { g_bg_schedulerp->setNumThreads(numThreads); }); } else { c_lua.writeFunction("setNumSchedulerThreads", [](int numThreads) { }); } if (!multi_lua && !client) { c_lua.writeFunction("cronScheduleBackgroundFunc", [](const std::string& cron_str, const std::string& func_name) { g_bg_schedulerp->cron(cron_str, [func_name] { try { g_luamultip->background(func_name); } catch(LuaContext::ExecutionErrorException& e) { try { std::rethrow_if_nested(e); errlog("Lua background function [%s] exception: %s", func_name, e.what()); } catch (const std::exception& ne) { errlog("Exception in background function [%s] exception: %s", func_name, ne.what()); } catch (const WforceException& ne) { errlog("Exception in background function [%s] exception: %s", func_name, ne.reason); } } }); }); } else { c_lua.writeFunction("cronScheduleBackgroundFunc", [](const std::string& cron_str, const std::string& func_name) { }); } if (!multi_lua && !client) { c_lua.writeFunction("intervalScheduleBackgroundFunc", [](const std::string& duration_str, const std::string& func_name) { std::stringstream ss(duration_str); boost::posix_time::time_duration td; if (ss >> td) { g_bg_schedulerp->interval(std::chrono::seconds(td.total_seconds()), [func_name] { try { g_luamultip->background(func_name); } catch(LuaContext::ExecutionErrorException& e) { try { std::rethrow_if_nested(e); errlog("Lua background function [%s] exception: %s", func_name, e.what()); } catch (const std::exception& ne) { errlog("Exception in background function [%s] exception: %s", func_name, ne.what()); } catch (const WforceException& ne) { errlog("Exception in background function [%s] exception: %s", func_name, ne.reason); } } }); } else { std::string errmsg = "Cannot parse duration string: " + duration_str; errlog(errmsg.c_str()); throw WforceException(errmsg); } }); } else { c_lua.writeFunction("intervalScheduleBackgroundFunc", [](const std::string& cron_str, const std::string& func_name) { }); } c_lua.writeFunction("durationToSeconds", [](const std::string& duration_str) { std::stringstream ss(duration_str); boost::posix_time::time_duration td; if (ss >> td) { return (long)td.total_seconds(); } else { errlog("Cannot parse duration string: %s",duration_str); return (long)0; } }); c_lua.registerMember("attrs", &CustomFuncArgs::attrs); c_lua.registerMember("attrs_mv", &CustomFuncArgs::attrs_mv); c_lua.writeFunction("setCustomEndpoint", [&custom_func_map, multi_lua, client](const std::string& f_name, custom_func_t func) { CustomFuncMapObject cobj; cobj.c_func = func; custom_func_map.insert(std::make_pair(f_name, cobj)); if (!multi_lua && !client) { addCommandStat(f_name); addPrometheusCommandMetric(f_name); // register a webserver command g_webserver.registerFunc(f_name, HTTPVerb::POST, WforceWSFunc(parseCustomCmd)); noticelog("Registering custom endpoint [%s]", f_name); } }); if (!multi_lua) { c_lua.writeFunction("showCustomEndpoints", []() { boost::format fmt("%-30.30s \n"); g_outputBuffer = (fmt % "Custom Endpoint").str(); for (const auto& i : g_custom_func_map) { g_outputBuffer += (fmt % i.first).str(); } }); } else { c_lua.writeFunction("showCustomEndpoints", []() { }); } if (!multi_lua) { c_lua.writeFunction("showVersion", []() { g_outputBuffer = "trackalert " + std::string(VERSION) + "\n"; }); } else { c_lua.writeFunction("showVersion", []() { }); } if (!multi_lua) { c_lua.writeFunction("setKey", [](const std::string& key) -> bool { string newkey; if(B64Decode(key, newkey) < 0) { g_outputBuffer=string("Unable to decode ")+key+" as Base64"; errlog("%s", g_outputBuffer); return false; } else { g_key = newkey; return true; } }); } else { c_lua.writeFunction("setKey", [](const std::string& key) { }); } if (!multi_lua) { c_lua.writeFunction("testCrypto", [](string testmsg) { try { SodiumNonce sn, sn2; sn.init(); sn2=sn; string encrypted = sodEncryptSym(testmsg, g_key, sn); string decrypted = sodDecryptSym(encrypted, g_key, sn2); sn.increment(); sn2.increment(); encrypted = sodEncryptSym(testmsg, g_key, sn); decrypted = sodDecryptSym(encrypted, g_key, sn2); if(testmsg == decrypted) g_outputBuffer="Everything is ok!\n"; else g_outputBuffer="Crypto failed..\n"; } catch(...) { g_outputBuffer="Crypto failed..\n"; }}); } else { c_lua.writeFunction("testCrypto", [](string testmsg) {}); } std::ifstream ifs(config); if(!ifs) warnlog("Unable to read configuration from '%s'", config); else if (!multi_lua) infolog("Read configuration from '%s'", config); c_lua.executeCode(ifs); if (!multi_lua) { auto ret=*launchWork; delete launchWork; launchWork=0; return ret; } else { return vector>(); } } weakforced-2.10.2/trackalert/trackalert-luastate.hh000066400000000000000000000117111461473602600223510ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/luawrapper/include/LuaContext.hpp" #include "misc.hh" #include #include #include "json11.hpp" #include "login_tuple.hh" #include "customfunc.hh" struct CustomFuncMapObject { custom_func_t c_func; }; typedef std::map CustomFuncMap; extern CustomFuncMap g_custom_func_map; typedef std::function report_t; extern report_t g_report; typedef std::function background_t; extern background_t g_background; typedef std::unordered_map bg_func_map_t; vector> setupLua(bool client, bool allow_report, LuaContext& c_lua, report_t& report_func, bg_func_map_t* bg_func_map, CustomFuncMap& custom_func_map, const std::string& config); struct LuaThreadContext { LuaContext lua_context; std::mutex lua_mutex; report_t report_func; bg_func_map_t bg_func_map; CustomFuncMap custom_func_map; }; #define NUM_LUA_STATES 6 class LuaMultiThread { public: LuaMultiThread() : num_states(NUM_LUA_STATES) { LuaMultiThread{num_states}; } LuaMultiThread(unsigned int nstates) : num_states(nstates) { for (unsigned int i = 0; i < num_states; i++) { lua_pool.push_back(std::make_shared()); } lua_read_only = lua_pool; // Make a copy for use by the control thread } LuaMultiThread(const LuaMultiThread&) = delete; LuaMultiThread& operator=(const LuaMultiThread&) = delete; // these are used to setup the function pointers std::vector>::iterator begin() { return lua_read_only.begin(); } std::vector>::iterator end() { return lua_read_only.end(); } void report(const LoginTuple& lt) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the report function lt_context->report_func(lt); } void background(const std::string& func_name) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the background function auto fn = lt_context->bg_func_map.find(func_name); if (fn != lt_context->bg_func_map.end()) fn->second(); } CustomFuncReturn custom_func(const std::string& command, const CustomFuncArgs& cfa) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the custom function for (const auto& i: lt_context->custom_func_map) { if (command.compare(i.first) == 0) { return i.second.c_func(cfa); } } return CustomFuncReturn(false, KeyValVector{}); } protected: class SharedPoolMember { public: SharedPoolMember(std::shared_ptr ptr, LuaMultiThread* pool) : d_pool_item(ptr), d_pool(pool) {} ~SharedPoolMember() { if (d_pool != nullptr) { d_pool->returnPoolMember(d_pool_item); } } SharedPoolMember(const SharedPoolMember&) = delete; SharedPoolMember& operator=(const SharedPoolMember&) = delete; std::shared_ptr getLuaContext() { return d_pool_item; } private: std::shared_ptr d_pool_item; LuaMultiThread* d_pool; }; SharedPoolMember getPoolMember() { std::lock_guard lock(mutx); auto member = lua_pool.back(); lua_pool.pop_back(); return SharedPoolMember(member, this); } void returnPoolMember(std::shared_ptr my_ptr) { std::lock_guard lock(mutx); lua_pool.push_back(my_ptr); } private: std::vector> lua_pool; std::vector> lua_read_only; unsigned int num_states; std::mutex mutx; }; extern std::shared_ptr g_luamultip; extern int g_num_luastates; weakforced-2.10.2/trackalert/trackalert-web.cc000066400000000000000000000163661461473602600213050ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "trackalert-web.hh" #include "wforce-webserver.hh" #include "wforce_ns.hh" #include "trackalert.hh" #include "json11.hpp" #include "ext/incbin/incbin.h" #include "dolog.hh" #include #include #include #include #include "namespaces.hh" #include #include #include "base64.hh" #include "perf-stats.hh" #include "trackalert-luastate.hh" #include "webhook.hh" #include "login_tuple.hh" #include "prometheus.hh" static int uptimeOfProcess() { static time_t start = time(0); return time(0) - start; } void reportLog(const LoginTuple& lt) { if (g_verbose) { std::ostringstream os; os << "reportLog: "; os << "remote=\"" << lt.remote.toString() << "\" "; os << "login=\"" << lt.login << "\" "; os << "success=\"" << lt.success << "\" "; os << "policy_reject=\"" << lt.policy_reject << "\" "; os << "pwhash=\"" << std::hex << std::uppercase << lt.pwhash << "\" "; os << "protocol=\"" << lt.protocol << "\" "; os << "device_id=\"" << lt.device_id << "\" "; os << DeviceAttrsToString(lt); os << LtAttrsToString(lt); infolog(os.str().c_str()); } } static void runReportLua(json11::Json msg, std::string command) { try { LoginTuple lt; lt.from_json(msg, nullptr); reportLog(lt); g_stats.reports++; g_luamultip->report(lt); } catch (LuaContext::ExecutionErrorException& e) { try { std::rethrow_if_nested(e); errlog("Lua function [%s] exception: %s", command, e.what()); } catch (const std::exception& ne) { errlog("Exception in command [%s] exception: %s", command, ne.what()); } catch (const WforceException& ne) { errlog("Exception in command [%s] exception: %s", command, ne.reason); } } catch (const std::exception& e) { errlog("Exception in command [%s] exception: %s", command, e.what()); } catch (const WforceException& e) { errlog("Exception in command [%s] exception: %s", command, e.reason); } } void parseReportCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; ss << "{\"status\":\"failure\", \"reason\":\"" << err << "\"}"; resp->setBody(ss.str()); } else { runReportLua(msg, command); resp->setStatusCode(drogon::k200OK); resp->setBody(R"({"status":"ok"})"); } incCommandStat("report"); incPrometheusCommandMetric("report"); } void parseStatsCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { struct rusage ru; getrusage(RUSAGE_SELF, &ru); json11::Json my_json = json11::Json::object{ {"reports", (int) g_stats.reports}, {"user-msec", (int) (ru.ru_utime.tv_sec * 1000ULL + ru.ru_utime.tv_usec / 1000)}, {"sys-msec", (int) (ru.ru_stime.tv_sec * 1000ULL + ru.ru_stime.tv_usec / 1000)}, {"uptime", uptimeOfProcess()}, {"perfstats", perfStatsToJson()} }; resp->setStatusCode(drogon::k200OK); resp->setBody(my_json.dump()); incCommandStat("stats"); incPrometheusCommandMetric("stats"); } enum CustomReturnFields { customRetStatus = 0, customRetAttrs = 1 }; void parseCustomCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; ss << "{\"success\":\"failure\", \"reason\":\"" << err << "\"}"; resp->setBody(ss.str()); } else { CustomFuncArgs cfa; try { cfa.setAttrs(msg); } catch (...) { resp->setStatusCode(drogon::k500InternalServerError); resp->setBody(R"({"success":false, "reason":"Could not parse input"})"); return; } try { CustomFuncReturn cr; { cr = g_luamultip->custom_func(command, cfa); } bool status = std::get(cr); KeyValVector ret_attrs = std::get(cr); json11::Json::object jattrs; for (auto& i : ret_attrs) { jattrs.insert(make_pair(i.first, json11::Json(i.second))); } msg = json11::Json::object{{"success", status}, {"r_attrs", jattrs}}; resp->setStatusCode(drogon::k200OK); resp->setBody(msg.dump()); } catch (LuaContext::ExecutionErrorException& e) { resp->setStatusCode(drogon::k500InternalServerError); try { std::rethrow_if_nested(e); std::stringstream ss; ss << "{\"success\":false, \"reason\":\"" << e.what() << "\"}"; resp->setBody(ss.str()); errlog("Lua custom function [%s] exception: %s", command, e.what()); } catch (const std::exception& ne) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; ss << "{\"success\":false, \"reason\":\"" << ne.what() << "\"}"; resp->setBody(ss.str()); errlog("Exception in command [%s] exception: %s", command, ne.what()); } catch (const WforceException& ne) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; ss << "{\"success\":false, \"reason\":\"" << ne.reason << "\"}"; resp->setBody(ss.str()); errlog("Exception in command [%s] exception: %s", command, ne.reason); } } catch (const std::exception& e) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; ss << "{\"success\":false, \"reason\":\"" << e.what() << "\"}"; resp->setBody(ss.str()); errlog("Exception in command [%s] exception: %s", command, e.what()); } } incCommandStat(command); incPrometheusCommandMetric(command); } void registerWebserverCommands() { addCommandStat("report"); addPrometheusCommandMetric("report"); g_webserver.registerFunc("report", HTTPVerb::POST, WforceWSFunc(parseReportCmd)); addCommandStat("stats"); addPrometheusCommandMetric("stats"); g_webserver.registerFunc("stats", HTTPVerb::GET, WforceWSFunc(parseStatsCmd)); } weakforced-2.10.2/trackalert/trackalert-web.hh000066400000000000000000000023121461473602600213010ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "wforce-webserver.hh" void setNumReportThreads(int numThreads); void registerWebserverCommands(); void parseCustomCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp); weakforced-2.10.2/trackalert/trackalert.cc000066400000000000000000000447111461473602600205250ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #define SYSLOG_NAMES #include #include "trackalert.hh" #include "sstuff.hh" #include "misc.hh" #include #include #include "dolog.hh" #include #include #include "base64.hh" #include #include "json11.hpp" #include #include #include "sodcrypto.hh" #include "perf-stats.hh" #include "trackalert-luastate.hh" #include "webhook.hh" #include "lock.hh" #include "trackalert-web.hh" #include "ext/threadname.hh" #include "prometheus.hh" #include #ifdef HAVE_LIBSYSTEMD #include #endif #include "device_parser.hh" #include "wforce_ns.hh" using std::atomic; using std::thread; bool g_verbose=false; struct TrackalertStats g_stats; bool g_console=false; bool g_docker=false; LogLevel g_loglevel{LogLevel::Info}; string g_outputBuffer; WebHookRunner g_webhook_runner; WebHookDB g_webhook_db; WebHookDB g_custom_webhook_db; WforceWebserver g_webserver; CustomFuncMap g_custom_func_map; curlTLSOptions g_curl_tls_options; std::string g_configDir; // where the config files are located bool g_configurationDone = false; // The Scheduler class should be thread-safe std::shared_ptr g_bg_schedulerp; int g_num_scheduler_threads = NUM_SCHEDULER_THREADS; bool getMsgLen(int fd, uint16_t* len) try { uint16_t raw; int ret = readn2(fd, &raw, 2); if(ret != 2) return false; *len = ntohs(raw); return true; } catch(...) { return false; } bool putMsgLen(int fd, uint16_t len) try { uint16_t raw = htons(len); int ret = writen2(fd, &raw, 2); return ret==2; } catch(...) { return false; } std::mutex g_luamutex; LuaContext g_lua; int g_num_luastates=NUM_LUA_STATES; std::shared_ptr g_luamultip; static void daemonize(void) { if(fork()) _exit(0); // bye bye setsid(); int i=open("/dev/null",O_RDWR); /* open stdin */ if(i < 0) ; // L<begin(); it != g_luamultip->end(); ++it) { std::lock_guard lock((*it)->lua_mutex); (*it)->lua_context.executeCode< boost::optional< boost::variant< string > > >(line); } { std::lock_guard lock(g_luamutex); g_outputBuffer.clear(); auto ret=g_lua.executeCode< boost::optional< boost::variant< string > > >(line); if(ret) { if (const auto strValue = boost::get(&*ret)) { response=*strValue; } } else response=g_outputBuffer; } } catch(const LuaContext::WrongTypeException& e) { response = "Command returned an object we can't print: " +std::string(e.what()) + "\n"; // tried to return something we don't understand } catch(const LuaContext::ExecutionErrorException& e) { response = "Error: " + string(e.what()) + ": "; try { std::rethrow_if_nested(e); } catch(const std::exception& e) { // e is the exception that was thrown from inside the lambda response+= string(e.what()); } } catch(const LuaContext::SyntaxErrorException& e) { response = "Error: " + string(e.what()) + ": "; } response = sodEncryptSym(response, g_key, writingNonce); putMsgLen(fd, response.length()); writen2(fd, response.c_str(), (uint16_t)response.length()); } // The Socket class wrapper will close the socket for us infolog("Closed control connection from %s", client.toStringWithPort()); } catch(const std::exception& e) { errlog("Got an exception in client connection from %s: %s", client.toStringWithPort(), e.what()); } void controlThread(int fd, ComboAddress local) try { ComboAddress client; int sock; setThreadName("wf/ctrl-accept"); noticelog("Accepting control connections on %s", local.toStringWithPort()); while((sock=SAccept(fd, client)) >= 0) { infolog("Got control connection from %s", client.toStringWithPort()); thread t(controlClientThread, sock, client); t.detach(); } } catch(const std::exception& e) { close(fd); errlog("Control connection died: %s", e.what()); } void doClient(ComboAddress server, const std::string& command) { cout<<"Connecting to "< dupper; { ifstream history(".history"); string line; while(getline(history, line)) add_history(line.c_str()); } ofstream history(".history", std::ios_base::app); string lastline; for(;;) { char* sline = readline("> "); rl_bind_key('\t',rl_complete); if(!sline) break; string line(sline); if(!line.empty() && line != lastline) { add_history(sline); history << sline < dupper; { ifstream history(".history"); string line; while(getline(history, line)) add_history(line.c_str()); } ofstream history(".history", std::ios_base::app); string lastline; for(;;) { char* sline = readline("> "); rl_bind_key('\t',rl_complete); if(!sline) break; string line(sline); if(!line.empty() && line != lastline) { add_history(sline); history << sline <begin(); it != g_luamultip->end(); ++it) { std::lock_guard lock((*it)->lua_mutex); (*it)->lua_context.executeCode< boost::optional< boost::variant< string > > >(line); } } { std::lock_guard lock(g_luamutex); g_outputBuffer.clear(); auto ret=g_lua.executeCode< boost::optional< boost::variant< string > > >(line); if(ret) { if (const auto strValue = boost::get(&*ret)) { cout<<*strValue< words {"addACL", "setACL", "showACL()", "shutdown()", "webserver", "controlSocket", "stats()", "newCA", "newNetmaskGroup", "makeKey", "reloadGeoIPDBs()", "setKey", "testCrypto", "showCustomWebHooks()", "showPerfStats()", "showCommandStats()", "showCustomStats()", "showVersion()", "showCustomEndpoints()", "setCustomEndpoint(", "addWebHook(", "addCustomWebHook(", "setNumWebHookThreads(" }; static int s_counter=0; int counter=0; if(!state) s_counter=0; for(auto w : words) { if(boost::starts_with(w, t) && counter++ == s_counter) { s_counter++; return strdup(w.c_str()); } } return 0; } static char** my_completion( const char * text , int start, int end) { char **matches=0; if (start == 0) matches = rl_completion_matches ((char*)text, &my_generator); else rl_bind_key('\t',rl_abort); if(!matches) rl_bind_key('\t', rl_abort); return matches; } } std::string findDefaultConfigFile() { std::string configFile = string(SYSCONFDIR) + "/wforce/trackalert.conf"; struct stat statbuf; if (stat(configFile.c_str(), &statbuf) != 0) { g_configDir = string(SYSCONFDIR); configFile = string(SYSCONFDIR) + "/trackalert.conf"; } else g_configDir = string(SYSCONFDIR) + "/wforce"; return configFile; } struct { bool beDaemon{false}; bool underSystemd{false}; bool underDocker{false}; bool beClient{false}; string command; string config; unsigned int facility{LOG_DAEMON}; } g_cmdLine; int main(int argc, char** argv) try { rl_attempted_completion_function = my_completion; rl_completion_append_character = 0; signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); g_console=true; #ifdef HAVE_LIBSODIUM if (sodium_init() == -1) { cerr<<"Unable to initialize crypto library"<(std::stoi(optarg)); } catch (const std::invalid_argument &ia) { cout << "Bad log level (" << optarg << ") - must be an integer" << endl; exit(1); } break; case 'h': cout<<"Syntax: wforce [-C,--config file] [-c,--client] [-d,--daemon] [-e,--execute cmd]\n"; cout<<"[-h,--help] [-l,--local addr]\n"; cout<<"\n"; cout<<"-C,--config file Load configuration from 'file'\n"; cout<<"-c [file], Operate as a client, connect to wforce, loading config from 'file' if specified\n"; cout<<"-s, Operate under systemd control.\n"; cout<<"-d,--daemon Operate as a daemon\n"; cout<<"-D,--docker Enable logging for docker\n"; cout<<"-e,--execute cmd Connect to wforce and execute 'cmd'\n"; cout<<"-f,--facility name Use log facility 'name'\n"; cout<<"-l,--loglevel level Log level as an integer. 0 is Emerg, 7 is Debug. Defaults to 6 (Info).\n"; cout<<"-h,--help Display this helpful message\n"; cout<<"\n"; exit(EXIT_SUCCESS); break; case 'v': g_verbose=true; g_loglevel=LogLevel::Debug; break; case '?': default: cout << "Option '-" << (char)optopt << "' is invalid: ignored\n"; } } argc-=optind; argv+=optind; g_webserver.setWebLogLevel(g_loglevel); openlog("trackalert", LOG_PID, LOG_DAEMON); g_singleThreaded = false; // start background scheduler g_bg_schedulerp = std::make_shared(g_num_scheduler_threads); if (chdir(g_configDir.c_str()) != 0) { warnlog("Could not change working directory to %s (%s)", g_configDir, strerror(errno)); } if(g_cmdLine.beClient || !g_cmdLine.command.empty()) { setupLua(true, false, g_lua, g_report, nullptr, g_custom_func_map, g_cmdLine.config); doClient(g_serverControl, g_cmdLine.command); exit(EXIT_SUCCESS); } // Initialise Prometheus Metrics initPrometheusMetrics(std::make_shared("trackalert")); // now we setup the multi-lua lua states (we do this first because there are routines such as the scheduler // in the global config that use the multi-lua states) g_luamultip = std::make_shared(g_num_luastates); // this sets up the global lua state used for config and setup auto todo=setupLua(false, false, g_lua, g_report, nullptr, g_custom_func_map, g_cmdLine.config); for (auto it = g_luamultip->begin(); it != g_luamultip->end(); ++it) { // first setup defaults in case the config doesn't specify anything (*it)->report_func = g_report; setupLua(false, true, (*it)->lua_context, (*it)->report_func, &((*it)->bg_func_map), (*it)->custom_func_map, g_cmdLine.config); } if(g_cmdLine.beDaemon) { g_console=false; daemonize(); } else if (g_cmdLine.underSystemd) { g_console=false; } else { vinfolog("Running in the foreground"); } g_webhook_runner.startThreads(); // register all the webserver commands registerWebserverCommands(); auto acl = g_webserver.getACL(); for(auto& addr : {"127.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "169.254.0.0/16", "192.168.0.0/16", "172.16.0.0/12", "::1/128", "fc00::/7", "fe80::/10"}) acl.addMask(addr); g_webserver.setACL(acl); vector vec; std::string acls; acl.toStringVector(&vec); for(const auto& s : vec) { if (!acls.empty()) acls += ", "; acls += s; } noticelog("ACL allowing queries from: %s", acls.c_str()); // start the performance stats thread startStatsThread(); // start the threads created by lua setup. Includes the webserver accept thread for(auto& t : todo) t(); #ifdef HAVE_LIBSYSTEMD sd_notify(0, "READY=1"); #endif g_configurationDone = true; if(!(g_cmdLine.beDaemon || g_cmdLine.underSystemd || g_cmdLine.underDocker)) { doConsole(); } else { while (true) pause(); } _exit(EXIT_SUCCESS); } catch(const LuaContext::ExecutionErrorException& e) { try { errlog("Fatal Lua error: %s", e.what()); std::rethrow_if_nested(e); } catch(const std::exception& e) { errlog("Details: %s", e.what()); } catch(WforceException &ae) { errlog("Fatal wforce error: %s", ae.reason); } _exit(EXIT_FAILURE); } catch(std::exception &e) { errlog("Fatal error: %s", e.what()); } catch(WforceException &ae) { errlog("Fatal wforce error: %s", ae.reason); _exit(EXIT_FAILURE); } weakforced-2.10.2/trackalert/trackalert.conf000066400000000000000000000013661461473602600210640ustar00rootroot00000000000000-- Old way to configure the webserver -- webserver("0.0.0.0:8085", "--WEBPWD") -- New way to configure the webserver -- IP addr:port Use SSL? Certificate File Private Key TLS Options - see https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html addListener("0.0.0.0:8085", false, "", "", {}) setWebserverPassword("--WEBPWD") --SETKEY controlSocket("127.0.0.1:4005") addACL("127.0.0.0/8") addACL("192.168.0.0/16") function report(lt) infoLog("Received report", { login=lt.login, remote=lt.remote:tostring(), timestamp=lt.t }) end setReport(report) function background() infoLog("Ran background thread", {}) end setBackground("background", background) cronScheduleBackgroundFunc("* * * * *", "background") weakforced-2.10.2/trackalert/trackalert.hh000066400000000000000000000050561461473602600205360ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/luawrapper/include/LuaContext.hpp" #include #include "misc.hh" #include "iputils.hh" #include #include #include #include "sholder.hh" #include "sstuff.hh" #include "webhook.hh" #include "wforce-webserver.hh" #include "wforce_exception.hh" #include "login_tuple.hh" #include "json11.hpp" #include "ext/scheduler/Scheduler.h" #include #include #include struct TrackalertStats { using stat_t=std::atomic; stat_t reports{0}; }; extern struct TrackalertStats g_stats; struct ClientState { ComboAddress local; int udpFD; int tcpFD; }; extern std::mutex g_luamutex; extern LuaContext g_lua; extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex (functions using g_outputBuffer MUST NOT be enabled for the allow/report lua contexts) extern WforceWebserver g_webserver; void receiveReports(ComboAddress local); extern GlobalStateHolder g_ACL; extern ComboAddress g_serverControl; // not changed during runtime extern std::string g_key; // in theory needs locking void controlThread(int fd, ComboAddress local); bool getMsgLen(int fd, uint16_t* len); bool putMsgLen(int fd, uint16_t len); void* tcpAcceptorThread(void* p); double getDoubleTime(); extern WebHookRunner g_webhook_runner; extern WebHookDB g_webhook_db; extern WebHookDB g_custom_webhook_db; extern bool g_configurationDone; #define NUM_SCHEDULER_THREADS 4 extern int g_num_scheduler_threads; extern std::shared_ptr g_bg_schedulerp; weakforced-2.10.2/trackalert/trackalert.service.in000066400000000000000000000007141461473602600222000ustar00rootroot00000000000000[Unit] Description=Trackalert Anti-Abuse Daemon Documentation=man:trackalert(1) Wants=network-online.target After=network-online.target [Service] Type=@type@ ExecStart=@bindir@/trackalert -s Restart=on-failure StartLimitInterval=0 PrivateTmp=true CapabilityBoundingSet=CAP_NET_BIND_SERVICE NoNewPrivileges=true ProtectSystem=full ProtectHome=true RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 User=wforce Group=wforce [Install] WantedBy=multi-user.target weakforced-2.10.2/trackalert/wforce-common-lua.cc000077700000000000000000000000001461473602600272502../wforce/wforce-common-lua.ccustar00rootroot00000000000000weakforced-2.10.2/trackalert/wforce-common-lua.hh000077700000000000000000000000001461473602600272742../wforce/wforce-common-lua.hhustar00rootroot00000000000000weakforced-2.10.2/wforce/000077500000000000000000000000001461473602600152045ustar00rootroot00000000000000weakforced-2.10.2/wforce/.gitignore000066400000000000000000000000701461473602600171710ustar00rootroot00000000000000/TAGS /wforce /wf_dump_entries /regexes.yaml /*.service weakforced-2.10.2/wforce/Makefile.am000066400000000000000000000061061461473602600172430ustar00rootroot00000000000000AM_CPPFLAGS = \ $(LUA_CFLAGS) \ $(WFORCE_CFLAGS) \ $(EXT_CFLAGS) \ $(libsodium_CFLAGS) \ $(GETDNS_CFLAGS) \ $(JSON11_CFLAGS) \ $(LIBHIREDIS_CFLAGS) \ $(LIBCURL_CFLAGS) \ $(LIBCRYPTO_INCLUDES) \ $(LIBDROGON_INCLUDES) \ $(LIBJSONCPP_INCLUDES) \ $(LIBZ_INCLUDES) \ $(LIBUUID_INCLUDES) \ $(LIBPROMETHEUS_INCLUDES) \ $(YAMLCPP_FLAGS) \ $(MMDB_CFLAGS) \ -DSYSCONFDIR='"$(sysconfdir)"' \ $(SANITIZER_FLAGS) AM_LDFLAGS = \ $(PROGRAM_LDFLAGS) \ $(THREADFLAGS) \ $(SANITIZER_FLAGS) UAP_REGEX_FILE=regexes.yaml EXTRA_DIST= wforce.conf wforce.service.in wforce.conf.example replication.proto $(UAP_REGEX_FILE) CLEANFILES = replication.pb.cc replication.pb.h wforce.service $(UAP_REGEX_FILE) distclean-local: -rm -rf *.dSYM sysconf_DATA= wforce.conf wforce.conf.example $(UAP_REGEX_FILE) bin_PROGRAMS = wforce \ wf_dump_entries wforce_SOURCES = \ wforce.cc wforce.hh \ wforce-lua.cc \ wforce-common-lua.cc wforce-common-lua.hh \ wforce-web.cc wforce-web.hh\ wforce-sibling.cc wforce-sibling.hh \ twmap.cc \ twmap-wrapper.cc twmap-wrapper.hh \ blackwhitelist.cc blackwhitelist.hh \ replication.cc replication.hh \ replication_sdb.hh replication_sdb.cc \ replication_bl.cc replication_bl.hh \ replication_wl.cc replication_wl.hh \ wforce-replication.cc wforce-replication.hh \ wforce-prometheus.hh wforce-prometheus.cc \ luastate.hh wforce_LDFLAGS = \ $(AM_LDFLAGS) \ $(LIBCRYPTO_LDFLAGS) \ $(LIBPROMETHEUS_LDFLAGS) \ $(LIBDROGON_LDFLAGS) \ $(LIBJSONCPP_LDFLAGS) \ $(LIBZ_LDFLAGS) \ $(LIBUUID_LDFLAGS) wforce_LDADD = \ -lreadline \ $(LUA_LIBS) $(EXT_LIBS) $(WFORCE_LIBS) \ ${libsodium_LIBS} $(GEOIP_LIBS) $(MMDB_LIBS) $(GETDNS_LIBS) $(PROTOBUF_LIBS) \ $(LIBSYSTEMD_LIBS) $(JSON11_LIBS) $(BOOST_DATE_TIME_LIBS) \ $(BOOST_REGEX_LIBS) $(LIBHIREDIS_LIBS) $(LIBDROGON_LIBS) \ $(LIBJSONCPP_LIBS) $(LIBZ_LIBS) $(LIBUUID_LIBS) $(LIBCURL) $(LIBCRYPTO_LIBS) \ $(YAMLCPP_LIBS) $(LIBPROMETHEUS_LIBS) $(LIBSSL_LIBS) $(DLLIBS) $(STDCXXFSLIBS) EXTRA_wforce_DEPENDENCIES = \ $(EXT_LIBS) $(WFORCE_LIBS) wf_dump_entries_SOURCES = \ dump_entries.cc wf_dump_entries_LDFLAGS = \ $(AM_LDFLAGS) wf_dump_entries_LDADD = \ $(WFORCE_LIBS) $(LIBCURL) ${libsodium_LIBS} EXTRA_wf_dump_entries_DEPENDENCIES = \ $(WFORCE_LIBS) noinst_HEADERS = \ blackwhitelist.hh \ wforce.hh \ replication.hh \ replication_sdb.hh \ replication_bl.hh \ twmap-wrapper.hh \ luastate.hh \ wforce-web.hh \ wforce-sibling.hh \ wforce-replication.hh BUILT_SOURCES = replication.pb.cc replication.pb.h replication.pb.cc: replication.proto $(AM_V_GEN)$(PROTOC) -I $(srcdir) --cpp_out=. $< replication.pb.h: replication.pb.cc nodist_wforce_SOURCES = replication.pb.cc replication.pb.h wforce.$(OBJEXT): replication.pb.cc $(UAP_REGEX_FILE): wget -q https://raw.githubusercontent.com/ua-parser/uap-core/master/$(UAP_REGEX_FILE) -O $(UAP_REGEX_FILE) if HAVE_SYSTEMD SYSTEMD_TYPE = notify systemdsystemunitdir = $(SYSTEMD_DIR) systemdsystemunit_DATA = wforce.service else SYSTEMD_TYPE = simple endif wforce.service: wforce.service.in $(AM_V_GEN)sed -e 's![@]bindir[@]!$(bindir)!' -e 's![@]type[@]!$(SYSTEMD_TYPE)!' < $< > $@ weakforced-2.10.2/wforce/blackwhitelist.cc000066400000000000000000000737201461473602600205350ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "blackwhitelist.hh" #include "replication.hh" #include "replication_bl.hh" #include "replication_wl.hh" #include "replication.pb.h" #include "wforce-prometheus.hh" #include "json11.hpp" #include #include #include #include #include #include #include #include #include #include "iputils.hh" using namespace json11; BlackWhiteListDB g_bl_db(BLWLDBType::BLACKLIST); BlackWhiteListDB g_wl_db(BLWLDBType::WHITELIST); std::string BlackWhiteListDB::ipLoginStr(const ComboAddress& ca, const std::string& login) const { return ca.toString() + ":" + login; } void BlackWhiteListDB::addEntry(const Netmask& nm, time_t seconds, const std::string& reason) { std::string key = nm.toStringNetwork(); addEntryInternal(key, seconds, IP_BLWL, reason, true); addEntryLog(IP_BLWL, key, seconds, reason); } void BlackWhiteListDB::addEntry(const ComboAddress& ca, time_t seconds, const std::string& reason) { std::string key = Netmask(ca).toStringNetwork(); addEntryInternal(key, seconds, IP_BLWL, reason, true); addEntryLog(IP_BLWL, key, seconds, reason); } void BlackWhiteListDB::addEntry(const std::string& login, time_t seconds, const std::string& reason) { addEntryInternal(login, seconds, LOGIN_BLWL, reason, true); addEntryLog(LOGIN_BLWL, login, seconds, reason); } void BlackWhiteListDB::addEntry(const ComboAddress& ca, const std::string& login, time_t seconds, const std::string& reason) { std::string key = ipLoginStr(ca, login); addEntryInternal(key, seconds, IP_LOGIN_BLWL, reason, true); addEntryLog(IP_LOGIN_BLWL, key, seconds, reason); } void BlackWhiteListDB::addEntryInternal(const std::string& key, time_t seconds, BLWLType blwl_type, const std::string& reason, bool replicate) { WriteLock wl(&rwlock); switch (blwl_type) { case IP_BLWL: _addEntry(key, seconds, ip_list, reason); iplist_netmask.addMask(key); addEntryLog(IP_BLWL, key, seconds, reason); if (persist && ((replicate == true) || ((replicate == false) && persist_replicated))) addPersistEntry(key, seconds, IP_BLWL, reason); if (replicate == true) { if (db_type == BLWLDBType::BLACKLIST) { std::shared_ptr bl_rop; bl_rop = std::make_shared(BLOperation_BLOpType_BLAdd, IP_BLWL, key, seconds, reason); ReplicationOperation rep_op(bl_rop, WforceReplicationMsg_RepType_BlacklistType); replicateOperation(rep_op); } else { std::shared_ptr wl_rop; wl_rop = std::make_shared(BLOperation_BLOpType_BLAdd, IP_BLWL, key, seconds, reason); ReplicationOperation rep_op(wl_rop, WforceReplicationMsg_RepType_WhitelistType); replicateOperation(rep_op); } } break; case LOGIN_BLWL: _addEntry(key, seconds, login_list, reason); addEntryLog(LOGIN_BLWL, key, seconds, reason); if (persist && ((replicate == true) || ((replicate == false) && persist_replicated))) addPersistEntry(key, seconds, LOGIN_BLWL, reason); if (replicate == true) { if (db_type == BLWLDBType::BLACKLIST) { std::shared_ptr bl_rop; bl_rop = std::make_shared(BLOperation_BLOpType_BLAdd, LOGIN_BLWL, key, seconds, reason); ReplicationOperation rep_op(bl_rop, WforceReplicationMsg_RepType_BlacklistType); replicateOperation(rep_op); } else { std::shared_ptr wl_rop; wl_rop = std::make_shared(BLOperation_BLOpType_BLAdd, LOGIN_BLWL, key, seconds, reason); ReplicationOperation rep_op(wl_rop, WforceReplicationMsg_RepType_WhitelistType); replicateOperation(rep_op); } } break; case IP_LOGIN_BLWL: _addEntry(key, seconds, ip_login_list, reason); addEntryLog(IP_LOGIN_BLWL, key, seconds, reason); if (persist && ((replicate == true) || ((replicate == false) && persist_replicated))) addPersistEntry(key, seconds, IP_LOGIN_BLWL, reason); if (replicate == true) { if (db_type == BLWLDBType::BLACKLIST) { std::shared_ptr bl_rop; bl_rop = std::make_shared(BLOperation_BLOpType_BLAdd, IP_LOGIN_BLWL, key, seconds, reason); ReplicationOperation rep_op(bl_rop, WforceReplicationMsg_RepType_BlacklistType); replicateOperation(rep_op); } else { std::shared_ptr wl_rop; wl_rop = std::make_shared(BLOperation_BLOpType_BLAdd, IP_LOGIN_BLWL, key, seconds, reason); ReplicationOperation rep_op(wl_rop, WforceReplicationMsg_RepType_WhitelistType); replicateOperation(rep_op); } } break; default: break; } std::string event_name; std::string type_name; if (db_type == BLWLDBType::BLACKLIST) { event_name = "addbl"; type_name = "bl_type"; } else { event_name = "addwl"; type_name = "wl_type"; } // only generate webhook for this event for the first add, not for replicas if (replicate == true) { Json jobj = Json::object{{"key", key}, {type_name, BLWLTypeToName(blwl_type)}, {"reason", reason}, {"expire_secs", (int) seconds}, {"type", "wforce_addblwl"}}; for (const auto& h: g_webhook_db.getWebHooksForEvent(event_name)) { if (auto hs = h.lock()) g_webhook_runner.runHook(event_name, hs, jobj); } } } void BlackWhiteListDB::_addEntry(const std::string& key, time_t seconds, blackwhitelist_t& blackwhitelist, const std::string& reason) { BlackWhiteListEntry bl; bl.key = key; bl.expiration = boost::posix_time::from_time_t(time(NULL)) + boost::posix_time::seconds(seconds); bl.reason = reason; auto& keyindex = blackwhitelist.get(); keyindex.erase(key); blackwhitelist.insert(bl); } bool BlackWhiteListDB::checkEntry(const ComboAddress& ca) const { ReadLock wl(&rwlock); return iplist_netmask.match(ca); } bool BlackWhiteListDB::checkEntry(const std::string& login) const { return _checkEntry(login, login_list); } bool BlackWhiteListDB::checkEntry(const ComboAddress& ca, const std::string& login) const { return _checkEntry(ipLoginStr(ca, login), ip_login_list); } bool BlackWhiteListDB::_checkEntry(const std::string& key, const blackwhitelist_t& blackwhitelist) const { ReadLock rl(&rwlock); auto& keyindex = blackwhitelist.get(); auto kit = keyindex.find(key); if (kit != keyindex.end()) return true; return false; } bool BlackWhiteListDB::getEntry(const ComboAddress& ca, BlackWhiteListEntry& ret) const { Netmask nm; { ReadLock wl(&rwlock); iplist_netmask.lookup(ca, &nm); } return _getEntry(nm.toString(), ip_list, ret); } bool BlackWhiteListDB::getEntry(const std::string& login, BlackWhiteListEntry& ret) const { return _getEntry(login, login_list, ret); } bool BlackWhiteListDB::getEntry(const ComboAddress& ca, const std::string& login, BlackWhiteListEntry& ret) const { return _getEntry(ipLoginStr(ca, login), ip_login_list, ret); } bool BlackWhiteListDB::_getEntry(const std::string& key, const blackwhitelist_t& blackwhitelist, BlackWhiteListEntry& ret_ble) const { ReadLock rl(&rwlock); auto& keyindex = blackwhitelist.get(); auto kit = keyindex.find(key); if (kit != keyindex.end()) { ret_ble = *kit; // copy return true; } return false; } void BlackWhiteListDB::deleteEntry(const Netmask& nm) { std::string key = nm.toStringNetwork(); deleteEntryInternal(key, IP_BLWL, true); } void BlackWhiteListDB::deleteEntry(const ComboAddress& ca) { std::string key = Netmask(ca).toStringNetwork(); deleteEntryInternal(key, IP_BLWL, true); } void BlackWhiteListDB::deleteEntry(const std::string& login) { deleteEntryInternal(login, LOGIN_BLWL, true); } void BlackWhiteListDB::deleteEntry(const ComboAddress& ca, const std::string& login) { std::string key = ipLoginStr(ca, login); deleteEntryInternal(key, IP_LOGIN_BLWL, true); } void BlackWhiteListDB::deleteEntryInternal(const std::string& key, BLWLType blwl_type, bool replicate) { WriteLock wl(&rwlock); switch (blwl_type) { case IP_BLWL: deleteEntryLog(IP_BLWL, key); _deleteEntry(key, ip_list); iplist_netmask.deleteMask(key); if (persist && ((replicate == true) || ((replicate == false) && persist_replicated))) { deletePersistEntry(key, blwl_type, ip_list); } if (replicate == true) { if (db_type == BLWLDBType::BLACKLIST) { std::shared_ptr bl_rop; bl_rop = std::make_shared(BLOperation_BLOpType_BLDelete, IP_BLWL, key, 0, ""); ReplicationOperation rep_op(bl_rop, WforceReplicationMsg_RepType_BlacklistType); replicateOperation(rep_op); } else { std::shared_ptr wl_rop; wl_rop = std::make_shared(BLOperation_BLOpType_BLDelete, IP_BLWL, key, 0, ""); ReplicationOperation rep_op(wl_rop, WforceReplicationMsg_RepType_WhitelistType); replicateOperation(rep_op); } } break; case LOGIN_BLWL: deleteEntryLog(LOGIN_BLWL, key); _deleteEntry(key, login_list); if (persist && ((replicate == true) || ((replicate == false) && persist_replicated))) { deletePersistEntry(key, blwl_type, login_list); } if (replicate == true) { if (db_type == BLWLDBType::BLACKLIST) { std::shared_ptr bl_rop; bl_rop = std::make_shared(BLOperation_BLOpType_BLDelete, LOGIN_BLWL, key, 0, ""); ReplicationOperation rep_op(bl_rop, WforceReplicationMsg_RepType_BlacklistType); replicateOperation(rep_op); } else { std::shared_ptr wl_rop; wl_rop = std::make_shared(BLOperation_BLOpType_BLDelete, LOGIN_BLWL, key, 0, ""); ReplicationOperation rep_op(wl_rop, WforceReplicationMsg_RepType_WhitelistType); replicateOperation(rep_op); } } break; case IP_LOGIN_BLWL: deleteEntryLog(IP_LOGIN_BLWL, key); _deleteEntry(key, ip_login_list); if (persist && ((replicate == true) || ((replicate == false) && persist_replicated))) { deletePersistEntry(key, blwl_type, ip_login_list); } if (replicate == true) { if (db_type == BLWLDBType::BLACKLIST) { std::shared_ptr bl_rop; bl_rop = std::make_shared(BLOperation_BLOpType_BLDelete, IP_LOGIN_BLWL, key, 0, ""); ReplicationOperation rep_op(bl_rop, WforceReplicationMsg_RepType_BlacklistType); replicateOperation(rep_op); } else { std::shared_ptr wl_rop; wl_rop = std::make_shared(BLOperation_BLOpType_BLDelete, IP_LOGIN_BLWL, key, 0, ""); ReplicationOperation rep_op(wl_rop, WforceReplicationMsg_RepType_WhitelistType); replicateOperation(rep_op); } } break; default: break; } std::string event_name; std::string type_name; if (db_type == BLWLDBType::BLACKLIST) { event_name = "delbl"; type_name = "bl_type"; } else { event_name = "delwl"; type_name = "wl_type"; } // only generate webhook for this event for the first delete, not for replicas if (replicate == true) { Json jobj = Json::object{{"key", key}, {type_name, BLWLTypeToName(blwl_type)}, {"type", "wforce_delblwl"}}; for (const auto& h: g_webhook_db.getWebHooksForEvent(event_name)) { if (auto hs = h.lock()) g_webhook_runner.runHook(event_name, hs, jobj); } } } bool BlackWhiteListDB::_deleteEntry(const std::string& key, blackwhitelist_t& blackwhitelist) { auto& keyindex = blackwhitelist.get(); auto kit = keyindex.find(key); if (kit != keyindex.end()) { keyindex.erase(key); return true; } return false; } time_t BlackWhiteListDB::getExpiration(const ComboAddress& ca) const { Netmask nm; iplist_netmask.lookup(ca, &nm); return _getExpiration(nm.toString(), ip_list); } time_t BlackWhiteListDB::getExpiration(const std::string& login) const { return _getExpiration(login, login_list); } time_t BlackWhiteListDB::getExpiration(const ComboAddress& ca, const std::string& login) const { return _getExpiration(ipLoginStr(ca, login), ip_login_list); } // to_time_t is missing in some versions of boost #if BOOST_VERSION < 105700 inline time_t my_to_time_t(boost::posix_time::ptime pt) { boost::posix_time::time_duration dur = pt - boost::posix_time::ptime(boost::gregorian::date(1970,1,1)); return std::time_t(dur.total_seconds()); } #define TO_TIME_T my_to_time_t #else #define TO_TIME_T boost::posix_time::to_time_t #endif time_t BlackWhiteListDB::_getExpiration(const std::string& key, const blackwhitelist_t& blackwhitelist) const { ReadLock rl(&rwlock); auto& keyindex = blackwhitelist.get(); auto kit = keyindex.find(key); if (kit != keyindex.end()) { time_t expiration_secs = TO_TIME_T(kit->expiration) - time(NULL); if (expiration_secs >= 0) return expiration_secs; else return -1; // expired already } return (time_t) -1; // not found } void BlackWhiteListDB::purgeEntries() { while (true) { sleep(1); _purgeEntries(IP_BLWL, ip_list, IP_BLWL); _purgeEntries(LOGIN_BLWL, login_list, LOGIN_BLWL); _purgeEntries(IP_LOGIN_BLWL, ip_login_list, IP_LOGIN_BLWL); { ReadLock rl(&rwlock); if (db_type == BLWLDBType::BLACKLIST) { setPrometheusBLIPEntries(ip_list.size()); setPrometheusBLLoginEntries(login_list.size()); setPrometheusBLIPLoginEntries(ip_login_list.size()); } else { setPrometheusWLIPEntries(ip_list.size()); setPrometheusWLLoginEntries(login_list.size()); setPrometheusWLIPLoginEntries(ip_login_list.size()); } } } } void BlackWhiteListDB::_purgeEntries(BLWLType blt, blackwhitelist_t& blackwhitelist, BLWLType blwl_type) { WriteLock wl(&rwlock); boost::system_time now = boost::get_system_time(); auto& timeindex = blackwhitelist.get(); std::string event_name; std::string type_name; if (db_type == BLWLDBType::BLACKLIST) { event_name = "expirebl"; type_name = "bl_type"; } else { event_name = "expirewl"; type_name = "wl_type"; } for (auto tit = timeindex.begin(); tit != timeindex.end();) { if (tit->expiration <= now) { Json jobj = Json::object{{"key", tit->key}, {type_name, BLWLTypeToName(blwl_type)}, {"type", "wforce_expireblwl"}}; for (const auto& h: g_webhook_db.getWebHooksForEvent(event_name)) { if (auto hs = h.lock()) g_webhook_runner.runHook(event_name, hs, jobj); } expireEntryLog(blt, tit->key); if (blwl_type == IP_BLWL) iplist_netmask.deleteMask(tit->key); tit = timeindex.erase(tit); } else break; // stop when we run out of entries to expire } } std::vector BlackWhiteListDB::getIPEntries() const { ReadLock rl(&rwlock); std::vector ret; auto& seqindex = ip_list.get(); for (const auto& a: seqindex) ret.push_back(a); return ret; } std::vector BlackWhiteListDB::getLoginEntries() const { ReadLock rl(&rwlock); std::vector ret; auto& seqindex = login_list.get(); for (const auto& a: seqindex) ret.push_back(a); return ret; } std::vector BlackWhiteListDB::getIPLoginEntries() const { ReadLock rl(&rwlock); std::vector ret; auto& seqindex = ip_login_list.get(); for (const auto& a: seqindex) ret.push_back(a); return ret; } void BlackWhiteListDB::addEntryLog(BLWLType blt, const std::string& key, time_t seconds, const std::string& reason) const { std::ostringstream os; std::string blwl_name = list_names[blt]; std::string key_name = string(key_names[blt]); std::string event_name; if (db_type == BLWLDBType::BLACKLIST) event_name = "addBLEntry"; else event_name = "addWLEntry"; os << event_name << " " << blwl_name << ": " << key_name << "=" << key << " expire_secs=" << std::to_string(seconds) << " reason=\"" << reason + "\""; noticelog(os.str().c_str()); } void BlackWhiteListDB::deleteEntryLog(BLWLType blt, const std::string& key) const { std::ostringstream os; std::string blwl_name = list_names[blt]; std::string key_name = string(key_names[blt]); std::string event_name; if (db_type == BLWLDBType::BLACKLIST) event_name = "deleteBLEntry"; else event_name = "deleteWLEntry"; os << event_name << " " << blwl_name << ": " << key_name << "=" + key; noticelog(os.str().c_str()); } void BlackWhiteListDB::expireEntryLog(BLWLType blt, const std::string& key) const { std::ostringstream os; std::string blwl_name = list_names[blt]; std::string key_name = string(key_names[blt]); std::string event_name; if (db_type == BLWLDBType::BLACKLIST) event_name = "deleteBLEntry"; else event_name = "deleteWLEntry"; os << event_name << " " << blwl_name << ": " << key_name << "=" << key; noticelog(os.str().c_str()); } void BlackWhiteListDB::setConnectTimeout(int timeout) { redis_timeout = timeout; // atomic } void BlackWhiteListDB::setRWTimeout(int timeout_secs, int timeout_usecs) { redis_rw_timeout_usecs = timeout_usecs; // atomic redis_rw_timeout_secs = timeout_secs; // atomic } bool BlackWhiteListDB::checkSetupContext() { if (redis_context == NULL || redis_context->err) { if (redis_context) redisFree(redis_context); // something went wrong previously, try again struct timeval tv; tv.tv_sec = redis_timeout; tv.tv_usec = 0; struct addrinfo hints; struct addrinfo *result, *rp; int s; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ s = getaddrinfo(redis_server.c_str(), std::to_string(redis_port).c_str(), &hints, &result); if (s != 0) { errlog("checkSetupContext: getaddrinfo: %s\n", gai_strerror(s)); if (db_type == BLWLDBType::BLACKLIST) incPrometheusRedisBLConnFailed(); else incPrometheusRedisWLConnFailed(); return false; } // Iterate through the returned IP addresses trying to connect to the all until we succeed for (rp = result; rp != NULL; rp = rp->ai_next) { std::string ip_addr = ComboAddress(rp->ai_addr, rp->ai_addrlen).toString(); redis_context = redisConnectWithTimeout(ip_addr.c_str(), redis_port, tv); if (redis_context == NULL || redis_context->err) { infolog("checkSetupContext: could not connect to redis BlackWhiteListDB on (%s:%d), will try other addresses", ip_addr, redis_port); if (redis_context) { redisFree(redis_context); redis_context = NULL; } } else { break; } } freeaddrinfo(result); if (redis_context == NULL || redis_context->err) { errlog("checkSetupContext: could not connect to any addresses for redis BlackWhiteListDB (%s:%d)", redis_server, redis_port); if (db_type == BLWLDBType::BLACKLIST) incPrometheusRedisBLConnFailed(); else incPrometheusRedisWLConnFailed(); return false; } else { struct timeval tv; tv.tv_sec = static_cast(redis_rw_timeout_secs); tv.tv_usec = static_cast(redis_rw_timeout_usecs); if (redisSetTimeout(redis_context, tv) == REDIS_ERR) { errlog("Could not set Redis Timeout to %u seconds, %u microseconds", tv.tv_sec, tv.tv_usec); throw WforceException("Error: could not set Redis Timeout in BlackWhitelist"); } else { infolog("Set Redis RW Timeout to %u seconds, %u microseconds", tv.tv_sec, tv.tv_usec); } if (redisEnableKeepAlive(redis_context) == REDIS_ERR) { errlog("Could not enable Redis KeepAlive"); throw WforceException("Error: Could not enable Redis KeepAlive in BlackWhitelist"); } } } // Now we have a working Redis connection, check if we need to authenticate if (redis_password.length()) { if (redis_username.length()) { redisReply* reply = (redisReply*) redisCommand(redis_context, "AUTH %s %s", redis_username.c_str(), redis_password.c_str()); if (reply != NULL) { if (reply->type == REDIS_REPLY_ERROR) { errlog("checkSetupContext: Error in AUTH to persistent redis BlackWhiteListDB (username: %s): %s", redis_username, reply->str); freeReplyObject(reply); throw WforceException("Error: Error in AUTH to persistent redis BlackWhiteListDB"); } freeReplyObject(reply); } } else { redisReply* reply = (redisReply*) redisCommand(redis_context, "AUTH %s", redis_password.c_str()); if (reply != NULL) { if (reply->type == REDIS_REPLY_ERROR) { errlog("checkSetupContext: Error in AUTH to persistent redis BlackWhiteListDB: %s", reply->str); freeReplyObject(reply); throw WforceException("Error: Error in AUTH to persistent redis BlackWhiteListDB"); } freeReplyObject(reply); } } } return true; } void BlackWhiteListDB::makePersistent(const std::string& host, unsigned int port = 6379) { persist = true; redis_server = host; redis_port = port; } bool BlackWhiteListDB::addPersistEntry(const std::string& key, time_t seconds, BLWLType blwl_type, const std::string& reason) { bool retval = false; if (checkSetupContext()) { std::stringstream redis_key_s; std::stringstream redis_value_s; std::string blwl_key = string(key_names[blwl_type]); time_t expiration_time = time(NULL) + seconds; redis_key_s << redis_prefix << ":" << blwl_key << ":" << key; redis_value_s << expiration_time << ":" << reason; redisReply* reply = (redisReply*) redisCommand(redis_context, "SET %s %b EX %d", redis_key_s.str().c_str(), redis_value_s.str().c_str(), redis_value_s.str().length(), seconds); if (reply != NULL) { if (reply->type == REDIS_REPLY_ERROR) { errlog("addPersistEntry: Error adding %s to persistent redis BlackWhiteListDB (%s)", key, reply->str); } else retval = true; freeReplyObject(reply); } } if (db_type == BLWLDBType::BLACKLIST) incPrometheusRedisBLUpdates(); else incPrometheusRedisWLUpdates(); return retval; } bool BlackWhiteListDB::deletePersistEntry(const std::string& key, BLWLType blwl_type, blackwhitelist_t& blackwhitelist) { bool retval = false; if (checkSetupContext()) { BlackWhiteListEntry ble; std::stringstream redis_key_s; std::string blwl_key = string(key_names[blwl_type]); redis_key_s << redis_prefix << ":" << blwl_key << ":" << key; redisReply* reply = (redisReply*) redisCommand(redis_context, "DEL %s", redis_key_s.str().c_str()); if (reply != NULL) { if (reply->type == REDIS_REPLY_ERROR) { errlog("deletePersistEntry: Error deleting %s from persistent redis BlackWhiteListDB (%s)", key, reply->str); } else retval = true; freeReplyObject(reply); } } return retval; } bool BlackWhiteListDB::loadPersistEntries() { bool retval = true; WriteLock wl(&rwlock); if (persist != false) { unsigned int num_entries = 0; if (checkSetupContext()) { bool end_it = false; unsigned int count_it = 0; while (end_it != true) { std::ostringstream oss; oss << "SCAN %d MATCH " << redis_prefix << ":* COUNT 1000"; redisReply* reply = (redisReply*) redisCommand(redis_context, oss.str().c_str(), count_it); if (reply != NULL) { if (reply->type == REDIS_REPLY_ERROR) { errlog("loadPersistEntries: Error scanning keys in persistent redis BlackWhiteListDB (%s)", reply->str); retval = false; } else if (reply->type == REDIS_REPLY_ARRAY) { if (reply->elements == 2) { redisReply* rcounter = reply->element[0]; count_it = rcounter->integer; if (count_it == 0) { end_it = true; } redisReply* keys = reply->element[1]; unsigned int num_keys = keys->elements; for (unsigned int i = 0; i < num_keys; ++i) { redisReply* key = keys->element[i]; if (key->type == REDIS_REPLY_STRING) { redisAppendCommand(redis_context, "GET %b", key->str, key->len); } } for (unsigned int i = 0; i < num_keys; ++i) { redisReply* get_reply = NULL; redisReply* key = keys->element[i]; redisGetReply(redis_context, (void**) &get_reply); if (get_reply != NULL) { if (get_reply->type == REDIS_REPLY_ERROR) { errlog("loadPersistEntries: Error getting key %b in persistent redis BlackWhiteListDB (%s)", key->str, key->len, get_reply->str); retval = false; } else if (get_reply->type == REDIS_REPLY_STRING) { std::string keystr(key->str, key->len); std::string valstr(get_reply->str, get_reply->len); // keystr looks like :ip_bl:key pair headersplit = splitField(keystr, ':'); pair keysplit = splitField(headersplit.second, ':'); // valstr looks like seconds:reason pair valsplit = splitField(valstr, ':'); std::string blwl_name = keysplit.first; std::string blwl_key = keysplit.second; time_t blwl_seconds = std::stoi(valsplit.first) - time(NULL); std::string blwl_reason = valsplit.second; BLWLType blwl_type = BLWLNameToType(blwl_name); switch (blwl_type) { case IP_BLWL: _addEntry(blwl_key, blwl_seconds, ip_list, blwl_reason); iplist_netmask.addMask(blwl_key); ++num_entries; break; case LOGIN_BLWL: _addEntry(blwl_key, blwl_seconds, login_list, blwl_reason); ++num_entries; break; case IP_LOGIN_BLWL: _addEntry(blwl_key, blwl_seconds, ip_login_list, blwl_reason); ++num_entries; break; default: errlog("loadPersistEntries: Error in blackwhitelist name retrieved from key: %s", blwl_name); retval = false; } } freeReplyObject(get_reply); } else { retval = false; errlog("loadPersistEntries: Redis GET failed: %s", redis_context->errstr); } if (retval == false) break; } } } freeReplyObject(reply); } else { retval = false; errlog("loadPersistEntries: Redis SCAN failed: %s", redis_context->errstr); } if (retval == false) break; } } else { retval = false; std::string myerr = "Error - could not connect to configured persistent redis DB (" + redis_server + ":" + std::to_string(redis_port) + ")"; errlog(myerr.c_str()); throw WforceException(myerr); } if (retval == true) noticelog("Loaded %d blackwhitelist entries from persistent redis DB", num_entries); } return retval; } BLWLType BlackWhiteListDB::BLWLNameToType(const std::string& blwl_name) const { for (unsigned int i = 0; key_names[i] != NULL; i++) { if (blwl_name.compare(key_names[i]) == 0) return (BLWLType) i; } return NONE_BLWL; } std::string BlackWhiteListDB::BLWLTypeToName(BLWLType blwl_type) const { if (blwl_type < list_names.size()) return list_names[blwl_type]; else return std::string(); } weakforced-2.10.2/wforce/blackwhitelist.hh000066400000000000000000000176301461473602600205450ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include "misc.hh" #include "lock.hh" #include "iputils.hh" #include #include #include #include #include "sholder.hh" #include "sstuff.hh" #include "dolog.hh" #include #include #include #include #include #include #include "ext/threadname.hh" #include "webhook.hh" struct BlackWhiteListEntry { std::string key; std::string reason; boost::system_time expiration; bool operator<(const BlackWhiteListEntry& r) const { if (key < r.key) return true; return false; } }; using namespace boost::multi_index; enum BLWLType { IP_BLWL=0, LOGIN_BLWL=1, IP_LOGIN_BLWL=2, NONE_BLWL=999 }; enum class BLWLDBType { BLACKLIST=0, WHITELIST=1 }; class BlackWhiteListDB { public: BlackWhiteListDB(BLWLDBType type) { db_type = type; if (type == BLWLDBType::BLACKLIST) { list_names = { "ip_bl", "login_bl", "ip_login_bl" }; redis_prefix = "wfbl"; ip_ret_msg = "Temporarily blacklisted IP Address - try again later"; login_ret_msg = "Temporarily blacklisted Login Name - try again later"; iplogin_ret_msg = "Temporarily blacklisted IP/Login Tuple - try again later"; } else { list_names = { "ip_wl", "login_wl", "ip_login_wl" }; redis_prefix = "wfwl"; ip_ret_msg = "Whitelisted IP Address"; login_ret_msg = "Whitelisted Login Name"; iplogin_ret_msg = "Whitelisted IP/Login Tuple"; } redis_context = NULL; redis_port = 6379; redis_timeout=1; redis_rw_timeout_usecs = 100000; } BlackWhiteListDB(const BlackWhiteListDB&) = delete; void addEntry(const Netmask& nm, time_t seconds, const std::string& reason); void addEntry(const ComboAddress& ca, time_t seconds, const std::string& reason); void addEntry(const std::string& login, time_t seconds, const std::string& reason); void addEntry(const ComboAddress& ca, const std::string& login, time_t seconds, const std::string& reason); bool checkEntry(const ComboAddress& ca) const; bool checkEntry(const std::string& login) const; bool checkEntry(const ComboAddress& ca, const std::string& login) const; bool getEntry(const ComboAddress& ca, BlackWhiteListEntry& ret) const; bool getEntry(const std::string& login, BlackWhiteListEntry& ret) const; bool getEntry(const ComboAddress& ca, const std::string& login, BlackWhiteListEntry& ret) const; void deleteEntry(const Netmask& nm); void deleteEntry(const ComboAddress& ca); void deleteEntry(const std::string& login); void deleteEntry(const ComboAddress& ca, const std::string& login); time_t getExpiration(const ComboAddress& ca) const; time_t getExpiration(const std::string& login) const; time_t getExpiration(const ComboAddress& ca, const std::string& login) const; void addEntryInternal(const std::string& key, time_t seconds, BLWLType bl_type, const std::string& reason, bool replicate); void deleteEntryInternal(const std::string& key, BLWLType bl_type, bool replicate); void makePersistent(const std::string& host, unsigned int port); void persistReplicated() { persist_replicated = true; } bool loadPersistEntries(); void purgeEntries(); static void purgeEntriesThread(BlackWhiteListDB* blwl_db) { setThreadName("wf/blwl-purge"); blwl_db->purgeEntries(); } std::vector getIPEntries() const; std::vector getLoginEntries() const; std::vector getIPLoginEntries() const; void setConnectTimeout(int timeout); void setRWTimeout(int timeout_secs, int timeout_usecs); const std::string& getIPRetMsg() const { return ip_ret_msg;} const std::string& getLoginRetMsg() const { return login_ret_msg; } const std::string& getIPLoginRetMsg() const { return iplogin_ret_msg; } void setIPRetMsg(const std::string& msg) { ip_ret_msg = msg; } void setLoginRetMsg(const std::string& msg) { login_ret_msg = msg; } void setIPLoginRetMsg(const std::string& msg) { iplogin_ret_msg = msg; } void setRedisUsername(const std::string& username) { redis_username = username; } void setRedisPassword(const std::string& password) { redis_password = password; } private: struct TimeTag{}; struct KeyTag{}; struct SeqTag{}; typedef multi_index_container< BlackWhiteListEntry, indexed_by< ordered_non_unique< tag, member >, hashed_unique< tag, member >, sequenced> > > blackwhitelist_t; std::vector list_names = { "ip_blwl", "login_blwl", "ip_login_blwl" }; const char* key_names[4] = { "ip", "login", "ip_login", NULL }; NetmaskGroup iplist_netmask; blackwhitelist_t ip_list; blackwhitelist_t login_list; blackwhitelist_t ip_login_list; mutable pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; bool persist=false; bool persist_replicated=false; std::string redis_server; unsigned int redis_port; std::string redis_password; std::string redis_username; redisContext* redis_context; std::atomic redis_timeout; std::atomic redis_rw_timeout_secs; std::atomic redis_rw_timeout_usecs; std::string redis_prefix = {"wfbl"}; BLWLDBType db_type; std::string ip_ret_msg; std::string login_ret_msg; std::string iplogin_ret_msg; void _addEntry(const std::string& key, time_t seconds, blackwhitelist_t& blackwhitelist, const std::string& reason); bool _checkEntry(const std::string& key, const blackwhitelist_t& blackwhitelist) const; bool _getEntry(const std::string& key, const blackwhitelist_t& blackwhitelist, BlackWhiteListEntry& ret_ble) const; bool _deleteEntry(const std::string& key, blackwhitelist_t& blackwhitelist); time_t _getExpiration(const std::string& key, const blackwhitelist_t& blackwhitelist) const; // returns number of seconds until expiration void _purgeEntries(BLWLType blt, blackwhitelist_t& blackwhitelist, BLWLType bl_type); void addEntryLog(BLWLType blt, const std::string& key, time_t seconds, const std::string& reason) const; void deleteEntryLog(BLWLType blt, const std::string& key) const; void expireEntryLog(BLWLType blt, const std::string& key) const; std::string ipLoginStr(const ComboAddress& ca, const std::string& login) const; bool checkSetupContext(); bool addPersistEntry(const std::string& key, time_t seconds, BLWLType bl_type, const std::string& reason); bool deletePersistEntry(const std::string& key, BLWLType bl_type, blackwhitelist_t& blackwhitelist); BLWLType BLWLNameToType(const std::string& bl_name) const; std::string BLWLTypeToName(BLWLType bl_type) const; }; extern BlackWhiteListDB g_bl_db; extern BlackWhiteListDB g_wl_db; // These are expected to be instantiated elsewhere extern WebHookRunner g_webhook_runner; extern WebHookDB g_webhook_db; weakforced-2.10.2/wforce/dump_entries.cc000066400000000000000000000136611461473602600202200ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #define SYSLOG_NAMES #include #include #include #include #include "minicurl.hh" #include "wforce_exception.hh" #include #include #include "iputils.hh" #include "json11.hpp" #include "base64.hh" #include "sstuff.hh" #include "dolog.hh" using namespace std; using namespace json11; void print_help() { cout<<"Syntax: dump_entries [-u,--uri wforce uri] [-l,--local-addr address] [-p,--local-port port] [-w,--wforce-pwd password] [-f,--filename file] [-h,--help]\n"; cout<<"\n"; cout<<"-u,--uri address Connect to wforce on the specified URI\n"; cout<<"-l,--local-addr address Listen for results on the specified local IP address\n"; cout<<"-p,--local-port port Listen on the specified port\n"; cout<<"-w,--wforce-pwd password The password to authenticate to wforce\n"; cout<<"-f,--filename file Send the results to a file\n"; cout<<"-h,--help Display this helpful message\n"; cout<<"\n"; } bool g_console = true; bool g_docker = false; LogLevel g_loglevel{LogLevel::Info}; int main(int argc, char** argv) { string uri, local_addr, password, filename; int local_port=0; struct option longopts[]={ {"uri", required_argument, 0, 'u'}, {"local-addr", required_argument, 0, 'l'}, {"local-port", required_argument, 0, 'p'}, {"wforce-pwd", required_argument, 0, 'w'}, {"filename", required_argument, 0, 'f'}, {"help", 0, 0, 'h'}, {0,0,0,0} }; int longindex=0; for(;;) { int c=getopt_long(argc, argv, "hu:l:p:w:f:", longopts, &longindex); if(c==-1) break; switch(c) { case 'u': uri = optarg; break; case 'l': local_addr = optarg; break; case 'p': try { local_port = stoi(optarg); } catch (const std::exception& e) { cerr << "Could not parse local port - must be an integer" << endl; exit(EXIT_FAILURE); } break; case 'w': password = optarg; break; case 'f': filename = optarg; break; case 'h': print_help(); exit(EXIT_SUCCESS); break; } } argc-=optind; argv+=optind; if ((uri.empty()) || (local_addr.empty()) || (local_port == 0) || (password.empty())) { cout << "Missing mandatory arguments...\n"; print_help(); exit(EXIT_FAILURE); } // Open file if specified ofstream myfile; if (!filename.empty()) { myfile.open(filename, ios::out | ios::trunc); if (!myfile.is_open()) { cerr << "Could not open " << filename << " for writing." << endl; exit(EXIT_FAILURE); } } // Listen on local IP and port ComboAddress local_ca; try { local_ca = ComboAddress(local_addr, local_port); } catch (const WforceException& e) { cerr << "Error parsing local address and port " << local_addr << ", " << local_port << ". Make sure to use IP addresses not hostnames" << endl; exit(EXIT_FAILURE); } try { int listen_sock = socket(local_ca.sin4.sin_family, SOCK_STREAM, 0); if (listen_sock < 0) throw std::runtime_error("Failed to create socket"); SSetsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 1); SBind(listen_sock, local_ca); SListen(listen_sock, 1024); Socket listen_sock_wrapper(listen_sock); // This ensures the socket gets closed // Connect to the specified wforce address and port // and request to dump entries to our local address and port MiniCurl mc; MiniCurlHeaders mch; string error_msg; mch.insert(make_pair("Authorization", "Basic " + Base64Encode("wforce:"+password))); mch.insert(make_pair("Content-Type", "application/json")); Json::object post_json = {{"dump_host", local_addr}, {"dump_port", local_port}}; if (uri.find("dumpEntries") == string::npos) { uri += "/?command=dumpEntries"; } if (mc.postURL(uri, Json(post_json).dump(), mch, error_msg) != true) { cerr << "Post to wforce URI [" << uri << "] failed, error=" << error_msg; exit(EXIT_FAILURE); } // Finally wait for the dumped entries and print them out ComboAddress remote(local_ca); int fd = SAccept(listen_sock, remote); Socket sock(fd); string buf; try { for (;;) { sock.read(buf); if (buf.empty()) break; if (filename.empty()) cout << buf; else myfile << buf; } } catch (const NetworkError& e) { cerr << e.what() << endl; } } catch (const std::exception& e) { cerr << "dump_entries: error: " << e.what() << endl; exit(EXIT_FAILURE); } catch (const WforceException& e){ cerr << "dump_entries: error: " << e.reason << endl; exit(EXIT_FAILURE); } if (filename.empty()) cout << endl; else myfile << endl; if (myfile.is_open()) myfile.close(); exit(EXIT_SUCCESS); } weakforced-2.10.2/wforce/luastate.hh000066400000000000000000000151321461473602600173510ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/luawrapper/include/LuaContext.hpp" #include "misc.hh" #include #include #include "json11.hpp" #include "login_tuple.hh" #include "customfunc.hh" typedef std::tuple AllowReturn; typedef std::function allow_t; extern allow_t g_allow; typedef std::function report_t; extern report_t g_report; typedef std::function reset_t; extern reset_t g_reset; typedef std::function canonicalize_t; struct CustomFuncMapObject { custom_func_t c_func; bool c_reportSink; }; typedef std::map CustomFuncMap; extern CustomFuncMap g_custom_func_map; typedef std::map CustomGetFuncMap; extern CustomGetFuncMap g_custom_get_func_map; vector> setupLua(bool client, bool allow_report, LuaContext& c_lua, allow_t& allow_func, report_t& report_func, reset_t& reset_func, canonicalize_t& canon_func, CustomFuncMap& custom_func_map, CustomGetFuncMap& custom_get_func_map, const std::string& config); struct LuaThreadContext { LuaContext lua_context; std::mutex lua_mutex; allow_t allow_func; report_t report_func; reset_t reset_func; canonicalize_t canon_func; CustomFuncMap custom_func_map; CustomGetFuncMap custom_get_func_map; }; #define NUM_LUA_STATES 6 class LuaMultiThread { public: LuaMultiThread() : num_states(NUM_LUA_STATES) { LuaMultiThread{num_states}; } LuaMultiThread(unsigned int nstates) : num_states(nstates) { for (unsigned int i = 0; i < num_states; i++) { lua_pool.push_back(std::make_shared()); } lua_read_only = lua_pool; // Make a copy for use by the control thread } LuaMultiThread(const LuaMultiThread&) = delete; LuaMultiThread& operator=(const LuaMultiThread&) = delete; // these are used to setup the allow and report function pointers std::vector>::iterator begin() { return lua_read_only.begin(); } std::vector>::iterator end() { return lua_read_only.end(); } bool reset(const std::string& type, const std::string& login_value, const ComboAddress& ca_value) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the reset function return lt_context->reset_func(type, login_value, ca_value); } AllowReturn allow(const LoginTuple& lt) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the allow function return lt_context->allow_func(lt); } void report(const LoginTuple& lt) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the report function lt_context->report_func(lt); } std::string canonicalize(const std::string& login) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the canonicalize function return lt_context->canon_func(login); } CustomFuncReturn custom_func(const std::string& command, const CustomFuncArgs& cfa, bool& reportSinkReturn) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the custom function for (const auto& i: lt_context->custom_func_map) { if (command.compare(i.first) == 0) { reportSinkReturn = i.second.c_reportSink; return i.second.c_func(cfa); } } return CustomFuncReturn(false, KeyValVector{}); } std::string custom_get_func(const std::string& command) { auto pool_member = getPoolMember(); auto lt_context = pool_member.getLuaContext(); // lock the lua state mutex std::lock_guard lock(lt_context->lua_mutex); // call the custom function for (const auto& i: lt_context->custom_get_func_map) { if (command.compare(i.first) == 0) { return i.second(); } } return string(); } protected: class SharedPoolMember { public: SharedPoolMember(std::shared_ptr ptr, LuaMultiThread* pool) : d_pool_item(ptr), d_pool(pool) {} ~SharedPoolMember() { if (d_pool != nullptr) { d_pool->returnPoolMember(d_pool_item); } } SharedPoolMember(const SharedPoolMember&) = delete; SharedPoolMember& operator=(const SharedPoolMember&) = delete; std::shared_ptr getLuaContext() { return d_pool_item; } private: std::shared_ptr d_pool_item; LuaMultiThread* d_pool; }; SharedPoolMember getPoolMember() { std::lock_guard lock(mutx); auto member = lua_pool.back(); lua_pool.pop_back(); return SharedPoolMember(member, this); } void returnPoolMember(std::shared_ptr my_ptr) { std::lock_guard lock(mutx); lua_pool.push_back(my_ptr); } private: std::vector> lua_pool; std::vector> lua_read_only; unsigned int num_states; std::mutex mutx; }; extern std::shared_ptr g_luamultip; extern int g_num_luastates; weakforced-2.10.2/wforce/replication.cc000066400000000000000000000045471461473602600200360ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "replication.hh" #include "replication_sdb.hh" #include "replication_bl.hh" #include "replication_wl.hh" std::string ReplicationOperation::serialize() const { if (rep_op != nullptr) { std::string bytes = rep_op->serialize(); WforceReplicationMsg msg; msg.set_rep_type(obj_type); msg.set_rep_op(bytes); msg.set_forwarded(forwarded); std::string ret_str; msg.SerializeToString(&ret_str); return ret_str; } return std::string(); } bool ReplicationOperation::unserialize(const std::string& str) { string err; bool retval=true; WforceReplicationMsg msg; if (msg.ParseFromString(str)) { obj_type = msg.rep_type(); if (obj_type == WforceReplicationMsg_RepType_SDBType) { SDBReplicationOperation sdb_op = SDBReplicationOperation(); rep_op = sdb_op.unserialize(msg.rep_op(), retval); } else if (obj_type == WforceReplicationMsg_RepType_BlacklistType) { BLReplicationOperation bl_op = BLReplicationOperation(); rep_op = bl_op.unserialize(msg.rep_op(), retval); } else if (obj_type == WforceReplicationMsg_RepType_WhitelistType) { WLReplicationOperation wl_op = WLReplicationOperation(); rep_op = wl_op.unserialize(msg.rep_op(), retval); } else retval = false; } forwarded = msg.forwarded(); return retval; } void ReplicationOperation::applyOperation() { if (rep_op != nullptr) rep_op->applyOperation(); } weakforced-2.10.2/wforce/replication.hh000066400000000000000000000042221461473602600200360ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "replication.pb.h" #include class AnyReplicationOperation; typedef std::shared_ptr AnyReplicationOperationP; class AnyReplicationOperation { public: virtual ~AnyReplicationOperation() = default; virtual std::string serialize()=0; virtual AnyReplicationOperationP unserialize(const std::string& data, bool& retval)=0; virtual void applyOperation()=0; }; class ReplicationOperation { public: ReplicationOperation() : obj_type(WforceReplicationMsg_RepType_NoneType) {} ReplicationOperation(AnyReplicationOperationP op, WforceReplicationMsg_RepType type) : rep_op(op), obj_type(type) { } ReplicationOperation(const ReplicationOperation&) = delete; ReplicationOperation& operator=(const ReplicationOperation&) = delete; std::string serialize() const; bool unserialize(const std::string& str); void applyOperation(); void setForwarded(bool fwd) { forwarded = fwd; } bool getForwarded() { return forwarded; } WforceReplicationMsg_RepType getType() { return obj_type; } private: bool forwarded = false; AnyReplicationOperationP rep_op; WforceReplicationMsg_RepType obj_type; }; void replicateOperation(const ReplicationOperation& rep_op); weakforced-2.10.2/wforce/replication.proto000066400000000000000000000026041461473602600206040ustar00rootroot00000000000000// replication.proto // Neil Cook // (C) Open-Xchange 2016 // // Protobuf definitions for replication of weakforce DBs // This is somewhat similar to a CmRDT except that currently // weakforced uses UDP rather than a "safe" transport like TCP syntax = "proto2"; message SDBOperation { required string db_name = 1; enum SDBOpType { SDBOpAdd = 0; SDBOpSub = 1; SDBOpReset = 2; SDBOpResetField = 3; SDBOpSyncKey = 4; SDBOpNone = 999; } message SDBTimeWindow { required uint64 timestamp = 1; required bytes tw_string = 2; } message SDBSyncField { required bytes field_name = 1; required uint64 start_time = 2; repeated SDBTimeWindow time_windows = 3; } required SDBOpType op_type = 2; required bytes key = 3; optional bytes field_name = 4; optional bytes str_param = 5; optional uint32 int_param = 6; repeated SDBSyncField sync_fields = 7; } message BLOperation { enum BLOpType { BLAdd = 0; BLDelete = 1; BLNone = 999; } required BLOpType op_type = 1; required uint32 type = 2; required bytes key = 3; optional uint32 ttl = 4; optional bytes reason = 5; } message WforceReplicationMsg { enum RepType { SDBType = 0; BlacklistType = 1; WhitelistType = 2; NoneType = 999; } required RepType rep_type = 1; required bytes rep_op = 2; optional bool forwarded = 3; } weakforced-2.10.2/wforce/replication_bl.cc000066400000000000000000000047411461473602600205070ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "replication_bl.hh" BLReplicationOperation::BLReplicationOperation() { bl_msg.set_op_type(BLOperation_BLOpType_BLNone); } BLReplicationOperation::BLReplicationOperation(BLOperation_BLOpType my_op_type, BLWLType my_bl_type, const std::string& my_key, time_t my_ttl, const std::string& my_reason) { bl_msg.set_op_type(my_op_type); bl_msg.set_type(my_bl_type); bl_msg.set_key(my_key); bl_msg.set_ttl(my_ttl); bl_msg.set_reason(my_reason); } std::string BLReplicationOperation::serialize() { std::string ret_str; bl_msg.SerializeToString(&ret_str); return ret_str; } std::shared_ptr BLReplicationOperation::unserialize(const std::string& str, bool& retval) { retval = true; if (bl_msg.ParseFromString(str)) { BLWLType type = static_cast(bl_msg.type()); return std::make_shared(bl_msg.op_type(), type, bl_msg.key(), bl_msg.ttl(), bl_msg.reason()); } retval = false; return std::make_shared(); } void BLReplicationOperation::applyOperation() { if (bl_msg.op_type() != BLOperation_BLOpType_BLNone) { BLWLType type = static_cast(bl_msg.type()); switch (bl_msg.op_type()) { case BLOperation_BLOpType_BLAdd: g_bl_db.addEntryInternal(bl_msg.key(), bl_msg.ttl(), type, bl_msg.reason(), false); break; case BLOperation_BLOpType_BLDelete: g_bl_db.deleteEntryInternal(bl_msg.key(), type, false); break; default: errlog("applyOperation: invalid blacklist operation type found"); } } } weakforced-2.10.2/wforce/replication_bl.hh000066400000000000000000000026611461473602600205200ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "replication.hh" #include "replication.pb.h" #include "blackwhitelist.hh" class BLReplicationOperation : public AnyReplicationOperation { public: BLReplicationOperation(); BLReplicationOperation(BLOperation_BLOpType op_type, BLWLType bl_type, const std::string& key, time_t ttl, const std::string& reason); std::string serialize(); AnyReplicationOperationP unserialize(const std::string& str, bool& retval); void applyOperation(); protected: BLOperation bl_msg; }; weakforced-2.10.2/wforce/replication_sdb.cc000066400000000000000000000152311461473602600206560ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "replication_sdb.hh" SDBReplicationOperation::SDBReplicationOperation() { sdb_msg.set_op_type(SDBOperation_SDBOpType_SDBOpNone); } SDBReplicationOperation::SDBReplicationOperation(const std::string& my_db_name, SDBOperation_SDBOpType my_op, const std::string& my_key) { sdb_msg.set_db_name(my_db_name); sdb_msg.set_op_type(my_op); sdb_msg.set_key(my_key); } SDBReplicationOperation::SDBReplicationOperation(const std::string& my_db_name, SDBOperation_SDBOpType my_op, const std::string& my_key, const std::string& my_field_name, const std::string& my_s) : SDBReplicationOperation(my_db_name, my_op, my_key) { sdb_msg.set_field_name(my_field_name); sdb_msg.set_str_param(my_s); } SDBReplicationOperation::SDBReplicationOperation(const std::string& my_db_name, SDBOperation_SDBOpType my_op, const std::string& my_key, const std::string& my_field_name, int my_a) : SDBReplicationOperation(my_db_name, my_op, my_key) { sdb_msg.set_field_name(my_field_name); sdb_msg.set_int_param(my_a); } SDBReplicationOperation::SDBReplicationOperation(const std::string& my_db_name, SDBOperation_SDBOpType my_op, const std::string& my_key, const std::string& my_field_name, const std::string& my_s, int my_a) : SDBReplicationOperation(my_db_name, my_op, my_key) { sdb_msg.set_field_name(my_field_name); sdb_msg.set_str_param(my_s); sdb_msg.set_int_param(my_a); } SDBReplicationOperation::SDBReplicationOperation(const std::string& my_db_name, SDBOperation_SDBOpType my_op, const std::string& my_key, const std::string& my_field_name) : SDBReplicationOperation(my_db_name, my_op, my_key) { sdb_msg.set_field_name(my_field_name); } SDBReplicationOperation::SDBReplicationOperation(const std::string& my_db_name, SDBOperation_SDBOpType my_op, const std::string& my_key, const TWStatsDBDumpEntry& my_entry) : SDBReplicationOperation(my_db_name, my_op, my_key) { for (auto i = my_entry.begin(); i != my_entry.end(); ++i) { auto my_sync = sdb_msg.add_sync_fields(); my_sync->set_field_name(i->first); my_sync->set_start_time(i->second.first); for (auto j = i->second.second.begin(); j != i->second.second.end(); ++j) { auto my_tw = my_sync->add_time_windows(); my_tw->set_timestamp(j->first); my_tw->set_tw_string(j->second.str()); } } } std::string SDBReplicationOperation::serialize() { std::string ret_str; sdb_msg.SerializeToString(&ret_str); return ret_str; } AnyReplicationOperationP SDBReplicationOperation::unserialize(const std::string& str, bool& retval) { retval = true; if (sdb_msg.ParseFromString(str)) { return std::make_shared(std::move(sdb_msg)); } retval = false; return std::make_shared(); } void SDBReplicationOperation::applyOperation() { if (sdb_msg.op_type() != SDBOperation_SDBOpType_SDBOpNone) { TWStringStatsDBWrapper* statsdb; { std::lock_guard lock(dbMap_mutx); auto it = dbMap.find(sdb_msg.db_name()); if (it != dbMap.end()) statsdb = &(it->second); else { errlog("applyOperation(): Could not find db %s", sdb_msg.db_name()); return; } } TWStatsDBDumpEntry entry; switch(sdb_msg.op_type()) { case SDBOperation_SDBOpType_SDBOpSyncKey: for (int i=0; i < sdb_msg.sync_fields_size(); i++) { TWStatsBufSerial sbs; const auto& sync_field = sdb_msg.sync_fields(i); for (int j=0; j < sync_field.time_windows_size(); j++) { const auto& time_window = sync_field.time_windows(j); sbs.push_back(std::make_pair(time_window.timestamp(), std::stringstream(time_window.tw_string()))); } entry.emplace(std::make_pair(sync_field.field_name(), std::make_pair(sync_field.start_time(), std::move(sbs)))); } statsdb->restoreEntry(sdb_msg.key(), entry); break; case SDBOperation_SDBOpType_SDBOpReset: statsdb->resetInternal(sdb_msg.key(), false); break; case SDBOperation_SDBOpType_SDBOpResetField: statsdb->resetFieldInternal(sdb_msg.key(), sdb_msg.field_name(), false); break; case SDBOperation_SDBOpType_SDBOpAdd: if (sdb_msg.has_field_name() == false) { errlog("applyOperation(): No field_name found for statsdb add"); return; } if ((sdb_msg.has_str_param() == true) && (sdb_msg.has_int_param() == true)) statsdb->addInternal(sdb_msg.key(), sdb_msg.field_name(), sdb_msg.str_param(), sdb_msg.int_param(), false); else if (sdb_msg.has_str_param() == true) statsdb->addInternal(sdb_msg.key(), sdb_msg.field_name(), sdb_msg.str_param(), {}, false); else if (sdb_msg.has_int_param() == true) statsdb->addInternal(sdb_msg.key(), sdb_msg.field_name(), sdb_msg.int_param(), {}, false); else errlog("applyOperation(): Malformed statsdb add"); break; case SDBOperation_SDBOpType_SDBOpSub: if (sdb_msg.has_field_name() == false) { errlog("applyOperation(): No field_name found for statsdb sub"); return; } if (sdb_msg.has_str_param() == true) statsdb->subInternal(sdb_msg.key(), sdb_msg.field_name(), sdb_msg.str_param(), false); else if (sdb_msg.has_int_param() == true) statsdb->subInternal(sdb_msg.key(), sdb_msg.field_name(), sdb_msg.int_param(), false); else errlog("applyOperation(): Malformed statsdb sub"); break; default: errlog("applyOperation(): Invalid operation %d", sdb_msg.op_type()); } } } weakforced-2.10.2/wforce/replication_sdb.hh000066400000000000000000000045431461473602600206740ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "twmap-wrapper.hh" #include "replication.hh" #include "replication.pb.h" class SDBReplicationOperation : public AnyReplicationOperation { public: SDBReplicationOperation(); SDBReplicationOperation(SDBOperation&& so) { sdb_msg = so; } SDBReplicationOperation(const std::string& db_name, SDBOperation_SDBOpType op, const std::string& key); SDBReplicationOperation(const std::string& db_name, SDBOperation_SDBOpType op, const std::string& key, const std::string& field_name, const std::string& s); SDBReplicationOperation(const std::string& db_name, SDBOperation_SDBOpType op, const std::string& key, const std::string& field_name, int a); SDBReplicationOperation(const std::string& db_name, SDBOperation_SDBOpType op, const std::string& key, const std::string& field_name, const std::string& s, int a); SDBReplicationOperation(const std::string& db_name, SDBOperation_SDBOpType op, const std::string& key, const std::string& field_name); SDBReplicationOperation(const std::string& my_db_name, SDBOperation_SDBOpType my_op, const std::string& my_key, const TWStatsDBDumpEntry& my_entry); std::string serialize(); AnyReplicationOperationP unserialize(const std::string& str, bool& retval); void applyOperation(); private: SDBOperation sdb_msg; }; weakforced-2.10.2/wforce/replication_wl.cc000066400000000000000000000036541461473602600205360ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "replication_wl.hh" std::shared_ptr WLReplicationOperation::unserialize(const std::string& str, bool& retval) { retval = true; if (bl_msg.ParseFromString(str)) { BLWLType type = static_cast(bl_msg.type()); return std::make_shared(bl_msg.op_type(), type, bl_msg.key(), bl_msg.ttl(), bl_msg.reason()); } retval = false; return std::make_shared(); } void WLReplicationOperation::applyOperation() { if (bl_msg.op_type() != BLOperation_BLOpType_BLNone) { BLWLType type = static_cast(bl_msg.type()); switch (bl_msg.op_type()) { case BLOperation_BLOpType_BLAdd: g_wl_db.addEntryInternal(bl_msg.key(), bl_msg.ttl(), type, bl_msg.reason(), false); break; case BLOperation_BLOpType_BLDelete: g_wl_db.deleteEntryInternal(bl_msg.key(), type, false); break; default: errlog("applyOperation: invalid whitelist operation type found"); } } } weakforced-2.10.2/wforce/replication_wl.hh000066400000000000000000000027671461473602600205540ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "replication.hh" #include "replication.pb.h" #include "blackwhitelist.hh" #include "replication_bl.hh" class WLReplicationOperation : public BLReplicationOperation { public: WLReplicationOperation() : BLReplicationOperation() { } WLReplicationOperation(BLOperation_BLOpType op_type, BLWLType wl_type, const std::string& key, time_t ttl, const std::string& reason) : BLReplicationOperation(op_type, wl_type, key, ttl, reason) { } AnyReplicationOperationP unserialize(const std::string& str, bool& retval); void applyOperation(); }; weakforced-2.10.2/wforce/twmap-wrapper.cc000066400000000000000000000344321461473602600203270ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "twmap-wrapper.hh" #include "replication.hh" #include "replication_sdb.hh" #include "replication.pb.h" TWStringStatsDBWrapper::TWStringStatsDBWrapper(const std::string& name, int w_size, int n_windows, int n_shards) : window_size(w_size), num_windows(n_windows), num_shards(n_shards), db_name(name) { sdbvp = std::make_shared>>>(); for (int i=0; ipush_back(std::make_shared>(name, w_size, n_windows)); } replicated = std::make_shared(false); } TWStringStatsDBWrapper::TWStringStatsDBWrapper(const std::string& name, int window_size, int num_windows) : TWStringStatsDBWrapper(name, window_size, num_windows, 1) { } TWStringStatsDBWrapper::TWStringStatsDBWrapper(const std::string& name, int window_size, int num_windows, const std::vector>& fmvec, int num_shards) : TWStringStatsDBWrapper(name, window_size, num_windows, num_shards) { (void)setFields(fmvec); } TWStringStatsDBWrapper::TWStringStatsDBWrapper(const std::string& name, int window_size, int num_windows, const std::vector>& fmvec) : TWStringStatsDBWrapper(name, window_size, num_windows, fmvec, 1) { } void TWStringStatsDBWrapper::enableReplication() { *replicated = true; } void TWStringStatsDBWrapper::disableReplication() { *replicated = false; } bool TWStringStatsDBWrapper::getReplicationStatus() { return *replicated; } std::string TWStringStatsDBWrapper::getDBName() { return db_name; } bool TWStringStatsDBWrapper::setFields(const std::vector>& fmvec) { for (const auto& sdbp : *sdbvp) { FieldMap fm; for(const auto& f : fmvec) { fm.insert(std::make_pair(f.first, f.second)); } sdbp->setFields(fm); } return true; } bool TWStringStatsDBWrapper::setFields(const FieldMap& fm) { for (const auto& sdbp : *sdbvp) { sdbp->setFields(fm); } return true; } const FieldMap& TWStringStatsDBWrapper::getFields() { // Just return the fields from the first return(sdbvp->at(0)->getFields()); } void TWStringStatsDBWrapper::setv4Prefix(uint8_t bits) { uint8_t v4_prefix = bits > 32 ? 32 : bits; for (const auto& sdbp : *sdbvp) { sdbp->setv4Prefix(v4_prefix); } } void TWStringStatsDBWrapper::setv6Prefix(uint8_t bits) { uint8_t v6_prefix = bits > 128 ? 128 : bits; for (const auto& sdbp : *sdbvp) { sdbp->setv6Prefix(v6_prefix); } } std::string TWStringStatsDBWrapper::getStringKey(const TWKeyType vkey) { if (vkey.which() == 0) { return boost::get(vkey); } else if (vkey.which() == 1) { return std::to_string(boost::get(vkey)); } else if (vkey.which() == 2) { const ComboAddress ca = boost::get(vkey); if (ca.isIpv4()) { uint8_t v4_prefix = sdbvp->at(0)->getv4Prefix(); return Netmask(ca, v4_prefix).toStringNetwork(); } else if (ca.isIpv6()) { uint8_t v6_prefix = sdbvp->at(0)->getv6Prefix(); return Netmask(ca, v6_prefix).toStringNetwork(); } } return std::string{}; } void TWStringStatsDBWrapper::add(const TWKeyType vkey, const std::string& field_name, const boost::variant& param1, boost::optional param2) { addInternal(vkey, field_name, param1, param2, true); } const uint32_t tw_hash_seed = 623; unsigned int TWStringStatsDBWrapper::getShardIndex(const std::string& key) { uint32_t hash; MurmurHash3_x86_32(key.c_str(), key.length(), tw_hash_seed, (void*) &hash); return hash % num_shards; } void TWStringStatsDBWrapper::addInternal(const TWKeyType vkey, const std::string& field_name, const boost::variant& param1, boost::optional param2, bool replicate) { std::string key = getStringKey(vkey); std::shared_ptr sdb_rop; std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); // we're using the three argument version if (param2) { if ((param1.which() != 0) && (param1.which() != 2)) return; else if (param1.which() == 0) { std::string mystr = boost::get(param1); sdbp->add(key, field_name, mystr, *param2); if ((replicate == true) && (*replicated == true)) sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpAdd, key, field_name, mystr, *param2); } else if (param1.which() == 2) { ComboAddress ca = boost::get(param1); sdbp->add(key, field_name, ca.toString(), *param2); if ((replicate == true) && (*replicated == true)) sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpAdd, key, field_name, ca.toString(), *param2); } } else { if (param1.which() == 0) { std::string mystr = boost::get(param1); sdbp->add(key, field_name, mystr); if ((replicate == true) && (*replicated == true)) sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpAdd, key, field_name, mystr); } else if (param1.which() == 1) { int myint = boost::get(param1); sdbp->add(key, field_name, myint); if ((replicate == true) && (*replicated == true)) sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpAdd, key, field_name, myint); } else if (param1.which() == 2) { ComboAddress ca = boost::get(param1); sdbp->add(key, field_name, ca.toString()); if ((replicate == true) && (*replicated == true)) sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpAdd, key, field_name, ca.toString()); } } if ((replicate == true) && (*replicated == true)) { ReplicationOperation rep_op(sdb_rop, WforceReplicationMsg_RepType_SDBType); // this actually does the replication replicateOperation(rep_op); } } void TWStringStatsDBWrapper::sub(const TWKeyType vkey, const std::string& field_name, const boost::variant& val) { subInternal(vkey, field_name, val, true); } void TWStringStatsDBWrapper::subInternal(const TWKeyType vkey, const std::string& field_name, const boost::variant& val, bool replicate) { std::string key = getStringKey(vkey); std::shared_ptr sdb_rop; std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); if (val.which() == 0) { std::string mystr = boost::get(val); sdbp->sub(key, field_name, mystr); if ((replicate == true) && (*replicated == true)) sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpSub, key, field_name, mystr); } else { int myint = boost::get(val); sdbp->sub(key, field_name, myint); if ((replicate == true) && (*replicated == true)) sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpSub, key, field_name, myint); } if ((replicate == true) && (*replicated == true)) { ReplicationOperation rep_op(sdb_rop, WforceReplicationMsg_RepType_SDBType); // this actually does the replication replicateOperation(rep_op); } } int TWStringStatsDBWrapper::get(const TWKeyType vkey, const std::string& field_name, const boost::optional> param1) { std::string key = getStringKey(vkey); std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); if (param1) { if (param1->which() == 0) { std::string s = boost::get(*param1); return sdbp->get(key, field_name, s); } else { ComboAddress ca = boost::get(*param1); return sdbp->get(key, field_name, ca.toString()); } } else { return sdbp->get(key, field_name); } } int TWStringStatsDBWrapper::get_current(const TWKeyType vkey, const std::string& field_name, const boost::optional> param1) { std::string key = getStringKey(vkey); std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); if (param1) { if (param1->which() == 0) { std::string s = boost::get(*param1); return sdbp->get_current(key, field_name, s); } else { ComboAddress ca = boost::get(*param1); return sdbp->get_current(key, field_name, ca.toString()); } } else { return sdbp->get_current(key, field_name); } } std::vector TWStringStatsDBWrapper::get_windows(const TWKeyType vkey, const std::string& field_name, const boost::optional> param1) { std::vector retvec; std::string key = getStringKey(vkey); std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); if (param1) { if (param1->which() == 0) { std::string s = boost::get(*param1); (void) sdbp->get_windows(key, field_name, s, retvec); } else { ComboAddress ca = boost::get(*param1); (void) sdbp->get_windows(key, field_name, ca.toString(), retvec); } } else (void) sdbp->get_windows(key, field_name, retvec); return retvec; // copy } bool TWStringStatsDBWrapper::get_all_fields(const TWKeyType vkey, std::vector>& ret_vec) { std::string key = getStringKey(vkey); std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); return sdbp->get_all_fields(key, ret_vec); } void TWStringStatsDBWrapper::reset(const TWKeyType vkey) { resetInternal(vkey, true); } void TWStringStatsDBWrapper::resetInternal(const TWKeyType vkey, bool replicate) { std::string key = getStringKey(vkey); std::shared_ptr sdb_rop; std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); sdbp->reset(key); if ((replicate == true) && (*replicated == true)) { sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpReset, key); ReplicationOperation rep_op(sdb_rop, WforceReplicationMsg_RepType_SDBType); // this actually does the replication replicateOperation(rep_op); } } void TWStringStatsDBWrapper::resetField(const TWKeyType vkey, const std::string& field_name) { resetFieldInternal(vkey, field_name, true); } void TWStringStatsDBWrapper::resetFieldInternal(const TWKeyType vkey, const std::string& field_name, bool replicate) { std::string key = getStringKey(vkey); std::shared_ptr sdb_rop; std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); sdbp->reset_field(key, field_name); if ((replicate == true) && (*replicated == true)) { sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpResetField, key, field_name); ReplicationOperation rep_op(sdb_rop, WforceReplicationMsg_RepType_SDBType); // this actually does the replication replicateOperation(rep_op); } } unsigned int TWStringStatsDBWrapper::get_size() { unsigned int size=0; for (const auto& sdbp : *sdbvp) { size += sdbp->get_size(); } return size; } void TWStringStatsDBWrapper::set_size_soft(unsigned int size) { for (const auto& sdbp : *sdbvp) { sdbp->set_map_size_soft(size/num_shards); } } void TWStringStatsDBWrapper::set_expire_sleep(unsigned int ms) { for (const auto& sdbp : *sdbvp) { sdbp->set_expire_sleep(ms); } } unsigned int TWStringStatsDBWrapper::get_max_size() { unsigned int max_size=0; for (const auto& sdbp : *sdbvp) { max_size += sdbp->get_max_size(); } return max_size; } int TWStringStatsDBWrapper::windowSize() { return window_size; } int TWStringStatsDBWrapper::numWindows() { return num_windows; } int TWStringStatsDBWrapper::numShards() { return num_shards; } VTWPtr::const_iterator TWStringStatsDBWrapper::begin() const { return sdbvp->cbegin(); } VTWPtr::const_iterator TWStringStatsDBWrapper::end() const { return sdbvp->cend(); } std::list::const_iterator TWStringStatsDBWrapper::startDBDump(VTWPtr::const_iterator& sdbp) const { return (*sdbp)->startDBDump(); } bool TWStringStatsDBWrapper::DBDumpEntry(VTWPtr::const_iterator& sdbp, std::list::const_iterator& i, TWStatsDBDumpEntry& entry, std::string& key) const { return (*sdbp)->DBDumpEntry(i, entry, key); } bool TWStringStatsDBWrapper::DBGetEntry(VTWPtr::const_iterator& sdbp, std::list::const_iterator& i, TWStatsDBEntry& entry, std::string& key) const { return (*sdbp)->DBGetEntry(i, entry, key); } const std::list::const_iterator TWStringStatsDBWrapper::DBDumpIteratorEnd(VTWPtr::const_iterator& sdbp) const { return (*sdbp)->DBDumpIteratorEnd(); } void TWStringStatsDBWrapper::endDBDump(VTWPtr::const_iterator& sdbp) const { (*sdbp)->endDBDump(); } void TWStringStatsDBWrapper::restoreEntry(const std::string& key, std::map>& entry) { std::shared_ptr> sdbp = sdbvp->at(getShardIndex(key)); sdbp->restoreEntry(key, entry); } void TWStringStatsDBWrapper::startExpireThread() { for (auto& sdbp : *sdbvp) { thread t(TWStatsDB::twExpireThread, sdbp); t.detach(); } } weakforced-2.10.2/wforce/twmap-wrapper.hh000066400000000000000000000115051461473602600203350ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "twmap.hh" typedef boost::variant TWKeyType; using VTWPtr = std::vector>>; // This is a Lua-friendly wrapper to the Stats DB // All db state is stored in the TWStatsDB shared pointer because passing back/forth to Lua means copies class TWStringStatsDBWrapper { private: std::shared_ptr sdbvp; std::shared_ptr replicated; int window_size=0; int num_windows=0; int num_shards=1; std::string db_name; protected: unsigned int getShardIndex(const std::string& key); public: TWStringStatsDBWrapper(const std::string& name, int window_size, int num_windows); TWStringStatsDBWrapper(const std::string& name, int window_size, int num_windows, const std::vector>& fmvec); TWStringStatsDBWrapper(const std::string& name, int window_size, int num_windows, int num_shards); TWStringStatsDBWrapper(const std::string& name, int window_size, int num_windows, const std::vector>& fmvec, int num_shards); void enableReplication(); void disableReplication(); bool getReplicationStatus(); std::string getDBName(); bool setFields(const std::vector>& fmvec); bool setFields(const FieldMap& fm); const FieldMap& getFields(); void setv4Prefix(uint8_t bits); void setv6Prefix(uint8_t bits); std::string getStringKey(const TWKeyType vkey); void add(const TWKeyType vkey, const std::string& field_name, const boost::variant& param1, boost::optional param2); void addInternal(const TWKeyType vkey, const std::string& field_name, const boost::variant& param1, boost::optional param2, bool replicate=true); void sub(const TWKeyType vkey, const std::string& field_name, const boost::variant& val); void subInternal(const TWKeyType vkey, const std::string& field_name, const boost::variant& val, bool replicate=true); int get(const TWKeyType vkey, const std::string& field_name, const boost::optional> param1); int get_current(const TWKeyType vkey, const std::string& field_name, const boost::optional> param1); std::vector get_windows(const TWKeyType vkey, const std::string& field_name, const boost::optional> param1); bool get_all_fields(const TWKeyType vkey, std::vector>& ret_vec); void reset(const TWKeyType vkey); void resetField(const TWKeyType vkey, const std::string& field_name); void resetInternal(const TWKeyType vkey, bool replicate=true); void resetFieldInternal(const TWKeyType vkey, const std::string& field_name, bool replicate=true); unsigned int get_size(); unsigned int get_max_size(); void set_size_soft(unsigned int size); void set_expire_sleep(unsigned int ms); int windowSize(); int numWindows(); int numShards(); VTWPtr::const_iterator begin() const; VTWPtr::const_iterator end() const; std::list::const_iterator startDBDump(VTWPtr::const_iterator& sdbp) const; bool DBDumpEntry(VTWPtr::const_iterator& sdbp, std::list::const_iterator& i, TWStatsDBDumpEntry& entry, std::string& key) const; bool DBGetEntry(VTWPtr::const_iterator& sdbp, std::list::const_iterator& i, TWStatsDBEntry& entry, std::string& key) const; const std::list::const_iterator DBDumpIteratorEnd(VTWPtr::const_iterator& sdbp) const; void endDBDump(VTWPtr::const_iterator& sdbp) const; void restoreEntry(const std::string& key, TWStatsDBDumpEntry& entry); void startExpireThread(); }; extern std::mutex dbMap_mutx; extern std::map dbMap; weakforced-2.10.2/wforce/twmap.cc000066400000000000000000000021161461473602600166430ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "twmap-wrapper.hh" #include #include std::mutex dbMap_mutx; std::map dbMap; weakforced-2.10.2/wforce/wforce-common-lua.cc000066400000000000000000000140161461473602600210470ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include "iputils.hh" #include "sstuff.hh" #include "wforce_ns.hh" #include "dolog.hh" #include "webhook.hh" #include "wforce-webserver.hh" #include "wforce-common-lua.hh" #include using std::thread; extern WforceWebserver g_webserver; extern ComboAddress g_serverControl; extern WebHookRunner g_webhook_runner; extern curlTLSOptions g_curl_tls_options; void controlThread(int fd, ComboAddress local); // These are Lua functions common to both wforce and trackalert (and crucially which have no differences in their // implementation) void setupWforceCommonLua(bool client, bool multi_lua, LuaContext& c_lua, std::vector>* launchwork) { c_lua.writeFunction("shutdown", []() { _exit(0);} ); if (!multi_lua) { c_lua.writeFunction("addListener", [client, launchwork](const std::string& address, bool useSSL, const std::string cert_file, const std::string& key_file, std::vector> opts) { if (client) return; ComboAddress local; try { local = ComboAddress(address); } catch (const WforceException& e) { errlog("addListener() error parsing address/port [%s]. Make sure to use IP addresses not hostnames", address); return; } if (useSSL) { // Check that the certificate and private key exist and are readable auto check_file = [](const std::string& filetype, const std::string& filename) -> bool { boost::filesystem::path p(filename); if (boost::filesystem::exists(p)) { if (!boost::filesystem::is_regular_file(p)) { errlog("addListener() %s file [%s] is not a regular file", filetype, filename); return false; } } else { errlog("addListener() %s file [%s] does not exist or cannot be read", filetype, filename); return false; } return true; }; if (!(check_file("certificate", cert_file) && check_file("key", key_file))) { return; } } g_webserver.addListener(local.toString(), local.getPort(), useSSL, cert_file, key_file, false, opts); auto launch =[]() { thread t(WforceWebserver::start, &g_webserver); t.detach(); }; if (launchwork) launchwork->push_back(launch); else launch(); }); } else { c_lua.writeFunction("addListener", [](const std::string& address, bool useSSL, const std::string cert_file, const std::string& private_key, std::vector> opts) { }); } if (!multi_lua) { c_lua.writeFunction("controlSocket", [client, launchwork](const std::string& str) { ComboAddress local; try { local = ComboAddress(str, 4004); } catch (const WforceException& e) { errlog("controlSocket() error parsing address/port [%s]. Make sure to use IP addresses not hostnames", str); return; } if(client) { g_serverControl = local; return; } try { int sock = socket(local.sin4.sin_family, SOCK_STREAM, 0); if (sock < 0) throw std::runtime_error("Failed to create control socket"); SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1); SBind(sock, local); SListen(sock, 5); auto launch=[sock, local]() { thread t(controlThread, sock, local); t.detach(); }; if(launchwork) launchwork->push_back(launch); else launch(); } catch(std::exception& e) { errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what()); _exit(EXIT_FAILURE); } }); } else { c_lua.writeFunction("controlSocket", [](const std::string& str) { }); } if (!multi_lua) { c_lua.writeFunction("disableCurlPeerVerification", []() { g_curl_tls_options.verifyPeer = false; g_webhook_runner.disablePeerVerification(); }); } else { c_lua.writeFunction("disableCurlPeerVerification", []() { }); } if (!multi_lua) { c_lua.writeFunction("disableCurlHostVerification", []() { g_curl_tls_options.verifyHost = false; g_webhook_runner.disableHostVerification(); }); } else { c_lua.writeFunction("disableCurlHostVerification", []() { }); } if (!multi_lua) { c_lua.writeFunction("setCurlCABundleFile", [](const std::string& filename) { g_curl_tls_options.caCertBundleFile = filename; g_webhook_runner.setCACertBundleFile(filename); }); } else { c_lua.writeFunction("setCurlCABundleFile", [](const std::string& filename) { }); } if (!multi_lua) { c_lua.writeFunction("setCurlClientCertAndKey", [](const std::string& certfile, const std::string& keyfile) { g_curl_tls_options.clientCertFile = certfile; g_curl_tls_options.clientKeyFile = keyfile; g_webhook_runner.setClientCertAndKey(certfile, keyfile); }); } else { c_lua.writeFunction("setCurlClientCertAndKey", [](const std::string& certfile, const std::string& keyfile) { }); } } weakforced-2.10.2/wforce/wforce-common-lua.hh000066400000000000000000000021731461473602600210620ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/luawrapper/include/LuaContext.hpp" void setupWforceCommonLua(bool client, bool multi_lua, LuaContext& c_lua, std::vector>* launchwork);weakforced-2.10.2/wforce/wforce-lua.cc000066400000000000000000001204221461473602600175600ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include "wforce.hh" #include "wforce-common-lua.hh" #include "wforce-replication.hh" #include #include "dolog.hh" #include "sodcrypto.hh" #include "base64.hh" #include "twmap-wrapper.hh" #include "blackwhitelist.hh" #include "luastate.hh" #include "perf-stats.hh" #include "wforce-web.hh" #include "common-lua.hh" #include #include #include #include "wforce-prometheus.hh" #include "drogon/drogon.h" #ifdef HAVE_GEOIP #include "wforce-geoip.hh" #endif #ifdef HAVE_MMDB #include "wforce-geoip2.hh" #endif #ifdef HAVE_GETDNS #include "dns_lookup.hh" #endif using std::thread; static vector>* launchWork; ComboAddress g_sibling_listen_addr; std::vector> getWLBLKeys(const std::vector& blv, const char* key_name) { std::vector> ret_vec; for (const auto& i : blv) { auto key_val = static_cast(i.key); std::map bl_map = {{key_name, key_val }, {"expiration", boost::posix_time::to_simple_string(i.expiration)}, {"reason", i.reason}}; ret_vec.emplace_back(bl_map); } return ret_vec; } // lua functions are split into three groups: // 1) Those which are only applicable as "config/setup" (single global lua state) (they are defined as blank/empty functions otherwise) // 2) Those which are only applicable inside the "allow", "report" (and similar) functions, where there are multiple lua states running in different threads, which are defined as empty otherwise // 3) Those which are applicable to both states // Functions that are in 2) or 3) MUST be thread-safe. The rest not as they are called at startup. // We have a single lua config file for historical reasons, hence the somewhat complex structure of this function // The Lua state and type is passed via "multi_lua" (true means it's one of the multi-lua states used for allow/report etc., false means it's the global lua config state) vector> setupLua(bool client, bool multi_lua, LuaContext& c_lua, allow_t& allow_func, report_t& report_func, reset_t& reset_func, canonicalize_t& canon_func, CustomFuncMap& custom_func_map, CustomGetFuncMap& custom_get_func_map, const std::string& config) { launchWork= new vector>(); setupCommonLua(client, multi_lua, c_lua, config); setupWforceCommonLua(client, multi_lua, c_lua, launchWork); if (!multi_lua && !client) { c_lua.writeFunction("addReportSink", [](const std::string& address) { ComboAddress ca; try { ca = ComboAddress(address, 4501); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("addReportSink() error parsing address/port [%s]. Make sure to use IP addresses not hostnames", address); g_outputBuffer += (fmt % "addReportSink(): Error parsing address/port. Make sure to use IP addresses not hostnames" % e.reason).str(); return; } g_report_sinks.modify([ca](vector>& v) { v.push_back(std::make_shared(ca)); }); const std::string notice = "addReportSinks() is deprecated, and will be removed in a future release. Use addNamedReportSink() instead"; errlog(notice.c_str()); g_outputBuffer += notice + "\n"; }); } else { c_lua.writeFunction("addReportSink", [](const std::string& address) { }); } if (!multi_lua && !client) { c_lua.writeFunction("setReportSinks", [](const vector>& parts) { vector> v; for(const auto& p : parts) { try { v.push_back(std::make_shared(ComboAddress(p.second, 4501))); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("setReportSinks() error parsing address/port [%s]. Make sure to use IP addresses not hostnames", p.second); g_outputBuffer += (fmt % "setReportSinks(): Error parsing address/port. Make sure to use IP addresses not hostnames" % e.reason).str(); return; } } g_report_sinks.setState(v); const std::string notice = "setReportSinks() is deprecated, and will be removed in a future release. Use setNamedReportSinks() instead"; errlog(notice.c_str()); g_outputBuffer += notice + "\n"; }); } else { c_lua.writeFunction("setReportSinks", [](const vector>& parts) { }); } if (!multi_lua && !client) { c_lua.writeFunction("addNamedReportSink", [](const std::string& sink_name, const std::string& address) { ComboAddress ca; try { ca = ComboAddress(address, 4501); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("addNamedReportSink() error parsing address/port [%s] for report sink [%s]. Make sure to use IP addresses not hostnames", address, sink_name); g_outputBuffer += (fmt % "addNamedReportSink(): Error parsing address/port. Make sure to use IP addresses not hostnames" % e.reason).str(); return; } g_named_report_sinks.modify([sink_name, ca](std::map>, std::vector>>>& m) { const auto& mp = m.find(sink_name); if (mp != m.end()) mp->second.second.push_back(std::make_shared(ca)); else { vector> vec; auto atomp = std::make_shared>(0); vec.push_back(std::make_shared(ca)); m.emplace(std::make_pair(sink_name, std::make_pair(atomp, std::move(vec)))); } }); }); } else { c_lua.writeFunction("addNamedReportSink", [](const std::string& sink_name, const std::string& address) { }); } if (!multi_lua && !client) { c_lua.writeFunction("setNamedReportSinks", [](const std::string& sink_name, const vector>& parts) { g_named_report_sinks.modify([sink_name, parts](std::map>, std::vector>>>& m) { vector> v; for(const auto& p : parts) { try { v.push_back(std::make_shared(ComboAddress(p.second, 4501))); } catch (const WforceException& e) { boost::format fmt("%s (%s)\n"); errlog("setNamedReportSinks() error parsing address/port [%s] for report sink [%s]. Make sure to use IP addresses not hostnames", p.second, sink_name); g_outputBuffer += (fmt % "setNamedReportSinks(): Error parsing address/port. Make sure to use IP addresses not hostnames" % e.reason).str(); return; } } const auto& mp = m.find(sink_name); if (mp != m.end()) mp->second.second = v; else { auto atomp = std::make_shared>(0); m.emplace(std::make_pair(sink_name, std::make_pair(atomp, std::move(v)))); } }); }); } else { c_lua.writeFunction("setNamedReportSinks", [](const std::string& sink_name, const vector>& parts) { }); } if (!multi_lua && !client) { c_lua.writeFunction("addSyncHost", [](const std::string& address, const std::string password, const std::string& sync_address, const std::string& webserver_address) { try { g_sync_data.sync_hosts.push_back(make_pair(address, password)); g_sync_data.sibling_listen_addr = ComboAddress(sync_address, 4001); g_sync_data.webserver_listen_addr = webserver_address; } catch (const WforceException& e) { const std::string errstr = (boost::format("%s (%s)\n") % "addSyncHost(): Error parsing address/port. Make sure to use IP addresses not hostnames" % e.reason).str(); errlog(errstr.c_str()); g_outputBuffer += errstr; return; } }); } else { c_lua.writeFunction("addSyncHost", [](const std::string& address, const std::string password, const std::string& sync_address, const std::string& webserver_address) {}); } if (!multi_lua) { c_lua.writeFunction("setMinSyncHostUptime", [](unsigned int uptime) { g_sync_data.min_sync_host_uptime = uptime; }); } else { c_lua.writeFunction("setMinSyncHostUptime", [](unsigned int uptime) {}); } if (!multi_lua) { c_lua.writeFunction("setMaxSiblingQueueSize", [](unsigned int size) { g_replication.setMaxSiblingRecvQueueSize(size); setMaxSiblingSendQueueSize(static_cast(size)); }); } else { c_lua.writeFunction("setMaxSiblingQueueSize", [](unsigned int size) {}); } if (!multi_lua && !client) { c_lua.writeFunction("addSibling", [](const std::string& address) { (void)addSibling(address, g_replication.getSiblings(), g_outputBuffer); }); } else { c_lua.writeFunction("addSibling", [](const std::string& address) { }); } if (!multi_lua && !client) { c_lua.writeFunction("addSiblingWithKey", [](const std::string& address, const std::string& key) { (void)addSiblingWithKey(address, g_replication.getSiblings(), g_outputBuffer, key); }); } else { c_lua.writeFunction("addSiblingWithKey", [](const std::string& address, const std::string& key) { }); } if (!multi_lua && !client) { c_lua.writeFunction("removeSibling", [](const std::string& address) { removeSibling(address, g_replication.getSiblings(), g_outputBuffer); }); } else { c_lua.writeFunction("removeSibling", [](const std::string& address) { }); } if (!multi_lua && !client) { c_lua.writeFunction("setSiblingsWithKey", [](const vector>>>& parts) { (void)setSiblingsWithKey(parts, g_replication.getSiblings(), g_outputBuffer); }); } else { c_lua.writeFunction("setSiblingsWithKey", [](const vector>>>& parts) { }); } if (!multi_lua && !client) { c_lua.writeFunction("setSiblings", [](const vector>& parts) { (void)setSiblings(parts, g_replication.getSiblings(), g_outputBuffer); }); } else { c_lua.writeFunction("setSiblings", [](const vector>& parts) { }); } if (!multi_lua && !client) { c_lua.writeFunction("setSiblingConnectTimeout", [](int timeout_ms) { setSiblingConnectTimeout(timeout_ms); }); } else { c_lua.writeFunction("setSiblingConnectTimeout", [](int timeout_ms) { }); } if (!multi_lua && !client) { c_lua.writeFunction("siblingListener", [](const std::string& address) { ComboAddress ca; try { ca = ComboAddress(address, 4001); } catch (const WforceException& e) { const std::string errstr = (boost::format("%s [%s]. %s (%s)\n") % "addSibling() error parsing address/port" % address % "Make sure to use IP addresses not hostnames" % e.reason).str(); errlog(errstr.c_str()); g_outputBuffer += errstr; return; } g_sibling_listen_addr = ca; auto launch = [ca]() { auto siblings = g_replication.getSiblings().getLocal(); for(auto& s : *siblings) { s->checkIgnoreSelf(ca); } thread t1([ca]() { g_replication.receiveReplicationOperations(ca); }); t1.detach(); thread t2([ca]() { g_replication.receiveReplicationOperationsTCP(ca); }); t2.detach(); }; if(launchWork) launchWork->push_back(launch); else launch(); }); } else { c_lua.writeFunction("siblingListener", [](const std::string& address) { }); } if (!multi_lua) { c_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password) { if (client) return; warnlog("Warning - webserver() configuration command is deprecated in favour of addListener() and will be removed in a future version"); ComboAddress local; try { local = ComboAddress(address); } catch (const WforceException& e) { errlog("webserver() error parsing address/port [%s]. Make sure to use IP addresses not hostnames", address); return; } g_sync_data.webserver_password = password; g_webserver.setBasicAuthPassword(password); g_webserver.addSimpleListener(local.toString(), local.getPort()); auto launch =[]() { thread t(WforceWebserver::start, &g_webserver); t.detach(); }; if (launchWork) launchWork->push_back(launch); else launch(); }); } else { c_lua.writeFunction("webserver", [](const std::string& address, const std::string& password) { }); } if (!multi_lua) { c_lua.writeFunction("setWebserverPassword", [](const std::string& password) { g_webserver.setBasicAuthPassword(password); g_sync_data.webserver_password = password; }); } else { c_lua.writeFunction("setWebserverPassword", [](const std::string& password) { }); } if (!multi_lua) { c_lua.writeFunction("stats", []() { boost::format fmt("%d reports, %d allow-queries (%d denies)\n"); g_outputBuffer = (fmt % g_stats.reports % g_stats.allows % g_stats.denieds).str(); }); } else { c_lua.writeFunction("stats", []() { }); } if (!multi_lua) { c_lua.writeFunction("siblings", []() { auto siblings = g_replication.getSiblings().getCopy(); boost::format fmt("%-35s %-15d %-14d %-15d %-14d %s\n"); g_outputBuffer= (fmt % "Address" % "Send Successes" % "Send Failures" % "Rcv Successes" % "Rcv Failures" % "Note").str(); for(const auto& s : siblings) { std::string addr = s->rem.toStringWithPort() + " " + Sibling::protocolToString(s->proto); g_outputBuffer += (fmt % addr % s->success % s->failures % s->rcvd_success % s->rcvd_fail % (s->d_ignoreself ? "Self" : "") ).str(); } }); } else { c_lua.writeFunction("siblings", []() { }); } if (!multi_lua) { c_lua.writeFunction("showReportSinks", []() { auto rsinks = g_report_sinks.getCopy(); boost::format fmt("%-35s %-10d %-9d\n"); g_outputBuffer= (fmt % "Address" % "Successes" % "Failures").str(); for(const auto& s : rsinks) g_outputBuffer += (fmt % s->rem.toStringWithPort() % s->success % s->failures).str(); }); } else { c_lua.writeFunction("showReportSinks", []() { }); } if (!multi_lua) { c_lua.writeFunction("showNamedReportSinks", []() { auto rsinks = g_named_report_sinks.getCopy(); boost::format fmt("%-15s %-35s %-10d %-9d\n"); g_outputBuffer= (fmt % "Name" % "Address" % "Successes" % "Failures").str(); for(const auto& s : rsinks) for (const auto& v : s.second.second) g_outputBuffer += (fmt % s.first % v->rem.toStringWithPort() % v->success % v->failures).str(); }); } else { c_lua.writeFunction("showNamedReportSinks", []() { }); } if (!multi_lua) { c_lua.writeFunction("setNumSiblingThreads", [](int numThreads) { // the number of threads used to process sibling reports g_replication.setNumSiblingThreads(numThreads); }); } else { c_lua.writeFunction("setNumSiblingThreads", [](int numThreads) { }); } #ifdef HAVE_GETDNS if (!multi_lua) { c_lua.writeFunction("newDNSResolver", [](const std::string& name) { std::lock_guard lock(resolv_mutx); resolvMap.insert(std::make_pair(name, std::make_shared(name))); addPrometheusDNSResolverMetric(name); }); } else { c_lua.writeFunction("newDNSResolver", [](const std::string& name) { }); } c_lua.registerFunction("addResolver", &WFResolver::add_resolver); c_lua.registerFunction("setRequestTimeout", &WFResolver::set_request_timeout); c_lua.registerFunction("setNumContexts", &WFResolver::set_num_contexts); c_lua.writeFunction("getDNSResolver", [](const std::string& name) { std::lock_guard lock(resolv_mutx); auto it = resolvMap.find(name); if (it != resolvMap.end()) return it->second; // copy else return std::make_shared(); // copy }); c_lua.registerFunction("lookupAddrByName", &WFResolver::lookup_address_by_name); c_lua.registerFunction("lookupNameByAddr", &WFResolver::lookup_name_by_address); c_lua.registerFunction("lookupRBL", &WFResolver::lookupRBL); // The following "show.." functions are mainly for regression tests if (!multi_lua) { c_lua.writeFunction("showAddrByName", [](std::shared_ptr resolvp, string name) { std::vector retvec = resolvp->lookup_address_by_name(name, 1); boost::format fmt("%s %s\n"); for (const auto& s : retvec) { g_outputBuffer += (fmt % name % s).str(); } }); } else { c_lua.writeFunction("showAddrByName", [](std::shared_ptr resolvp, string name) { }); } if (!multi_lua) { c_lua.writeFunction("showNameByAddr", [](std::shared_ptr resolvp, ComboAddress address) { std::vector retvec = resolvp->lookup_name_by_address(address, 1); boost::format fmt("%s %s\n"); for (const auto& s : retvec) { g_outputBuffer += (fmt % address.toString() % s).str(); } }); } else { c_lua.writeFunction("showNameByAddr", [](std::shared_ptr resolvp, ComboAddress address) { }); } if (!multi_lua) { c_lua.writeFunction("showRBL", [](std::shared_ptr resolvp, ComboAddress address, string rblname) { std::vector retvec = resolvp->lookupRBL(address, rblname, 1); boost::format fmt("%s\n"); for (const auto& s : retvec) { g_outputBuffer += (fmt % s).str(); } }); } else { c_lua.writeFunction("showRBL", [](std::shared_ptr resolvp, ComboAddress address, string rblname) { }); } #endif // HAVE_GETDNS if (!multi_lua) { c_lua.writeFunction("newStringStatsDB", [](const std::string& name, int window_size, int num_windows, const std::vector>& fmvec) { auto twsdbw = TWStringStatsDBWrapper(name, window_size, num_windows, fmvec); // register this statsDB in a map for retrieval later std::lock_guard lock(dbMap_mutx); dbMap.emplace(std::make_pair(name, twsdbw)); }); } else { c_lua.writeFunction("newStringStatsDB", [](const std::string& name, int window_size, int num_windows, const std::vector>& fmvec) { }); } if (!multi_lua) { c_lua.writeFunction("newShardedStringStatsDB", [](const std::string& name, int window_size, int num_windows, const std::vector>& fmvec, int num_shards) { auto twsdbw = TWStringStatsDBWrapper(name, window_size, num_windows, fmvec, num_shards); // register this statsDB in a map for retrieval later std::lock_guard lock(dbMap_mutx); dbMap.emplace(std::make_pair(name, twsdbw)); }); } else { c_lua.writeFunction("newShardedStringStatsDB", [](const std::string& name, int window_size, int num_windows, const std::vector>& fmvec, int num_shards) { }); } c_lua.writeFunction("getStringStatsDB", [](const std::string& name) { std::lock_guard lock(dbMap_mutx); auto it = dbMap.find(name); if (it != dbMap.end()) return it->second; // copy else { warnlog("getStringStatsDB(): could not find DB %s", name); return TWStringStatsDBWrapper("none", 1, 1); } }); if (!multi_lua) { c_lua.writeFunction("showStringStatsDB", []() { std::lock_guard lock(dbMap_mutx); boost::format fmt("%-20.20d %-5.5s %-11.11d %-9d %-9d %-16.16s %-s\n"); g_outputBuffer= (fmt % "DB Name/Shards" % "Repl?" % "Win Size/No" % "Max Size" % "Cur Size" % "Field Name" % "Type").str(); for (auto& i : dbMap) { const FieldMap fields = i.second.getFields(); for (auto f=fields.begin(); f!=fields.end(); ++f) { if (f == fields.begin()) { std::string win = std::to_string(i.second.windowSize()) + "/" + std::to_string(i.second.numWindows()); std::string name_shards = i.first + "/" + std::to_string(i.second.numShards()); g_outputBuffer += (fmt % name_shards % (i.second.getReplicationStatus() ? "yes" : "no") % win % i.second.get_max_size() % i.second.get_size() % f->first % f->second).str(); } else { g_outputBuffer += (fmt % "" % "" % "" % "" % "" % f->first % f->second).str(); } } } }); } else { c_lua.writeFunction("showStringStatsDB", []() { }); } c_lua.registerFunction("twAdd", &TWStringStatsDBWrapper::add); c_lua.registerFunction("twSub", &TWStringStatsDBWrapper::sub); c_lua.registerFunction("twGet", &TWStringStatsDBWrapper::get); c_lua.registerFunction("twGetCurrent", &TWStringStatsDBWrapper::get_current); c_lua.registerFunction("twGetWindows", &TWStringStatsDBWrapper::get_windows); c_lua.registerFunction("twSetv4Prefix", &TWStringStatsDBWrapper::setv4Prefix); c_lua.registerFunction("twSetv6Prefix", &TWStringStatsDBWrapper::setv6Prefix); c_lua.registerFunction("twGetSize", &TWStringStatsDBWrapper::get_size); c_lua.registerFunction("twSetMaxSize", &TWStringStatsDBWrapper::set_size_soft); c_lua.registerFunction("twReset", &TWStringStatsDBWrapper::reset); c_lua.registerFunction("twResetField", &TWStringStatsDBWrapper::resetField); c_lua.registerFunction("twEnableReplication", &TWStringStatsDBWrapper::enableReplication); c_lua.registerFunction("twGetName", &TWStringStatsDBWrapper::getDBName); c_lua.registerFunction("twSetExpireSleep", &TWStringStatsDBWrapper::set_expire_sleep); // Blacklists if (!multi_lua) { c_lua.writeFunction("disableBuiltinBlacklists", []() { g_builtin_bl_enabled = false; }); } else { c_lua.writeFunction("disableBuiltinBlacklists", []() {}); } if (!client) { c_lua.writeFunction("blacklistNetmask", [](const Netmask& nm, unsigned int seconds, const std::string& reason) { g_bl_db.addEntry(nm, seconds, reason); }); c_lua.writeFunction("blacklistIP", [](const ComboAddress& ca, unsigned int seconds, const std::string& reason) { g_bl_db.addEntry(ca, seconds, reason); }); c_lua.writeFunction("blacklistLogin", [](const std::string& login, unsigned int seconds, const std::string& reason) { g_bl_db.addEntry(login, seconds, reason); }); c_lua.writeFunction("blacklistIPLogin", [](const ComboAddress& ca, const std::string& login, unsigned int seconds, const std::string& reason) { g_bl_db.addEntry(ca, login, seconds, reason); }); c_lua.writeFunction("unblacklistNetmask", [](const Netmask& nm) { g_bl_db.deleteEntry(nm); }); c_lua.writeFunction("unblacklistIP", [](const ComboAddress& ca) { g_bl_db.deleteEntry(ca); }); c_lua.writeFunction("unblacklistLogin", [](const std::string& login) { g_bl_db.deleteEntry(login); }); c_lua.writeFunction("unblacklistIPLogin", [](const ComboAddress& ca, const std::string& login) { g_bl_db.deleteEntry(ca, login); }); c_lua.writeFunction("checkBlacklistIP", [](const ComboAddress& ca) { return g_bl_db.checkEntry(ca); }); c_lua.writeFunction("checkBlacklistLogin", [](const std::string& login) { return g_bl_db.checkEntry(login); }); c_lua.writeFunction("checkBlacklistIPLogin", [](const ComboAddress& ca, const std::string& login) { return g_bl_db.checkEntry(ca, login); }); c_lua.writeFunction("getIPBlacklist", []() { auto bl = g_bl_db.getIPEntries(); return getWLBLKeys(bl, "ip"); }); c_lua.writeFunction("getLoginBlacklist", []() { auto bl = g_bl_db.getLoginEntries(); return getWLBLKeys(bl, "login"); }); c_lua.writeFunction("getIPLoginBlacklist", []() { auto bl = g_bl_db.getIPLoginEntries(); return getWLBLKeys(bl, "iplogin"); }); c_lua.writeFunction("getBlacklistIPRetMsg", []() { return g_bl_db.getIPRetMsg(); }); c_lua.writeFunction("getBlacklistLoginRetMsg", []() { return g_bl_db.getLoginRetMsg(); }); c_lua.writeFunction("getBlacklistIPLoginRetMsg", []() { return g_bl_db.getIPLoginRetMsg(); }); } else { c_lua.writeFunction("blacklistNetmask", [](const Netmask& nm, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("blacklistIP", [](const ComboAddress& ca, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("blacklistLogin", [](const std::string& login, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("blacklistIPLogin", [](const ComboAddress& ca, const std::string& login, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("unblacklistNetmask", [](const Netmask& nm) { }); c_lua.writeFunction("unblacklistIP", [](const ComboAddress& ca) { }); c_lua.writeFunction("unblacklistLogin", [](const std::string& login) { }); c_lua.writeFunction("unblacklistIPLogin", [](const ComboAddress& ca) { }); c_lua.writeFunction("checkBlacklistIP", [](const ComboAddress& ca) {}); c_lua.writeFunction("checkBlacklistLogin", [](const std::string& login) {}); c_lua.writeFunction("checkBlacklistIPLogin", [](const ComboAddress& ca, const std::string& login) {}); c_lua.writeFunction("getIPBlacklist", []() {}); c_lua.writeFunction("getLoginBlacklist", []() {}); c_lua.writeFunction("getIPLoginBlacklist", []() {}); c_lua.writeFunction("getBlacklistIPRetMsg", []() {}); c_lua.writeFunction("getBlacklistLoginRetMsg", []() {}); c_lua.writeFunction("getBlacklistIPLoginRetMsg", []() {}); } if (!multi_lua) { c_lua.writeFunction("blacklistPersistDB", [](const std::string& ip, unsigned int port) { g_bl_db.makePersistent(ip, port); }); c_lua.writeFunction("blacklistRedisUsername", [](const std::string& username) { g_bl_db.setRedisUsername(username); }); c_lua.writeFunction("blacklistRedisPassword", [](const std::string& password) { g_bl_db.setRedisPassword(password); }); c_lua.writeFunction("blacklistPersistReplicated", []() { g_bl_db.persistReplicated(); }); c_lua.writeFunction("blacklistPersistConnectTimeout", [](int timeout_secs) { g_bl_db.setConnectTimeout(timeout_secs); }); c_lua.writeFunction("blacklistPersistRWTimeout", [](int timeout_secs, int timeout_usecs) { g_bl_db.setRWTimeout(timeout_secs, timeout_usecs); }); c_lua.writeFunction("setBlacklistIPRetMsg", [](const std::string& msg) { g_bl_db.setIPRetMsg(msg); }); c_lua.writeFunction("setBlacklistLoginRetMsg", [](const std::string& msg) { g_bl_db.setLoginRetMsg(msg); }); c_lua.writeFunction("setBlacklistIPLoginRetMsg", [](const std::string& msg) { g_bl_db.setIPLoginRetMsg(msg); }); } else { c_lua.writeFunction("blacklistPersistDB", [](const std::string& ip, unsigned int port) {}); c_lua.writeFunction("blacklistRedisUsername", [](const std::string& username) {}); c_lua.writeFunction("blacklistRedisPassword", [](const std::string& password) {}); c_lua.writeFunction("blacklistPersistReplicated", []() {}); c_lua.writeFunction("blacklistPersistConnectTimeout", [](int timeout_secs) {}); c_lua.writeFunction("blacklistPersistRWTimeout", [](int timeout_secs, int timeout_usecs) {}); c_lua.writeFunction("setBlacklistIPRetMsg", [](const std::string& msg) {}); c_lua.writeFunction("setBlacklistLoginRetMsg", [](const std::string& msg) {}); c_lua.writeFunction("setBlacklistIPLoginRetMsg", [](const std::string& msg) {}); } // End blacklists // Whitelists if (!multi_lua) { c_lua.writeFunction("disableBuiltinWhitelists", []() { g_builtin_wl_enabled = false; }); } else { c_lua.writeFunction("disableBuiltinWhitelists", []() {}); } if (!client) { c_lua.writeFunction("whitelistNetmask", [](const Netmask& nm, unsigned int seconds, const std::string& reason) { g_wl_db.addEntry(nm, seconds, reason); }); c_lua.writeFunction("whitelistIP", [](const ComboAddress& ca, unsigned int seconds, const std::string& reason) { g_wl_db.addEntry(ca, seconds, reason); }); c_lua.writeFunction("whitelistLogin", [](const std::string& login, unsigned int seconds, const std::string& reason) { g_wl_db.addEntry(login, seconds, reason); }); c_lua.writeFunction("whitelistIPLogin", [](const ComboAddress& ca, const std::string& login, unsigned int seconds, const std::string& reason) { g_wl_db.addEntry(ca, login, seconds, reason); }); c_lua.writeFunction("unwhitelistNetmask", [](const Netmask& nm) { g_wl_db.deleteEntry(nm); }); c_lua.writeFunction("unwhitelistIP", [](const ComboAddress& ca) { g_wl_db.deleteEntry(ca); }); c_lua.writeFunction("unwhitelistLogin", [](const std::string& login) { g_wl_db.deleteEntry(login); }); c_lua.writeFunction("unwhitelistIPLogin", [](const ComboAddress& ca, const std::string& login) { g_wl_db.deleteEntry(ca, login); }); c_lua.writeFunction("checkWhitelistIP", [](const ComboAddress& ca) { return g_wl_db.checkEntry(ca); }); c_lua.writeFunction("checkWhitelistLogin", [](const std::string& login) { return g_wl_db.checkEntry(login); }); c_lua.writeFunction("checkWhitelistIPLogin", [](const ComboAddress& ca, const std::string& login) { return g_wl_db.checkEntry(ca, login); }); c_lua.writeFunction("getIPWhitelist", []() { auto wl = g_wl_db.getIPEntries(); return getWLBLKeys(wl, "ip"); }); c_lua.writeFunction("getLoginWhitelist", []() { auto wl = g_wl_db.getLoginEntries(); return getWLBLKeys(wl, "login"); }); c_lua.writeFunction("getIPLoginWhitelist", []() { auto wl = g_wl_db.getIPLoginEntries(); return getWLBLKeys(wl, "iplogin"); }); } else { c_lua.writeFunction("whitelistNetmask", [](const Netmask& nm, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("whitelistIP", [](const ComboAddress& ca, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("whitelistLogin", [](const std::string& login, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("whitelistIPLogin", [](const ComboAddress& ca, const std::string& login, unsigned int seconds, const std::string& reason) { }); c_lua.writeFunction("unwhitelistNetmask", [](const Netmask& nm) { }); c_lua.writeFunction("unwhitelistIP", [](const ComboAddress& ca) { }); c_lua.writeFunction("unwhitelistLogin", [](const std::string& login) { }); c_lua.writeFunction("unwhitelistIPLogin", [](const ComboAddress& ca) { }); c_lua.writeFunction("checkWhitelistIP", [](const ComboAddress& ca) {}); c_lua.writeFunction("checkWhitelistLogin", [](const std::string& login) {}); c_lua.writeFunction("checkWhitelistIPLogin", [](const ComboAddress& ca, const std::string& login) {}); c_lua.writeFunction("getIPWhitelist", []() {}); c_lua.writeFunction("getLoginWhitelist", []() {}); c_lua.writeFunction("getIPLoginWhitelist", []() {}); } if (!multi_lua) { c_lua.writeFunction("whitelistPersistDB", [](const std::string& ip, unsigned int port) { g_wl_db.makePersistent(ip, port); }); c_lua.writeFunction("whitelistRedisUsername", [](const std::string& username) { g_wl_db.setRedisUsername(username); }); c_lua.writeFunction("whitelistRedisPassword", [](const std::string& password) { g_wl_db.setRedisPassword(password); }); c_lua.writeFunction("whitelistPersistReplicated", []() { g_wl_db.persistReplicated(); }); c_lua.writeFunction("whitelistPersistConnectTimeout", [](int timeout_secs) { g_wl_db.setConnectTimeout(timeout_secs); }); c_lua.writeFunction("whitelistPersistRWTimeout", [](int timeout_secs, int timeout_usecs) { g_wl_db.setRWTimeout(timeout_secs, timeout_usecs); }); } else { c_lua.writeFunction("whitelistPersistDB", [](const std::string& ip, unsigned int port) {}); c_lua.writeFunction("whitelistRedisUsername", [](const std::string& username) {}); c_lua.writeFunction("whitelistRedisPassword", [](const std::string& password) {}); c_lua.writeFunction("whitelistPersistReplicated", []() {}); c_lua.writeFunction("whitelistPersistConnectTimeout", [](int timeout_secs) {}); c_lua.writeFunction("whitelistPersistRWTimeout", [](int timeout_secs, int timeout_usecs) {}); } // End whitelists if (multi_lua) { c_lua.writeFunction("setAllow", [&allow_func](allow_t func) { allow_func=func;}); } else { c_lua.writeFunction("setAllow", [](allow_t func) { }); } if (multi_lua) { c_lua.writeFunction("setReport", [&report_func](report_t func) { report_func=func;}); } else { c_lua.writeFunction("setReport", [](report_t func) { }); } if (multi_lua) { c_lua.writeFunction("setReset", [&reset_func](reset_t func) { reset_func=func;}); } else { c_lua.writeFunction("setReset", [](reset_t func) { }); } if (multi_lua) { c_lua.writeFunction("setCanonicalize", [&canon_func](canonicalize_t func) { canon_func=func;}); } else { c_lua.writeFunction("setCanonicalize", [](canonicalize_t func) { }); } if (!multi_lua) { c_lua.writeFunction("setVerboseAllowLog", []() { g_allowlog_verbose = true; }); } else { c_lua.writeFunction("setVerboseAllowLog", []() { }); } c_lua.registerMember("attrs", &CustomFuncArgs::attrs); c_lua.registerMember("attrs_mv", &CustomFuncArgs::attrs_mv); c_lua.writeFunction("setCustomEndpoint", [&custom_func_map, multi_lua, client](const std::string& f_name, bool reportSink, custom_func_t func) { CustomFuncMapObject cobj; cobj.c_func = func; cobj.c_reportSink = reportSink; custom_func_map.insert(std::make_pair(f_name, cobj)); if (!multi_lua && !client) { addCommandStat(f_name); addPrometheusCommandMetric("f_name"); // register a webserver command g_webserver.registerFunc(f_name, HTTPVerb::POST, WforceWSFunc(parseCustomCmd)); noticelog("Registering custom POST endpoint [%s]", f_name); } }); c_lua.writeFunction("setCustomGetEndpoint", [&custom_get_func_map, multi_lua, client](const std::string& f_name, custom_get_func_t func) { custom_get_func_map.insert(std::make_pair(f_name, func)); if (!multi_lua && !client) { addCommandStat(f_name+"_Get"); addPrometheusCommandMetric(f_name+"_Get"); // register a webserver command g_webserver.registerFunc(f_name, HTTPVerb::GET, WforceWSFunc(parseCustomGetCmd, drogon::CT_TEXT_PLAIN)); noticelog("Registering custom GET endpoint [%s]", f_name); } }); if (!multi_lua) { c_lua.writeFunction("showCustomEndpoints", []() { boost::format fmt("%-30.30s %-5.5s %-s \n"); g_outputBuffer = (fmt % "Custom Endpoint" % "Type" % "Send to Report Sink?").str(); for (const auto& i : g_custom_func_map) { std::string reportStr = i.second.c_reportSink == true ? "true" : "false"; g_outputBuffer += (fmt % i.first % "POST" % reportStr).str(); } for (const auto& i : g_custom_get_func_map) { g_outputBuffer += (fmt % i.first % "GET" % "false").str(); } }); } else { c_lua.writeFunction("showCustomEndpoints", []() { }); } if (!multi_lua) { c_lua.writeFunction("showWebHooks", []() { auto webhooks = g_webhook_db.getWebHooks(); boost::format fmt("%-9d %-9d %-9d %-45.45s %-s\n"); g_outputBuffer= (fmt % "ID" % "Successes" % "Failures" % "URL" % "Events").str(); for(const auto& i : webhooks) { if (auto is = i.lock()) if (is) g_outputBuffer += (fmt % is->getID() % is->getSuccess() % is->getFailed() % is->getConfigKey("url") % is->getEventsStr()).str(); } }); } else { c_lua.writeFunction("showWebHooks", []() { }); } if (!(multi_lua || client)) { c_lua.writeFunction("addWebHook", [](const std::vector>& events_vec, const std::vector>& ck_vec) { std::string err; WHEvents events; WHConfigMap config_keys; auto id = g_webhook_db.getNewID(); for (const auto& ev : events_vec) { events.push_back(ev.second); } for (const auto& ck : ck_vec) { config_keys.insert(ck); } auto i = config_keys.find("kafka"); if (i != config_keys.end()) { if (i->second == "true") config_keys.insert(make_pair("content-type", "application/vnd.kafka.json.v2+json")); } auto ret = g_webhook_db.addWebHook(WebHook(id, events, true, config_keys), err); if (ret != true) { errlog("Registering webhook id=%d from Lua failed [%s]", id, err); } }); } else { c_lua.writeFunction("addWebHook", [](const std::vector>& events_vec, const std::vector>& ck_vec) { }); } if (!multi_lua) { c_lua.writeFunction("showVersion", []() { g_outputBuffer = "wforce " + std::string(VERSION) + "\n"; }); } else { c_lua.writeFunction("showVersion", []() { }); } if (!multi_lua) { c_lua.writeFunction("setKey", [](const std::string& key) -> bool { string newkey; if(B64Decode(key, newkey) < 0) { g_outputBuffer=string("Unable to decode ")+key+" as Base64"; errlog("%s", g_outputBuffer); return false; } else { g_replication.setEncryptionKey(newkey); return true; } }); } else { c_lua.writeFunction("setKey", [](const std::string& key) { }); } if (!multi_lua) { c_lua.writeFunction("testCrypto", [](string testmsg) { try { SodiumNonce sn, sn2; sn.init(); sn2=sn; std::string key = g_replication.getEncryptionKey(); string encrypted = sodEncryptSym(testmsg, key, sn); string decrypted = sodDecryptSym(encrypted, key, sn2); sn.increment(); sn2.increment(); encrypted = sodEncryptSym(testmsg, key, sn); decrypted = sodDecryptSym(encrypted, key, sn2); if(testmsg == decrypted) g_outputBuffer="Everything is ok!\n"; else g_outputBuffer="Crypto failed..\n"; } catch(...) { g_outputBuffer="Crypto failed..\n"; }}); } else { c_lua.writeFunction("testCrypto", [](string testmsg) {}); } if (!multi_lua) { c_lua.writeFunction("setHLLBits", [](unsigned int nbits) { TWStatsMemberHLL::setNumBits(nbits); }); } else { c_lua.writeFunction("setHLLBits", [](unsigned int nbits) { }); } if (!multi_lua) { c_lua.writeFunction("setCountMinBits", [](float gamma, float eps) { TWStatsMemberCountMin::setGamma(gamma); TWStatsMemberCountMin::setEPS(eps); }); } else { c_lua.writeFunction("setCountMinBits", [](float gamma, float eps) { }); } std::ifstream ifs(config); if(!ifs) warnlog("Unable to read configuration from '%s'", config); else if (!multi_lua) infolog("Read configuration from '%s'", config); c_lua.executeCode(ifs); if (!multi_lua) { auto ret=*launchWork; delete launchWork; launchWork=0; return ret; } else { return vector>(); } } weakforced-2.10.2/wforce/wforce-prometheus.cc000066400000000000000000000222231461473602600211720ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "wforce-prometheus.hh" static std::shared_ptr wforce_prom_metrics; void initWforcePrometheusMetrics(std::shared_ptr wpmp) { wforce_prom_metrics = wpmp; initPrometheusMetrics(wpmp); } void WforcePrometheus::addAllowStatusMetric(const std::string& name) { Counter* c = &(allow_status_family->Add({{"status", name}})); allow_status_metrics.insert(std::make_pair(name, c)); } void WforcePrometheus::incAllowStatusMetric(const std::string& name) { auto i = allow_status_metrics.find(name); if (i != allow_status_metrics.end()) { i->second->Increment(); } } void WforcePrometheus::addReplicationSibling(const std::string& name) { std::lock_guard lock(repl_mutx); Counter* c = &(repl_sent_family->Add({{"sibling", name}, {"status", "ok"}})); repl_sent_ok_metrics.insert(std::make_pair(name, c)); c = &(repl_sent_family->Add({{"sibling", name}, {"status", "error"}})); repl_sent_err_metrics.insert(std::make_pair(name, c)); c = &(repl_rcvd_family->Add({{"sibling", name}, {"status", "ok"}})); repl_rcvd_ok_metrics.insert(std::make_pair(name, c)); c = &(repl_rcvd_family->Add({{"sibling", name}, {"status", "error"}})); repl_rcvd_err_metrics.insert(std::make_pair(name, c)); c = &(repl_connfail_family->Add({{"sibling", name}})); repl_connfail_metrics.insert(std::make_pair(name, c)); } void WforcePrometheus::removeReplicationSiblingNoLock(const std::string& name) { auto c = repl_sent_ok_metrics.find(name); if (c != repl_sent_ok_metrics.end()) { repl_sent_family->Remove(c->second); repl_sent_ok_metrics.erase(name); } c = repl_sent_err_metrics.find(name); if (c != repl_sent_err_metrics.end()) { repl_sent_family->Remove(c->second); repl_sent_err_metrics.erase(name); } c = repl_rcvd_ok_metrics.find(name); if (c != repl_rcvd_ok_metrics.end()) { repl_rcvd_family->Remove(c->second); repl_rcvd_ok_metrics.erase(name); } c = repl_rcvd_err_metrics.find(name); if (c != repl_rcvd_err_metrics.end()) { repl_rcvd_family->Remove(c->second); repl_rcvd_err_metrics.erase(name); } c = repl_connfail_metrics.find(name); if (c != repl_connfail_metrics.end()) { repl_connfail_family->Remove(c->second); repl_connfail_metrics.erase(name); } } void WforcePrometheus::removeReplicationSibling(const std::string& name) { std::lock_guard lock(repl_mutx); removeReplicationSiblingNoLock(name); } void WforcePrometheus::removeAllReplicationSiblings() { std::lock_guard lock(repl_mutx); std::vector keys; for (auto& i : repl_sent_ok_metrics) { keys.emplace_back(i.first); } for (auto& key : keys) { removeReplicationSiblingNoLock(key); } } void WforcePrometheus::incReplicationSent(const std::string& name, bool success) { std::lock_guard lock(repl_mutx); if (success == true) { auto i = repl_sent_ok_metrics.find(name); if (i != repl_sent_ok_metrics.end()) { i->second->Increment(); } } else { auto i = repl_sent_err_metrics.find(name); if (i != repl_sent_err_metrics.end()) { i->second->Increment(); } } } void WforcePrometheus::incReplicationRcvd(const std::string& name, bool success) { std::lock_guard lock(repl_mutx); if (success == true) { auto i = repl_rcvd_ok_metrics.find(name); if (i != repl_rcvd_ok_metrics.end()) { i->second->Increment(); } } else { auto i = repl_rcvd_err_metrics.find(name); if (i != repl_rcvd_err_metrics.end()) { i->second->Increment(); } } } void WforcePrometheus::incReplicationConnFail(const std::string& name) { std::lock_guard lock(repl_mutx); auto i = repl_connfail_metrics.find(name); if (i != repl_connfail_metrics.end()) { i->second->Increment(); } } void WforcePrometheus::incRedisBLUpdates() { if (redis_bl_updates != nullptr) redis_bl_updates->Increment(); } void WforcePrometheus::incRedisWLUpdates() { if (redis_wl_updates != nullptr) redis_wl_updates->Increment(); } void WforcePrometheus::incRedisBLConnFailed() { if (redis_bl_connfail != nullptr) redis_bl_connfail->Increment(); } void WforcePrometheus::incRedisWLConnFailed() { if (redis_wl_connfail != nullptr) redis_wl_connfail->Increment(); } void WforcePrometheus::setBLIPEntries(int num_entries) { if (bl_entries_ip != nullptr) bl_entries_ip->Set(num_entries); } void WforcePrometheus::setBLLoginEntries(int num_entries) { if (bl_entries_login != nullptr) bl_entries_login->Set(num_entries); } void WforcePrometheus::setBLIPLoginEntries(int num_entries) { if (bl_entries_iplogin != nullptr) bl_entries_iplogin->Set(num_entries); } void WforcePrometheus::setWLIPEntries(int num_entries) { if (wl_entries_ip != nullptr) wl_entries_ip->Set(num_entries); } void WforcePrometheus::setWLLoginEntries(int num_entries) { if (wl_entries_login != nullptr) wl_entries_login->Set(num_entries); } void WforcePrometheus::setWLIPLoginEntries(int num_entries) { if (wl_entries_iplogin != nullptr) wl_entries_iplogin->Set(num_entries); } void addPrometheusAllowStatusMetric(const std::string& name) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->addAllowStatusMetric(name); } } void incPrometheusAllowStatusMetric(const std::string& name) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incAllowStatusMetric(name); } } void addPrometheusReplicationSibling(const std::string& name) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->addReplicationSibling(name); } } void removePrometheusReplicationSibling(const std::string& name) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->removeReplicationSibling(name); } } void removeAllPrometheusReplicationSiblings() { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->removeAllReplicationSiblings(); } } void incPrometheusReplicationSent(const std::string& name, bool success) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incReplicationSent(name, success); } } void incPrometheusReplicationRcvd(const std::string& name, bool success) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incReplicationRcvd(name, success); } } void incPrometheusReplicationConnFail(const std::string& name) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incReplicationConnFail(name); } } void incPrometheusRedisBLUpdates() { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incRedisBLUpdates(); } } void incPrometheusRedisBLConnFailed() { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incRedisBLConnFailed(); } } void incPrometheusRedisWLUpdates() { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incRedisWLUpdates(); } } void incPrometheusRedisWLConnFailed() { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->incRedisWLConnFailed(); } } void setPrometheusBLIPEntries(int num_entries) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->setBLIPEntries(num_entries); } } void setPrometheusWLIPEntries(int num_entries) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->setWLIPEntries(num_entries); } } void setPrometheusBLLoginEntries(int num_entries) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->setBLLoginEntries(num_entries); } } void setPrometheusWLLoginEntries(int num_entries) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->setWLLoginEntries(num_entries); } } void setPrometheusBLIPLoginEntries(int num_entries) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->setBLIPLoginEntries(num_entries); } } void setPrometheusWLIPLoginEntries(int num_entries) { if (wforce_prom_metrics != nullptr) { wforce_prom_metrics->setWLIPLoginEntries(num_entries); } } void setPrometheusReplRecvQueueSize(int value) { if (wforce_prom_metrics) { wforce_prom_metrics->setReplRecvQueueSize(value); } } void setPrometheusReplRecvQueueRetrieveFunc(std::function func) { if (wforce_prom_metrics) { wforce_prom_metrics->setReplRecvQueueRetrieveFunc(func); } } weakforced-2.10.2/wforce/wforce-prometheus.hh000066400000000000000000000164451461473602600212150ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include "prometheus.hh" #include "wforce_exception.hh" class WforcePrometheus : public PrometheusMetrics { public: WforcePrometheus(const std::string& prefix) : PrometheusMetrics(prefix) { if (d_registry != nullptr) { allow_status_family = &(BuildCounter() .Name(d_prefix+"_allow_status_total") .Help("Count of different allow status responses") .Register(*d_registry)); repl_sent_family = &(BuildCounter() .Name(d_prefix+"_replication_sent_total") .Help("How many replication messages were sent?") .Register(*d_registry)); repl_rcvd_family = &(BuildCounter() .Name(d_prefix+"_replication_rcvd_total") .Help("How many replication messages were rcvd?") .Register(*d_registry)); repl_connfail_family = &(BuildCounter() .Name(d_prefix+"_replication_tcp_connfailed_total") .Help("How many TCP connections failed?") .Register(*d_registry)); auto& redis_wlbl_family = BuildCounter() .Name(d_prefix+"_redis_wlbl_updates_total") .Help("How many redis wl/bl updates succeeded?") .Register(*d_registry); redis_bl_updates = &(redis_wlbl_family.Add({{"type", "bl"}})); redis_wl_updates = &(redis_wlbl_family.Add({{"type", "wl"}})); auto& redis_wlbl_connfail_family = BuildCounter() .Name(d_prefix+"_redis_wlbl_connfailed_total") .Help("How many redis wl/bl connection attempts failed?") .Register(*d_registry); redis_bl_connfail = &(redis_wlbl_connfail_family.Add({{"type", "bl"}})); redis_wl_connfail = &(redis_wlbl_connfail_family.Add({{"type", "wl"}})); auto& bl_entries_family = BuildGauge() .Name(d_prefix+"_bl_entries") .Help("How many entries are in the blacklist?") .Register(*d_registry); bl_entries_ip = &(bl_entries_family.Add({{"type", "ip"}})); bl_entries_login = &(bl_entries_family.Add({{"type", "login"}})); bl_entries_iplogin = &(bl_entries_family.Add({{"type", "iplogin"}})); auto& wl_entries_family = BuildGauge() .Name(d_prefix+"_wl_entries") .Help("How many entries are in the whitelist?") .Register(*d_registry); wl_entries_ip = &(wl_entries_family.Add({{"type", "ip"}})); wl_entries_login = &(wl_entries_family.Add({{"type", "login"}})); wl_entries_iplogin = &(wl_entries_family.Add({{"type", "iplogin"}})); auto& repl_recv_queue_family = BuildGauge() .Name(d_prefix+"_repl_recv_queue_size") .Help("How full is the replication recv worker thread queue?") .Register(*d_registry); repl_recv_queue_size = &(repl_recv_queue_family.Add({})); } else { throw WforceException("Could not allocate memory for Prometheus Registry"); } repl_queue_func = [](){ return 0; }; } void addAllowStatusMetric(const std::string& name); void incAllowStatusMetric(const std::string& name); void addReplicationSibling(const std::string& name); void removeReplicationSibling(const std::string& name); void removeAllReplicationSiblings(); void incReplicationSent(const std::string& name, bool success); void incReplicationRcvd(const std::string& name, bool success); void incReplicationConnFail(const std::string& name); void incRedisBLUpdates(); void incRedisBLConnFailed(); void incRedisWLUpdates(); void incRedisWLConnFailed(); void setBLIPEntries(int); void setWLIPEntries(int); void setBLLoginEntries(int); void setWLLoginEntries(int); void setBLIPLoginEntries(int); void setWLIPLoginEntries(int); void setReplRecvQueueSize(int value) { repl_recv_queue_size->Set(value); } void setReplRecvQueueRetrieveFunc(std::function func) { repl_queue_func = func; } std::string serialize() override { // We want to retrieve the value of the worker thread queue only when metrics // are asked for. The promethus-cpp library doesn't allow this itself setReplRecvQueueSize(repl_queue_func()); return PrometheusMetrics::serialize(); } protected: void removeReplicationSiblingNoLock(const std::string& name); private: Family* allow_status_family; std::map allow_status_metrics; // This mutex is because replication siblings are allowed to change dynamically // unlike the other metrics std::mutex repl_mutx; Family* repl_sent_family; std::map repl_sent_ok_metrics; std::map repl_sent_err_metrics; Family* repl_rcvd_family; std::map repl_rcvd_ok_metrics; std::map repl_rcvd_err_metrics; Family* repl_connfail_family; std::map repl_connfail_metrics; Counter* redis_bl_updates; Counter* redis_bl_connfail; Counter* redis_wl_updates; Counter* redis_wl_connfail; Gauge* bl_entries_ip; Gauge* bl_entries_login; Gauge* bl_entries_iplogin; Gauge* wl_entries_ip; Gauge* wl_entries_login; Gauge* wl_entries_iplogin; Gauge* repl_recv_queue_size; std::function repl_queue_func; }; void initWforcePrometheusMetrics(std::shared_ptr wpmp); void addPrometheusAllowStatusMetric(const std::string& name); void incPrometheusAllowStatusMetric(const std::string& name); void addPrometheusReplicationSibling(const std::string& name); void removePrometheusReplicationSibling(const std::string& name); void removeAllPrometheusReplicationSiblings(); void incPrometheusReplicationSent(const std::string& name, bool success); void incPrometheusReplicationRcvd(const std::string& name, bool success); void incPrometheusReplicationConnFail(const std::string& name); void incPrometheusRedisBLUpdates(); void incPrometheusRedisBLConnFailed(); void incPrometheusRedisWLUpdates(); void incPrometheusRedisWLConnFailed(); void setPrometheusBLIPEntries(int); void setPrometheusWLIPEntries(int); void setPrometheusBLLoginEntries(int); void setPrometheusWLLoginEntries(int); void setPrometheusBLIPLoginEntries(int); void setPrometheusWLIPLoginEntries(int); void setPrometheusReplRecvQueueSize(int value); void setPrometheusReplRecvQueueRetrieveFunc(std::function func); weakforced-2.10.2/wforce/wforce-replication.cc000066400000000000000000000222521461473602600213120ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include "wforce_ns.hh" #include "sstuff.hh" #include "misc.hh" #include #include #include "dolog.hh" #include #include "json11.hpp" #include #include #include "sodcrypto.hh" #include "perf-stats.hh" #include "lock.hh" #include "replication_sdb.hh" #include "iputils.hh" #include "ext/threadname.hh" #include "wforce-replication.hh" #include "wforce-prometheus.hh" using std::atomic; using std::thread; void WforceReplication::encryptMsg(const std::string& msg, std::string& packet) { std::lock_guard lock(d_sod_mutx); packet=d_sodnonce.toString(); packet+=sodEncryptSym(msg, d_key, d_sodnonce); } void WforceReplication::encryptMsgWithKey(const std::string& msg, std::string& packet, const std::string& key, SodiumNonce& nonce, std::mutex& mutex) { std::lock_guard lock(mutex); packet=nonce.toString(); packet+=sodEncryptSym(msg, key, nonce); } bool WforceReplication::decryptMsg(const char* buf, size_t len, std::string& msg) { SodiumNonce nonce; if (len < static_cast(sizeof(nonce.value))) { errlog("Could not decrypt replication operation: not enough bytes (%d) to hold nonce", len); return false; } memcpy((char*)&nonce, buf, sizeof(nonce.value)); string packet(buf + sizeof(nonce.value), buf+len); try { msg=sodDecryptSym(packet, d_key, nonce); } catch (std::runtime_error& e) { errlog("Could not decrypt replication operation: %s", e.what()); return false; } return true; } void WforceReplication::replicateOperation(const ReplicationOperation& rep_op) { auto siblings = d_siblings.getLocal(); string msg = rep_op.serialize(); string default_packet, sibling_packet; encryptMsg(msg, default_packet); for(auto& s : *siblings) { bool use_sibling_packet = false; if (s->d_has_key) { if (s->d_key != d_key) { encryptMsgWithKey(msg, sibling_packet, s->d_key, s->d_nonce, s->mutx); use_sibling_packet = true; } } if (use_sibling_packet) s->queueMsg(sibling_packet); else s->queueMsg(default_packet); } } bool WforceReplication::checkConnFromSibling(const ComboAddress& remote, shared_ptr& recv_sibling) { auto siblings = d_siblings.getLocal(); for (auto &s : *siblings) { if (ComboAddress::addressOnlyEqual()(s->rem, remote) == true) { recv_sibling = s; break; } } if (recv_sibling == nullptr) { errlog("Message received from host (%s) that is not configured as a sibling", remote.toStringWithPort()); return false; } return true; } void WforceReplication::startReplicationWorkerThreads() { // Tell prometheus how to get the recv queue size setPrometheusReplRecvQueueRetrieveFunc(std::function([this]() -> int { std::lock_guard lock(this->d_sibling_queue_mutex); return this->d_sibling_queue.size(); })); for (size_t i=0; i lock(this->d_sibling_queue_mutex); while (this->d_sibling_queue.size() == 0) { this->d_sibling_queue_cv.wait(lock); } sqi = std::move(this->d_sibling_queue.front()); this->d_sibling_queue.pop(); } ReplicationOperation rep_op; if (rep_op.unserialize(sqi.msg) != false) { rep_op.applyOperation(); sqi.recv_sibling->rcvd_success++; // note no port because it can be ephemeral // (thus rcvd stats are tracked only on a per-IP basis) incPrometheusReplicationRcvd(sqi.remote.toString(), true); } else { errlog("Invalid replication operation received from %s", sqi.remote.toString()); sqi.recv_sibling->rcvd_fail++; incPrometheusReplicationRcvd(sqi.remote.toString(), false); } } }); t.detach(); } } void WforceReplication::parseReceivedReplicationMsg(const std::string& msg, const ComboAddress& remote, std::shared_ptr recv_sibling) { SiblingQueueItem sqi = { msg, remote, recv_sibling }; { std::lock_guard lock(d_sibling_queue_mutex); if (d_sibling_queue.size() >= d_max_sibling_queue_size) { errlog("parseReceivedReplicationMsg: max sibling recv queue size (%d) reached - dropping replication msg", d_max_sibling_queue_size); return; } else { d_sibling_queue.push(sqi); } } d_sibling_queue_cv.notify_one(); } void WforceReplication::parseTCPReplication(std::shared_ptr sockp, const ComboAddress& remote, std::shared_ptr recv_sibling) { setThreadName("wf/repl-tcp"); infolog("New TCP Replication connection from %s", remote.toString()); uint16_t size; ssize_t ssize = static_cast(sizeof(size)); char buffer[65535]; ssize_t len; unsigned int num_rcvd=0; try { while(true) { len = sockp->readAll((char*)&size, ssize); if (len != ssize) { if (len) errlog("parseTCPReplication: error reading size, got %d bytes, but was expecting %d bytes", len, ssize); break; } size = ntohs(size); if (!size) { errlog("parseTCPReplication: Zero-length size field"); break; } if (size > sizeof(buffer)) { errlog("parseTCPReplication: This should not happen - asked to read more than 65535 bytes"); break; } int size_read = 0; if ((size_read = sockp->readAll(buffer, size)) != size) { errlog("parseTCPReplication: Told to read %d bytes, but actually read %d bytes", size, size_read); break; } std::string msg; if (!decryptMsg(buffer, size_read, msg)) { recv_sibling->rcvd_fail++; continue; } parseReceivedReplicationMsg(msg, remote, recv_sibling); num_rcvd++; } infolog("Received %d Replication entries from %s", num_rcvd, remote.toString()); } catch(std::exception& e) { errlog("ParseTCPReplication: client thread died with error: %s", e.what()); } } void WforceReplication::receiveReplicationOperationsTCP(const ComboAddress& local) { Socket sock(local.sin4.sin_family, SOCK_STREAM, 0); ComboAddress remote=local; setThreadName("wf/rcv-repl-tcp"); if (local.isIpv6()) sock.setV6Only(); sock.setReuseAddr(); sock.bind(local); sock.listen(1024); noticelog("Launched TCP sibling replication listener on %s", local.toStringWithPort()); for (;;) { // we wait for activity try { shared_ptr recv_sibling = nullptr; std::shared_ptr connp(sock.accept()); connp->setKeepAlive(); if (connp->getRemote(remote) && !checkConnFromSibling(remote, recv_sibling)) { continue; } thread t1([connp, remote, recv_sibling, this]() { this->parseTCPReplication(connp, remote, recv_sibling); }); t1.detach(); } catch(std::exception& e) { errlog("receiveReplicationOperationsTCP: error accepting new connection: %s", e.what()); } } } void WforceReplication::receiveReplicationOperations(const ComboAddress& local) { Socket sock(local.sin4.sin_family, SOCK_DGRAM); if (local.isIpv6()) sock.setV6Only(); sock.bind(local); char buf[1500]; ComboAddress remote=local; socklen_t remlen=remote.getSocklen(); int len; auto siblings = d_siblings.getLocal(); setThreadName("wf/rcv-repl-udp"); noticelog("Launched UDP sibling replication listener on %s", local.toStringWithPort()); for(;;) { shared_ptr recv_sibling = nullptr; len=recvfrom(sock.getHandle(), buf, sizeof(buf), 0, (struct sockaddr*)&remote, &remlen); if(len <= 0 || len >= (int)sizeof(buf)) continue; if (!checkConnFromSibling(remote, recv_sibling)) { continue; } string msg; if (!decryptMsg(buf, len, msg)) { recv_sibling->rcvd_fail++; continue; } parseReceivedReplicationMsg(msg, remote, recv_sibling); } } void WforceReplication::setMaxSiblingRecvQueueSize(size_t size) { d_max_sibling_queue_size = size; } weakforced-2.10.2/wforce/wforce-replication.hh000066400000000000000000000057241461473602600213310ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "sholder.hh" #include "iputils.hh" #include "sodcrypto.hh" #include "wforce-sibling.hh" class WforceReplication { public: WforceReplication() { d_sodnonce.init(); } virtual ~WforceReplication() = default; virtual void receiveReplicationOperationsTCP(const ComboAddress& local); virtual void receiveReplicationOperations(const ComboAddress& local); virtual void startReplicationWorkerThreads(); virtual void encryptMsg(const std::string& msg, std::string& packet); virtual void encryptMsgWithKey(const std::string& msg, std::string& packet, const std::string& key, SodiumNonce& nonce, std::mutex& mutex); virtual bool decryptMsg(const char* buf, size_t len, std::string& msg); void setMaxSiblingRecvQueueSize(size_t size); void setNumSiblingThreads(unsigned int num_threads) { d_num_sibling_threads = num_threads; } GlobalStateHolder>>& getSiblings() { return d_siblings; } virtual void replicateOperation(const ReplicationOperation& rep_op); void setEncryptionKey(const std::string& key) { d_key = key; } std::string getEncryptionKey() const { return d_key; } protected: virtual bool checkConnFromSibling(const ComboAddress& remote, shared_ptr& recv_sibling); virtual void parseTCPReplication(std::shared_ptr sockp, const ComboAddress& remote, std::shared_ptr recv_sibling); virtual void parseReceivedReplicationMsg(const std::string& msg, const ComboAddress& remote, std::shared_ptr recv_sibling); struct SiblingQueueItem { std::string msg; ComboAddress remote; std::shared_ptr recv_sibling; }; GlobalStateHolder>> d_siblings; SodiumNonce d_sodnonce; std::string d_key; // The default key to use if no per-sibling key std::mutex d_sod_mutx; std::mutex d_sibling_queue_mutex; std::queue d_sibling_queue; std::condition_variable d_sibling_queue_cv; size_t d_max_sibling_queue_size = 5000; unsigned int d_num_sibling_threads = 2; };weakforced-2.10.2/wforce/wforce-sibling.cc000066400000000000000000000511211461473602600204250ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include "wforce-sibling.hh" #include "wforce_exception.hh" #include "wforce_ns.hh" #include #include #define SYSLOG_NAMES #include #include "sstuff.hh" #include "misc.hh" #include #include #include #include "dolog.hh" #include #include "sodcrypto.hh" #include "iputils.hh" #include "base64.hh" #include "ext/threadname.hh" #include "wforce-prometheus.hh" using std::atomic; using std::thread; static std::atomic sibling_connect_timeout(5000); // milliseconds static std::atomic sibling_queue_size(5000); extern ComboAddress g_sibling_listen_addr; Sibling::Sibling(const ComboAddress& ca) : Sibling(ca, Protocol::UDP) { } Sibling::Sibling(const ComboAddress& ca, const Protocol& p, int timeout, size_t queue_size, bool sdb_send, bool wlbl_send) : Sibling(ca, p, std::string(), timeout, queue_size, sdb_send, wlbl_send) { } void Sibling::connectSibling() { sockp = wforce::make_unique(rem.sin4.sin_family, static_cast(proto)); if (proto == Protocol::UDP) { sockp->connect(rem); } else if (proto == Protocol::TCP) { if (!d_ignoreself) { try { sockp->setKeepAlive(); sockp->connectWithTimeout(rem, connect_timeout); } catch (const NetworkError& e) { errlog("TCP Connect to Sibling %s failed (%s)", rem .toStringWithPort(), e.what()); incPrometheusReplicationConnFail(rem.toStringWithPort()); } } } } Sibling::Sibling(const ComboAddress& ca, const Protocol& p, const std::string& key, int timeout, size_t queue_size, bool send_sdb, bool send_wlbl) : rem(ca), proto(p), connect_timeout(timeout), max_queue_size(queue_size), queue_thread_run(true), sdb_send(send_sdb), wlbl_send(send_wlbl), d_ignoreself(false), d_key(key) { if (!d_key.empty()) { d_has_key = true; } d_nonce.init(); if (proto != Protocol::NONE) { { std::lock_guard lock(mutx); connectSibling(); } // std::thread is moveable queue_thread = std::thread([this]() { thread_local bool init = false; if (!init) { setThreadName("wf/sibling-worker"); init = true; } while (true) { std::string msg; { std::unique_lock lock(queue_mutx); while ((queue.size() == 0) && queue_thread_run) { queue_cv.wait(lock); } if (!queue_thread_run) return; msg = std::move(queue.front()); queue.pop(); } send(msg); } }); } } Sibling::~Sibling() { if (proto != Protocol::NONE) { { std::lock_guard lock(queue_mutx); queue_thread_run = false; } queue_cv.notify_one(); queue_thread.join(); } } void Sibling::checkIgnoreSelf(const ComboAddress& ca) { if (proto != Protocol::NONE) { ComboAddress actualLocal; actualLocal.sin4.sin_family = ca.sin4.sin_family; socklen_t socklen = actualLocal.getSocklen(); if (getsockname(sockp->getHandle(), (struct sockaddr*) &actualLocal, &socklen) < 0) { return; } actualLocal.sin4.sin_port = ca.sin4.sin_port; if (actualLocal == rem) { d_ignoreself = true; } } } void Sibling::send(const std::string& msg) { if (d_ignoreself) return; if (proto == Protocol::UDP) { if (::send(sockp->getHandle(), msg.c_str(), msg.length(), 0) <= 0) { ++failures; incPrometheusReplicationSent(rem.toStringWithPort(), false); } else { ++success; incPrometheusReplicationSent(rem.toStringWithPort(), true); } } else if (proto == Protocol::TCP) { // This needs protecting with a mutex because of the reconnect logic std::lock_guard lock(mutx); // Try to send. In case of error, try to reconnect (but only once) and then try to send again for (int i = 0; i < 2; ++i) { uint16_t nsize = htons(msg.length()); try { sockp->writen(std::string((char*) &nsize, sizeof(nsize))); sockp->writen(msg); ++success; incPrometheusReplicationSent(rem.toStringWithPort(), true); break; } catch (const NetworkError& e) { if (i == 0) { ++failures; incPrometheusReplicationSent(rem.toStringWithPort(), false); errlog("Error writing to Sibling %s, reconnecting (%s)", rem.toStringWithPort(), e.what()); connectSibling(); } } } } } void Sibling::queueMsg(const std::string& msg) { { std::lock_guard lock(queue_mutx); if (queue.size() >= max_queue_size) { errlog("Sibling::queueMsg: max sibling send queue size (%d) reached - dropping replication msg", max_queue_size); return; } else { queue.push(msg); } } queue_cv.notify_one(); } // Utility functions for managing siblings // siblingHostToAddress takes string representing either a hostname or an IP address // and returns a string representing the IP address. // Throws a WforceException if no valid IP address could be determined std::string siblingHostToAddress(const std::string& host) { std::string sibling_address; struct addrinfo* res=nullptr, *res0=nullptr; int error = getaddrinfo(host.c_str(), nullptr, nullptr, &res0); if (error) { if (res0) freeaddrinfo(res0); throw WforceException(std::string("Error calling getaddrinfo() for sibling host: ") + gai_strerror(error)); } bool found_addr = false; for (res = res0; res != nullptr; res = res->ai_next) { sibling_address = ComboAddress(res->ai_addr, res->ai_addrlen).toString(); found_addr = true; break; } if (res0) freeaddrinfo(res0); if (!found_addr) { throw WforceException(std::string("Could not determine IP address for sibling host: ") + host); } return sibling_address; } // createSiblingAddress returns a string that combines the host, port and protocol // using ":" as the separator (this is the format used for Lua functions) // For example "[::1]:8081:tcp" // Both the ComboAddress constructor and Sibling::protocolToString can throw WforceException on error std::string createSiblingAddress(const std::string& host, int port, Sibling::Protocol proto) { std::string address; if (host.find(':') != string::npos) { // IPv6 needs special handling because it needs [] address = ComboAddress(host, port).toStringWithPort() + ":" + Sibling::protocolToString(proto); } else { address = host + ":" + itoa(port) + ":" + Sibling::protocolToString(proto); } return address; } // parseSiblingString takes a string (as constructed by parseSiblingAddress() for example) representing // a sibling IP address, port and protocol, and sets the ComboAddress and SiblingProtocol parameters // based on the values in the string // The ComboAddress constructor will throw a WforceException if the resulting ComboAddress is not valid // If an invalid protocol is supplied, then UDP will be assumed void parseSiblingString(const std::string& str, ComboAddress& ca, Sibling::Protocol& proto) { std::vector sres; boost::split(sres, str, boost::is_any_of(":")); std::string host_port; std::string address; // Get the Protocol try { proto = Sibling::stringToProtocol(sres.back()); sres.pop_back(); host_port = boost::join(sres, ":"); } catch (const WforceException& e) { proto = Sibling::Protocol::UDP; host_port = str; } // Split the host and port if necessary // Possibilities are: v4 only (1.2.3.4), v6 only ([::1]), hostname only (host.example.com) // v4 plus port (1.2.3.4:4001), v6 plus port ([::1]:4002), hostname plus port (host.example.com:4004) boost::split(sres, host_port, boost::is_any_of(":")); if (sres.size() == 1) { // 1 only matches bare v4 or hostnames address = siblingHostToAddress(sres[0]); } else if (sres.size() == 2) { // 2 only v4 or hostname plus port address = siblingHostToAddress(sres[0]) + ":" + sres[1]; } else { // Everything else we can leave as it is address = host_port; } ca = ComboAddress(address, Sibling::defaultPort); } // siblingAddressPortExists returns true if a sibling already exists in the supplied siblings vector // with the same address and port as supplied in address, otherwise it returns false bool siblingAddressPortExists(const GlobalStateHolder>>& siblings, const ComboAddress& address) { auto local_siblings = siblings.getLocal(); for (auto& s : *local_siblings) { if (s->rem == address) { return true; } } return false; } // removeSibling removes a sibling from the supplied vector. // Only host and port are supplied, as protocol is irrelevant when removing // If the removal was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool removeSibling(const std::string& host, int port, GlobalStateHolder>>& siblings, std::string& output_buffer) { return removeSibling(createSiblingAddress(host, port, Sibling::Protocol::UDP), siblings, output_buffer); } // removeSibling removes a sibling from the supplied vector. // Address parameter should be in the form created by createSiblingAddress() // If the removal was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool removeSibling(const std::string& address, GlobalStateHolder>>& siblings, std::string& output_buffer) { ComboAddress ca; Sibling::Protocol proto; bool retval = false; try { parseSiblingString(address, ca, proto); } catch (const WforceException& e) { const std::string errstr = (boost::format("%s [%s]. %s (%s)") % "removeSibling() error parsing address/port" % address % "Make sure to use IP addresses not hostnames" % e.reason).str(); errlog(errstr.c_str()); output_buffer += errstr; return retval; } siblings.modify([ca, &retval](vector>& v) { for (auto i = v.begin(); i != v.end();) { if ((i->get()->rem == ca)) { i = v.erase(i); retval = true; break; } else { i++; } } }); // We don't remove any existing prometheus metrics for this sibling, otherwise we might cut off metrics before they are scraped // This means that of sibling membership is highly dynamic, the metrics for siblings will "grow"; this is considered to be acceptable. return retval; } // addSibling adds a sibling to the supplied vector. // Address parameter should be in the form created by createSiblingAddress() // If the add was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool addSibling(const std::string& address, GlobalStateHolder>>& siblings, std::string& output_buffer, bool send_sdb, bool send_wlbl) { // Empty key string means no key for this sibling (i.e. global key will be used as previously) return addSiblingWithKey(address, siblings, output_buffer, std::string(), send_sdb, send_wlbl); } // addSibling adds a sibling to the supplied vector. // If the add was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool addSibling(const std::string& host, int port, Sibling::Protocol proto, GlobalStateHolder>>& siblings, std::string& output_buffer, bool send_sdb, bool send_wlbl) { return addSibling(createSiblingAddress(host, port, proto), siblings, output_buffer, send_sdb, send_wlbl); } // addSiblingWithKey adds a sibling to the supplied vector, with a supplied key for encrypting traffic to that sibling // If the add was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool addSiblingWithKey(const std::string& host, int port, Sibling::Protocol proto, GlobalStateHolder>>& siblings, std::string& output_buffer, const std::string& key, bool send_sdb, bool send_wlbl) { return addSiblingWithKey(createSiblingAddress(host, port, proto), siblings, output_buffer, key, send_sdb, send_wlbl); } // addSiblingWithKey adds a sibling to the supplied vector, with a supplied key for encrypting traffic to that sibling // Address parameter should be in the form created by createSiblingAddress() // If the add was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool addSiblingWithKey(const std::string& address, GlobalStateHolder>>& siblings, std::string& output_buffer, const std::string& key, bool send_sdb, bool send_wlbl) { ComboAddress ca; Sibling::Protocol proto; string raw_key; if (B64Decode(key, raw_key) < 0) { output_buffer += string("Unable to decode ") + key + " as Base64"; errlog("%s", output_buffer); return false; } try { parseSiblingString(address, ca, proto); } catch (const WforceException& e) { const std::string errstr = (boost::format("%s [%s]. %s (%s)") % "addSiblingWithKey() error parsing address/port" % address % "Make sure to use IP addresses not hostnames" % e.reason).str(); errlog(errstr.c_str()); output_buffer += errstr; return false; } auto sibling = std::make_shared(ca, proto, raw_key, sibling_connect_timeout, sibling_queue_size, send_sdb, send_wlbl); sibling->checkIgnoreSelf(g_sibling_listen_addr); return addSibling(sibling, siblings, output_buffer); } // addSibling performs the actual modification of the siblings vector bool addSibling(std::shared_ptr sibling, GlobalStateHolder>>& siblings, std::string& output_buffer) { // Ensure the Sibling isn't already there if (siblingAddressPortExists(siblings, sibling->rem)) { const std::string errstr = (boost::format("%s [%s]") % "addSibling() cannot add duplicate sibling" % sibling->rem.toStringWithPort()).str(); errlog(errstr.c_str()); output_buffer += errstr; return false; } // This is for sending when we know the port addPrometheusReplicationSibling(sibling->rem.toStringWithPort()); // This is for receiving when the port may be ephemeral addPrometheusReplicationSibling(sibling->rem.toString()); siblings.modify([sibling](vector>& v) { v.push_back(sibling); }); return true; } // setSiblings sets the siblings in the siblings vector using the arguments in the parts vector // The parts vector is converted from the former simple list of index/address string pairs (from Lua, hence the index) // to the new more complex list of lists which is designed to handle keys in addition to addresses // If the add was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool setSiblings(const vector>& parts, GlobalStateHolder>>& siblings, std::string& output_buffer) { std::vector>>> t_vec; for (auto& i : parts) { t_vec.emplace_back( // Note that an empty string indicates no key hence the std::string() here std::pair>>{i.first, {{0, i.second}, {1, std::string()}}}); } return setSiblingsWithKey(t_vec, siblings, output_buffer); } // Utility function to convert strings to boolean bool toBool(const std::string& bool_str) { std::string str(bool_str); std::transform(str.begin(), str.end(), str.begin(), ::tolower); std::istringstream is(str); bool b; is >> std::boolalpha >> b; return b; } // setSiblings sets the siblings in the siblings vector using the arguments in the parts vector // The parts vector is the new more complex list of lists which is designed to handle keys (and other parameters) // in addition to addresses. // The inner vector can contain 2 or 4 parameters (2 means address+key, 4 means address+key+send_sdb+send_wlbl) // Any prometheus metrics associated with previously created siblings will not be removed // If the add was successful, returns true. // If unsuccessful, returns false and output_buffer contains an error string bool setSiblingsWithKey(const std::vector>>>& parts, GlobalStateHolder>>& siblings, std::string& output_buffer) { // Before we do anything, make sure we don't get any errors parsing the input for (const auto& p : parts) { try { ComboAddress ca; Sibling::Protocol proto; parseSiblingString(p.second[0].second, ca, proto); if ((p.second.size() != 2) && (p.second.size() != 4)) { errlog("setSiblings[WithKey](): Invalid values - was expecting 2 or 4 args, got %d", p.second.size()); output_buffer += "Invalid number of args - was expecting 2 args (sibling address & key) or 4 (sibling address, key, replicate sdb, replicate wlbl)"; return false; } string raw_key; if (B64Decode(p.second[1].second, raw_key) < 0) { output_buffer += string("Unable to decode ") + p.second[1].second + " as Base64"; errlog("%s", output_buffer); return false; } } catch (const WforceException& e) { const std::string errstr = (boost::format("%s [%s]. %s (%s)") % "setSiblings() error parsing address/port" % p.second[0].second % "Reason: " % e.reason).str(); errlog(errstr.c_str()); output_buffer += errstr; return false; } } // Construct the new siblings vector> v; for (const auto& p : parts) { bool send_sdb = true; bool send_wlbl = true; ComboAddress ca; Sibling::Protocol proto; string raw_key; B64Decode(p.second[1].second, raw_key); parseSiblingString(p.second[0].second, ca, proto); if (p.second.size() == 4) { send_sdb = toBool(p.second[2].second); send_wlbl = toBool(p.second[3].second); } bool skip = false; for (auto& s : v) { if (s->rem == ca) { errlog("setSiblings(): Two siblings with the same address and port are not allowed, ignoring the duplicate (%s)", p.second[0].second); skip = true; break; } } // Skip over duplicates if (skip) continue; // This is for sending when we know the port addPrometheusReplicationSibling(ca.toStringWithPort()); // This is for receiving when the port may be ephemeral addPrometheusReplicationSibling(ca.toString()); // Create the sibling after the Prometheus metrics so connfail stats get updated auto sibling = std::make_shared(ca, proto, raw_key, sibling_connect_timeout, sibling_queue_size, send_sdb, send_wlbl); sibling->checkIgnoreSelf(g_sibling_listen_addr); v.push_back(sibling); } siblings.setState(v); return true; } void setMaxSiblingSendQueueSize(size_t queue_size) { sibling_queue_size.store(queue_size); } void setSiblingConnectTimeout(int timeout) { sibling_connect_timeout.store(timeout); } weakforced-2.10.2/wforce/wforce-sibling.hh000066400000000000000000000143411461473602600204420ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/luawrapper/include/LuaContext.hpp" #include #include "misc.hh" #include "iputils.hh" #include #include #include #include #include #include "sholder.hh" #include "sstuff.hh" #include "sodcrypto.hh" // Represents a replication destination/source // All methods in this class are thread-safe struct Sibling { enum class Protocol : int { UDP = SOCK_DGRAM, TCP = SOCK_STREAM, NONE = -1 }; static const int defaultPort = 4001; explicit Sibling(const ComboAddress& ca); explicit Sibling(const ComboAddress& ca, const Protocol& p, int timeout = 1000, size_t queue_size = 5000, bool sdb_send=true, bool wlbl_send=true); explicit Sibling(const ComboAddress& ca, const Protocol& p, const std::string& key, int timeout = 1000, size_t queue_size = 5000, bool sdb_send=true, bool wlbl_send=true); ~Sibling(); Sibling(const Sibling&) = delete; ComboAddress rem; std::mutex mutx; std::unique_ptr sockp; Protocol proto = Protocol::UDP; int connect_timeout; // milliseconds // The queue is used to process messages in a separate thread // so that replication delays/timeouts don't affect the caller. // To use this asynchronous behaviour, queueMsg() must be used. // To send synchronously, use send(). std::mutex queue_mutx; std::queue queue; std::condition_variable queue_cv; size_t max_queue_size; std::thread queue_thread; // We hang on to this so that the queue thread can be terminated in the destructor bool queue_thread_run; // Whether to send replication and/or wlbl events bool sdb_send; bool wlbl_send; std::atomic success{0}; std::atomic failures{0}; std::atomic rcvd_fail{0}; std::atomic rcvd_success{0}; void send(const std::string& msg); void queueMsg(const std::string& msg); void checkIgnoreSelf(const ComboAddress& ca); void connectSibling(); static Protocol stringToProtocol(const std::string& s) { if (s.compare("tcp") == 0) return Sibling::Protocol::TCP; else if (s.compare("udp") == 0) return Sibling::Protocol::UDP; else if (s.compare("none") == 0) return Sibling::Protocol::NONE; else if (s.empty()) return Sibling::Protocol::UDP; // Default to UDP if no protocol supplied else { std::string err = "Sibling::stringToProtocol(): Unknown protocol " + s; throw WforceException(err); } } static std::string protocolToString(const Protocol& p) { if (p == Protocol::TCP) return std::string("tcp"); else if (p == Protocol::UDP) return std::string("udp"); else if (p == Protocol::NONE) return std::string("none"); else throw WforceException("Sibling::protocolToString(): unknown protocol"); } bool d_ignoreself{false}; // Optional per-sibling encryption - if key is not empty then // it and the nonce will be used, otherwise global key & nonce are used bool d_has_key = false; std::string d_base64_key; std::string d_key; SodiumNonce d_nonce; }; void setMaxSiblingSendQueueSize(size_t queue_size); void setSiblingConnectTimeout(int timeout); // milliseconds void parseSiblingString(const std::string& str, ComboAddress& ca, Sibling::Protocol& proto); std::string createSiblingAddress(const std::string& host, int port, Sibling::Protocol proto); bool removeSibling(const std::string& host, int port, GlobalStateHolder>>& siblings, std::string& output_buffer); bool removeSibling(const std::string& address, GlobalStateHolder>>& siblings, std::string& output_buffer); bool addSibling(const std::string& address, GlobalStateHolder>>& siblings, std::string& output_buffer, bool send_sdb=true, bool send_wlbl=true); bool addSibling(const std::string& host, int port, Sibling::Protocol proto, GlobalStateHolder>>& siblings, std::string& output_buffer, bool send_sdb=true, bool send_wlbl=true); bool addSiblingWithKey(const std::string& host, int port, Sibling::Protocol proto, GlobalStateHolder>>& siblings, std::string& output_buffer, const std::string& key, bool send_sdb=true, bool send_wlbl=true); bool addSiblingWithKey(const std::string& address, GlobalStateHolder>>& siblings, std::string& output_buffer, const std::string& key, bool send_sdb=true, bool send_wlbl=true); bool addSibling(std::shared_ptr sibling, GlobalStateHolder>>& siblings, std::string& output_buffer); bool setSiblings(const vector>& parts, GlobalStateHolder>>& siblings, std::string& output_buffer); bool setSiblingsWithKey(const vector>>>& parts, GlobalStateHolder>>& siblings, std::string& output_buffer); weakforced-2.10.2/wforce/wforce-web.cc000066400000000000000000001421551461473602600175630ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "wforce.hh" #include "sstuff.hh" #include "json11.hpp" #include "ext/incbin/incbin.h" #include "dolog.hh" #include #include #include #include "namespaces.hh" #include #include #include "base64.hh" #include "blackwhitelist.hh" #include "twmap-wrapper.hh" #include "customfunc.hh" #include "perf-stats.hh" #include "luastate.hh" #include "login_tuple.hh" #include "webhook.hh" #include "boost/date_time/posix_time/posix_time.hpp" #include "boost/date_time/gregorian/gregorian.hpp" #include "boost/algorithm/string.hpp" #include "boost/regex.hpp" #include "wforce-prometheus.hh" #include "wforce-sibling.hh" #include "wforce-replication.hh" using std::thread; using namespace boost::posix_time; using namespace boost::gregorian; GlobalStateHolder>> g_report_sinks; GlobalStateHolder>, vector>>>> g_named_report_sinks; bool g_builtin_bl_enabled = true; bool g_builtin_wl_enabled = true; static time_t start = time(0); static inline void setErrorCodeAndReason(drogon::HttpStatusCode code, const std::string& reason, const drogon::HttpResponsePtr& resp) { resp->setStatusCode(code); json11::Json j = json11::Json::object { { "status", "failure" }, { "reason", reason }, }; resp->setBody(j.dump()); } static int uptimeOfProcess() { return time(0) - start; } void reportLog(const LoginTuple& lt) { if (g_verbose) { std::ostringstream os; os << "reportLog: "; os << "remote=\"" << lt.remote.toString() << "\" "; os << "login=\"" << lt.login << "\" "; os << "success=\"" << lt.success << "\" "; os << "policy_reject=\"" << lt.policy_reject << "\" "; os << "pwhash=\"" << std::hex << std::uppercase << lt.pwhash << "\" "; os << "protocol=\"" << lt.protocol << "\" "; os << "device_id=\"" << lt.device_id << "\" "; os << "session_id=\"" << lt.session_id << "\" "; os << DeviceAttrsToString(lt); os << LtAttrsToString(lt); infolog(os.str().c_str()); } } bool g_allowlog_verbose = false; void allowLog(int retval, const std::string& msg, const LoginTuple& lt, const std::vector>& kvs) { std::ostringstream os; if ((retval != 0) || g_allowlog_verbose) { os << "allowLog " << msg << ": "; os << "allow=\"" << retval << "\" "; os << "remote=\"" << lt.remote.toString() << "\" "; os << "login=\"" << lt.login << "\" "; os << "protocol=\"" << lt.protocol << "\" "; os << "device_id=\"" << lt.device_id << "\" "; os << "session_id=\"" << lt.session_id << "\" "; os << DeviceAttrsToString(lt); os << LtAttrsToString(lt); os << "rattrs={"; for (const auto& i : kvs) { os << i.first << "=" << "\"" << i.second << "\"" << " "; } os << "}"; } if ((retval == 0) && (!g_allowlog_verbose)) { // do not log if retval == 0 and not verbose } else if ((retval == 0) && g_allowlog_verbose) { infolog(os.str().c_str()); } else { // only log at notice if login was rejected or tarpitted noticelog(os.str().c_str()); } } void addBLWLEntries(const std::vector& blv, const char* key_name, json11::Json::array& my_entries) { for (auto i = blv.begin(); i != blv.end(); ++i) { json11::Json my_entry = json11::Json::object{ {key_name, (std::string) i->key}, {"expiration", (std::string) boost::posix_time::to_simple_string(i->expiration)}, {"reason", (std::string) i->reason} }; my_entries.push_back(my_entry); } } bool canonicalizeLogin(std::string& login, const drogon::HttpResponsePtr& resp) { bool retval = true; try { // canonicalize the login - e.g. turn "foo" into "foo@foobar.com" and bar into "bar@barfoo.com" login = g_luamultip->canonicalize(login); } catch (LuaContext::ExecutionErrorException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Lua canonicalize function exception: %s", e.what()); retval = false; } catch (...) { setErrorCodeAndReason(drogon::k500InternalServerError, "unknown", resp); retval = false; } return retval; } static std::mutex dump_mutex; void parseDumpEntriesCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; resp->setStatusCode(drogon::k200OK); msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { try { if (msg["dump_host"].is_null() || msg["dump_port"].is_null()) { throw WforceException("One of mandatory parameters [dump_host, dump_port] is missing"); } string myip = msg["dump_host"].string_value(); int myport = msg["dump_port"].int_value(); ComboAddress replication_ca(myip, myport); std::unique_lock lock(dump_mutex, std::defer_lock); if (lock.try_lock()) { thread t(dumpEntriesThread, replication_ca, std::move(lock)); t.detach(); } else { setErrorCodeAndReason(drogon::k503ServiceUnavailable, "A DB dump is already in progress", resp); } } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); } } if (resp->getStatusCode() == drogon::k200OK) resp->setBody(R"({"status":"ok"})"); incCommandStat("dumpEntries"); incPrometheusCommandMetric("dumpEntries"); } void parseSyncCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; resp->setStatusCode(drogon::k200OK); msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { try { if (msg["replication_host"].is_null() || msg["replication_port"].is_null() || msg["callback_url"].is_null() || msg["callback_auth_pw"].is_null()) { throw WforceException( "One of mandatory parameters [replication_host, replication_port, callback_url, callback_auth_pw] is missing"); } string myip = msg["replication_host"].string_value(); int myport = msg["replication_port"].int_value(); ComboAddress replication_ca(myip, myport); std::string callback_url = msg["callback_url"].string_value(); std::string callback_pw = msg["callback_auth_pw"].string_value(); std::string encryption_key; if (!msg["encryption_key"].is_null()) { encryption_key = msg["encryption_key"].string_value(); } else { encryption_key = g_replication.getEncryptionKey(); } thread t(syncDBThread, replication_ca, callback_url, callback_pw, encryption_key); t.detach(); } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); } } if (resp->getStatusCode() == drogon::k200OK) resp->setBody(R"({"status":"ok"})"); incCommandStat("syncDBs"); incPrometheusCommandMetric("syncDBs"); } void parseAddSiblingCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; resp->setStatusCode(drogon::k200OK); msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { try { if (msg["sibling_host"].is_null() || msg["sibling_port"].is_null()) { throw WforceException("One of mandatory parameters [sibling_host, sibling_port] is missing"); } std::string sibling_host = msg["sibling_host"].string_value(); int sibling_port = msg["sibling_port"].int_value(); bool has_encryption_key = false; std::string encryption_key; if (!msg["encryption_key"].is_null()) { has_encryption_key = true; encryption_key = msg["encryption_key"].string_value(); } Sibling::Protocol proto = Sibling::Protocol::UDP; if (!msg["sibling_protocol"].is_null()) { proto = Sibling::stringToProtocol(msg["sibling_protocol"].string_value()); } if (proto == Sibling::Protocol::NONE) { throw WforceException(std::string("Invalid sibling_protocol: ") + msg["sibling_protocol"].string_value()); } std::string error_msg; if (!has_encryption_key) { if (!addSibling(sibling_host, sibling_port, proto, g_replication.getSiblings(), error_msg)) { throw WforceException(error_msg); } } else { if (!addSiblingWithKey(sibling_host, sibling_port, proto, g_replication.getSiblings(), error_msg, encryption_key)) { throw WforceException(error_msg); } } } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); } } if (resp->getStatusCode() == drogon::k200OK) resp->setBody(R"({"status":"ok"})"); incCommandStat("addSibling"); incPrometheusCommandMetric("addSibling"); } void parseRemoveSiblingCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; resp->setStatusCode(drogon::k200OK); msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { try { if (msg["sibling_host"].is_null() || msg["sibling_port"].is_null()) { throw WforceException("One of mandatory parameters [sibling_host, sibling_port] is missing"); } std::string sibling_host = msg["sibling_host"].string_value(); int sibling_port = msg["sibling_port"].int_value(); std::string error_msg; if (!removeSibling(sibling_host, sibling_port, g_replication.getSiblings(), error_msg)) { throw WforceException(error_msg); } } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); } } if (resp->getStatusCode() == drogon::k200OK) resp->setBody(R"({"status":"ok"})"); incCommandStat("removeSibling"); incPrometheusCommandMetric("removeSibling"); } void parseSetSiblingsCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; std::vector>>> new_siblings; resp->setStatusCode(drogon::k200OK); msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { try { if (msg["siblings"].is_null() || !msg["siblings"].is_array()) { throw WforceException("Mandatory parameter [siblings] is missing or is not an array"); } auto siblings = msg["siblings"].array_items(); for (auto& i : siblings) { if (i["sibling_host"].is_null() || i["sibling_port"].is_null()) { throw WforceException("Mandatory parameter(s) [sibling_host, sibling_port] are missing"); } std::string sibling_host = i["sibling_host"].string_value(); int sibling_port = i["sibling_port"].int_value(); std::string encryption_key; if (!i["encryption_key"].is_null()) { encryption_key = i["encryption_key"].string_value(); } else { encryption_key = Base64Encode(g_replication.getEncryptionKey()); } Sibling::Protocol proto = Sibling::Protocol::UDP; if (!i["sibling_protocol"].is_null()) { proto = Sibling::stringToProtocol(i["sibling_protocol"].string_value()); } if (proto == Sibling::Protocol::NONE) { throw WforceException(std::string("Invalid sibling_protocol: ") + msg["sibling_protocol"].string_value()); } new_siblings.emplace_back(std::pair>>{0, {{0, createSiblingAddress( sibling_host, sibling_port, proto)}, {1, encryption_key}}}); } std::string error_msg; if (!setSiblingsWithKey(new_siblings, g_replication.getSiblings(), error_msg)) { throw WforceException(error_msg); } } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); } } if (resp->getStatusCode() == drogon::k200OK) resp->setBody(R"({"status":"ok"})"); incCommandStat("setSiblings"); incPrometheusCommandMetric("setSiblings"); } void parseAddDelBLWLEntryCmd(const drogon::HttpRequestPtr& req, const drogon::HttpResponsePtr& resp, bool addCmd, bool blacklist) { json11::Json msg; string err; resp->setStatusCode(drogon::k200OK); msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { bool haveIP = false; bool haveLogin = false; bool haveNetmask = false; unsigned int bl_seconds = 0; std::string bl_reason; std::string en_login; ComboAddress en_ca; Netmask en_nm; try { if (!msg["ip"].is_null()) { string myip = msg["ip"].string_value(); en_ca = ComboAddress(myip); en_nm = Netmask(myip); haveIP = true; } if (!msg["netmask"].is_null()) { string mynm = msg["netmask"].string_value(); en_nm = Netmask(mynm); haveNetmask = true; } if (!msg["login"].is_null()) { en_login = msg["login"].string_value(); if (!canonicalizeLogin(en_login, resp)) return; haveLogin = true; } if (addCmd) { if (!msg["expire_secs"].is_null()) { bl_seconds = msg["expire_secs"].int_value(); } else { throw std::runtime_error("Missing mandatory expire_secs field"); } if (!msg["reason"].is_null()) { bl_reason = msg["reason"].string_value(); } else { throw std::runtime_error("Missing mandatory reason field"); } } if (haveLogin && haveIP) { if (addCmd) { if (blacklist) g_bl_db.addEntry(en_ca, en_login, bl_seconds, bl_reason); else g_wl_db.addEntry(en_ca, en_login, bl_seconds, bl_reason); } else { if (blacklist) g_bl_db.deleteEntry(en_ca, en_login); else g_wl_db.deleteEntry(en_ca, en_login); } } else if (haveLogin) { if (addCmd) { if (blacklist) g_bl_db.addEntry(en_login, bl_seconds, bl_reason); else g_wl_db.addEntry(en_login, bl_seconds, bl_reason); } else { if (blacklist) g_bl_db.deleteEntry(en_login); else g_wl_db.deleteEntry(en_login); } } else if (haveIP || haveNetmask) { if (addCmd) { if (blacklist) g_bl_db.addEntry(en_nm, bl_seconds, bl_reason); else g_wl_db.addEntry(en_nm, bl_seconds, bl_reason); } else { if (blacklist) g_bl_db.deleteEntry(en_nm); else g_wl_db.deleteEntry(en_nm); } } } catch (std::runtime_error& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); } catch (...) { resp->setStatusCode(drogon::k500InternalServerError); resp->setBody(R"({"status":"failure"})"); } } if (resp->getStatusCode() == drogon::k200OK) resp->setBody(R"({"status":"ok"})"); } void parseAddBLEntryCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { parseAddDelBLWLEntryCmd(req, resp, true, true); incCommandStat("addBLEntry"); incPrometheusCommandMetric("addBLEntry"); } void parseDelBLEntryCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { parseAddDelBLWLEntryCmd(req, resp, false, true); incCommandStat("delBLEntry"); incPrometheusCommandMetric("delBLEntry"); } void parseAddWLEntryCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { parseAddDelBLWLEntryCmd(req, resp, true, false); incCommandStat("addWLEntry"); incPrometheusCommandMetric("addWLEntry"); } void parseDelWLEntryCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { parseAddDelBLWLEntryCmd(req, resp, false, false); incCommandStat("delWLEntry"); incPrometheusCommandMetric("delWLEntry"); } void parseResetCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { try { bool haveIP = false; bool haveLogin = false; std::string en_type, en_login; ComboAddress en_ca; if (!msg["ip"].is_null()) { en_ca = ComboAddress(msg["ip"].string_value()); haveIP = true; } if (!msg["login"].is_null()) { en_login = msg["login"].string_value(); // canonicalize the login - e.g. turn "foo" into "foo@foobar.com" and bar into "bar@barfoo.com" if (!canonicalizeLogin(en_login, resp)) return; haveLogin = true; } if (haveLogin && haveIP) { en_type = "ip:login"; g_bl_db.deleteEntry(en_ca, en_login); } else if (haveLogin) { en_type = "login"; g_bl_db.deleteEntry(en_login); } else if (haveIP) { en_type = "ip"; g_bl_db.deleteEntry(en_ca); } if (!haveLogin && !haveIP) { setErrorCodeAndReason(drogon::k400BadRequest, "No ip or login field supplied", resp); } else { bool reset_ret; { reset_ret = g_luamultip->reset(en_type, en_login, en_ca); } if (reset_ret) { resp->setStatusCode(drogon::k200OK); resp->setBody(R"({"status":"ok"})"); } else { setErrorCodeAndReason(drogon::k500InternalServerError, "reset function returned false", resp); } } // generate webhook events json11::Json jobj = json11::Json::object{{"login", en_login}, {"ip", en_ca.toString()}, {"type", "wforce_reset"}}; for (const auto& h : g_webhook_db.getWebHooksForEvent("reset")) { if (auto hs = h.lock()) { g_webhook_runner.runHook("reset", hs, jobj); } } } catch (LuaContext::ExecutionErrorException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Lua reset function exception: %s", e.what()); } catch (...) { resp->setStatusCode(drogon::k500InternalServerError); resp->setBody(R"({"status":"failure"})"); } } incCommandStat("reset"); incPrometheusCommandMetric("reset"); } void parseReportCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { try { LoginTuple lt; lt.from_json(msg, g_ua_parser_p); // canonicalize the login - e.g. turn "foo" into "foo@foobar.com" and bar into "bar@barfoo.com" if (!canonicalizeLogin(lt.login, resp)) return; lt.t = getDoubleTime(); // set the time reportLog(lt); g_stats.reports++; { g_luamultip->report(lt); } // If any normal or named report sinks are configured, send the report to one of them sendReportSink(lt); // XXX - this is deprecated now in favour of NamedReportSinks sendNamedReportSink(lt.serialize()); json11::Json msg = lt.to_json(); json11::Json::object jobj = msg.object_items(); jobj.insert(make_pair("type", "wforce_report")); for (const auto& h : g_webhook_db.getWebHooksForEvent("report")) { if (auto hs = h.lock()) g_webhook_runner.runHook("report", hs, jobj); } resp->setStatusCode(drogon::k200OK); resp->setBody(R"({"status":"ok"})"); } catch (LuaContext::ExecutionErrorException& e) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; try { std::rethrow_if_nested(e); setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Lua function [%s] exception: %s", command, e.what()); } catch (const std::exception& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.what(), resp); errlog("Exception in command [%s] exception: %s", command, ne.what()); } catch (const WforceException& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.reason, resp); errlog("Exception in command [%s] exception: %s", command, ne.reason); } } catch (const std::exception& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Exception in command [%s] exception: %s", command, e.what()); } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); } } incCommandStat("report"); incPrometheusCommandMetric("report"); } #define OX_PROTECT_NOTIFY 0 void genProtectHookData(const json11::Json& lt, const std::string& msg, json11::Json& ret_json) { json11::Json::object jobj; double dtime = lt["t"].number_value(); unsigned int isecs = (int) dtime; unsigned int iusecs = (dtime - isecs) * 1000000; ptime t(date(1970, Jan, 01), seconds(isecs) + microsec(iusecs)); jobj.insert(std::make_pair("timestamp", to_iso_extended_string(t) + "Z")); jobj.insert(std::make_pair("user", lt["login"].string_value())); jobj.insert(std::make_pair("device", lt["device_id"].string_value())); jobj.insert(std::make_pair("ip", lt["remote"].string_value())); jobj.insert(std::make_pair("message", msg)); jobj.insert(std::make_pair("code", OX_PROTECT_NOTIFY)); jobj.insert(std::make_pair("application", "wforce")); ret_json = json11::Json(std::move(jobj)); } bool allow_filter(std::shared_ptr hook, int status) { bool retval = false; if (hook->hasConfigKey("allow_filter")) { std::string filter = hook->getConfigKey("allow_filter"); if (((filter.find("reject") != string::npos) && (status < 0)) || (((filter.find("allow") != string::npos) && (status == 0))) || (((filter.find("tarpit") != string::npos) && (status > 0)))) { retval = true; vdebuglog("allow_filter: filter evaluates to true (allowing event)"); } } else retval = true; return retval; } void runAllowWebHook(const json11::Json& request, const json11::Json& response, int status) { json11::Json jobj = json11::Json::object{{"request", request}, {"response", response}, {"type", "wforce_allow"}}; for (const auto& h : g_webhook_db.getWebHooksForEvent("allow")) { if (auto hs = h.lock()) { json11::Json pj; if (hs->hasConfigKey("ox-protect")) { genProtectHookData(jobj["request"], hs->getConfigKey("ox-protect"), pj); if (allow_filter(hs, status)) g_webhook_runner.runHook("allow", hs, pj); } else { if (allow_filter(hs, status)) g_webhook_runner.runHook("allow", hs, jobj); } } } } enum AllowReturnFields { allowRetStatus = 0, allowRetMsg = 1, allowRetLogMsg = 2, allowRetAttrs = 3 }; const std::string ipPattern = "{ip}"; const std::string loginPattern = "{login}"; std::string substituteIPLogin(const std::string msg, const ComboAddress& ca, const std::string login) { bool replace_ip = false; bool replace_login = false; std::string new_msg; if (msg.find(ipPattern) != std::string::npos) replace_ip = true; if (msg.find(loginPattern) != std::string::npos) replace_login = true; if (replace_ip || replace_login) // The maximum length of an IPv6 address string is 39 characters new_msg.reserve(msg.length()+39+login.length()); // This should prevent a realloc in the majority of cases if (replace_ip) new_msg = boost::algorithm::replace_all_copy(msg, "{ip}", ca.toString()); else new_msg = msg; if (replace_login) boost::algorithm::replace_all(new_msg, "{login}", login); return new_msg; } void parseAllowCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; std::vector> ret_attrs; incCommandStat("allow"); incPrometheusCommandMetric("allow"); g_stats.allows++; resp->setStatusCode(drogon::k200OK); msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { LoginTuple lt; int status = -1; std::string ret_msg; try { lt.from_json(msg, g_ua_parser_p); lt.t = getDoubleTime(); } catch (const std::exception& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Exception in command [%s] exception: %s", command, e.what()); return; } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); return; } if (!canonicalizeLogin(lt.login, resp)) { return; } // check the built-in whitelists bool whitelisted = false; BlackWhiteListEntry wle; if (g_builtin_wl_enabled) { if (g_wl_db.getEntry(lt.remote, wle)) { std::vector> log_attrs = {{"expiration", boost::posix_time::to_simple_string(wle.expiration)}, {"reason", wle.reason}, {"whitelisted", "1"}, {"key", "ip"}}; status = 0; allowLog(status, std::string("whitelisted IP"), lt, log_attrs); ret_msg = substituteIPLogin(g_wl_db.getIPRetMsg(), lt.remote, lt.login); ret_attrs = std::move(log_attrs); whitelisted = true; } else if (g_wl_db.getEntry(lt.login, wle)) { std::vector> log_attrs = {{"expiration", boost::posix_time::to_simple_string(wle.expiration)}, {"reason", wle.reason}, {"whitelisted", "1"}, {"key", "login"}}; status = 0; allowLog(status, std::string("whitelisted Login"), lt, log_attrs); ret_msg = substituteIPLogin(g_wl_db.getLoginRetMsg(), lt.remote, lt.login); ret_attrs = std::move(log_attrs); whitelisted = true; } else if (g_wl_db.getEntry(lt.remote, lt.login, wle)) { std::vector> log_attrs = {{"expiration", boost::posix_time::to_simple_string(wle.expiration)}, {"reason", wle.reason}, {"whitelisted", "1"}, {"key", "iplogin"}}; status = 0; allowLog(status, std::string("whitelisted IPLogin"), lt, log_attrs); ret_msg = substituteIPLogin(g_wl_db.getIPLoginRetMsg(), lt.remote, lt.login); ret_attrs = std::move(log_attrs); whitelisted = true; } } // next check the built-in blacklists bool blacklisted = false; BlackWhiteListEntry ble; if (g_builtin_bl_enabled) { if (g_bl_db.getEntry(lt.remote, ble)) { std::vector> log_attrs = {{"expiration", boost::posix_time::to_simple_string(ble.expiration)}, {"reason", ble.reason}, {"blacklisted", "1"}, {"key", "ip"}}; allowLog(status, std::string("blacklisted IP"), lt, log_attrs); ret_msg = substituteIPLogin(g_bl_db.getIPRetMsg(), lt.remote, lt.login); ret_attrs = std::move(log_attrs); blacklisted = true; } else if (g_bl_db.getEntry(lt.login, ble)) { std::vector> log_attrs = {{"expiration", boost::posix_time::to_simple_string(ble.expiration)}, {"reason", ble.reason}, {"blacklisted", "1"}, {"key", "login"}}; allowLog(status, std::string("blacklisted Login"), lt, log_attrs); ret_msg = substituteIPLogin(g_bl_db.getLoginRetMsg(), lt.remote, lt.login); ret_attrs = std::move(log_attrs); blacklisted = true; } else if (g_bl_db.getEntry(lt.remote, lt.login, ble)) { std::vector> log_attrs = {{"expiration", boost::posix_time::to_simple_string(ble.expiration)}, {"reason", ble.reason}, {"blacklisted", "1"}, {"key", "iplogin"}}; allowLog(status, std::string("blacklisted IPLogin"), lt, log_attrs); ret_msg = substituteIPLogin(g_bl_db.getIPLoginRetMsg(), lt.remote, lt.login); ret_attrs = std::move(log_attrs); blacklisted = true; } } if (blacklisted || whitelisted) { // run webhook json11::Json::object jattrs; for (auto& i : ret_attrs) { jattrs.insert(make_pair(i.first, json11::Json(i.second))); } msg = json11::Json::object{{"status", status}, {"msg", ret_msg}, {"r_attrs", jattrs}}; runAllowWebHook(lt.to_json(), msg, status); if (blacklisted) { g_stats.denieds++; incCommandStat("allow_blacklisted"); incPrometheusAllowStatusMetric("blacklisted"); incCommandStat("allow_denied"); incPrometheusAllowStatusMetric("denied"); } if (whitelisted) { incCommandStat("allow_whitelisted"); incPrometheusAllowStatusMetric("whitelisted"); } } else { try { AllowReturn ar; { ar = g_luamultip->allow(lt); } status = std::get(ar); ret_msg = std::get(ar); std::string log_msg = std::get(ar); std::vector> log_attrs = std::get(ar); // log the results of the allow function allowLog(status, log_msg, lt, log_attrs); ret_attrs = std::move(log_attrs); if (status < 0) { g_stats.denieds++; incCommandStat("allow_denied"); incPrometheusAllowStatusMetric("denied"); } else if (status > 0) { incCommandStat("allow_tarpitted"); incPrometheusAllowStatusMetric("tarpitted"); } else { incCommandStat("allow_allowed"); incPrometheusAllowStatusMetric("allowed"); } json11::Json::object jattrs; for (auto& i : ret_attrs) { jattrs.insert(make_pair(i.first, json11::Json(i.second))); } msg = json11::Json::object{{"status", status}, {"msg", ret_msg}, {"r_attrs", jattrs}}; // generate webhook events runAllowWebHook(lt.to_json(), msg, status); resp->setStatusCode(drogon::k200OK); resp->setBody(msg.dump()); } catch (LuaContext::ExecutionErrorException& e) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; try { std::rethrow_if_nested(e); setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Lua function [%s] exception: %s", command, e.what()); } catch (const std::exception& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.what(), resp); errlog("Exception in command [%s] exception: %s", command, ne.what()); } catch (const WforceException& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.reason, resp); errlog("Exception in command [%s] exception: %s", command, ne.reason); } } catch (const std::exception& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Exception in command [%s] exception: %s", command, e.what()); } catch (const WforceException& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.reason, resp); errlog("Exception in command [%s] exception: %s", command, e.reason); } } if (resp->getStatusCode() == drogon::k200OK) { json11::Json::object jattrs; for (auto& i : ret_attrs) { jattrs.insert(make_pair(i.first, json11::Json(i.second))); } msg = json11::Json::object{{"status", status}, {"msg", ret_msg}, {"r_attrs", jattrs}}; resp->setBody(msg.dump()); } } } // XXX BEGIN Deprecated functions - will be removed in a later release void parseStatsCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { struct rusage ru; getrusage(RUSAGE_SELF, &ru); json11::Json my_json = json11::Json::object{ {"reports", (int) g_stats.reports}, {"allows", (int) g_stats.allows}, {"denieds", (int) g_stats.denieds}, {"user-msec", (int) (ru.ru_utime.tv_sec * 1000ULL + ru.ru_utime.tv_usec / 1000)}, {"sys-msec", (int) (ru.ru_stime.tv_sec * 1000ULL + ru.ru_stime.tv_usec / 1000)}, {"uptime", uptimeOfProcess()}, {"perfstats", perfStatsToJson()}, {"commandstats", commandStatsToJson()}, {"customstats", customStatsToJson()} }; resp->setStatusCode(drogon::k200OK); resp->setBody(my_json.dump()); incCommandStat("stats"); incPrometheusCommandMetric("stats"); } // XXX END Deprecated functions - will be removed in a later release void parseGetBLWLCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp, bool blacklist) { json11::Json::array my_entries; std::vector lv; std::string key_name; std::string cmd_stat; if (blacklist) { key_name = "bl_entries"; cmd_stat = "getBL"; } else { key_name = "wl_entries"; cmd_stat = "getWL"; } if (blacklist) lv = g_bl_db.getIPEntries(); else lv = g_wl_db.getIPEntries(); addBLWLEntries(lv, "ip", my_entries); if (blacklist) lv = g_bl_db.getLoginEntries(); else lv = g_wl_db.getLoginEntries(); addBLWLEntries(lv, "login", my_entries); if (blacklist) lv = g_bl_db.getIPLoginEntries(); else lv = g_wl_db.getIPLoginEntries(); addBLWLEntries(lv, "iplogin", my_entries); json11::Json ret_json = json11::Json::object{ {key_name, my_entries} }; resp->setStatusCode(drogon::k200OK); resp->setBody(ret_json.dump()); incCommandStat(cmd_stat); incPrometheusCommandMetric(cmd_stat); } void parseGetBLCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { parseGetBLWLCmd(req, command, resp, true); } void parseGetWLCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { parseGetBLWLCmd(req, command, resp, false); } void parseGetStatsCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { bool haveIP = false; bool haveLogin = false; std::string en_type, en_login; std::string key_name, key_value; TWKeyType lookup_key; bool is_blacklisted = false; bool is_whitelisted = false; std::string bl_reason; std::string bl_expire; std::string wl_reason; std::string wl_expire; ComboAddress en_ca; BlackWhiteListEntry bwle; try { if (!msg["ip"].is_null()) { string myip = msg["ip"].string_value(); en_ca = ComboAddress(myip); haveIP = true; } if (!msg["login"].is_null()) { en_login = msg["login"].string_value(); if (!canonicalizeLogin(en_login, resp)) return; haveLogin = true; } if (haveLogin && haveIP) { key_name = "ip_login"; key_value = en_ca.toString() + ":" + en_login; lookup_key = key_value; if (g_bl_db.getEntry(en_ca, en_login, bwle)) { is_blacklisted = true; bl_reason = bwle.reason; bl_expire = boost::posix_time::to_simple_string(bwle.expiration); } if (g_wl_db.getEntry(en_ca, en_login, bwle)) { is_whitelisted = true; wl_reason = bwle.reason; wl_expire = boost::posix_time::to_simple_string(bwle.expiration); } } else if (haveLogin) { key_name = "login"; key_value = en_login; lookup_key = en_login; if (g_bl_db.getEntry(en_login, bwle)) { is_blacklisted = true; bl_reason = bwle.reason; bl_expire = boost::posix_time::to_simple_string(bwle.expiration); } if (g_wl_db.getEntry(en_login, bwle)) { is_whitelisted = true; wl_reason = bwle.reason; wl_expire = boost::posix_time::to_simple_string(bwle.expiration); } } else if (haveIP) { key_name = "ip"; key_value = en_ca.toString(); lookup_key = en_ca; if (g_bl_db.getEntry(en_ca, bwle)) { is_blacklisted = true; bl_reason = bwle.reason; bl_expire = boost::posix_time::to_simple_string(bwle.expiration); } if (g_wl_db.getEntry(en_ca, bwle)) { is_whitelisted = true; wl_reason = bwle.reason; wl_expire = boost::posix_time::to_simple_string(bwle.expiration); } } } catch (...) { setErrorCodeAndReason(drogon::k400BadRequest, "Could not parse input", resp); return; } if (!haveLogin && !haveIP) { setErrorCodeAndReason(drogon::k400BadRequest, "No ip or login field supplied", resp); } else { json11::Json::object js_db_stats; { std::lock_guard lock(dbMap_mutx); for (auto i = dbMap.begin(); i != dbMap.end(); ++i) { std::string dbName = i->first; std::vector> db_fields; if (i->second.get_all_fields(lookup_key, db_fields)) { json11::Json::object js_db_fields; for (auto j = db_fields.begin(); j != db_fields.end(); ++j) { js_db_fields.insert(make_pair(j->first, j->second)); } js_db_stats.insert(make_pair(dbName, js_db_fields)); } } } json11::Json ret_json = json11::Json::object{ {key_name, key_value}, {"whitelisted", is_whitelisted}, {"wl_reason", wl_reason}, {"wl_expire", wl_expire}, {"blacklisted", is_blacklisted}, {"bl_reason", bl_reason}, {"bl_expire", bl_expire}, {"stats", js_db_stats} }; resp->setStatusCode(drogon::k200OK); resp->setBody(ret_json.dump()); } } incCommandStat("getDBStats"); incPrometheusCommandMetric("getDBStats"); } enum CustomReturnFields { customRetStatus = 0, customRetAttrs = 1 }; void parseCustomCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { json11::Json msg; string err; KeyValVector ret_attrs; msg = json11::Json::parse(req->bodyData(), err); if (msg.is_null()) { setErrorCodeAndReason(drogon::k400BadRequest, err, resp); } else { CustomFuncArgs cfa; bool status = false; try { cfa.setAttrs(msg); } catch (...) { setErrorCodeAndReason(drogon::k400BadRequest, "Could not parse input", resp); return; } try { bool reportSink = false; CustomFuncReturn cr; { cr = g_luamultip->custom_func(command, cfa, reportSink); } status = std::get(cr); ret_attrs = std::get(cr); json11::Json::object jattrs; for (auto& i : ret_attrs) { jattrs.insert(make_pair(i.first, json11::Json(i.second))); } msg = json11::Json::object{{"success", status}, {"r_attrs", jattrs}}; // send the custom parameters to the report sink if configured if (reportSink) sendNamedReportSink(cfa.serialize()); resp->setStatusCode(drogon::k200OK); resp->setBody(msg.dump()); } catch (LuaContext::ExecutionErrorException& e) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; try { std::rethrow_if_nested(e); setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Lua custom function [%s] exception: %s", command, e.what()); } catch (const std::exception& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.what(), resp); errlog("Exception in command [%s] exception: %s", command, ne.what()); } catch (const WforceException& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.reason, resp); errlog("Exception in command [%s] exception: %s", command, ne.reason); } } catch (const std::exception& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Exception in command [%s] exception: %s", command, e.what()); } } incCommandStat(command); incPrometheusCommandMetric(command); } void parseCustomGetCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { string err; try { std::string ret_msg = g_luamultip->custom_get_func(command); resp->setStatusCode(drogon::k200OK); resp->setBody(ret_msg); } catch (LuaContext::ExecutionErrorException& e) { resp->setStatusCode(drogon::k500InternalServerError); std::stringstream ss; try { std::rethrow_if_nested(e); setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Lua custom function [%s] exception: %s", command, e.what()); } catch (const std::exception& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.what(), resp); errlog("Exception in command [%s] exception: %s", command, ne.what()); } catch (const WforceException& ne) { setErrorCodeAndReason(drogon::k500InternalServerError, ne.reason, resp); errlog("Exception in command [%s] exception: %s", command, ne.reason); } } catch (const std::exception& e) { setErrorCodeAndReason(drogon::k500InternalServerError, e.what(), resp); errlog("Exception in command [%s] exception: %s", command, e.what()); } incCommandStat(command + "_Get"); incPrometheusCommandMetric(command + "_Get"); } std::atomic g_ping_up{false}; void parsePingCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { resp->setStatusCode(drogon::k200OK); if (g_ping_up) resp->setBody(R"({"status":"ok"})"); else resp->setBody(R"({"status":"warmup"})"); incCommandStat("ping"); incPrometheusCommandMetric("ping"); } void parseSyncDoneCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp) { resp->setStatusCode(drogon::k200OK); g_ping_up = true; resp->setBody(R"({"status":"ok"})"); incCommandStat("syncDone"); incPrometheusCommandMetric("syncDone"); } void registerWebserverCommands() { addCommandStat("addWLEntry"); addPrometheusCommandMetric("addWLEntry"); g_webserver.registerFunc("addWLEntry", HTTPVerb::POST, WforceWSFunc(parseAddWLEntryCmd)); addCommandStat("delWLEntry"); addPrometheusCommandMetric("delWLEntry"); g_webserver.registerFunc("delWLEntry", HTTPVerb::POST, WforceWSFunc(parseDelWLEntryCmd)); addCommandStat("addBLEntry"); addPrometheusCommandMetric("addBLEntry"); g_webserver.registerFunc("addBLEntry", HTTPVerb::POST, WforceWSFunc(parseAddBLEntryCmd)); addCommandStat("delBLEntry"); addPrometheusCommandMetric("delWLEntry"); g_webserver.registerFunc("delBLEntry", HTTPVerb::POST, WforceWSFunc(parseDelBLEntryCmd)); addCommandStat("reset"); addPrometheusCommandMetric("reset"); g_webserver.registerFunc("reset", HTTPVerb::POST, WforceWSFunc(parseResetCmd)); addCommandStat("report"); addPrometheusCommandMetric("report"); g_webserver.registerFunc("report", HTTPVerb::POST, WforceWSFunc(parseReportCmd)); addCommandStat("allow"); addPrometheusCommandMetric("allow"); addCommandStat("allow_allowed"); addPrometheusAllowStatusMetric("allowed"); addCommandStat("allow_blacklisted"); addPrometheusAllowStatusMetric("blacklisted"); addCommandStat("allow_whitelisted"); addPrometheusAllowStatusMetric("whitelisted"); addCommandStat("allow_denied"); addPrometheusAllowStatusMetric("denied"); addCommandStat("allow_tarpitted"); addPrometheusAllowStatusMetric("tarpitted"); g_webserver.registerFunc("allow", HTTPVerb::POST, WforceWSFunc(parseAllowCmd)); addCommandStat("stats"); addPrometheusCommandMetric("stats"); g_webserver.registerFunc("stats", HTTPVerb::GET, WforceWSFunc(parseStatsCmd)); addCommandStat("getBL"); addPrometheusCommandMetric("getBL"); g_webserver.registerFunc("getBL", HTTPVerb::GET, WforceWSFunc(parseGetBLCmd)); addCommandStat("getWL"); addPrometheusCommandMetric("getWL"); g_webserver.registerFunc("getWL", HTTPVerb::GET, WforceWSFunc(parseGetWLCmd)); addCommandStat("getDBStats"); addPrometheusCommandMetric("getDBStats"); g_webserver.registerFunc("getDBStats", HTTPVerb::POST, WforceWSFunc(parseGetStatsCmd)); addCommandStat("ping"); addPrometheusCommandMetric("ping"); g_webserver.registerFunc("ping", HTTPVerb::GET, WforceWSFunc(parsePingCmd)); addCommandStat("syncDBs"); addPrometheusCommandMetric("syncDBs"); g_webserver.registerFunc("syncDBs", HTTPVerb::POST, WforceWSFunc(parseSyncCmd)); addCommandStat("syncDone"); addPrometheusCommandMetric("syncDone"); g_webserver.registerFunc("syncDone", HTTPVerb::GET, WforceWSFunc(parseSyncDoneCmd)); addCommandStat("dumpEntries"); addPrometheusCommandMetric("dumpEntries"); g_webserver.registerFunc("dumpEntries", HTTPVerb::POST, WforceWSFunc(parseDumpEntriesCmd)); addCommandStat("addSibling"); addPrometheusCommandMetric("addSibling"); g_webserver.registerFunc("addSibling", HTTPVerb::POST, WforceWSFunc(parseAddSiblingCmd)); addCommandStat("removeSibling"); addPrometheusCommandMetric("removeSibling"); g_webserver.registerFunc("removeSibling", HTTPVerb::POST, WforceWSFunc(parseRemoveSiblingCmd)); addCommandStat("setSiblings"); addPrometheusCommandMetric("setSiblings"); g_webserver.registerFunc("setSiblings", HTTPVerb::POST, WforceWSFunc(parseSetSiblingsCmd)); } weakforced-2.10.2/wforce/wforce-web.hh000066400000000000000000000027421461473602600175720ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include // If false, then ping commands will return "warmup". // If true then ping commands will return "ok" extern std::atomic g_ping_up; void parseCustomCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp); void parseCustomGetCmd(const drogon::HttpRequestPtr& req, const std::string& command, const drogon::HttpResponsePtr& resp); void registerWebserverCommands(); weakforced-2.10.2/wforce/wforce.cc000066400000000000000000001021651461473602600170050ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #define SYSLOG_NAMES #include #include "wforce.hh" #include "wforce_ns.hh" #include "sstuff.hh" #include "misc.hh" #include #include #include "dolog.hh" #include #include #include "base64.hh" #include #include "json11.hpp" #include #include #include "sodcrypto.hh" #include "blackwhitelist.hh" #include "perf-stats.hh" #include "luastate.hh" #include "webhook.hh" #include "lock.hh" #include "wforce-web.hh" #include "twmap-wrapper.hh" #include "replication_sdb.hh" #include "wforce-replication.hh" #include "minicurl.hh" #include "iputils.hh" #include "ext/threadname.hh" #include "wforce-prometheus.hh" #include #ifdef HAVE_LIBSYSTEMD #include #endif #include "device_parser.hh" #include "wforce_ns.hh" using std::atomic; using std::thread; bool g_verbose=false; bool g_docker=false; LogLevel g_loglevel{LogLevel::Info}; struct WForceStats g_stats; bool g_console; string g_outputBuffer; WebHookRunner g_webhook_runner; WebHookDB g_webhook_db; WebHookDB g_custom_webhook_db; WforceWebserver g_webserver; syncData g_sync_data; WforceReplication g_replication; curlTLSOptions g_curl_tls_options; struct SiblingQueueItem { std::string msg; ComboAddress remote; std::shared_ptr recv_sibling; }; std::string g_configDir; // where the config files are located std::shared_ptr g_ua_parser_p; struct { bool beDaemon{false}; bool underSystemd{false}; bool underDocker{false}; bool beClient{false}; string command; string config; string regexes; unsigned int facility{LOG_DAEMON}; } g_cmdLine; bool getMsgLen(int fd, uint16_t* len) try { uint16_t raw; int ret = readn2(fd, &raw, 2); if(ret != 2) return false; *len = ntohs(raw); return true; } catch(...) { return false; } bool putMsgLen(int fd, uint16_t len) try { uint16_t raw = htons(len); int ret = writen2(fd, &raw, 2); return ret==2; } catch(...) { return false; } std::mutex g_luamutex; LuaContext g_lua; int g_num_luastates=NUM_LUA_STATES; std::shared_ptr g_luamultip; static void daemonize(void) { if(fork()) _exit(0); // bye bye setsid(); int i=open("/dev/null",O_RDWR); /* open stdin */ if(i < 0) ; // L<begin(); it != g_luamultip->end(); ++it) { std::lock_guard lock((*it)->lua_mutex); (*it)->lua_context.executeCode< boost::optional< boost::variant< string > > >(line); } { std::lock_guard lock(g_luamutex); g_outputBuffer.clear(); auto ret=g_lua.executeCode< boost::optional< boost::variant< string > > >(line); if(ret) { if (const auto strValue = boost::get(&*ret)) { response=*strValue; } } else response=g_outputBuffer; } } catch(const LuaContext::WrongTypeException& e) { response = "Command returned an object we can't print: " +std::string(e.what()) + "\n"; // tried to return something we don't understand } catch(const LuaContext::ExecutionErrorException& e) { response = "Error: " + string(e.what()) + ": "; try { std::rethrow_if_nested(e); } catch(const std::exception& e) { // e is the exception that was thrown from inside the lambda response+= string(e.what()); } } catch(const LuaContext::SyntaxErrorException& e) { response = "Error: " + string(e.what()) + ": "; } response = sodEncryptSym(response, key, writingNonce); putMsgLen(fd, response.length()); writen2(fd, response.c_str(), (uint16_t)response.length()); } // The Socket class wrapper will close the socket for us infolog("Closed control connection from %s", client.toStringWithPort()); } catch(std::exception& e) { errlog("Got an exception in client connection from %s: %s", client.toStringWithPort(), e.what()); } void controlThread(int fd, ComboAddress local) try { ComboAddress client; int sock; setThreadName("wf/ctrl-accept"); noticelog("Accepting control connections on %s", local.toStringWithPort()); while((sock=SAccept(fd, client)) >= 0) { infolog("Got control connection from %s", client.toStringWithPort()); thread t(controlClientThread, sock, client); t.detach(); } } catch(std::exception& e) { close(fd); errlog("Control connection died: %s", e.what()); } void doClient(ComboAddress server, const std::string& command) { cout<<"Connecting to "< dupper; { ifstream history(".history"); string line; while(getline(history, line)) add_history(line.c_str()); } ofstream history(".history", std::ios_base::app); string lastline; for(;;) { char* sline = readline("> "); rl_bind_key('\t',rl_complete); if(!sline) break; string line(sline); if(!line.empty() && line != lastline) { add_history(sline); history << sline < dupper; { ifstream history(".history"); string line; while(getline(history, line)) add_history(line.c_str()); } ofstream history(".history", std::ios_base::app); string lastline; for(;;) { char* sline = readline("> "); rl_bind_key('\t',rl_complete); if(!sline) break; string line(sline); if(!line.empty() && line != lastline) { add_history(sline); history << sline <begin(); it != g_luamultip->end(); ++it) { std::lock_guard lock((*it)->lua_mutex); (*it)->lua_context.executeCode< boost::optional< boost::variant< string > > >(line); } } { std::lock_guard lock(g_luamutex); g_outputBuffer.clear(); auto ret=g_lua.executeCode< boost::optional< boost::variant< string > > >(line); if(ret) { if (const auto strValue = boost::get(&*ret)) { cout<<*strValue< g_report_sink_rr(0); void sendReportSink(const LoginTuple& lt) { auto rsinks = g_report_sinks.getLocal(); auto msg = lt.serialize(); auto vsize = rsinks->size(); if (vsize == 0) return; // round-robin between report sinks unsigned int i = g_report_sink_rr++ % vsize; (*rsinks)[i]->queueMsg(msg); } void sendNamedReportSink(const std::string& msg) { auto rsinks = g_named_report_sinks.getLocal(); for (auto& i : *rsinks) { auto vsize = i.second.second.size(); if (vsize == 0) continue; // round-robin between report sinks unsigned int j = (*i.second.first)++ % vsize; auto& vec = i.second.second; vec[j]->queueMsg(msg); } } void setMiniCurlTLSOptions(MiniCurl& mc) { mc.setCurlOption(CURLOPT_SSL_VERIFYPEER, g_curl_tls_options.verifyPeer ? 1L : 0L); mc.setCurlOption(CURLOPT_SSL_VERIFYHOST, g_curl_tls_options.verifyHost ? 2L : 0L); if (g_curl_tls_options.caCertBundleFile.length() != 0) mc.setCurlOption(CURLOPT_CAINFO, g_curl_tls_options.caCertBundleFile.c_str()); if (g_curl_tls_options.clientCertFile.length() != 0) mc.setCurlOption(CURLOPT_SSLCERT, g_curl_tls_options.clientCertFile.c_str()); if (g_curl_tls_options.clientKeyFile.length() != 0) mc.setCurlOption(CURLOPT_SSLKEY, g_curl_tls_options.clientKeyFile.c_str()); } json11::Json callWforceGetURL(const std::string& url, const std::string& password, std::string& err) { MiniCurl mc; MiniCurlHeaders mch; mc.setTimeout(5); setMiniCurlTLSOptions(mc); mch.insert(std::make_pair("Authorization", "Basic " + Base64Encode(std::string("wforce") + ":" + password))); std::string get_result = mc.getURL(url, mch); return json11::Json::parse(get_result, err); } json11::Json callWforcePostURL(const std::string& url, const std::string& password, const std::string& post_body, std::string& err) { MiniCurl mc; MiniCurlHeaders mch; std::string post_res, post_err; mc.setTimeout(5); setMiniCurlTLSOptions(mc); mch.insert(std::make_pair("Authorization", "Basic " + Base64Encode(std::string("wforce") + ":" + password))); mch.insert(std::make_pair("Content-Type", "application/json")); if (mc.postURL(url, post_body, mch, post_res, post_err)) { return json11::Json::parse(post_res, err); } else { err = post_err; return json11::Json(); } } unsigned int dumpEntriesToNetwork(const ComboAddress& ca) { Socket sock(ca.sin4.sin_family, SOCK_STREAM, 0); // This will be automatically closed when the function ends sock.connect(ca); unsigned num_synced = 0; // loop through the DBs std::map my_dbmap; { std::lock_guard lock(dbMap_mutx); // copy (this is safe - everything important is in a shared ptr) my_dbmap = dbMap; } sock.writen("{"); for (auto& i : my_dbmap) { TWStringStatsDBWrapper sdb = i.second; std::string db_name = i.first; sock.writen("\"" + db_name + "\": {"); for (auto vi = sdb.begin(); vi != sdb.end(); ++vi) { for (auto it = sdb.startDBDump(vi); it != sdb.DBDumpIteratorEnd(vi); ++it) { try { TWStatsDBEntry entry; std::string key; if (sdb.DBGetEntry(vi, it, entry, key)) { json11::Json::array windows; json11::Json::object fields; for (auto& fit : entry) { for (auto& wit : fit.second) { windows.push_back(wit); } fields.emplace(make_pair(fit.first, windows)); } sock.writen("\"" + key + "\": "); sock.writen(json11::Json(fields).dump()); auto dupe_it = it; if (++dupe_it != sdb.DBDumpIteratorEnd(vi)) { sock.writen(","); } num_synced++; } } catch(const std::exception& e) { sdb.endDBDump(vi); auto eptr = std::current_exception(); std::rethrow_exception(eptr); } } sdb.endDBDump(vi); } sock.writen("}"); } sock.writen("}"); return num_synced; } // This function is only called once the lock on the mutex is acquired // The lock is released automatically once this function finishes void dumpEntriesThread(const ComboAddress& ca, std::unique_lock lock) { unsigned int num_synced = 0; noticelog("Dumping Entries to: %s", ca.toStringWithPort()); try { num_synced = dumpEntriesToNetwork(ca); infolog("Dump of Entries to: %s was completed. Dumped %d entries.", ca.toStringWithPort(), num_synced); } catch (NetworkError& e) { errlog("Dump of Entries to: %s did not complete. [Network Error: %s]", ca.toStringWithPort(), e.what()); } catch(const WforceException& e) { errlog("Dump of Entries to: %s did not complete. [Wforce Error: %s]", ca.toStringWithPort(), e.reason); } catch (const std::exception& e) { errlog("Dump of Entries to: %s did not complete. [exception Error: %s]", ca.toStringWithPort(), e.what()); } } unsigned int dumpDBToNetwork(const ComboAddress& ca, const std::string& encryption_key) { Socket rep_sock(ca.sin4.sin_family, SOCK_STREAM, 0); // This will be automatically closed when the function ends rep_sock.connect(ca); unsigned num_synced = 0; SodiumNonce nonce; std::mutex mutex; nonce.init(); // loop through the DBs std::map my_dbmap; { std::lock_guard lock(dbMap_mutx); // copy (this is safe - everything important is in a shared ptr) my_dbmap = dbMap; } for (auto& i : my_dbmap) { TWStringStatsDBWrapper sdb = i.second; std::string db_name = i.first; for (auto vi = sdb.begin(); vi != sdb.end(); ++vi) { for (auto it = sdb.startDBDump(vi); it != sdb.DBDumpIteratorEnd(vi); ++it) { try { TWStatsDBDumpEntry entry; std::string key; if (sdb.DBDumpEntry(vi, it, entry, key)) { std::shared_ptr sdb_rop = std::make_shared(db_name, SDBOperation_SDBOpType_SDBOpSyncKey, key, entry); ReplicationOperation rep_op(sdb_rop, WforceReplicationMsg_RepType_SDBType); string msg = rep_op.serialize(); string packet; g_replication.encryptMsgWithKey(msg, packet, encryption_key, nonce, mutex); uint16_t nsize = htons(packet.length()); rep_sock.writen(std::string((char*)&nsize, sizeof(nsize))); rep_sock.writen(packet); num_synced++; } } catch(const std::exception& e) { sdb.endDBDump(vi); auto eptr = std::current_exception(); std::rethrow_exception(eptr); } } sdb.endDBDump(vi); } } return num_synced; } void syncDBThread(const ComboAddress& ca, const std::string& callback_url, const std::string& callback_pw, const std::string& encryption_key) { unsigned int num_synced = 0; noticelog("Synchronizing DBs to: %s, will notify on callback url: %s", ca.toStringWithPort(), callback_url); try { num_synced = dumpDBToNetwork(ca, encryption_key); infolog("Synchronizing DBs to: %s was completed. Synced %d entries.", ca.toStringWithPort(), num_synced); } catch (NetworkError& e) { errlog("Synchronizing DBs to: %s did not complete. [Network Error: %s]", ca.toStringWithPort(), e.what()); } catch(const WforceException& e) { errlog("Synchronizing DBs to: %s did not complete. [Wforce Error: %s]", ca.toStringWithPort(), e.reason); } catch (const std::exception& e) { errlog("Synchronizing DBs to: %s did not complete. [exception Error: %s]", ca.toStringWithPort(), e.what()); } // Once we've finished replicating we need to let the requestor know we're // done by calling the callback URL std::string err; json11::Json msg = callWforceGetURL(callback_url, callback_pw, err); if (msg.is_null()) { errlog("Synchronizing DBs callback to: %s failed due to no parseable result returned [Error: %s]", callback_url, err); } else { if (!msg["status"].is_null()) { std::string status = msg["status"].string_value(); if (status == std::string("ok")) { noticelog("Synchronizing DBs callback to: %s was successful", callback_url); return; } } errlog("Synchronizing DBs callback to: %s was unsuccessful (no status=ok in result)", callback_url); } } unsigned int checkHostUptime(const std::string& url, const std::string& password) { unsigned int ret_uptime = 0; std::string err; json11::Json msg = callWforceGetURL(url, password, err); if (!msg.is_null()) { if (!msg["uptime"].is_null()) { ret_uptime = msg["uptime"].int_value(); infolog("checkSyncHosts: uptime: %d returned from sync host: %s", ret_uptime, url); } else { errlog("checkSyncHosts: No uptime in response from sync host: %s", url); } } else { errlog("checkSyncHosts: No valid response from sync host: %s [Error: %s]", url, err); } return ret_uptime; } void checkSyncHosts() { bool found_sync_host = false; std::string key = g_replication.getEncryptionKey(); for (auto i : g_sync_data.sync_hosts) { std::string sync_host = i.first; std::string password = i.second; std::string stats_url = sync_host + "/?command=stats"; unsigned int uptime = checkHostUptime(stats_url, password); if (uptime > g_sync_data.min_sync_host_uptime) { // we have a winner, maybe std::string err; std::string sync_url = sync_host + "/?command=syncDBs"; std::string callback_url = g_sync_data.webserver_listen_addr + "/?command=syncDone"; json11::Json post_json = json11::Json::object{{"replication_host", g_sync_data.sibling_listen_addr.toString()}, {"replication_port", ntohs(g_sync_data.sibling_listen_addr.sin4.sin_port)}, {"callback_url", callback_url}, {"callback_auth_pw", g_sync_data.webserver_password}, {"encryption_key", key}}; json11::Json msg = callWforcePostURL(sync_url, password, post_json.dump(), err); if (!msg.is_null()) { if (!msg["status"].is_null()) { std::string status = msg["status"].string_value(); if (status == "ok") { found_sync_host = true; infolog("checkSyncHosts: Successful request to synchronize DBs with sync host: %s", sync_host); break; } else { errlog("checkSyncHosts: Error returned status=%s from sync_host: %s", status, sync_url); } } else { errlog("checkSyncHosts: No status in response from sync host: %s", sync_url); } } else { errlog("checkSyncHosts: No valid response from sync host: %s [Error: %s]", sync_url, err); } } } // If we didn't find a sync host we can't warm up if (found_sync_host == false) g_ping_up = true; } void replicateOperation(const ReplicationOperation& rep_op) { g_replication.replicateOperation(rep_op); } AllowReturn defaultAllowTuple(const LoginTuple& lp) { // do nothing: we expect Lua function to be registered if wforce is required to actually do something std::vector> myvec; return std::make_tuple(0, "", "", myvec); } void defaultReportTuple(const LoginTuple& lp) { // do nothing: we expect Lua function to be registered if something custom is needed } bool defaultReset(const std::string& type, const std::string& str_val, const ComboAddress& ca_val) { return true; } std::string defaultCanonicalize(const std::string& login) { return login; } allow_t g_allow{defaultAllowTuple}; report_t g_report{defaultReportTuple}; reset_t g_reset{defaultReset}; canonicalize_t g_canon{defaultCanonicalize}; CustomFuncMap g_custom_func_map; CustomGetFuncMap g_custom_get_func_map; /**** CARGO CULT CODE AHEAD ****/ extern "C" { char* my_generator(const char* text, int state) { string t(text); vector words {"addACL", "addSibling(", "setSiblings(", "siblingListener(", "setACL", "showACL()", "shutdown()", "webserver", "controlSocket", "stats()", "siblings()", "newCA", "newNetmaskGroup", "makeKey", "setKey", "testCrypto", "showWebHooks()", "showCustomWebHooks()", "showCustomEndpoints()", "showNamedReportSinks()", "addNamedReportSink(", "setNamedReportSinks(", "showPerfStats()", "showCommandStats()", "showCustomStats()", "showStringStatsDB()", "showVersion()", "addWebHook(", "addCustomWebHook(", "setNumWebHookThreads(", "blacklistPersistDB(", "blacklistPersistReplicated()", "blacklistNetmask", "blacklistIP", "blacklistLogin", "blacklistIPLogin", "unblacklistNetmask", "unblacklistIP", "unblacklistLogin", "unblacklistIPLogin", "checkBlacklistIP", "checkBlacklistLogin", "checkBlacklistIPLogin", "whitelistlistPersistDB(", "whitelistPersistReplicated()", "whitelistNetmask", "whitelistIP", "whitelistLogin", "whitelistIPLogin", "unwhitelistNetmask", "unwhitelistIP", "unwhitelistLogin", "unwhitelistIPLogin", "checkWhitelistIP", "checkWhitelistLogin", "checkWhitelistIPLogin", "reloadGeoIPDBs()", "addCustomStat", "setSiblingsWithKey", "addSiblingWithKey", "removeSibling", "setSiblingConnectTimeout", "setMaxSiblingQueueSize", "setCustomEndpoint", "setCustomGetEndpoint", "setVerboseAllowLog", "incCustomStat" }; static int s_counter=0; int counter=0; if(!state) s_counter=0; for(auto w : words) { if(boost::starts_with(w, t) && counter++ == s_counter) { s_counter++; return strdup(w.c_str()); } } return 0; } static char** my_completion( const char * text , int start, int end) { char **matches=0; if (start == 0) matches = rl_completion_matches ((char*)text, &my_generator); else rl_bind_key('\t',rl_abort); if(!matches) rl_bind_key('\t', rl_abort); return matches; } } std::string findDefaultConfigFile() { std::string configFile = string(SYSCONFDIR) + "/wforce/wforce.conf"; struct stat statbuf; if (stat(configFile.c_str(), &statbuf) != 0) { g_configDir = string(SYSCONFDIR); configFile = string(SYSCONFDIR) + "/wforce.conf"; } else g_configDir = string(SYSCONFDIR) + "/wforce"; return configFile; } std::string findUaRegexFile() { return(g_configDir + "/regexes.yaml"); } void checkUaRegexFile(const std::string& regexFile) { struct stat statbuf; if (stat(regexFile.c_str(), &statbuf) != 0) { errlog("Fatal error: cannot find regexes.yaml at %d", regexFile); exit(-1); } } int main(int argc, char** argv) try { g_stats.latency=0; rl_attempted_completion_function = my_completion; rl_completion_append_character = 0; signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); g_console=true; #ifdef HAVE_LIBSODIUM if (sodium_init() == -1) { cerr<<"Unable to initialize crypto library"<(std::stoi(optarg)); } catch (const std::invalid_argument &ia) { cout << "Bad log level (" << optarg << ") - must be an integer" << endl; exit(1); } break; case 'h': cout<<"Syntax: wforce [-C,--config file] [-R,--regexes file] [-c,--client] [-d,--daemon] [-e,--execute cmd]\n"; cout<<"[-h,--help] [-l,--local addr]\n"; cout<<"\n"; cout<<"-C,--config file Load configuration from 'file'\n"; cout<<"-R,--regexes file Load User-Agent regexes from 'file'\n"; cout<<"-c [file], Operate as a client, connect to wforce, loading config from 'file' if specified\n"; cout<<"-s, Operate under systemd control.\n"; cout<<"-d,--daemon Operate as a daemon\n"; cout<<"-D,--docker Enable logging for docker\n"; cout<<"-e,--execute cmd Connect to wforce and execute 'cmd'\n"; cout<<"-f,--facility name Use log facility 'name'\n"; cout<<"-l,--loglevel level Log level as an integer. 0 is Emerg, 7 is Debug. Defaults to 6 (Info).\n"; cout<<"-h,--help Display this helpful message\n"; cout<<"\n"; exit(EXIT_SUCCESS); break; case 'v': g_verbose=true; g_loglevel=LogLevel::Debug; break; case '?': default: cout << "Option '-" << (char)optopt << "' is invalid: ignored\n"; } } argc-=optind; argv+=optind; g_webserver.setWebLogLevel(g_loglevel); openlog("wforce", LOG_PID, g_cmdLine.facility); g_singleThreaded = false; if (chdir(g_configDir.c_str()) != 0) { warnlog("Could not change working directory to %s (%s)", g_configDir, strerror(errno)); } if (!g_cmdLine.beClient) { checkUaRegexFile(g_cmdLine.regexes); vinfolog("Will read UserAgent regexes from %s", g_cmdLine.regexes); g_ua_parser_p = std::make_shared(g_cmdLine.regexes); } if(g_cmdLine.beClient || !g_cmdLine.command.empty()) { setupLua(true, false, g_lua, g_allow, g_report, g_reset, g_canon, g_custom_func_map, g_custom_get_func_map, g_cmdLine.config); doClient(g_serverControl, g_cmdLine.command); exit(EXIT_SUCCESS); } // Initialise Prometheus Metrics initWforcePrometheusMetrics(std::make_shared("wforce")); // Setup a sensible ACL, but allow it to be overridden by Lua if necessary auto acl = g_webserver.getACL(); for(auto& addr : {"127.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "169.254.0.0/16", "192.168.0.0/16", "172.16.0.0/12", "::1/128", "fc00::/7", "fe80::/10"}) acl.addMask(addr); g_webserver.setACL(acl); // this sets up the global lua state used for config and setup auto todo=setupLua(false, false, g_lua, g_allow, g_report, g_reset, g_canon, g_custom_func_map, g_custom_get_func_map, g_cmdLine.config); // now we setup the allow/report lua states g_luamultip = std::make_shared(g_num_luastates); for (auto it = g_luamultip->begin(); it != g_luamultip->end(); ++it) { // first setup defaults in case the config doesn't specify anything (*it)->allow_func = g_allow; (*it)->report_func = g_report; (*it)->reset_func = g_reset; (*it)->canon_func = g_canon; setupLua(false, true, (*it)->lua_context, (*it)->allow_func, (*it)->report_func, (*it)->reset_func, (*it)->canon_func, (*it)->custom_func_map, (*it)->custom_get_func_map, g_cmdLine.config); } if(g_cmdLine.beDaemon) { g_console=false; daemonize(); } else if (g_cmdLine.underSystemd) { g_console=false; } else { vinfolog("Running in the foreground"); } g_webhook_runner.startThreads(); // register all the webserver commands registerWebserverCommands(); acl = g_webserver.getACL(); vector vec; std::string acls; acl.toStringVector(&vec); for(const auto& s : vec) { if (!acls.empty()) acls += ", "; acls += s; } noticelog("ACL allowing queries from: %s", acls.c_str()); // setup blacklist_db purge thread thread t1(BlackWhiteListDB::purgeEntriesThread, &g_bl_db); t1.detach(); thread t2(BlackWhiteListDB::purgeEntriesThread, &g_wl_db); t2.detach(); // start the performance stats thread startStatsThread(); // load the persistent blacklist entries if (!g_bl_db.loadPersistEntries()) { errlog("Could not load persistent BL DB entries, please fix configuration or check redis availability. Exiting."); exit(1); } if (!g_wl_db.loadPersistEntries()) { errlog("Could not load persistent WL DB entries, please fix configuration or check redis availability. Exiting."); exit(1); } // Start the replication worker threads g_replication.startReplicationWorkerThreads(); // Start the StatsDB expire threads (this must be done after any daemonizing) { std::lock_guard lock(dbMap_mutx); for (auto& i : dbMap) { i.second.startExpireThread(); } } // start the threads created by lua setup. Includes the webserver accept thread for(auto& t : todo) t(); // Give time for the webserver to start while (!g_webserver.isRunning()) { sleep(1); } // Loop through the list of configured sync hosts, check if any have been // up long enough and if so, kick off a DB sync operation to fill our DBs checkSyncHosts(); #ifdef HAVE_LIBSYSTEMD sd_notify(0, "READY=1"); #endif if(!(g_cmdLine.beDaemon || g_cmdLine.underSystemd || g_cmdLine.underDocker)) { doConsole(); } else { while (true) pause(); } _exit(EXIT_SUCCESS); } catch(const LuaContext::ExecutionErrorException& e) { try { errlog("Fatal Lua error: %s", e.what()); std::rethrow_if_nested(e); } catch(const std::exception& e) { errlog("Details: %s", e.what()); } catch(WforceException &ae) { errlog("Fatal wforce error: %s", ae.reason); } _exit(EXIT_FAILURE); } catch(std::exception &e) { errlog("Fatal error: %s", e.what()); } catch(WforceException &ae) { errlog("Fatal wforce error: %s", ae.reason); _exit(EXIT_FAILURE); } weakforced-2.10.2/wforce/wforce.conf000066400000000000000000000051111461473602600173360ustar00rootroot00000000000000-- Old way to configure the webserver (doesn't handle HTTPS or multiple listeners) -- webserver("0.0.0.0:8084", "--WEBPWD") -- New way to configure the webserver -- IP addr:port Use SSL? Certificate File Private Key TLS Options - see https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html addListener("0.0.0.0:8084", false, "", "", {}) setWebserverPassword("--WEBPWD") --SETKEY controlSocket("127.0.0.1:4004") addACL("127.0.0.0/8") addACL("192.168.0.0/16") local bulkRetrievers = newNetmaskGroup() --bulkRetrievers:addMask("130.161.0.0/16") --bulkRetrievers:addMask("145.132.0.0/16") local string_find = string.find local field_map = {} field_map["diffFailedPasswords"] = "hll" newStringStatsDB("OneHourDB", 600, 6, field_map) function twreport(lt) if (not lt.success and not lt.policy_reject) then local sdb = getStringStatsDB("OneHourDB") sdb:twAdd(lt.remote, "diffFailedPasswords", lt.pwhash) addrlogin = lt.remote:tostring() .. ":" .. lt.login sdb:twAdd(addrlogin, "diffFailedPasswords", lt.pwhash) end end setReport(twreport) function allow(lt) local sdb = getStringStatsDB("OneHourDB") if(bulkRetrievers:match(lt.remote)) then -- return , , , return 0, "", "bulkRetrievers match", {} end if(sdb:twGet(lt.remote, "diffFailedPasswords") > 50) then return -1, "", "too many different failed password attempts by IP", { attempts=50 } end local addrlogin = lt.remote:tostring() .. ":" .. lt.login if(sdb:twGet(addrlogin, "diffFailedPasswords") > 3) then return 3, "", "too many different failed password attempts by IP/login", { attempts=3 } end -- you *must* return with 4 arguments like this: , , , return 0, "", "", { defaultReturn=1 } end setAllow(allow) function reset(type, login, ip) local sdb = getStringStatsDB("OneHourDB") if (string_find(type, "ip")) then sdb:twReset(ip) -- if you set a non-default prefix for IP addresses, then reset will not necessarily do what you expect -- for example if v4Prefix==24 and you reset an IP address it will reset the stats for all IPs in that range end if (string_find(type, "login")) then -- we do not actually set any login-only keys sdb:twReset(login) end if (string_find(type, "ip") and string_find(type, "login")) then local iplogin = ip:tostring() .. ":" .. login sdb:twReset(iplogin) end return true end setReset(reset) weakforced-2.10.2/wforce/wforce.conf.example000066400000000000000000000227361461473602600210040ustar00rootroot00000000000000addListener("0.0.0.0:8084", true, "/etc/tls/certificate.pem", "/etc/tls/private_key.pem", {minimum_protocol="TLSv1.3"}) setWebserverPassword("aVerySecurePassword") setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=") controlSocket("0.0.0.0:4004") addSibling("192.168.1.79") addSibling("192.168.1.30") addSibling("192.168.1.54") siblingListener("0.0.0.0:4001") addACL("127.0.0.0/8") addACL("192.168.0.0/16") -- Number of lua states used for allow/report queries. -- Increasing this number will reduce lock contention if that is a problem -- A lot of states will increase memory, but not by too much setNumLuaStates(6) -- Number of worker threads used for allow/report queries -- Increasing this number increases concurrency but too many could cause thrashing -- Should be >= number of lua states, and ideally == number of cores setNumWorkerThreads(4) -- Number of worker threads used to process siblings reports -- Increasing this number increases concurrency but too many could cause thrashing -- Should be around numWorkerThreads/2 setNumSiblingThreads(2) -- Number of threads to use for sending webhook events setNumWebHookThreads(2) -- Register a webhook for "report" events local config_keys={} config_keys["url"] = "http://localhost:8080/webhook/regression" config_keys["secret"] = "verysecretcode" local events = { "report" } addWebHook(events, config_keys) -- Register a custom webhook for custom events config_keys={} config_keys["url"] = "http://localhost:8080/webhook/regression" config_keys["secret"] = "verysecretcustomcode" -- Change Content-Type of the HTTP post from the default application/json -- config_keys["content-type"] = "text/plain" addCustomWebHook("mycustomhook", config_keys) local bulkRetrievers = newNetmaskGroup() bulkRetrievers:addMask("130.161.0.0/16") bulkRetrievers:addMask("145.132.0.0/16") -- Field map contains as many different fields as you like -- Supported types are: -- "int" - simple counter -- "hll" - cardinality (count how many different things you put in the bucket) -- "countmin" - like a multi-valued bloom filter. Counts how many of each type there are. local field_map = {} field_map["countLogins"] = "int" field_map["diffPasswords"] = "hll" field_map["diffIPs"] = "hll" field_map["countryCount"] = "countmin" field_map["osCount"] = "countmin" -- create a db for storing stats. -- You can create multiple dbs with different window lengths for storing stats over different time windows -- This one is 6 windows of 10 minutes each, so an hour in total. Every 10 minutes the oldest windows data will expire newStringStatsDB("OneHourDB",600,6,field_map) -- Persist the blacklist entries in the specified redis DB -- blacklistPersistDB("127.0.0.1", 6379) -- Only set this option if every wforce server is using a separate redis DB -- Do not set when there is a central redis DB used by all wforce servers -- blacklistPersistReplicated() -- This one is 24 windows of 1 hour each, so 24 hours in total. Useful for tracking long-term stats. -- You can reuse field_map or create a different one newStringStatsDB("24HourDB",3600, 24, field_map) -- Only initialize the GeoIPDB if you need it - this initializes the "country" -- GeoIP DBs (ipv4 and ipv6) initGeoIPDB() -- You can also initialize the "city" and "ISP" databases (ensure you have -- downloaded them, again you need both ipv4 and ipv6 DBs) -- initGeoIPCityDB() -- initGeoIPISPDB() -- The report function is used to store custom stats -- The allow and report functions cannot access any lua variables defined outside, hence the get... functions function report(lt) local sdb = getStringStatsDB("OneHourDB") local sdb_day = getStringStatsDB("24HourDB") local cur_ct = lookupCountry(lt.remote) -- A note on keys: You can use strings or ComboAddresses. -- You can specify a separate ipv4 and v6 netmasks for the DB, which will be used for all ComboAddress keys, thus allowing aggregation of IP stats -- Example for IPv4 addresses: sdb:twSetv4Prefix(24) -- Example for IPv6 addresses: sdb:twSetv6Prefix(64) -- twAdd() is the generic way to add things to the stats bucket. For integers it does addition sdb:twAdd(lt.login, "countLogins", 1) sdb:twAdd(lt.remote, "countLogins", 1) sdb:twAdd(lt.login, "diffPasswords", lt.pwhash) sdb:twAdd(lt.login, "diffIPs", lt.remote) sdb_day:twAdd(lt.login, "countryCount", cur_ct) -- look for things in device_attrs if (not ((lt.device_attrs["os.family"] == "") or (lt.device_attrs["os.family"] == nil))) then -- store the os family sdb:twAdd(lt.login, "osCount", lt.device_attrs["os.family"]) end -- twSub() can be used for the integer type. It does what you expect end -- initialise some DNS lookup objects newDNSResolver("Resolv") local resolv = getDNSResolver("Resolv") newDNSResolver("RBLResolv") local rblresolv = getDNSResolver("RBLResolv") -- Configure some resolvers for it to use resolv:addResolver("8.8.8.8", 53) rblresolv:addResolver("127.0.0.1", 5353) -- The allow and report functions cannot access any lua variables defined outside, hence the get... functions function allow(lt) local sdb = getStringStatsDB("OneHourDB") local sdb_day = getStringStatsDB("24HourDB") local resolv = getDNSResolver("Resolv") local rblresolv = getDNSResolver("RBLResolv") -- look for things in device_attrs if (lt.device_attrs["os.family"] == "iOS") then -- do something special for iOS end if (bulkRetrievers:match(lt.remote)) then -- Must return 4 args - , , , return 0, "bulkRetrievers", "bulkRetrievers", { bulkRetrievers=1 } end -- check optional attrs for things that indicate badness for k, v in pairs(lt.attrs) do if ((k == "accountStatus") and (v == "blocked")) then return -1, "account blocked", "account blocked", { accountStatus="blocked" } end end -- attrs can be multi-valued, in which case they will appear in attrs_mv automagically for k, v in pairs(lt.attrs_mv) do for i, vi in ipairs(v) do if ((k == "countryList") and (vi == "Blockmestan")) then return -1, "blocked country list", "blocked country list", { countryList="Blockmestan" } end end end -- Example GeoIP lookup if (lookupCountry(lt.remote) == "XX") then return -1, "country blocked", "country blocked", { remoteCountry="XX" } end -- You can also lookup ISP names: -- lookupISP(lt.remote) -- And more detailed information like city/lat/long: -- gip_record = lookupCity(lt.remote) -- local my_city = gip_record.city -- Example DNS lookups local dnames = resolv:lookupNameByAddr(lt.remote) for i, dname in ipairs(dnames) do if (string.match(dname, "XXX")) then return -1, "Reverse IP", "Reverse IP", { ptrContains="XXX" } end end dnames = rblresolv:lookupRBL(lt.remote, "sbl.spamhaus.org") for i, dname in ipairs(dnames) do if (string.match(dname, "127.0.0.2")) then -- tarpit the connection return 5, "RBL", "RBL", { spamhaus=1 } end end -- twGet() returns the "sum" of all the values over all the time windows. -- For integer and countmin types, this is just a sum. For HLL, it's a union. if (sdb:twGet(lt.login, "diffPasswords") > 90) then -- blacklist the login for 300 seconds blacklistLogin(lt.login, 300, "too many different incorrect password attempts") return -1, "too many different password attempts for this account", "diffPasswords", { diffPasswords=90 } end -- If user is logging in from multiple IPs and we haven't seen this country in the last 24 hours then reject local cur_ct = lookupCountry(lt.remote) if ((sdb:twGet(lt.login, "diffIPs") > 5) and (sdb_day.twGet(lt.login, "countryCount", cur_ct) < 2)) then return -1, "Too many IPs and countries", "diffIPs", { diffIPs=5, countryCount24Hrs=1, country=cur_ct } end -- We also have: -- twGetCurrent() which returns the value for the current window -- twGetWindows() which returns a table with all window values - the first item in the list is the "current" window. The last is the "oldest" window. -- return must have these 4 arguments return 0, "allowed", "allowed", {} end -- Use this function to reset stats if needed for particular IPs, logins or both function reset(type, login, ip) local sdb = getStringStatsDB("OneHourDB") local sdb_day = getStringStatsDB("24HourDB") if (string.find(type, "ip")) then sdb:twReset(ip) -- if you set a non-default prefix for IP addresses, then reset will not necessarily do what you expect -- for example if v4Prefix==24 and you reset an IP address it will reset the stats for all IPs in that range end if (string.find(type, "login")) then -- we do not actually set any login-only keys sdb:twReset(login) sdb_day:twReset(login) end if (string.find(type, "ip") and string.find(type, "login")) then -- we do not set any compound keys in this policy end return true end setReport(report) setAllow(allow) setReset(reset) function custom(args) for k,v in pairs(args.attrs) do infoLog("custom func argument attrs", { key=k, value=v }); end runCustomWebHook("mycustomhook", "{ \"foo\":\"bar\" }") -- return consists of a boolean, followed by { key-value pairs } return true, { key=value } end -- Register a custom endpoint -- Parameters: name, send arguments to report sink?, function) setCustomEndpoint("custom", false, custom) weakforced-2.10.2/wforce/wforce.hh000066400000000000000000000072131461473602600170150ustar00rootroot00000000000000/* * This file is part of PowerDNS or weakforced. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "ext/luawrapper/include/LuaContext.hpp" #include #include "misc.hh" #include "iputils.hh" #include #include #include #include #include "sholder.hh" #include "sstuff.hh" #include "replication.hh" #include "webhook.hh" #include "wforce-webserver.hh" #include "wforce_exception.hh" #include "login_tuple.hh" #include "json11.hpp" #include #include #include #include "wforce-sibling.hh" #include "wforce-replication.hh" struct WForceStats { using stat_t=std::atomic; stat_t reports{0}; stat_t allows{0}; stat_t denieds{0}; double latency{0}; }; extern struct WForceStats g_stats; struct ClientState { ComboAddress local; int udpFD; int tcpFD; }; extern std::mutex g_luamutex; extern LuaContext g_lua; extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex (functions using g_outputBuffer MUST NOT be enabled for the allow/report lua contexts) extern WforceWebserver g_webserver; extern WforceReplication g_replication; extern GlobalStateHolder g_ACL; extern ComboAddress g_sibling_listen; extern ComboAddress g_serverControl; // not changed during runtime struct dnsheader; void controlThread(int fd, ComboAddress local); bool getMsgLen(int fd, uint16_t* len); bool putMsgLen(int fd, uint16_t len); void* tcpAcceptorThread(void* p); double getDoubleTime(); extern GlobalStateHolder>> g_report_sinks; extern GlobalStateHolder>, std::vector>>>> g_named_report_sinks; void sendReportSink(const LoginTuple& lt); void sendNamedReportSink(const std::string& msg); extern WebHookRunner g_webhook_runner; extern WebHookDB g_webhook_db; extern WebHookDB g_custom_webhook_db; extern std::shared_ptr g_ua_parser_p; extern bool g_allowlog_verbose; // Whether to log allow returns of 0 void dumpEntriesThread(const ComboAddress& ca, std::unique_lock lock); void syncDBThread(const ComboAddress& ca, const std::string& callback_url, const std::string& callback_pw, const std::string& encryption_key); struct syncData { std::vector> sync_hosts; unsigned int min_sync_host_uptime; ComboAddress sibling_listen_addr; std::string webserver_listen_addr; std::string webserver_password; }; void replicateOperation(const ReplicationOperation& rep_op); extern syncData g_sync_data; extern bool g_builtin_bl_enabled; extern bool g_builtin_wl_enabled; extern curlTLSOptions g_curl_tls_options; weakforced-2.10.2/wforce/wforce.service.in000066400000000000000000000007001461473602600204550ustar00rootroot00000000000000[Unit] Description=Wforce Anti-Abuse Daemon Documentation=man:wforce(1) Wants=network-online.target After=network-online.target [Service] Type=@type@ ExecStart=@bindir@/wforce -s Restart=on-failure StartLimitInterval=0 PrivateTmp=true CapabilityBoundingSet=CAP_NET_BIND_SERVICE NoNewPrivileges=true ProtectSystem=full ProtectHome=true RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 User=wforce Group=wforce [Install] WantedBy=multi-user.target