pax_global_header 0000666 0000000 0000000 00000000064 14614736026 0014523 g ustar 00root root 0000000 0000000 52 comment=635faf96e43e7119e6c7aac9e480fb42573c1d0f
weakforced-2.10.2/ 0000775 0000000 0000000 00000000000 14614736026 0013717 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/.github/ 0000775 0000000 0000000 00000000000 14614736026 0015257 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14614736026 0017442 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/.github/ISSUE_TEMPLATE/bug_report.md 0000664 0000000 0000000 00000001230 14614736026 0022130 0 ustar 00root root 0000000 0000000 ---
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.md 0000664 0000000 0000000 00000001153 14614736026 0023167 0 ustar 00root root 0000000 0000000 ---
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/ 0000775 0000000 0000000 00000000000 14614736026 0017314 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/.github/workflows/builder.yml 0000664 0000000 0000000 00000001114 14614736026 0021462 0 ustar 00root root 0000000 0000000 ---
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.yml 0000664 0000000 0000000 00000001133 14614736026 0022730 0 ustar 00root root 0000000 0000000 ---
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.yml 0000664 0000000 0000000 00000003741 14614736026 0021313 0 ustar 00root root 0000000 0000000 ---
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.yml 0000664 0000000 0000000 00000002025 14614736026 0021707 0 ustar 00root root 0000000 0000000 ---
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.yml 0000664 0000000 0000000 00000000506 14614736026 0022220 0 ustar 00root root 0000000 0000000 ---
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: gcc weakforced-2.10.2/.github/workflows/regression_reusable.yml 0000664 0000000 0000000 00000004525 14614736026 0024107 0 ustar 00root root 0000000 0000000 ---
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/.gitignore 0000664 0000000 0000000 00000001333 14614736026 0015707 0 ustar 00root root 0000000 0000000 .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/.gitmodules 0000664 0000000 0000000 00000000132 14614736026 0016070 0 ustar 00root root 0000000 0000000 [submodule "builder"]
path = builder
url = https://github.com/PowerDNS/pdns-builder.git
weakforced-2.10.2/CHANGELOG.md 0000664 0000000 0000000 00000014651 14614736026 0015537 0 ustar 00root root 0000000 0000000 # 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/LICENSE 0000664 0000000 0000000 00000104513 14614736026 0014730 0 ustar 00root root 0000000 0000000 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.am 0000664 0000000 0000000 00000000602 14614736026 0015751 0 ustar 00root root 0000000 0000000 ACLOCAL_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/NOTICE 0000664 0000000 0000000 00000001436 14614736026 0014627 0 ustar 00root root 0000000 0000000 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.
weakforced-2.10.2/README.md 0000664 0000000 0000000 00000047424 14614736026 0015211 0 ustar 00root root 0000000 0000000 Weakforced
----------
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/ 0000775 0000000 0000000 00000000000 14614736026 0015345 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/builder-support/ 0000775 0000000 0000000 00000000000 14614736026 0017057 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/builder-support/debian/ 0000775 0000000 0000000 00000000000 14614736026 0020301 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/builder-support/debian/compat 0000664 0000000 0000000 00000000002 14614736026 0021477 0 ustar 00root root 0000000 0000000 9
weakforced-2.10.2/builder-support/debian/control 0000664 0000000 0000000 00000003565 14614736026 0021715 0 ustar 00root root 0000000 0000000 Source: 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/rules 0000775 0000000 0000000 00000000723 14614736026 0021363 0 ustar 00root root 0000000 0000000 #!/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/ 0000775 0000000 0000000 00000000000 14614736026 0021601 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/builder-support/debian/source/format 0000664 0000000 0000000 00000000015 14614736026 0023010 0 ustar 00root root 0000000 0000000 3.0 (quilt)
weakforced-2.10.2/builder-support/debian/wforce-trackalert.docs 0000664 0000000 0000000 00000000052 14614736026 0024567 0 ustar 00root root 0000000 0000000 trackalert/README.md
LICENSE
CHANGELOG.md
weakforced-2.10.2/builder-support/debian/wforce-trackalert.install 0000664 0000000 0000000 00000000130 14614736026 0025302 0 ustar 00root root 0000000 0000000 usr/bin/trackalert
usr/lib/systemd/system/trackalert.service
etc/wforce/trackalert.conf
weakforced-2.10.2/builder-support/debian/wforce-trackalert.manpages 0000664 0000000 0000000 00000000133 14614736026 0025432 0 ustar 00root root 0000000 0000000 debian/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.postinst 0000775 0000000 0000000 00000002010 14614736026 0025521 0 ustar 00root root 0000000 0000000 #!/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.docs 0000664 0000000 0000000 00000000224 14614736026 0022436 0 ustar 00root root 0000000 0000000 README.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.install 0000664 0000000 0000000 00000000144 14614736026 0023155 0 ustar 00root root 0000000 0000000 usr/bin/wforce
usr/lib/systemd/system/wforce.service
etc/wforce/regexes.yaml
etc/wforce/wforce.conf
weakforced-2.10.2/builder-support/debian/wforce.manpages 0000664 0000000 0000000 00000000202 14614736026 0023275 0 ustar 00root root 0000000 0000000 debian/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.postinst 0000775 0000000 0000000 00000003236 14614736026 0023402 0 ustar 00root root 0000000 0000000 #!/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/ 0000775 0000000 0000000 00000000000 14614736026 0021351 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.debbuild 0000664 0000000 0000000 00000002615 14614736026 0025120 0 ustar 00root root 0000000 0000000 FROM 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.rpmbuild 0000664 0000000 0000000 00000005265 14614736026 0025170 0 ustar 00root root 0000000 0000000 FROM 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.rpmtest 0000664 0000000 0000000 00000000624 14614736026 0025042 0 ustar 00root root 0000000 0000000 # 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-2 0000664 0000000 0000000 00000000722 14614736026 0026254 0 ustar 00root root 0000000 0000000 @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-7 0000664 0000000 0000000 00000000775 14614736026 0026277 0 ustar 00root root 0000000 0000000 @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-bullseye 0000664 0000000 0000000 00000000271 14614736026 0027673 0 ustar 00root root 0000000 0000000 # 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-buster 0000664 0000000 0000000 00000000267 14614736026 0027360 0 ustar 00root root 0000000 0000000 # 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.debuild 0000664 0000000 0000000 00000001135 14614736026 0026237 0 ustar 00root root 0000000 0000000 FROM 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-7 0000664 0000000 0000000 00000000043 14614736026 0025370 0 ustar 00root root 0000000 0000000 @INCLUDE Dockerfile.target.centos-7 weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.el-8 0000664 0000000 0000000 00000000040 14614736026 0025366 0 ustar 00root root 0000000 0000000 @INCLUDE Dockerfile.target.ol-8
weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.el-9 0000664 0000000 0000000 00000000040 14614736026 0025367 0 ustar 00root root 0000000 0000000 @INCLUDE Dockerfile.target.ol-9
weakforced-2.10.2/builder-support/dockerfiles/Dockerfile.target.ol-8 0000664 0000000 0000000 00000001211 14614736026 0025401 0 ustar 00root root 0000000 0000000 @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-9 0000664 0000000 0000000 00000001342 14614736026 0025407 0 ustar 00root root 0000000 0000000 @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.sdist 0000664 0000000 0000000 00000000602 14614736026 0025753 0 ustar 00root root 0000000 0000000 # 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.wforce 0000664 0000000 0000000 00000004400 14614736026 0024625 0 ustar 00root root 0000000 0000000 FROM 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-version 0000775 0000000 0000000 00000004272 14614736026 0021246 0 ustar 00root root 0000000 0000000 #!/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/ 0000775 0000000 0000000 00000000000 14614736026 0020174 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/builder-support/specs/wforce.spec 0000664 0000000 0000000 00000021666 14614736026 0022350 0 ustar 00root root 0000000 0000000 %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/ 0000775 0000000 0000000 00000000000 14614736026 0015207 5 ustar 00root root 0000000 0000000 weakforced-2.10.2/common/Makefile.am 0000664 0000000 0000000 00000005511 14614736026 0017245 0 ustar 00root root 0000000 0000000 AM_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.hh 0000664 0000000 0000000 00000002163 14614736026 0016616 0 ustar 00root root 0000000 0000000 /*
* 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.cc 0000664 0000000 0000000 00000040523 14614736026 0017571 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000003651 14614736026 0017604 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000004033 14614736026 0017716 0 ustar 00root root 0000000 0000000 /*
* 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.cc 0000664 0000000 0000000 00000010515 14614736026 0020333 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000004612 14614736026 0020346 0 ustar 00root root 0000000 0000000 /*
* 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.cc 0000664 0000000 0000000 00000034264 14614736026 0017704 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000006047 14614736026 0017714 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000007526 14614736026 0016646 0 ustar 00root root 0000000 0000000 /*
* 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.cc 0000664 0000000 0000000 00000005022 14614736026 0016425 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000002326 14614736026 0016443 0 ustar 00root root 0000000 0000000 /*
* 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.cc 0000664 0000000 0000000 00000030420 14614736026 0017206 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000067323 14614736026 0017234 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000010443 14614736026 0016462 0 ustar 00root root 0000000 0000000 /*
* 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.cc 0000664 0000000 0000000 00000016607 14614736026 0020051 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000004353 14614736026 0020056 0 ustar 00root root 0000000 0000000 /*
* 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.cc 0000664 0000000 0000000 00000031607 14614736026 0017347 0 ustar 00root root 0000000 0000000 /*
* 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.hh 0000664 0000000 0000000 00000012061 14614736026 0017352 0 ustar 00root root 0000000 0000000 /*
* 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