├── man ├── Makefile ├── flame.1.md └── flame.1 ├── 3rd ├── docopt.cpp │ ├── VERSION │ ├── LICENSE-MIT │ ├── LICENSE-Boost-1.0 │ └── include │ │ ├── docopt_util.h │ │ ├── docopt.h │ │ └── docopt_value.h ├── json │ ├── VERSION │ └── LICENSE.MIT ├── urlparse │ ├── VERSION │ ├── COPYING │ └── include │ │ └── urlparse.h ├── cpp-base64 │ ├── VERSION │ ├── LICENSE │ └── include │ │ └── base64.h ├── cpp-httplib │ ├── VERSION │ └── LICENSE ├── uvw │ ├── VERSION │ ├── include │ │ ├── uvw │ │ │ ├── config.h │ │ │ ├── check.h │ │ │ ├── prepare.h │ │ │ ├── async.h │ │ │ ├── request.hpp │ │ │ ├── work.h │ │ │ ├── idle.h │ │ │ ├── type_info.hpp │ │ │ ├── resource.hpp │ │ │ ├── lib.h │ │ │ ├── signal.h │ │ │ ├── fs_poll.h │ │ │ ├── uv_type.hpp │ │ │ ├── enum.hpp │ │ │ ├── poll.h │ │ │ ├── timer.h │ │ │ ├── fs_event.h │ │ │ ├── tty.h │ │ │ ├── emitter.h │ │ │ ├── pipe.h │ │ │ ├── tcp.h │ │ │ ├── handle.hpp │ │ │ └── process.h │ │ └── uvw.hpp │ ├── src │ │ ├── async.cpp │ │ ├── idle.cpp │ │ ├── check.cpp │ │ ├── emitter.cpp │ │ ├── prepare.cpp │ │ ├── lib.cpp │ │ ├── work.cpp │ │ ├── stream.cpp │ │ ├── signal.cpp │ │ ├── timer.cpp │ │ ├── fs_poll.cpp │ │ ├── fs_event.cpp │ │ ├── poll.cpp │ │ ├── tty.cpp │ │ ├── pipe.cpp │ │ ├── tcp.cpp │ │ ├── loop.cpp │ │ ├── process.cpp │ │ ├── dns.cpp │ │ ├── thread.cpp │ │ └── udp.cpp │ └── LICENSE ├── update_cpp-httplib.sh ├── update_json.sh ├── update_urlparse.sh ├── update_cpp-base64.sh ├── update_uvw.sh └── update_docopt.sh ├── .gitignore ├── flame ├── version.h ├── http.h ├── target.h ├── utils.h ├── utils.cpp ├── config.h ├── addr.cpp ├── tcpsession.h ├── tcptlssession.h ├── tokenbucket.h ├── addr.h ├── tcpsession.cpp ├── trafgen.h ├── httpssession.h ├── metrics.h ├── query.h └── tcptlssession.cpp ├── meson_options.txt ├── CONTRIBUTORS.md ├── Dockerfile ├── .github └── workflows │ └── build.yml ├── .clang-format ├── meson.build └── README.md /man/Makefile: -------------------------------------------------------------------------------- 1 | flame.1: flame.1.md 2 | pandoc -s -o $@ $^ 3 | -------------------------------------------------------------------------------- /3rd/docopt.cpp/VERSION: -------------------------------------------------------------------------------- 1 | 05d507da0d153faff381f44968833ebffdc03447 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | build_output/ 3 | cmake-build-*/ 4 | .idea* 5 | -------------------------------------------------------------------------------- /3rd/json/VERSION: -------------------------------------------------------------------------------- 1 | 55f93686c01528224f448c19128836e7df245f72 v3.12.0 2 | -------------------------------------------------------------------------------- /3rd/urlparse/VERSION: -------------------------------------------------------------------------------- 1 | c8168839c89fc5c5917c375e1bd6ffeddf3f7f88 main 2 | -------------------------------------------------------------------------------- /3rd/cpp-base64/VERSION: -------------------------------------------------------------------------------- 1 | 951de609dbe27ce8864dfe47323c4ade96bee86e master 2 | -------------------------------------------------------------------------------- /3rd/cpp-httplib/VERSION: -------------------------------------------------------------------------------- 1 | adf58bf474fac638160592d6c3f67da4ebc7df20 v0.28.0 2 | -------------------------------------------------------------------------------- /3rd/uvw/VERSION: -------------------------------------------------------------------------------- 1 | d32ddd297034119c57a255325f43706a11d489a6 v3.4.0_libuv_v1.48 2 | -------------------------------------------------------------------------------- /flame/version.h: -------------------------------------------------------------------------------- 1 | #define FLAME_VERSION_NUM "0.12.0" 2 | #define FLAME_VERSION "Flamethrower 0.12.0-main" 3 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('doh', type: 'boolean', value: true, description: 'Enable DNS-over-HTTPS support.') 2 | -------------------------------------------------------------------------------- /flame/http.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #pragma once 5 | 6 | enum class HTTPMethod { 7 | POST, 8 | GET, 9 | }; 10 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/config.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_CONFIG_H 2 | #define UVW_CONFIG_H 3 | 4 | #ifndef UVW_AS_LIB 5 | # define UVW_INLINE inline 6 | #else 7 | # define UVW_INLINE 8 | #endif 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Many thanks to all of flamethrower's contributors (in alphabetical order): 2 | 3 | banburybill 4 | elindsey 5 | fcelda 6 | pemensik 7 | supernomad 8 | tomaskrizek 9 | weyrick 10 | yantarou 11 | zach-johnson 12 | -------------------------------------------------------------------------------- /flame/target.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "addr.h" 9 | 10 | #include 11 | 12 | struct Target { 13 | urlparse_url parsed; 14 | flame::socket_address address; 15 | std::string uri; 16 | }; 17 | -------------------------------------------------------------------------------- /flame/utils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | template 10 | void split(const std::string &s, char delim, Out result); 11 | 12 | std::vector split(const std::string &s, char delim); 13 | -------------------------------------------------------------------------------- /flame/utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 NSONE, Inc 2 | 3 | #include "utils.h" 4 | #include 5 | 6 | template 7 | void split(const std::string &s, char delim, Out result) 8 | { 9 | std::stringstream ss; 10 | ss.str(s); 11 | std::string item; 12 | while (std::getline(ss, item, delim)) { 13 | *(result++) = item; 14 | } 15 | } 16 | 17 | std::vector split(const std::string &s, char delim) 18 | { 19 | std::vector elems; 20 | split(s, delim, std::back_inserter(elems)); 21 | return elems; 22 | } 23 | -------------------------------------------------------------------------------- /3rd/uvw/src/async.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "async.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE void async_handle::send_callback(uv_async_t *hndl) { 10 | async_handle &async = *(static_cast(hndl->data)); 11 | async.publish(async_event{}); 12 | } 13 | 14 | UVW_INLINE int async_handle::init() { 15 | return leak_if(uv_async_init(parent().raw(), raw(), &send_callback)); 16 | } 17 | 18 | UVW_INLINE int async_handle::send() { 19 | return uv_async_send(raw()); 20 | } 21 | 22 | } // namespace uvw 23 | -------------------------------------------------------------------------------- /3rd/uvw/src/idle.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "idle.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE void idle_handle::start_callback(uv_idle_t *hndl) { 10 | idle_handle &idle = *(static_cast(hndl->data)); 11 | idle.publish(idle_event{}); 12 | } 13 | 14 | UVW_INLINE int idle_handle::init() { 15 | return leak_if(uv_idle_init(parent().raw(), raw())); 16 | } 17 | 18 | UVW_INLINE int idle_handle::start() { 19 | return uv_idle_start(raw(), &start_callback); 20 | } 21 | 22 | UVW_INLINE int idle_handle::stop() { 23 | return uv_idle_stop(raw()); 24 | } 25 | 26 | } // namespace uvw 27 | -------------------------------------------------------------------------------- /3rd/uvw/src/check.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "check.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE void check_handle::start_callback(uv_check_t *hndl) { 10 | check_handle &check = *(static_cast(hndl->data)); 11 | check.publish(check_event{}); 12 | } 13 | 14 | UVW_INLINE int check_handle::init() { 15 | return leak_if(uv_check_init(parent().raw(), raw())); 16 | } 17 | 18 | UVW_INLINE int check_handle::start() { 19 | return uv_check_start(raw(), &start_callback); 20 | } 21 | 22 | UVW_INLINE int check_handle::stop() { 23 | return uv_check_stop(raw()); 24 | } 25 | 26 | } // namespace uvw 27 | -------------------------------------------------------------------------------- /3rd/uvw/src/emitter.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "emitter.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE int error_event::translate(int sys) noexcept { 10 | return uv_translate_sys_error(sys); 11 | } 12 | 13 | UVW_INLINE const char *error_event::what() const noexcept { 14 | return uv_strerror(ec); 15 | } 16 | 17 | UVW_INLINE const char *error_event::name() const noexcept { 18 | return uv_err_name(ec); 19 | } 20 | 21 | UVW_INLINE int error_event::code() const noexcept { 22 | return ec; 23 | } 24 | 25 | UVW_INLINE error_event::operator bool() const noexcept { 26 | return ec < 0; 27 | } 28 | 29 | } // namespace uvw 30 | -------------------------------------------------------------------------------- /3rd/uvw/src/prepare.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "prepare.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE void prepare_handle::start_callback(uv_prepare_t *hndl) { 10 | prepare_handle &prepare = *(static_cast(hndl->data)); 11 | prepare.publish(prepare_event{}); 12 | } 13 | 14 | UVW_INLINE int prepare_handle::init() { 15 | return leak_if(uv_prepare_init(parent().raw(), raw())); 16 | } 17 | 18 | UVW_INLINE int prepare_handle::start() { 19 | return uv_prepare_start(raw(), &start_callback); 20 | } 21 | 22 | UVW_INLINE int prepare_handle::stop() { 23 | return uv_prepare_stop(raw()); 24 | } 25 | 26 | } // namespace uvw 27 | -------------------------------------------------------------------------------- /3rd/uvw/src/lib.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "lib.h" 3 | #endif 4 | 5 | #include 6 | #include "config.h" 7 | 8 | namespace uvw { 9 | 10 | UVW_INLINE shared_lib::shared_lib(loop::token token, std::shared_ptr ref, const std::string &filename) noexcept 11 | : uv_type{token, std::move(ref)} { 12 | opened = (0 == uv_dlopen(filename.data(), raw())); 13 | } 14 | 15 | UVW_INLINE shared_lib::~shared_lib() noexcept { 16 | uv_dlclose(raw()); 17 | } 18 | 19 | UVW_INLINE shared_lib::operator bool() const noexcept { 20 | return opened; 21 | } 22 | 23 | UVW_INLINE const char *shared_lib::error() const noexcept { 24 | return uv_dlerror(raw()); 25 | } 26 | 27 | } // namespace uvw 28 | -------------------------------------------------------------------------------- /3rd/update_cpp-httplib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | SRC_REPO=${SRC_REPO:-https://github.com/yhirose/cpp-httplib.git} 6 | DESTDIR=${DESTDDIR:-$(dirname "$0")/cpp-httplib} 7 | REVISION=${REVISION:-v0.28.0} 8 | 9 | SRCDIR=$(mktemp -d) 10 | # shellcheck disable=SC2064 11 | trap "rm -rf \"$SRCDIR\"" SIGINT SIGTERM EXIT 12 | 13 | git clone --depth=1 --branch="$REVISION" -- "$SRC_REPO" "$SRCDIR" 14 | 15 | rm -rf "$DESTDIR" 16 | install -m 0755 -d "$DESTDIR" 17 | install -m 0644 "$SRCDIR/LICENSE" "$DESTDIR" 18 | install -m 0755 -d "$DESTDIR/include" 19 | install -m 0644 "$SRCDIR/httplib.h" "$DESTDIR/include" 20 | 21 | printf "%s %s\n" "$(git --git-dir "$SRCDIR/.git" --work-tree "$SRCDIR" rev-parse HEAD)" "$REVISION" | tee "$DESTDIR/VERSION" 22 | -------------------------------------------------------------------------------- /3rd/update_json.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | SRC_REPO=${SRC_REPO:-https://github.com/nlohmann/json.git} 6 | DESTDIR=${DESTDDIR:-$(dirname "$0")/json} 7 | REVISION=${REVISION:-v3.12.0} 8 | 9 | SRCDIR=$(mktemp -d) 10 | # shellcheck disable=SC2064 11 | trap "rm -rf \"$SRCDIR\"" SIGINT SIGTERM EXIT 12 | 13 | git clone --depth=1 --branch="$REVISION" -- "$SRC_REPO" "$SRCDIR" 14 | 15 | rm -rf "$DESTDIR" 16 | install -m 0755 -d "$DESTDIR" 17 | install -m 0644 "$SRCDIR/LICENSE.MIT" "$DESTDIR" 18 | install -m 0755 -d "$DESTDIR/include/nlohmann" 19 | install -m 0644 "$SRCDIR/single_include/nlohmann/json.hpp" "$DESTDIR/include/nlohmann/" 20 | 21 | printf "%s %s\n" "$(git --git-dir "$SRCDIR/.git" --work-tree "$SRCDIR" rev-parse HEAD)" "$REVISION" | tee "$DESTDIR/VERSION" 22 | -------------------------------------------------------------------------------- /flame/config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 NSONE, Inc 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | class Config { 8 | private: 9 | std::string _output_file; 10 | int _verbosity{1}; 11 | long _rate_limit{0}; 12 | 13 | public: 14 | Config( 15 | int verbosity, 16 | const std::string output_file, 17 | long rate_limit) 18 | : _output_file(output_file) 19 | , _verbosity(verbosity) 20 | , _rate_limit(rate_limit) 21 | { 22 | } 23 | 24 | int verbosity() 25 | { 26 | return _verbosity; 27 | } 28 | 29 | const std::string &output_file() 30 | { 31 | return _output_file; 32 | } 33 | 34 | long rate_limit() 35 | { 36 | return _rate_limit; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora-minimal:43 AS build 2 | 3 | RUN \ 4 | dnf --setopt=install_weak_deps=False --no-docs --assumeyes install \ 5 | gcc g++ meson pkgconf ninja-build redhat-rpm-config \ 6 | ldns-devel libuv-devel gnutls-devel libnghttp2-devel \ 7 | && dnf clean all 8 | 9 | COPY . /mnt/src 10 | 11 | RUN \ 12 | mkdir /mnt/build \ 13 | && cd /mnt/build \ 14 | && meson setup /mnt/src \ 15 | && ninja 16 | 17 | FROM registry.fedoraproject.org/fedora-minimal:43 AS runtime 18 | 19 | RUN \ 20 | dnf --setopt=install_weak_deps=False --no-docs --assumeyes install \ 21 | ldns libuv gnutls libnghttp2 \ 22 | && dnf clean all 23 | 24 | COPY --from=build /mnt/build/flame /usr/local/bin/flame 25 | 26 | ENTRYPOINT ["/usr/local/bin/flame"] 27 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw.hpp: -------------------------------------------------------------------------------- 1 | #include "uvw/async.h" 2 | #include "uvw/check.h" 3 | #include "uvw/config.h" 4 | #include "uvw/dns.h" 5 | #include "uvw/emitter.h" 6 | #include "uvw/enum.hpp" 7 | #include "uvw/fs.h" 8 | #include "uvw/fs_event.h" 9 | #include "uvw/fs_poll.h" 10 | #include "uvw/handle.hpp" 11 | #include "uvw/idle.h" 12 | #include "uvw/lib.h" 13 | #include "uvw/loop.h" 14 | #include "uvw/pipe.h" 15 | #include "uvw/poll.h" 16 | #include "uvw/prepare.h" 17 | #include "uvw/process.h" 18 | #include "uvw/request.hpp" 19 | #include "uvw/resource.hpp" 20 | #include "uvw/signal.h" 21 | #include "uvw/tcp.h" 22 | #include "uvw/thread.h" 23 | #include "uvw/timer.h" 24 | #include "uvw/tty.h" 25 | #include "uvw/udp.h" 26 | #include "uvw/util.h" 27 | #include "uvw/uv_type.hpp" 28 | #include "uvw/work.h" 29 | -------------------------------------------------------------------------------- /3rd/update_urlparse.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | SRC_REPO=${SRC_REPO:-https://github.com/ngtcp2/urlparse.git} 6 | DESTDIR=${DESTDDIR:-$(dirname "$0")/urlparse} 7 | REVISION=${REVISION:-main} 8 | 9 | SRCDIR=$(mktemp -d) 10 | # shellcheck disable=SC2064 11 | trap "rm -rf \"$SRCDIR\"" SIGINT SIGTERM EXIT 12 | 13 | git clone --depth=1 --branch="$REVISION" -- "$SRC_REPO" "$SRCDIR" 14 | 15 | rm -rf "$DESTDIR" 16 | install -m 0755 -d "$DESTDIR" 17 | install -m 0644 "$SRCDIR"/COPYING "$DESTDIR" 18 | install -m 0755 -d "$DESTDIR/src" 19 | install -m 0644 "$SRCDIR"/urlparse.c "$DESTDIR/src" 20 | install -m 0755 -d "$DESTDIR/include" 21 | install -m 0644 "$SRCDIR"/urlparse.h "$DESTDIR/include" 22 | 23 | printf "%s %s\n" "$(git --git-dir "$SRCDIR/.git" --work-tree "$SRCDIR" rev-parse HEAD)" "$REVISION" | tee "$DESTDIR/VERSION" 24 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | os: [ ubuntu-latest ] 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - uses: actions/checkout@v6 20 | 21 | - name: Install build requirements 22 | if: matrix.os == 'ubuntu-latest' 23 | run: | 24 | sudo apt-get install --yes --no-install-recommends g++ meson ninja-build libuv1-dev libldns-dev libnghttp2-dev libgnutls30-dev pkgconf 25 | 26 | - name: Setup Meson Build 27 | shell: bash 28 | run: meson setup ${{github.workspace}}/build 29 | 30 | - name: Build 31 | shell: bash 32 | run: ninja -C ${{github.workspace}}/build -------------------------------------------------------------------------------- /3rd/update_cpp-base64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | SRC_REPO=${SRC_REPO:-https://github.com/ReneNyffenegger/cpp-base64.git} 6 | DESTDIR=${DESTDDIR:-$(dirname "$0")/cpp-base64} 7 | REVISION=${REVISION:-master} 8 | 9 | SRCDIR=$(mktemp -d) 10 | # shellcheck disable=SC2064 11 | trap "rm -rf \"$SRCDIR\"" SIGINT SIGTERM EXIT 12 | 13 | git clone --depth=1 --branch="$REVISION" -- "$SRC_REPO" "$SRCDIR" 14 | 15 | rm -rf "$DESTDIR" 16 | install -m 0755 -d "$DESTDIR" 17 | install -m 0644 "$SRCDIR/LICENSE" "$DESTDIR" 18 | install -m 0755 -d "$DESTDIR/src" 19 | install -m 0644 "$SRCDIR/base64.cpp" "$DESTDIR/src/" 20 | install -m 0755 -d "$DESTDIR/include" 21 | install -m 0644 "$SRCDIR/base64.h" "$DESTDIR/include/" 22 | 23 | printf "%s %s\n" "$(git --git-dir "$SRCDIR/.git" --work-tree "$SRCDIR" rev-parse HEAD)" "$REVISION" | tee "$DESTDIR/VERSION" 24 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | Standard: c++20 4 | 5 | # Our coding style is based on WebKit with some Linux coding style overrides. 6 | BasedOnStyle: WebKit 7 | 8 | # Line break after brace only in function definitions: 9 | BreakBeforeBraces: Custom 10 | BraceWrapping: 11 | AfterFunction: true 12 | AfterNamespace: false 13 | AfterClass: false 14 | AfterControlStatement: false 15 | AfterEnum: false 16 | AfterObjCDeclaration: false 17 | AfterStruct: false 18 | AfterUnion: false 19 | BeforeCatch: false 20 | BeforeElse: false 21 | IndentBraces: false 22 | 23 | # Line breaks are not enforced: 24 | ColumnLimit: 0 25 | 26 | AllowShortFunctionsOnASingleLine: None 27 | 28 | PointerAlignment: Right 29 | 30 | NamespaceIndentation: None 31 | Cpp11BracedListStyle: true 32 | SpaceBeforeCpp11BracedList: false 33 | 34 | AlignTrailingComments: true 35 | -------------------------------------------------------------------------------- /3rd/uvw/src/work.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "work.h" 3 | #endif 4 | 5 | #include 6 | 7 | #include "config.h" 8 | 9 | namespace uvw { 10 | 11 | UVW_INLINE work_req::work_req(loop::token token, std::shared_ptr ref, task t) 12 | : request{token, std::move(ref)}, func{t} {} 13 | 14 | UVW_INLINE void work_req::work_callback(uv_work_t *req) { 15 | static_cast(req->data)->func(); 16 | } 17 | 18 | UVW_INLINE void work_req::after_work_callback(uv_work_t* req, int status) { 19 | if(auto ptr = reserve(req); status) { 20 | ptr->publish(error_event{status}); 21 | } else { 22 | ptr->publish(work_event{}); 23 | } 24 | } 25 | 26 | UVW_INLINE int work_req::queue() { 27 | return this->leak_if(uv_queue_work(parent().raw(), raw(), &work_callback, &after_work_callback)); 28 | } 29 | 30 | } // namespace uvw 31 | -------------------------------------------------------------------------------- /3rd/update_uvw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | SRC_REPO=${SRC_REPO:-https://github.com/skypjack/uvw} 6 | DESTDIR=${DESTDDIR:-$(dirname "$0")/uvw} 7 | REVISION=${REVISION:-v3.4.0_libuv_v1.48} 8 | 9 | SRCDIR=$(mktemp -d) 10 | # shellcheck disable=SC2064 11 | trap "rm -rf \"$SRCDIR\"" SIGINT SIGTERM EXIT 12 | 13 | git clone --depth=1 --branch="$REVISION" -- "$SRC_REPO" "$SRCDIR" 14 | 15 | rm -rf "$DESTDIR" 16 | install -m 0755 -d "$DESTDIR" 17 | install -m 0644 "$SRCDIR"/LICENSE "$DESTDIR" 18 | install -m 0755 -d "$DESTDIR/src" 19 | install -m 0644 "$SRCDIR"/src/uvw/*.cpp "$DESTDIR/src" 20 | install -m 0755 -d "$DESTDIR/include/uvw" 21 | install -m 0644 "$SRCDIR"/src/uvw.hpp "$DESTDIR/include" 22 | install -m 0644 "$SRCDIR"/src/uvw/*.{h,hpp} "$DESTDIR/include/uvw" 23 | 24 | printf "%s %s\n" "$(git --git-dir "$SRCDIR/.git" --work-tree "$SRCDIR" rev-parse HEAD)" "$REVISION" | tee "$DESTDIR/VERSION" 25 | -------------------------------------------------------------------------------- /3rd/uvw/src/stream.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "stream.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE data_event::data_event(std::unique_ptr buf, std::size_t len) noexcept 10 | : data{std::move(buf)}, 11 | length{len} {} 12 | 13 | UVW_INLINE void details::connect_req::connect_callback(uv_connect_t *req, int status) { 14 | if(auto ptr = reserve(req); status) { 15 | ptr->publish(error_event{status}); 16 | } else { 17 | ptr->publish(connect_event{}); 18 | } 19 | } 20 | 21 | UVW_INLINE void details::shutdown_req::shoutdown_callback(uv_shutdown_t *req, int status) { 22 | if(auto ptr = reserve(req); status) { 23 | ptr->publish(error_event{status}); 24 | } else { 25 | ptr->publish(shutdown_event{}); 26 | } 27 | } 28 | 29 | UVW_INLINE int details::shutdown_req::shutdown(uv_stream_t *hndl) { 30 | return this->leak_if(uv_shutdown(raw(), hndl, &shoutdown_callback)); 31 | } 32 | 33 | } // namespace uvw 34 | -------------------------------------------------------------------------------- /3rd/uvw/src/signal.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "signal.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE signal_event::signal_event(int sig) noexcept 10 | : signum{sig} {} 11 | 12 | UVW_INLINE void signal_handle::start_callback(uv_signal_t *hndl, int signum) { 13 | signal_handle &signal = *(static_cast(hndl->data)); 14 | signal.publish(signal_event{signum}); 15 | } 16 | 17 | UVW_INLINE int signal_handle::init() { 18 | return leak_if(uv_signal_init(parent().raw(), raw())); 19 | } 20 | 21 | UVW_INLINE int signal_handle::start(int signum) { 22 | return uv_signal_start(raw(), &start_callback, signum); 23 | } 24 | 25 | UVW_INLINE int signal_handle::one_shot(int signum) { 26 | return uv_signal_start_oneshot(raw(), &start_callback, signum); 27 | } 28 | 29 | UVW_INLINE int signal_handle::stop() { 30 | return uv_signal_stop(raw()); 31 | } 32 | 33 | UVW_INLINE int signal_handle::signal() const noexcept { 34 | return raw()->signum; 35 | } 36 | 37 | } // namespace uvw 38 | -------------------------------------------------------------------------------- /3rd/json/LICENSE.MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-2025 Niels Lohmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /3rd/uvw/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2024 Michele Caini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copy of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copy or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /flame/addr.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Flamethrower Contributors 2 | 3 | #include "addr.h" 4 | 5 | #include 6 | #include 7 | 8 | flame::socket_address::socket_address(const struct sockaddr &addr, socklen_t len) 9 | : socket_address() 10 | { 11 | if (addr.sa_family == AF_UNSPEC) { 12 | return; 13 | } 14 | 15 | if ((addr.sa_family == AF_INET && len >= sizeof(sockaddr_in)) || (addr.sa_family == AF_INET6 && len >= sizeof(sockaddr_in6))) { 16 | memmove(&_addr, &addr, len); 17 | return; 18 | } 19 | 20 | throw unsupported_family(); 21 | } 22 | 23 | std::string flame::socket_address::ip_str() const 24 | { 25 | if (family() != AF_INET && family() != AF_INET6) { 26 | return ""; 27 | } 28 | 29 | char buf[256] = {0}; 30 | uv_ip_name(&sockaddr(), buf, sizeof(buf)); 31 | 32 | return buf; 33 | } 34 | 35 | uint16_t flame::socket_address::port() const 36 | { 37 | switch (family()) { 38 | case AF_INET: 39 | return htons(_addr.ipv4.sin_port); 40 | case AF_INET6: 41 | return htons(_addr.ipv6.sin6_port); 42 | default: 43 | return 0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /3rd/cpp-httplib/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 yhirose 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /3rd/urlparse/COPYING: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2024 urlparse contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /3rd/docopt.cpp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Vladimir Keleshev, 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software 6 | without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to 9 | whom the Software is furnished to do so, subject to the 10 | following conditions: 11 | 12 | The above copyright notice and this permission notice shall 13 | be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 17 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 18 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 19 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /3rd/update_docopt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | SRC_REPO=${SRC_REPO:-https://github.com/docopt/docopt.cpp} 6 | DESTDIR=${DESTDDIR:-$(dirname "$0")/docopt.cpp} 7 | REVISION=${REVISION:-master} 8 | 9 | SRCDIR=$(mktemp -d) 10 | # shellcheck disable=SC2064 11 | trap "rm -rf \"$SRCDIR\"" SIGINT SIGTERM EXIT 12 | 13 | git clone --depth=1 --branch="$REVISION" -- "$SRC_REPO" "$SRCDIR" 14 | 15 | rm -rf "$DESTDIR" 16 | install -m 0755 -d "$DESTDIR" 17 | install -m 0755 -d "$DESTDIR/"{src,include} 18 | install -m 0644 "$SRCDIR"/LICENSE* "$DESTDIR" 19 | install -m 0644 "$SRCDIR"/docopt.cpp "$DESTDIR/src" 20 | install -m 0644 "$SRCDIR"/docopt*.h "$DESTDIR/include" 21 | 22 | git --git-dir "$SRCDIR/.git" --work-tree "$SRCDIR" rev-parse HEAD | tee "$DESTDIR/VERSION" 23 | 24 | # patch to fix build with LLVM and C++23 25 | patch -d "$DESTDIR" -p0 <<"EOF" 26 | diff --git src/docopt.cpp src/docopt.cpp 27 | --- src/docopt.cpp 28 | +++ src/docopt.cpp 29 | @@ -12,9 +12,9 @@ 30 | 31 | #include "docopt_value.h" 32 | 33 | +#include 34 | #include 35 | #include 36 | -#include 37 | #include 38 | #include 39 | #include 40 | "EOF" 41 | -------------------------------------------------------------------------------- /3rd/uvw/src/timer.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "timer.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE void timer_handle::start_callback(uv_timer_t *hndl) { 10 | timer_handle &timer = *(static_cast(hndl->data)); 11 | timer.publish(timer_event{}); 12 | } 13 | 14 | UVW_INLINE int timer_handle::init() { 15 | return leak_if(uv_timer_init(parent().raw(), raw())); 16 | } 17 | 18 | UVW_INLINE int timer_handle::start(timer_handle::time timeout, timer_handle::time repeat) { 19 | return uv_timer_start(raw(), &start_callback, timeout.count(), repeat.count()); 20 | } 21 | 22 | UVW_INLINE int timer_handle::stop() { 23 | return uv_timer_stop(raw()); 24 | } 25 | 26 | UVW_INLINE int timer_handle::again() { 27 | return uv_timer_again(raw()); 28 | } 29 | 30 | UVW_INLINE void timer_handle::repeat(timer_handle::time repeat) { 31 | uv_timer_set_repeat(raw(), repeat.count()); 32 | } 33 | 34 | UVW_INLINE timer_handle::time timer_handle::repeat() { 35 | return time{uv_timer_get_repeat(raw())}; 36 | } 37 | 38 | UVW_INLINE timer_handle::time timer_handle::due_in() { 39 | return time{uv_timer_get_due_in(raw())}; 40 | } 41 | 42 | } // namespace uvw 43 | -------------------------------------------------------------------------------- /3rd/uvw/src/fs_poll.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "fs_poll.h" 3 | #endif 4 | 5 | #include 6 | #include "config.h" 7 | 8 | namespace uvw { 9 | 10 | UVW_INLINE fs_poll_event::fs_poll_event(file_info previous, file_info current) noexcept 11 | : prev{std::move(previous)}, curr{std::move(current)} {} 12 | 13 | UVW_INLINE void fs_poll_handle::start_callback(uv_fs_poll_t *hndl, int status, const uv_stat_t *prev, const uv_stat_t *curr) { 14 | if(fs_poll_handle &fsPoll = *(static_cast(hndl->data)); status) { 15 | fsPoll.publish(error_event{status}); 16 | } else { 17 | fsPoll.publish(fs_poll_event{*prev, *curr}); 18 | } 19 | } 20 | 21 | UVW_INLINE int fs_poll_handle::init() { 22 | return leak_if(uv_fs_poll_init(parent().raw(), raw())); 23 | } 24 | 25 | UVW_INLINE int fs_poll_handle::start(const std::string &file, fs_poll_handle::time interval) { 26 | return uv_fs_poll_start(raw(), &start_callback, file.data(), interval.count()); 27 | } 28 | 29 | UVW_INLINE int fs_poll_handle::stop() { 30 | return uv_fs_poll_stop(raw()); 31 | } 32 | 33 | UVW_INLINE std::string fs_poll_handle::path() noexcept { 34 | return details::try_read(&uv_fs_poll_getpath, raw()); 35 | } 36 | 37 | } // namespace uvw 38 | -------------------------------------------------------------------------------- /3rd/cpp-base64/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2004-2017 by René Nyffenegger 2 | 3 | This source code is provided 'as-is', without any express or implied 4 | warranty. In no event will the author be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this source code must not be misrepresented; you must not 12 | claim that you wrote the original source code. If you use this source code 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original source code. 18 | 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /flame/tcpsession.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | class TCPSession { 12 | public: 13 | using malformed_data_cb = std::function; 14 | using got_dns_msg_cb = std::function data, size_t size)>; 15 | using connection_ready_cb = std::function; 16 | 17 | TCPSession(std::shared_ptr handle, 18 | malformed_data_cb malformed_data_handler, 19 | got_dns_msg_cb got_dns_msg_handler, 20 | connection_ready_cb connection_ready_handler); 21 | virtual ~TCPSession(); 22 | 23 | virtual bool setup(); 24 | 25 | virtual void on_connect_event(); 26 | virtual void on_end_event(); 27 | virtual void on_shutdown_event(); 28 | 29 | virtual void close(); 30 | virtual void receive_data(const char data[], size_t len); 31 | virtual void write(std::unique_ptr data, size_t len); 32 | 33 | private: 34 | std::string _buffer; 35 | std::shared_ptr _handle; 36 | malformed_data_cb _malformed_data; 37 | got_dns_msg_cb _got_dns_msg; 38 | connection_ready_cb _connection_ready; 39 | }; 40 | -------------------------------------------------------------------------------- /3rd/uvw/src/fs_event.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "fs_event.h" 3 | #endif 4 | 5 | #include 6 | #include "config.h" 7 | 8 | namespace uvw { 9 | 10 | UVW_INLINE fs_event_event::fs_event_event(const char *pathname, details::uvw_fs_event events) 11 | : filename{pathname}, flags{std::move(events)} {} 12 | 13 | UVW_INLINE void fs_event_handle::start_callback(uv_fs_event_t *hndl, const char *filename, int events, int status) { 14 | if(fs_event_handle &fsEvent = *(static_cast(hndl->data)); status) { 15 | fsEvent.publish(error_event{status}); 16 | } else { 17 | fsEvent.publish(fs_event_event{filename, details::uvw_fs_event(events)}); 18 | } 19 | } 20 | 21 | UVW_INLINE int fs_event_handle::init() { 22 | return leak_if(uv_fs_event_init(parent().raw(), raw())); 23 | } 24 | 25 | UVW_INLINE int fs_event_handle::start(const std::string &path, event_flags flags) { 26 | return uv_fs_event_start(raw(), &start_callback, path.data(), static_cast(flags)); 27 | } 28 | 29 | UVW_INLINE int fs_event_handle::stop() { 30 | return uv_fs_event_stop(raw()); 31 | } 32 | 33 | UVW_INLINE std::string fs_event_handle::path() noexcept { 34 | return details::try_read(&uv_fs_event_getpath, raw()); 35 | } 36 | 37 | } // namespace uvw 38 | -------------------------------------------------------------------------------- /3rd/cpp-base64/include/base64.h: -------------------------------------------------------------------------------- 1 | // 2 | // base64 encoding and decoding with C++. 3 | // Version: 2.rc.09 (release candidate) 4 | // 5 | 6 | #ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 7 | #define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 8 | 9 | #include 10 | 11 | #if __cplusplus >= 201703L 12 | #include 13 | #endif // __cplusplus >= 201703L 14 | 15 | std::string base64_encode (std::string const& s, bool url = false); 16 | std::string base64_encode_pem (std::string const& s); 17 | std::string base64_encode_mime(std::string const& s); 18 | 19 | std::string base64_decode(std::string const& s, bool remove_linebreaks = false); 20 | std::string base64_encode(unsigned char const*, size_t len, bool url = false); 21 | 22 | #if __cplusplus >= 201703L 23 | // 24 | // Interface with std::string_view rather than const std::string& 25 | // Requires C++17 26 | // Provided by Yannic Bonenberger (https://github.com/Yannic) 27 | // 28 | std::string base64_encode (std::string_view s, bool url = false); 29 | std::string base64_encode_pem (std::string_view s); 30 | std::string base64_encode_mime(std::string_view s); 31 | 32 | std::string base64_decode(std::string_view s, bool remove_linebreaks = false); 33 | #endif // __cplusplus >= 201703L 34 | 35 | #endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ 36 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/check.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_CHECK_INCLUDE_H 2 | #define UVW_CHECK_INCLUDE_H 3 | 4 | #include 5 | #include "handle.hpp" 6 | #include "loop.h" 7 | 8 | namespace uvw { 9 | 10 | /*! @brief Check event. */ 11 | struct check_event {}; 12 | 13 | /** 14 | * @brief The check handle. 15 | * 16 | * Check handles will emit a check event once per loop iteration, right after 17 | * polling for I/O. 18 | * 19 | * To create a `check_handle` through a `loop`, no arguments are required. 20 | */ 21 | class check_handle final: public handle { 22 | static void start_callback(uv_check_t *hndl); 23 | 24 | public: 25 | using handle::handle; 26 | 27 | /** 28 | * @brief Initializes the handle. 29 | * @return Underlying return value. 30 | */ 31 | int init(); 32 | 33 | /** 34 | * @brief Starts the handle. 35 | * 36 | * A check event will be emitted once per loop iteration, right after 37 | * polling for I/O. 38 | * 39 | * @return Underlying return value. 40 | */ 41 | int start(); 42 | 43 | /** 44 | * @brief Stops the handle. 45 | * @return Underlying return value. 46 | */ 47 | int stop(); 48 | }; 49 | 50 | } // namespace uvw 51 | 52 | #ifndef UVW_AS_LIB 53 | # include "check.cpp" 54 | #endif 55 | 56 | #endif // UVW_CHECK_INCLUDE_H 57 | -------------------------------------------------------------------------------- /flame/tcptlssession.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "tcpsession.h" 9 | 10 | class TCPTLSSession : public TCPSession { 11 | public: 12 | using handshake_error_cb = std::function; 13 | 14 | TCPTLSSession(std::shared_ptr handle, 15 | TCPSession::malformed_data_cb malformed_data_handler, 16 | TCPSession::got_dns_msg_cb got_dns_msg_handler, 17 | TCPSession::connection_ready_cb connection_ready_handler, 18 | handshake_error_cb handshake_error_handler); 19 | virtual ~TCPTLSSession(); 20 | 21 | virtual bool setup(); 22 | 23 | virtual void on_connect_event(); 24 | 25 | virtual void close(); 26 | virtual void receive_data(const char data[], size_t len); 27 | virtual void write(std::unique_ptr data, size_t len); 28 | 29 | int gnutls_pull(void *buf, size_t len); 30 | int gnutls_push(const void *buf, size_t len); 31 | 32 | protected: 33 | void do_handshake(); 34 | 35 | private: 36 | enum class LinkState { HANDSHAKE, 37 | DATA, 38 | CLOSE } _tls_state; 39 | handshake_error_cb _handshake_error; 40 | std::string _pull_buffer; 41 | 42 | gnutls_session_t _gnutls_session; 43 | gnutls_certificate_credentials_t _gnutls_cert_credentials; 44 | }; 45 | -------------------------------------------------------------------------------- /3rd/docopt.cpp/LICENSE-Boost-1.0: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /flame/tokenbucket.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | class TokenBucket { 9 | public: 10 | TokenBucket() 11 | : _rate_qps(0) 12 | , _token_wallet(0) 13 | , _last_fill_ms(0) 14 | { 15 | } 16 | 17 | TokenBucket(double rate) 18 | : _rate_qps(rate) 19 | , _token_wallet(0) 20 | , _last_fill_ms(0) 21 | { 22 | } 23 | 24 | bool consume(const uint64_t tokens, const uvw::loop::time now_ms) 25 | { 26 | if (_token_wallet < tokens) { 27 | if (_last_fill_ms.count() == 0) { 28 | _last_fill_ms = now_ms; 29 | } else if (now_ms > _last_fill_ms) { 30 | auto elapsed_ms = (now_ms - _last_fill_ms).count(); 31 | double add = _rate_qps * elapsed_ms / 1000.0; 32 | if (_token_wallet + add >= tokens) { 33 | _token_wallet += add; 34 | _last_fill_ms = now_ms; 35 | } 36 | } 37 | if (_token_wallet < tokens) { 38 | return false; 39 | } 40 | } 41 | _token_wallet -= tokens; 42 | return true; 43 | } 44 | 45 | private: 46 | double _rate_qps; 47 | double _token_wallet; 48 | // milliseconds, based on uv_now() http://docs.libuv.org/en/v1.x/loop.html#c.uv_now 49 | uvw::loop::time _last_fill_ms; 50 | }; 51 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/prepare.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_PREPARE_INCLUDE_H 2 | #define UVW_PREPARE_INCLUDE_H 3 | 4 | #include 5 | #include "handle.hpp" 6 | #include "loop.h" 7 | 8 | namespace uvw { 9 | 10 | /*! @brief Prepare event. */ 11 | struct prepare_event {}; 12 | 13 | /** 14 | * @brief The prepare handle. 15 | * 16 | * Prepare handles will emit a prepare event once per loop iteration, right 17 | * before polling for I/O. 18 | * 19 | * To create a `prepare_handle` through a `loop`, no arguments are required. 20 | */ 21 | class prepare_handle final: public handle { 22 | static void start_callback(uv_prepare_t *hndl); 23 | 24 | public: 25 | using handle::handle; 26 | 27 | /** 28 | * @brief Initializes the handle. 29 | * @return Underlying return value. 30 | */ 31 | int init(); 32 | 33 | /** 34 | * @brief Starts the handle. 35 | * 36 | * A prepare event will be emitted once per loop iteration, right before 37 | * polling for I/O. 38 | * 39 | * The handle will start emitting prepare events when needed. 40 | * 41 | * @return Underlying return value. 42 | */ 43 | int start(); 44 | 45 | /** 46 | * @brief Stops the handle. 47 | * @return Underlying return value. 48 | */ 49 | int stop(); 50 | }; 51 | 52 | } // namespace uvw 53 | 54 | #ifndef UVW_AS_LIB 55 | # include "prepare.cpp" 56 | #endif 57 | 58 | #endif // UVW_PREPARE_INCLUDE_H 59 | -------------------------------------------------------------------------------- /3rd/uvw/src/poll.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "poll.h" 3 | #endif 4 | 5 | #include 6 | #include "config.h" 7 | 8 | namespace uvw { 9 | 10 | UVW_INLINE poll_event::poll_event(details::uvw_poll_event events) noexcept 11 | : flags{std::move(events)} {} 12 | 13 | UVW_INLINE poll_handle::poll_handle(loop::token token, std::shared_ptr ref, int desc) 14 | : handle{token, std::move(ref)}, tag{FD}, file_desc{desc} {} 15 | 16 | UVW_INLINE poll_handle::poll_handle(loop::token token, std::shared_ptr ref, os_socket_handle sock) 17 | : handle{token, std::move(ref)}, tag{SOCKET}, socket{sock} {} 18 | 19 | UVW_INLINE void poll_handle::start_callback(uv_poll_t *hndl, int status, int events) { 20 | if(poll_handle &poll = *(static_cast(hndl->data)); status) { 21 | poll.publish(error_event{status}); 22 | } else { 23 | poll.publish(poll_event{poll_event_flags(events)}); 24 | } 25 | } 26 | 27 | UVW_INLINE int poll_handle::init() { 28 | if(tag == SOCKET) { 29 | return leak_if(uv_poll_init_socket(parent().raw(), raw(), socket)); 30 | } else { 31 | return leak_if(uv_poll_init(parent().raw(), raw(), file_desc)); 32 | } 33 | } 34 | 35 | UVW_INLINE int poll_handle::start(poll_event_flags flags) { 36 | return uv_poll_start(raw(), static_cast(flags), &start_callback); 37 | } 38 | 39 | UVW_INLINE int poll_handle::stop() { 40 | return uv_poll_stop(raw()); 41 | } 42 | 43 | } // namespace uvw 44 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/async.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_ASYNC_INCLUDE_H 2 | #define UVW_ASYNC_INCLUDE_H 3 | 4 | #include 5 | #include "handle.hpp" 6 | #include "loop.h" 7 | 8 | namespace uvw { 9 | 10 | /*! @brief Async event. */ 11 | struct async_event {}; 12 | 13 | /** 14 | * @brief The async handle. 15 | * 16 | * Async handles allow the user to _wakeup_ the event loop and get an event 17 | * emitted from another thread. 18 | * 19 | * To create an `async_handle` through a `loop`, no arguments are required. 20 | */ 21 | class async_handle final: public handle { 22 | static void send_callback(uv_async_t *hndl); 23 | 24 | public: 25 | using handle::handle; 26 | 27 | /** 28 | * @brief Initializes the handle. 29 | * 30 | * Unlike other handle initialization functions, it immediately starts the 31 | * handle. 32 | * 33 | * @return Underlying return value. 34 | */ 35 | int init(); 36 | 37 | /** 38 | * @brief Wakeups the event loop and emits the async event. 39 | * 40 | * It’s safe to call this function from any thread.
41 | * An async event is emitted on the loop thread. 42 | * 43 | * See the official 44 | * [documentation](http://docs.libuv.org/en/v1.x/async.html#c.uv_async_send) 45 | * for further details. 46 | * 47 | * @return Underlying return value. 48 | */ 49 | int send(); 50 | }; 51 | 52 | } // namespace uvw 53 | 54 | #ifndef UVW_AS_LIB 55 | # include "async.cpp" 56 | #endif 57 | 58 | #endif // UVW_ASYNC_INCLUDE_H 59 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/request.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UVW_REQUEST_INCLUDE_H 2 | #define UVW_REQUEST_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "config.h" 9 | #include "resource.hpp" 10 | 11 | namespace uvw { 12 | 13 | /** 14 | * @brief Request base class. 15 | * 16 | * Base type for all `uvw` request types. 17 | */ 18 | template 19 | class request: public resource { 20 | protected: 21 | static auto reserve(U *req) { 22 | auto ptr = static_cast(req->data)->shared_from_this(); 23 | ptr->self_reset(); 24 | return ptr; 25 | } 26 | 27 | public: 28 | using resource::resource; 29 | 30 | /** 31 | * @brief Cancels a pending request. 32 | * 33 | * This method fails if the request is executing or has finished 34 | * executing. 35 | * 36 | * See the official 37 | * [documentation](http://docs.libuv.org/en/v1.x/request.html#c.uv_cancel) 38 | * for further details. 39 | * 40 | * @return Underlying return value. 41 | */ 42 | int cancel() { 43 | return uv_cancel(reinterpret_cast(this->raw())); 44 | } 45 | 46 | /** 47 | * @brief Returns the size of the underlying request type. 48 | * @return The size of the underlying request type. 49 | */ 50 | std::size_t size() const noexcept { 51 | return uv_req_size(reinterpret_cast(this->raw())->type); 52 | } 53 | }; 54 | 55 | } // namespace uvw 56 | 57 | #endif // UVW_REQUEST_INCLUDE_H 58 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/work.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_WORK_INCLUDE_H 2 | #define UVW_WORK_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "loop.h" 8 | #include "request.hpp" 9 | 10 | namespace uvw { 11 | 12 | /*! @brief Work event. */ 13 | struct work_event {}; 14 | 15 | /** 16 | * @brief The work request. 17 | * 18 | * It runs user code using a thread from the threadpool and gets notified in the 19 | * loop thread by means of an event. 20 | * 21 | * To create a `work_req` through a `loop`, arguments follow: 22 | * 23 | * * A valid instance of a `Task`, that is of type `std::function`. 24 | * 25 | * See the official 26 | * [documentation](http://docs.libuv.org/en/v1.x/threadpool.html) 27 | * for further details. 28 | */ 29 | class work_req final: public request { 30 | static void work_callback(uv_work_t *req); 31 | static void after_work_callback(uv_work_t *req, int status); 32 | 33 | public: 34 | using task = std::function; 35 | 36 | explicit work_req(loop::token token, std::shared_ptr ref, task t); 37 | 38 | /** 39 | * @brief Runs the given task in a separate thread. 40 | * 41 | * A work event will be emitted on the loop thread when the task is 42 | * finished.
43 | * This request can be cancelled with `cancel()`. 44 | * 45 | * @return Underlying return value. 46 | */ 47 | int queue(); 48 | 49 | private: 50 | task func{}; 51 | }; 52 | 53 | } // namespace uvw 54 | 55 | #ifndef UVW_AS_LIB 56 | # include "work.cpp" 57 | #endif 58 | 59 | #endif // UVW_WORK_INCLUDE_H 60 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/idle.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_IDLE_INCLUDE_H 2 | #define UVW_IDLE_INCLUDE_H 3 | 4 | #include 5 | #include "handle.hpp" 6 | #include "loop.h" 7 | 8 | namespace uvw { 9 | 10 | /*! @brief Idle event. */ 11 | struct idle_event {}; 12 | 13 | /** 14 | * @brief The idle handle. 15 | * 16 | * Idle handles will emit a idle event once per loop iteration, right before the 17 | * prepare handles. 18 | * 19 | * The notable difference with prepare handles is that when there are active 20 | * idle handles, the loop will perform a zero timeout poll instead of blocking 21 | * for I/O. 22 | * 23 | * @note 24 | * Despite the name, idle handles will emit events on every loop iteration, not 25 | * when the loop is actually _idle_. 26 | * 27 | * To create an `idle_handle` through a `loop`, no arguments are required. 28 | */ 29 | class idle_handle final: public handle { 30 | static void start_callback(uv_idle_t *hndl); 31 | 32 | public: 33 | using handle::handle; 34 | 35 | /** 36 | * @brief Initializes the handle. 37 | * @return Underlying return value. 38 | */ 39 | int init(); 40 | 41 | /** 42 | * @brief Starts the handle. 43 | * 44 | * An idle event will be emitted once per loop iteration, right before 45 | * polling the prepare handles. 46 | * 47 | * @return Underlying return value. 48 | */ 49 | int start(); 50 | 51 | /** 52 | * @brief Stops the handle. 53 | * 54 | * @return Underlying return value. 55 | */ 56 | int stop(); 57 | }; 58 | 59 | } // namespace uvw 60 | 61 | #ifndef UVW_AS_LIB 62 | # include "idle.cpp" 63 | #endif 64 | 65 | #endif // UVW_IDLE_INCLUDE_H 66 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/type_info.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UVW_TYPE_INFO_INCLUDE_HPP 2 | #define UVW_TYPE_INFO_INCLUDE_HPP 3 | 4 | #include 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | /** 10 | * @cond TURN_OFF_DOXYGEN 11 | * Internal details not to be documented. 12 | */ 13 | 14 | namespace internal { 15 | 16 | // Fowler-Noll-Vo hash function v. 1a - the good 17 | [[nodiscard]] static constexpr std::uint32_t fnv1a(const char *curr) noexcept { 18 | constexpr std::uint32_t offset = 2166136261; 19 | constexpr std::uint32_t prime = 16777619; 20 | auto value = offset; 21 | 22 | while(*curr != 0) { 23 | auto curr_val_int = static_cast(*(curr++)); 24 | value = (value ^ curr_val_int) * prime; 25 | } 26 | 27 | return value; 28 | } 29 | 30 | [[nodiscard]] static inline std::uint32_t counter() noexcept { 31 | static std::uint32_t cnt{}; 32 | return cnt++; 33 | } 34 | 35 | template 36 | [[nodiscard]] static std::uint32_t fake() noexcept { 37 | static std::uint32_t local = counter(); 38 | return local; 39 | } 40 | 41 | } // namespace internal 42 | 43 | /** 44 | * Internal details not to be documented. 45 | * @endcond 46 | */ 47 | 48 | /** 49 | * @brief Returns a numerical identifier for a given type. 50 | * @tparam Type The type for which to return the numerical identifier. 51 | * @return The numerical identifier of the give type. 52 | */ 53 | template 54 | [[nodiscard]] static constexpr std::uint32_t type() noexcept { 55 | #if defined __clang__ || defined __GNUC__ 56 | return internal::fnv1a(__PRETTY_FUNCTION__); 57 | #elif defined _MSC_VER 58 | return internal::fnv1a(__FUNCSIG__); 59 | #else 60 | return internal::fake(); 61 | #endif 62 | } 63 | 64 | } // namespace uvw 65 | 66 | #endif // UVW_TYPE_INFO_INCLUDE_HPP 67 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/resource.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UVW_RESOURCE_INCLUDE_H 2 | #define UVW_RESOURCE_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include "config.h" 7 | #include "emitter.h" 8 | #include "uv_type.hpp" 9 | 10 | namespace uvw { 11 | 12 | /** 13 | * @brief Common class for almost all the resources available in `uvw`. 14 | * 15 | * This is the base class for handles and requests. 16 | */ 17 | template 18 | class resource: public uv_type, public emitter, public std::enable_shared_from_this { 19 | protected: 20 | int leak_if(int err) noexcept { 21 | if(err == 0) { 22 | self_ptr = this->shared_from_this(); 23 | } 24 | 25 | return err; 26 | } 27 | 28 | void self_reset() noexcept { 29 | self_ptr.reset(); 30 | } 31 | 32 | bool has_self() const noexcept { 33 | return static_cast(self_ptr); 34 | } 35 | 36 | public: 37 | explicit resource(loop::token token, std::shared_ptr ref) 38 | : uv_type{token, std::move(ref)} { 39 | this->raw()->data = this; 40 | } 41 | 42 | /** 43 | * @brief Gets user-defined data. `uvw` won't use this field in any case. 44 | * @return User-defined data if any, an invalid pointer otherwise. 45 | */ 46 | template 47 | std::shared_ptr data() const { 48 | return std::static_pointer_cast(user_data); 49 | } 50 | 51 | /** 52 | * @brief Sets arbitrary data. `uvw` won't use this field in any case. 53 | * @param udata User-defined arbitrary data. 54 | */ 55 | void data(std::shared_ptr udata) { 56 | user_data = std::move(udata); 57 | } 58 | 59 | private: 60 | std::shared_ptr user_data{nullptr}; 61 | std::shared_ptr self_ptr{nullptr}; 62 | }; 63 | 64 | } // namespace uvw 65 | 66 | #endif // UVW_RESOURCE_INCLUDE_H 67 | -------------------------------------------------------------------------------- /flame/addr.h: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Flamethrower Contributors 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace flame { 14 | 15 | /** 16 | * socket_address is similar to sockaddr_storage but it can hold only AF_INET or AF_INET6 address. 17 | */ 18 | class socket_address { 19 | union { 20 | sockaddr_in ipv4; 21 | sockaddr_in6 ipv6; 22 | } _addr; 23 | 24 | class unsupported_family : std::invalid_argument { 25 | public: 26 | unsupported_family() 27 | : std::invalid_argument("unsupported address family") 28 | { 29 | } 30 | }; 31 | 32 | public: 33 | socket_address() 34 | : _addr{} 35 | { 36 | } 37 | 38 | socket_address(const sockaddr &addr, socklen_t len); 39 | 40 | const struct sockaddr &sockaddr() const 41 | { 42 | return reinterpret_cast(_addr); 43 | } 44 | 45 | struct sockaddr &sockaddr() 46 | { 47 | return reinterpret_cast(_addr); 48 | } 49 | 50 | operator struct sockaddr &() 51 | { 52 | return sockaddr(); 53 | } 54 | 55 | operator const struct sockaddr &() const 56 | { 57 | return sockaddr(); 58 | } 59 | 60 | socklen_t size() 61 | { 62 | switch (family()) { 63 | case AF_INET: 64 | return sizeof(struct sockaddr_in); 65 | case AF_INET6: 66 | return sizeof(struct sockaddr_in6); 67 | default: 68 | return 0; 69 | } 70 | } 71 | 72 | static constexpr socklen_t capacity() 73 | { 74 | return sizeof(_addr); 75 | } 76 | 77 | int family() const 78 | { 79 | return sockaddr().sa_family; 80 | } 81 | 82 | std::string ip_str() const; 83 | uint16_t port() const; 84 | }; 85 | 86 | } // namespace flame 87 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/lib.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_LIB_INCLUDE_H 2 | #define UVW_LIB_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "config.h" 9 | #include "loop.h" 10 | #include "uv_type.hpp" 11 | 12 | namespace uvw { 13 | 14 | /** 15 | * @brief The shared lib class. 16 | * 17 | * `uvw` provides cross platform utilities for loading shared libraries and 18 | * retrieving symbols from them, by means of the API offered by `libuv`. 19 | */ 20 | class shared_lib final: public uv_type { 21 | public: 22 | explicit shared_lib(loop::token token, std::shared_ptr ref, const std::string &filename) noexcept; 23 | 24 | ~shared_lib() noexcept; 25 | 26 | /** 27 | * @brief Checks if the library has been correctly opened. 28 | * @return True if the library is opened, false otherwise. 29 | */ 30 | explicit operator bool() const noexcept; 31 | 32 | /** 33 | * @brief Retrieves a data pointer from a dynamic library. 34 | * 35 | * `F` shall be a valid function type (as an example, `void(int)`).
36 | * It is legal for a symbol to map to `nullptr`. 37 | * 38 | * @param name The symbol to be retrieved. 39 | * @return A valid function pointer in case of success, `nullptr` otherwise. 40 | */ 41 | template 42 | F *sym(const std::string &name) { 43 | static_assert(std::is_function_v); 44 | F *func; 45 | auto err = uv_dlsym(raw(), name.data(), reinterpret_cast(&func)); 46 | if(err) { func = nullptr; } 47 | return func; 48 | } 49 | 50 | /** 51 | * @brief Returns the last error message, if any. 52 | * @return The last error message, if any. 53 | */ 54 | const char *error() const noexcept; 55 | 56 | private: 57 | bool opened; 58 | }; 59 | 60 | } // namespace uvw 61 | 62 | #ifndef UVW_AS_LIB 63 | # include "lib.cpp" 64 | #endif 65 | 66 | #endif // UVW_LIB_INCLUDE_H 67 | -------------------------------------------------------------------------------- /3rd/uvw/src/tty.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "tty.h" 3 | #endif 4 | 5 | #include 6 | #include "config.h" 7 | 8 | namespace uvw { 9 | 10 | UVW_INLINE details::reset_mode_memo::~reset_mode_memo() { 11 | uv_tty_reset_mode(); 12 | } 13 | 14 | UVW_INLINE tty_handle::tty_handle(loop::token token, std::shared_ptr ref, file_handle desc, bool readable) 15 | : stream_handle{token, std::move(ref)}, 16 | memo{mode_memo_handler()}, 17 | fd{desc}, 18 | rw{readable} {} 19 | 20 | UVW_INLINE std::shared_ptr tty_handle::mode_memo_handler() { 21 | static std::weak_ptr weak; 22 | auto shared = weak.lock(); 23 | if(!shared) { weak = shared = std::make_shared(); } 24 | return shared; 25 | } 26 | 27 | UVW_INLINE int tty_handle::init() { 28 | return leak_if(uv_tty_init(parent().raw(), raw(), fd, rw)); 29 | } 30 | 31 | UVW_INLINE bool tty_handle::mode(tty_handle::tty_mode m) { 32 | return (0 == uv_tty_set_mode(raw(), static_cast(m))); 33 | } 34 | 35 | UVW_INLINE bool tty_handle::reset_mode() noexcept { 36 | return (0 == uv_tty_reset_mode()); 37 | } 38 | 39 | UVW_INLINE win_size tty_handle::get_win_size() { 40 | win_size size; 41 | 42 | if(0 != uv_tty_get_winsize(raw(), &size.width, &size.height)) { 43 | size.width = -1; 44 | size.height = -1; 45 | } 46 | 47 | return size; 48 | } 49 | 50 | UVW_INLINE void tty_handle::vterm_state(tty_handle::tty_vtermstate s) const noexcept { 51 | switch(s) { 52 | case tty_vtermstate::SUPPORTED: 53 | uv_tty_set_vterm_state(uv_tty_vtermstate_t::UV_TTY_SUPPORTED); 54 | break; 55 | case tty_vtermstate::UNSUPPORTED: 56 | uv_tty_set_vterm_state(uv_tty_vtermstate_t::UV_TTY_UNSUPPORTED); 57 | break; 58 | } 59 | } 60 | 61 | UVW_INLINE tty_handle::tty_vtermstate tty_handle::vterm_state() const noexcept { 62 | uv_tty_vtermstate_t state; 63 | uv_tty_get_vterm_state(&state); 64 | return tty_vtermstate{state}; 65 | } 66 | 67 | } // namespace uvw 68 | -------------------------------------------------------------------------------- /3rd/uvw/src/pipe.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "pipe.h" 3 | #endif 4 | 5 | #include 6 | #include "config.h" 7 | 8 | namespace uvw { 9 | 10 | UVW_INLINE pipe_handle::pipe_handle(loop::token token, std::shared_ptr ref, bool pass) 11 | : stream_handle{token, std::move(ref)}, ipc{pass} {} 12 | 13 | UVW_INLINE int pipe_handle::init() { 14 | return leak_if(uv_pipe_init(parent().raw(), raw(), ipc)); 15 | } 16 | 17 | UVW_INLINE int pipe_handle::open(file_handle file) { 18 | return uv_pipe_open(raw(), file); 19 | } 20 | 21 | UVW_INLINE int pipe_handle::bind(const std::string &name, const bool no_truncate) { 22 | return uv_pipe_bind2(raw(), name.data(), name.size(), no_truncate * UV_PIPE_NO_TRUNCATE); 23 | } 24 | 25 | UVW_INLINE int pipe_handle::connect(const std::string &name, const bool no_truncate) { 26 | auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { 27 | ptr->publish(event); 28 | }; 29 | 30 | auto connect = parent().resource(); 31 | connect->on(listener); 32 | connect->on(listener); 33 | 34 | unsigned int flags = no_truncate * UV_PIPE_NO_TRUNCATE; 35 | return connect->connect(&uv_pipe_connect2, raw(), name.data(), name.size(), flags); 36 | } 37 | 38 | UVW_INLINE std::string pipe_handle::sock() const noexcept { 39 | return details::try_read(&uv_pipe_getsockname, raw()); 40 | } 41 | 42 | UVW_INLINE std::string pipe_handle::peer() const noexcept { 43 | return details::try_read(&uv_pipe_getpeername, raw()); 44 | } 45 | 46 | UVW_INLINE void pipe_handle::pending(int count) noexcept { 47 | uv_pipe_pending_instances(raw(), count); 48 | } 49 | 50 | UVW_INLINE int pipe_handle::pending() noexcept { 51 | return uv_pipe_pending_count(raw()); 52 | } 53 | 54 | UVW_INLINE handle_type pipe_handle::receive() noexcept { 55 | handle_category category = uv_pipe_pending_type(raw()); 56 | return utilities::guess_handle(category); 57 | } 58 | 59 | UVW_INLINE int pipe_handle::chmod(chmod_flags flags) noexcept { 60 | return uv_pipe_chmod(raw(), static_cast(flags)); 61 | } 62 | 63 | } // namespace uvw 64 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/signal.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_SIGNAL_INCLUDE_H 2 | #define UVW_SIGNAL_INCLUDE_H 3 | 4 | #include 5 | #include "config.h" 6 | #include "handle.hpp" 7 | #include "loop.h" 8 | 9 | namespace uvw { 10 | 11 | /*! @brief Signal event. */ 12 | struct signal_event { 13 | explicit signal_event(int sig) noexcept; 14 | 15 | int signum; /*!< The signal being monitored by this handle. */ 16 | }; 17 | 18 | /** 19 | * @brief The signal handle. 20 | * 21 | * Signal handles implement Unix style signal handling on a per-event loop 22 | * bases.
23 | * Reception of some signals is emulated on Windows. 24 | * 25 | * To create a `signal_handle` through a `loop`, no arguments are required. 26 | * 27 | * See the official 28 | * [documentation](http://docs.libuv.org/en/v1.x/signal.html) 29 | * for further details. 30 | */ 31 | class signal_handle final: public handle { 32 | static void start_callback(uv_signal_t *hndl, int signum); 33 | 34 | public: 35 | using handle::handle; 36 | 37 | /** 38 | * @brief Initializes the handle. 39 | * @return Underlying return value. 40 | */ 41 | int init(); 42 | 43 | /** 44 | * @brief Starts the handle. 45 | * 46 | * The handle will start emitting signal events when needed. 47 | * 48 | * @param signum The signal to be monitored. 49 | * @return Underlying return value. 50 | */ 51 | int start(int signum); 52 | 53 | /** 54 | * @brief Starts the handle. 55 | * 56 | * Same functionality as signal_handle::start but the signal handler is 57 | * reset the moment the signal is received. 58 | * 59 | * @param signum The signal to be monitored. 60 | * @return Underlying return value. 61 | */ 62 | int one_shot(int signum); 63 | 64 | /** 65 | * @brief Stops the handle. 66 | * @return Underlying return value. 67 | */ 68 | int stop(); 69 | 70 | /** 71 | * @brief Gets the signal being monitored. 72 | * @return The signal being monitored. 73 | */ 74 | int signal() const noexcept; 75 | }; 76 | 77 | } // namespace uvw 78 | 79 | #ifndef UVW_AS_LIB 80 | # include "signal.cpp" 81 | #endif 82 | 83 | #endif // UVW_SIGNAL_INCLUDE_H 84 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/fs_poll.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_FS_POLL_INCLUDE_H 2 | #define UVW_FS_POLL_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "config.h" 8 | #include "handle.hpp" 9 | #include "loop.h" 10 | #include "util.h" 11 | 12 | namespace uvw { 13 | 14 | /*! @brief Fs pos event. */ 15 | struct fs_poll_event { 16 | explicit fs_poll_event(file_info previous, file_info current) noexcept; 17 | 18 | file_info prev; /*!< The old file_info struct. */ 19 | file_info curr; /*!< The new file_info struct. */ 20 | }; 21 | 22 | /** 23 | * @brief The fs poll handle. 24 | * 25 | * It allows users to monitor a given path for changes. Unlike fs_event_handle, 26 | * fs_poll_handle uses stat to detect when a file has changed so it can work on 27 | * file systems where fs_event_handle handles can’t. 28 | * 29 | * To create a `fs_poll_handle` through a `loop`, no arguments are required. 30 | */ 31 | class fs_poll_handle final: public handle { 32 | static void start_callback(uv_fs_poll_t *hndl, int status, const uv_stat_t *prev, const uv_stat_t *curr); 33 | 34 | public: 35 | using time = std::chrono::duration; 36 | 37 | using handle::handle; 38 | 39 | /** 40 | * @brief Initializes the handle. 41 | * @return Underlying return value. 42 | */ 43 | int init(); 44 | 45 | /** 46 | * @brief Starts the handle. 47 | * 48 | * The handle will start emitting fs_poll_event when needed. 49 | * 50 | * @param file The path to the file to be checked. 51 | * @param interval Milliseconds between successive checks. 52 | * @return Underlying return value. 53 | */ 54 | int start(const std::string &file, time interval); 55 | 56 | /** 57 | * @brief Stops the handle. 58 | * @return Underlying return value. 59 | */ 60 | int stop(); 61 | 62 | /** 63 | * @brief Gets the path being monitored by the handle. 64 | * @return The path being monitored by the handle, an empty string in case 65 | * of errors. 66 | */ 67 | std::string path() noexcept; 68 | }; 69 | 70 | } // namespace uvw 71 | 72 | #ifndef UVW_AS_LIB 73 | # include "fs_poll.cpp" 74 | #endif 75 | 76 | #endif // UVW_FS_POLL_INCLUDE_H 77 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/uv_type.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UVW_UV_TYPE_INCLUDE_H 2 | #define UVW_UV_TYPE_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "config.h" 8 | #include "loop.h" 9 | 10 | namespace uvw { 11 | 12 | /** 13 | * @brief Wrapper class for underlying types. 14 | * 15 | * It acts mainly as a wrapper around data structures of the underlying library. 16 | */ 17 | template 18 | struct uv_type { 19 | explicit uv_type(loop::token, std::shared_ptr ref) noexcept 20 | : owner{std::move(ref)}, resource{} {} 21 | 22 | uv_type(const uv_type &) = delete; 23 | uv_type(uv_type &&) = delete; 24 | 25 | uv_type &operator=(const uv_type &) = delete; 26 | uv_type &operator=(uv_type &&) = delete; 27 | 28 | /** 29 | * @brief Gets the loop from which the resource was originated. 30 | * @return A reference to a loop instance. 31 | */ 32 | loop &parent() const noexcept { 33 | return *owner; 34 | } 35 | 36 | /** 37 | * @brief Gets the underlying raw data structure. 38 | * 39 | * This function should not be used, unless you know exactly what you are 40 | * doing and what are the risks.
41 | * Going raw is dangerous, mainly because the lifetime management of a loop, 42 | * a handle or a request is in charge to the library itself and users should 43 | * not work around it. 44 | * 45 | * @warning 46 | * Use this function at your own risk, but do not expect any support in case 47 | * of bugs. 48 | * 49 | * @return The underlying raw data structure. 50 | */ 51 | const U *raw() const noexcept { 52 | return &resource; 53 | } 54 | 55 | /** 56 | * @brief Gets the underlying raw data structure. 57 | * 58 | * This function should not be used, unless you know exactly what you are 59 | * doing and what are the risks.
60 | * Going raw is dangerous, mainly because the lifetime management of a loop, 61 | * a handle or a request is in charge to the library itself and users should 62 | * not work around it. 63 | * 64 | * @warning 65 | * Use this function at your own risk, but do not expect any support in case 66 | * of bugs. 67 | * 68 | * @return The underlying raw data structure. 69 | */ 70 | U *raw() noexcept { 71 | return &resource; 72 | } 73 | 74 | protected: 75 | ~uv_type() = default; 76 | 77 | private: 78 | std::shared_ptr owner; 79 | U resource; 80 | }; 81 | 82 | } // namespace uvw 83 | 84 | #endif // UVW_UV_TYPE_INCLUDE_H 85 | -------------------------------------------------------------------------------- /flame/tcpsession.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "tcpsession.h" 9 | 10 | TCPSession::TCPSession(std::shared_ptr handle, 11 | malformed_data_cb malformed_data_handler, 12 | got_dns_msg_cb got_dns_msg_handler, 13 | connection_ready_cb connection_ready_handler) 14 | : _handle{handle} 15 | , _malformed_data{std::move(malformed_data_handler)} 16 | , _got_dns_msg{std::move(got_dns_msg_handler)} 17 | , _connection_ready{std::move(connection_ready_handler)} 18 | { 19 | } 20 | 21 | TCPSession::~TCPSession() 22 | { 23 | } 24 | 25 | // do any pre-connection setup, return true if all OK. 26 | bool TCPSession::setup() 27 | { 28 | return true; 29 | } 30 | 31 | void TCPSession::on_connect_event() 32 | { 33 | _connection_ready(); 34 | } 35 | 36 | // remote peer closed connection 37 | void TCPSession::on_end_event() 38 | { 39 | _handle->close(); 40 | } 41 | 42 | // all local writes now finished 43 | void TCPSession::on_shutdown_event() 44 | { 45 | _handle->close(); 46 | } 47 | 48 | // gracefully terminate the session 49 | void TCPSession::close() 50 | { 51 | _handle->stop(); 52 | _handle->shutdown(); 53 | } 54 | 55 | // accumulate data and try to extract DNS messages 56 | void TCPSession::receive_data(const char data[], size_t len) 57 | { 58 | // dnsheader is 12, at least one byte for the minimum name, 59 | // two bytes for the qtype and another two for the qclass 60 | const size_t MIN_DNS_RESPONSE_SIZE = 17; 61 | 62 | _buffer.append(data, len); 63 | 64 | for (;;) { 65 | std::uint16_t size; 66 | 67 | if (_buffer.size() < sizeof(size)) 68 | break; 69 | 70 | // size is in network byte order. 71 | size = static_cast(_buffer[1]) | static_cast(_buffer[0]) << 8; 72 | 73 | // no need to check the maximum size here since the maximum size 74 | // that a std::uint16t_t can hold, std::numeric_limits::max() 75 | // (65535 bytes) is allowed over TCP 76 | if (size < MIN_DNS_RESPONSE_SIZE) { 77 | _malformed_data(); 78 | break; 79 | } 80 | 81 | if (_buffer.size() >= sizeof(size) + size) { 82 | auto data = std::make_unique(size); 83 | std::memcpy(data.get(), _buffer.data() + sizeof(size), size); 84 | _buffer.erase(0, sizeof(size) + size); 85 | _got_dns_msg(std::move(data), size); 86 | } else { 87 | // Nope, we need more data. 88 | break; 89 | } 90 | } 91 | } 92 | 93 | // send data, giving data ownership to async library 94 | void TCPSession::write(std::unique_ptr data, size_t len) 95 | { 96 | _handle->write(std::move(data), len); 97 | } 98 | -------------------------------------------------------------------------------- /3rd/uvw/src/tcp.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "tcp.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE tcp_handle::tcp_handle(loop::token token, std::shared_ptr ref, unsigned int f) 10 | : stream_handle{token, std::move(ref)}, tag{f ? FLAGS : DEFAULT}, flags{f} {} 11 | 12 | UVW_INLINE int tcp_handle::init() { 13 | if(tag == FLAGS) { 14 | return leak_if(uv_tcp_init_ex(parent().raw(), raw(), flags)); 15 | } else { 16 | return leak_if(uv_tcp_init(parent().raw(), raw())); 17 | } 18 | } 19 | 20 | UVW_INLINE int tcp_handle::open(os_socket_handle socket) { 21 | return uv_tcp_open(raw(), socket); 22 | } 23 | 24 | UVW_INLINE bool tcp_handle::no_delay(bool value) { 25 | return (0 == uv_tcp_nodelay(raw(), value)); 26 | } 27 | 28 | UVW_INLINE bool tcp_handle::keep_alive(bool enable, tcp_handle::time val) { 29 | return (0 == uv_tcp_keepalive(raw(), enable, val.count())); 30 | } 31 | 32 | UVW_INLINE bool tcp_handle::simultaneous_accepts(bool enable) { 33 | return (0 == uv_tcp_simultaneous_accepts(raw(), enable)); 34 | } 35 | 36 | UVW_INLINE int tcp_handle::bind(const sockaddr &addr, tcp_flags opts) { 37 | return uv_tcp_bind(raw(), &addr, static_cast(opts)); 38 | } 39 | 40 | UVW_INLINE int tcp_handle::bind(const std::string &ip, unsigned int port, tcp_flags opts) { 41 | return bind(details::ip_addr(ip.data(), port), opts); 42 | } 43 | 44 | UVW_INLINE int tcp_handle::bind(socket_address addr, tcp_flags opts) { 45 | return bind(addr.ip, addr.port, opts); 46 | } 47 | 48 | UVW_INLINE socket_address tcp_handle::sock() const noexcept { 49 | sockaddr_storage storage; 50 | int len = sizeof(sockaddr_storage); 51 | uv_tcp_getsockname(raw(), reinterpret_cast(&storage), &len); 52 | return details::sock_addr(storage); 53 | } 54 | 55 | UVW_INLINE socket_address tcp_handle::peer() const noexcept { 56 | sockaddr_storage storage; 57 | int len = sizeof(sockaddr_storage); 58 | uv_tcp_getpeername(raw(), reinterpret_cast(&storage), &len); 59 | return details::sock_addr(storage); 60 | } 61 | 62 | UVW_INLINE int tcp_handle::connect(const std::string &ip, unsigned int port) { 63 | return connect(details::ip_addr(ip.data(), port)); 64 | } 65 | 66 | UVW_INLINE int tcp_handle::connect(socket_address addr) { 67 | return connect(addr.ip, addr.port); 68 | } 69 | 70 | UVW_INLINE int tcp_handle::connect(const sockaddr &addr) { 71 | auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { 72 | ptr->publish(event); 73 | }; 74 | 75 | auto req = parent().resource(); 76 | req->on(listener); 77 | req->on(listener); 78 | 79 | return req->connect(&uv_tcp_connect, raw(), &addr); 80 | } 81 | 82 | UVW_INLINE int tcp_handle::close_reset() { 83 | return uv_tcp_close_reset(raw(), &this->close_callback); 84 | } 85 | 86 | } // namespace uvw 87 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/enum.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UVW_ENUM_INCLUDE_HPP 2 | #define UVW_ENUM_INCLUDE_HPP 3 | 4 | #include 5 | #include "config.h" 6 | 7 | /** 8 | * @brief Operator available for enums for which bitmask support is enabled. 9 | * @tparam Type Enum class type. 10 | * @param lhs The first value to use. 11 | * @param rhs The second value to use. 12 | * @return The result of invoking the operator on the underlying types of the 13 | * two values provided. 14 | */ 15 | template 16 | [[nodiscard]] constexpr std::enable_if_t, decltype(Type::_UVW_ENUM)> 17 | operator|(const Type lhs, const Type rhs) noexcept { 18 | return static_cast(static_cast>(lhs) | static_cast>(rhs)); 19 | } 20 | 21 | /*! @copydoc operator| */ 22 | template 23 | [[nodiscard]] constexpr std::enable_if_t, decltype(Type::_UVW_ENUM)> 24 | operator&(const Type lhs, const Type rhs) noexcept { 25 | return static_cast(static_cast>(lhs) & static_cast>(rhs)); 26 | } 27 | 28 | /*! @copydoc operator| */ 29 | template 30 | [[nodiscard]] constexpr std::enable_if_t, decltype(Type::_UVW_ENUM)> 31 | operator^(const Type lhs, const Type rhs) noexcept { 32 | return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); 33 | } 34 | 35 | /** 36 | * @brief Operator available for enums for which bitmask support is enabled. 37 | * @tparam Type Enum class type. 38 | * @param value The value to use. 39 | * @return The result of invoking the operator on the underlying types of the 40 | * value provided. 41 | */ 42 | template 43 | [[nodiscard]] constexpr std::enable_if_t, decltype(Type::_UVW_ENUM)> 44 | operator~(const Type value) noexcept { 45 | return static_cast(~static_cast>(value)); 46 | } 47 | 48 | /*! @copydoc operator~ */ 49 | template 50 | [[nodiscard]] constexpr std::enable_if_t, decltype(Type::_UVW_ENUM, bool{})> 51 | operator!(const Type value) noexcept { 52 | return !static_cast>(value); 53 | } 54 | 55 | /*! @copydoc operator| */ 56 | template 57 | constexpr std::enable_if_t, decltype(Type::_UVW_ENUM) &> 58 | operator|=(Type &lhs, const Type rhs) noexcept { 59 | return (lhs = (lhs | rhs)); 60 | } 61 | 62 | /*! @copydoc operator| */ 63 | template 64 | constexpr std::enable_if_t, decltype(Type::_UVW_ENUM) &> 65 | operator&=(Type &lhs, const Type rhs) noexcept { 66 | return (lhs = (lhs & rhs)); 67 | } 68 | 69 | /*! @copydoc operator| */ 70 | template 71 | constexpr std::enable_if_t, decltype(Type::_UVW_ENUM) &> 72 | operator^=(Type &lhs, const Type rhs) noexcept { 73 | return (lhs = (lhs ^ rhs)); 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /flame/trafgen.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 NSONE, Inc 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "config.h" 10 | 11 | #ifdef DOH_ENABLE 12 | #include "http.h" 13 | #include "httpssession.h" 14 | #endif 15 | 16 | #include "addr.h" 17 | #include "metrics.h" 18 | #include "query.h" 19 | #include "target.h" 20 | #include "tcpsession.h" 21 | #include "tokenbucket.h" 22 | 23 | #include 24 | 25 | enum class Protocol { 26 | UDP, 27 | TCP, 28 | #ifdef DOH_ENABLE 29 | DOH, 30 | #endif 31 | DOT, 32 | }; 33 | 34 | struct TrafGenConfig { 35 | std::vector target_list; 36 | unsigned int _current_target{0}; 37 | flame::socket_address bind_addr; 38 | uint16_t port{53}; 39 | int r_timeout{3}; 40 | long s_delay{1}; 41 | long batch_count{10}; 42 | Protocol protocol{Protocol::UDP}; 43 | #ifdef DOH_ENABLE 44 | HTTPMethod method{HTTPMethod::POST}; 45 | #endif 46 | const Target &next_target() 47 | { 48 | const Target &next = target_list[_current_target]; 49 | _current_target++; 50 | if (_current_target >= target_list.size()) 51 | _current_target = 0; 52 | return next; 53 | } 54 | }; 55 | 56 | class TrafGen { 57 | 58 | std::shared_ptr _loop; 59 | std::shared_ptr _metrics; 60 | std::shared_ptr _config; 61 | std::shared_ptr _traf_config; 62 | std::shared_ptr _qgen; 63 | std::shared_ptr _rate_limit; 64 | 65 | std::shared_ptr _udp_handle; 66 | std::shared_ptr _tcp_handle; 67 | std::shared_ptr _tcp_session; 68 | 69 | std::shared_ptr _sender_timer; 70 | std::shared_ptr _timeout_timer; 71 | std::shared_ptr _shutdown_timer; 72 | std::shared_ptr _finish_session_timer; 73 | 74 | // a hash of in flight queries, keyed by query id 75 | std::unordered_map _in_flight; 76 | // a randomized list of query ids that are not currently in flight 77 | std::vector _free_id_list; 78 | 79 | bool _started_sending; 80 | bool _stopping; 81 | 82 | void handle_timeouts(bool force_reset = false); 83 | 84 | void process_wire(const char data[], size_t len); 85 | 86 | void start_udp(); 87 | void udp_send(); 88 | 89 | void connect_tcp_events(); 90 | void start_tcp_session(); 91 | void start_wait_timer_for_tcp_finish(); 92 | 93 | public: 94 | TrafGen(std::shared_ptr l, 95 | std::shared_ptr s, 96 | std::shared_ptr c, 97 | std::shared_ptr tgc, 98 | std::shared_ptr q, 99 | std::shared_ptr r); 100 | 101 | void start(); 102 | 103 | void stop(); 104 | std::vector::size_type in_flight_cnt() 105 | { 106 | return _in_flight.size(); 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /flame/httpssession.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef DOH_ENABLE 11 | #include "base64.h" 12 | #endif 13 | 14 | #include 15 | 16 | #include "http.h" 17 | #include "target.h" 18 | #include "tcpsession.h" 19 | 20 | struct http2_stream_data { 21 | http2_stream_data(std::string _scheme, std::string _authority, std::string _path, int32_t _id, std::string _data) 22 | : scheme(_scheme) 23 | , authority(_authority) 24 | , path(_path) 25 | , id(_id) 26 | , data(_data) 27 | { 28 | } 29 | std::string scheme; 30 | std::string authority; 31 | std::string path; 32 | int32_t id; 33 | std::string data; 34 | }; 35 | 36 | enum STATE_HTTP2 { 37 | WAIT_SETTINGS, 38 | SENDING_DATA 39 | }; 40 | 41 | class HTTPSSession : public TCPSession { 42 | public: 43 | using log_send_cb = std::function; 44 | using handshake_error_cb = std::function; 45 | 46 | HTTPSSession(std::shared_ptr handle, 47 | TCPSession::malformed_data_cb malformed_data_handler, 48 | TCPSession::got_dns_msg_cb got_dns_msg_handler, 49 | TCPSession::connection_ready_cb connection_ready_handler, 50 | handshake_error_cb handshake_error_handler, 51 | Target target, 52 | HTTPMethod method); 53 | virtual ~HTTPSSession(); 54 | 55 | virtual bool setup(); 56 | 57 | virtual void on_connect_event(); 58 | 59 | void send_tls(void *data, size_t len); 60 | void init_nghttp2(); 61 | void send_settings(); 62 | void receive_response(const char data[], size_t len); 63 | int session_send(); 64 | int session_receive(); 65 | 66 | virtual void close(); 67 | virtual void receive_data(const char data[], size_t len); 68 | virtual void write(std::unique_ptr data, size_t len); 69 | void process_receive(const uint8_t *data, size_t len); 70 | 71 | int gnutls_pull(void *buf, size_t len); 72 | int gnutls_push(const void *buf, size_t len); 73 | 74 | std::unique_ptr create_http2_stream_data(std::unique_ptr data, size_t len); 75 | void add_stream(http2_stream_data *stream_data); 76 | void remove_stream(http2_stream_data *stream_data); 77 | 78 | void settings_received(); 79 | 80 | std::unordered_map> _recv_chunks; 81 | 82 | protected: 83 | void destroy_stream(); 84 | void destroy_session(); 85 | void do_handshake(); 86 | 87 | private: 88 | STATE_HTTP2 http2_state; 89 | malformed_data_cb _malformed_data; 90 | got_dns_msg_cb _got_dns_msg; 91 | std::shared_ptr _handle; 92 | enum class LinkState { HANDSHAKE, 93 | DATA, 94 | CLOSE } _tls_state; 95 | handshake_error_cb _handshake_error; 96 | Target _target; 97 | HTTPMethod _method; 98 | 99 | nghttp2_session *_current_session; 100 | std::string _pull_buffer; 101 | 102 | gnutls_session_t _gnutls_session; 103 | gnutls_certificate_credentials_t _gnutls_cert_credentials; 104 | }; 105 | -------------------------------------------------------------------------------- /3rd/docopt.cpp/include/docopt_util.h: -------------------------------------------------------------------------------- 1 | // 2 | // docopt_util.h 3 | // docopt 4 | // 5 | // Created by Jared Grubb on 2013-11-04. 6 | // Copyright (c) 2013 Jared Grubb. All rights reserved. 7 | // 8 | 9 | #ifndef docopt_docopt_util_h 10 | #define docopt_docopt_util_h 11 | 12 | #if DOCTOPT_USE_BOOST_REGEX 13 | #include 14 | namespace std { 15 | using boost::regex; 16 | using boost::sregex_token_iterator; 17 | } 18 | #else 19 | #include 20 | #endif 21 | 22 | #if 0 23 | #pragma mark - 24 | #pragma mark General utility 25 | #endif 26 | 27 | namespace { 28 | bool starts_with(std::string const& str, std::string const& prefix) 29 | { 30 | if (str.length() < prefix.length()) 31 | return false; 32 | return std::equal(prefix.begin(), prefix.end(), 33 | str.begin()); 34 | } 35 | 36 | std::string trim(std::string&& str, 37 | const std::string& whitespace = " \t\n") 38 | { 39 | const auto strEnd = str.find_last_not_of(whitespace); 40 | if (strEnd==std::string::npos) 41 | return {}; // no content 42 | str.erase(strEnd+1); 43 | 44 | const auto strBegin = str.find_first_not_of(whitespace); 45 | str.erase(0, strBegin); 46 | 47 | return std::move(str); 48 | } 49 | 50 | std::vector split(std::string const& str, size_t pos = 0) 51 | { 52 | const char* const anySpace = " \t\r\n\v\f"; 53 | 54 | std::vector ret; 55 | while (pos != std::string::npos) { 56 | auto start = str.find_first_not_of(anySpace, pos); 57 | if (start == std::string::npos) break; 58 | 59 | auto end = str.find_first_of(anySpace, start); 60 | auto size = end==std::string::npos ? end : end-start; 61 | ret.emplace_back(str.substr(start, size)); 62 | 63 | pos = end; 64 | } 65 | 66 | return ret; 67 | } 68 | 69 | std::tuple partition(std::string str, std::string const& point) 70 | { 71 | std::tuple ret; 72 | 73 | auto i = str.find(point); 74 | 75 | if (i == std::string::npos) { 76 | // no match: string goes in 0th spot only 77 | } else { 78 | std::get<2>(ret) = str.substr(i + point.size()); 79 | std::get<1>(ret) = point; 80 | str.resize(i); 81 | } 82 | std::get<0>(ret) = std::move(str); 83 | 84 | return ret; 85 | } 86 | 87 | template 88 | std::string join(I iter, I end, std::string const& delim) { 89 | if (iter==end) 90 | return {}; 91 | 92 | std::string ret = *iter; 93 | for(++iter; iter!=end; ++iter) { 94 | ret.append(delim); 95 | ret.append(*iter); 96 | } 97 | return ret; 98 | } 99 | 100 | std::vector regex_split(std::string const& text, std::regex const& re) 101 | { 102 | std::vector ret; 103 | for (auto it = std::sregex_token_iterator(text.begin(), text.end(), re, -1); 104 | it != std::sregex_token_iterator(); 105 | ++it) { 106 | ret.emplace_back(*it); 107 | } 108 | return ret; 109 | } 110 | } 111 | 112 | namespace docopt { 113 | template 114 | inline void hash_combine(std::size_t& seed, T const& v) 115 | { 116 | // stolen from boost::hash_combine 117 | std::hash hasher; 118 | seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); 119 | } 120 | } 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /3rd/uvw/src/loop.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "loop.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE loop::loop(std::unique_ptr ptr) noexcept 10 | : uv_loop{std::move(ptr)} {} 11 | 12 | UVW_INLINE std::shared_ptr loop::create() { 13 | auto ptr = std::unique_ptr{new uv_loop_t, [](uv_loop_t *l) { delete l; }}; 14 | auto curr = std::shared_ptr{new loop{std::move(ptr)}}; 15 | 16 | if(uv_loop_init(curr->uv_loop.get())) { 17 | curr = nullptr; 18 | } 19 | 20 | return curr; 21 | } 22 | 23 | UVW_INLINE std::shared_ptr loop::create(uv_loop_t *res) { 24 | auto ptr = std::unique_ptr{res, [](uv_loop_t *) {}}; 25 | return std::shared_ptr{new loop{std::move(ptr)}}; 26 | } 27 | 28 | UVW_INLINE std::shared_ptr loop::get_default() { 29 | static std::weak_ptr ref; 30 | std::shared_ptr curr; 31 | 32 | if(ref.expired()) { 33 | auto def = uv_default_loop(); 34 | 35 | if(def) { 36 | auto ptr = std::unique_ptr(def, [](uv_loop_t *) {}); 37 | curr = std::shared_ptr{new loop{std::move(ptr)}}; 38 | } 39 | 40 | ref = curr; 41 | } else { 42 | curr = ref.lock(); 43 | } 44 | 45 | return curr; 46 | } 47 | 48 | UVW_INLINE loop::~loop() noexcept { 49 | if(uv_loop) { 50 | close(); 51 | } 52 | } 53 | 54 | UVW_INLINE int loop::close() { 55 | int ret = 0; 56 | 57 | if(uv_loop) { 58 | ret = uv_loop_close(uv_loop.get()); 59 | uv_loop.reset(); 60 | } 61 | 62 | return ret; 63 | } 64 | 65 | UVW_INLINE int loop::run(run_mode mode) noexcept { 66 | return uv_run(uv_loop.get(), static_cast(mode)); 67 | } 68 | 69 | UVW_INLINE bool loop::alive() const noexcept { 70 | return !!uv_loop_alive(uv_loop.get()); 71 | } 72 | 73 | UVW_INLINE void loop::stop() noexcept { 74 | uv_stop(uv_loop.get()); 75 | } 76 | 77 | UVW_INLINE int loop::descriptor() const noexcept { 78 | return uv_backend_fd(uv_loop.get()); 79 | } 80 | 81 | UVW_INLINE std::pair loop::timeout() const noexcept { 82 | auto to = uv_backend_timeout(uv_loop.get()); 83 | return std::make_pair(to == -1, time{to}); 84 | } 85 | 86 | UVW_INLINE loop::time loop::idle_time() const noexcept { 87 | return time{uv_metrics_idle_time(uv_loop.get())}; 88 | } 89 | 90 | UVW_INLINE metrics_type loop::metrics() const noexcept { 91 | metrics_type res{}; 92 | uv_metrics_info(uv_loop.get(), &res); 93 | return res; 94 | } 95 | 96 | UVW_INLINE loop::time loop::now() const noexcept { 97 | return time{uv_now(uv_loop.get())}; 98 | } 99 | 100 | UVW_INLINE void loop::update() const noexcept { 101 | return uv_update_time(uv_loop.get()); 102 | } 103 | 104 | UVW_INLINE int loop::fork() noexcept { 105 | return uv_loop_fork(uv_loop.get()); 106 | } 107 | 108 | UVW_INLINE void loop::data(std::shared_ptr ud) { 109 | user_data = std::move(ud); 110 | } 111 | 112 | UVW_INLINE const uv_loop_t *loop::raw() const noexcept { 113 | return uv_loop.get(); 114 | } 115 | 116 | UVW_INLINE uv_loop_t *loop::raw() noexcept { 117 | return const_cast(const_cast(this)->raw()); 118 | } 119 | 120 | } // namespace uvw 121 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/poll.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_POLL_INCLUDE_H 2 | #define UVW_POLL_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "config.h" 8 | #include "enum.hpp" 9 | #include "handle.hpp" 10 | #include "util.h" 11 | 12 | namespace uvw { 13 | 14 | namespace details { 15 | 16 | enum class uvw_poll_event : std::underlying_type_t { 17 | READABLE = UV_READABLE, 18 | WRITABLE = UV_WRITABLE, 19 | DISCONNECT = UV_DISCONNECT, 20 | PRIORITIZED = UV_PRIORITIZED, 21 | _UVW_ENUM = 0 22 | }; 23 | 24 | } 25 | 26 | /*! @brief Poll event. */ 27 | struct poll_event { 28 | explicit poll_event(details::uvw_poll_event events) noexcept; 29 | 30 | /** 31 | * @brief Detected events all in one. 32 | * 33 | * Available flags are: 34 | * 35 | * * `poll_handle::event::READABLE` 36 | * * `poll_handle::event::WRITABLE` 37 | * * `poll_handle::event::DISCONNECT` 38 | * * `poll_handle::event::PRIORITIZED` 39 | */ 40 | details::uvw_poll_event flags; 41 | }; 42 | 43 | /** 44 | * @brief The poll handle. 45 | * 46 | * Poll handles are used to watch file descriptors for readability, writability 47 | * and disconnection. 48 | * 49 | * To create a `poll_handle` through a `loop`, arguments follow: 50 | * 51 | * * A descriptor that can be: 52 | * * either an `int` file descriptor 53 | * * or a `os_socket_handle` socket descriptor 54 | * 55 | * See the official 56 | * [documentation](http://docs.libuv.org/en/v1.x/poll.html) 57 | * for further details. 58 | */ 59 | class poll_handle final: public handle { 60 | static void start_callback(uv_poll_t *hndl, int status, int events); 61 | 62 | public: 63 | using poll_event_flags = details::uvw_poll_event; 64 | 65 | explicit poll_handle(loop::token token, std::shared_ptr ref, int desc); 66 | explicit poll_handle(loop::token token, std::shared_ptr ref, os_socket_handle sock); 67 | 68 | /** 69 | * @brief Initializes the handle. 70 | * @return Underlying return value. 71 | */ 72 | int init(); 73 | 74 | /** 75 | * @brief Starts polling the file descriptor. 76 | * 77 | * Available flags are: 78 | * 79 | * * `poll_handle::event::READABLE` 80 | * * `poll_handle::event::WRITABLE` 81 | * * `poll_handle::event::DISCONNECT` 82 | * * `poll_handle::event::PRIORITIZED` 83 | * 84 | * As soon as an event is detected, a poll event is emitted by the 85 | * handle. 86 | * 87 | * Calling more than once this method will update the flags to which the 88 | * caller is interested. 89 | * 90 | * @param flags The events to which the caller is interested. 91 | * @return Underlying return value. 92 | */ 93 | int start(poll_event_flags flags); 94 | 95 | /** 96 | * @brief Stops polling the file descriptor. 97 | * @return Underlying return value. 98 | */ 99 | int stop(); 100 | 101 | private: 102 | enum { 103 | FD, 104 | SOCKET 105 | } tag; 106 | 107 | union { 108 | int file_desc; 109 | os_socket_handle socket; 110 | }; 111 | }; 112 | 113 | } // namespace uvw 114 | 115 | #ifndef UVW_AS_LIB 116 | # include "poll.cpp" 117 | #endif 118 | 119 | #endif // UVW_POLL_INCLUDE_H 120 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_TIMER_INCLUDE_H 2 | #define UVW_TIMER_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "handle.hpp" 8 | #include "loop.h" 9 | 10 | namespace uvw { 11 | 12 | /*! @brief Timer event. */ 13 | struct timer_event {}; 14 | 15 | /** 16 | * @brief The timer handle. 17 | * 18 | * Timer handles are used to schedule events to be emitted in the future. 19 | * 20 | * To create a `timer_handle` through a `loop`, no arguments are required. 21 | */ 22 | class timer_handle final: public handle { 23 | static void start_callback(uv_timer_t *hndl); 24 | 25 | public: 26 | using time = std::chrono::duration; 27 | 28 | using handle::handle; 29 | 30 | /** 31 | * @brief Initializes the handle. 32 | * @return Underlying return value. 33 | */ 34 | int init(); 35 | 36 | /** 37 | * @brief Starts the timer. 38 | * 39 | * If timeout is zero, a timer event is emitted on the next event loop 40 | * iteration. If repeat is non-zero, a timer event is emitted first after 41 | * timeout milliseconds and then repeatedly after repeat milliseconds. 42 | * 43 | * @param timeout Milliseconds before to emit an event (use 44 | * `std::chrono::duration`). 45 | * @param repeat Milliseconds between successive events (use 46 | * `std::chrono::duration`). 47 | * 48 | * @return Underlying return value. 49 | */ 50 | int start(time timeout, time repeat); 51 | 52 | /** 53 | * @brief Stops the handle. 54 | * @return Underlying return value. 55 | */ 56 | int stop(); 57 | 58 | /** 59 | * @brief Stops the timer and restarts it if it was repeating. 60 | * 61 | * Stop the timer, and if it is repeating restart it using the repeat value 62 | * as the timeout. 63 | * 64 | * @return Underlying return value. 65 | */ 66 | int again(); 67 | 68 | /** 69 | * @brief Sets the repeat interval value. 70 | * 71 | * The timer will be scheduled to run on the given interval and will follow 72 | * normal timer semantics in the case of a time-slice overrun.
73 | * For example, if a 50ms repeating timer first runs for 17ms, it will be 74 | * scheduled to run again 33ms later. If other tasks consume more than the 75 | * 33ms following the first timer event, then another event will be emitted 76 | * as soon as possible. 77 | * 78 | * If the repeat value is set from a listener bound to an event, it does 79 | * not immediately take effect. If the timer was non-repeating before, it 80 | * will have been stopped. If it was repeating, then the old repeat value 81 | * will have been used to schedule the next timeout. 82 | * 83 | * @param repeat Repeat interval in milliseconds (use 84 | * `std::chrono::duration`). 85 | */ 86 | void repeat(time repeat); 87 | 88 | /** 89 | * @brief Gets the timer repeat value. 90 | * @return Timer repeat value in milliseconds (as a 91 | * `std::chrono::duration`). 92 | */ 93 | time repeat(); 94 | 95 | /** 96 | * @brief Gets the timer due value. 97 | * 98 | * The time is relative to `loop::now()`. 99 | * 100 | * @return The timer due value or 0 if it has expired. 101 | */ 102 | time due_in(); 103 | }; 104 | 105 | } // namespace uvw 106 | 107 | #ifndef UVW_AS_LIB 108 | # include "timer.cpp" 109 | #endif 110 | 111 | #endif // UVW_TIMER_INCLUDE_H 112 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/fs_event.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_FS_EVENT_INCLUDE_H 2 | #define UVW_FS_EVENT_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "config.h" 8 | #include "enum.hpp" 9 | #include "handle.hpp" 10 | #include "loop.h" 11 | #include "util.h" 12 | 13 | namespace uvw { 14 | 15 | namespace details { 16 | 17 | enum class uvw_fs_event_flags : std::underlying_type_t { 18 | WATCH_ENTRY = UV_FS_EVENT_WATCH_ENTRY, 19 | STAT = UV_FS_EVENT_STAT, 20 | RECURSIVE = UV_FS_EVENT_RECURSIVE, 21 | _UVW_ENUM = 0 22 | }; 23 | 24 | enum class uvw_fs_event : std::underlying_type_t { 25 | RENAME = UV_RENAME, 26 | CHANGE = UV_CHANGE 27 | }; 28 | 29 | } // namespace details 30 | 31 | /*! @brief Fs event event. */ 32 | struct fs_event_event { 33 | fs_event_event(const char *pathname, details::uvw_fs_event events); 34 | 35 | /** 36 | * @brief The path to the file being monitored. 37 | * 38 | * If the handle was started with a directory, the filename parameter will 39 | * be a relative path to a file contained in the directory. 40 | */ 41 | const char *filename; 42 | 43 | /** 44 | * @brief Detected events all in one. 45 | * 46 | * Available flags are: 47 | * 48 | * * `fs_event_handle::watch::RENAME` 49 | * * `fs_event_handle::watch::CHANGE` 50 | */ 51 | details::uvw_fs_event flags; 52 | }; 53 | 54 | /** 55 | * @brief The fs event handle. 56 | * 57 | * These handles allow the user to monitor a given path for changes, for 58 | * example, if the file was renamed or there was a generic change in it. The 59 | * best backend for the job on each platform is chosen by the handle. 60 | * 61 | * To create a `fs_event_handle` through a `loop`, no arguments are required. 62 | * 63 | * See the official 64 | * [documentation](http://docs.libuv.org/en/v1.x/fs_event.html) 65 | * for further details. 66 | */ 67 | class fs_event_handle final: public handle { 68 | static void start_callback(uv_fs_event_t *hndl, const char *filename, int events, int status); 69 | 70 | public: 71 | using watch = details::uvw_fs_event; 72 | using event_flags = details::uvw_fs_event_flags; 73 | 74 | using handle::handle; 75 | 76 | /** 77 | * @brief Initializes the handle. 78 | * @return Underlying return value. 79 | */ 80 | int init(); 81 | 82 | /** 83 | * @brief Starts watching the specified path. 84 | * 85 | * It will watch the specified path for changes.
86 | * As soon as a change is observed, a fs_event_event is emitted by the 87 | * handle. 88 | * 89 | * Available flags are: 90 | * 91 | * * `fs_event_handle::event_flags::WATCH_ENTRY` 92 | * * `fs_event_handle::event_flags::STAT` 93 | * * `fs_event_handle::event_flags::RECURSIVE` 94 | * 95 | * @param path The file or directory to be monitored. 96 | * @param flags Additional flags to control the behavior. 97 | * @return Underlying return value. 98 | */ 99 | int start(const std::string &path, event_flags flags = event_flags::_UVW_ENUM); 100 | 101 | /** 102 | * @brief Stops polling the file descriptor. 103 | * @return Underlying return value. 104 | */ 105 | int stop(); 106 | 107 | /** 108 | * @brief Gets the path being monitored. 109 | * @return The path being monitored, an empty string in case of errors. 110 | */ 111 | std::string path() noexcept; 112 | }; 113 | 114 | } // namespace uvw 115 | 116 | #ifndef UVW_AS_LIB 117 | # include "fs_event.cpp" 118 | #endif 119 | 120 | #endif // UVW_FS_EVENT_INCLUDE_H 121 | -------------------------------------------------------------------------------- /3rd/uvw/src/process.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "process.h" 3 | #endif 4 | 5 | #include 6 | #include "config.h" 7 | 8 | namespace uvw { 9 | 10 | UVW_INLINE exit_event::exit_event(int64_t code, int sig) noexcept 11 | : status{code}, signal{sig} {} 12 | 13 | UVW_INLINE void process_handle::exit_callback(uv_process_t *hndl, int64_t exit_status, int term_signal) { 14 | process_handle &process = *(static_cast(hndl->data)); 15 | process.publish(exit_event{exit_status, term_signal}); 16 | } 17 | 18 | UVW_INLINE process_handle::process_handle(loop::token token, std::shared_ptr ref) 19 | : handle{token, std::move(ref)} {} 20 | 21 | UVW_INLINE void process_handle::disable_stdio_inheritance() noexcept { 22 | uv_disable_stdio_inheritance(); 23 | } 24 | 25 | UVW_INLINE bool process_handle::kill(int pid, int signum) noexcept { 26 | return (0 == uv_kill(pid, signum)); 27 | } 28 | 29 | UVW_INLINE int process_handle::init() { 30 | // deferred initialization: libuv initializes process handles only when 31 | // uv_spawn is invoked and uvw stays true to the underlying library 32 | return 0; 33 | } 34 | 35 | UVW_INLINE int process_handle::spawn(const char *file, char **args, char **env) { 36 | uv_process_options_t po; 37 | 38 | po.exit_cb = &exit_callback; 39 | po.file = file; 40 | po.args = args; 41 | po.env = env; 42 | po.cwd = po_cwd.empty() ? nullptr : po_cwd.data(); 43 | po.flags = static_cast(po_flags); 44 | po.uid = po_uid; 45 | po.gid = po_gid; 46 | 47 | std::vector poStdio; 48 | poStdio.reserve(po_fd_stdio.size() + po_stream_stdio.size()); 49 | poStdio.insert(poStdio.begin(), po_fd_stdio.cbegin(), po_fd_stdio.cend()); 50 | poStdio.insert(poStdio.end(), po_stream_stdio.cbegin(), po_stream_stdio.cend()); 51 | 52 | po.stdio_count = static_cast(poStdio.size()); 53 | po.stdio = poStdio.data(); 54 | 55 | // see init member function for more details 56 | leak_if(0); 57 | 58 | return uv_spawn(parent().raw(), raw(), &po); 59 | } 60 | 61 | UVW_INLINE int process_handle::kill(int signum) { 62 | return uv_process_kill(raw(), signum); 63 | } 64 | 65 | UVW_INLINE int process_handle::pid() noexcept { 66 | return raw()->pid; 67 | } 68 | 69 | UVW_INLINE process_handle &process_handle::cwd(const std::string &path) noexcept { 70 | po_cwd = path; 71 | return *this; 72 | } 73 | 74 | UVW_INLINE process_handle &process_handle::flags(process_flags flags) noexcept { 75 | po_flags = flags; 76 | return *this; 77 | } 78 | 79 | UVW_INLINE process_handle &process_handle::stdio(file_handle fd, stdio_flags flags) { 80 | auto fgs = static_cast(flags); 81 | 82 | auto actual = uvw::file_handle{fd}; 83 | 84 | auto it = std::find_if(po_fd_stdio.begin(), po_fd_stdio.end(), [actual](auto &&container) { 85 | return static_cast>(container.data.fd) == static_cast>(actual); 86 | }); 87 | 88 | if(it == po_fd_stdio.cend()) { 89 | uv_stdio_container_t container; 90 | container.flags = fgs; 91 | container.data.fd = actual; 92 | po_fd_stdio.push_back(std::move(container)); 93 | } else { 94 | it->flags = fgs; 95 | it->data.fd = actual; 96 | } 97 | 98 | return *this; 99 | } 100 | 101 | UVW_INLINE process_handle &process_handle::uid(uid_type id) { 102 | po_uid = id; 103 | return *this; 104 | } 105 | 106 | UVW_INLINE process_handle &process_handle::gid(gid_type id) { 107 | po_gid = id; 108 | return *this; 109 | } 110 | 111 | } // namespace uvw 112 | -------------------------------------------------------------------------------- /3rd/docopt.cpp/include/docopt.h: -------------------------------------------------------------------------------- 1 | // 2 | // docopt.h 3 | // docopt 4 | // 5 | // Created by Jared Grubb on 2013-11-03. 6 | // Copyright (c) 2013 Jared Grubb. All rights reserved. 7 | // 8 | 9 | #ifndef docopt__docopt_h_ 10 | #define docopt__docopt_h_ 11 | 12 | #ifdef DOCOPT_HEADER_ONLY 13 | #define DOCOPT_INLINE inline 14 | #define DOCOPT_API 15 | #else 16 | #define DOCOPT_INLINE 17 | 18 | // With Microsoft Visual Studio, export certain symbols so they 19 | // are available to users of docopt.dll (shared library). The DOCOPT_DLL 20 | // macro should be defined if building a DLL (with Visual Studio), 21 | // and by clients using the DLL. The CMakeLists.txt and the 22 | // docopt-config.cmake it generates handle this. 23 | #ifdef DOCOPT_DLL 24 | // Whoever is *building* the DLL should define DOCOPT_EXPORTS. 25 | // The CMakeLists.txt that comes with docopt does this. 26 | // Clients of docopt.dll should NOT define DOCOPT_EXPORTS. 27 | #ifdef DOCOPT_EXPORTS 28 | #define DOCOPT_API __declspec(dllexport) 29 | #else 30 | #define DOCOPT_API __declspec(dllimport) 31 | #endif 32 | #else 33 | #define DOCOPT_API 34 | #endif 35 | #endif 36 | 37 | #include "docopt_value.h" 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | namespace docopt { 45 | 46 | // Usage string could not be parsed (ie, the developer did something wrong) 47 | struct DocoptLanguageError : std::runtime_error { using runtime_error::runtime_error; }; 48 | 49 | // Arguments passed by user were incorrect (ie, developer was good, user is wrong) 50 | struct DocoptArgumentError : std::runtime_error { using runtime_error::runtime_error; }; 51 | 52 | // Arguments contained '--help' and parsing was aborted early 53 | struct DocoptExitHelp : std::runtime_error { DocoptExitHelp() : std::runtime_error("Docopt --help argument encountered"){} }; 54 | 55 | // Arguments contained '--version' and parsing was aborted early 56 | struct DocoptExitVersion : std::runtime_error { DocoptExitVersion() : std::runtime_error("Docopt --version argument encountered") {} }; 57 | 58 | /// A map of options set by the user 59 | using Options = std::map; 60 | 61 | /// Parse user options from the given option string. 62 | /// 63 | /// @param doc The usage string 64 | /// @param argv The user-supplied arguments 65 | /// @param help Whether to end early if '-h' or '--help' is in the argv 66 | /// @param version Whether to end early if '--version' is in the argv 67 | /// @param options_first Whether options must precede all args (true), or if args and options 68 | /// can be arbitrarily mixed. 69 | /// 70 | /// @throws DocoptLanguageError if the doc usage string had errors itself 71 | /// @throws DocoptExitHelp if 'help' is true and the user has passed the '--help' argument 72 | /// @throws DocoptExitVersion if 'version' is true and the user has passed the '--version' argument 73 | /// @throws DocoptArgumentError if the user's argv did not match the usage patterns 74 | Options DOCOPT_API docopt_parse(std::string const& doc, 75 | std::vector const& argv, 76 | bool help = true, 77 | bool version = true, 78 | bool options_first = false); 79 | 80 | /// Parse user options from the given string, and exit appropriately 81 | /// 82 | /// Calls 'docopt_parse' and will terminate the program if any of the exceptions above occur: 83 | /// * DocoptLanguageError - print error and terminate (with exit code -1) 84 | /// * DocoptExitHelp - print usage string and terminate (with exit code 0) 85 | /// * DocoptExitVersion - print version and terminate (with exit code 0) 86 | /// * DocoptArgumentError - print error and usage string and terminate (with exit code -1) 87 | Options DOCOPT_API docopt(std::string const& doc, 88 | std::vector const& argv, 89 | bool help = true, 90 | std::string const& version = {}, 91 | bool options_first = false) noexcept; 92 | } 93 | 94 | #ifdef DOCOPT_HEADER_ONLY 95 | #include "docopt.cpp" 96 | #endif 97 | 98 | #endif /* defined(docopt__docopt_h_) */ 99 | -------------------------------------------------------------------------------- /3rd/uvw/src/dns.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "dns.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE addr_info_event::addr_info_event(std::unique_ptr addr) 10 | : data{std::move(addr)} {} 11 | 12 | UVW_INLINE name_info_event::name_info_event(const char *host, const char *serv) 13 | : hostname{host}, service{serv} {} 14 | 15 | UVW_INLINE void get_addr_info_req::addr_info_callback(uv_getaddrinfo_t *req, int status, addrinfo *res) { 16 | if(auto ptr = reserve(req); status) { 17 | ptr->publish(error_event{status}); 18 | } else { 19 | auto data = std::unique_ptr{res, [](addrinfo *addr) { uv_freeaddrinfo(addr); }}; 20 | ptr->publish(addr_info_event{std::move(data)}); 21 | } 22 | } 23 | 24 | UVW_INLINE int get_addr_info_req::node_addr_info(const char *node, const char *service, addrinfo *hints) { 25 | return this->leak_if(uv_getaddrinfo(parent().raw(), raw(), &addr_info_callback, node, service, hints)); 26 | } 27 | 28 | UVW_INLINE auto get_addr_info_req::node_addr_info_sync(const char *node, const char *service, addrinfo *hints) { 29 | auto req = raw(); 30 | auto err = uv_getaddrinfo(parent().raw(), req, nullptr, node, service, hints); 31 | auto data = std::unique_ptr{req->addrinfo, [](addrinfo *addr) { uv_freeaddrinfo(addr); }}; 32 | return std::make_pair(!err, std::move(data)); 33 | } 34 | 35 | UVW_INLINE int get_addr_info_req::node_addr_info(const std::string &node, addrinfo *hints) { 36 | return node_addr_info(node.data(), nullptr, hints); 37 | } 38 | 39 | UVW_INLINE std::pair> get_addr_info_req::node_addr_info_sync(const std::string &node, addrinfo *hints) { 40 | return node_addr_info_sync(node.data(), nullptr, hints); 41 | } 42 | 43 | UVW_INLINE int get_addr_info_req::service_addr_info(const std::string &service, addrinfo *hints) { 44 | return node_addr_info(nullptr, service.data(), hints); 45 | } 46 | 47 | UVW_INLINE std::pair> get_addr_info_req::service_addr_info_sync(const std::string &service, addrinfo *hints) { 48 | return node_addr_info_sync(nullptr, service.data(), hints); 49 | } 50 | 51 | UVW_INLINE int get_addr_info_req::addr_info(const std::string &node, const std::string &service, addrinfo *hints) { 52 | return node_addr_info(node.data(), service.data(), hints); 53 | } 54 | 55 | UVW_INLINE std::pair> get_addr_info_req::addr_info_sync(const std::string &node, const std::string &service, addrinfo *hints) { 56 | return node_addr_info_sync(node.data(), service.data(), hints); 57 | } 58 | 59 | UVW_INLINE void get_name_info_req::name_info_callback(uv_getnameinfo_t *req, int status, const char *hostname, const char *service) { 60 | if(auto ptr = reserve(req); status) { 61 | ptr->publish(error_event{status}); 62 | } else { 63 | ptr->publish(name_info_event{hostname, service}); 64 | } 65 | } 66 | 67 | UVW_INLINE int get_name_info_req::name_info(const sockaddr &addr, int flags) { 68 | return this->leak_if(uv_getnameinfo(parent().raw(), raw(), &name_info_callback, &addr, flags)); 69 | } 70 | 71 | UVW_INLINE int get_name_info_req::name_info(const std::string &ip, unsigned int port, int flags) { 72 | return name_info(details::ip_addr(ip.data(), port), flags); 73 | } 74 | 75 | UVW_INLINE int get_name_info_req::name_info(socket_address addr, int flags) { 76 | return name_info(std::move(addr.ip), addr.port, flags); 77 | } 78 | 79 | UVW_INLINE std::pair> get_name_info_req::name_info_sync(const sockaddr &addr, int flags) { 80 | auto req = raw(); 81 | auto err = uv_getnameinfo(parent().raw(), req, nullptr, &addr, flags); 82 | return std::make_pair(!err, std::make_pair(req->host, req->service)); 83 | } 84 | 85 | UVW_INLINE std::pair> get_name_info_req::name_info_sync(const std::string &ip, unsigned int port, int flags) { 86 | return name_info_sync(details::ip_addr(ip.data(), port), flags); 87 | } 88 | 89 | UVW_INLINE std::pair> get_name_info_req::name_info_sync(socket_address addr, int flags) { 90 | return name_info_sync(addr.ip, addr.port, flags); 91 | } 92 | 93 | } // namespace uvw 94 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'flamethrower', 3 | 'cpp', 'c', 4 | version: '0.12', 5 | meson_version: '>= 1.3.0', 6 | default_options: ['cpp_std=c++23,c++20', 'c_std=c23,c17', 'warning_level=3'], 7 | ) 8 | 9 | # libraries: 10 | 11 | libuv = dependency('libuv') 12 | ldns = dependency('ldns') 13 | gnutls = dependency('gnutls') 14 | 15 | nghttp2 = dependency('libnghttp2', 'nghttp2', required: get_option('doh')) 16 | 17 | # bundled libraries 18 | 19 | uvw_sources = files( 20 | '3rd/uvw/src/async.cpp', 21 | '3rd/uvw/src/check.cpp', 22 | '3rd/uvw/src/dns.cpp', 23 | '3rd/uvw/src/emitter.cpp', 24 | '3rd/uvw/src/fs.cpp', 25 | '3rd/uvw/src/fs_event.cpp', 26 | '3rd/uvw/src/fs_poll.cpp', 27 | '3rd/uvw/src/idle.cpp', 28 | '3rd/uvw/src/lib.cpp', 29 | '3rd/uvw/src/loop.cpp', 30 | '3rd/uvw/src/pipe.cpp', 31 | '3rd/uvw/src/poll.cpp', 32 | '3rd/uvw/src/prepare.cpp', 33 | '3rd/uvw/src/process.cpp', 34 | '3rd/uvw/src/signal.cpp', 35 | '3rd/uvw/src/stream.cpp', 36 | '3rd/uvw/src/tcp.cpp', 37 | '3rd/uvw/src/thread.cpp', 38 | '3rd/uvw/src/timer.cpp', 39 | '3rd/uvw/src/tty.cpp', 40 | '3rd/uvw/src/udp.cpp', 41 | '3rd/uvw/src/util.cpp', 42 | '3rd/uvw/src/work.cpp', 43 | ) 44 | 45 | uvw_includes = include_directories( 46 | '3rd/uvw/include', 47 | ) 48 | 49 | uvw_library = static_library( 50 | 'uvw', 51 | uvw_sources, 52 | cpp_args: ['-DUVW_AS_LIB', '-iquote', meson.current_source_dir() / '3rd/uvw/include/uvw'], 53 | dependencies: [libuv], 54 | ) 55 | 56 | uvw = declare_dependency( 57 | link_with: uvw_library, 58 | compile_args: ['-DUVW_AS_LIB', '-iquote', meson.current_source_dir() / '3rd/uvw/include/uvw'], 59 | include_directories: uvw_includes, 60 | ) 61 | 62 | json = declare_dependency( 63 | include_directories: include_directories('3rd/json/include'), 64 | ) 65 | 66 | httplib = declare_dependency( 67 | include_directories: include_directories('3rd/cpp-httplib/include'), 68 | ) 69 | 70 | urlparse_sources = files( 71 | '3rd/urlparse/src/urlparse.c', 72 | ) 73 | 74 | urlparse_includes = include_directories( 75 | '3rd/urlparse/include', 76 | ) 77 | 78 | urlparse_library = static_library( 79 | 'urlparse', 80 | urlparse_sources, 81 | include_directories: urlparse_includes, 82 | ) 83 | 84 | urlparse = declare_dependency( 85 | link_with: [urlparse_library], 86 | include_directories: urlparse_includes, 87 | ) 88 | 89 | base64_sources = files( 90 | '3rd/cpp-base64/src/base64.cpp', 91 | ) 92 | 93 | base64_includes = include_directories( 94 | '3rd/cpp-base64/include', 95 | ) 96 | 97 | base64_library = static_library( 98 | 'base64', 99 | base64_sources, 100 | include_directories: base64_includes, 101 | ) 102 | 103 | base64 = declare_dependency( 104 | link_with: [base64_library], 105 | include_directories: base64_includes, 106 | ) 107 | 108 | docopt_includes = include_directories( 109 | '3rd/docopt.cpp/include', 110 | ) 111 | 112 | docopt_sources = files( 113 | '3rd/docopt.cpp/src/docopt.cpp', 114 | ) 115 | 116 | docopt_library = static_library( 117 | 'docopt', 118 | docopt_sources, 119 | include_directories: docopt_includes, 120 | ) 121 | 122 | docopt = declare_dependency( 123 | link_with: [docopt_library], 124 | include_directories: docopt_includes, 125 | ) 126 | 127 | # x 128 | 129 | flame_sources = files( 130 | 'flame/addr.cpp', 131 | 'flame/metrics.cpp', 132 | 'flame/metrics.h', 133 | 'flame/query.cpp', 134 | 'flame/query.h', 135 | 'flame/target.h', 136 | 'flame/trafgen.cpp', 137 | 'flame/trafgen.h', 138 | 'flame/tcpsession.cpp', 139 | 'flame/tcpsession.h', 140 | 'flame/tcptlssession.cpp', 141 | 'flame/tcptlssession.h', 142 | 'flame/utils.cpp', 143 | 'flame/utils.h', 144 | 'flame/main.cpp', 145 | ) 146 | 147 | flame_deps = [ 148 | libuv, 149 | uvw, 150 | docopt, 151 | json, 152 | httplib, 153 | urlparse, 154 | gnutls, 155 | ldns, 156 | base64, 157 | ] 158 | 159 | flame_args = [ 160 | '-DHAVE_STDBOOL_H', # workaround for ldns re-defining bool type 161 | ] 162 | 163 | if get_option('doh') 164 | flame_sources += [ 165 | 'flame/http.h', 166 | 'flame/httpssession.cpp', 167 | 'flame/httpssession.h', 168 | ] 169 | flame_args += [ 170 | '-DDOH_ENABLE' 171 | ] 172 | flame_deps += [ 173 | nghttp2, 174 | ] 175 | endif 176 | 177 | flame = executable( 178 | 'flame', 179 | flame_sources, 180 | dependencies: flame_deps, 181 | cpp_args: flame_args, 182 | include_directories: include_directories('flame'), 183 | install: true, 184 | ) 185 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_TTY_INCLUDE_H 2 | #define UVW_TTY_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "config.h" 8 | #include "stream.h" 9 | #include "util.h" 10 | 11 | namespace uvw { 12 | 13 | namespace details { 14 | 15 | struct reset_mode_memo { 16 | ~reset_mode_memo(); 17 | }; 18 | 19 | enum class uvw_tty_mode_t : std::underlying_type_t { 20 | NORMAL = UV_TTY_MODE_NORMAL, 21 | RAW = UV_TTY_MODE_RAW, 22 | IO = UV_TTY_MODE_IO 23 | }; 24 | 25 | enum class uvw_tty_vtermstate_t : std::underlying_type_t { 26 | SUPPORTED = UV_TTY_SUPPORTED, 27 | UNSUPPORTED = UV_TTY_UNSUPPORTED 28 | }; 29 | 30 | } // namespace details 31 | 32 | /** 33 | * @brief The tty handle. 34 | * 35 | * TTY handles represent a stream for the console. 36 | * 37 | * To create a `tty_handle` through a `loop`, arguments follow: 38 | * 39 | * * A valid file_handle. Usually the file descriptor will be: 40 | * * `uvw::std_in` or `0` for `stdin` 41 | * * `uvw::std_out` or `1` for `stdout` 42 | * * `uvw::std_err` or `2` for `stderr` 43 | * * A boolean value that specifies the plan on calling `read()` with this 44 | * stream. Remember that `stdin` is readable, `stdout` is not. 45 | * 46 | * See the official 47 | * [documentation](http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_init) 48 | * for further details. 49 | */ 50 | class tty_handle final: public stream_handle { 51 | static std::shared_ptr mode_memo_handler(); 52 | 53 | public: 54 | using tty_mode = details::uvw_tty_mode_t; 55 | using tty_vtermstate = details::uvw_tty_vtermstate_t; 56 | 57 | explicit tty_handle(loop::token token, std::shared_ptr ref, file_handle desc, bool readable); 58 | 59 | /** 60 | * @brief Initializes the handle. 61 | * @return Underlying return value. 62 | */ 63 | int init(); 64 | 65 | /** 66 | * @brief Sets the TTY using the specified terminal mode. 67 | * 68 | * Available modes are: 69 | * 70 | * * `TTY::tty_mode::NORMAL` 71 | * * `TTY::tty_mode::RAW` 72 | * * `TTY::tty_mode::IO` 73 | * 74 | * See the official 75 | * [documentation](http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t) 76 | * for further details. 77 | * 78 | * @param m The mode to be set. 79 | * @return True in case of success, false otherwise. 80 | */ 81 | bool mode(tty_mode m); 82 | 83 | /** 84 | * @brief Resets TTY settings to default values. 85 | * @return True in case of success, false otherwise. 86 | */ 87 | bool reset_mode() noexcept; 88 | 89 | /** 90 | * @brief Gets the current Window size. 91 | * @return The current Window size or `{-1, -1}` in case of errors. 92 | */ 93 | win_size get_win_size(); 94 | 95 | /** 96 | * @brief Controls whether console virtual terminal sequences are processed 97 | * by the library or console. 98 | * 99 | * This function is only meaningful on Windows systems. On Unix it is 100 | * silently ignored. 101 | * 102 | * Available states are: 103 | * 104 | * * `TTY::tty_vtermstate::SUPPORTED` 105 | * * `TTY::tty_vtermstate::UNSUPPORTED` 106 | * 107 | * See the official 108 | * [documentation](http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_vtermstate_t) 109 | * for further details. 110 | * 111 | * @param s The state to be set. 112 | */ 113 | void vterm_state(tty_vtermstate s) const noexcept; 114 | 115 | /** 116 | * @brief Gets the current state of whether console virtual terminal 117 | * sequences are handled by the library or the console. 118 | * 119 | * This function is not implemented on Unix. 120 | * 121 | * Available states are: 122 | * 123 | * * `TTY::tty_vtermstate::SUPPORTED` 124 | * * `TTY::tty_vtermstate::UNSUPPORTED` 125 | * 126 | * See the official 127 | * [documentation](http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_vtermstate_t) 128 | * for further details. 129 | * 130 | * @return The current state. 131 | */ 132 | tty_vtermstate vterm_state() const noexcept; 133 | 134 | private: 135 | std::shared_ptr memo; 136 | file_handle fd; 137 | int rw; 138 | }; 139 | 140 | } // namespace uvw 141 | 142 | #ifndef UVW_AS_LIB 143 | # include "tty.cpp" 144 | #endif 145 | 146 | #endif // UVW_TTY_INCLUDE_H 147 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/emitter.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_EMITTER_INCLUDE_H 2 | #define UVW_EMITTER_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "config.h" 14 | #include "type_info.hpp" 15 | 16 | namespace uvw { 17 | 18 | /** 19 | * @brief Error event. 20 | * 21 | * Custom wrapper around error constants of `libuv`. 22 | */ 23 | struct error_event { 24 | template>> 25 | explicit error_event(Type val) noexcept 26 | : ec{static_cast(val)} {} 27 | 28 | /** 29 | * @brief Returns the `libuv` error code equivalent to the given platform dependent error code. 30 | * 31 | * It returns: 32 | * * POSIX error codes on Unix (the ones stored in errno). 33 | * * Win32 error codes on Windows (those returned by GetLastError() or WSAGetLastError()). 34 | * 35 | * If `sys` is already a `libuv` error code, it is simply returned. 36 | * 37 | * @param sys A platform dependent error code. 38 | * @return The `libuv` error code equivalent to the given platform dependent error code. 39 | */ 40 | static int translate(int sys) noexcept; 41 | 42 | /** 43 | * @brief Returns the error message for the given error code. 44 | * 45 | * Leaks a few bytes of memory when you call it with an unknown error code. 46 | * 47 | * @return The error message for the given error code. 48 | */ 49 | const char *what() const noexcept; 50 | 51 | /** 52 | * @brief Returns the error name for the given error code. 53 | * 54 | * Leaks a few bytes of memory when you call it with an unknown error code. 55 | * 56 | * @return The error name for the given error code. 57 | */ 58 | const char *name() const noexcept; 59 | 60 | /** 61 | * @brief Gets the underlying error code, that is an error constant of `libuv`. 62 | * @return The underlying error code. 63 | */ 64 | int code() const noexcept; 65 | 66 | /** 67 | * @brief Checks if the event contains a valid error code. 68 | * @return True in case of success, false otherwise. 69 | */ 70 | explicit operator bool() const noexcept; 71 | 72 | private: 73 | const int ec; 74 | }; 75 | 76 | /** 77 | * @brief Event emitter base class. 78 | * 79 | * Almost everything in `uvw` is an event emitter.
80 | * This is the base class from which resources and loops inherit. 81 | */ 82 | template 83 | class emitter { 84 | public: 85 | template 86 | using listener_t = std::function; 87 | 88 | private: 89 | template 90 | const auto &handler() const noexcept { 91 | return std::get>(handlers); 92 | } 93 | 94 | template 95 | auto &handler() noexcept { 96 | return std::get>(handlers); 97 | } 98 | 99 | protected: 100 | template 101 | void publish(Type event) { 102 | if(auto &listener = handler(); listener) { 103 | listener(event, *static_cast(this)); 104 | } 105 | } 106 | 107 | public: 108 | virtual ~emitter() noexcept { 109 | static_assert(std::is_base_of_v, Elem>); 110 | } 111 | 112 | /** 113 | * @brief Registers a long-lived listener with the event emitter. 114 | * 115 | * This method is used to register a listener with the emitter.
116 | * A listener is usually defined as a callable object assignable to a 117 | * `std::function 123 | void on(listener_t f) { 124 | handler() = std::move(f); 125 | } 126 | 127 | /*! @brief Disconnects the listener for the given event type. */ 128 | template 129 | void reset() noexcept { 130 | handler() = nullptr; 131 | } 132 | 133 | /*! @brief Disconnects all listeners. */ 134 | void reset() noexcept { 135 | reset(); 136 | (reset(), ...); 137 | } 138 | 139 | /** 140 | * @brief Checks if there is a listener registered for the specific event. 141 | * @return True if there is a listener registered for the specific event, 142 | * false otherwise. 143 | */ 144 | template 145 | bool has() const noexcept { 146 | return static_cast(handler()); 147 | } 148 | 149 | private: 150 | std::tuple, listener_t...> handlers{}; 151 | }; 152 | 153 | } // namespace uvw 154 | 155 | #ifndef UVW_AS_LIB 156 | # include "emitter.cpp" 157 | #endif 158 | 159 | #endif // UVW_EMITTER_INCLUDE_H 160 | -------------------------------------------------------------------------------- /3rd/uvw/src/thread.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "thread.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE thread::thread(loop::token token, std::shared_ptr ref, task t, std::shared_ptr d) noexcept 10 | : uv_type{token, std::move(ref)}, 11 | data{std::move(d)}, 12 | func{std::move(t)} {} 13 | 14 | UVW_INLINE void thread::create_callback(void *arg) { 15 | thread &curr = *(static_cast(arg)); 16 | curr.func(curr.data); 17 | } 18 | 19 | UVW_INLINE thread::type thread::self() noexcept { 20 | return uv_thread_self(); 21 | } 22 | 23 | UVW_INLINE int thread::getcpu() noexcept { 24 | return uv_thread_getcpu(); 25 | } 26 | 27 | UVW_INLINE bool thread::equal(const thread &tl, const thread &tr) noexcept { 28 | return !(0 == uv_thread_equal(tl.raw(), tr.raw())); 29 | } 30 | 31 | UVW_INLINE thread::~thread() noexcept { 32 | join(); 33 | } 34 | 35 | UVW_INLINE bool thread::run() noexcept { 36 | return (0 == uv_thread_create(raw(), &create_callback, this)); 37 | } 38 | 39 | UVW_INLINE bool thread::run(create_flags opts, std::size_t stack) noexcept { 40 | uv_thread_options_t params{static_cast(opts), stack}; 41 | return (0 == uv_thread_create_ex(raw(), ¶ms, &create_callback, this)); 42 | } 43 | 44 | UVW_INLINE bool thread::join() noexcept { 45 | return (0 == uv_thread_join(raw())); 46 | } 47 | 48 | UVW_INLINE thread_local_storage::thread_local_storage(loop::token token, std::shared_ptr ref) noexcept 49 | : uv_type{token, std::move(ref)} { 50 | uv_key_create(uv_type::raw()); 51 | } 52 | 53 | UVW_INLINE thread_local_storage::~thread_local_storage() noexcept { 54 | uv_key_delete(uv_type::raw()); 55 | } 56 | 57 | UVW_INLINE uv_once_t *once::guard() noexcept { 58 | static uv_once_t once = UV_ONCE_INIT; 59 | return &once; 60 | } 61 | 62 | UVW_INLINE mutex::mutex(loop::token token, std::shared_ptr ref, bool recursive) noexcept 63 | : uv_type{token, std::move(ref)} { 64 | if(recursive) { 65 | uv_mutex_init_recursive(raw()); 66 | } else { 67 | uv_mutex_init(raw()); 68 | } 69 | } 70 | 71 | UVW_INLINE mutex::~mutex() noexcept { 72 | uv_mutex_destroy(raw()); 73 | } 74 | 75 | UVW_INLINE void mutex::lock() noexcept { 76 | uv_mutex_lock(raw()); 77 | } 78 | 79 | UVW_INLINE bool mutex::try_lock() noexcept { 80 | return (0 == uv_mutex_trylock(raw())); 81 | } 82 | 83 | UVW_INLINE void mutex::unlock() noexcept { 84 | uv_mutex_unlock(raw()); 85 | } 86 | 87 | UVW_INLINE rwlock::rwlock(loop::token token, std::shared_ptr ref) noexcept 88 | : uv_type{token, std::move(ref)} { 89 | uv_rwlock_init(raw()); 90 | } 91 | 92 | UVW_INLINE rwlock::~rwlock() noexcept { 93 | uv_rwlock_destroy(raw()); 94 | } 95 | 96 | UVW_INLINE void rwlock::rdlock() noexcept { 97 | uv_rwlock_rdlock(raw()); 98 | } 99 | 100 | UVW_INLINE bool rwlock::try_rdlock() noexcept { 101 | return (0 == uv_rwlock_tryrdlock(raw())); 102 | } 103 | 104 | UVW_INLINE void rwlock::rdunlock() noexcept { 105 | uv_rwlock_rdunlock(raw()); 106 | } 107 | 108 | UVW_INLINE void rwlock::wrlock() noexcept { 109 | uv_rwlock_wrlock(raw()); 110 | } 111 | 112 | UVW_INLINE bool rwlock::try_wrlock() noexcept { 113 | return (0 == uv_rwlock_trywrlock(raw())); 114 | } 115 | 116 | UVW_INLINE void rwlock::wrunlock() noexcept { 117 | uv_rwlock_wrunlock(raw()); 118 | } 119 | 120 | UVW_INLINE semaphore::semaphore(loop::token token, std::shared_ptr ref, unsigned int value) noexcept 121 | : uv_type{token, std::move(ref)} { 122 | uv_sem_init(raw(), value); 123 | } 124 | 125 | UVW_INLINE semaphore::~semaphore() noexcept { 126 | uv_sem_destroy(raw()); 127 | } 128 | 129 | UVW_INLINE void semaphore::post() noexcept { 130 | uv_sem_post(raw()); 131 | } 132 | 133 | UVW_INLINE void semaphore::wait() noexcept { 134 | uv_sem_wait(raw()); 135 | } 136 | 137 | UVW_INLINE bool semaphore::try_wait() noexcept { 138 | return (0 == uv_sem_trywait(raw())); 139 | } 140 | 141 | UVW_INLINE condition::condition(loop::token token, std::shared_ptr ref) noexcept 142 | : uv_type{token, std::move(ref)} { 143 | uv_cond_init(raw()); 144 | } 145 | 146 | UVW_INLINE condition::~condition() noexcept { 147 | uv_cond_destroy(raw()); 148 | } 149 | 150 | UVW_INLINE void condition::signal() noexcept { 151 | uv_cond_signal(raw()); 152 | } 153 | 154 | UVW_INLINE void condition::broadcast() noexcept { 155 | uv_cond_broadcast(raw()); 156 | } 157 | 158 | UVW_INLINE void condition::wait(mutex &mtx) noexcept { 159 | uv_cond_wait(raw(), mtx.raw()); 160 | } 161 | 162 | UVW_INLINE bool condition::timed_wait(mutex &mtx, uint64_t timeout) noexcept { 163 | return (0 == uv_cond_timedwait(raw(), mtx.raw(), timeout)); 164 | } 165 | 166 | UVW_INLINE barrier::barrier(loop::token token, std::shared_ptr ref, unsigned int count) noexcept 167 | : uv_type{token, std::move(ref)} { 168 | uv_barrier_init(raw(), count); 169 | } 170 | 171 | UVW_INLINE barrier::~barrier() noexcept { 172 | uv_barrier_destroy(raw()); 173 | } 174 | 175 | UVW_INLINE bool barrier::wait() noexcept { 176 | return (0 == uv_barrier_wait(raw())); 177 | } 178 | 179 | } // namespace uvw 180 | -------------------------------------------------------------------------------- /flame/metrics.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 NSONE, Inc 2 | 3 | #pragma once 4 | 5 | #include "config.h" 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | class Metrics; 16 | 17 | class MetricsMgr { 18 | std::chrono::high_resolution_clock::time_point _start_time; 19 | std::chrono::high_resolution_clock::time_point _stop_time; 20 | 21 | std::shared_ptr _loop; 22 | std::shared_ptr _config; 23 | 24 | // aggregation and output 25 | std::shared_ptr _metric_period_timer; 26 | 27 | // metric output file, if enabled 28 | std::ofstream _metric_file; 29 | 30 | // metric counters, one per TrafGen 31 | std::vector> _metrics; 32 | 33 | std::unordered_map _response_codes; 34 | 35 | // command line XXX move to config 36 | std::string _cmdline; 37 | 38 | // unique run id 39 | std::string _run_id; 40 | std::string _start_ts; 41 | double _runtime_s{0.0}; 42 | 43 | // qps avg calculations 44 | u_long _avg_qps_calc_r_count{0}; 45 | u_long _avg_qps_calc_s_count{0}; 46 | std::chrono::high_resolution_clock::time_point _qps_clock; 47 | 48 | // times we've aggregated (used in avg calcs) 49 | u_long _aggregate_count{0}; 50 | 51 | // aggregated totals throughout entire run 52 | u_long _agg_total_r_count{0}; 53 | u_long _agg_total_s_count{0}; 54 | u_long _agg_total_qps_r_avg{0}; 55 | u_long _agg_total_qps_s_avg{0}; 56 | u_long _agg_total_timeouts{0}; 57 | u_long _agg_total_bad_count{0}; 58 | u_long _agg_total_net_errors{0}; 59 | u_long _agg_total_tcp_connections{0}; 60 | double _agg_total_response_min_ms{0.0}; 61 | double _agg_total_response_max_ms{0.0}; 62 | 63 | // TODO current avg of avgs, switch to percentiles 64 | double _agg_total_pkt_size_avg{0.0}; 65 | double _agg_total_response_avg_ms{0.0}; 66 | 67 | // aggregated totals for single time period 68 | // these all need to be reset in reset_periodic_stats() 69 | u_long _agg_period_r_count{0}; 70 | u_long _agg_period_s_count{0}; 71 | u_long _agg_period_in_flight{0}; 72 | u_long _agg_period_timeouts{0}; 73 | u_long _agg_period_bad_count{0}; 74 | u_long _agg_period_net_errors{0}; 75 | u_long _agg_period_tcp_connections{0}; 76 | double _agg_period_response_min_ms{0.0}; 77 | double _agg_period_response_max_ms{0.0}; 78 | 79 | // TODO avg of avgs, switch to percentiles 80 | double _agg_period_pkt_size_avg{0.0}; 81 | double _agg_period_response_avg_ms{0.0}; 82 | 83 | // do we record each individual trafgen, or only aggregate? 84 | bool _per_trafgen_metrics{true}; 85 | 86 | void header_to_disk(); 87 | void flush_to_disk(); 88 | void periodic_stats(); 89 | 90 | // console display 91 | void display_periodic_stats(); 92 | void display_final_text(); 93 | 94 | void update_runtime(); 95 | 96 | void aggregate(bool no_avgs = false); 97 | void aggregate_trafgen(const Metrics *m); 98 | 99 | public: 100 | MetricsMgr(std::shared_ptr l, std::shared_ptr c, const std::string &cmdline) 101 | : _loop(l) 102 | , _config(c) 103 | , _cmdline(cmdline) 104 | { 105 | } 106 | 107 | void start(); 108 | 109 | void stop(); 110 | 111 | void finalize(); 112 | 113 | std::string toJSON() const; 114 | 115 | std::shared_ptr create_trafgen_metrics(); 116 | }; 117 | 118 | class Metrics { 119 | friend class MetricsMgr; 120 | 121 | std::shared_ptr _loop; 122 | 123 | std::string _trafgen_id; 124 | 125 | // total sends entire lifetime 126 | u_long _total_r_count{0}; 127 | u_long _total_s_count{0}; 128 | 129 | // period counters, reset each flush to MetricsMgr 130 | u_long _period_r_count{0}; 131 | u_long _period_s_count{0}; 132 | u_long _period_bad_count{0}; 133 | u_long _period_net_errors{0}; 134 | u_long _period_timeouts{0}; 135 | u_long _period_tcp_connections{0}; 136 | double _period_response_avg_ms{0.0}; 137 | double _period_response_min_ms{0.0}; 138 | double _period_response_max_ms{0.0}; 139 | double _period_pkt_size_avg{0.0}; 140 | 141 | // updated during operations that adjust in_flight like send, recv, timeout 142 | u_long _in_flight{0}; 143 | 144 | std::unordered_map _response_codes; 145 | 146 | public: 147 | constexpr static const double HR_TO_SEC_MULT = 0.000000001; 148 | constexpr static const double HR_TO_MSEC_MULT = 0.000001; 149 | 150 | Metrics(std::shared_ptr l) 151 | : _loop(l) 152 | { 153 | } 154 | 155 | void trafgen_id(u_int port); 156 | 157 | void receive(const std::chrono::high_resolution_clock::time_point &rcv_time, uint8_t rcode, u_long in_f); 158 | 159 | void reset_periodic_stats(); 160 | 161 | void timeout(u_long in_f); 162 | 163 | void net_error(); 164 | 165 | void send(u_long size, u_long i, u_long in_f); 166 | 167 | void tcp_connection() 168 | { 169 | _period_tcp_connections++; 170 | } 171 | 172 | void bad_receive(u_long in_f); 173 | 174 | void toJSON(nlohmann::json &j) const; 175 | }; 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flamethrower 2 | 3 | A DNS performance and functional testing utility. 4 | 5 | ## Overview 6 | 7 | Flamethrower is a small, fast, configurable tool for functional testing, benchmarking, and stress testing DNS servers and networks. It supports IPv4, IPv6, UDP, TCP, DoT, and DoH and has a modular system for generating queries used in the tests. 8 | 9 | Originally built as an alternative to [dnsperf](https://github.com/DNS-OARC/dnsperf), many of the command line options are compatible. 10 | 11 | ## Getting Started 12 | 13 | The easiest way to get started with Flamethrower is to use the [public docker image](https://hub.docker.com/repository/docker/ns1labs/flame): 14 | ``` 15 | docker pull ns1labs/flame 16 | docker run ns1labs/flame --help 17 | ``` 18 | 19 | There are currently no prebuilt operating system packages. If you would like to build your own executable, please see the Build section below. 20 | 21 | ## Usage 22 | 23 | Current command line options are described with: 24 | 25 | ``` 26 | flame --help 27 | ``` 28 | 29 | ## Quick Examples 30 | 31 | Flame localhost port 53, UDP, maximum speed: 32 | ``` 33 | flame localhost 34 | ``` 35 | 36 | Flame target, port 5300, TCP: 37 | ``` 38 | flame -p 5300 -P tcp target.test.com 39 | ``` 40 | 41 | Flame target, port 443, DoT: 42 | ``` 43 | flame -p 443 -P dot target.test.com 44 | ``` 45 | 46 | Flame target, DNS over HTTPS GET: 47 | ``` 48 | flame -P doh target.test.com/dns-query 49 | ``` 50 | 51 | Flame target, DNS over HTTPS POST: 52 | ``` 53 | flame -P doh -M POST target.test.com/dns-query 54 | ``` 55 | 56 | Flame target with random labels: 57 | ``` 58 | flame target.test.com -g randomlabel lblsize=10 lblcount=4 count=1000 59 | ``` 60 | 61 | Flame multiple target at once, reading the list from a file: 62 | ``` 63 | flame file --targets myresolvers.txt 64 | ``` 65 | 66 | ## Detailed Features 67 | 68 | ### Query Generators 69 | 70 | Flamethrower uses a modular system for generating queries. Most modules generate all queries before sending begins, for performance reasons. 71 | Each module may include its own list of configuration options which can be set via key/value pairs on the command line. 72 | See full `--help` for the current list of generators and their options. 73 | 74 | ### Rate Limiting 75 | 76 | By default, Flamethrower will send traffic as fast as possible. To limit to a specific overall queries per second, use `-Q` 77 | 78 | ### Dynamic QPS Flow 79 | 80 | Flamethrower can adjust its QPS flow over time. This is useful for generating a "signal" of traffic (e.g. a square wave) for calibrating metrics collection. For example, to send 10 QPS for 120000ms, then 80 QPS for 120000ms, etc use `--qps-flow "10,120000;80,120000;10,120000;"`. Flow change will not loop, you should list as many changes as necessary. Once the flow reaches the final QPS number, it will hold it until program termination. 81 | 82 | ### Output Metrics 83 | 84 | Flamethrower can generate detailed metrics for each of its concurrent senders. Metrics include send and receive counts, timeouts, min, max and average latency, errors, and the like. The output format is JSON, and is suitable for ingestion into databases such as Elastic for further processing or visualization. See the `-o` flag. 85 | 86 | ### Concurrency 87 | 88 | Flamethrower is single threaded, async i/o. You specify the amount of concurrent senders with the `-c` option. Each of these senders will send a configurable number of consecutive queries (see `-q`), then enter a configurable delay period (see `-d`) before looping. 89 | 90 | Each concurrent sender will pull the next query from the total queries generated by the Query Generator, looping once it reaches the end of the query list (if the program is configured to continue). 91 | 92 | There is currently no built-in support for multiprocess sending, so the maximum throughput will be reached once a single CPU is saturated. However, you may manually start several concurrent `flame` processes, including up to 1 per CPU available. There is future planned support for builtin multiprocess sending. 93 | 94 | ## Build Dependencies 95 | 96 | * Linux or macOS 97 | * C++ compiler supporting C++20 98 | * Meson Build System 99 | * Ninja 100 | * pkgconf 101 | 102 | * libuv 103 | * libldns 104 | * gnutls 105 | * nghttp2 (optional for DNS-over-HTTPS support) 106 | 107 | To insta 108 | 109 | ## Building 110 | 111 | Start by installing build requirements. On Fedora, run: 112 | 113 | ``` 114 | dnf install gcc g++ meson pkgconf ninja-build ldns-devel libuv-devel gnutls-devel libnghttp2-devel 115 | ``` 116 | 117 | Go to the checked-out repository and run: 118 | 119 | ``` 120 | meson setup build 121 | cd build 122 | ninja 123 | ``` 124 | 125 | The DNS-over-HTTPS support will be enabled if libnghttp2 is detected. To explicity build with or without DoH support, use: 126 | 127 | ``` 128 | meson setup -Ddoh=false build 129 | ``` 130 | 131 | To build the docker image, run: 132 | 133 | ``` 134 | docker build -t ns1labs/flame . 135 | docker run --rm --net host ns1labs/flame --help 136 | ``` 137 | 138 | ## Contributions 139 | 140 | Pull Requests and issues are welcome. 141 | 142 | ## License 143 | 144 | This code is released under Apache License 2.0. You can find terms and conditions in the LICENSE file. 145 | 146 | Copyright 2017-2022 NSONE, Inc. 147 | 148 | Copyright 2025 Flamethrower Contributors 149 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/pipe.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_PIPE_INCLUDE_H 2 | #define UVW_PIPE_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "config.h" 9 | #include "enum.hpp" 10 | #include "loop.h" 11 | #include "request.hpp" 12 | #include "stream.h" 13 | #include "util.h" 14 | 15 | namespace uvw { 16 | 17 | namespace details { 18 | 19 | enum class uvw_chmod_flags : std::underlying_type_t { 20 | READABLE = UV_READABLE, 21 | WRITABLE = UV_WRITABLE, 22 | _UVW_ENUM = 0 23 | }; 24 | 25 | } 26 | 27 | /** 28 | * @brief The pipe handle. 29 | * 30 | * Pipe handles provide an abstraction over local domain sockets on Unix and 31 | * named pipes on Windows. 32 | * 33 | * To create a `pipe_handle` through a `loop`, arguments follow: 34 | * 35 | * * An optional boolean value that indicates if this pipe will be used for 36 | * handle passing between processes. 37 | */ 38 | class pipe_handle final: public stream_handle { 39 | public: 40 | using chmod_flags = details::uvw_chmod_flags; 41 | 42 | explicit pipe_handle(loop::token token, std::shared_ptr ref, bool pass = false); 43 | 44 | /** 45 | * @brief Initializes the handle. 46 | * @return Underlying return value. 47 | */ 48 | int init(); 49 | 50 | /** 51 | * @brief Opens an existing file descriptor or HANDLE as a pipe. 52 | * 53 | * The passed file descriptor or HANDLE is not checked for its type, but 54 | * it’s required that it represents a valid pipe. 55 | * 56 | * @param file A valid file handle (either a file descriptor or a HANDLE). 57 | * @return Underlying return value. 58 | */ 59 | int open(file_handle file); 60 | 61 | /** 62 | * @brief bind Binds the pipe to a file path (Unix) or a name (Windows). 63 | * 64 | * Paths on Unix get truncated typically between 92 and 108 bytes. 65 | * 66 | * @param name A valid file path. 67 | * @param no_truncate Force an error rather than allow truncating a path. 68 | * @return Underlying return value. 69 | */ 70 | int bind(const std::string &name, const bool no_truncate = false); 71 | 72 | /** 73 | * @brief Connects to the Unix domain socket or the named pipe. 74 | * 75 | * Paths on Unix get truncated typically between 92 and 108 bytes.
76 | * A connect event is emitted when the connection has been 77 | * established. 78 | * 79 | * @param name A valid domain socket or named pipe. 80 | * @param no_truncate Force an error rather than allow truncating a path. 81 | * @return Underlying return value. 82 | */ 83 | int connect(const std::string &name, const bool no_truncate = false); 84 | 85 | /** 86 | * @brief Gets the name of the Unix domain socket or the named pipe. 87 | * @return The name of the Unix domain socket or the named pipe, an empty 88 | * string in case of errors. 89 | */ 90 | std::string sock() const noexcept; 91 | 92 | /** 93 | * @brief Gets the name of the Unix domain socket or the named pipe to which 94 | * the handle is connected. 95 | * @return The name of the Unix domain socket or the named pipe to which 96 | * the handle is connected, an empty string in case of errors. 97 | */ 98 | std::string peer() const noexcept; 99 | 100 | /** 101 | * @brief Sets the number of pending pipe this instance can handle. 102 | * 103 | * This method can be used to set the number of pending pipe this instance 104 | * handles when the pipe server is waiting for connections.
105 | * Note that this setting applies to Windows only. 106 | * 107 | * @param count The number of accepted pending pipe. 108 | */ 109 | void pending(int count) noexcept; 110 | 111 | /** 112 | * @brief Gets the number of pending pipe this instance can handle. 113 | * @return The number of pending pipe this instance can handle. 114 | */ 115 | int pending() noexcept; 116 | 117 | /** 118 | * @brief Used to receive handles over IPC pipes. 119 | * 120 | * Steps to be done: 121 | * 122 | * * Call `pending()`, if it’s greater than zero then proceed. 123 | * * Initialize a handle of the given type, as returned by `receive()`. 124 | * * Call `accept(pipe, handle)`. 125 | * 126 | * @return The type of the pending handle. Possible values are: 127 | * 128 | * * `handle_type::PIPE` 129 | * * `handle_type::TCP` 130 | * * `handle_type::UDP` 131 | * * `handle_type::UNKNOWN` 132 | */ 133 | handle_type receive() noexcept; 134 | 135 | /** 136 | * @brief Alters pipe permissions. 137 | * 138 | * It allows the pipe to be accessed from processes run by different users. 139 | * 140 | * Available flags are: 141 | * 142 | * * `pipe_handle::chmod_flags::READABLE` 143 | * * `pipe_handle::chmod_flags::WRITABLE` 144 | * 145 | * See the official 146 | * [documentation](http://docs.libuv.org/en/v1.x/pipe.html#c.uv_pipe_chmod) 147 | * for further details. 148 | * 149 | * @param flags A valid set of flags. 150 | * @return Underlying return value. 151 | */ 152 | int chmod(chmod_flags flags) noexcept; 153 | 154 | private: 155 | bool ipc; 156 | }; 157 | 158 | } // namespace uvw 159 | 160 | #ifndef UVW_AS_LIB 161 | # include "pipe.cpp" 162 | #endif 163 | 164 | #endif // UVW_PIPE_INCLUDE_H 165 | -------------------------------------------------------------------------------- /3rd/urlparse/include/urlparse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * urlparse 3 | * 4 | * Copyright (c) 2024 urlparse contributors 5 | * Copyright (c) 2023 sfparse contributors 6 | * Copyright (c) 2019 nghttp3 contributors 7 | * Copyright (c) 2015 nghttp2 contributors 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining 10 | * a copy of this software and associated documentation files (the 11 | * "Software"), to deal in the Software without restriction, including 12 | * without limitation the rights to use, copy, modify, merge, publish, 13 | * distribute, sublicense, and/or sell copies of the Software, and to 14 | * permit persons to whom the Software is furnished to do so, subject to 15 | * the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | #ifndef URLPARSE_H 29 | #define URLPARSE_H 30 | 31 | /* Define WIN32 when build target is Win32 API (borrowed from 32 | libcurl) */ 33 | #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) 34 | # define WIN32 35 | #endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif /* defined(__cplusplus) */ 40 | 41 | #if defined(_MSC_VER) && (_MSC_VER < 1800) 42 | /* MSVC < 2013 does not have inttypes.h because it is not C99 43 | compliant. See compiler macros and version number in 44 | https://sourceforge.net/p/predef/wiki/Compilers/ */ 45 | # include 46 | #else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ 47 | # include 48 | #endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ 49 | #include 50 | #include 51 | 52 | /** 53 | * @macro 54 | * 55 | * :macro:`URLPARSE_ERR_PARSE` indicates that an error occurred while 56 | * the parser is processing a URL. 57 | */ 58 | #define URLPARSE_ERR_PARSE -1 59 | 60 | /** 61 | * @enum 62 | * 63 | * :type:`urlparse_url_fields` defines URL component fields. 64 | */ 65 | typedef enum urlparse_url_fields { 66 | /** 67 | * :enum:`URLPARSE_SCHEMA` is a URL scheme. 68 | */ 69 | URLPARSE_SCHEMA = 0, 70 | /** 71 | * :enum:`URLPARSE_HOST` is a host. 72 | */ 73 | URLPARSE_HOST = 1, 74 | /** 75 | * :enum:`URLPARSE_PORT` is a port. 76 | */ 77 | URLPARSE_PORT = 2, 78 | /** 79 | * :enum:`URLPARSE_PATH` is a path. 80 | */ 81 | URLPARSE_PATH = 3, 82 | /** 83 | * :enum:`URLPARSE_QUERY` is a query. 84 | */ 85 | URLPARSE_QUERY = 4, 86 | /** 87 | * :enum:`URLPARSE_FRAGMENT` is a fragment. 88 | */ 89 | URLPARSE_FRAGMENT = 5, 90 | /** 91 | * :enum:`URLPARSE_USERINFO` is a userinfo. 92 | */ 93 | URLPARSE_USERINFO = 6, 94 | /** 95 | * :enum:`URLPARSE_MAX` is the number of fields. 96 | */ 97 | URLPARSE_MAX = 7 98 | } urlparse_url_fields; 99 | 100 | /** 101 | * @struct 102 | * 103 | * :type:`urlparse_url` is a struct to store the result of parsing a 104 | * URL. 105 | */ 106 | typedef struct urlparse_url { 107 | /** 108 | * :member:`field_set` is a bitmask of (1 << :type:`URLPARSE_* 109 | * `) values. 110 | */ 111 | uint16_t field_set; 112 | /** 113 | * :member:`port` is the integer representation of 114 | * :enum:`URLPARSE_PORT ` string. 115 | * It is assigned only when (:member:`field_set` & (1 << 116 | * :enum:`URLPARSE_PORT `)) is 117 | * nonzero. 118 | */ 119 | uint16_t port; 120 | 121 | /** 122 | * @anonstruct_start 123 | * 124 | * @struct_urlparse_field_data 125 | * 126 | * :member:`field_data` stores the position and its length of each 127 | * URL component if the corresponding bit is set in 128 | * :member:`field_set`. For example, 129 | * field_data[:enum:`URLPARSE_HOST 130 | * `] is assigned if 131 | * (:member:`field_set` & (1 << :enum:`URLPARSE_HOST 132 | * `)) is nonzero. 133 | */ 134 | struct { 135 | /** 136 | * :member:`off` is an offset into buffer in which field starts. 137 | */ 138 | uint16_t off; 139 | /** 140 | * :member:`len` is a length of run in buffer. 141 | */ 142 | uint16_t len; 143 | /** 144 | * @anonstruct_end 145 | */ 146 | } field_data[URLPARSE_MAX]; 147 | } urlparse_url; 148 | 149 | /** 150 | * @function 151 | * 152 | * `urlparse_parse_url` parses |url| of length |urllen| bytes, and 153 | * stores the result in |u|. If |is_connect| is nonzero, it parses 154 | * the URL as a request target that appears in CONNECT request, that 155 | * is, consisting of only the host and port number. 156 | * 157 | * This function initializes |u| before its use. If this function 158 | * returns nonzero, |u| might not be initialized. 159 | * 160 | * This function returns 0 if it succeeds, or 161 | * :macro:`URLPARSE_ERR_PARSE`. 162 | */ 163 | int urlparse_parse_url(const char *url, size_t urllen, int is_connect, 164 | urlparse_url *u); 165 | 166 | #ifdef __cplusplus 167 | } 168 | #endif /* defined(__cplusplus) */ 169 | 170 | #endif /* !defined(URLPARSE_H) */ 171 | -------------------------------------------------------------------------------- /flame/query.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 NSONE, Inc 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef DOH_ENABLE 15 | #include "base64.h" 16 | #endif 17 | 18 | #include "config.h" 19 | #include 20 | 21 | enum class GeneratorArgFmt { 22 | POSITIONAL, 23 | KEYVAL, 24 | }; 25 | 26 | class Query { 27 | public: 28 | // XXX ip we send query to, so we can track mismatched ips 29 | // XXX qname we sent to ^ 30 | std::chrono::high_resolution_clock::time_point send_time; 31 | }; 32 | 33 | class QueryGenerator { 34 | public: 35 | using WireTpt = std::pair; 36 | 37 | protected: 38 | unsigned long _loops{0}; 39 | std::string _qclass; 40 | std::string _qname; 41 | std::string _qtype; 42 | bool _dnssec{false}; 43 | std::vector _positional_args; 44 | std::map _kv_args; 45 | GeneratorArgFmt _args_fmt; 46 | 47 | std::shared_ptr _config; 48 | std::vector _wire_buffers; 49 | unsigned long _reqs{0}; 50 | ldns_rr_type cvt_qtype(const std::string &t); 51 | 52 | void new_rec(uint8_t **dest, size_t *dest_len, const char *qname, size_t len, 53 | const std::string &qtype, const std::string &prefix, bool binary, uint16_t id); 54 | void push_rec(const char *qname, size_t len, const std::string &qtype, bool binary); 55 | void push_rec(const std::string &qname, const std::string &qtype, const std::string &prefix, bool binary); 56 | void push_rec(const std::string &qname, const std::string &qtype, bool binary); 57 | 58 | public: 59 | using QueryTpt = std::tuple, std::size_t>; 60 | 61 | QueryGenerator(std::shared_ptr c) 62 | : _config(c) 63 | { 64 | } 65 | virtual ~QueryGenerator(); 66 | 67 | virtual void init() = 0; 68 | 69 | #ifdef DOH_ENABLE 70 | virtual QueryTpt next_base64url(uint16_t); 71 | #endif 72 | virtual QueryTpt next_udp(uint16_t); 73 | virtual QueryTpt next_tcp(const std::vector &); 74 | bool finished(); 75 | 76 | virtual const char *name() = 0; 77 | 78 | virtual bool synthesizedQueries() = 0; 79 | 80 | void set_args(const std::vector &args); 81 | 82 | void set_loops(unsigned long l) 83 | { 84 | _loops = l; 85 | } 86 | 87 | void set_qtype(const std::string &q) 88 | { 89 | _qtype = q; 90 | } 91 | 92 | void set_qclass(const std::string &q) 93 | { 94 | _qclass = q; 95 | } 96 | 97 | void set_qname(const std::string &q) 98 | { 99 | _qname = q; 100 | } 101 | 102 | void set_dnssec(bool d) 103 | { 104 | _dnssec = d; 105 | } 106 | 107 | unsigned long loops() const 108 | { 109 | return _loops; 110 | } 111 | 112 | const std::string &qtype() const 113 | { 114 | return _qtype; 115 | } 116 | 117 | const std::string &qclass() const 118 | { 119 | return _qclass; 120 | } 121 | 122 | const std::string &qname() const 123 | { 124 | return _qname; 125 | } 126 | 127 | bool dnssec() const 128 | { 129 | return _dnssec; 130 | } 131 | 132 | size_t size() 133 | { 134 | return _wire_buffers.size(); 135 | } 136 | 137 | void randomize(); 138 | }; 139 | 140 | class StaticQueryGenerator : public QueryGenerator { 141 | 142 | public: 143 | StaticQueryGenerator(std::shared_ptr c) 144 | : QueryGenerator(c) { }; 145 | 146 | void init(); 147 | 148 | const char *name() 149 | { 150 | return "static"; 151 | } 152 | 153 | bool synthesizedQueries() 154 | { 155 | return false; 156 | } 157 | }; 158 | 159 | class FileQueryGenerator : public QueryGenerator { 160 | 161 | public: 162 | FileQueryGenerator(std::shared_ptr c, 163 | const std::string &fname); 164 | 165 | void init() 166 | { 167 | } 168 | 169 | const char *name() 170 | { 171 | return "file"; 172 | } 173 | bool synthesizedQueries() 174 | { 175 | return false; 176 | } 177 | }; 178 | 179 | class RandomPktQueryGenerator : public QueryGenerator { 180 | 181 | public: 182 | RandomPktQueryGenerator(std::shared_ptr c) 183 | : QueryGenerator(c) { }; 184 | 185 | void init(); 186 | 187 | const char *name() 188 | { 189 | return "randompkt"; 190 | } 191 | bool synthesizedQueries() 192 | { 193 | return false; 194 | } 195 | }; 196 | 197 | class RandomQNameQueryGenerator : public QueryGenerator { 198 | 199 | public: 200 | RandomQNameQueryGenerator(std::shared_ptr c) 201 | : QueryGenerator(c) { }; 202 | 203 | void init(); 204 | 205 | const char *name() 206 | { 207 | return "randomqname"; 208 | } 209 | bool synthesizedQueries() 210 | { 211 | return false; 212 | } 213 | }; 214 | 215 | class RandomLabelQueryGenerator : public QueryGenerator { 216 | 217 | public: 218 | RandomLabelQueryGenerator(std::shared_ptr c) 219 | : QueryGenerator(c) { }; 220 | 221 | void init(); 222 | 223 | const char *name() 224 | { 225 | return "randomlabel"; 226 | } 227 | bool synthesizedQueries() 228 | { 229 | return false; 230 | } 231 | }; 232 | 233 | class NumberNameQueryGenerator : public QueryGenerator { 234 | 235 | std::mt19937_64 _generator; 236 | std::uniform_int_distribution<> _namedist; 237 | 238 | public: 239 | NumberNameQueryGenerator(std::shared_ptr c) 240 | : QueryGenerator(c) 241 | { 242 | } 243 | 244 | void init(); 245 | 246 | // QueryTpt next_base64url(uint16_t); 247 | QueryTpt next_udp(uint16_t); 248 | QueryTpt next_tcp(const std::vector &); 249 | 250 | const char *name() 251 | { 252 | return "numberqname"; 253 | } 254 | bool synthesizedQueries() 255 | { 256 | return true; 257 | } 258 | }; 259 | -------------------------------------------------------------------------------- /man/flame.1.md: -------------------------------------------------------------------------------- 1 | % FLAME(1) 0.9 | Flamethrower 2 | % 3 | % Februrary 6, 2019 4 | 5 | # NAME 6 | 7 | flame -- DNS performance and functional testing utility 8 | 9 | # SYNOPSIS 10 | 11 | flame [*options*] *target* [*generator-options*] 12 | 13 | flame \--help 14 | 15 | flame \--version 16 | 17 | # DESCRIPTION 18 | 19 | Flamethrower is a small, fast, configurable tool for functional testing, benchmarking, 20 | and stress testing DNS servers and networks. It supports IPv4, IPv6, UDP, TCP, and DoT and 21 | has a modular system for generating queries used in the tests. 22 | 23 | Originally built as an alternative to dnsperf (https://github.com/DNS-OARC/dnsperf), 24 | many of the command line options are compatible. 25 | 26 | ## Target 27 | 28 | Target can be either an IP address or host name which will be resolved first. 29 | 30 | ## Options 31 | 32 | -b *BIND_IP* 33 | : IP address to bind to. Default is 0.0.0.0 for inet or ::0 for inet6. 34 | 35 | -q *QCOUNT* 36 | : Number of queries to send every *DELAY_MS* interval. Default is 10. For DoH this specifies the number of concurrent HTTP/2 streams. If this number is larger than the maximum number of concurrent streams supported by the DoH server (100 for most), timeouts will happen. 37 | 38 | -c *TCOUNT* 39 | : Number of concurrent traffic generators per process. Default is 10. 40 | 41 | -p *PORT* 42 | : Which port to flame. Default is 53 for UDP/TCP, 443 for DoH and 853 for DoT. 43 | 44 | -d *DELAY_MS* 45 | : Delay between each traffic generator's run in milliseconds. Default is 1. 46 | 47 | -r *RECORD* 48 | : The base record to use as the query for generators. Default is test.com. 49 | 50 | -T *QTYPE* 51 | : The query type to use for generators. Default is A. 52 | 53 | -o *FILE* 54 | : Metrics output file in JSON format. 55 | 56 | -l *LIMIT_SECS* 57 | : Traffic generation limit in seconds. 0 for unlimited. Default is 0. 58 | 59 | -t *TIMEOUT* 60 | : Query timeout in seconds. Default is 3. 61 | 62 | -F ( inet | inet6 ) 63 | : Internet family. Default is inet. 64 | 65 | -f *FILE* 66 | : Read records from a file, one per row, QNAME and QTYPE. Used with the file generator. 67 | 68 | -n *LOOP* 69 | : Number of loops in the record list, 0 is unlimited. Default is 1. 70 | 71 | -R 72 | : Randomize the query list before sending. Default is false. 73 | 74 | -P ( udp | tcp | dot | doh ) 75 | : Protocol to use. Default is udp. 76 | 77 | -M ( GET | POST ) 78 | : HTTP method to use for DNS over HTTPS. Default is GET. 79 | 80 | -Q *QPS* 81 | : Rate limit to a maximum queries per second, 0 is unlimited. Default is 0. 82 | 83 | -g *GENERATOR* 84 | : Query generator to use. The generators and their options are described in a 85 | separate section. Default is static. 86 | 87 | -v *VERBOSITY* 88 | : Output verbosity, 0 is silent. Default is 1. 89 | 90 | \--dnssec 91 | : Send queries with DNSSEC OK flag set. Default is false. 92 | 93 | \--class *CLASS* 94 | : Send queries with given DNS class. Default is IN. 95 | 96 | \--qps-flow 97 | : Change rate limit over time, format: QPS,MS;QPS,MS;... 98 | 99 | 100 | # Generators 101 | 102 | Flamethrower uses a modular system for generating queries. Each module may 103 | include its own list of configuration options which can be set via 104 | *KEY*=*VALUE* pairs on the command line via *generator-options*. 105 | 106 | ## static 107 | 108 | The generator sends the same query for a QNAME and QTYPE specified via the 109 | *-r* and *-t* options. It doesn't use generator options. 110 | 111 | ## file 112 | 113 | The generator reads dnsperf-compatible input file containing QNAME and QTYPE 114 | pairs on individual lines. The name and type is separated by a space. The input 115 | file is specified via the *-f* option. File generator doesn't use generator 116 | options. 117 | 118 | ## numberqname 119 | 120 | The generator sends queries to one-label subdomain with a number for a record 121 | specified with -*r*. The generator uses following generator options: 122 | 123 | - *low* - Lowest numeric value to write into the label. Default is 0. 124 | - *high* - Highest number value to write into the label. Default is 100000. 125 | 126 | ## randompkt 127 | 128 | The generator sends random chunks of data and uses following generator options: 129 | 130 | - *count* - number of chunks (packets) to generate. Default is 1000. 131 | - *size* - maximal size of the chunk in bytes. Default is 600. 132 | 133 | ## randomqname 134 | 135 | The generator sends queries to random subdomains of the record specified with 136 | *-r*. The subdomains may contain binary (non-printable characters) including 137 | zero byte. The following generator options are available: 138 | 139 | - *count* - number of queries to generate. Default is 100. 140 | - *size* - maximum length of the added label(s). Default is 255. 141 | 142 | ## randomlabel 143 | 144 | The generator sends queries to random subdomains of the record specified with 145 | *-r*. The subdomains may contain only characters valid in a DNS names. The 146 | following generator options are available: 147 | 148 | - *count* - number of queries to generate. Default is 1000. 149 | - *lblsize* - maximum length of a single added label. Default is 10. 150 | - *lblcount* - maximum number of labels to add. Default is 5. 151 | 152 | # EXAMPLES 153 | 154 | Flame localhost over IPv4 on UDP port 53, use default static generator sending 155 | test.com/A queries, no QPS limit, terminate after 10 seconds: 156 | 157 | $ flame -l 10 localhost 158 | 159 | Flame target.example.test over IPv6 on TCP port 5300 with default generator and 160 | no QPS limit: 161 | 162 | $ flame -p 5300 -P tcp -F inet6 target.example.test 163 | 164 | Flame target.example.test over IPv4 on UDP port 53 with 10 q/s limit, send AAAA 165 | type queries for random one-label subdomains of example.test, limit the query 166 | speed to 10 q/s, terminate after 1000 queries: 167 | 168 | $ flame -Q 10 -r example.test -t AAAA -g randomlabel target.example.test lblsize=10 lblcount=1 count=1000 169 | 170 | # AUTHORS 171 | 172 | [NS1](https://ns1.com) 173 | 174 | # BUGS 175 | 176 | [Flamethrower at GitHub](https://github.com/DNS-OARC/flamethrower/issues) 177 | 178 | # COPYRIGHT 179 | 180 | Copyright 2019, NSONE, Inc. 181 | 182 | -------------------------------------------------------------------------------- /flame/tcptlssession.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 NSONE, Inc 2 | // Copyright 2025 Flamethrower Contributors 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "tcptlssession.h" 9 | 10 | static ssize_t gnutls_pull_trampoline(gnutls_transport_ptr_t h, void *buf, size_t len) 11 | { 12 | auto session = static_cast(h); 13 | return session->gnutls_pull(buf, len); 14 | } 15 | 16 | static ssize_t gnutls_push_trampoline(gnutls_transport_ptr_t h, const void *buf, size_t len) 17 | { 18 | auto session = static_cast(h); 19 | return session->gnutls_push(buf, len); 20 | } 21 | 22 | TCPTLSSession::TCPTLSSession(std::shared_ptr handle, 23 | TCPSession::malformed_data_cb malformed_data_handler, 24 | TCPSession::got_dns_msg_cb got_dns_msg_handler, 25 | TCPSession::connection_ready_cb connection_ready_handler, 26 | handshake_error_cb handshake_error_handler) 27 | : TCPSession(handle, malformed_data_handler, got_dns_msg_handler, connection_ready_handler) 28 | , _tls_state{LinkState::HANDSHAKE} 29 | , _handshake_error{handshake_error_handler} 30 | { 31 | } 32 | 33 | TCPTLSSession::~TCPTLSSession() 34 | { 35 | gnutls_certificate_free_credentials(_gnutls_cert_credentials); 36 | gnutls_deinit(_gnutls_session); 37 | } 38 | 39 | bool TCPTLSSession::setup() 40 | { 41 | int ret; 42 | 43 | ret = gnutls_init(&_gnutls_session, GNUTLS_CLIENT | GNUTLS_NONBLOCK); 44 | if (ret != GNUTLS_E_SUCCESS) { 45 | std::cerr << "GNUTLS init failed: " << gnutls_strerror(ret) << std::endl; 46 | return false; 47 | } 48 | 49 | ret = gnutls_set_default_priority(_gnutls_session); 50 | if (ret != GNUTLS_E_SUCCESS) { 51 | std::cerr << "GNUTLS failed to set default priority: " << gnutls_strerror(ret) << std::endl; 52 | return false; 53 | } 54 | 55 | ret = gnutls_certificate_allocate_credentials(&_gnutls_cert_credentials); 56 | if (ret < 0) { 57 | std::cerr << "GNUTLS failed to allocate credentials: " << gnutls_strerror(ret) << std::endl; 58 | return false; 59 | } 60 | 61 | ret = gnutls_certificate_set_x509_system_trust(_gnutls_cert_credentials); 62 | if (ret < 0) { 63 | std::cerr << "GNUTLS failed to set system trust: " << gnutls_strerror(ret) << std::endl; 64 | return false; 65 | } 66 | 67 | ret = gnutls_credentials_set(_gnutls_session, GNUTLS_CRD_CERTIFICATE, 68 | _gnutls_cert_credentials); 69 | if (ret < 0) { 70 | std::cerr << "GNUTLS failed to set system credentials" << gnutls_strerror(ret) << std::endl; 71 | return false; 72 | } 73 | 74 | gnutls_transport_set_ptr(_gnutls_session, this); 75 | gnutls_transport_set_pull_function(_gnutls_session, gnutls_pull_trampoline); 76 | gnutls_transport_set_push_function(_gnutls_session, gnutls_push_trampoline); 77 | gnutls_handshake_set_timeout(_gnutls_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); 78 | return true; 79 | } 80 | 81 | void TCPTLSSession::on_connect_event() 82 | { 83 | do_handshake(); 84 | } 85 | 86 | // gracefully terminate the session 87 | void TCPTLSSession::close() 88 | { 89 | _tls_state = LinkState::CLOSE; 90 | gnutls_bye(_gnutls_session, GNUTLS_SHUT_WR); 91 | TCPSession::close(); 92 | } 93 | 94 | void TCPTLSSession::receive_data(const char data[], size_t len) 95 | { 96 | _pull_buffer.append(data, len); 97 | switch (_tls_state) { 98 | case LinkState::HANDSHAKE: 99 | do_handshake(); 100 | break; 101 | 102 | case LinkState::DATA: 103 | for (;;) { 104 | char buf[16384]; 105 | ssize_t len = gnutls_record_recv(_gnutls_session, buf, sizeof(buf)); 106 | if (len > 0) { 107 | TCPSession::receive_data(buf, len); 108 | } else { 109 | if (len == GNUTLS_E_AGAIN) { 110 | // Check if we don't have any data left to read 111 | if (_pull_buffer.empty()) { 112 | break; 113 | } 114 | continue; 115 | } else if (len == GNUTLS_E_INTERRUPTED) { 116 | continue; 117 | } 118 | break; 119 | } 120 | } 121 | break; 122 | 123 | case LinkState::CLOSE: 124 | break; 125 | } 126 | } 127 | 128 | void TCPTLSSession::write(std::unique_ptr data, size_t len) 129 | { 130 | size_t pos = 0; 131 | do { 132 | // The number of bytes sent might be less than requested. 133 | // The maximum number of bytes this function can send in a single call depends on 134 | // the negotiated maximum record size. 135 | ssize_t sent = gnutls_record_send(_gnutls_session, data.get() + pos, len - pos); 136 | if (sent < 0) { 137 | std::cerr << "Error in sending data: " << gnutls_strerror(sent) << std::endl; 138 | return; 139 | } else { 140 | pos += static_cast(sent); 141 | } 142 | } while (pos < len); 143 | } 144 | 145 | void TCPTLSSession::do_handshake() 146 | { 147 | int err = gnutls_handshake(_gnutls_session); 148 | if (err == GNUTLS_E_SUCCESS) { 149 | _tls_state = LinkState::DATA; 150 | TCPSession::on_connect_event(); 151 | } else if (err < 0 && gnutls_error_is_fatal(err)) { 152 | std::cerr << "Handshake failed: " << gnutls_strerror(err) << std::endl; 153 | _handshake_error(); 154 | } else if (err != GNUTLS_E_AGAIN && err != GNUTLS_E_INTERRUPTED) { 155 | std::cout << "Handshake " << gnutls_strerror(err) << std::endl; 156 | } 157 | } 158 | 159 | int TCPTLSSession::gnutls_pull(void *buf, size_t len) 160 | { 161 | if (!_pull_buffer.empty()) { 162 | len = std::min(len, _pull_buffer.size()); 163 | std::memcpy(buf, _pull_buffer.data(), len); 164 | _pull_buffer.erase(0, len); 165 | return len; 166 | } 167 | 168 | errno = EAGAIN; 169 | return -1; 170 | } 171 | 172 | int TCPTLSSession::gnutls_push(const void *buf, size_t len) 173 | { 174 | auto data = std::make_unique(len); 175 | memcpy(data.get(), const_cast(reinterpret_cast(buf)), len); 176 | TCPSession::write(std::move(data), len); 177 | return len; 178 | } 179 | -------------------------------------------------------------------------------- /man/flame.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.7.3 2 | .\" 3 | .TH "FLAME" "1" "Februrary 6, 2019" "0.9" "Flamethrower" 4 | .hy 5 | .SH NAME 6 | .PP 7 | flame \[en] DNS performance and functional testing utility 8 | .SH SYNOPSIS 9 | .PP 10 | flame [\f[I]options\f[R]] \f[I]target\f[R] [\f[I]generator-options\f[R]] 11 | .PP 12 | flame --help 13 | .PP 14 | flame --version 15 | .SH DESCRIPTION 16 | .PP 17 | Flamethrower is a small, fast, configurable tool for functional testing, 18 | benchmarking, and stress testing DNS servers and networks. 19 | It supports IPv4, IPv6, UDP, TCP, and DoT and has a modular system for 20 | generating queries used in the tests. 21 | .PP 22 | Originally built as an alternative to dnsperf 23 | (https://github.com/DNS-OARC/dnsperf), many of the command line options 24 | are compatible. 25 | .SS Target 26 | .PP 27 | Target can be either an IP address or host name which will be resolved 28 | first. 29 | .SS Options 30 | .TP 31 | .B -b \f[I]BIND_IP\f[R] 32 | IP address to bind to. 33 | Default is 0.0.0.0 for inet or ::0 for inet6. 34 | .TP 35 | .B -q \f[I]QCOUNT\f[R] 36 | Number of queries to send every \f[I]DELAY_MS\f[R] interval. 37 | Default is 10. 38 | For DoH this specifies the number of concurrent HTTP/2 streams. 39 | If this number is larger than the maximum number of concurrent streams 40 | supported by the DoH server (100 for most), timeouts will happen. 41 | .TP 42 | .B -c \f[I]TCOUNT\f[R] 43 | Number of concurrent traffic generators per process. 44 | Default is 10. 45 | .TP 46 | .B -p \f[I]PORT\f[R] 47 | Which port to flame. 48 | Default is 53 for UDP/TCP, 443 for DoH and 853 for DoT. 49 | .TP 50 | .B -d \f[I]DELAY_MS\f[R] 51 | Delay between each traffic generator\[cq]s run in milliseconds. 52 | Default is 1. 53 | .TP 54 | .B -r \f[I]RECORD\f[R] 55 | The base record to use as the query for generators. 56 | Default is test.com. 57 | .TP 58 | .B -T \f[I]QTYPE\f[R] 59 | The query type to use for generators. 60 | Default is A. 61 | .TP 62 | .B -o \f[I]FILE\f[R] 63 | Metrics output file in JSON format. 64 | .TP 65 | .B -l \f[I]LIMIT_SECS\f[R] 66 | Traffic generation limit in seconds. 67 | 0 for unlimited. 68 | Default is 0. 69 | .TP 70 | .B -t \f[I]TIMEOUT\f[R] 71 | Query timeout in seconds. 72 | Default is 3. 73 | .TP 74 | .B -F ( inet | inet6 ) 75 | Internet family. 76 | Default is inet. 77 | .TP 78 | .B -f \f[I]FILE\f[R] 79 | Read records from a file, one per row, QNAME and QTYPE. 80 | Used with the file generator. 81 | .TP 82 | .B -n \f[I]LOOP\f[R] 83 | Number of loops in the record list, 0 is unlimited. 84 | Default is 1. 85 | .TP 86 | .B -R 87 | Randomize the query list before sending. 88 | Default is false. 89 | .TP 90 | .B -P ( udp | tcp | dot | doh ) 91 | Protocol to use. 92 | Default is udp. 93 | .TP 94 | .B -M ( GET | POST ) 95 | HTTP method to use for DNS over HTTPS. 96 | Default is GET. 97 | .TP 98 | .B -Q \f[I]QPS\f[R] 99 | Rate limit to a maximum queries per second, 0 is unlimited. 100 | Default is 0. 101 | .TP 102 | .B -g \f[I]GENERATOR\f[R] 103 | Query generator to use. 104 | The generators and their options are described in a separate section. 105 | Default is static. 106 | .TP 107 | .B -v \f[I]VERBOSITY\f[R] 108 | Output verbosity, 0 is silent. 109 | Default is 1. 110 | .TP 111 | .B --dnssec 112 | Send queries with DNSSEC OK flag set. 113 | Default is false. 114 | .TP 115 | .B --class \f[I]CLASS\f[R] 116 | Send queries with given DNS class. 117 | Default is IN. 118 | .TP 119 | .B --qps-flow 120 | Change rate limit over time, format: QPS,MS;QPS,MS;\&... 121 | .SH Generators 122 | .PP 123 | Flamethrower uses a modular system for generating queries. 124 | Each module may include its own list of configuration options which can 125 | be set via \f[I]KEY\f[R]=\f[I]VALUE\f[R] pairs on the command line via 126 | \f[I]generator-options\f[R]. 127 | .SS static 128 | .PP 129 | The generator sends the same query for a QNAME and QTYPE specified via 130 | the \f[I]-r\f[R] and \f[I]-t\f[R] options. 131 | It doesn\[cq]t use generator options. 132 | .SS file 133 | .PP 134 | The generator reads dnsperf-compatible input file containing QNAME and 135 | QTYPE pairs on individual lines. 136 | The name and type is separated by a space. 137 | The input file is specified via the \f[I]-f\f[R] option. 138 | File generator doesn\[cq]t use generator options. 139 | .SS numberqname 140 | .PP 141 | The generator sends queries to one-label subdomain with a number for a 142 | record specified with -\f[I]r\f[R]. 143 | The generator uses following generator options: 144 | .IP \[bu] 2 145 | \f[I]low\f[R] - Lowest numeric value to write into the label. 146 | Default is 0. 147 | .IP \[bu] 2 148 | \f[I]high\f[R] - Highest number value to write into the label. 149 | Default is 100000. 150 | .SS randompkt 151 | .PP 152 | The generator sends random chunks of data and uses following generator 153 | options: 154 | .IP \[bu] 2 155 | \f[I]count\f[R] - number of chunks (packets) to generate. 156 | Default is 1000. 157 | .IP \[bu] 2 158 | \f[I]size\f[R] - maximal size of the chunk in bytes. 159 | Default is 600. 160 | .SS randomqname 161 | .PP 162 | The generator sends queries to random subdomains of the record specified 163 | with \f[I]-r\f[R]. 164 | The subdomains may contain binary (non-printable characters) including 165 | zero byte. 166 | The following generator options are available: 167 | .IP \[bu] 2 168 | \f[I]count\f[R] - number of queries to generate. 169 | Default is 100. 170 | .IP \[bu] 2 171 | \f[I]size\f[R] - maximum length of the added label(s). 172 | Default is 255. 173 | .SS randomlabel 174 | .PP 175 | The generator sends queries to random subdomains of the record specified 176 | with \f[I]-r\f[R]. 177 | The subdomains may contain only characters valid in a DNS names. 178 | The following generator options are available: 179 | .IP \[bu] 2 180 | \f[I]count\f[R] - number of queries to generate. 181 | Default is 1000. 182 | .IP \[bu] 2 183 | \f[I]lblsize\f[R] - maximum length of a single added label. 184 | Default is 10. 185 | .IP \[bu] 2 186 | \f[I]lblcount\f[R] - maximum number of labels to add. 187 | Default is 5. 188 | .SH EXAMPLES 189 | .PP 190 | Flame localhost over IPv4 on UDP port 53, use default static generator 191 | sending test.com/A queries, no QPS limit, terminate after 10 seconds: 192 | .IP 193 | .nf 194 | \f[C] 195 | $ flame -l 10 localhost 196 | \f[R] 197 | .fi 198 | .PP 199 | Flame target.example.test over IPv6 on TCP port 5300 with default 200 | generator and no QPS limit: 201 | .IP 202 | .nf 203 | \f[C] 204 | $ flame -p 5300 -P tcp -F inet6 target.example.test 205 | \f[R] 206 | .fi 207 | .PP 208 | Flame target.example.test over IPv4 on UDP port 53 with 10 q/s limit, 209 | send AAAA type queries for random one-label subdomains of example.test, 210 | limit the query speed to 10 q/s, terminate after 1000 queries: 211 | .IP 212 | .nf 213 | \f[C] 214 | $ flame -Q 10 -r example.test -t AAAA -g randomlabel target.example.test lblsize=10 lblcount=1 count=1000 215 | \f[R] 216 | .fi 217 | .SH AUTHORS 218 | .PP 219 | NS1 (https://ns1.com) 220 | .SH BUGS 221 | .PP 222 | Flamethrower at GitHub (https://github.com/DNS-OARC/flamethrower/issues) 223 | .SH COPYRIGHT 224 | .PP 225 | Copyright 2019, NSONE, Inc. 226 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/tcp.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_TCP_INCLUDE_H 2 | #define UVW_TCP_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "config.h" 11 | #include "enum.hpp" 12 | #include "request.hpp" 13 | #include "stream.h" 14 | #include "util.h" 15 | 16 | namespace uvw { 17 | 18 | namespace details { 19 | 20 | enum class uvw_tcp_flags : std::underlying_type_t { 21 | IPV6ONLY = UV_TCP_IPV6ONLY, 22 | _UVW_ENUM = 0 23 | }; 24 | 25 | } 26 | 27 | /** 28 | * @brief The TCP handle. 29 | * 30 | * TCP handles are used to represent both TCP streams and servers.
31 | * By default, _ipv4_ is used as a template parameter. The handle already 32 | * supports _IPv6_ out-of-the-box by using `uvw::ipv6`. 33 | * 34 | * To create a `tcp_handle` through a `loop`, arguments follow: 35 | * 36 | * * An optional integer value that indicates the flags used to initialize 37 | * the socket. 38 | * 39 | * See the official 40 | * [documentation](http://docs.libuv.org/en/v1.x/tcp.html#c.uv_tcp_init_ex) 41 | * for further details. 42 | */ 43 | class tcp_handle final: public stream_handle { 44 | public: 45 | using time = std::chrono::duration; 46 | using tcp_flags = details::uvw_tcp_flags; 47 | using ipv4 = uvw::ipv4; 48 | using ipv6 = uvw::ipv6; 49 | 50 | explicit tcp_handle(loop::token token, std::shared_ptr ref, unsigned int f = {}); 51 | 52 | /** 53 | * @brief Initializes the handle. No socket is created as of yet. 54 | * @return Underlying return value. 55 | */ 56 | int init(); 57 | 58 | /** 59 | * @brief Opens an existing file descriptor or SOCKET as a TCP handle. 60 | * 61 | * The passed file descriptor or SOCKET is not checked for its type, but 62 | * it’s required that it represents a valid stream socket. 63 | * 64 | * @param socket A valid socket handle (either a file descriptor or a 65 | * SOCKET). 66 | * 67 | * @return Underlying return value. 68 | */ 69 | int open(os_socket_handle socket); 70 | 71 | /** 72 | * @brief Enables/Disables Nagle’s algorithm. 73 | * @param value True to enable it, false otherwise. 74 | * @return True in case of success, false otherwise. 75 | */ 76 | bool no_delay(bool value = false); 77 | 78 | /** 79 | * @brief Enables/Disables TCP keep-alive. 80 | * @param enable True to enable it, false otherwise. 81 | * @param val Initial delay in seconds (use 82 | * `std::chrono::duration`). 83 | * @return True in case of success, false otherwise. 84 | */ 85 | bool keep_alive(bool enable = false, time val = time{0}); 86 | 87 | /** 88 | * @brief Enables/Disables simultaneous asynchronous accept requests. 89 | * 90 | * Enables/Disables simultaneous asynchronous accept requests that are 91 | * queued by the operating system when listening for new TCP 92 | * connections.
93 | * This setting is used to tune a TCP server for the desired performance. 94 | * Having simultaneous accepts can significantly improve the rate of 95 | * accepting connections (which is why it is enabled by default) but may 96 | * lead to uneven load distribution in multi-process setups. 97 | * 98 | * @param enable True to enable it, false otherwise. 99 | * @return True in case of success, false otherwise. 100 | */ 101 | bool simultaneous_accepts(bool enable = true); 102 | 103 | /** 104 | * @brief Binds the handle to an address and port. 105 | * 106 | * A successful call to this function does not guarantee that the call to 107 | * `listen()` or `connect()` will work properly. 108 | * 109 | * Available flags are: 110 | * 111 | * * `tcp_handle::tcp_flags::IPV6ONLY`: it disables dual-stack support and 112 | * only IPv6 is used. 113 | * 114 | * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. 115 | * @param opts Optional additional flags. 116 | * @return Underlying return value. 117 | */ 118 | int bind(const sockaddr &addr, tcp_flags opts = tcp_flags::_UVW_ENUM); 119 | 120 | /** 121 | * @brief Binds the handle to an address and port. 122 | * 123 | * A successful call to this function does not guarantee that the call to 124 | * `listen()` or `connect()` will work properly. 125 | * 126 | * Available flags are: 127 | * 128 | * * `tcp_handle::tcp_flags::IPV6ONLY`: it disables dual-stack support and 129 | * only IPv6 is used. 130 | * 131 | * @param ip The address to which to bind. 132 | * @param port The port to which to bind. 133 | * @param opts Optional additional flags. 134 | * @return Underlying return value. 135 | */ 136 | int bind(const std::string &ip, unsigned int port, tcp_flags opts = tcp_flags::_UVW_ENUM); 137 | 138 | /** 139 | * @brief Binds the handle to an address and port. 140 | * 141 | * A successful call to this function does not guarantee that the call to 142 | * `listen()` or `connect()` will work properly. 143 | * 144 | * Available flags are: 145 | * 146 | * * `tcp_handle::tcp_flags::IPV6ONLY`: it disables dual-stack support and 147 | * only IPv6 is used. 148 | * 149 | * @param addr A valid instance of socket_address. 150 | * @param opts Optional additional flags. 151 | * @return Underlying return value. 152 | */ 153 | int bind(socket_address addr, tcp_flags opts = tcp_flags::_UVW_ENUM); 154 | 155 | /** 156 | * @brief Gets the current address to which the handle is bound. 157 | * @return A valid instance of socket_address, an empty one in case of 158 | * errors. 159 | */ 160 | socket_address sock() const noexcept; 161 | 162 | /** 163 | * @brief Gets the address of the peer connected to the handle. 164 | * @return A valid instance of socket_address, an empty one in case of 165 | * errors. 166 | */ 167 | socket_address peer() const noexcept; 168 | 169 | /** 170 | * @brief Establishes an IPv4 or IPv6 TCP connection. 171 | * 172 | * On Windows if the addr is initialized to point to an unspecified address 173 | * (`0.0.0.0` or `::`) it will be changed to point to localhost. This is 174 | * done to match the behavior of Linux systems. 175 | * 176 | * A connect event is emitted when the connection has been established. 177 | * 178 | * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. 179 | * @return Underlying return value. 180 | */ 181 | int connect(const sockaddr &addr); 182 | 183 | /** 184 | * @brief Establishes an IPv4 or IPv6 TCP connection. 185 | * 186 | * A connect event is emitted when the connection has been established. 187 | * 188 | * @param ip The address to which to bind. 189 | * @param port The port to which to bind. 190 | * @return Underlying return value. 191 | */ 192 | int connect(const std::string &ip, unsigned int port); 193 | 194 | /** 195 | * @brief Establishes an IPv4 or IPv6 TCP connection. 196 | * 197 | * A connect event is emitted when the connection has been established. 198 | * 199 | * @param addr A valid instance of socket_address. 200 | * @return Underlying return value. 201 | */ 202 | int connect(socket_address addr); 203 | 204 | /** 205 | * @brief Resets a TCP connection by sending a RST packet. 206 | * 207 | * This is accomplished by setting the `SO_LINGER` socket option with a 208 | * linger interval of zero and then calling `close`.
209 | * Due to some platform inconsistencies, mixing of `shutdown` and 210 | * `close_reset` calls is not allowed. 211 | * 212 | * A close event is emitted when the connection has been reset. 213 | * 214 | * @return Underlying return value. 215 | */ 216 | int close_reset(); 217 | 218 | private: 219 | enum { 220 | DEFAULT, 221 | FLAGS 222 | } tag; 223 | 224 | unsigned int flags; 225 | }; 226 | 227 | } // namespace uvw 228 | 229 | #ifndef UVW_AS_LIB 230 | # include "tcp.cpp" 231 | #endif 232 | 233 | #endif // UVW_TCP_INCLUDE_H 234 | -------------------------------------------------------------------------------- /3rd/uvw/src/udp.cpp: -------------------------------------------------------------------------------- 1 | #ifdef UVW_AS_LIB 2 | # include "udp.h" 3 | #endif 4 | 5 | #include "config.h" 6 | 7 | namespace uvw { 8 | 9 | UVW_INLINE udp_data_event::udp_data_event(socket_address sndr, std::unique_ptr buf, std::size_t len, bool part) noexcept 10 | : data{std::move(buf)}, 11 | length{len}, 12 | sender{std::move(sndr)}, 13 | partial{part} {} 14 | 15 | UVW_INLINE void details::send_req::udp_send_callback(uv_udp_send_t *req, int status) { 16 | if(auto ptr = reserve(req); status) { 17 | ptr->publish(error_event{status}); 18 | } else { 19 | ptr->publish(send_event{}); 20 | } 21 | } 22 | 23 | UVW_INLINE details::send_req::send_req(loop::token token, std::shared_ptr parent, std::unique_ptr dt, unsigned int len) 24 | : request{token, std::move(parent)}, 25 | data{std::move(dt)}, 26 | buf{uv_buf_init(data.get(), len)} {} 27 | 28 | UVW_INLINE int details::send_req::send(uv_udp_t *hndl, const struct sockaddr *addr) { 29 | return this->leak_if(uv_udp_send(raw(), hndl, &buf, 1, addr, &udp_send_callback)); 30 | } 31 | 32 | UVW_INLINE void udp_handle::recv_callback(uv_udp_t *hndl, ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, unsigned flags) { 33 | udp_handle &udp = *(static_cast(hndl->data)); 34 | // data will be destroyed no matter of what the value of nread is 35 | std::unique_ptr data{buf->base}; 36 | 37 | if(nread > 0) { 38 | // data available (can be truncated) 39 | udp.publish(udp_data_event{details::sock_addr(*addr), std::move(data), static_cast(nread), !(0 == (flags & UV_UDP_PARTIAL))}); 40 | } else if(nread == 0 && addr == nullptr) { 41 | // no more data to be read, doing nothing is fine 42 | } else if(nread == 0 && addr != nullptr) { 43 | // empty udp packet 44 | udp.publish(udp_data_event{details::sock_addr(*addr), std::move(data), static_cast(nread), false}); 45 | } else { 46 | // transmission error 47 | udp.publish(error_event(nread)); 48 | } 49 | } 50 | 51 | UVW_INLINE udp_handle::udp_handle(loop::token token, std::shared_ptr ref, unsigned int f) 52 | : handle{token, std::move(ref)}, tag{FLAGS}, flags{f} {} 53 | 54 | UVW_INLINE int udp_handle::init() { 55 | if(tag == FLAGS) { 56 | return leak_if(uv_udp_init_ex(parent().raw(), raw(), flags)); 57 | } else { 58 | return leak_if(uv_udp_init(parent().raw(), raw())); 59 | } 60 | } 61 | 62 | UVW_INLINE int udp_handle::open(os_socket_handle socket) { 63 | return uv_udp_open(raw(), socket); 64 | } 65 | 66 | UVW_INLINE int udp_handle::connect(const sockaddr &addr) { 67 | return uv_udp_connect(raw(), &addr); 68 | } 69 | 70 | UVW_INLINE int udp_handle::connect(const std::string &ip, unsigned int port) { 71 | return connect(details::ip_addr(ip.data(), port)); 72 | } 73 | 74 | UVW_INLINE int udp_handle::connect(socket_address addr) { 75 | return connect(addr.ip, addr.port); 76 | } 77 | 78 | UVW_INLINE int udp_handle::disconnect() { 79 | return uv_udp_connect(raw(), nullptr); 80 | } 81 | 82 | UVW_INLINE socket_address udp_handle::peer() const noexcept { 83 | sockaddr_storage storage; 84 | int len = sizeof(sockaddr_storage); 85 | uv_udp_getpeername(raw(), reinterpret_cast(&storage), &len); 86 | return details::sock_addr(storage); 87 | } 88 | 89 | UVW_INLINE int udp_handle::bind(const sockaddr &addr, udp_handle::udp_flags opts) { 90 | return uv_udp_bind(raw(), &addr, static_cast(opts)); 91 | } 92 | 93 | UVW_INLINE int udp_handle::bind(const std::string &ip, unsigned int port, udp_flags opts) { 94 | return bind(details::ip_addr(ip.data(), port), opts); 95 | } 96 | 97 | UVW_INLINE int udp_handle::bind(socket_address addr, udp_flags opts) { 98 | return bind(addr.ip, addr.port, opts); 99 | } 100 | 101 | UVW_INLINE socket_address udp_handle::sock() const noexcept { 102 | sockaddr_storage storage; 103 | int len = sizeof(sockaddr_storage); 104 | uv_udp_getsockname(raw(), reinterpret_cast(&storage), &len); 105 | return details::sock_addr(storage); 106 | } 107 | 108 | UVW_INLINE bool udp_handle::multicast_membership(const std::string &multicast, const std::string &iface, membership ms) { 109 | return (0 == uv_udp_set_membership(raw(), multicast.data(), iface.data(), static_cast(ms))); 110 | } 111 | 112 | UVW_INLINE bool udp_handle::multicast_loop(bool enable) { 113 | return (0 == uv_udp_set_multicast_loop(raw(), enable)); 114 | } 115 | 116 | UVW_INLINE bool udp_handle::multicast_ttl(int val) { 117 | return (0 == uv_udp_set_multicast_ttl(raw(), val > 255 ? 255 : val)); 118 | } 119 | 120 | UVW_INLINE bool udp_handle::multicast_interface(const std::string &iface) { 121 | return (0 == uv_udp_set_multicast_interface(raw(), iface.data())); 122 | } 123 | 124 | UVW_INLINE bool udp_handle::broadcast(bool enable) { 125 | return (0 == uv_udp_set_broadcast(raw(), enable)); 126 | } 127 | 128 | UVW_INLINE bool udp_handle::ttl(int val) { 129 | return (0 == uv_udp_set_ttl(raw(), val > 255 ? 255 : val)); 130 | } 131 | 132 | UVW_INLINE int udp_handle::send(const sockaddr &addr, std::unique_ptr data, unsigned int len) { 133 | auto req = parent().resource(std::unique_ptr{data.release(), [](char *ptr) { delete[] ptr; }}, len); 134 | 135 | auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { 136 | ptr->publish(event); 137 | }; 138 | 139 | req->on(listener); 140 | req->on(listener); 141 | 142 | return req->send(raw(), &addr); 143 | } 144 | 145 | UVW_INLINE int udp_handle::send(const std::string &ip, unsigned int port, std::unique_ptr data, unsigned int len) { 146 | return send(details::ip_addr(ip.data(), port), std::move(data), len); 147 | } 148 | 149 | UVW_INLINE int udp_handle::send(socket_address addr, std::unique_ptr data, unsigned int len) { 150 | return send(addr.ip, addr.port, std::move(data), len); 151 | } 152 | 153 | UVW_INLINE int udp_handle::send(const sockaddr &addr, char *data, unsigned int len) { 154 | auto req = parent().resource(std::unique_ptr{data, [](char *) {}}, len); 155 | 156 | auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { 157 | ptr->publish(event); 158 | }; 159 | 160 | req->on(listener); 161 | req->on(listener); 162 | 163 | return req->send(raw(), &addr); 164 | } 165 | 166 | UVW_INLINE int udp_handle::send(const std::string &ip, unsigned int port, char *data, unsigned int len) { 167 | return send(details::ip_addr(ip.data(), port), data, len); 168 | } 169 | 170 | UVW_INLINE int udp_handle::send(socket_address addr, char *data, unsigned int len) { 171 | return send(addr.ip, addr.port, data, len); 172 | } 173 | 174 | UVW_INLINE int udp_handle::try_send(const sockaddr &addr, std::unique_ptr data, unsigned int len) { 175 | uv_buf_t bufs[] = {uv_buf_init(data.get(), len)}; 176 | return uv_udp_try_send(raw(), bufs, 1, &addr); 177 | } 178 | 179 | UVW_INLINE int udp_handle::try_send(const std::string &ip, unsigned int port, std::unique_ptr data, unsigned int len) { 180 | return try_send(details::ip_addr(ip.data(), port), std::move(data), len); 181 | } 182 | 183 | UVW_INLINE int udp_handle::try_send(socket_address addr, std::unique_ptr data, unsigned int len) { 184 | return try_send(addr.ip, addr.port, std::move(data), len); 185 | } 186 | 187 | UVW_INLINE int udp_handle::try_send(const sockaddr &addr, char *data, unsigned int len) { 188 | uv_buf_t bufs[] = {uv_buf_init(data, len)}; 189 | return uv_udp_try_send(raw(), bufs, 1, &addr); 190 | } 191 | 192 | UVW_INLINE int udp_handle::try_send(const std::string &ip, unsigned int port, char *data, unsigned int len) { 193 | return try_send(details::ip_addr(ip.data(), port), data, len); 194 | } 195 | 196 | UVW_INLINE int udp_handle::try_send(socket_address addr, char *data, unsigned int len) { 197 | return try_send(addr.ip, addr.port, data, len); 198 | } 199 | 200 | UVW_INLINE int udp_handle::recv() { 201 | return uv_udp_recv_start(raw(), &details::common_alloc_callback, &recv_callback); 202 | } 203 | 204 | UVW_INLINE int udp_handle::stop() { 205 | return uv_udp_recv_stop(raw()); 206 | } 207 | 208 | UVW_INLINE size_t udp_handle::send_queue_size() const noexcept { 209 | return uv_udp_get_send_queue_size(raw()); 210 | } 211 | 212 | UVW_INLINE size_t udp_handle::send_queue_count() const noexcept { 213 | return uv_udp_get_send_queue_count(raw()); 214 | } 215 | 216 | } // namespace uvw 217 | -------------------------------------------------------------------------------- /3rd/docopt.cpp/include/docopt_value.h: -------------------------------------------------------------------------------- 1 | // 2 | // value.h 3 | // docopt 4 | // 5 | // Created by Jared Grubb on 2013-10-14. 6 | // Copyright (c) 2013 Jared Grubb. All rights reserved. 7 | // 8 | 9 | #ifndef docopt__value_h_ 10 | #define docopt__value_h_ 11 | 12 | #include 13 | #include 14 | #include // std::hash 15 | #include 16 | #include 17 | 18 | namespace docopt { 19 | 20 | enum class Kind { 21 | Empty, 22 | Bool, 23 | Long, 24 | String, 25 | StringList 26 | }; 27 | 28 | /// A generic type to hold the various types that can be produced by docopt. 29 | /// 30 | /// This type can be one of: {bool, long, string, vector}, or empty. 31 | struct value { 32 | /// An empty value 33 | value() {} 34 | 35 | value(std::string); 36 | value(std::vector); 37 | 38 | explicit value(bool); 39 | explicit value(long); 40 | explicit value(int v) : value(static_cast(v)) {} 41 | 42 | ~value(); 43 | value(value const&); 44 | value(value&&) noexcept; 45 | value& operator=(value const&); 46 | value& operator=(value&&) noexcept; 47 | 48 | Kind kind() const { return kind_; } 49 | 50 | // Test if this object has any contents at all 51 | explicit operator bool() const { return kind_ != Kind::Empty; } 52 | 53 | // Test the type contained by this value object 54 | bool isBool() const { return kind_==Kind::Bool; } 55 | bool isString() const { return kind_==Kind::String; } 56 | bool isLong() const { return kind_==Kind::Long; } 57 | bool isStringList() const { return kind_==Kind::StringList; } 58 | 59 | // Throws std::invalid_argument if the type does not match 60 | bool asBool() const; 61 | long asLong() const; 62 | std::string const& asString() const; 63 | std::vector const& asStringList() const; 64 | 65 | size_t hash() const noexcept; 66 | 67 | friend bool operator==(value const&, value const&); 68 | friend bool operator!=(value const&, value const&); 69 | 70 | private: 71 | union Variant { 72 | Variant() {} 73 | ~Variant() { /* do nothing; will be destroyed by ~value */ } 74 | 75 | bool boolValue; 76 | long longValue; 77 | std::string strValue; 78 | std::vector strList; 79 | }; 80 | 81 | static const char* kindAsString(Kind kind) { 82 | switch (kind) { 83 | case Kind::Empty: return "empty"; 84 | case Kind::Bool: return "bool"; 85 | case Kind::Long: return "long"; 86 | case Kind::String: return "string"; 87 | case Kind::StringList: return "string-list"; 88 | } 89 | return "unknown"; 90 | } 91 | 92 | void throwIfNotKind(Kind expected) const { 93 | if (kind_ == expected) 94 | return; 95 | 96 | std::string error = "Illegal cast to "; 97 | error += kindAsString(expected); 98 | error += "; type is actually "; 99 | error += kindAsString(kind_); 100 | throw std::runtime_error(std::move(error)); 101 | } 102 | 103 | Kind kind_ = Kind::Empty; 104 | Variant variant_ {}; 105 | }; 106 | 107 | /// Write out the contents to the ostream 108 | DOCOPT_API std::ostream& operator<<(std::ostream&, value const&); 109 | } 110 | 111 | namespace std { 112 | template <> 113 | struct hash { 114 | size_t operator()(docopt::value const& val) const noexcept { 115 | return val.hash(); 116 | } 117 | }; 118 | } 119 | 120 | namespace docopt { 121 | inline 122 | value::value(bool v) 123 | : kind_(Kind::Bool) 124 | { 125 | variant_.boolValue = v; 126 | } 127 | 128 | inline 129 | value::value(long v) 130 | : kind_(Kind::Long) 131 | { 132 | variant_.longValue = v; 133 | } 134 | 135 | inline 136 | value::value(std::string v) 137 | : kind_(Kind::String) 138 | { 139 | new (&variant_.strValue) std::string(std::move(v)); 140 | } 141 | 142 | inline 143 | value::value(std::vector v) 144 | : kind_(Kind::StringList) 145 | { 146 | new (&variant_.strList) std::vector(std::move(v)); 147 | } 148 | 149 | inline 150 | value::value(value const& other) 151 | : kind_(other.kind_) 152 | { 153 | switch (kind_) { 154 | case Kind::String: 155 | new (&variant_.strValue) std::string(other.variant_.strValue); 156 | break; 157 | 158 | case Kind::StringList: 159 | new (&variant_.strList) std::vector(other.variant_.strList); 160 | break; 161 | 162 | case Kind::Bool: 163 | variant_.boolValue = other.variant_.boolValue; 164 | break; 165 | 166 | case Kind::Long: 167 | variant_.longValue = other.variant_.longValue; 168 | break; 169 | 170 | case Kind::Empty: 171 | default: 172 | break; 173 | } 174 | } 175 | 176 | inline 177 | value::value(value&& other) noexcept 178 | : kind_(other.kind_) 179 | { 180 | switch (kind_) { 181 | case Kind::String: 182 | new (&variant_.strValue) std::string(std::move(other.variant_.strValue)); 183 | break; 184 | 185 | case Kind::StringList: 186 | new (&variant_.strList) std::vector(std::move(other.variant_.strList)); 187 | break; 188 | 189 | case Kind::Bool: 190 | variant_.boolValue = other.variant_.boolValue; 191 | break; 192 | 193 | case Kind::Long: 194 | variant_.longValue = other.variant_.longValue; 195 | break; 196 | 197 | case Kind::Empty: 198 | default: 199 | break; 200 | } 201 | } 202 | 203 | inline 204 | value::~value() 205 | { 206 | switch (kind_) { 207 | case Kind::String: 208 | variant_.strValue.~basic_string(); 209 | break; 210 | 211 | case Kind::StringList: 212 | variant_.strList.~vector(); 213 | break; 214 | 215 | case Kind::Empty: 216 | case Kind::Bool: 217 | case Kind::Long: 218 | default: 219 | // trivial dtor 220 | break; 221 | } 222 | } 223 | 224 | inline 225 | value& value::operator=(value const& other) { 226 | // make a copy and move from it; way easier. 227 | return *this = value{other}; 228 | } 229 | 230 | inline 231 | value& value::operator=(value&& other) noexcept { 232 | // move of all the types involved is noexcept, so we dont have to worry about 233 | // these two statements throwing, which gives us a consistency guarantee. 234 | this->~value(); 235 | new (this) value(std::move(other)); 236 | 237 | return *this; 238 | } 239 | 240 | template 241 | void hash_combine(std::size_t& seed, const T& v); 242 | 243 | inline 244 | size_t value::hash() const noexcept 245 | { 246 | switch (kind_) { 247 | case Kind::String: 248 | return std::hash()(variant_.strValue); 249 | 250 | case Kind::StringList: { 251 | size_t seed = std::hash()(variant_.strList.size()); 252 | for(auto const& str : variant_.strList) { 253 | hash_combine(seed, str); 254 | } 255 | return seed; 256 | } 257 | 258 | case Kind::Bool: 259 | return std::hash()(variant_.boolValue); 260 | 261 | case Kind::Long: 262 | return std::hash()(variant_.longValue); 263 | 264 | case Kind::Empty: 265 | default: 266 | return std::hash()(nullptr); 267 | } 268 | } 269 | 270 | inline 271 | bool value::asBool() const 272 | { 273 | throwIfNotKind(Kind::Bool); 274 | return variant_.boolValue; 275 | } 276 | 277 | inline 278 | long value::asLong() const 279 | { 280 | // Attempt to convert a string to a long 281 | if (kind_ == Kind::String) { 282 | const std::string& str = variant_.strValue; 283 | std::size_t pos; 284 | const long ret = stol(str, &pos); // Throws if it can't convert 285 | if (pos != str.length()) { 286 | // The string ended in non-digits. 287 | throw std::runtime_error( str + " contains non-numeric characters."); 288 | } 289 | return ret; 290 | } 291 | throwIfNotKind(Kind::Long); 292 | return variant_.longValue; 293 | } 294 | 295 | inline 296 | std::string const& value::asString() const 297 | { 298 | throwIfNotKind(Kind::String); 299 | return variant_.strValue; 300 | } 301 | 302 | inline 303 | std::vector const& value::asStringList() const 304 | { 305 | throwIfNotKind(Kind::StringList); 306 | return variant_.strList; 307 | } 308 | 309 | inline 310 | bool operator==(value const& v1, value const& v2) 311 | { 312 | if (v1.kind_ != v2.kind_) 313 | return false; 314 | 315 | switch (v1.kind_) { 316 | case Kind::String: 317 | return v1.variant_.strValue==v2.variant_.strValue; 318 | 319 | case Kind::StringList: 320 | return v1.variant_.strList==v2.variant_.strList; 321 | 322 | case Kind::Bool: 323 | return v1.variant_.boolValue==v2.variant_.boolValue; 324 | 325 | case Kind::Long: 326 | return v1.variant_.longValue==v2.variant_.longValue; 327 | 328 | case Kind::Empty: 329 | default: 330 | return true; 331 | } 332 | } 333 | 334 | inline 335 | bool operator!=(value const& v1, value const& v2) 336 | { 337 | return !(v1 == v2); 338 | } 339 | } 340 | 341 | #endif /* defined(docopt__value_h_) */ 342 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/handle.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UVW_HANDLE_INCLUDE_H 2 | #define UVW_HANDLE_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "config.h" 9 | #include "resource.hpp" 10 | #include "util.h" 11 | 12 | namespace uvw { 13 | 14 | /*! @brief Close event. */ 15 | struct close_event {}; 16 | 17 | /** 18 | * @brief Handle base class. 19 | * 20 | * Base type for all `uvw` handle types. 21 | */ 22 | template 23 | class handle: public resource { 24 | protected: 25 | static void close_callback(uv_handle_t *hndl) { 26 | handle &ref = *(static_cast(hndl->data)); 27 | [[maybe_unused]] auto ptr = ref.shared_from_this(); 28 | ref.self_reset(); 29 | ref.publish(close_event{}); 30 | } 31 | 32 | uv_handle_t *as_uv_handle() { 33 | return reinterpret_cast(this->raw()); 34 | } 35 | 36 | const uv_handle_t *as_uv_handle() const { 37 | return reinterpret_cast(this->raw()); 38 | } 39 | 40 | public: 41 | using resource::resource; 42 | 43 | /** 44 | * @brief Gets the category of the handle. 45 | * 46 | * A base handle offers no functionality to promote it to the actual handle 47 | * type. By means of this function, an opaque value that identifies the 48 | * category of the handle is made available to the users. 49 | * 50 | * @return The actual category of the handle. 51 | */ 52 | handle_category category() const noexcept { 53 | return handle_category{as_uv_handle()->type}; 54 | } 55 | 56 | /** 57 | * @brief Gets the type of the handle. 58 | * 59 | * A base handle offers no functionality to promote it to the actual handle 60 | * type. By means of this function, the type of the underlying handle as 61 | * specified by handle_type is made available to the users. 62 | * 63 | * @return The actual type of the handle. 64 | */ 65 | handle_type type() const noexcept { 66 | return utilities::guess_handle(category()); 67 | } 68 | 69 | /** 70 | * @brief Checks if the handle is active. 71 | * 72 | * What _active_ means depends on the type of handle: 73 | * 74 | * * An async_handle handle is always active and cannot be deactivated, 75 | * except by closing it with uv_close(). 76 | * * A pipe, tcp, udp, etc. handle - basically any handle that deals with 77 | * I/O - is active when it is doing something that involves I/O, like 78 | * reading, writing, connecting, accepting new connections, etc. 79 | * * A check, idle, timer, etc. handle is active when it has been started 80 | * with a call to `start()`. 81 | * 82 | * Rule of thumb: if a handle of type `foo_handle` has a `start()` member 83 | * method, then it’s active from the moment that method is called. Likewise, 84 | * `stop()` deactivates the handle again. 85 | * 86 | * @return True if the handle is active, false otherwise. 87 | */ 88 | bool active() const noexcept { 89 | return !!uv_is_active(as_uv_handle()); 90 | } 91 | 92 | /** 93 | * @brief Checks if a handle is closing or closed. 94 | * 95 | * This function should only be used between the initialization of the 96 | * handle and the arrival of the close callback. 97 | * 98 | * @return True if the handle is closing or closed, false otherwise. 99 | */ 100 | bool closing() const noexcept { 101 | return !!uv_is_closing(as_uv_handle()); 102 | } 103 | 104 | /** 105 | * @brief Request handle to be closed. 106 | * 107 | * This **must** be called on each handle before memory is released.
108 | * In-progress requests are cancelled and this can result in errors. 109 | * 110 | * The handle will emit a close event when finished. 111 | */ 112 | void close() noexcept { 113 | if(!closing()) { 114 | uv_close(as_uv_handle(), &handle::close_callback); 115 | } 116 | } 117 | 118 | /** 119 | * @brief Reference the given handle. 120 | * 121 | * References are idempotent, that is, if a handle is already referenced 122 | * calling this function again will have no effect. 123 | */ 124 | void reference() noexcept { 125 | uv_ref(as_uv_handle()); 126 | } 127 | 128 | /** 129 | * @brief Unreference the given handle. 130 | * 131 | * References are idempotent, that is, if a handle is not referenced calling 132 | * this function again will have no effect. 133 | */ 134 | void unreference() noexcept { 135 | uv_unref(as_uv_handle()); 136 | } 137 | 138 | /** 139 | * @brief Checks if the given handle referenced. 140 | * @return True if the handle referenced, false otherwise. 141 | */ 142 | bool referenced() const noexcept { 143 | return !!uv_has_ref(as_uv_handle()); 144 | } 145 | 146 | /** 147 | * @brief Returns the size of the underlying handle type. 148 | * @return The size of the underlying handle type. 149 | */ 150 | std::size_t size() const noexcept { 151 | return uv_handle_size(as_uv_handle()->type); 152 | } 153 | 154 | /** 155 | * @brief Gets the size of the send buffer used for the socket. 156 | * 157 | * Gets the size of the send buffer that the operating system uses for the 158 | * socket.
159 | * This function works for tcp, pipeand udp handles on Unix and for tcp and 160 | * udp handles on Windows.
161 | * Note that Linux will return double the size of the original set value. 162 | * 163 | * @return The size of the send buffer, the underlying return value in case 164 | * of errors. 165 | */ 166 | int send_buffer_size() { 167 | int value = 0; 168 | auto err = uv_send_buffer_size(as_uv_handle(), &value); 169 | return err ? err : value; 170 | } 171 | 172 | /** 173 | * @brief Sets the size of the send buffer used for the socket. 174 | * 175 | * Sets the size of the send buffer that the operating system uses for the 176 | * socket.
177 | * This function works for tcp, pipe and udp handles on Unix and for tcp and 178 | * udp handles on Windows.
179 | * Note that Linux will set double the size. 180 | * 181 | * @return Underlying return value. 182 | */ 183 | int send_buffer_size(int value) { 184 | return uv_send_buffer_size(as_uv_handle(), &value); 185 | } 186 | 187 | /** 188 | * @brief Gets the size of the receive buffer used for the socket. 189 | * 190 | * Gets the size of the receive buffer that the operating system uses for 191 | * the socket.
192 | * This function works for tcp, pipe and udp handles on Unix and for tcp and 193 | * udp handles on Windows.
194 | * Note that Linux will return double the size of the original set value. 195 | * 196 | * @return The size of the receive buffer, the underlying return value in 197 | * case of errors. 198 | */ 199 | int recv_buffer_size() { 200 | int value = 0; 201 | auto err = uv_recv_buffer_size(as_uv_handle(), &value); 202 | return err ? err : value; 203 | } 204 | 205 | /** 206 | * @brief Sets the size of the receive buffer used for the socket. 207 | * 208 | * Sets the size of the receive buffer that the operating system uses for 209 | * the socket.
210 | * This function works for tcp, pipe and udp handles on Unix and for tcp and 211 | * udp handles on Windows.
212 | * Note that Linux will set double the size. 213 | * 214 | * @return Underlying return value. 215 | */ 216 | int recv_buffer_size(int value) { 217 | return uv_recv_buffer_size(as_uv_handle(), &value); 218 | } 219 | 220 | /** 221 | * @brief Gets the platform dependent file descriptor equivalent. 222 | * 223 | * Supported handles: 224 | * 225 | * * tcp_handle 226 | * * pipe_handle 227 | * * tty_handle 228 | * * udp_handle 229 | * * poll_handle 230 | * 231 | * If invoked on a different handle, one that doesn’t have an attached file 232 | * descriptor yet or one which was closed, an invalid value is returned. 233 | * 234 | * See the official 235 | * [documentation](http://docs.libuv.org/en/v1.x/handle.html#c.uv_fileno) 236 | * for further details. 237 | * 238 | * @return The file descriptor attached to the hande or a negative value in 239 | * case of errors. 240 | */ 241 | os_file_descriptor fd() const { 242 | uv_os_fd_t fd; 243 | uv_fileno(as_uv_handle(), &fd); 244 | return fd; 245 | } 246 | }; 247 | 248 | } // namespace uvw 249 | 250 | #endif // UVW_HANDLE_INCLUDE_H 251 | -------------------------------------------------------------------------------- /3rd/uvw/include/uvw/process.h: -------------------------------------------------------------------------------- 1 | #ifndef UVW_PROCESS_INCLUDE_H 2 | #define UVW_PROCESS_INCLUDE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "config.h" 10 | #include "enum.hpp" 11 | #include "handle.hpp" 12 | #include "loop.h" 13 | #include "stream.h" 14 | #include "util.h" 15 | 16 | namespace uvw { 17 | 18 | namespace details { 19 | 20 | enum class uvw_process_flags : std::underlying_type_t { 21 | SETUID = UV_PROCESS_SETUID, 22 | SETGID = UV_PROCESS_SETGID, 23 | WINDOWS_VERBATIM_ARGUMENTS = UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, 24 | DETACHED = UV_PROCESS_DETACHED, 25 | WINDOWS_HIDE = UV_PROCESS_WINDOWS_HIDE, 26 | WINDOWS_HIDE_CONSOLE = UV_PROCESS_WINDOWS_HIDE_CONSOLE, 27 | WINDOWS_HIDE_GUI = UV_PROCESS_WINDOWS_HIDE_GUI, 28 | WINDOWS_FILE_PATH_EXACT_NAME = UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME, 29 | _UVW_ENUM = 0 30 | }; 31 | 32 | enum class uvw_stdio_flags : std::underlying_type_t { 33 | IGNORE_STREAM = UV_IGNORE, 34 | CREATE_PIPE = UV_CREATE_PIPE, 35 | INHERIT_FD = UV_INHERIT_FD, 36 | INHERIT_STREAM = UV_INHERIT_STREAM, 37 | READABLE_PIPE = UV_READABLE_PIPE, 38 | WRITABLE_PIPE = UV_WRITABLE_PIPE, 39 | OVERLAPPED_PIPE = UV_OVERLAPPED_PIPE, 40 | _UVW_ENUM = 0 41 | }; 42 | 43 | } // namespace details 44 | 45 | /*! @brief Exit event. */ 46 | struct exit_event { 47 | explicit exit_event(int64_t code, int sig) noexcept; 48 | 49 | int64_t status; /*!< The exit status. */ 50 | int signal; /*!< The signal that caused the process to terminate, if any. */ 51 | }; 52 | 53 | /** 54 | * @brief The process handle. 55 | * 56 | * Process handles will spawn a new process and allow the user to control it and 57 | * establish communication channels with it using streams. 58 | */ 59 | class process_handle final: public handle { 60 | static void exit_callback(uv_process_t *hndl, int64_t exit_status, int term_signal); 61 | 62 | public: 63 | using process_flags = details::uvw_process_flags; 64 | using stdio_flags = details::uvw_stdio_flags; 65 | 66 | process_handle(loop::token token, std::shared_ptr ref); 67 | 68 | /** 69 | * @brief Disables inheritance for file descriptors/handles. 70 | * 71 | * Disables inheritance for file descriptors/handles that this process 72 | * inherited from its parent. The effect is that child processes spawned by 73 | * this process don’t accidentally inherit these handles.
74 | * It is recommended to call this function as early in your program as 75 | * possible, before the inherited file descriptors can be closed or 76 | * duplicated. 77 | * 78 | * See the official 79 | * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_disable_stdio_inheritance) 80 | * for further details. 81 | */ 82 | static void disable_stdio_inheritance() noexcept; 83 | 84 | /** 85 | * @brief kill Sends the specified signal to the given PID. 86 | * @param pid A valid process id. 87 | * @param signum A valid signal identifier. 88 | * @return True in case of success, false otherwise. 89 | */ 90 | static bool kill(int pid, int signum) noexcept; 91 | 92 | /** 93 | * @brief Initializes the handle. 94 | * @return Underlying return value. 95 | */ 96 | int init(); 97 | 98 | /** 99 | * @brief spawn Starts the process. 100 | * 101 | * See the official 102 | * [documentation](http://docs.libuv.org/en/v1.x/process.html) 103 | * for further details. 104 | * 105 | * @param file Path pointing to the program to be executed. 106 | * @param args Command line arguments. 107 | * @param env Optional environment for the new process. 108 | * @return Underlying return value. 109 | */ 110 | int spawn(const char *file, char **args, char **env = nullptr); 111 | 112 | /** 113 | * @brief Sends the specified signal to the internal process handle. 114 | * @param signum A valid signal identifier. 115 | * @return Underlying return value. 116 | */ 117 | int kill(int signum); 118 | 119 | /** 120 | * @brief Gets the PID of the spawned process. 121 | * 122 | * It’s set after calling `spawn()`. 123 | * 124 | * @return The PID of the spawned process. 125 | */ 126 | int pid() noexcept; 127 | 128 | /** 129 | * @brief Sets the current working directory for the subprocess. 130 | * @param path The working directory to be used when `spawn()` is invoked. 131 | * @return A reference to this process handle. 132 | */ 133 | process_handle &cwd(const std::string &path) noexcept; 134 | 135 | /** 136 | * @brief Sets flags that control how `spawn()` behaves. 137 | * 138 | * Available flags are: 139 | * 140 | * * `process_handle::process_flags::SETUID` 141 | * * `process_handle::process_flags::SETGID` 142 | * * `process_handle::process_flags::WINDOWS_VERBATIM_ARGUMENTS` 143 | * * `process_handle::process_flags::DETACHED` 144 | * * `process_handle::process_flags::WINDOWS_HIDE` 145 | * * `process_handle::process_flags::WINDOWS_HIDE_CONSOLE` 146 | * * `process_handle::process_flags::WINDOWS_HIDE_GUI` 147 | * * `process_handle::process_flags::WINDOWS_FILE_PATH_EXACT_NAME` 148 | * 149 | * See the official 150 | * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_process_flags) 151 | * for further details. 152 | * 153 | * @param flags A valid set of flags. 154 | * @return A reference to this process handle. 155 | */ 156 | process_handle &flags(process_flags flags) noexcept; 157 | 158 | /** 159 | * @brief Makes a `stdio` handle available to the child process. 160 | * 161 | * Available flags are: 162 | * 163 | * * `process_handle::stdio_flags::IGNORE_STREAM` 164 | * * `process_handle::stdio_flags::CREATE_PIPE` 165 | * * `process_handle::stdio_flags::INHERIT_FD` 166 | * * `process_handle::stdio_flags::INHERIT_STREAM` 167 | * * `process_handle::stdio_flags::READABLE_PIPE` 168 | * * `process_handle::stdio_flags::WRITABLE_PIPE` 169 | * * `process_handle::stdio_flags::OVERLAPPED_PIPE` 170 | * 171 | * See the official 172 | * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags) 173 | * for further details. 174 | * 175 | * @param stream A valid `stdio` handle. 176 | * @param flags A valid set of flags. 177 | * @return A reference to this process handle. 178 | */ 179 | template 180 | process_handle &stdio(stream_handle &stream, stdio_flags flags) { 181 | uv_stdio_container_t container; 182 | container.flags = static_cast(flags); 183 | container.data.stream = reinterpret_cast(stream.raw()); 184 | po_stream_stdio.push_back(std::move(container)); 185 | return *this; 186 | } 187 | 188 | /** 189 | * @brief Makes a file descriptor available to the child process. 190 | * 191 | * Available flags are: 192 | * 193 | * * `process_handle::stdio_flags::IGNORE_STREAM` 194 | * * `process_handle::stdio_flags::CREATE_PIPE` 195 | * * `process_handle::stdio_flags::INHERIT_FD` 196 | * * `process_handle::stdio_flags::INHERIT_STREAM` 197 | * * `process_handle::stdio_flags::READABLE_PIPE` 198 | * * `process_handle::stdio_flags::WRITABLE_PIPE` 199 | * * `process_handle::stdio_flags::OVERLAPPED_PIPE` 200 | * 201 | * Default file descriptors are: 202 | * * `uvw::std_in` for `stdin` 203 | * * `uvw::std_out` for `stdout` 204 | * * `uvw::std_err` for `stderr` 205 | * 206 | * See the official 207 | * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags) 208 | * for further details. 209 | * 210 | * @param fd A valid file descriptor. 211 | * @param flags A valid set of flags. 212 | * @return A reference to this process handle. 213 | */ 214 | process_handle &stdio(file_handle fd, stdio_flags flags); 215 | 216 | /** 217 | * @brief Sets the child process' user id. 218 | * @param id A valid user id to be used. 219 | * @return A reference to this process handle. 220 | */ 221 | process_handle &uid(uid_type id); 222 | 223 | /** 224 | * @brief Sets the child process' group id. 225 | * @param id A valid group id to be used. 226 | * @return A reference to this process handle. 227 | */ 228 | process_handle &gid(gid_type id); 229 | 230 | private: 231 | std::string po_cwd; 232 | process_flags po_flags; 233 | std::vector po_fd_stdio; 234 | std::vector po_stream_stdio; 235 | uid_type po_uid; 236 | gid_type po_gid; 237 | }; 238 | 239 | } // namespace uvw 240 | 241 | #ifndef UVW_AS_LIB 242 | # include "process.cpp" 243 | #endif 244 | 245 | #endif // UVW_PROCESS_INCLUDE_H 246 | --------------------------------------------------------------------------------