Showing preview only (3,399K chars total). Download the full file or copy to clipboard to get everything.
Repository: batchar2/fptn
Branch: master
Commit: 450be140e7ad
Files: 233
Total size: 3.2 MB
Directory structure:
gitextract_4uyv479r/
├── .clang-format
├── .clang-tidy
├── .cmake-format
├── .conan/
│ └── recipes/
│ └── boringssl/
│ └── conanfile.py
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── custom.md
│ │ └── feature_request.md
│ └── workflows/
│ └── main.yml
├── .gitignore
├── CLAUDE.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── README_RU.md
├── conanfile.py
├── cpplint.py
├── depends/
│ └── cmake/
│ ├── CamouflageTLS.cmake
│ ├── FetchBase64.cmake
│ ├── FetchLibTunTap.cmake
│ ├── FetchWintun.cmake
│ └── NtpClient.cmake
├── deploy/
│ ├── docker/
│ │ ├── Dockerfile
│ │ ├── config/
│ │ │ ├── supervisor/
│ │ │ │ ├── dns-server.conf
│ │ │ │ └── fptn-server.conf
│ │ │ └── supervisord.conf
│ │ └── scripts/
│ │ ├── start-dns-server.sh
│ │ ├── start-fptn.sh
│ │ └── token-generator.py
│ ├── linux/
│ │ ├── deb/
│ │ │ ├── README.md
│ │ │ ├── create-client-cli-deb-package.sh
│ │ │ ├── create-client-gui-deb-package.sh
│ │ │ └── create-server-deb-package.sh
│ │ └── wifi/
│ │ ├── README.md
│ │ ├── README.ru.md
│ │ ├── dnsmasq/
│ │ │ ├── fptn-dnsmasq.conf
│ │ │ └── fptn-dnsmasq.service
│ │ ├── fptn-setup-network/
│ │ │ ├── fptn-setup-network.service
│ │ │ └── fptn-setup-network.sh
│ │ └── hostapd/
│ │ ├── fptn-hostapd.conf
│ │ └── fptn-hostapd.service
│ ├── macos/
│ │ ├── README.md
│ │ ├── assets/
│ │ │ └── FptnClient.icns
│ │ ├── create-pkg.py
│ │ └── scripts/
│ │ ├── fptn-client-cli-wrapper.sh
│ │ ├── fptn-client-gui-wrapper.sh
│ │ ├── post_install.sh
│ │ └── uninstall.sh
│ ├── sni/
│ │ ├── global.sni
│ │ └── russia.sni
│ └── windows/
│ ├── .gitignore
│ ├── README.md
│ ├── conan-replace-version.py
│ ├── create-installer.py
│ └── installer/
│ ├── app.manifest
│ └── fptn-installer.iss
├── docker-compose/
│ ├── .gitignore
│ ├── README.md
│ └── docker-compose.yml
├── docs/
│ ├── CNAME
│ ├── index-ru.html
│ └── index.html
├── pyproject.toml
├── renovate.json
├── src/
│ ├── common/
│ │ ├── client_id.h
│ │ ├── data/
│ │ │ ├── channel.h
│ │ │ └── channel_async.h
│ │ ├── jwt_token/
│ │ │ └── token_manager.h
│ │ ├── logger/
│ │ │ └── logger.h
│ │ ├── network/
│ │ │ ├── ip_address.h
│ │ │ ├── ip_packet.h
│ │ │ ├── ipv4_generator.h
│ │ │ ├── ipv6_generator.h
│ │ │ ├── ipv6_utils.h
│ │ │ ├── net_interface.h
│ │ │ ├── resolv.h
│ │ │ ├── tun/
│ │ │ │ ├── darwin_tun_device.h
│ │ │ │ ├── linux_tun_device.h
│ │ │ │ └── win_tun_device.h
│ │ │ └── utils.h
│ │ ├── system/
│ │ │ └── command.h
│ │ ├── user/
│ │ │ └── common_user_manager.h
│ │ └── utils/
│ │ ├── base64.h
│ │ └── utils.h
│ ├── fptn-client/
│ │ ├── CMakeLists.txt
│ │ ├── config/
│ │ │ ├── config_file.cpp
│ │ │ └── config_file.h
│ │ ├── fptn-client-cli.cpp
│ │ ├── fptn-client-gui.cpp
│ │ ├── gui/
│ │ │ ├── autostart/
│ │ │ │ └── autostart.h
│ │ │ ├── autoupdate/
│ │ │ │ └── autoupdate.h
│ │ │ ├── resources/
│ │ │ │ ├── resources.qrc
│ │ │ │ └── translations/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── fptn_en.ts
│ │ │ │ └── fptn_ru.ts
│ │ │ ├── server_menu_item_widget/
│ │ │ │ ├── server_menu_item_widget.cpp
│ │ │ │ └── server_menu_item_widget.h
│ │ │ ├── settingsmodel/
│ │ │ │ ├── settingsmodel.cpp
│ │ │ │ └── settingsmodel.h
│ │ │ ├── settingswidget/
│ │ │ │ ├── settings.cpp
│ │ │ │ └── settings.h
│ │ │ ├── sni_autoscan_dialog/
│ │ │ │ ├── sni_autoscan_dialog.cpp
│ │ │ │ └── sni_autoscan_dialog.h
│ │ │ ├── sni_manager/
│ │ │ │ ├── sni_manager.cpp
│ │ │ │ └── sni_manager.h
│ │ │ ├── speedwidget/
│ │ │ │ ├── speedwidget.cpp
│ │ │ │ └── speedwidget.h
│ │ │ ├── style/
│ │ │ │ ├── style.cpp
│ │ │ │ └── style.h
│ │ │ ├── tokendialog/
│ │ │ │ ├── tokendialog.cpp
│ │ │ │ └── tokendialog.h
│ │ │ ├── translations/
│ │ │ │ ├── translations.cpp
│ │ │ │ └── translations.h
│ │ │ └── tray/
│ │ │ ├── tray.cpp
│ │ │ └── tray.h
│ │ ├── plugins/
│ │ │ ├── base_plugin.h
│ │ │ ├── blacklist/
│ │ │ │ ├── domain_blacklist.cpp
│ │ │ │ └── domain_blacklist.h
│ │ │ └── split/
│ │ │ ├── tunneling.cpp
│ │ │ └── tunneling.h
│ │ ├── routing/
│ │ │ ├── route_manager.cpp
│ │ │ └── route_manager.h
│ │ ├── utils/
│ │ │ ├── brotli/
│ │ │ │ └── brotli.h
│ │ │ ├── macos/
│ │ │ │ └── admin.h
│ │ │ ├── signal/
│ │ │ │ └── main_loop.h
│ │ │ ├── speed_estimator/
│ │ │ │ ├── server_info.h
│ │ │ │ ├── speed_estimator.cpp
│ │ │ │ └── speed_estimator.h
│ │ │ ├── utils.h
│ │ │ └── windows/
│ │ │ └── vpn_conflict.h
│ │ └── vpn/
│ │ ├── http/
│ │ │ ├── client.cpp
│ │ │ └── client.h
│ │ ├── vpn_client.cpp
│ │ └── vpn_client.h
│ ├── fptn-passwd/
│ │ ├── CMakeLists.txt
│ │ └── fptn-passwd.cpp
│ ├── fptn-protocol-lib/
│ │ ├── CMakeLists.txt
│ │ ├── https/
│ │ │ ├── api_client/
│ │ │ │ ├── api_client.cpp
│ │ │ │ └── api_client.h
│ │ │ ├── censorship_strategy.h
│ │ │ ├── obfuscator/
│ │ │ │ ├── methods/
│ │ │ │ │ ├── detector.h
│ │ │ │ │ ├── obfuscator_interface.h
│ │ │ │ │ ├── tls/
│ │ │ │ │ │ ├── tls_obfuscator.cpp
│ │ │ │ │ │ └── tls_obfuscator.h
│ │ │ │ │ └── tls2/
│ │ │ │ │ ├── tls_obfuscator2.cpp
│ │ │ │ │ └── tls_obfuscator2.h
│ │ │ │ └── tcp_stream/
│ │ │ │ └── tcp_stream.h
│ │ │ ├── utils/
│ │ │ │ ├── change_cipher_spec.h
│ │ │ │ └── tls/
│ │ │ │ ├── tls.cpp
│ │ │ │ └── tls.h
│ │ │ └── websocket_client/
│ │ │ ├── websocket_client.cpp
│ │ │ └── websocket_client.h
│ │ ├── protobuf/
│ │ │ ├── protocol.cpp
│ │ │ ├── protocol.h
│ │ │ └── protocol.proto
│ │ └── time/
│ │ ├── time_provider.cpp
│ │ └── time_provider.h
│ └── fptn-server/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── client/
│ │ ├── session.cpp
│ │ └── session.h
│ ├── config/
│ │ ├── command_line_config.cpp
│ │ └── command_line_config.h
│ ├── filter/
│ │ ├── filters/
│ │ │ ├── antiscan/
│ │ │ │ ├── antiscan.cpp
│ │ │ │ └── antiscan.h
│ │ │ ├── base_filter.h
│ │ │ └── bittorrent/
│ │ │ ├── bittorrent.cpp
│ │ │ └── bittorrent.h
│ │ ├── manager.cpp
│ │ └── manager.h
│ ├── fptn-server.cpp
│ ├── nat/
│ │ ├── table.cpp
│ │ └── table.h
│ ├── network/
│ │ ├── virtual_interface.cpp
│ │ └── virtual_interface.h
│ ├── routing/
│ │ ├── iptables.cpp
│ │ └── iptables.h
│ ├── statistic/
│ │ ├── metrics.cpp
│ │ └── metrics.h
│ ├── traffic_shaper/
│ │ ├── leaky_bucket.cpp
│ │ └── leaky_bucket.h
│ ├── user/
│ │ ├── user_manager.cpp
│ │ └── user_manager.h
│ ├── vpn/
│ │ ├── manager.cpp
│ │ └── manager.h
│ └── web/
│ ├── api/
│ │ └── handle.h
│ ├── handshake/
│ │ ├── handshake_cache_manager.cpp
│ │ └── handshake_cache_manager.h
│ ├── listener/
│ │ ├── listener.cpp
│ │ └── listener.h
│ ├── server.cpp
│ ├── server.h
│ └── session/
│ ├── session.cpp
│ └── session.h
├── sysadmin-tools/
│ ├── grafana/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── configs/
│ │ │ ├── grafana/
│ │ │ │ ├── dashboards/
│ │ │ │ │ ├── dashboards.yaml
│ │ │ │ │ ├── fptn_dashboard.json
│ │ │ │ │ └── node-exporter-full.json
│ │ │ │ └── datasources/
│ │ │ │ └── datasources.yaml
│ │ │ ├── nginx/
│ │ │ │ └── nginx.conf.template
│ │ │ └── prometheus/
│ │ │ └── prometheus.yaml.template
│ │ ├── docker-compose.build.yml
│ │ ├── docker-compose.yml
│ │ └── proxy-server/
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── CMakeLists.txt
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── conanfile.py
│ │ └── src/
│ │ └── proxy-server.cpp
│ └── telegram-bot/
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── configs/
│ │ ├── premium_servers.json.demo
│ │ ├── servers.json.demo
│ │ └── servers_censored_zone.json.demo
│ ├── docker-compose.yml
│ └── src/
│ ├── bot.py
│ └── requirements.txt
└── tests/
├── CMakeLists.txt
├── common/
│ ├── CMakeLists.txt
│ ├── data/
│ │ └── ChannelTest.cpp
│ └── network/
│ ├── ClientStopRaceTest.cpp
│ ├── IPv4GeneratorTest.cpp
│ ├── IPv6GeneratorTest.cpp
│ ├── IPv6UtilsTest.cpp
│ └── TunDeviceTest.cpp
├── fptnlib/
│ ├── CMakeLists.txt
│ └── api_client/
│ └── ApiClientTest.cpp
└── server/
├── CMakeLists.txt
├── filter/
│ └── antiscan/
│ └── AntiScanTest.cpp
└── statistic/
└── MetricTest.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .clang-format
================================================
# Use the Google style in this project.
Language: Cpp
BasedOnStyle: Google
Standard: c++11
# Some folks prefer to write "int& foo" while others prefer "int &foo". The
# Google Style Guide only asks for consistency within a project, we chose
# "int& foo" for this project:
DerivePointerAlignment: false
PointerAlignment: Left
UseTab: Never
AlignAfterOpenBracket: DontAlign
AllowAllParametersOfDeclarationOnNextLine: true
BinPackParameters: false
# The Google Style Guide only asks for consistency w.r.t. "east const" vs.
# "const west" alignment of cv-qualifiers. In this project we use "const west".
#QualifierAlignment: Right
# Adjust the include ordering
IncludeCategories:
- Regex: '^<gtest/.*>'
Priority: 4
- Regex: '^<Q[A-Z].*>' # QT Core (QApplication, QWidget и т.д.)
Priority: 3
- Regex: '^<Qt[A-Z].*>' # QT Modules (QtCore, QtGui и т.д.)
Priority: 3
- Regex: '^<protocol.pb.h>'
Priority: 2
- Regex: '^<tuntap++.*>'
Priority: 2
- Regex: '^<ntp_client.hpp>'
Priority: 2
- Regex: '^<zlib.*>'
Priority: 2
- Regex: '^<argparse/.*>'
Priority: 2
- Regex: '^<camouflage/.*>'
Priority: 2
- Regex: '^<boost/.*>'
Priority: 2
- Regex: '^<pcpp/.*>'
Priority: 2
- Regex: '^<jwt-cpp/.*>'
Priority: 2
- Regex: '^<nlohmann/.*>'
Priority: 2
- Regex: '^<httplib/.*>'
Priority: 2
- Regex: '^<spdlog/.*>'
Priority: 2
- Regex: '^<pcapplusplus/.*>'
Priority: 2
- Regex: '^<prometheus/.*>'
Priority: 2
- Regex: '^<openssl/.*>'
Priority: 2
- Regex: '^<fmt/.*>'
Priority: 2
- Regex: '^<brotli/.*>'
Priority: 2
- Regex: '^<re2/.*>'
Priority: 2
- Regex: '^"(common/.*)"'
Priority: 5
- Regex: '^<.*>'
Priority: 1
- Regex: '^".*"'
Priority: 6
# Format raw string literals with a `pb` or `proto` tag as proto.
RawStringFormats:
- Language: TextProto
Delimiters:
- 'pb'
- 'proto'
BasedOnStyle: Google
CommentPragmas: '(@copydoc|@copybrief|@see|@overload|@snippet)'
================================================
FILE: .clang-tidy
================================================
---
# Configure clang-tidy for this project.
# Here is an explanation for why some of the checks are disabled:
#
# -google-readability-namespace-comments: the *_CLIENT_NS is a macro, and
# clang-tidy fails to match it against the initial value.
#
# -modernize-use-trailing-return-type: clang-tidy recommends using
# `auto Foo() -> std::string { return ...; }`, we think the code is less
# readable in this form.
#
# --modernize-concat-nested-namespaces: clang-tidy recommends
# `namespace google::cloud {}` over `namespace google { namespace cloud { } }`
# We need to support C++14, which does not supported nested namespaces.
#
# --modernize-use-nodiscard: clang-tidy recommends adding a nodiscard annotation
# to functions where the return value should not be ignored.
# We need to support C++14, which does not supported the annotation.
#
# -modernize-return-braced-init-list: We think removing typenames and using
# only braced-init can hurt readability.
#
# -modernize-avoid-c-arrays: We only use C arrays when they seem to be the
# right tool for the job, such as `char foo[] = "hello"`. In these cases,
# avoiding C arrays often makes the code less readable, and std::array is
# not a drop-in replacement because it doesn't deduce the size.
#
# -modernize-type-traits: clang-tidy recommands using c++17 style variable
# templates. We will enable this check after we moved to c++17.
#
# -modernize-unary-static-assert: clang-tidy asks removing empty string in
# static_assert(), the check is only applicable for c++17 and later code.
# We will enable this check after we moved to c++17.
#
# -performance-move-const-arg: This warning requires the developer to
# know/care more about the implementation details of types/functions than
# should be necessary. For example, `A a; F(std::move(a));` will trigger a
# warning IFF `A` is a trivial type (and therefore the move is
# meaningless). It would also warn if `F` accepts by `const&`, which is
# another detail that the caller need not care about.
#
# -performance-avoid-endl: we would like to turn this on, but there are too
# many legitimate uses in our samples.
#
# -performance-enum-size: Smaller enums may or not may be faster, it depends on
# the architechture. If data size was a consideration, we might decide to
# enable the warnings.
#
# -readability-redundant-declaration: A friend declaration inside a class
# counts as a declaration, so if we also declare that friend outside the
# class in order to document it as part of the public API, that will
# trigger a redundant declaration warning from this check.
#
# -readability-avoid-return-with-void-value: We believe this is idiomatic
# and saves typing, and the intent is obvious.
#
# -readability-function-cognitive-complexity: too many false positives with
# clang-tidy-12. We need to disable this check in macros, and that setting
# only appears in clang-tidy-13.
#
# -bugprone-narrowing-conversions: too many false positives around
# `std::size_t` vs. `*::difference_type`.
#
# -bugprone-easily-swappable-parameters: too many false positives.
#
# -bugprone-implicit-widening-of-multiplication-result: too many false positives.
# Almost any expression of the form `2 * variable` or `long x = a_int * b_int;`
# generates an error.
#
# -bugprone-unchecked-optional-access: too many false positives in tests.
# Despite what the documentation says, this warning appears after
# `ASSERT_TRUE(variable)` or `ASSERT_TRUE(variable.has_value())`.
#
# TODO(#14162): Enable clang-tidy checks. We initially omitted these checks
# because they require large cleanup efforts or were blocking the clang-tidy
# X update.
Checks: >
-*,
abseil-*,
bugprone-*,
google-*,
misc-*,
modernize-*,
performance-*,
portability-*,
readability-*,
-google-readability-braces-around-statements,
-google-readability-namespace-comments,
-google-runtime-references,
-misc-non-private-member-variables-in-classes,
-misc-const-correctness,
-misc-include-cleaner,
-modernize-return-braced-init-list,
-modernize-use-trailing-return-type,
-modernize-use-nodiscard,
-modernize-avoid-c-arrays,
-modernize-type-traits,
-modernize-unary-static-assert,
-performance-move-const-arg,
-performance-avoid-endl,
-performance-enum-size,
-readability-braces-around-statements,
-readability-identifier-length,
-readability-magic-numbers,
-readability-named-parameter,
-readability-redundant-declaration,
-readability-avoid-return-with-void-value,
-readability-function-cognitive-complexity,
-bugprone-narrowing-conversions,
-bugprone-easily-swappable-parameters,
-bugprone-inc-dec-in-conditions,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-unchecked-optional-access,
-bugprone-unused-local-non-trivial-variable,
-bugprone-unused-return-value,
-portability-template-virtual-member-function
# Turn all the warnings from the checks above into errors.
WarningsAsErrors: "*"
HeaderFilterRegex: "(google/cloud/|generator/).*\\.h$"
CheckOptions:
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase }
- { key: readability-identifier-naming.FunctionCase, value: aNy_CasE }
- { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-identifier-naming.ClassMemberCase, value: lower_case }
- { key: readability-identifier-naming.ClassMemberSuffix, value: _ }
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
- { key: readability-identifier-naming.EnumConstantCase, value: CamelCase }
- { key: readability-identifier-naming.EnumConstantPrefix, value: k }
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
- { key: readability-identifier-naming.ConstexprVariablePrefix, value: k }
- { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase }
- { key: readability-identifier-naming.GlobalConstantPrefix, value: k }
- { key: readability-identifier-naming.MemberConstantCase, value: CamelCase }
- { key: readability-identifier-naming.MemberConstantPrefix, value: k }
- { key: readability-identifier-naming.StaticConstantCase, value: CamelCase }
- { key: readability-identifier-naming.StaticConstantPrefix, value: k }
- { key: readability-implicit-bool-conversion.AllowIntegerConditions, value: 1 }
- { key: readability-implicit-bool-conversion.AllowPointerConditions, value: 1 }
- { key: readability-function-cognitive-complexity.IgnoreMacros, value: 1 }
================================================
FILE: .cmake-format
================================================
_help_parse: Options affecting listfile parsing
parse:
_help_additional_commands:
- Specify structure for custom cmake functions
additional_commands:
foo:
flags:
- BAR
- BAZ
kwargs:
HEADERS: '*'
SOURCES: '*'
DEPENDS: '*'
_help_override_spec:
- Override configurations per-command where available
override_spec: {}
_help_vartags:
- Specify variable tags.
vartags: []
_help_proptags:
- Specify property tags.
proptags: []
_help_format: Options affecting formatting.
format:
_help_disable:
- Disable formatting entirely, making cmake-format a no-op
disable: false
_help_line_width:
- How wide to allow formatted cmake files
line_width: 150
_help_tab_size:
- How many spaces to tab for indent
tab_size: 2
_help_max_subgroups_hwrap:
- If an argument group contains more than this many sub-groups
- (parg or kwarg groups) then force it to a vertical layout.
max_subgroups_hwrap: 2
_help_max_pargs_hwrap:
- If a positional argument group contains more than this many
- arguments, then force it to a vertical layout.
max_pargs_hwrap: 6
_help_max_rows_cmdline:
- If a cmdline positional group consumes more than this many
- lines without nesting, then invalidate the layout (and nest)
max_rows_cmdline: 2
_help_separate_ctrl_name_with_space:
- If true, separate flow control names from their parentheses
- with a space
separate_ctrl_name_with_space: false
_help_separate_fn_name_with_space:
- If true, separate function names from parentheses with a
- space
separate_fn_name_with_space: false
_help_dangle_parens:
- If a statement is wrapped to more than one line, than dangle
- the closing parenthesis on its own line.
dangle_parens: false
_help_dangle_align:
- If the trailing parenthesis must be 'dangled' on its on
- 'line, then align it to this reference: `prefix`: the start'
- 'of the statement, `prefix-indent`: the start of the'
- 'statement, plus one indentation level, `child`: align to'
- the column of the arguments
dangle_align: prefix
_help_min_prefix_chars:
- If the statement spelling length (including space and
- parenthesis) is smaller than this amount, then force reject
- nested layouts.
min_prefix_chars: 4
_help_max_prefix_chars:
- If the statement spelling length (including space and
- parenthesis) is larger than the tab width by more than this
- amount, then force reject un-nested layouts.
max_prefix_chars: 10
_help_max_lines_hwrap:
- If a candidate layout is wrapped horizontally but it exceeds
- this many lines, then reject the layout.
max_lines_hwrap: 2
_help_line_ending:
- What style line endings to use in the output.
line_ending: unix
_help_command_case:
- Format command names consistently as 'lower' or 'upper' case
command_case: canonical
_help_keyword_case:
- Format keywords consistently as 'lower' or 'upper' case
keyword_case: unchanged
_help_always_wrap:
- A list of command names which should always be wrapped
always_wrap: []
_help_enable_sort:
- If true, the argument lists which are known to be sortable
- will be sorted lexicographicall
enable_sort: true
_help_autosort:
- If true, the parsers may infer whether or not an argument
- list is sortable (without annotation).
autosort: false
_help_require_valid_layout:
- By default, if cmake-format cannot successfully fit
- everything into the desired linewidth it will apply the
- last, most agressive attempt that it made. If this flag is
- True, however, cmake-format will print error, exit with non-
- zero status code, and write-out nothing
require_valid_layout: false
_help_layout_passes:
- A dictionary mapping layout nodes to a list of wrap
- decisions. See the documentation for more information.
layout_passes: {}
_help_markup: Options affecting comment reflow and formatting.
markup:
_help_bullet_char:
- What character to use for bulleted lists
bullet_char: '*'
_help_enum_char:
- What character to use as punctuation after numerals in an
- enumerated list
enum_char: .
_help_first_comment_is_literal:
- If comment markup is enabled, don't reflow the first comment
- block in each listfile. Use this to preserve formatting of
- your copyright/license statements.
first_comment_is_literal: false
_help_literal_comment_pattern:
- If comment markup is enabled, don't reflow any comment block
- which matches this (regex) pattern. Default is `None`
- (disabled).
literal_comment_pattern: null
_help_fence_pattern:
- Regular expression to match preformat fences in comments
- default= ``r'^\s*([`~]{3}[`~]*)(.*)$'``
fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$
_help_ruler_pattern:
- Regular expression to match rulers in comments default=
- '``r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''``'
ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$
_help_explicit_trailing_pattern:
- If a comment line matches starts with this pattern then it
- is explicitly a trailing comment for the preceeding
- argument. Default is '#<'
explicit_trailing_pattern: '#<'
_help_hashruler_min_length:
- If a comment line starts with at least this many consecutive
- hash characters, then don't lstrip() them off. This allows
- for lazy hash rulers where the first hash char is not
- separated by space
hashruler_min_length: 10
_help_canonicalize_hashrulers:
- If true, then insert a space between the first hash char and
- remaining hash chars in a hash ruler, and normalize its
- length to fill the column
canonicalize_hashrulers: true
_help_enable_markup:
- enable comment markup parsing and reflow
enable_markup: false
_help_lint: Options affecting the linter
lint:
_help_disabled_codes:
- a list of lint codes to disable
disabled_codes: []
_help_function_pattern:
- regular expression pattern describing valid function names
function_pattern: '[0-9a-z_]+'
_help_macro_pattern:
- regular expression pattern describing valid macro names
macro_pattern: '[0-9A-Z_]+'
_help_global_var_pattern:
- regular expression pattern describing valid names for
- variables with global (cache) scope
global_var_pattern: '[A-Z][0-9A-Z_]+'
_help_internal_var_pattern:
- regular expression pattern describing valid names for
- variables with global scope (but internal semantic)
internal_var_pattern: _[A-Z][0-9A-Z_]+
_help_local_var_pattern:
- regular expression pattern describing valid names for
- variables with local scope
local_var_pattern: '[a-z][a-z0-9_]+'
_help_private_var_pattern:
- regular expression pattern describing valid names for
- privatedirectory variables
private_var_pattern: _[0-9a-z_]+
_help_public_var_pattern:
- regular expression pattern describing valid names for public
- directory variables
public_var_pattern: '[A-Z][0-9A-Z_]+'
_help_argument_var_pattern:
- regular expression pattern describing valid names for
- function/macro arguments and loop variables.
argument_var_pattern: '[a-z][a-z0-9_]+'
_help_keyword_pattern:
- regular expression pattern describing valid names for
- keywords used in functions or macros
keyword_pattern: '[A-Z][0-9A-Z_]+'
_help_max_conditionals_custom_parser:
- In the heuristic for C0201, how many conditionals to match
- within a loop in before considering the loop a parser.
max_conditionals_custom_parser: 2
_help_min_statement_spacing:
- Require at least this many newlines between statements
min_statement_spacing: 1
_help_max_statement_spacing:
- Require no more than this many newlines between statements
max_statement_spacing: 2
max_returns: 6
max_branches: 12
max_arguments: 5
max_localvars: 15
max_statements: 50
_help_encode: Options affecting file encoding
encode:
_help_emit_byteorder_mark:
- If true, emit the unicode byte-order mark (BOM) at the start
- of the file
emit_byteorder_mark: false
_help_input_encoding:
- Specify the encoding of the input file. Defaults to utf-8
input_encoding: utf-8
_help_output_encoding:
- Specify the encoding of the output file. Defaults to utf-8.
- Note that cmake only claims to support utf-8 so be careful
- when using anything else
output_encoding: utf-8
_help_misc: Miscellaneous configurations options.
misc:
_help_per_command:
- A dictionary containing any per-command configuration
- overrides. Currently only `command_case` is supported.
per_command: {}
================================================
FILE: .conan/recipes/boringssl/conanfile.py
================================================
from conan import ConanFile
from conan.tools.files import get
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
class BoringSSLConan(ConanFile):
name = "openssl"
version = "boringssl"
settings = "os", "arch", "compiler", "build_type"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
def layout(self):
cmake_layout(self)
def source(self):
url = "https://github.com/batchar2/boringssl/archive/refs/heads/working-russia-tls-handshake.tar.gz"
get(self, url, strip_root=True)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["BUILD_TESTING"] = False
tc.variables["ENABLE_EXPRESSION_TESTS"] = False
# Правильная настройка для iOS
if self.settings.os == "iOS":
tc.variables["CMAKE_SYSTEM_NAME"] = "iOS"
tc.variables["CMAKE_OSX_DEPLOYMENT_TARGET"] = str(self.settings.os.version)
tc.variables["CMAKE_OSX_ARCHITECTURES"] = self.settings.arch
tc.variables["CMAKE_MACOSX_BUNDLE"] = False
if "simulator" in str(self.settings.os.sdk).lower():
tc.variables["CMAKE_OSX_SYSROOT"] = "iphonesimulator"
else:
tc.variables["CMAKE_OSX_SYSROOT"] = "iphoneos"
tc.variables["CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED"] = "NO"
tc.variables["CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED"] = "NO"
tc.variables["CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE"] = "YES"
tc.variables["CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH"] = "NO"
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
self.cpp_info.components["ssl"].libs = ["ssl"]
self.cpp_info.components["crypto"].libs = ["crypto"]
# include dir
self.cpp_info.includedirs = ["include"]
self.cpp_info.components["ssl"].includedirs = ["include"]
self.cpp_info.components["crypto"].includedirs = ["include"]
# for CMake find_package(OpenSSL)
self.cpp_info.set_property("cmake_file_name", "OpenSSL")
self.cpp_info.components["ssl"].set_property("cmake_target_name", "OpenSSL::SSL")
self.cpp_info.components["crypto"].set_property("cmake_target_name", "OpenSSL::Crypto")
if self.settings.os == "iOS":
self.cpp_info.frameworks = ["Security", "Foundation", "CoreFoundation"]
self.cpp_info.system_libs = ["c++"]
================================================
FILE: .dockerignore
================================================
.cache
.vscode
.vsconan
.git/
README.md
LICENSE
CMakeUserPresets.json
CI/
tmp/
keys/
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/main.yml
================================================
name: Build and Test
on:
push:
branches:
- '**'
tags:
- '*'
release:
types: [ published ]
env:
DOCKER_REGISTRY: fptnvpn
IMAGE_NAME: fptn-vpn-server
jobs:
build_ubuntu_x86_64:
runs-on: Ubuntu_x86_64
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: true
- name: Set env
if: github.event_name == 'release'
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Print Release Version
if: github.event_name == 'release'
run: |
echo "Release Version: $RELEASE_VERSION"
- name: Update FPTN_VERSION in conanfile.py
if: github.event_name == 'release'
run: |
sed -i "s/^FPTN_VERSION = \".*\"/FPTN_VERSION = \"$RELEASE_VERSION\"/" conanfile.py
- name: Install dependencies
run: |
conan install . --output-folder=build --build=missing -o with_gui_client=True --settings build_type=Release -s compiler.cppstd=17
- name: Run cpplint
run: |
python3 cpplint.py --recursive --filter=-build/c++17,-whitespace/newline,-readability/braces --counting=total ./src/ ./tests/
- name: Run cppcheck
run: |
cppcheck --error-exitcode=1 --enable=all --check-level=exhaustive --language=c++ --suppress=unusedFunction --inline-suppr --suppress=missingIncludeSystem --suppress=unknownMacro --suppress=unmatchedSuppression -I ./src/fptn-client/ -I ./src/fptn-server/ -I ./src/fptn-passwd/ -I ./src/fptn-client-protocol-lib -I ./src/ ./src/ ./tests/
- name: Run cmake-format
run: |
cmake-format -i CMakeLists.txt src/fptn-client/CMakeLists.txt src/fptn-passwd/CMakeLists.txt src/fptn-server/CMakeLists.txt src/fptn-protocol-lib/CMakeLists.txt depends/cmake/FetchBase64.cmake depends/cmake/FetchLibTunTap.cmake depends/cmake/FetchWintun.cmake
- name: Build
run: |
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
- name: Test
run: |
cd build
ctest -C Release
- name: Build Debian package
run: |
cd build
cmake --build . --target build-deb
cmake --build . --target build-deb-gui
- name: Upload deb RELEASE
if: github.event_name == 'release'
uses: AButler/upload-release-assets@v3.0
with:
files: "fptn-*.deb"
repo-token: ${{ secrets.CI_TOKEN }}
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: Ubuntu86_64Artifacts
path: |
*.deb
if-no-files-found: warn
- name: Build and Push Docker Image (amd64)
if: github.event_name == 'release'
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker buildx create --name amd64-builder --platform linux/amd64 --use || true
docker buildx build \
--platform linux/amd64 \
-f ./deploy/docker/Dockerfile \
-t "$DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-amd64" \
--build-arg FPTN_SERVER_PATH=build/src/fptn-server/fptn-server \
--build-arg FPTN_PASSWD_PATH=build/src/fptn-passwd/fptn-passwd \
--push \
--provenance=false \
--sbom=false .
build_ubuntu_arm64:
runs-on: Ubuntu_ARM64_Desktop
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: true
- name: Set env
if: github.event_name == 'release'
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Print Release Version
if: github.event_name == 'release'
run: |
echo "Release Version: $RELEASE_VERSION"
- name: Update FPTN_VERSION in conanfile.py
if: github.event_name == 'release'
run: |
sed -i "s/^FPTN_VERSION = \".*\"/FPTN_VERSION = \"$RELEASE_VERSION\"/" conanfile.py
- name: Install dependencies
run: |
conan install . --output-folder=build --build=missing -o with_gui_client=True --settings build_type=Release -s compiler.cppstd=17
- name: Run cpplint
run: |
python3 cpplint.py --recursive --filter=-build/c++17,-whitespace/newline,-readability/braces --counting=total ./src/ ./tests/
- name: Run cppcheck
run: |
cppcheck --error-exitcode=1 --enable=all --check-level=exhaustive --language=c++ --suppress=unusedFunction --inline-suppr --suppress=missingIncludeSystem --suppress=unmatchedSuppression -I ./src/fptn-client/ -I ./src/fptn-server/ -I ./src/fptn-passwd/ -I ./src/fptn-client-protocol-lib -I ./src/ ./src/ ./tests/
- name: Run cmake-format
run: |
cmake-format -i CMakeLists.txt src/fptn-client/CMakeLists.txt src/fptn-passwd/CMakeLists.txt src/fptn-server/CMakeLists.txt src/fptn-protocol-lib/CMakeLists.txt depends/cmake/FetchBase64.cmake depends/cmake/FetchLibTunTap.cmake depends/cmake/FetchWintun.cmake
- name: Build
run: |
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
- name: Test
run: |
cd build
ctest -C Release
- name: Build Debian package
run: |
cd build
cmake --build . --target build-deb
cmake --build . --target build-deb-gui
- name: Upload deb
if: github.event_name == 'release'
uses: AButler/upload-release-assets@v3.0
with:
files: "fptn-*.deb"
repo-token: ${{ secrets.CI_TOKEN }}
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: UbuntuArmArtifacts
path: |
*.deb
if-no-files-found: warn
- name: Build and Push Docker Image (arm64)
if: github.event_name == 'release'
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker buildx create --name arm64-builder --platform linux/arm64 --use || true
docker buildx build \
--platform linux/arm64 \
-f ./deploy/docker/Dockerfile \
-t "$DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-arm64" \
--build-arg FPTN_SERVER_PATH=build/src/fptn-server/fptn-server \
--build-arg FPTN_PASSWD_PATH=build/src/fptn-passwd/fptn-passwd \
--push \
--provenance=false \
--sbom=false .
build_macos_arm64:
runs-on: MacOS_ARM64
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: true
- name: Set env
if: github.event_name == 'release'
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Print Release Version
if: github.event_name == 'release'
run: |
echo "Release Version: $RELEASE_VERSION"
- name: Update FPTN_VERSION in conanfile.py
if: github.event_name == 'release'
run: |
sed -i '' "s/^FPTN_VERSION = \".*\"/FPTN_VERSION = \"$RELEASE_VERSION\"/" conanfile.py
- name: Install dependencies
run: |
conan install . --output-folder=build --build=missing -o with_gui_client=True --settings build_type=Release -s compiler.cppstd=17
- name: Run cpplint
run: |
python3 cpplint.py --recursive --filter=-build/c++17,-whitespace/newline,-readability/braces --counting=total ./src/ ./tests/
- name: Run cppcheck
run: |
cppcheck --error-exitcode=1 --enable=all --check-level=exhaustive --language=c++ --suppress=unusedFunction --inline-suppr --suppress=missingIncludeSystem --suppress=unknownMacro --suppress=unmatchedSuppression -I ./src/fptn-client/ -I ./src/fptn-server/ -I ./src/fptn-passwd/ -I ./src/fptn-client-protocol-lib -I ./src/ ./src/ ./tests/
- name: Run cmake-format
run: |
cmake-format -i CMakeLists.txt src/fptn-client/CMakeLists.txt src/fptn-passwd/CMakeLists.txt src/fptn-server/CMakeLists.txt src/fptn-protocol-lib/CMakeLists.txt depends/cmake/FetchBase64.cmake depends/cmake/FetchLibTunTap.cmake depends/cmake/FetchWintun.cmake
- name: Build
run: |
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
- name: Test
run: |
cd build
ctest -C Release
- name: Build MacOS pkg
run: |
cd build
cmake --build . --target build-pkg
- name: Upload macOS pkg
if: github.event_name == 'release'
uses: AButler/upload-release-assets@v3.0
with:
files: "*.pkg"
repo-token: ${{ secrets.CI_TOKEN }}
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: MacOsAppleSiliconArtifacts
path: |
*.pkg
if-no-files-found: warn
build_windows_AMD64:
runs-on: Windows11_AMD64
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: true
- name: Set env
shell: powershell
if: github.event_name == 'release'
run: |
$releaseVersion = "${{ github.ref_name }}"
echo "RELEASE_VERSION=$releaseVersion" >> $env:GITHUB_ENV
- name: Print Release Version
shell: powershell
if: github.event_name == 'release'
run: |
Write-Output "Release Version: $env:RELEASE_VERSION"
- name: Update FPTN_VERSION in conanfile.py
shell: powershell
if: github.event_name == 'release'
run: |
python.exe deploy\windows\conan-replace-version.py conanfile.py "$env:RELEASE_VERSION"
- name: Install dependencies
shell: powershell
run: |
conan install . --output-folder=build --build=missing -o with_gui_client=True --settings build_type=Release -s compiler.cppstd=17
- name: Run cpplint
run: |
python cpplint.py --recursive --filter=-build/c++17,-whitespace/newline,-readability/braces --counting=total ./src/ ./tests/
- name: Run cppcheck
shell: powershell
run: |
cppcheck --error-exitcode=1 --enable=all --check-level=exhaustive --language=c++ --suppress=unusedFunction --inline-suppr --suppress=missingIncludeSystem --suppress=unknownMacro --suppress=unmatchedSuppression -I ./src/fptn-client/ -I ./src/fptn-server/ -I ./src/fptn-passwd/ -I ./src/fptn-client-protocol-lib -I ./src/ ./src/ ./tests/
- name: Run cmake-format
shell: powershell
run: |
cmake-format -i CMakeLists.txt src/fptn-client/CMakeLists.txt src/fptn-passwd/CMakeLists.txt src/fptn-server/CMakeLists.txt src/fptn-protocol-lib/CMakeLists.txt depends/cmake/FetchBase64.cmake depends/cmake/FetchLibTunTap.cmake depends/cmake/FetchWintun.cmake
- name: Build
shell: powershell
run: |
cd build
cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release
- name: Test
shell: powershell
run: |
cd build
ctest -C Release
- name: Build Windows installer
shell: powershell
run: |
cd build
cmake --build . --config Release --target build-installer
- name: Zip the .exe
shell: powershell
run: |
$exePath = Get-ChildItem -Path "." -Filter "FptnClientInstaller-*-windows-x64_x86.exe" | Select-Object -First 1
Compress-Archive -Path $exePath.FullName -DestinationPath "FptnClientInstaller-$env:RELEASE_VERSION-windows-x64_x86.zip"
- name: Upload Windows installer
if: github.event_name == 'release'
uses: AButler/upload-release-assets@v3.0
with:
files: "*.zip"
repo-token: ${{ secrets.CI_TOKEN }}
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: WindowsArtifacts
path: |
*.zip
if-no-files-found: warn
create_multiarch_manifest:
needs: [build_ubuntu_x86_64, build_ubuntu_arm64]
runs-on: ubuntu-latest
steps:
- name: Get release version
if: github.event_name == 'release'
run: |
RELEASE_VERSION="${GITHUB_REF#refs/*/}"
echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV
echo "Version: $RELEASE_VERSION"
- name: Login to Docker Hub
if: github.event_name == 'release'
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Create multi-arch manifests
if: github.event_name == 'release'
run: |
docker pull --quiet $DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-amd64
docker pull --quiet $DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-arm64
docker manifest rm $DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION 2>/dev/null || true
docker manifest rm $DOCKER_REGISTRY/$IMAGE_NAME:latest 2>/dev/null || true
docker buildx imagetools create \
-t $DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION \
$DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-amd64 \
$DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-arm64
docker buildx imagetools create \
-t "$DOCKER_REGISTRY/$IMAGE_NAME":latest \
$DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-amd64 \
$DOCKER_REGISTRY/$IMAGE_NAME:$RELEASE_VERSION-arm64
================================================
FILE: .gitignore
================================================
.env
.cache/
.vsconan/
Testing/
conan_provider.cmake
.conan_provider.cmake
logs/
*.dmg
*.deb
*.pkg
.vs/
.vscode/
tmp/
keys/
build*/
cmake-build-*/
.idea/
libhv.*
macos-build/
linux-build/
linux-build22/
linux-build24/
CMakeUserPresets.json
.env
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
build/
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
---
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.16)
project(fptn VERSION "${FPTN_VERSION}" LANGUAGES CXX)
# project settings
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (APPLE)
set(CMAKE_CXX_STANDARD 20)
else ()
add_definitions(-DBOOST_ASIO_HAS_CO_AWAIT)
add_definitions(-DBOOST_ASIO_HAS_CO_SPAWN)
add_definitions(-DBOOST_ASIO_HAS_COROUTINES)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument")
endif ()
endif ()
if(MSVC)
add_compile_options("/bigobj")
add_compile_options("/std:c++20")
add_compile_options("/Zc:inline-")
add_compile_options("/permissive-")
add_compile_options("/Zc:__cplusplus")
add_compile_options("/d2SSAOptimizer-")
add_compile_definitions(_WIN32_WINNT=0x0A00) # Windows 10
endif()
# Global project definitions
if (FPTN_BUILD_ONLY_FPTN_LIB)
# build fptnlib without pcappp
set(FPTN_IP_ADDRESS_WITHOUT_PCAP ON)
add_compile_definitions(FPTN_IP_ADDRESS_WITHOUT_PCAP)
else()
set(FPTN_WITH_LIBIDN2 ON)
add_compile_definitions(FPTN_WITH_LIBIDN2)
endif()
add_compile_definitions(FPTN_VERSION=\"${FPTN_VERSION}\")
add_compile_definitions(FPTN_MTU_SIZE=1500)
add_compile_definitions(FPTN_DEFAULT_SNI=\"rutube.ru\")
add_compile_definitions(FPTN_IP_PACKET_MAX_SIZE=1500)
add_compile_definitions(FPTN_ENABLE_PACKET_PADDING=1)
add_compile_definitions(FPTN_PROTOBUF_PROTOCOL_VERSION=0x01)
# client
add_compile_definitions(FPTN_CLIENT_DEFAULT_ADDRESS_IP6=\"fd00::1\")
add_compile_definitions(FPTN_CLIENT_DEFAULT_ADDRESS_IP4=\"10.0.0.1\")
add_compile_definitions(FPTN_CLIENT_DEFAULT_EXCLUDE_NETWORKS=\"10.0.0.0/8,192.168.0.0/16\")
add_compile_definitions(FPTN_CLIENT_DEFAULT_SPLIT_TUNNEL_DOMAINS=\"domain:ru,domain:su,domain:рф,domain:vk.com,domain:yandex.com,domain:userapi.com,domain:yandex.net,domain:clstorage.net\")
add_compile_definitions(FPTN_CLIENT_DEFAULT_BLACKLIST_DOMAINS=\"domain:solovev-live.ru,domain:ria.ru,domain:tass.ru,domain:1tv.ru,domain:ntv.ru,domain:rt.com,domain:lenta.ru\")
# server
add_compile_definitions(FPTN_SERVER_DEFAULT_ADDRESS_IP6=\"fc00:1::1\")
add_compile_definitions(FPTN_SERVER_DEFAULT_NET_ADDRESS_IP6=\"fc00:1::\")
add_compile_definitions(FPTN_SERVER_DEFAULT_ADDRESS_IP4=\"172.20.0.1\")
add_compile_definitions(FPTN_SERVER_DEFAULT_NET_ADDRESS_IP4=\"172.20.0.0\")
# github
add_compile_definitions(FPTN_GITHUB_USERNAME=\"fptn-project\")
add_compile_definitions(FPTN_GITHUB_REPOSITORY=\"fptn\")
add_compile_definitions(FPTN_GITHUB_PAGE_LINK=\"https://storage.googleapis.com/fptn.org/index.html\")
# Boost
add_compile_definitions(BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS)
add_compile_definitions(BOOST_IOSTREAMS_USE_BZIP2)
add_compile_definitions(BOOST_IOSTREAMS_USE_ZLIB)
# Fix boringssl build for windows
add_definitions(-DNOMINMAX)
add_definitions(-DWIN32_LEAN_AND_MEAN)
# Minimize Windows headers and avoid NOMINMAX conflict (needed for BoringSSL)
add_definitions(-DNOMINMAX)
add_definitions(-DWIN32_LEAN_AND_MEAN)
set(FPTN_SERVER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/fptn-server")
# --- depends ---
include(depends/cmake/FetchBase64.cmake)
include(depends/cmake/NtpClient.cmake)
include(depends/cmake/CamouflageTLS.cmake)
if (NOT FPTN_BUILD_ONLY_FPTN_LIB)
if (APPLE OR UNIX)
include(depends/cmake/FetchLibTunTap.cmake)
elseif (WIN32)
include(depends/cmake/FetchWintun.cmake)
else ()
message(FATAL_ERROR "Unsupported platform")
endif ()
endif ()
# --- project ---
set(CMAKE_COMPILE_WARNING_AS_ERROR ON) # check all warnings!
if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
add_compile_options(/W4 /WX)
else ()
add_compile_options(-Wall -Werror -pedantic)
endif ()
# --- clang-tidy setup ---
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# search clang-tidy
find_program(
CLANG_TIDY_EXE
NAMES clang-tidy-22
clang-tidy-21
clang-tidy-20
clang-tidy-19
clang-tidy-18
clang-tidy-17
clang-tidy-16
clang-tidy-15
clang-tidy)
if (CLANG_TIDY_EXE)
execute_process(
COMMAND ${CLANG_TIDY_EXE} --version
OUTPUT_VARIABLE CLANG_TIDY_VERSION_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "[CLANG-TIDY] clang-tidy version: ${CLANG_TIDY_VERSION_STRING}")
if (CLANG_TIDY_VERSION_STRING MATCHES "version ([0-9]+)")
set(CLANG_TIDY_VERSION ${CMAKE_MATCH_1})
if (CLANG_TIDY_VERSION GREATER_EQUAL 20)
message(STATUS "[CLANG-TIDY] clang-tidy ${CLANG_TIDY_VERSION} accepted, enabling")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-extra-arg=-std=c++20")
else ()
message(WARNING "[CLANG-TIDY] clang-tidy version ${CLANG_TIDY_VERSION} is too old (<15), disabling")
endif ()
else ()
message(WARNING "[CLANG-TIDY] Could not parse clang-tidy version, disabling")
endif ()
else ()
message(WARNING "[CLANG-TIDY] clang-tidy not found, skipping clang-tidy checks.")
endif ()
# --- include ---
include_directories(src/)
# --- build ---
add_subdirectory(src/fptn-protocol-lib)
if (NOT FPTN_BUILD_ONLY_FPTN_LIB)
if (APPLE OR UNIX)
add_subdirectory(src/fptn-server)
add_subdirectory(src/fptn-passwd)
endif ()
add_subdirectory(src/fptn-client)
# --- install ---
install(TARGETS fptn-client-cli DESTINATION bin)
if (APPLE OR UNIX)
install(TARGETS fptn-server DESTINATION bin)
install(TARGETS fptn-passwd DESTINATION bin)
endif ()
# --- packaging ---
if (CMAKE_SYSTEM_NAME STREQUAL "Linux") # deb
add_custom_target(
build-deb
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND
bash -c
"${CMAKE_CURRENT_SOURCE_DIR}/deploy/linux/deb/create-client-cli-deb-package.sh ${CMAKE_BINARY_DIR}/src/fptn-client/fptn-client-cli ${FPTN_VERSION}; ${CMAKE_CURRENT_SOURCE_DIR}/deploy/linux/deb/create-server-deb-package.sh ${CMAKE_BINARY_DIR}/src/fptn-server/fptn-server ${CMAKE_BINARY_DIR}/src/fptn-passwd/fptn-passwd ${FPTN_VERSION}"
COMMENT "Building .deb package"
VERBATIM)
if ("${FPTN_BUILD_WITH_GUI_CLIENT}")
add_custom_target(
build-deb-gui
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND
bash -c
"${CMAKE_CURRENT_SOURCE_DIR}/deploy/linux/deb/create-client-gui-deb-package.sh ${CMAKE_BINARY_DIR}/src/fptn-client/fptn-client-gui ${CMAKE_CURRENT_SOURCE_DIR}/deploy/linux/deb/assets/FptnClient512x512.png ${FPTN_VERSION} ${CMAKE_CURRENT_SOURCE_DIR}/deploy/sni"
COMMENT "Building .deb package"
VERBATIM)
endif ()
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") # MacOS
if ("${FPTN_BUILD_WITH_GUI_CLIENT}")
add_custom_target(
build-pkg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND
bash -c
"python3 \"${CMAKE_CURRENT_SOURCE_DIR}/deploy/macos/create-pkg.py\" --fptn-client-cli=\"${CMAKE_BINARY_DIR}/src/fptn-client/fptn-client-cli\" --fptn-client-gui=\"${CMAKE_BINARY_DIR}/src/fptn-client/fptn-client-gui\" --version=\"${FPTN_VERSION}\""
COMMENT "Building .dmg package"
VERBATIM)
endif ()
elseif (WIN32)
if ("${FPTN_BUILD_WITH_GUI_CLIENT}")
add_custom_target(
build-installer
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E echo "Building installer for Windows..."
COMMAND
python ${CMAKE_CURRENT_SOURCE_DIR}/deploy/windows/create-installer.py --wintun-dll=${CMAKE_BINARY_DIR}/wintun/wintun.dll
--fptn-client=${CMAKE_BINARY_DIR}/src/fptn-client/Release/fptn-client-gui.exe
--fptn-client-cli=${CMAKE_BINARY_DIR}/src/fptn-client/Release/fptn-client-cli.exe --output-folder=${CMAKE_CURRENT_SOURCE_DIR}
--version=${FPTN_VERSION}
COMMENT "Building .exe installer"
VERBATIM)
endif ()
endif ()
# --- tests ---
enable_testing()
add_subdirectory(tests)
endif ()
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 Stas Skokov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<H1>FPTN</H1>
<H6>Custom VPN technology</H6>
[\[English\]](README.md)
•
[\[Русский\]](README_RU.md)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/actions/workflows/main.yml)
[](https://github.com/batchar2/fptn/releases)
</div>
---
### Core Features of FPTN
FPTN is a VPN technology engineered from the ground up to provide secure, robust, and censorship-resistant connections capable of bypassing network filtering and deep packet inspection (DPI).
Project website: [https://storage.googleapis.com/fptn.org/index.html](https://storage.googleapis.com/fptn.org/index.html)
Key Technical Features:
1. **L3 Tunnel (Network Layer)**
- **IP Packet Tunneling:** Encapsulates and transmits raw IP packets (IPv4/IPv6) over a secure tunnel to the VPN server.
- **Split Tunneling:** Provides granular control over routing policies. Users can define rules (based on domains or IP networks) to specify which traffic is routed through the VPN tunnel; all other traffic uses the direct internet connection.
- **Server-side NAT:** Implements Network Address Translation (NAT) on the server. Future roadmap includes support for user grouping into virtual LANs (VLANs) for peer-to-peer communication within the VPN.
2. **Traffic Obfuscation and Blocking Evasion**
- **Resistance to active Deep Packet Inspection (DPI):** The server can identify FPTN clients during the TLS handshake by analyzing the session_id (which the FPTN client can set using a special time-based method). If the client is not recognized as an FPTN client, the server acts as a transparent proxy and returns legitimate content for the requested domain.
- The VPN connection is masqueraded as regular HTTPS traffic (a mode for short-lived HTTPS connections is also under development).
- Three implemented methods for bypassing blocks:
- **SNI Spoofing:** A fake domain name is set in the TLS ClientHello packet that initiates the connection. Traffic analysis systems observe a legitimate TLS connection, while the traffic is actually routed to the VPN server.
- **Obfuscation:** The traffic is disguised as an already established TLS session, hiding the initial TLS handshake and preventing detection by DPI systems.
- **Reality Mode with SNI Spoofing:** The client initiates a connection to the VPN server using a spoofed Server Name Indication (SNI), receives a genuine TLS handshake response from the actual (spoofed) website, and then continues data exchange with the VPN server within the same connection.
- The desktop client includes an integrated `SNI scanner utility`.
3. Transport Protocol
- Uses a proprietary transport protocol based on Protocol Buffers (Protobuf) for data exchange between the client and server.
- **Protocol-level padding:** Data packets are padded with random data to randomize traffic patterns and complicate analysis.
- The server provides a **REST API** for client authentication and retrieving specific configuration settings.
4. **Advanced Functionality**
- Built-in filtering of unwanted traffic (e.g., the BitTorrent protocol).
- Per-user bandwidth and traffic control: The server employs a traffic shaper based on the **Leaky Bucket** algorithm, allowing for granular bandwidth policy configuration.
- Support for a multi-server architecture with a single master server that stores all user data and configuration.
- System monitoring via **Prometheus** and visualization dashboards in **Grafana**.
- Ability for users to connect and manage their service via a **Telegram bot**.
5. **Cross-Platform Clients**
- A cross-platform core library, **libfptn**, has been developed for use across various operating systems. It implements the FPTN network protocol, connection management, and data transmission mechanisms for the VPN tunnel.
- **Desktop Clients**: Windows, macOS, Linux — a minimalist client focused on ease of use.
- **Mobile Clients**: Android, iOS (under development).
6. **Simple Token-Based Configuration**
- A **Token** is a specially generated configuration file containing all necessary settings for the system.
- Enables connection to the VPN without manual configuration: the user simply imports the token into the client application to begin using the service.
---
### Demonstration
Download the FPTN client from the [website](http://batchar2.github.io/fptn/) or [GitHub](https://github.com/batchar2/fptn/releases). After downloading, install and launch the client.
The client is a compact application whose icon resides in the system tray.
Simply click the icon to open the context menu.
<img style="max-height: 100px" class="img-center" src="docs/images/macos/en/client.png" alt="Application"/>
Navigate to the "Settings" menu, where you need to add an access token.
Obtain a token by contacting our <a target="_blank" href="https://t.me/fptn_bot">Telegram bot</a>,
<img style="max-height: 200px" class="img-center" src="docs/images/telegram_token_en.png" alt="Settings"/>
Copy the token, click the "Add Token" button, paste it into the form, and save.
<img style="max-height: 250px" class="img-center" src="docs/images/macos/en/settings-2.png" alt="Settings"/>
After this, available servers will appear in the list.
<img style="max-height: 250px" class="img-center" src="docs/images/macos/en/settings-3.png" alt="Settings"/>
Ease of use:
<img style="max-height: 250px" class="img-center" src="docs/images/macos/en/running-client.png" alt="Settings"/>
You can also easily turn your Raspberry Pi or Orange Pi into a WiFi access point and install the FPTN client on it.
In this case, all devices connected to this WiFi network will be able to access the internet, bypassing any restrictions.
[Read more here](https://github.com/batchar2/fptn/blob/master/deploy/linux/wifi/README.md)
<img style="max-height: 350px" class="img-center" src="docs/images/orangepi.jpg" alt="Settings"/>
---
### Installation, Building, and Configuration
<details>
<summary><strong>Installing and Configuring the FPTN Server</strong></summary>
Setting up and running your own FPTN server is done via Docker.
This ensures easy deployment, convenient updates, and environment isolation.
Instructions are available on [DockerHub](https://hub.docker.com/r/fptnvpn/fptn-vpn-server).
You can also deploy your own management and monitoring tools:
- **Telegram bot** – issuing tokens to users [sysadmin-tools/telegram-bot/README.md](sysadmin-tools/telegram-bot/README.md).
- **Grafana + Prometheus** – monitoring server and user status [sysadmin-tools/grafana/README.md](sysadmin-tools/grafana/README.md)
</details>
<details>
<summary>Building the Project from Source</summary>
1. Install required dependencies
- For [Windows](deploy/windows/README.md)
- For [Ubuntu](deploy/linux/deb/README.md)
- For [macOS](deploy/macos/README.md)
2. Install Conan (version 2.24.0):
```bash
pip install conan==2.24.0
```
3. Detect and configure the Conan profile:
```bash
conan profile detect --force
```
4. Install dependencies, build, and install:
*(For debugging and development purposes, use Debug instead of Release.)*
```bash
conan install . --output-folder=build --build=missing -s compiler.cppstd=17 -o with_gui_client=True --settings build_type=Debug
cd build
# Linux & macOS only
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug
# Windows only
cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Debug
cmake --build . --config Debug
ctest
```
5. Building the Installer
*(For debugging and development purposes, use Debug instead of Release.)*
- Windows
```bash
cmake --build . --config Release --target build-installer
```
- Ubuntu
```bash
cmake --build . --config Release --target build-deb-gui
```
- macOS
```bash
cmake --build . --target build-pkg
```
</details>
<details>
<summary>Using CLion IDE for Development</summary>
Run the following command in the project's root folder:
```bash
conan install . --output-folder=cmake-build-debug --build=missing -s compiler.cppstd=17 -o with_gui_client=True --settings build_type=Debug
```
Open the project in CLion. After opening, the Open Project Wizard window will appear automatically. In it, you need to add the following CMake parameter:
```bash
-DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
```
</details>
---
### About the Project
FPTN is developed by a team of volunteers and independent developers.
If you wish to support the project, you can donate via [Boosty](https://boosty.to/fptn). Project sponsors have speed limits removed on our servers and (optionally) have their usernames published in FPTN clients.
Our Telegram chat for users and developers: [FPTN Project](https://t.me/fptn_project)
Join the community and the development team!
---
## Community Tools
The following tools are built and maintained by the community to extend or simplify working with FPTN.
### fptn-manager
A small external management tool built around FPTN, focused on simplifying deployment and common day-to-day administrative tasks.
It is especially useful for users who prefer not to work directly with Docker commands or internal configuration details.
It provides:
- A Docker-based installer
- An interactive CLI for user, password, and token management
- Easier initial setup and repeated operations
Project repository:
https://github.com/FarazFe/fptn-manager
================================================
FILE: README_RU.md
================================================
<div align="center">
<H1>FPTN</H1>
<H6>Custom VPN technology</H6>
[\[English\]](README.md)
•
[\[Русский\]](README_RU.md)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/releases)
[](https://github.com/batchar2/fptn/actions/workflows/main.yml)
[](https://github.com/batchar2/fptn/releases)
</div>
---
### Основные возможности FPTN
FPTN — это VPN-технология, созданная с нуля для безопасного и устойчивого к блокировкам соединения, позволяющего обходить цензуру и сетевую фильтрацию.
Сайт проекта: [https://storage.googleapis.com/fptn.org/index.html](https://storage.googleapis.com/fptn.org/index.html)
Основные возможности включают:
1. **L3-туннель (IP-уровень)**
- Передача IP-пакетов (IPv4 и IPv6) через VPN-туннель до сервера.
- Поддержка **split-tunneling** — возможность направлять через VPN только определённый трафик, а остальной трафик идёт напрямую. Позволяет гибко настраивать политику маршрутизации на основе указания правил для доменов и сетей.
- На серверной стороне реализован **NAT**. В дальнейшем планируется поддержка объединения пользователей в группы с созданием виртуальных локальных сетей для совместного взаимодействия.
2. **Маскировка трафика и обход блокировок**
- **Устойчивость к активному DPI**: сервер способен идентифицировать клиентов по TLS-handshake, анализируя session_id (значение которого умеет устанавливать FPTN-клиент по специальному методу от времени). Если определяется, что клиент не является FPTN-клиентом, сервер возвращает легитимный контент запрашиваемого домена, выступая в роли прозрачного прокси.
- VPN-соединение маскируется под обычный HTTPS-трафик (еще в разработке — режим короткоживущих HTTPS-соединений).
- Реализованы три метода обхода блокировок:
1. **Подмена SNI**: в инициирующем соединение TLS-пакете устанавливается поддельный домен. Системы анализа трафика видят легитимное соединение, а на самом деле трафик направляется на VPN-сервер.
2. **Обфускация**: трафик выглядит как уже установленная TLS-сессия, скрывая TLS-handshake и предотвращая детектирование DPI.
3. **Reality Mode + SNI**: клиент инициирует соединение с VPN-сервером с подменой SNI, получает реальный TLS-handshake от настоящего сайта, после чего в том же соединении продолжается обмен данными с VPN-сервером.
- В десктопной версии клиента реализован `сканнер SNI`.
3. **Транспортный протокол**
- Используется собственный транспортный протокол на основе **Protobuf** для передачи данных между клиентом и сервером.
- **Padding на уровне протокола**: пакеты данных дополняются случайными данными для рандомизации трафика и затруднения анализа.
- Сервер предоставляет **REST API** для авторизации клиентов и получения специальных настроек.
4. **Специальные возможности**
- Встроенная фильтрация нежелательного трафика (например, протокол BitTorrent).
- Контроль скорости и трафика каждого пользователя: сервер включает шейпер на основе алгоритма **Leaky Bucket**, что позволяет гибко настраивать политику скорости.
- Поддержка многосерверной архитектуры с одним мастер-сервером, где хранится вся информация о пользователях.
- Мониторинг работы системы через **Prometheus** и визуализация в **Grafana**.
- Возможность подключения пользователей через **Telegram-бота**.
5. **Кроссплатформенные клиенты**
- Разработана кроссплатформенная библиотека **`libfptn`**, которая может использоваться на различных операционных системах. Внутри реализованы сетевой протокол FPTN, управление соединением и механизмы передачи данных через VPN-туннель.
- **Десктоп:** Windows, macOS, Linux — минималистичный клиент с акцентом на простоту использования.
- **Мобильные устройства:** Android, iOS (в разработке).
6. **Простая настройка через токен**
- **Токен** — это специально сгенерированный конфигурационный файл, который содержит все необходимые настройки системы.
- Позволяет подключаться к VPN без ручной конфигурации и лишних действий: достаточно добавить токен в клиент, чтобы начать работу.
---
### Демонстрация работы
*🍏🍎Пользователям MacOS рекомендуется ознакомиться с [руководством по установке для macOS](docs/macos/README.md), так как в macOS присутствуют дополнительные меры безопасности, которые могут потребовать особых действий.*
Скачайте клиент FPTN с [веб-сайта](http://batchar2.github.io/fptn/) или [GitHub](https://github.com/batchar2/fptn/releases). После скачивания установите и запустите клиент.
Клиент представляет собой компактное приложение, значок которого находится в системном трее.
Просто нажмите на значок, чтобы открыть контекстное меню.
<img style="max-height: 100px" class="img-center" src="docs/images/macos/ru/client.png" alt="Приложение"/>
Перейдите в меню "Настройки", где необходимо добавить токен доступа.
Получите токен, обратившись к нашему <a target="_blank" href="https://t.me/fptn_bot">Telegram-боту</a>,
<img style="max-height: 200px" class="img-center" src="docs/images/telegram_token_ru.png" alt="Настройки"/>
Скопируйте токен, нажмите кнопку "Добавить токен", вставьте его в форму и сохраните.
<img style="max-height: 250px" class="img-center" src="docs/images/macos/ru/settings-2.png" alt="Настройки"/>
После этого в списке появятся доступные серверы.
<img style="max-height: 250px" class="img-center" src="docs/images/macos/ru/settings-3.png" alt="Настройки"/>
Простота использования:
<img style="max-height: 250px" class="img-center" src="docs/images/macos/ru/running-client.png" alt="Настройки"/>
Вы также можете легко превратить свой Raspberry Pi или Orange Pi в точку доступа WiFi и установить на него клиент FPTN.
В этом случае все устройства, подключенные к этой WiFi-сети, смогут выходить в интернет, обходя любые ограничения.
[Подробнее читайте здесь](https://github.com/batchar2/fptn/blob/master/deploy/linux/wifi/README.md)
<img style="max-height: 350px" class="img-center" src="docs/images/orangepi.jpg" alt="Настройки"/>
---
### Установка, сборка и настройка
<details>
<summary><strong>Установка и настройка FPTN сервера</strong></summary>
Настройка и запуск собственного сервера FPTN выполняются через Docker.
Это обеспечивает простое развертывание, удобное обновление и изоляцию окружения.
Инструкция доступна в [DockerHub](https://hub.docker.com/r/fptnvpn/fptn-vpn-server).
Так же вы можете развернуть собственные инструменты для управления и мониторинга:
- **Telegram-бот** — выдача токенов пользователмм [sysadmin-tools/telegram-bot/README.md](sysadmin-tools/telegram-bot/README.md).
- **Grafana + Prometheus** — мониторинг состояния серверов и пользователей [sysadmin-tools/grafana/README.md](sysadmin-tools/grafana/README.md)
</details>
<details>
<summary>Сборка проекта из исходников</summary>
1. Установите требуемые зависимости
- Для [Windows](deploy/windows/README.md)
- Для [Ubuntu](deploy/linux/deb/README.md)
- Для [macOS](deploy/macos/README.md)
2. Установите Conan (версия 2.24.0):
```bash
pip install conan==2.24.0
```
3. Определите и настройте профиль Conan:
```bash
conan profile detect --force
```
4. Установите зависимости, выполните сборку и установку:
```bash
conan install . --output-folder=build --build=missing -s compiler.cppstd=17 -o with_gui_client=True --settings build_type=Release
cd build
# Только Linux & macOS
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug
# Только для Windows
cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Debug
cmake --build . --config Release
ctest
````
5. Сборка установщика
- Windows
```bash
cmake --build . --config Release --target build-installer
```
- Ubuntu
```bash
cmake --build . --config Release --target build-deb-gui
```
- macOS
```bash
cmake --build . --target build-pkg
```
</details>
<details>
<summary>Использование CLion IDE для разработки</summary>
Выполните следующую команду в корневой папке проекта:
```bash
conan install . --output-folder=cmake-build-debug --build=missing -s compiler.cppstd=17 -o with_gui_client=True --settings build_type=Debug
```
Откройте проект в CLion. После открытия автоматически появится окно **Open Project Wizard**. В нём необходимо добавить следующий параметр CMake:
```bash
-DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
```
</details>
---
### О проекте
FPTN развивается командой волонтёров и независимых разработчиков.
Если вы хотите поддержать проект, вы можете оформить донат на [Boosty](https://boosty.to/fptn). Спонсорам проекта снимаем ограничения скорости на наших серверах и (по желанию) публикуем их ники в FPTN-клиентах.
Наш Telegram-чат для пользователей и разработчиков [FPTN Project](https://t.me/fptn_project)
Присоединяйтесь к сообществу и команде разработчиков!
---
## Инструменты сообщества
Следующие инструменты разработаны и поддерживаются сообществом для расширения возможностей или упрощения работы с FPTN.
### fptn-manager
Небольшой внешний инструмент управления, построенный вокруг FPTN и ориентированный на упрощение развёртывания и повседневных административных задач.
Особенно полезен для пользователей, которые не хотят работать напрямую с Docker-командами или внутренними настройками конфигурации.
Возможности:
- Установщик на базе Docker
- Интерактивный CLI для управления пользователями, паролями и токенами
- Упрощённая первичная настройка и повторяющиеся операции
Репозиторий проекта: https://github.com/FarazFe/fptn-manager
================================================
FILE: conanfile.py
================================================
import os
import subprocess
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake
from conan.tools.files import copy
# CI will replace this automatically
FPTN_VERSION = "0.0.0"
class FPTN(ConanFile):
name = "fptn"
version = FPTN_VERSION
requires = (
"argparse/3.2",
"boost/1.90.0",
"brotli/1.2.0",
"cpp-httplib/0.30.0",
"fmt/12.1.0",
"jwt-cpp/0.7.1",
"nlohmann_json/3.12.0",
"protobuf/5.29.3",
"re2/20251105",
"spdlog/1.17.0",
"zlib/1.3.1",
)
settings = (
"os",
"arch",
"compiler",
"build_type",
)
generators = ("CMakeDeps",)
options = {
"setup": [True, False],
"with_gui_client": [True, False],
"build_only_fptn_lib": [True, False],
}
default_options = {
# --- program ---
"setup": False,
"with_gui_client": False,
"build_only_fptn_lib": False,
# -- depends --
"*:fPIC": True,
"*:shared": False,
# --- protobuf options ---
"protobuf/*:lite": True,
"protobuf/*:upb": False,
"protobuf/*:with_rtti": False,
"protobuf/*:with_zlib": False,
"protobuf/*:upb": False,
"protobuf/*:debug_suffix": False,
# --- boost options ---
"boost/*:without_atomic": False,
"boost/*:without_system": False,
"boost/*:without_process": False,
"boost/*:without_exception": False,
"boost/*:without_container": False,
"boost/*:without_filesystem": False,
"boost/*:without_coroutine": False,
"boost/*:without_context": False,
"boost/*:without_timer": False,
"boost/*:without_json": False,
"boost/*:without_random": False,
"boost/*:without_iostreams": False,
"boost/*:without_chrono": False,
"boost/*:without_regex": False,
"boost/*:without_zlib": False,
"boost/*:without_nowide": False,
"boost/*:without_locale": False,
"boost/*:without_thread": False,
"boost/*:without_python": True,
"boost/*:without_contract": True,
"boost/*:without_fiber": True,
"boost/*:without_graph": True,
"boost/*:without_graph_parallel": True,
"boost/*:without_log": True,
"boost/*:without_math": True,
"boost/*:without_mpi": True,
"boost/*:without_program_options": True,
"boost/*:without_serialization": True,
"boost/*:without_stacktrace": True,
"boost/*:without_test": True,
"boost/*:without_url": True,
"boost/*:without_type_erasure": True,
"boost/*:without_wave": True,
# --- Qt ---
"qt/*:shared": True,
"qt/*:openssl": False,
"qt/*:qttools": True,
"qt/*:with_harfbuzz": False,
"qt/*:with_mysql": False,
"qt/*:with_pq": False,
"qt/*:with_odbc": False,
"qt/*:with_zstd": False,
"qt/*:with_brotli": False,
"qt/*:with_dbus": False,
"qt/*:with_openal": False,
"qt/*:with_gstreamer": False,
"qt/*:with_pulseaudio": False,
# --- prometheuscpp dependency ---
"prometheus-cpp/*:with_compression": False,
"prometheus-cpp/*:with_push": False,
"civetweb/*:with_ssl": False,
"civetweb/*:disable_werror": True,
# --- freetype ---
"freetype/*:with_brotli": False,
}
exports_sources = (
"CMakeLists.txt",
"src/*",
"depends/*",
"tests/*",
)
def requirements(self):
self._register_local_recipe("boringssl", "openssl", "boringssl", True, False)
if self.options.with_gui_client:
self.requires("qt/6.7.3")
if self.settings.os != "Windows":
self.requires("meson/1.10.0", override=True, force=True)
if not self.options.build_only_fptn_lib:
self.requires("libidn2/2.3.8")
self.requires("prometheus-cpp/1.3.0")
# pcap++ does not support iOS and Android.
# Since libfptn is built as a detached part of the whole project, we don't use pcap++ in that case.
self.requires("pcapplusplus/25.05")
def build_requirements(self):
self.build_requires("cmake/3.22.0", override=True)
self.tool_requires("protobuf/5.29.3")
self.test_requires("gtest/1.17.0")
if self.settings.os != "Windows":
self.build_requires("meson/1.10.0", override=True)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["FPTN_VERSION"] = FPTN_VERSION
if self.options.with_gui_client:
tc.variables["FPTN_BUILD_WITH_GUI_CLIENT"] = "True"
if self.options.build_only_fptn_lib:
tc.variables["FPTN_BUILD_ONLY_FPTN_LIB"] = "True"
# setup protobuf compiler
protobuf_build = self.dependencies.build["protobuf"]
protoc_path = os.path.join(protobuf_build.package_folder, "bin", "protoc")
tc.cache_variables["Protobuf_PROTOC_EXECUTABLE"] = protoc_path
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
if self.options.build_only_fptn_lib:
copy(
self,
"*.h",
src=os.path.join(self.source_folder, "src", "fptn-protocol-lib"),
dst=os.path.join(self.package_folder, "include", "fptn"),
)
copy(
self,
"*.h",
src=os.path.join(self.source_folder, "src", "common"),
dst=os.path.join(self.package_folder, "include", "fptn", "common"),
)
copy(
self,
"*.h",
src=os.path.join(self.build_folder, "src", "fptn-protocol-lib", "protobuf"),
dst=os.path.join(self.package_folder, "include", "fptn", "protobuf"),
)
# copy lib
copy(
self,
"*.a",
src=os.path.join(self.build_folder, "src", "fptn-protocol-lib"),
dst=os.path.join(self.package_folder, "lib"),
)
copy(
self,
"*.lib",
src=os.path.join(self.build_folder, "src", "fptn-protocol-lib"),
dst=os.path.join(self.package_folder, "lib"),
)
ntp_client_build_include = os.path.join(self.build_folder, "_deps", "ntp_client-src", "include")
# copy NTP depends
if os.path.exists(ntp_client_build_include):
copy(
self,
"*.h",
src=ntp_client_build_include,
dst=os.path.join(self.package_folder, "include", "ntp_client"),
)
ntp_client_lib_src = os.path.join(self.build_folder, "_deps", "ntp_client-build")
if os.path.exists(ntp_client_lib_src):
copy(
self,
"*.a",
src=ntp_client_lib_src,
dst=os.path.join(self.package_folder, "lib"),
)
copy(
self,
"*.lib",
src=ntp_client_lib_src,
dst=os.path.join(self.package_folder, "lib"),
)
# copy camouflage-tls depends
camouflage_tls_build_include = os.path.join(self.build_folder, "_deps", "camouflagetls-src", "include")
if os.path.exists(camouflage_tls_build_include):
copy(
self,
"*.h",
src=camouflage_tls_build_include,
dst=os.path.join(self.package_folder, "include", "camouflage"),
)
camouflage_tls_lib_src = os.path.join(self.build_folder, "_deps", "camouflagetls-build")
if os.path.exists(camouflage_tls_lib_src):
copy(
self,
"*.a",
src=camouflage_tls_lib_src,
dst=os.path.join(self.package_folder, "lib"),
)
copy(
self,
"*.lib",
src=camouflage_tls_lib_src,
dst=os.path.join(self.package_folder, "lib"),
)
def package_info(self):
if self.options.build_only_fptn_lib:
self.cpp_info.libs = [
"fptn-protocol-lib_static",
"ntp_client",
]
self.cpp_info.includedirs = ["include"]
self.cpp_info.libdirs = ["lib"]
self.cpp_info.set_property("cmake_file_name", "fptn")
self.cpp_info.set_property("cmake_target_name", "fptn::fptn")
self.cpp_info.set_property("cmake_find_mode", "both")
# Add depends
self.cpp_info.requires = [
"argparse::argparse",
"cpp-httplib::cpp-httplib",
"boost::boost",
"fmt::fmt",
"jwt-cpp::jwt-cpp",
"nlohmann_json::nlohmann_json",
"protobuf::protobuf",
"spdlog::spdlog",
"zlib::zlib",
"re2::re2",
"brotli::brotli",
]
if self.settings.os == "iOS":
self.cpp_info.frameworks = ["Security", "CFNetwork", "SystemConfiguration"]
self.cpp_info.system_libs = ["resolv"]
def config_options(self):
if self.settings.os == "Windows":
self.options.rm_safe("fPIC")
if self.settings.os in ["iOS", "Android"] or self.options.build_only_fptn_lib:
self.options["boost"].without_process = True
def export(self):
copy(self, f"*", src=self.recipe_folder, dst=self.export_folder)
def _register_local_recipe(self, recipe, name, version, override=False, force=False):
script_dir = os.path.dirname(os.path.abspath(__file__))
recipe_rel_path = os.path.join(script_dir, ".conan", "recipes", recipe)
subprocess.run(
[
"conan",
"export",
recipe_rel_path,
f"--name={name}",
f"--version={version}",
"--user=local",
"--channel=local",
],
check=True,
)
self.requires(f"{name}/{version}@local/local", override=override, force=force)
================================================
FILE: cpplint.py
================================================
#!/usr/bin/env python
#
# Copyright (c) 2009 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Does google-lint on c++ files.
The goal of this script is to identify places in the code that *may*
be in non-compliance with google style. It does not attempt to fix
up these problems -- the point is to educate. It does also not
attempt to find all problems, or to ensure that everything it does
find is legitimately a problem.
In particular, we can get very confused by /* and // inside strings!
We do a small hack, which is to ignore //'s with "'s after them on the
same line, but it is far from perfect (in either direction).
"""
from __future__ import annotations # PEP 604 not in 3.9
import codecs
import collections
import copy
import getopt
import glob
import itertools
import math # for log
import os
import re
import string
import sys
import sysconfig
import unicodedata
import xml.etree.ElementTree
# if empty, use defaults
_valid_extensions: set[str] = set()
__VERSION__ = "2.0.2"
_USAGE = """
Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit|sed|gsed]
[--filter=-x,+y,...]
[--counting=total|toplevel|detailed] [--root=subdir]
[--repository=path]
[--linelength=digits] [--headers=x,y,...]
[--recursive]
[--exclude=path]
[--extensions=hpp,cpp,...]
[--includeorder=default|standardcfirst]
[--config=filename]
[--quiet]
[--version]
<file> [file] ...
Style checker for C/C++ source files.
This is a fork of the Google style checker with minor extensions.
The style guidelines this tries to follow are those in
https://google.github.io/styleguide/cppguide.html
Every problem is given a confidence score from 1-5, with 5 meaning we are
certain of the problem, and 1 meaning it could be a legitimate construct.
This will miss some errors, and is not a substitute for a code review.
To suppress false-positive errors of certain categories, add a
'NOLINT(category[, category...])' comment to the line. NOLINT or NOLINT(*)
suppresses errors of all categories on that line. To suppress categories
on the next line use NOLINTNEXTLINE instead of NOLINT. To suppress errors in
a block of code 'NOLINTBEGIN(category[, category...])' comment to a line at
the start of the block and to end the block add a comment with 'NOLINTEND'.
NOLINT blocks are inclusive so any statements on the same line as a BEGIN
or END will have the error suppression applied.
The files passed in will be linted; at least one file must be provided.
Default linted extensions are %s.
Other file types will be ignored.
Change the extensions with the --extensions flag.
Flags:
output=emacs|eclipse|vs7|junit|sed|gsed
By default, the output is formatted to ease emacs parsing. Visual Studio
compatible output (vs7) may also be used. Further support exists for
eclipse (eclipse), and JUnit (junit). XML parsers such as those used
in Jenkins and Bamboo may also be used.
The sed format outputs sed commands that should fix some of the errors.
Note that this requires gnu sed. If that is installed as gsed on your
routing (common e.g. on macOS with homebrew) you can use the gsed output
format. Sed commands are written to stdout, not stderr, so you should be
able to pipe output straight to a shell to run the fixes.
verbose=#
Specify a number 0-5 to restrict errors to certain verbosity levels.
Errors with lower verbosity levels have lower confidence and are more
likely to be false positives.
quiet
Don't print anything if no errors are found.
filter=-x,+y,...
Specify a comma-separated list of category-filters to apply: only
error messages whose category names pass the filters will be printed.
(Category names are printed with the message and look like
"[whitespace/indent]".) Filters are evaluated left to right.
"-FOO" means "do not print categories that start with FOO".
"+FOO" means "do print categories that start with FOO".
Examples: --filter=-whitespace,+whitespace/braces
--filter=-whitespace,-runtime/printf,+runtime/printf_format
--filter=-,+build/include_what_you_use
To see a list of all the categories used in cpplint, pass no arg:
--filter=
Filters can directly be limited to files and also line numbers. The
syntax is category:file:line , where line is optional. The filter limitation
works for both + and - and can be combined with ordinary filters:
Examples: --filter=-whitespace:foo.h,+whitespace/braces:foo.h
--filter=-whitespace,-runtime/printf:foo.h:14,+runtime/printf_format:foo.h
--filter=-,+build/include_what_you_use:foo.h:321
counting=total|toplevel|detailed
The total number of errors found is always printed. If
'toplevel' is provided, then the count of errors in each of
the top-level categories like 'build' and 'whitespace' will
also be printed. If 'detailed' is provided, then a count
is provided for each category like 'legal/copyright'.
repository=path
The top level directory of the repository, used to derive the header
guard CPP variable. By default, this is determined by searching for a
path that contains .git, .hg, or .svn. When this flag is specified, the
given path is used instead. This option allows the header guard CPP
variable to remain consistent even if members of a team have different
repository root directories (such as when checking out a subdirectory
with SVN). In addition, users of non-mainstream version control systems
can use this flag to ensure readable header guard CPP variables.
Examples:
Assuming that Alice checks out ProjectName and Bob checks out
ProjectName/trunk and trunk contains src/chrome/ui/browser.h, then
with no --repository flag, the header guard CPP variable will be:
Alice => TRUNK_SRC_CHROME_BROWSER_UI_BROWSER_H_
Bob => SRC_CHROME_BROWSER_UI_BROWSER_H_
If Alice uses the --repository=trunk flag and Bob omits the flag or
uses --repository=. then the header guard CPP variable will be:
Alice => SRC_CHROME_BROWSER_UI_BROWSER_H_
Bob => SRC_CHROME_BROWSER_UI_BROWSER_H_
root=subdir
The root directory used for deriving header guard CPP variable.
This directory is relative to the top level directory of the repository
which by default is determined by searching for a directory that contains
.git, .hg, or .svn but can also be controlled with the --repository flag.
If the specified directory does not exist, this flag is ignored.
Examples:
Assuming that src is the top level directory of the repository (and
cwd=top/src), the header guard CPP variables for
src/chrome/browser/ui/browser.h are:
No flag => CHROME_BROWSER_UI_BROWSER_H_
--root=chrome => BROWSER_UI_BROWSER_H_
--root=chrome/browser => UI_BROWSER_H_
--root=.. => SRC_CHROME_BROWSER_UI_BROWSER_H_
linelength=digits
This is the allowed line length for the project. The default value is
80 characters.
Examples:
--linelength=120
recursive
Search for files to lint recursively. Each directory given in the list
of files to be linted is replaced by all files that descend from that
directory. Files with extensions not in the valid extensions list are
excluded.
exclude=path
Exclude the given path from the list of files to be linted. Relative
paths are evaluated relative to the current directory and shell globbing
is performed. This flag can be provided multiple times to exclude
multiple files.
Examples:
--exclude=one.cc
--exclude=src/*.cc
--exclude=src/*.cc --exclude=test/*.cc
extensions=extension,extension,...
The allowed file extensions that cpplint will check
Examples:
--extensions=%s
includeorder=default|standardcfirst
For the build/include_order rule, the default is to blindly assume angle
bracket includes with file extension are c-routing-headers (default),
even knowing this will have false classifications.
The default is established at google.
standardcfirst means to instead use an allow-list of known c headers and
treat all others as separate group of "other routing headers". The C headers
included are those of the C-standard lib and closely related ones.
config=filename
Search for config files with the specified name instead of CPPLINT.cfg
headers=x,y,...
The header extensions that cpplint will treat as .h in checks. Values are
automatically added to --extensions list.
(by default, only files with extensions %s will be assumed to be headers)
Examples:
--headers=%s
--headers=hpp,hxx
--headers=hpp
cpplint.py supports per-directory configurations specified in CPPLINT.cfg
files. CPPLINT.cfg file can contain a number of key=value pairs.
Currently the following options are supported:
set noparent
filter=+filter1,-filter2,...
exclude_files=regex
linelength=80
root=subdir
headers=x,y,...
"set noparent" option prevents cpplint from traversing directory tree
upwards looking for more .cfg files in parent directories. This option
is usually placed in the top-level project directory.
The "filter" option is similar in function to --filter flag. It specifies
message filters in addition to the |_DEFAULT_FILTERS| and those specified
through --filter command-line flag.
"exclude_files" allows to specify a regular expression to be matched against
a file name. If the expression matches, the file is skipped and not run
through the linter.
"linelength" allows to specify the allowed line length for the project.
The "root" option is similar in function to the --root flag (see example
above). Paths are relative to the directory of the CPPLINT.cfg.
The "headers" option is similar in function to the --headers flag
(see example above).
CPPLINT.cfg has an effect on files in the same directory and all
sub-directories, unless overridden by a nested configuration file.
Example file:
filter=-build/include_order,+build/include_alpha
exclude_files=.*\\.cc
The above example disables build/include_order warning and enables
build/include_alpha as well as excludes all .cc from being
processed by linter, in the current directory (where the .cfg
file is located) and all sub-directories.
"""
# We categorize each error message we print. Here are the categories.
# We want an explicit list so we can list them all in cpplint --filter=.
# If you add a new error message with a new category, add it to the list
# here! cpplint_unittest.py should tell you if you forget to do this.
_ERROR_CATEGORIES = [
"build/c++11",
"build/c++17",
"build/deprecated",
"build/endif_comment",
"build/explicit_make_pair",
"build/forward_decl",
"build/header_guard",
"build/include",
"build/include_subdir",
"build/include_alpha",
"build/include_order",
"build/include_what_you_use",
"build/namespaces_headers",
"build/namespaces_literals",
"build/namespaces",
"build/printf_format",
"build/storage_class",
"legal/copyright",
"readability/alt_tokens",
"readability/braces",
"readability/casting",
"readability/check",
"readability/constructors",
"readability/fn_size",
"readability/inheritance",
"readability/multiline_comment",
"readability/multiline_string",
"readability/namespace",
"readability/nolint",
"readability/nul",
"readability/todo",
"readability/utf8",
"runtime/arrays",
"runtime/casting",
"runtime/explicit",
"runtime/int",
"runtime/init",
"runtime/invalid_increment",
"runtime/member_string_references",
"runtime/memset",
"runtime/operator",
"runtime/printf",
"runtime/printf_format",
"runtime/references",
"runtime/string",
"runtime/threadsafe_fn",
"runtime/vlog",
"whitespace/blank_line",
"whitespace/braces",
"whitespace/comma",
"whitespace/comments",
"whitespace/empty_conditional_body",
"whitespace/empty_if_body",
"whitespace/empty_loop_body",
"whitespace/end_of_line",
"whitespace/ending_newline",
"whitespace/forcolon",
"whitespace/indent",
"whitespace/indent_namespace",
"whitespace/line_length",
"whitespace/newline",
"whitespace/operators",
"whitespace/parens",
"whitespace/semicolon",
"whitespace/tab",
"whitespace/todo",
]
# keywords to use with --outputs which generate stdout for machine processing
_MACHINE_OUTPUTS = ["junit", "sed", "gsed"]
# These error categories are no longer enforced by cpplint, but for backwards-
# compatibility they may still appear in NOLINT comments.
_LEGACY_ERROR_CATEGORIES = [
"build/class",
"readability/streams",
"readability/function",
]
# These prefixes for categories should be ignored since they relate to other
# tools which also use the NOLINT syntax, e.g. clang-tidy.
_OTHER_NOLINT_CATEGORY_PREFIXES = [
"clang-analyzer-",
"abseil-",
"altera-",
"android-",
"boost-",
"bugprone-",
"cert-",
"concurrency-",
"cppcoreguidelines-",
"darwin-",
"fuchsia-",
"google-",
"hicpp-",
"linuxkernel-",
"llvm-",
"llvmlibc-",
"misc-",
"modernize-",
"mpi-",
"objc-",
"openmp-",
"performance-",
"portability-",
"readability-",
"zircon-",
]
# The default state of the category filter. This is overridden by the --filter=
# flag. By default all errors are on, so only add here categories that should be
# off by default (i.e., categories that must be enabled by the --filter= flags).
# All entries here should start with a '-' or '+', as in the --filter= flag.
_DEFAULT_FILTERS = [
"-build/include_alpha",
"-readability/fn_size",
"-runtime/references",
]
# The default list of categories suppressed for C (not C++) files.
_DEFAULT_C_SUPPRESSED_CATEGORIES = [
"readability/casting",
]
# The default list of categories suppressed for Linux Kernel files.
_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [
"whitespace/tab",
]
# We used to check for high-bit characters, but after much discussion we
# decided those were OK, as long as they were in UTF-8 and didn't represent
# hard-coded international strings, which belong in a separate i18n file.
# C++ headers
_CPP_HEADERS = frozenset(
[
# Legacy
"algobase.h",
"algo.h",
"alloc.h",
"builtinbuf.h",
"bvector.h",
# 'complex.h', collides with System C header "complex.h" since C11
"defalloc.h",
"deque.h",
"editbuf.h",
"fstream.h",
"function.h",
"hash_map",
"hash_map.h",
"hash_set",
"hash_set.h",
"hashtable.h",
"heap.h",
"indstream.h",
"iomanip.h",
"iostream.h",
"istream.h",
"iterator.h",
"list.h",
"map.h",
"multimap.h",
"multiset.h",
"ostream.h",
"pair.h",
"parsestream.h",
"pfstream.h",
"procbuf.h",
"pthread_alloc",
"pthread_alloc.h",
"rope",
"rope.h",
"ropeimpl.h",
"set.h",
"slist",
"slist.h",
"stack.h",
"stdiostream.h",
"stl_alloc.h",
"stl_relops.h",
"streambuf.h",
"stream.h",
"strfile.h",
"strstream.h",
"tempbuf.h",
"tree.h",
"type_traits.h",
"vector.h",
# C++ library headers
"algorithm",
"array",
"atomic",
"bitset",
"chrono",
"codecvt",
"complex",
"condition_variable",
"deque",
"exception",
"forward_list",
"fstream",
"functional",
"future",
"initializer_list",
"iomanip",
"ios",
"iosfwd",
"iostream",
"istream",
"iterator",
"limits",
"list",
"locale",
"map",
"memory",
"mutex",
"new",
"numeric",
"ostream",
"queue",
"random",
"ratio",
"regex",
"scoped_allocator",
"set",
"sstream",
"stack",
"stdexcept",
"streambuf",
"string",
"strstream",
"system_error",
"thread",
"tuple",
"typeindex",
"typeinfo",
"type_traits",
"unordered_map",
"unordered_set",
"utility",
"valarray",
"vector",
# C++14 headers
"shared_mutex",
# C++17 headers
"any",
"charconv",
"codecvt",
"execution",
"filesystem",
"memory_resource",
"optional",
"string_view",
"variant",
# C++20 headers
"barrier",
"bit",
"compare",
"concepts",
"coroutine",
"format",
"latch",
"numbers",
"ranges",
"semaphore",
"source_location",
"span",
"stop_token",
"syncstream",
"version",
# C++23 headers
"expected",
"flat_map",
"flat_set",
"generator",
"mdspan",
"print",
"spanstream",
"stacktrace",
"stdfloat",
# C++ headers for C library facilities
"cassert",
"ccomplex",
"cctype",
"cerrno",
"cfenv",
"cfloat",
"cinttypes",
"ciso646",
"climits",
"clocale",
"cmath",
"csetjmp",
"csignal",
"cstdalign",
"cstdarg",
"cstdbool",
"cstddef",
"cstdint",
"cstdio",
"cstdlib",
"cstring",
"ctgmath",
"ctime",
"cuchar",
"cwchar",
"cwctype",
]
)
# C headers
_C_HEADERS = frozenset(
[
# System C headers
"assert.h",
"complex.h",
"ctype.h",
"errno.h",
"fenv.h",
"float.h",
"inttypes.h",
"iso646.h",
"limits.h",
"locale.h",
"math.h",
"setjmp.h",
"signal.h",
"stdalign.h",
"stdarg.h",
"stdatomic.h",
"stdbool.h",
"stddef.h",
"stdint.h",
"stdio.h",
"stdlib.h",
"stdnoreturn.h",
"string.h",
"tgmath.h",
"threads.h",
"time.h",
"uchar.h",
"wchar.h",
"wctype.h",
# C23 headers
"stdbit.h",
"stdckdint.h",
# additional POSIX C headers
"aio.h",
"arpa/inet.h",
"cpio.h",
"dirent.h",
"dlfcn.h",
"fcntl.h",
"fmtmsg.h",
"fnmatch.h",
"ftw.h",
"glob.h",
"grp.h",
"iconv.h",
"langinfo.h",
"libgen.h",
"monetary.h",
"mqueue.h",
"ndbm.h",
"net/if.h",
"netdb.h",
"netinet/in.h",
"netinet/tcp.h",
"nl_types.h",
"poll.h",
"pthread.h",
"pwd.h",
"regex.h",
"sched.h",
"search.h",
"semaphore.h",
"setjmp.h",
"signal.h",
"spawn.h",
"strings.h",
"stropts.h",
"syslog.h",
"tar.h",
"termios.h",
"trace.h",
"ulimit.h",
"unistd.h",
"utime.h",
"utmpx.h",
"wordexp.h",
# additional GNUlib headers
"a.out.h",
"aliases.h",
"alloca.h",
"ar.h",
"argp.h",
"argz.h",
"byteswap.h",
"crypt.h",
"endian.h",
"envz.h",
"err.h",
"error.h",
"execinfo.h",
"fpu_control.h",
"fstab.h",
"fts.h",
"getopt.h",
"gshadow.h",
"ieee754.h",
"ifaddrs.h",
"libintl.h",
"mcheck.h",
"mntent.h",
"obstack.h",
"paths.h",
"printf.h",
"pty.h",
"resolv.h",
"shadow.h",
"sysexits.h",
"ttyent.h",
# Additional linux glibc headers
"dlfcn.h",
"elf.h",
"features.h",
"gconv.h",
"gnu-versions.h",
"lastlog.h",
"libio.h",
"link.h",
"malloc.h",
"memory.h",
"netash/ash.h",
"netatalk/at.h",
"netax25/ax25.h",
"neteconet/ec.h",
"netipx/ipx.h",
"netiucv/iucv.h",
"netpacket/packet.h",
"netrom/netrom.h",
"netrose/rose.h",
"nfs/nfs.h",
"nl_types.h",
"nss.h",
"re_comp.h",
"regexp.h",
"sched.h",
"sgtty.h",
"stab.h",
"stdc-predef.h",
"stdio_ext.h",
"syscall.h",
"termio.h",
"thread_db.h",
"ucontext.h",
"ustat.h",
"utmp.h",
"values.h",
"wait.h",
"xlocale.h",
# Hardware specific headers
"arm_neon.h",
"emmintrin.h",
"xmmintin.h",
]
)
# Folders of C libraries so commonly used in C++,
# that they have parity with standard C libraries.
C_STANDARD_HEADER_FOLDERS = frozenset(
[
# standard C library
"sys",
# glibc for linux
"arpa",
"asm-generic",
"bits",
"gnu",
"net",
"netinet",
"protocols",
"rpc",
"rpcsvc",
"scsi",
# linux kernel header
"drm",
"linux",
"misc",
"mtd",
"rdma",
"sound",
"video",
"xen",
]
)
# Type names
_TYPES = re.compile(
r"^(?:"
# [dcl.type.simple]
r"(char(16_t|32_t)?)|wchar_t|"
r"bool|short|int|long|signed|unsigned|float|double|"
# [support.types]
r"(ptrdiff_t|size_t|max_align_t|nullptr_t)|"
# [cstdint.syn]
r"(u?int(_fast|_least)?(8|16|32|64)_t)|"
r"(u?int(max|ptr)_t)|"
r")$"
)
# These headers are excluded from [build/include] and [build/include_order]
# checks:
# - Anything not following google file name conventions (containing an
# uppercase character, such as Python.h or nsStringAPI.h, for example).
# - Lua headers.
_THIRD_PARTY_HEADERS_PATTERN = re.compile(r"^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$")
# Pattern for matching FileInfo.BaseName() against test file name
_test_suffixes = ["_test", "_regtest", "_unittest"]
_TEST_FILE_SUFFIX = "(" + "|".join(_test_suffixes) + r")$"
# Pattern that matches only complete whitespace, possibly across multiple lines.
_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r"^\s*$", re.DOTALL)
# Assertion macros. These are defined in base/logging.h and
# testing/base/public/gunit.h.
_CHECK_MACROS = [
"DCHECK",
"CHECK",
"EXPECT_TRUE",
"ASSERT_TRUE",
"EXPECT_FALSE",
"ASSERT_FALSE",
]
# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
_CHECK_REPLACEMENT: dict[str, dict[str, str]] = {macro_var: {} for macro_var in _CHECK_MACROS}
for op, replacement in [
("==", "EQ"),
("!=", "NE"),
(">=", "GE"),
(">", "GT"),
("<=", "LE"),
("<", "LT"),
]:
_CHECK_REPLACEMENT["DCHECK"][op] = f"DCHECK_{replacement}"
_CHECK_REPLACEMENT["CHECK"][op] = f"CHECK_{replacement}"
_CHECK_REPLACEMENT["EXPECT_TRUE"][op] = f"EXPECT_{replacement}"
_CHECK_REPLACEMENT["ASSERT_TRUE"][op] = f"ASSERT_{replacement}"
for op, inv_replacement in [
("==", "NE"),
("!=", "EQ"),
(">=", "LT"),
(">", "LE"),
("<=", "GT"),
("<", "GE"),
]:
_CHECK_REPLACEMENT["EXPECT_FALSE"][op] = f"EXPECT_{inv_replacement}"
_CHECK_REPLACEMENT["ASSERT_FALSE"][op] = f"ASSERT_{inv_replacement}"
# Alternative tokens and their replacements. For full list, see section 2.5
# Alternative tokens [lex.digraph] in the C++ standard.
#
# Digraphs (such as '%:') are not included here since it's a mess to
# match those on a word boundary.
_ALT_TOKEN_REPLACEMENT = {
"and": "&&",
"bitor": "|",
"or": "||",
"xor": "^",
"compl": "~",
"bitand": "&",
"and_eq": "&=",
"or_eq": "|=",
"xor_eq": "^=",
"not": "!",
"not_eq": "!=",
}
# Compile regular expression that matches all the above keywords. The "[ =()]"
# bit is meant to avoid matching these keywords outside of boolean expressions.
#
# False positives include C-style multi-line comments and multi-line strings
# but those have always been troublesome for cpplint.
_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
r"([ =()])(" + ("|".join(_ALT_TOKEN_REPLACEMENT.keys())) + r")([ (]|$)"
)
# These constants define types of headers for use with
# _IncludeState.CheckNextIncludeOrder().
_C_SYS_HEADER = 1
_CPP_SYS_HEADER = 2
_OTHER_SYS_HEADER = 3
_LIKELY_MY_HEADER = 4
_POSSIBLE_MY_HEADER = 5
_OTHER_HEADER = 6
# These constants define the current inline assembly state
_NO_ASM = 0 # Outside of inline assembly block
_INSIDE_ASM = 1 # Inside inline assembly block
_END_ASM = 2 # Last line of inline assembly block
_BLOCK_ASM = 3 # The whole block is an inline assembly block
# Match start of assembly blocks
_MATCH_ASM = re.compile(
r"^\s*(?:asm|_asm|__asm|__asm__)"
r"(?:\s+(volatile|__volatile__))?"
r"\s*[{(]"
)
# Match strings that indicate we're working on a C (not C++) file.
_SEARCH_C_FILE = re.compile(
r"\b(?:LINT_C_FILE|"
r"vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))"
)
# Match string that indicates we're working on a Linux Kernel file.
_SEARCH_KERNEL_FILE = re.compile(r"\b(?:LINT_KERNEL_FILE)")
# Commands for sed to fix the problem
_SED_FIXUPS = {
"Remove spaces around =": r"s/ = /=/",
"Remove spaces around !=": r"s/ != /!=/",
"Remove space before ( in if (": r"s/if (/if(/",
"Remove space before ( in for (": r"s/for (/for(/",
"Remove space before ( in while (": r"s/while (/while(/",
"Remove space before ( in switch (": r"s/switch (/switch(/",
"Should have a space between // and comment": r"s/\/\//\/\/ /",
"Missing space before {": r"s/\([^ ]\){/\1 {/",
"Tab found, replace by spaces": r"s/\t/ /g",
"Line ends in whitespace. Consider deleting these extra spaces.": r"s/\s*$//",
"You don't need a ; after a }": r"s/};/}/",
"Missing space after ,": r"s/,\([^ ]\)/, \1/g",
}
# The root directory used for deriving header guard CPP variable.
# This is set by --root flag.
_root = None
_root_debug = False
# The top level repository directory. If set, _root is calculated relative to
# this directory instead of the directory containing version control artifacts.
# This is set by the --repository flag.
_repository = None
# Files to exclude from linting. This is set by the --exclude flag.
_excludes = None
# Whether to suppress all PrintInfo messages, UNRELATED to --quiet flag
_quiet = False
# The allowed line length of files.
# This is set by --linelength flag.
_line_length = 80
# This allows to use different include order rule than default
_include_order = "default"
# This allows different config files to be used
_config_filename = "CPPLINT.cfg"
# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc.
# This is set by --headers flag.
_hpp_headers: set[str] = set()
class ErrorSuppressions:
"""Class to track all error suppressions for cpplint"""
class LineRange:
"""Class to represent a range of line numbers for which an error is suppressed"""
def __init__(self, begin, end):
self.begin = begin
self.end = end
def __str__(self):
return f"[{self.begin}-{self.end}]"
def __contains__(self, obj):
return self.begin <= obj <= self.end
def ContainsRange(self, other):
return self.begin <= other.begin and self.end >= other.end
def __init__(self):
self._suppressions = collections.defaultdict(list)
self._open_block_suppression = None
def _AddSuppression(self, category, line_range):
suppressed = self._suppressions[category]
if not (suppressed and suppressed[-1].ContainsRange(line_range)):
suppressed.append(line_range)
def GetOpenBlockStart(self):
""":return: The start of the current open block or `-1` if there is not an open block"""
return self._open_block_suppression.begin if self._open_block_suppression else -1
def AddGlobalSuppression(self, category):
"""Add a suppression for `category` which is suppressed for the whole file"""
self._AddSuppression(category, self.LineRange(0, math.inf))
def AddLineSuppression(self, category, linenum):
"""Add a suppression for `category` which is suppressed only on `linenum`"""
self._AddSuppression(category, self.LineRange(linenum, linenum))
def StartBlockSuppression(self, category, linenum):
"""Start a suppression block for `category` on `linenum`. inclusive"""
if self._open_block_suppression is None:
self._open_block_suppression = self.LineRange(linenum, math.inf)
self._AddSuppression(category, self._open_block_suppression)
def EndBlockSuppression(self, linenum):
"""End the current block suppression on `linenum`. inclusive"""
if self._open_block_suppression:
self._open_block_suppression.end = linenum
self._open_block_suppression = None
def IsSuppressed(self, category, linenum):
""":return: `True` if `category` is suppressed for `linenum`"""
suppressed = self._suppressions[category] + self._suppressions[None]
return any(linenum in lr for lr in suppressed)
def HasOpenBlock(self):
""":return: `True` if a block suppression was started but not ended"""
return self._open_block_suppression is not None
def Clear(self):
"""Clear all current error suppressions"""
self._suppressions.clear()
self._open_block_suppression = None
# {str, set(int)}: a map from error categories to sets of linenumbers
# on which those errors are expected and should be suppressed.
_error_suppressions = ErrorSuppressions()
def ProcessHppHeadersOption(val):
global _hpp_headers
try:
_hpp_headers = {ext.strip() for ext in val.split(",")}
except ValueError:
PrintUsage("Header extensions must be comma separated list.")
def ProcessIncludeOrderOption(val):
if val is None or val == "default":
pass
elif val == "standardcfirst":
global _include_order
_include_order = val
else:
PrintUsage("Invalid includeorder value %s. Expected default|standardcfirst")
def IsHeaderExtension(file_extension):
return file_extension in GetHeaderExtensions()
def GetHeaderExtensions():
if _hpp_headers:
return _hpp_headers
if _valid_extensions:
return {h for h in _valid_extensions if "h" in h}
return {"h", "hh", "hpp", "hxx", "h++", "cuh"}
# The allowed extensions for file names
# This is set by --extensions flag
def GetAllExtensions():
return GetHeaderExtensions().union(_valid_extensions or {"c", "cc", "cpp", "cxx", "c++", "cu"})
def ProcessExtensionsOption(val):
global _valid_extensions
try:
extensions = [ext.strip() for ext in val.split(",")]
_valid_extensions = set(extensions)
except ValueError:
PrintUsage(
"Extensions should be a comma-separated list of values;"
"for example: extensions=hpp,cpp\n"
f'This could not be parsed: "{val}"'
)
def GetNonHeaderExtensions():
return GetAllExtensions().difference(GetHeaderExtensions())
def ParseNolintSuppressions(filename, raw_line, linenum, error):
"""Updates the global list of line error-suppressions.
Parses any NOLINT comments on the current line, updating the global
error_suppressions store. Reports an error if the NOLINT comment
was malformed.
Args:
filename: str, the name of the input file.
raw_line: str, the line of input text, with comments.
linenum: int, the number of the current line.
error: function, an error handler.
"""
if matched := re.search(r"\bNOLINT(NEXTLINE|BEGIN|END)?\b(\([^)]+\))?", raw_line):
no_lint_type = matched.group(1)
if no_lint_type == "NEXTLINE":
def ProcessCategory(category):
_error_suppressions.AddLineSuppression(category, linenum + 1)
elif no_lint_type == "BEGIN":
if _error_suppressions.HasOpenBlock():
error(
filename,
linenum,
"readability/nolint",
5,
(
"NONLINT block already defined on line "
f"{_error_suppressions.GetOpenBlockStart()}"
),
)
def ProcessCategory(category):
_error_suppressions.StartBlockSuppression(category, linenum)
elif no_lint_type == "END":
if not _error_suppressions.HasOpenBlock():
error(filename, linenum, "readability/nolint", 5, "Not in a NOLINT block")
def ProcessCategory(category):
if category is not None:
error(
filename,
linenum,
"readability/nolint",
5,
f"NOLINT categories not supported in block END: {category}",
)
_error_suppressions.EndBlockSuppression(linenum)
else:
def ProcessCategory(category):
_error_suppressions.AddLineSuppression(category, linenum)
categories = matched.group(2)
if categories in (None, "(*)"): # => "suppress all"
ProcessCategory(None)
elif categories.startswith("(") and categories.endswith(")"):
for category in {c.strip() for c in categories[1:-1].split(",")}:
if category in _ERROR_CATEGORIES:
ProcessCategory(category)
elif any(c for c in _OTHER_NOLINT_CATEGORY_PREFIXES if category.startswith(c)):
# Ignore any categories from other tools.
pass
elif category not in _LEGACY_ERROR_CATEGORIES:
error(
filename,
linenum,
"readability/nolint",
5,
f"Unknown NOLINT error category: {category}",
)
def ProcessGlobalSuppressions(filename: str, lines: list[str]) -> None:
"""Updates the list of global error suppressions.
Parses any lint directives in the file that have global effect.
Args:
lines: An array of strings, each representing a line of the file, with the
last element being empty if the file is terminated with a newline.
filename: str, the name of the input file.
"""
for line in lines:
if _SEARCH_C_FILE.search(line) or filename.lower().endswith((".c", ".cu")):
for category in _DEFAULT_C_SUPPRESSED_CATEGORIES:
_error_suppressions.AddGlobalSuppression(category)
if _SEARCH_KERNEL_FILE.search(line):
for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES:
_error_suppressions.AddGlobalSuppression(category)
def ResetNolintSuppressions():
"""Resets the set of NOLINT suppressions to empty."""
_error_suppressions.Clear()
def IsErrorSuppressedByNolint(category, linenum):
"""Returns true if the specified error category is suppressed on this line.
Consults the global error_suppressions map populated by
ParseNolintSuppressions/ProcessGlobalSuppressions/ResetNolintSuppressions.
Args:
category: str, the category of the error.
linenum: int, the current line number.
Returns:
bool, True iff the error should be suppressed due to a NOLINT comment,
block suppression or global suppression.
"""
return _error_suppressions.IsSuppressed(category, linenum)
def _IsSourceExtension(s):
"""File extension (excluding dot) matches a source file extension."""
return s in GetNonHeaderExtensions()
class _IncludeState:
"""Tracks line numbers for includes, and the order in which includes appear.
include_list contains list of lists of (header, line number) pairs.
It's a lists of lists rather than just one flat list to make it
easier to update across preprocessor boundaries.
Call CheckNextIncludeOrder() once for each header in the file, passing
in the type constants defined above. Calls in an illegal order will
raise an _IncludeError with an appropriate error message.
"""
# self._section will move monotonically through this set. If it ever
# needs to move backwards, CheckNextIncludeOrder will raise an error.
_INITIAL_SECTION = 0
_MY_H_SECTION = 1
_C_SECTION = 2
_CPP_SECTION = 3
_OTHER_SYS_SECTION = 4
_OTHER_H_SECTION = 5
_TYPE_NAMES = {
_C_SYS_HEADER: "C routing header",
_CPP_SYS_HEADER: "C++ routing header",
_OTHER_SYS_HEADER: "other routing header",
_LIKELY_MY_HEADER: "header this file implements",
_POSSIBLE_MY_HEADER: "header this file may implement",
_OTHER_HEADER: "other header",
}
_SECTION_NAMES = {
_INITIAL_SECTION: "... nothing. (This can't be an error.)",
_MY_H_SECTION: "a header this file implements",
_C_SECTION: "C routing header",
_CPP_SECTION: "C++ routing header",
_OTHER_SYS_SECTION: "other routing header",
_OTHER_H_SECTION: "other header",
}
def __init__(self):
self.include_list = [[]]
self._section = None
self._last_header = None
self.ResetSection("")
def FindHeader(self, header):
"""Check if a header has already been included.
Args:
header: header to check.
Returns:
Line number of previous occurrence, or -1 if the header has not
been seen before.
"""
for section_list in self.include_list:
for f in section_list:
if f[0] == header:
return f[1]
return -1
def ResetSection(self, directive):
"""Reset section checking for preprocessor directive.
Args:
directive: preprocessor directive (e.g. "if", "else").
"""
# The name of the current section.
self._section = self._INITIAL_SECTION
# The path of last found header.
self._last_header = ""
# Update list of includes. Note that we never pop from the
# include list.
if directive in ("if", "ifdef", "ifndef"):
self.include_list.append([])
elif directive in ("else", "elif"):
self.include_list[-1] = []
def SetLastHeader(self, header_path):
self._last_header = header_path
def CanonicalizeAlphabeticalOrder(self, header_path):
"""Returns a path canonicalized for alphabetical comparison.
- replaces "-" with "_" so they both cmp the same.
- removes '-inl' since we don't require them to be after the main header.
- lowercase everything, just in case.
Args:
header_path: Path to be canonicalized.
Returns:
Canonicalized path.
"""
return header_path.replace("-inl.h", ".h").replace("-", "_").lower()
def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
"""Check if a header is in alphabetical order with the previous header.
Args:
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
header_path: Canonicalized header to be checked.
Returns:
Returns true if the header is in alphabetical order.
"""
# If previous section is different from current section, _last_header will
# be reset to empty string, so it's always less than current header.
#
# If previous line was a blank line, assume that the headers are
# intentionally sorted the way they are.
return not (
self._last_header > header_path
and re.match(r"^\s*#\s*include\b", clean_lines.elided[linenum - 1])
)
def CheckNextIncludeOrder(self, header_type):
"""Returns a non-empty error message if the next header is out of order.
This function also updates the internal state to be ready to check
the next include.
Args:
header_type: One of the _XXX_HEADER constants defined above.
Returns:
The empty string if the header is in the right order, or an
error message describing what's wrong.
"""
error_message = (
f"Found {self._TYPE_NAMES[header_type]} after {self._SECTION_NAMES[self._section]}"
)
last_section = self._section
if header_type == _C_SYS_HEADER:
if self._section <= self._C_SECTION:
self._section = self._C_SECTION
else:
self._last_header = ""
return error_message
elif header_type == _CPP_SYS_HEADER:
if self._section <= self._CPP_SECTION:
self._section = self._CPP_SECTION
else:
self._last_header = ""
return error_message
elif header_type == _OTHER_SYS_HEADER:
if self._section <= self._OTHER_SYS_SECTION:
self._section = self._OTHER_SYS_SECTION
else:
self._last_header = ""
return error_message
elif header_type == _LIKELY_MY_HEADER:
if self._section <= self._MY_H_SECTION:
self._section = self._MY_H_SECTION
else:
self._section = self._OTHER_H_SECTION
elif header_type == _POSSIBLE_MY_HEADER:
if self._section <= self._MY_H_SECTION:
self._section = self._MY_H_SECTION
else:
# This will always be the fallback because we're not sure
# enough that the header is associated with this file.
self._section = self._OTHER_H_SECTION
else:
assert header_type == _OTHER_HEADER
self._section = self._OTHER_H_SECTION
if last_section != self._section:
self._last_header = ""
return ""
class _CppLintState:
"""Maintains module-wide state.."""
def __init__(self):
self.verbose_level = 1 # global setting.
self.error_count = 0 # global count of reported errors
# filters to apply when emitting error messages
self.filters = _DEFAULT_FILTERS[:]
# backup of filter list. Used to restore the state after each file.
self._filters_backup = self.filters[:]
self.counting = "total" # In what way are we counting errors?
self.errors_by_category = {} # string to int dict storing error counts
self.quiet = False # Suppress non-error messages?
# output format:
# "emacs" - format that emacs can parse (default)
# "eclipse" - format that eclipse can parse
# "vs7" - format that Microsoft Visual Studio 7 can parse
# "junit" - format that Jenkins, Bamboo, etc can parse
# "sed" - returns a gnu sed command to fix the problem
# "gsed" - like sed, but names the command gsed, e.g. for macOS homebrew users
self.output_format = "emacs"
# For JUnit output, save errors and failures until the end so that they
# can be written into the XML
self._junit_errors = []
self._junit_failures = []
def SetOutputFormat(self, output_format):
"""Sets the output format for errors."""
self.output_format = output_format
def SetQuiet(self, quiet):
"""Sets the module's quiet settings, and returns the previous setting."""
last_quiet = self.quiet
self.quiet = quiet
return last_quiet
def SetVerboseLevel(self, level):
"""Sets the module's verbosity, and returns the previous setting."""
last_verbose_level = self.verbose_level
self.verbose_level = level
return last_verbose_level
def SetCountingStyle(self, counting_style):
"""Sets the module's counting options."""
self.counting = counting_style
def SetFilters(self, filters):
"""Sets the error-message filters.
These filters are applied when deciding whether to emit a given
error message.
Args:
filters: A string of comma-separated filters (eg "+whitespace/indent").
Each filter should start with + or -; else we die.
Raises:
ValueError: The comma-separated filters did not all start with '+' or '-'.
E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
"""
# Default filters always have less priority than the flag ones.
self.filters = _DEFAULT_FILTERS[:]
self.AddFilters(filters)
def AddFilters(self, filters):
"""Adds more filters to the existing list of error-message filters."""
for filt in filters.split(","):
clean_filt = filt.strip()
if clean_filt:
self.filters.append(clean_filt)
for filt in self.filters:
if not filt.startswith(("+", "-")):
msg = f"Every filter in --filters must start with + or - ({filt} does not)"
raise ValueError(msg)
def BackupFilters(self):
"""Saves the current filter list to backup storage."""
self._filters_backup = self.filters[:]
def RestoreFilters(self):
"""Restores filters previously backed up."""
self.filters = self._filters_backup[:]
def ResetErrorCounts(self):
"""Sets the module's error statistic back to zero."""
self.error_count = 0
self.errors_by_category = {}
def IncrementErrorCount(self, category):
"""Bumps the module's error statistic."""
self.error_count += 1
if self.counting in ("toplevel", "detailed"):
if self.counting != "detailed":
category = category.split("/")[0]
if category not in self.errors_by_category:
self.errors_by_category[category] = 0
self.errors_by_category[category] += 1
def PrintErrorCounts(self):
"""Print a summary of errors by category, and the total."""
for category, count in sorted(dict.items(self.errors_by_category)):
self.PrintInfo(f"Category '{category}' errors found: {count}\n")
if self.error_count > 0:
self.PrintInfo(f"Total errors found: {self.error_count}\n")
def PrintInfo(self, message):
# _quiet does not represent --quiet flag.
# Hide infos from stdout to keep stdout pure for machine consumption
if not _quiet and self.output_format not in _MACHINE_OUTPUTS:
sys.stdout.write(message)
def PrintError(self, message):
if self.output_format == "junit":
self._junit_errors.append(message)
else:
sys.stderr.write(message)
def AddJUnitFailure(self, filename, linenum, message, category, confidence):
self._junit_failures.append((filename, linenum, message, category, confidence))
def FormatJUnitXML(self):
num_errors = len(self._junit_errors)
num_failures = len(self._junit_failures)
testsuite = xml.etree.ElementTree.Element("testsuite")
testsuite.attrib["errors"] = str(num_errors)
testsuite.attrib["failures"] = str(num_failures)
testsuite.attrib["name"] = "cpplint"
if num_errors == 0 and num_failures == 0:
testsuite.attrib["tests"] = str(1)
xml.etree.ElementTree.SubElement(testsuite, "testcase", name="passed")
else:
testsuite.attrib["tests"] = str(num_errors + num_failures)
if num_errors > 0:
testcase = xml.etree.ElementTree.SubElement(testsuite, "testcase")
testcase.attrib["name"] = "errors"
error = xml.etree.ElementTree.SubElement(testcase, "error")
error.text = "\n".join(self._junit_errors)
if num_failures > 0:
# Group failures by file
failed_file_order = []
failures_by_file = {}
for failure in self._junit_failures:
failed_file = failure[0]
if failed_file not in failed_file_order:
failed_file_order.append(failed_file)
failures_by_file[failed_file] = []
failures_by_file[failed_file].append(failure)
# Create a testcase for each file
for failed_file in failed_file_order:
failures = failures_by_file[failed_file]
testcase = xml.etree.ElementTree.SubElement(testsuite, "testcase")
testcase.attrib["name"] = failed_file
failure = xml.etree.ElementTree.SubElement(testcase, "failure")
template = "{0}: {1} [{2}] [{3}]"
texts = [template.format(f[1], f[2], f[3], f[4]) for f in failures]
failure.text = "\n".join(texts)
xml_decl = '<?xml version="1.0" encoding="UTF-8" ?>\n'
return xml_decl + xml.etree.ElementTree.tostring(testsuite, "utf-8").decode("utf-8")
_cpplint_state = _CppLintState()
def _OutputFormat():
"""Gets the module's output format."""
return _cpplint_state.output_format
def _SetOutputFormat(output_format):
"""Sets the module's output format."""
_cpplint_state.SetOutputFormat(output_format)
def _Quiet():
"""Return's the module's quiet setting."""
return _cpplint_state.quiet
def _SetQuiet(quiet):
"""Set the module's quiet status, and return previous setting."""
return _cpplint_state.SetQuiet(quiet)
def _VerboseLevel():
"""Returns the module's verbosity setting."""
return _cpplint_state.verbose_level
def _SetVerboseLevel(level):
"""Sets the module's verbosity, and returns the previous setting."""
return _cpplint_state.SetVerboseLevel(level)
def _SetCountingStyle(level):
"""Sets the module's counting options."""
_cpplint_state.SetCountingStyle(level)
def _Filters():
"""Returns the module's list of output filters, as a list."""
return _cpplint_state.filters
def _SetFilters(filters):
"""Sets the module's error-message filters.
These filters are applied when deciding whether to emit a given
error message.
Args:
filters: A string of comma-separated filters (eg "whitespace/indent").
Each filter should start with + or -; else we die.
"""
_cpplint_state.SetFilters(filters)
def _AddFilters(filters):
"""Adds more filter overrides.
Unlike _SetFilters, this function does not reset the current list of filters
available.
Args:
filters: A string of comma-separated filters (eg "whitespace/indent").
Each filter should start with + or -; else we die.
"""
_cpplint_state.AddFilters(filters)
def _BackupFilters():
"""Saves the current filter list to backup storage."""
_cpplint_state.BackupFilters()
def _RestoreFilters():
"""Restores filters previously backed up."""
_cpplint_state.RestoreFilters()
class _FunctionState:
"""Tracks current function name and the number of lines in its body."""
_NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
_TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
def __init__(self):
self.in_a_function = False
self.lines_in_function = 0
self.current_function = ""
def Begin(self, function_name):
"""Start analyzing function body.
Args:
function_name: The name of the function being tracked.
"""
self.in_a_function = True
self.lines_in_function = 0
self.current_function = function_name
def Count(self):
"""Count line in current function body."""
if self.in_a_function:
self.lines_in_function += 1
def Check(self, error, filename, linenum):
"""Report if too many lines in function body.
Args:
error: The function to call with any errors found.
filename: The name of the current file.
linenum: The number of the line to check.
"""
if not self.in_a_function:
return
if re.match(r"T(EST|est)", self.current_function):
base_trigger = self._TEST_TRIGGER
else:
base_trigger = self._NORMAL_TRIGGER
trigger = base_trigger * 2 ** _VerboseLevel()
if self.lines_in_function > trigger:
error_level = int(math.log2(self.lines_in_function / base_trigger))
# 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
error_level = min(error_level, 5)
error(
filename,
linenum,
"readability/fn_size",
error_level,
"Small and focused functions are preferred:"
f" {self.current_function} has {self.lines_in_function} non-comment lines"
f" (error triggered by exceeding {trigger} lines).",
)
def End(self):
"""Stop analyzing function body."""
self.in_a_function = False
class _IncludeError(Exception):
"""Indicates a problem with the include order in a file."""
pass
class FileInfo:
"""Provides utility functions for filenames.
FileInfo provides easy access to the components of a file's path
relative to the project root.
"""
def __init__(self, filename):
self._filename = filename
def FullName(self):
"""Make Windows paths like Unix."""
return os.path.abspath(self._filename).replace("\\", "/")
def RepositoryName(self):
r"""FullName after removing the local path to the repository.
If we have a real absolute path name here we can try to do something smart:
detecting the root of the checkout and truncating /path/to/checkout from
the name so that we get header guards that don't include things like
"C:\\Documents and Settings\\..." or "/home/username/..." in them and thus
people on different computers who have checked the source out to different
locations won't see bogus errors.
"""
fullname = self.FullName()
if os.path.exists(fullname):
project_dir = os.path.dirname(fullname)
# If the user specified a repository path, it exists, and the file is
# contained in it, use the specified repository path
if _repository:
repo = FileInfo(_repository).FullName()
root_dir = project_dir
while os.path.exists(root_dir):
# allow case insensitive compare on Windows
if os.path.normcase(root_dir) == os.path.normcase(repo):
return os.path.relpath(fullname, root_dir).replace("\\", "/")
one_up_dir = os.path.dirname(root_dir)
if one_up_dir == root_dir:
break
root_dir = one_up_dir
if os.path.exists(os.path.join(project_dir, ".svn")):
# If there's a .svn file in the current directory, we recursively look
# up the directory tree for the top of the SVN checkout
root_dir = project_dir
one_up_dir = os.path.dirname(root_dir)
while os.path.exists(os.path.join(one_up_dir, ".svn")):
root_dir = os.path.dirname(root_dir)
one_up_dir = os.path.dirname(one_up_dir)
prefix = os.path.commonprefix([root_dir, project_dir])
return fullname[len(prefix) + 1 :]
# Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
# searching up from the current path.
root_dir = current_dir = os.path.dirname(fullname)
while current_dir != os.path.dirname(current_dir):
if (
os.path.exists(os.path.join(current_dir, ".git"))
or os.path.exists(os.path.join(current_dir, ".hg"))
or os.path.exists(os.path.join(current_dir, ".svn"))
):
root_dir = current_dir
break
current_dir = os.path.dirname(current_dir)
if (
os.path.exists(os.path.join(root_dir, ".git"))
or os.path.exists(os.path.join(root_dir, ".hg"))
or os.path.exists(os.path.join(root_dir, ".svn"))
):
prefix = os.path.commonprefix([root_dir, project_dir])
return fullname[len(prefix) + 1 :]
# Don't know what to do; header guard warnings may be wrong...
return fullname
def Split(self):
"""Splits the file into the directory, basename, and extension.
For 'chrome/browser/browser.cc', Split() would
return ('chrome/browser', 'browser', '.cc')
Returns:
A tuple of (directory, basename, extension).
"""
googlename = self.RepositoryName()
project, rest = os.path.split(googlename)
return (project,) + os.path.splitext(rest)
def BaseName(self):
"""File base name - text after the final slash, before the final period."""
return self.Split()[1]
def Extension(self):
"""File extension - text following the final period, includes that period."""
return self.Split()[2]
def NoExtension(self):
"""File has no source file extension."""
return "/".join(self.Split()[0:2])
def IsSource(self):
"""File has a source file extension."""
return _IsSourceExtension(self.Extension()[1:])
def _ShouldPrintError(category, confidence, filename, linenum):
"""If confidence >= verbose, category passes filter and is not suppressed."""
# There are three ways we might decide not to print an error message:
# a "NOLINT(category)" comment appears in the source,
# the verbosity level isn't high enough, or the filters filter it out.
if IsErrorSuppressedByNolint(category, linenum):
return False
if confidence < _cpplint_state.verbose_level:
return False
is_filtered = False
for one_filter in _Filters():
filter_cat, filter_file, filter_line = _ParseFilterSelector(one_filter[1:])
category_match = category.startswith(filter_cat)
file_match = filter_file in ("", filename)
line_match = filter_line in (linenum, -1)
if one_filter.startswith("-"):
if category_match and file_match and line_match:
is_filtered = True
elif one_filter.startswith("+"):
if category_match and file_match and line_match:
is_filtered = False
else:
# should have been checked for in SetFilter.
msg = f"Invalid filter: {one_filter}"
raise ValueError(msg)
return not is_filtered
def Error(filename, linenum, category, confidence, message):
"""Logs the fact we've found a lint error.
We log where the error was found, and also our confidence in the error,
that is, how certain we are this is a legitimate style regression, and
not a misidentification or a use that's sometimes justified.
False positives can be suppressed by the use of "NOLINT(category)"
comments, NOLINTNEXTLINE or in blocks started by NOLINTBEGIN. These
are parsed into _error_suppressions.
Args:
filename: The name of the file containing the error.
linenum: The number of the line containing the error.
category: A string used to describe the "category" this bug
falls under: "whitespace", say, or "runtime". Categories
may have a hierarchy separated by slashes: "whitespace/indent".
confidence: A number from 1-5 representing a confidence score for
the error, with 5 meaning that we are certain of the problem,
and 1 meaning that it could be a legitimate construct.
message: The error message.
"""
if _ShouldPrintError(category, confidence, filename, linenum):
_cpplint_state.IncrementErrorCount(category)
if _cpplint_state.output_format == "vs7":
_cpplint_state.PrintError(
f"{filename}({linenum}): error cpplint: [{category}] {message} [{confidence}]\n"
)
elif _cpplint_state.output_format == "eclipse":
sys.stderr.write(
f"{filename}:{linenum}: warning: {message} [{category}] [{confidence}]\n"
)
elif _cpplint_state.output_format == "junit":
_cpplint_state.AddJUnitFailure(filename, linenum, message, category, confidence)
elif _cpplint_state.output_format in ["sed", "gsed"]:
if message in _SED_FIXUPS:
sys.stdout.write(
f"{_cpplint_state.output_format} -i"
f" '{linenum}{_SED_FIXUPS[message]}' {filename}"
f" # {message} [{category}] [{confidence}]\n"
)
else:
sys.stderr.write(
f'# {filename}:{linenum}: "{message}" [{category}] [{confidence}]\n'
)
else:
final_message = f"{filename}:{linenum}: {message} [{category}] [{confidence}]\n"
sys.stderr.write(final_message)
# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.
_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
# Match a single C style comment on the same line.
_RE_PATTERN_C_COMMENTS = r"/\*(?:[^*]|\*(?!/))*\*/"
# Matches multi-line C style comments.
# This RE is a little bit more complicated than one might expect, because we
# have to take care of space removals tools so we can handle comments inside
# statements better.
# The current rule is: We only clear spaces from both sides when we're at the
# end of the line. Otherwise, we try to remove spaces from the right side,
# if this doesn't work we try on left side but only if there's a non-character
# on the right.
_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
r"(\s*"
+ _RE_PATTERN_C_COMMENTS
+ r"\s*$|"
+ _RE_PATTERN_C_COMMENTS
+ r"\s+|"
+ r"\s+"
+ _RE_PATTERN_C_COMMENTS
+ r"(?=\W)|"
+ _RE_PATTERN_C_COMMENTS
+ r")"
)
def IsCppString(line):
"""Does line terminate so, that the next symbol is in string constant.
This function does not consider single-line nor multi-line comments.
Args:
line: is a partial line of code starting from the 0..n.
Returns:
True, if next character appended to 'line' is inside a
string constant.
"""
line = line.replace(r"\\", "XX") # after this, \\" does not match to \"
return ((line.count('"') - line.count(r"\"") - line.count("'\"'")) & 1) == 1
def CleanseRawStrings(raw_lines):
"""Removes C++11 raw strings from lines.
Before:
static const char kData[] = R"(
multi-line string
)";
After:
static const char kData[] = ""
(replaced by blank line)
"";
Args:
raw_lines: list of raw lines.
Returns:
list of lines with C++11 raw strings replaced by empty strings.
"""
delimiter = None
lines_without_raw_strings = []
for line in raw_lines:
if delimiter:
# Inside a raw string, look for the end
end = line.find(delimiter)
if end >= 0:
# Found the end of the string, match leading space for this
# line and resume copying the original lines, and also insert
# a "" on the last line.
leading_space = re.match(r"^(\s*)\S", line)
line = leading_space.group(1) + '""' + line[end + len(delimiter) :]
delimiter = None
else:
# Haven't found the end yet, append a blank line.
line = '""'
# Look for beginning of a raw string, and replace them with
# empty strings. This is done in a loop to handle multiple raw
# strings on the same line.
while delimiter is None:
# Look for beginning of a raw string.
# See 2.14.15 [lex.string] for syntax.
#
# Once we have matched a raw string, we check the prefix of the
# line to make sure that the line is not part of a single line
# comment. It's done this way because we remove raw strings
# before removing comments as opposed to removing comments
# before removing raw strings. This is because there are some
# cpplint checks that requires the comments to be preserved, but
# we don't want to check comments that are inside raw strings.
matched = re.match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
if matched and not re.match(
r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', matched.group(1)
):
delimiter = ")" + matched.group(2) + '"'
end = matched.group(3).find(delimiter)
if end >= 0:
# Raw string ended on same line
line = matched.group(1) + '""' + matched.group(3)[end + len(delimiter) :]
delimiter = None
else:
# Start of a multi-line raw string
line = matched.group(1) + '""'
else:
break
lines_without_raw_strings.append(line)
# TODO(google): if delimiter is not None here, we might want to
# emit a warning for unterminated string.
return lines_without_raw_strings
def FindNextMultiLineCommentStart(lines, lineix):
"""Find the beginning marker for a multiline comment."""
while lineix < len(lines):
if lines[lineix].strip().startswith("/*"):
# Only return this marker if the comment goes beyond this line
if lines[lineix].strip().find("*/", 2) < 0:
return lineix
lineix += 1
return len(lines)
def FindNextMultiLineCommentEnd(lines, lineix):
"""We are inside a comment, find the end marker."""
while lineix < len(lines):
if lines[lineix].strip().endswith("*/"):
return lineix
lineix += 1
return len(lines)
def RemoveMultiLineCommentsFromRange(lines, begin, end):
"""Clears a range of lines for multi-line comments."""
# Having // <empty> comments makes the lines non-empty, so we will not get
# unnecessary blank line warnings later in the code.
for i in range(begin, end):
lines[i] = "/**/"
def RemoveMultiLineComments(filename, lines, error):
"""Removes multiline (c-style) comments from lines."""
lineix = 0
while lineix < len(lines):
lineix_begin = FindNextMultiLineCommentStart(lines, lineix)
if lineix_begin >= len(lines):
return
lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
if lineix_end >= len(lines):
error(
filename,
lineix_begin + 1,
"readability/multiline_comment",
5,
"Could not find end of multi-line comment",
)
return
RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
lineix = lineix_end + 1
def CleanseComments(line):
"""Removes //-comments and single-line C-style /* */ comments.
Args:
line: A line of C++ source.
Returns:
The line with single-line comments removed.
"""
commentpos = line.find("//")
if commentpos != -1 and not IsCppString(line[:commentpos]):
line = line[:commentpos].rstrip()
# get rid of /* ... */
return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub("", line)
def ReplaceAlternateTokens(line):
"""Replace any alternate token by its original counterpart.
In order to comply with the google rule stating that unary operators should
never be followed by a space, an exception is made for the 'not' and 'compl'
alternate tokens. For these, any trailing space is removed during the
conversion.
Args:
line: The line being processed.
Returns:
The line with alternate tokens replaced.
"""
for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line):
token = _ALT_TOKEN_REPLACEMENT[match.group(2)]
tail = "" if match.group(2) in ["not", "compl"] and match.group(3) == " " else r"\3"
line = re.sub(match.re, rf"\1{token}{tail}", line, count=1)
return line
class CleansedLines:
"""Holds 4 copies of all lines with different preprocessing applied to them.
1) elided member contains lines without strings and comments.
2) lines member contains lines without comments.
3) raw_lines member contains all the lines without processing.
4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw
strings removed.
All these members are of <type 'list'>, and of the same length.
"""
def __init__(self, lines):
if "-readability/alt_tokens" in _cpplint_state.filters:
for i, line in enumerate(lines):
lines[i] = ReplaceAlternateTokens(line)
self.elided = []
self.lines = []
self.raw_lines = lines
self.num_lines = len(lines)
self.lines_without_raw_strings = CleanseRawStrings(lines)
for line in self.lines_without_raw_strings:
self.lines.append(CleanseComments(line))
elided = self._CollapseStrings(line)
self.elided.append(CleanseComments(elided))
def NumLines(self):
"""Returns the number of lines represented."""
return self.num_lines
@staticmethod
def _CollapseStrings(elided):
"""Collapses strings and chars on a line to simple "" or '' blocks.
We nix strings first so we're not fooled by text like '"http://"'
Args:
elided: The line being processed.
Returns:
The line with collapsed strings.
"""
if _RE_PATTERN_INCLUDE.match(elided):
return elided
# Remove escaped characters first to make quote/single quote collapsing
# basic. Things that look like escaped characters shouldn't occur
# outside of strings and chars.
elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub("", elided)
# Replace quoted strings and digit separators. Both single quotes
# and double quotes are processed in the same loop, otherwise
# nested quotes wouldn't work.
collapsed = ""
while True:
# Find the first quote character
match = re.match(r'^([^\'"]*)([\'"])(.*)$', elided)
if not match:
collapsed += elided
break
head, quote, tail = match.groups()
if quote == '"':
# Collapse double quoted strings
second_quote = tail.find('"')
if second_quote >= 0:
collapsed += head + '""'
elided = tail[second_quote + 1 :]
else:
# Unmatched double quote, don't bother processing the rest
# of the line since this is probably a multiline string.
collapsed += elided
break
else:
# Found single quote, check nearby text to eliminate digit separators.
#
# There is no special handling for floating point here, because
# the integer/fractional/exponent parts would all be parsed
# correctly as long as there are digits on both sides of the
# separator. So we are fine as long as we don't see something
# like "0.'3" (gcc 4.9.0 will not allow this literal).
if re.search(r"\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$", head):
match_literal = re.match(r"^((?:\'?[0-9a-zA-Z_])*)(.*)$", "'" + tail)
collapsed += head + match_literal.group(1).replace("'", "")
elided = match_literal.group(2)
else:
second_quote = tail.find("'")
if second_quote >= 0:
collapsed += head + "''"
elided = tail[second_quote + 1 :]
else:
# Unmatched single quote
collapsed += elided
break
return collapsed
def FindEndOfExpressionInLine(line, startpos, stack):
"""Find the position just after the end of current parenthesized expression.
Args:
line: a CleansedLines line.
startpos: start searching at this position.
stack: nesting stack at startpos.
Returns:
On finding matching end: (index just after matching end, None)
On finding an unclosed expression: (-1, None)
Otherwise: (-1, new stack at end of this line)
"""
for i in range(startpos, len(line)):
char = line[i]
if char in "([{":
# Found start of parenthesized expression, push to expression stack
stack.append(char)
elif char == "<":
# Found potential start of template argument list
if i > 0 and line[i - 1] == "<":
# Left shift operator
if stack and stack[-1] == "<":
stack.pop()
if not stack:
return (-1, None)
elif i > 0 and re.search(r"\boperator\s*$", line[0:i]):
# operator<, don't add to stack
continue
else:
# Tentative start of template argument list
stack.append("<")
elif char in ")]}":
# Found end of parenthesized expression.
#
# If we are currently expecting a matching '>', the pending '<'
# must have been an operator. Remove them from expression stack.
while stack and stack[-1] == "<":
stack.pop()
if not stack:
return (-1, None)
if (
(stack[-1] == "(" and char == ")")
or (stack[-1] == "[" and char == "]")
or (stack[-1] == "{" and char == "}")
):
stack.pop()
if not stack:
return (i + 1, None)
else:
# Mismatched parentheses
return (-1, None)
elif char == ">":
# Found potential end of template argument list.
# Ignore "->" and operator functions
if i > 0 and (line[i - 1] == "-" or re.search(r"\boperator\s*$", line[0 : i - 1])):
continue
# Pop the stack if there is a matching '<'. Otherwise, ignore
# this '>' since it must be an operator.
if stack and stack[-1] == "<":
stack.pop()
if not stack:
return (i + 1, None)
elif char == ";":
# Found something that look like end of statements. If we are currently
# expecting a '>', the matching '<' must have been an operator, since
# template argument list should not contain statements.
while stack and stack[-1] == "<":
stack.pop()
if not stack:
return (-1, None)
# Did not find end of expression or unbalanced parentheses on this line
return (-1, stack)
def CloseExpression(clean_lines, linenum, pos):
"""If input points to ( or { or [ or <, finds the position that closes it.
If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the
linenum/pos that correspond to the closing of the expression.
TODO(google): cpplint spends a fair bit of time matching parentheses.
Ideally we would want to index all opening and closing parentheses once
and have CloseExpression be just a simple lookup, but due to preprocessor
tricks, this is not so easy.
Args:
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
pos: A position on the line.
Returns:
A tuple (line, linenum, pos) pointer *past* the closing brace, or
(line, len(lines), -1) if we never find a close. Note we ignore
strings and comments when matching; and the line we return is the
'cleansed' line at linenum.
"""
line = clean_lines.elided[linenum]
if (line[pos] not in "({[<") or re.match(r"<[<=]", line[pos:]):
return (line, clean_lines.NumLines(), -1)
# Check first line
(end_pos, stack) = FindEndOfExpressionInLine(line, pos, [])
if end_pos > -1:
return (line, linenum, end_pos)
# Continue scanning forward
while stack and linenum < clean_lines.NumLines() - 1:
linenum += 1
line = clean_lines.elided[linenum]
(end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack)
if end_pos > -1:
return (line, linenum, end_pos)
# Did not find end of expression before end of file, give up
return (line, clean_lines.NumLines(), -1)
def FindStartOfExpressionInLine(line, endpos, stack):
"""Find position at the matching start of current expression.
This is almost the reverse of FindEndOfExpressionInLine, but note
that the input position and returned position differs by 1.
Args:
line: a CleansedLines line.
endpos: start searching at this position.
stack: nesting stack at endpos.
Returns:
On finding matching start: (index at matching start, None)
On finding an unclosed expression: (-1, None)
Otherwise: (-1, new stack at beginning of this line)
"""
i = endpos
while i >= 0:
char = line[i]
if char in ")]}":
# Found end of expression, push to expression stack
stack.append(char)
elif char == ">":
# Found potential end of template argument list.
#
# Ignore it if it's a "->" or ">=" or "operator>"
if i > 0 and (
line[i - 1] == "-"
or re.match(r"\s>=\s", line[i - 1 :])
or re.search(r"\boperator\s*$", line[0:i])
):
i -= 1
else:
stack.append(">")
elif char == "<":
# Found potential start of template argument list
if i > 0 and line[i - 1] == "<":
# Left shift operator
i -= 1
else:
# If there is a matching '>', we can pop the expression stack.
# Otherwise, ignore this '<' since it must be an operator.
if stack and stack[-1] == ">":
stack.pop()
if not stack:
return (i, None)
elif char in "([{":
# Found start of expression.
#
# If there are any unmatched '>' on the stack, they must be
# operators. Remove those.
while stack and stack[-1] == ">":
stack.pop()
if not stack:
return (-1, None)
if (
(char == "(" and stack[-1] == ")")
or (char == "[" and stack[-1] == "]")
or (char == "{" and stack[-1] == "}")
):
stack.pop()
if not stack:
return (i, None)
else:
# Mismatched parentheses
return (-1, None)
elif char == ";":
# Found something that look like end of statements. If we are currently
# expecting a '<', the matching '>' must have been an operator, since
# template argument list should not contain statements.
while stack and stack[-1] == ">":
stack.pop()
if not stack:
return (-1, None)
i -= 1
return (-1, stack)
def ReverseCloseExpression(clean_lines, linenum, pos):
"""If input points to ) or } or ] or >, finds the position that opens it.
If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the
linenum/pos that correspond to the opening of the expression.
Args:
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
pos: A position on the line.
Returns:
A tuple (line, linenum, pos) pointer *at* the opening brace, or
(line, 0, -1) if we never find the matching opening brace. Note
we ignore strings and comments when matching; and the line we
return is the 'cleansed' line at linenum.
"""
line = clean_lines.elided[linenum]
if line[pos] not in ")}]>":
return (line, 0, -1)
# Check last line
(start_pos, stack) = FindStartOfExpressionInLine(line, pos, [])
if start_pos > -1:
return (line, linenum, start_pos)
# Continue scanning backward
while stack and linenum > 0:
linenum -= 1
line = clean_lines.elided[linenum]
(start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack)
if start_pos > -1:
return (line, linenum, start_pos)
# Did not find start of expression before beginning of file, give up
return (line, 0, -1)
def CheckForCopyright(filename, lines, error):
"""Logs an error if no Copyright message appears at the top of the file."""
# We'll say it should occur by line 10. Don't forget there's a
# placeholder line at the front.
for line in range(1, min(len(lines), 11)):
if re.search(r"Copyright", lines[line], re.IGNORECASE):
break
else: # means no copyright line was found
error(
filename,
0,
"legal/copyright",
5,
"No copyright message found. "
'You should have a line: "Copyright [year] <Copyright Owner>"',
)
def GetIndentLevel(line):
"""Return the number of leading spaces in line.
Args:
line: A string to check.
Returns:
An integer count of leading spaces, possibly zero.
"""
if indent := re.match(r"^( *)\S", line):
return len(indent.group(1))
return 0
def PathSplitToList(path):
"""Returns the path split into a list by the separator.
Args:
path: An absolute or relative path (e.g. '/a/b/c/' or '../a')
Returns:
A list of path components (e.g. ['a', 'b', 'c]).
"""
lst = []
while True:
(head, tail) = os.path.split(path)
if head == path: # absolute paths end
lst.append(head)
break
if tail == path: # relative paths end
lst.append(tail)
break
path = head
lst.append(tail)
lst.reverse()
return lst
def GetHeaderGuardCPPVariable(filename):
"""Returns the CPP variable that should be used as a header guard.
Args:
filename: The name of a C++ header file.
Returns:
The CPP variable that should be used as a header guard in the
named file.
"""
# Restores original filename in case that cpplint is invoked from Emacs's
# flymake.
filename = re.sub(r"_flymake\.h$", ".h", filename)
filename = re.sub(r"/\.flymake/([^/]*)$", r"/\1", filename)
# Replace 'c++' with 'cpp'.
filename = filename.replace("C++", "cpp").replace("c++", "cpp")
fileinfo = FileInfo(filename)
file_path_from_root = fileinfo.RepositoryName()
def FixupPathFromRoot():
if _root_debug:
sys.stderr.write(
f"\n_root fixup, _root = '{_root}',"
f" repository name = '{fileinfo.RepositoryName()}'\n"
)
# Process the file path with the --root flag if it was set.
if not _root:
if _root_debug:
sys.stderr.write("_root unspecified\n")
return file_path_from_root
def StripListPrefix(lst, prefix):
# f(['x', 'y'], ['w, z']) -> None (not a valid prefix)
if lst[: len(prefix)] != prefix:
return None
# f(['a, 'b', 'c', 'd'], ['a', 'b']) -> ['c', 'd']
return lst[(len(prefix)) :]
# root behavior:
# --root=subdir , lstrips subdir from the header guard
maybe_path = StripListPrefix(PathSplitToList(file_path_from_root), PathSplitToList(_root))
if _root_debug:
sys.stderr.write(
("_root lstrip (maybe_path=%s, file_path_from_root=%s," + " _root=%s)\n")
% (maybe_path, file_path_from_root, _root)
)
if maybe_path:
return os.path.join(*maybe_path)
# --root=.. , will prepend the outer directory to the header guard
full_path = fileinfo.FullName()
# adapt slashes for windows
root_abspath = os.path.abspath(_root).replace("\\", "/")
maybe_path = StripListPrefix(PathSplitToList(full_path), PathSplitToList(root_abspath))
if _root_debug:
sys.stderr.write(
("_root prepend (maybe_path=%s, full_path=%s, " + "root_abspath=%s)\n")
% (maybe_path, full_path, root_abspath)
)
if maybe_path:
return os.path.join(*maybe_path)
if _root_debug:
sys.stderr.write(f"_root ignore, returning {file_path_from_root}\n")
# --root=FAKE_DIR is ignored
return file_path_from_root
file_path_from_root = FixupPathFromRoot()
return re.sub(r"[^a-zA-Z0-9]", "_", file_path_from_root).upper() + "_"
def CheckForHeaderGuard(filename, clean_lines, error, cppvar):
"""Checks that the file contains a header guard.
Logs an error if no #ifndef header guard is present. For other
headers, checks that the full pathname is used.
Args:
filename: The name of the C++ header file.
clean_lines: A CleansedLines instance containing the file.
error: The function to call with any errors found.
"""
# Don't check for header guards if there are error suppression
# comments somewhere in this file.
#
# Because this is silencing a warning for a nonexistent line, we
# only support the very specific NOLINT(build/header_guard) syntax,
# and not the general NOLINT or NOLINT(*) syntax.
raw_lines = clean_lines.lines_without_raw_strings
for i in raw_lines:
if re.search(r"//\s*NOLINT\(build/header_guard\)", i):
return
# Allow pragma once instead of header guards
for i in raw_lines:
if re.search(r"^\s*#pragma\s+once", i):
return
ifndef = ""
ifndef_linenum = 0
define = ""
endif = ""
endif_linenum = 0
for linenum, line in enumerate(raw_lines):
linesplit = line.split()
if len(linesplit) >= 2:
# find the first occurrence of #ifndef and #define, save arg
if not ifndef and linesplit[0] == "#ifndef":
# set ifndef to the header guard presented on the #ifndef line.
ifndef = linesplit[1]
ifndef_linenum = linenum
if not define and linesplit[0] == "#define":
define = linesplit[1]
# find the last occurrence of #endif, save entire line
if line.startswith("#endif"):
endif = line
endif_linenum = linenum
if not ifndef or not define or ifndef != define:
error(
filename,
0,
"build/header_guard",
5,
f"No #ifndef header guard found, suggested CPP variable is: {cppvar}",
)
return
# The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__
# for backward compatibility.
if ifndef != cppvar:
error_level = 0
if ifndef != cppvar + "_":
error_level = 5
ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, error)
error(
filename,
ifndef_linenum,
"build/header_guard",
error_level,
f"#ifndef header guard has wrong style, please use: {cppvar}",
)
# Check for "//" comments on endif line.
ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, error)
match = re.match(r"#endif\s*//\s*" + cppvar + r"(_)?\b", endif)
if match:
if match.group(1) == "_":
# Issue low severity warning for deprecated double trailing underscore
error(
filename,
endif_linenum,
"build/header_guard",
0,
f'#endif line should be "#endif // {cppvar}"',
)
return
# Didn't find the corresponding "//" comment. If this file does not
# contain any "//" comments at all, it could be that the compiler
# only wants "/**/" comments, look for those instead.
no_single_line_comments = True
for i in range(1, len(raw_lines) - 1):
line = raw_lines[i]
if re.match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line):
no_single_line_comments = False
break
if no_single_line_comments:
match = re.match(r"#endif\s*/\*\s*" + cppvar + r"(_)?\s*\*/", endif)
if match:
if match.group(1) == "_":
# Low severity warning for double trailing underscore
error(
filename,
endif_linenum,
"build/header_guard",
0,
f'#endif line should be "#endif /* {cppvar} */"',
)
return
# Didn't find anything
error(
filename,
endif_linenum,
"build/header_guard",
5,
f'#endif line should be "#endif // {cppvar}"',
)
def CheckHeaderFileIncluded(filename, include_state, error):
"""Logs an error if a source file does not include its header."""
# Do not check test files
fileinfo = FileInfo(filename)
if re.search(_TEST_FILE_SUFFIX, fileinfo.BaseName()):
return
first_include = message = None
basefilename = filename[0 : len(filename) - len(fileinfo.Extension())]
for ext in GetHeaderExtensions():
headerfile = basefilename + "." + ext
if not os.path.exists(headerfile):
continue
headername = FileInfo(headerfile).RepositoryName()
include_uses_unix_dir_aliases = False
for section_list in include_state.include_list:
for f in section_list:
include_text = f[0]
if "./" in include_text:
include_uses_unix_dir_aliases = True
if headername in include_text or include_text in headername:
return
if not first_include:
first_include = f[1]
message = f"{fileinfo.RepositoryName()} should include its header file {headername}"
if include_uses_unix_dir_aliases:
message += ". Relative paths like . and .. are not allowed."
if message:
error(filename, first_include, "build/include", 5, message)
def CheckForBadCharacters(filename, lines, error):
"""Logs an error for each line containing bad characters.
Two kinds of bad characters:
1. Unicode replacement characters: These indicate that either the file
contained invalid UTF-8 (likely) or Unicode replacement characters (which
it shouldn't). Note that it's possible for this to throw off line
numbering if the invalid UTF-8 occurred adjacent to a newline.
2. NUL bytes. These are problematic for some tools.
Args:
filename: The name of the current file.
lines: An array of strings, each representing a line of the file.
error: The function to call with any errors found.
"""
for linenum, line in enumerate(lines):
if "\ufffd" in line:
error(
filename,
linenum,
"readability/utf8",
5,
"Line contains invalid UTF-8 (or Unicode replacement character).",
)
if "\0" in line:
error(filename, linenum, "readability/nul", 5, "Line contains NUL byte.")
def CheckForNewlineAtEOF(filename, lines, error):
"""Logs an error if there is no newline char at the end of the file.
Args:
filename: The name of the current file.
lines: An array of strings, each representing a line of the file.
error: The function to call with any errors found.
"""
# The array lines() was created by adding two newlines to the
# original file (go figure), then splitting on \n.
# To verify that the file ends in \n, we just have to make sure the
# last-but-two element of lines() exists and is empty.
if len(lines) < 3 or lines[-2]:
error(
filename,
len(lines) - 2,
"whitespace/ending_newline",
5,
"Could not find a newline character at the end of the file.",
)
def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
"""Logs an error if we see /* ... */ or "..." that extend past one line.
/* ... */ comments are legit inside macros, for one line.
Otherwise, we prefer // comments, so it's ok to warn about the
other. Likewise, it's ok for strings to extend across multiple
lines, as long as a line continuation character (backslash)
terminates each line. Although not currently prohibited by the C++
style guide, it's ugly and unnecessary. We don't do well with either
in this lint program, so we warn about both.
Args:
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
error: The function to call with any errors found.
"""
line = clean_lines.elided[linenum]
# Remove all \\ (escaped backslashes) from the line. They are OK, and the
# second (escaped) slash may trigger later \" detection erroneously.
line = line.replace("\\\\", "")
if line.count("/*") > line.count("*/"):
error(
filename,
linenum,
"readability/multiline_comment",
5,
"Complex multi-line /*...*/-style comment found. "
"Lint may give bogus warnings. "
"Consider replacing these with //-style comments, "
"with #if 0...#endif, "
"or with more clearly structured multi-line comments.",
)
if (line.count('"') - line.count('\\"')) % 2:
error(
filename,
linenum,
"readability/multiline_string",
5,
'Multi-line string ("...") found. This lint script doesn\'t '
"do well with such strings, and may give bogus warnings. "
"Use C++11 raw strings or concatenation instead.",
)
# (non-threadsafe name, thread-safe alternative, validation pattern)
#
# The validation pattern is used to eliminate false positives such as:
# _rand(); // false positive due to substring match.
# ->rand(); // some member function rand().
# ACMRandom rand(seed); // some variable named rand.
# ISAACRandom rand(); // another variable named rand.
#
# Basically we require the return value of these functions to be used
# in some expression context on the same line by matching on some
# operator before the function name. This eliminates constructors and
# member function calls.
_UNSAFE_FUNC_PREFIX = r"(?:[-+*/=%^&|(<]\s*|>\s+)"
_THREADING_LIST = (
("asctime(", "asctime_r(", _UNSAFE_FUNC_PREFIX + r"asctime\([^)]+\)"),
("ctime(", "ctime_r(", _UNSAFE_FUNC_PREFIX + r"ctime\([^)]+\)"),
("getgrgid(", "getgrgid_r(", _UNSAFE_FUNC_PREFIX + r"getgrgid\([^)]+\)"),
("getgrnam(", "getgrnam_r(", _UNSAFE_FUNC_PREFIX + r"getgrnam\([^)]+\)"),
("getlogin(", "getlogin_r(", _UNSAFE_FUNC_PREFIX + r"getlogin\(\)"),
("getpwnam(", "getpwnam_r(", _UNSAFE_FUNC_PREFIX + r"getpwnam\([^)]+\)"),
("getpwuid(", "getpwuid_r(", _UNSAFE_FUNC_PREFIX + r"getpwuid\([^)]+\)"),
("gmtime(", "gmtime_r(", _UNSAFE_FUNC_PREFIX + r"gmtime\([^)]+\)"),
("localtime(", "localtime_r(", _UNSAFE_FUNC_PREFIX + r"localtime\([^)]+\)"),
("rand(", "rand_r(", _UNSAFE_FUNC_PREFIX + r"rand\(\)"),
("strtok(", "strtok_r(", _UNSAFE_FUNC_PREFIX + r"strtok\([^)]+\)"),
("ttyname(", "ttyname_r(", _UNSAFE_FUNC_PREFIX + r"ttyname\([^)]+\)"),
)
def CheckPosixThreading(filename, clean_lines, linenum, error):
"""Checks for calls to thread-unsafe functions.
Much code has been originally written without consideration of
multi-threading. Also, engineers are relying on their old experience;
they have learned posix before threading extensions were added. These
tests guide the engineers to use thread-safe functions (when using
posix directly).
Args:
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
error: The function to call with any errors found.
"""
line = clean_lines.elided[linenum]
for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST:
# Additional pattern matching check to confirm that this is the
# function we are looking for
if re.search(pattern, line):
error(
filename,
linenum,
"runtime/threadsafe_fn",
2,
"Consider using "
+ multithread_safe_func
+ "...) instead of "
+ single_thread_func
+ "...) for improved thread safety.",
)
def CheckVlogArguments(filename, clean_lines, linenum, error):
"""Checks that VLOG() is only used for defining a logging level.
For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and
VLOG(FATAL) are not.
Args:
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
error: The function to call with any errors found.
"""
line = clean_lines.elided[linenum]
if re.search(r"\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)", line):
error(
filename,
linenum,
"runtime/vlog",
5,
"VLOG() should be used with numeric verbosity level. "
"Use LOG() if you want symbolic severity levels.",
)
# Matches invalid increment: *count++, which moves pointer instead of
# incrementing a value.
_RE_PATTERN_INVALID_INCREMENT = re.compile(r"^\s*\*\w+(\+\+|--);")
def CheckInvalidIncrement(filename, clean_lines, linenum, error):
"""Checks for invalid increment *count++.
For example following function:
void increment_counter(int* count) {
*count++;
}
is invalid, because it effectively does count++, moving pointer, and should
be replaced with ++*count, (*count)++ or *count += 1.
Args:
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
error: The function to call with any errors found.
"""
line = clean_lines.elided[linenum]
if _RE_PATTERN_INVALID_INCREMENT.match(line):
error(
filename,
linenum,
"runtime/invalid_increment",
5,
"Changing pointer instead of value (or unused value of operator*).",
)
def IsMacroDefinition(clean_lines, linenum):
if re.search(r"^#define", clean_lines[linenum]):
return True
return bool(linenum > 0 and re.search(r"\\$", clean_lines[linenum - 1]))
def IsForwardClassDeclaration(clean_lines, linenum):
return re.match(r"^\s*(\btemplate\b)*.*class\s+\w+;\s*$", clean_lines[linenum])
class _BlockInfo:
"""Stores information about a generic block of code."""
def __init__(self, linenum, seen_open_brace):
self.starting_linenum = linenum
self.seen_open_brace = seen_open_brace
self.open_parentheses = 0
self.inline_asm = _NO_ASM
self.check_namespace_indentation = False
def CheckBegin(self, filename, clean_lines, linenum, error):
"""Run checks that applies to text up to the opening brace.
This is mostly for checking the text after the class identifier
and the "{", usually where the base class is specified. For other
blocks, there isn't much to check, so we always pass.
Args:
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
error: The function to call with any errors found.
"""
pass
def CheckEnd(self, filename, clean_lines, linenum, error):
"""Run checks that applies to text after the closing brace.
This is mostly used for checking end of namespace comments.
Args:
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
error: The function to call with any errors found.
"""
pass
def IsBlockInfo(self):
"""Returns true if this block is a _BlockInfo.
This is convenient for verifying that an object is an instance of
a _BlockInfo, but not an instance of any of the derived classes.
Returns:
True for this class, False for derived classes.
"""
return self.__class__ == _BlockInfo
class _ExternCInfo(_BlockInfo):
"""Stores information about an 'extern "C"' block."""
def __init__(self, linenum):
_BlockInfo.__init__(self, linenum, True)
class _ClassInfo(_BlockInfo):
"""Stores information about a class."""
def __init__(self, name, class_or_struct, clean_lines, linenum):
_BlockInfo.__init__(self, linenum, False)
self.name = name
self.is_derived = False
self.check_namespace_indentation = True
if class_or_struct == "struct":
self.access = "public"
self.is_struct = True
else:
self.access = "private"
self.is_struct = False
# Remember initial indentation level for this class. Using raw_lines here
# instead of elided to account for leading comments.
self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum])
# Try to find the end of the class. This will be confused by things like:
# class A {
# } *x = { ...
#
# But it's still good enough for CheckSectionSpacing.
self.last_line = 0
depth = 0
for i in range(linenum, clean_lines.NumLines()):
line = clean_lines.elided[i]
depth += line.count("{") - line.count("}")
if not depth:
self.last_line = i
break
def CheckBegin(self, filename, clean_lines, linenum, error):
# Look for a bare ':'
if re.search("(^|[^:]):($|[^:])", clean_lines.elided[linenum]):
self.is_derived = True
def CheckEnd(self, filename, clean_lines, linenum, error):
# If there is a DISALLOW macro, it should appear near the end of
# the class.
seen_last_thing_in_class = False
for i in range(linenum - 1, self.starting_linenum, -1):
match = re.search(
r"\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\("
+ self.name
+ r"\)",
clean_lines.elided[i],
)
if match:
if seen_last_thing_in_class:
error(
filename,
i,
"readability/constructors",
3,
match.group(1) + " should be the last thing in the class",
)
break
if not re.match(r"^\s*$", clean_lines.elided[i]):
seen_last_thing_in_class = True
# Check that closing brace is aligned with beginning of the class.
# Only do this if the closing brace is indented by only whitespaces.
# This means we will not check single-line class definitions.
indent = re.match(r"^( *)\}", clean_lines.elided[linenum])
if indent and len(indent.group(1)) != self.class_indent:
if self.is_struct:
parent = "struct " + self.name
else:
parent = "class " + self.name
error(
filename,
linenum,
"whitespace/indent",
3,
f"Closing brace should be aligned with beginning of {parent}",
)
class _ConstructorInfo(_BlockInfo):
"""Stores information about a constructor.
For detecting member initializer lists."""
def __init__(self, linenum: int):
_BlockInfo.__init__(self, linenum, seen_open_brace=False)
class _NamespaceInfo(_BlockInfo):
"""Stores information about a namespace."""
def __init__(self, name, linenum):
_BlockInfo.__init__(self, linenum, False)
self.name = name or ""
self.check_namespace_indentation = True
def CheckEnd(self, filename, clean_lines, linenum, error):
"""Check end of namespace comments."""
line = clean_lines.raw_lines[linenum]
# Check how many lines is enclosed in this namespace. Don't issue
# warning for missing namespace comments if there aren't enough
# lines. However, do apply checks if there is already an end of
# namespace comment and it's incorrect.
#
# TODO(google): We always want to check end of namespace comments
# if a namespace is large, but sometimes we also want to apply the
# check if a short namespace contained nontrivial things (something
# other than forward declarations). There is currently no logic on
# deciding what these nontrivial things are, so this check is
# triggered by namespace size only, which works most of the time.
if linenum - self.starting_linenum < 10 and not re.match(
r"^\s*};*\s*(//|/\*).*\bnamespace\b", line
):
return
# Look for matching comment at end of namespace.
#
# Note that we accept C style "/* */" comments for terminating
# namespaces, so that code that terminate namespaces inside
# preprocessor macros can be cpplint clean.
#
# We also accept stuff like "// end of namespace <name>." with the
# period at the end.
#
# Besides these, we don't accept anything else, otherwise we might
# get false negatives when existing comment is a substring of the
# expected namespace.
if self.name:
# Named namespace
if not re.match(
(r"^\s*};*\s*(//|/\*).*\bnamespace\s+" + re.escape(self.name) + r"[\*/\.\\\s]*$"),
line,
):
error(
filename,
linenum,
"readability/namespace",
5,
f'Namespace should be terminated with "// namespace {self.name}"',
)
else:
# Anonymous namespace
if not re.match(r"^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$", line):
# If "// namespace anonymous" or "// anonymous namespace (more text)",
# mention "// anonymous namespace" as an acceptable form
if re.match(r"^\s*}.*\b(namespace anonymous|anonymous namespace)\b", line):
error(
filename,
linenum,
"readability/namespace",
5,
'Anonymous namespace should be terminated with "// namespace"'
' or "// anonymous namespace"',
)
else:
error(
filename,
linenum,
"readability/namespace",
5,
'Anonymous namespace should be terminated with "// namespace"',
)
class _WrappedInfo(_BlockInfo):
"""Stores information about parentheses, initializer lists, etc.
Not exactly a block but we do need the same signature.
Needed to avoid namespace indentation false positives,
though parentheses tracking would slow us down a lot
and is effectively already done by open_parentheses."""
pass
class _MemInitListInfo(_WrappedInfo):
"""Stores information about member initializer lists."""
pass
class _PreprocessorInfo:
"""Stores checkpoints of nesting stacks when #if/#else is seen."""
def __init__(self, stack_before_if):
# The entire nesting stack before #if
self.stack_before_if = stack_before_if
# The entire nesting stack up to #else
self.stack_before_else = []
# Whether we have already seen #else or #elif
self.seen_else = False
class NestingState:
"""Holds states related to parsing braces."""
def __init__(self):
# Stack for tracking all braces. An object is pushed whenever we
# see a "{", and popped when we see a "}". Only 3 types of
# objects are possible:
# - _ClassInfo: a class or struct.
# - _NamespaceInfo: a namespace.
# - _BlockInfo: some other type of block.
self.stack: list[_BlockInfo] = []
# Top of the previous stack before each Update().
#
# Because the nesting_stack is updated at the end of each line, we
# had to do some convoluted checks to find out what is the current
# scope at the beginning of the line. This check is simplified by
# saving the previous top of nesting stack.
#
# We could save the full stack, but we only need the top. Copying
# the full nesting stack would slow down cpplint by ~10%.
self.previous_stack_top: _BlockInfo | None = None
# The number of open parentheses in the previous stack top before the last update.
# Used to prevent false indentation detection when e.g. a function parameter is indented.
# We can't use previous_stack_top, a shallow copy whose open_parentheses value is updated.
self.previous_open_parentheses = 0
# The last stack item we popped.
self.popped_top: _BlockInfo | None = None
# Stack of _PreprocessorInfo objects.
self.pp_stack = []
def SeenOpenBrace(self):
"""Check if we have seen the opening brace for the innermost block.
Returns:
True if we have seen the opening brace, False if the innermost
block is still expecting an opening brace.
"""
return (not self.stack) or self.stack[-1].seen_open_brace
def InNamespaceBody(self):
"""Check if we are currently one level inside a namespace body.
Returns:
True if top of the stack is a namespace block, False otherwise.
"""
return self.stack and isinstance(self.stack[-1], _NamespaceInfo)
def InExternC(self):
"""Check if we are currently one level inside an 'extern "C"' block.
Returns:
True if top of the stack is an extern block, False otherwise.
"""
return self.stack and isinstance(self.stack[-1], _ExternCInfo)
def InClassDeclaration(self):
"""Check if we are currently one level inside a class or struct declaration.
Returns:
True if top of the stack is a class/struct, False otherwise.
"""
return self.stack and isinstance(self.stack[-1], _ClassInfo)
def InAsmBlock(self):
"""Check if we are currently one level inside an inline ASM block.
Returns:
True if the top of the stack is a block containing inline ASM.
"""
return self.stack and self.stack[-1].inline_asm != _NO_ASM
def InTemplateArgumentList(self, clean_lines, linenum, pos):
"""Check if current position is inside template argument list.
Args:
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
pos: position just after the suspected template argument.
Returns:
True if (linenum, pos) is inside template arguments.
"""
while linenum < clean_lines.NumLines():
# Find the earliest character that might indicate a template argument
line = clean_lines.elided[linenum]
match = re.match(r"^[^{};=\[\]\.<>]*(.)", line[pos:])
if not match:
linenum += 1
pos = 0
continue
token = match.group(1)
gitextract_4uyv479r/
├── .clang-format
├── .clang-tidy
├── .cmake-format
├── .conan/
│ └── recipes/
│ └── boringssl/
│ └── conanfile.py
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── custom.md
│ │ └── feature_request.md
│ └── workflows/
│ └── main.yml
├── .gitignore
├── CLAUDE.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── README_RU.md
├── conanfile.py
├── cpplint.py
├── depends/
│ └── cmake/
│ ├── CamouflageTLS.cmake
│ ├── FetchBase64.cmake
│ ├── FetchLibTunTap.cmake
│ ├── FetchWintun.cmake
│ └── NtpClient.cmake
├── deploy/
│ ├── docker/
│ │ ├── Dockerfile
│ │ ├── config/
│ │ │ ├── supervisor/
│ │ │ │ ├── dns-server.conf
│ │ │ │ └── fptn-server.conf
│ │ │ └── supervisord.conf
│ │ └── scripts/
│ │ ├── start-dns-server.sh
│ │ ├── start-fptn.sh
│ │ └── token-generator.py
│ ├── linux/
│ │ ├── deb/
│ │ │ ├── README.md
│ │ │ ├── create-client-cli-deb-package.sh
│ │ │ ├── create-client-gui-deb-package.sh
│ │ │ └── create-server-deb-package.sh
│ │ └── wifi/
│ │ ├── README.md
│ │ ├── README.ru.md
│ │ ├── dnsmasq/
│ │ │ ├── fptn-dnsmasq.conf
│ │ │ └── fptn-dnsmasq.service
│ │ ├── fptn-setup-network/
│ │ │ ├── fptn-setup-network.service
│ │ │ └── fptn-setup-network.sh
│ │ └── hostapd/
│ │ ├── fptn-hostapd.conf
│ │ └── fptn-hostapd.service
│ ├── macos/
│ │ ├── README.md
│ │ ├── assets/
│ │ │ └── FptnClient.icns
│ │ ├── create-pkg.py
│ │ └── scripts/
│ │ ├── fptn-client-cli-wrapper.sh
│ │ ├── fptn-client-gui-wrapper.sh
│ │ ├── post_install.sh
│ │ └── uninstall.sh
│ ├── sni/
│ │ ├── global.sni
│ │ └── russia.sni
│ └── windows/
│ ├── .gitignore
│ ├── README.md
│ ├── conan-replace-version.py
│ ├── create-installer.py
│ └── installer/
│ ├── app.manifest
│ └── fptn-installer.iss
├── docker-compose/
│ ├── .gitignore
│ ├── README.md
│ └── docker-compose.yml
├── docs/
│ ├── CNAME
│ ├── index-ru.html
│ └── index.html
├── pyproject.toml
├── renovate.json
├── src/
│ ├── common/
│ │ ├── client_id.h
│ │ ├── data/
│ │ │ ├── channel.h
│ │ │ └── channel_async.h
│ │ ├── jwt_token/
│ │ │ └── token_manager.h
│ │ ├── logger/
│ │ │ └── logger.h
│ │ ├── network/
│ │ │ ├── ip_address.h
│ │ │ ├── ip_packet.h
│ │ │ ├── ipv4_generator.h
│ │ │ ├── ipv6_generator.h
│ │ │ ├── ipv6_utils.h
│ │ │ ├── net_interface.h
│ │ │ ├── resolv.h
│ │ │ ├── tun/
│ │ │ │ ├── darwin_tun_device.h
│ │ │ │ ├── linux_tun_device.h
│ │ │ │ └── win_tun_device.h
│ │ │ └── utils.h
│ │ ├── system/
│ │ │ └── command.h
│ │ ├── user/
│ │ │ └── common_user_manager.h
│ │ └── utils/
│ │ ├── base64.h
│ │ └── utils.h
│ ├── fptn-client/
│ │ ├── CMakeLists.txt
│ │ ├── config/
│ │ │ ├── config_file.cpp
│ │ │ └── config_file.h
│ │ ├── fptn-client-cli.cpp
│ │ ├── fptn-client-gui.cpp
│ │ ├── gui/
│ │ │ ├── autostart/
│ │ │ │ └── autostart.h
│ │ │ ├── autoupdate/
│ │ │ │ └── autoupdate.h
│ │ │ ├── resources/
│ │ │ │ ├── resources.qrc
│ │ │ │ └── translations/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── fptn_en.ts
│ │ │ │ └── fptn_ru.ts
│ │ │ ├── server_menu_item_widget/
│ │ │ │ ├── server_menu_item_widget.cpp
│ │ │ │ └── server_menu_item_widget.h
│ │ │ ├── settingsmodel/
│ │ │ │ ├── settingsmodel.cpp
│ │ │ │ └── settingsmodel.h
│ │ │ ├── settingswidget/
│ │ │ │ ├── settings.cpp
│ │ │ │ └── settings.h
│ │ │ ├── sni_autoscan_dialog/
│ │ │ │ ├── sni_autoscan_dialog.cpp
│ │ │ │ └── sni_autoscan_dialog.h
│ │ │ ├── sni_manager/
│ │ │ │ ├── sni_manager.cpp
│ │ │ │ └── sni_manager.h
│ │ │ ├── speedwidget/
│ │ │ │ ├── speedwidget.cpp
│ │ │ │ └── speedwidget.h
│ │ │ ├── style/
│ │ │ │ ├── style.cpp
│ │ │ │ └── style.h
│ │ │ ├── tokendialog/
│ │ │ │ ├── tokendialog.cpp
│ │ │ │ └── tokendialog.h
│ │ │ ├── translations/
│ │ │ │ ├── translations.cpp
│ │ │ │ └── translations.h
│ │ │ └── tray/
│ │ │ ├── tray.cpp
│ │ │ └── tray.h
│ │ ├── plugins/
│ │ │ ├── base_plugin.h
│ │ │ ├── blacklist/
│ │ │ │ ├── domain_blacklist.cpp
│ │ │ │ └── domain_blacklist.h
│ │ │ └── split/
│ │ │ ├── tunneling.cpp
│ │ │ └── tunneling.h
│ │ ├── routing/
│ │ │ ├── route_manager.cpp
│ │ │ └── route_manager.h
│ │ ├── utils/
│ │ │ ├── brotli/
│ │ │ │ └── brotli.h
│ │ │ ├── macos/
│ │ │ │ └── admin.h
│ │ │ ├── signal/
│ │ │ │ └── main_loop.h
│ │ │ ├── speed_estimator/
│ │ │ │ ├── server_info.h
│ │ │ │ ├── speed_estimator.cpp
│ │ │ │ └── speed_estimator.h
│ │ │ ├── utils.h
│ │ │ └── windows/
│ │ │ └── vpn_conflict.h
│ │ └── vpn/
│ │ ├── http/
│ │ │ ├── client.cpp
│ │ │ └── client.h
│ │ ├── vpn_client.cpp
│ │ └── vpn_client.h
│ ├── fptn-passwd/
│ │ ├── CMakeLists.txt
│ │ └── fptn-passwd.cpp
│ ├── fptn-protocol-lib/
│ │ ├── CMakeLists.txt
│ │ ├── https/
│ │ │ ├── api_client/
│ │ │ │ ├── api_client.cpp
│ │ │ │ └── api_client.h
│ │ │ ├── censorship_strategy.h
│ │ │ ├── obfuscator/
│ │ │ │ ├── methods/
│ │ │ │ │ ├── detector.h
│ │ │ │ │ ├── obfuscator_interface.h
│ │ │ │ │ ├── tls/
│ │ │ │ │ │ ├── tls_obfuscator.cpp
│ │ │ │ │ │ └── tls_obfuscator.h
│ │ │ │ │ └── tls2/
│ │ │ │ │ ├── tls_obfuscator2.cpp
│ │ │ │ │ └── tls_obfuscator2.h
│ │ │ │ └── tcp_stream/
│ │ │ │ └── tcp_stream.h
│ │ │ ├── utils/
│ │ │ │ ├── change_cipher_spec.h
│ │ │ │ └── tls/
│ │ │ │ ├── tls.cpp
│ │ │ │ └── tls.h
│ │ │ └── websocket_client/
│ │ │ ├── websocket_client.cpp
│ │ │ └── websocket_client.h
│ │ ├── protobuf/
│ │ │ ├── protocol.cpp
│ │ │ ├── protocol.h
│ │ │ └── protocol.proto
│ │ └── time/
│ │ ├── time_provider.cpp
│ │ └── time_provider.h
│ └── fptn-server/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── client/
│ │ ├── session.cpp
│ │ └── session.h
│ ├── config/
│ │ ├── command_line_config.cpp
│ │ └── command_line_config.h
│ ├── filter/
│ │ ├── filters/
│ │ │ ├── antiscan/
│ │ │ │ ├── antiscan.cpp
│ │ │ │ └── antiscan.h
│ │ │ ├── base_filter.h
│ │ │ └── bittorrent/
│ │ │ ├── bittorrent.cpp
│ │ │ └── bittorrent.h
│ │ ├── manager.cpp
│ │ └── manager.h
│ ├── fptn-server.cpp
│ ├── nat/
│ │ ├── table.cpp
│ │ └── table.h
│ ├── network/
│ │ ├── virtual_interface.cpp
│ │ └── virtual_interface.h
│ ├── routing/
│ │ ├── iptables.cpp
│ │ └── iptables.h
│ ├── statistic/
│ │ ├── metrics.cpp
│ │ └── metrics.h
│ ├── traffic_shaper/
│ │ ├── leaky_bucket.cpp
│ │ └── leaky_bucket.h
│ ├── user/
│ │ ├── user_manager.cpp
│ │ └── user_manager.h
│ ├── vpn/
│ │ ├── manager.cpp
│ │ └── manager.h
│ └── web/
│ ├── api/
│ │ └── handle.h
│ ├── handshake/
│ │ ├── handshake_cache_manager.cpp
│ │ └── handshake_cache_manager.h
│ ├── listener/
│ │ ├── listener.cpp
│ │ └── listener.h
│ ├── server.cpp
│ ├── server.h
│ └── session/
│ ├── session.cpp
│ └── session.h
├── sysadmin-tools/
│ ├── grafana/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── configs/
│ │ │ ├── grafana/
│ │ │ │ ├── dashboards/
│ │ │ │ │ ├── dashboards.yaml
│ │ │ │ │ ├── fptn_dashboard.json
│ │ │ │ │ └── node-exporter-full.json
│ │ │ │ └── datasources/
│ │ │ │ └── datasources.yaml
│ │ │ ├── nginx/
│ │ │ │ └── nginx.conf.template
│ │ │ └── prometheus/
│ │ │ └── prometheus.yaml.template
│ │ ├── docker-compose.build.yml
│ │ ├── docker-compose.yml
│ │ └── proxy-server/
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── CMakeLists.txt
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── conanfile.py
│ │ └── src/
│ │ └── proxy-server.cpp
│ └── telegram-bot/
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── configs/
│ │ ├── premium_servers.json.demo
│ │ ├── servers.json.demo
│ │ └── servers_censored_zone.json.demo
│ ├── docker-compose.yml
│ └── src/
│ ├── bot.py
│ └── requirements.txt
└── tests/
├── CMakeLists.txt
├── common/
│ ├── CMakeLists.txt
│ ├── data/
│ │ └── ChannelTest.cpp
│ └── network/
│ ├── ClientStopRaceTest.cpp
│ ├── IPv4GeneratorTest.cpp
│ ├── IPv6GeneratorTest.cpp
│ ├── IPv6UtilsTest.cpp
│ └── TunDeviceTest.cpp
├── fptnlib/
│ ├── CMakeLists.txt
│ └── api_client/
│ └── ApiClientTest.cpp
└── server/
├── CMakeLists.txt
├── filter/
│ └── antiscan/
│ └── AntiScanTest.cpp
└── statistic/
└── MetricTest.cpp
SYMBOL INDEX (589 symbols across 127 files)
FILE: .conan/recipes/boringssl/conanfile.py
class BoringSSLConan (line 6) | class BoringSSLConan(ConanFile):
method config_options (line 14) | def config_options(self):
method layout (line 18) | def layout(self):
method source (line 21) | def source(self):
method generate (line 25) | def generate(self):
method build (line 47) | def build(self):
method package (line 52) | def package(self):
method package_info (line 56) | def package_info(self):
FILE: conanfile.py
class FPTN (line 12) | class FPTN(ConanFile):
method requirements (line 118) | def requirements(self):
method build_requirements (line 131) | def build_requirements(self):
method generate (line 140) | def generate(self):
method build (line 154) | def build(self):
method package (line 159) | def package(self):
method package_info (line 239) | def package_info(self):
method config_options (line 270) | def config_options(self):
method export (line 276) | def export(self):
method _register_local_recipe (line 279) | def _register_local_recipe(self, recipe, name, version, override=False...
FILE: cpplint.py
class ErrorSuppressions (line 969) | class ErrorSuppressions:
class LineRange (line 972) | class LineRange:
method __init__ (line 975) | def __init__(self, begin, end):
method __str__ (line 979) | def __str__(self):
method __contains__ (line 982) | def __contains__(self, obj):
method ContainsRange (line 985) | def ContainsRange(self, other):
method __init__ (line 988) | def __init__(self):
method _AddSuppression (line 992) | def _AddSuppression(self, category, line_range):
method GetOpenBlockStart (line 997) | def GetOpenBlockStart(self):
method AddGlobalSuppression (line 1001) | def AddGlobalSuppression(self, category):
method AddLineSuppression (line 1005) | def AddLineSuppression(self, category, linenum):
method StartBlockSuppression (line 1009) | def StartBlockSuppression(self, category, linenum):
method EndBlockSuppression (line 1015) | def EndBlockSuppression(self, linenum):
method IsSuppressed (line 1021) | def IsSuppressed(self, category, linenum):
method HasOpenBlock (line 1026) | def HasOpenBlock(self):
method Clear (line 1030) | def Clear(self):
function ProcessHppHeadersOption (line 1041) | def ProcessHppHeadersOption(val):
function ProcessIncludeOrderOption (line 1049) | def ProcessIncludeOrderOption(val):
function IsHeaderExtension (line 1059) | def IsHeaderExtension(file_extension):
function GetHeaderExtensions (line 1063) | def GetHeaderExtensions():
function GetAllExtensions (line 1073) | def GetAllExtensions():
function ProcessExtensionsOption (line 1077) | def ProcessExtensionsOption(val):
function GetNonHeaderExtensions (line 1090) | def GetNonHeaderExtensions():
function ParseNolintSuppressions (line 1094) | def ParseNolintSuppressions(filename, raw_line, linenum, error):
function ProcessGlobalSuppressions (line 1167) | def ProcessGlobalSuppressions(filename: str, lines: list[str]) -> None:
function ResetNolintSuppressions (line 1186) | def ResetNolintSuppressions():
function IsErrorSuppressedByNolint (line 1191) | def IsErrorSuppressedByNolint(category, linenum):
function _IsSourceExtension (line 1207) | def _IsSourceExtension(s):
class _IncludeState (line 1212) | class _IncludeState:
method __init__ (line 1251) | def __init__(self):
method FindHeader (line 1257) | def FindHeader(self, header):
method ResetSection (line 1272) | def ResetSection(self, directive):
method SetLastHeader (line 1290) | def SetLastHeader(self, header_path):
method CanonicalizeAlphabeticalOrder (line 1293) | def CanonicalizeAlphabeticalOrder(self, header_path):
method IsInAlphabeticalOrder (line 1308) | def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
method CheckNextIncludeOrder (line 1329) | def CheckNextIncludeOrder(self, header_type):
class _CppLintState (line 1389) | class _CppLintState:
method __init__ (line 1392) | def __init__(self):
method SetOutputFormat (line 1417) | def SetOutputFormat(self, output_format):
method SetQuiet (line 1421) | def SetQuiet(self, quiet):
method SetVerboseLevel (line 1427) | def SetVerboseLevel(self, level):
method SetCountingStyle (line 1433) | def SetCountingStyle(self, counting_style):
method SetFilters (line 1437) | def SetFilters(self, filters):
method AddFilters (line 1455) | def AddFilters(self, filters):
method BackupFilters (line 1466) | def BackupFilters(self):
method RestoreFilters (line 1470) | def RestoreFilters(self):
method ResetErrorCounts (line 1474) | def ResetErrorCounts(self):
method IncrementErrorCount (line 1479) | def IncrementErrorCount(self, category):
method PrintErrorCounts (line 1489) | def PrintErrorCounts(self):
method PrintInfo (line 1496) | def PrintInfo(self, message):
method PrintError (line 1502) | def PrintError(self, message):
method AddJUnitFailure (line 1508) | def AddJUnitFailure(self, filename, linenum, message, category, confid...
method FormatJUnitXML (line 1511) | def FormatJUnitXML(self):
function _OutputFormat (line 1558) | def _OutputFormat():
function _SetOutputFormat (line 1563) | def _SetOutputFormat(output_format):
function _Quiet (line 1568) | def _Quiet():
function _SetQuiet (line 1573) | def _SetQuiet(quiet):
function _VerboseLevel (line 1578) | def _VerboseLevel():
function _SetVerboseLevel (line 1583) | def _SetVerboseLevel(level):
function _SetCountingStyle (line 1588) | def _SetCountingStyle(level):
function _Filters (line 1593) | def _Filters():
function _SetFilters (line 1598) | def _SetFilters(filters):
function _AddFilters (line 1611) | def _AddFilters(filters):
function _BackupFilters (line 1624) | def _BackupFilters():
function _RestoreFilters (line 1629) | def _RestoreFilters():
class _FunctionState (line 1634) | class _FunctionState:
method __init__ (line 1640) | def __init__(self):
method Begin (line 1645) | def Begin(self, function_name):
method Count (line 1655) | def Count(self):
method Check (line 1660) | def Check(self, error, filename, linenum):
method End (line 1691) | def End(self):
class _IncludeError (line 1696) | class _IncludeError(Exception):
class FileInfo (line 1702) | class FileInfo:
method __init__ (line 1709) | def __init__(self, filename):
method FullName (line 1712) | def FullName(self):
method RepositoryName (line 1716) | def RepositoryName(self):
method Split (line 1781) | def Split(self):
method BaseName (line 1795) | def BaseName(self):
method Extension (line 1799) | def Extension(self):
method NoExtension (line 1803) | def NoExtension(self):
method IsSource (line 1807) | def IsSource(self):
function _ShouldPrintError (line 1812) | def _ShouldPrintError(category, confidence, filename, linenum):
function Error (line 1844) | def Error(filename, linenum, category, confidence, message):
function IsCppString (line 1920) | def IsCppString(line):
function CleanseRawStrings (line 1937) | def CleanseRawStrings(raw_lines):
function FindNextMultiLineCommentStart (line 2012) | def FindNextMultiLineCommentStart(lines, lineix):
function FindNextMultiLineCommentEnd (line 2023) | def FindNextMultiLineCommentEnd(lines, lineix):
function RemoveMultiLineCommentsFromRange (line 2032) | def RemoveMultiLineCommentsFromRange(lines, begin, end):
function RemoveMultiLineComments (line 2040) | def RemoveMultiLineComments(filename, lines, error):
function CleanseComments (line 2061) | def CleanseComments(line):
function ReplaceAlternateTokens (line 2077) | def ReplaceAlternateTokens(line):
class CleansedLines (line 2098) | class CleansedLines:
method __init__ (line 2109) | def __init__(self, lines):
method NumLines (line 2123) | def NumLines(self):
method _CollapseStrings (line 2128) | def _CollapseStrings(elided):
function FindEndOfExpressionInLine (line 2195) | def FindEndOfExpressionInLine(line, startpos, stack):
function CloseExpression (line 2273) | def CloseExpression(clean_lines, linenum, pos):
function FindStartOfExpressionInLine (line 2317) | def FindStartOfExpressionInLine(line, endpos, stack):
function ReverseCloseExpression (line 2397) | def ReverseCloseExpression(clean_lines, linenum, pos):
function CheckForCopyright (line 2435) | def CheckForCopyright(filename, lines, error):
function GetIndentLevel (line 2454) | def GetIndentLevel(line):
function PathSplitToList (line 2468) | def PathSplitToList(path):
function GetHeaderGuardCPPVariable (line 2494) | def GetHeaderGuardCPPVariable(filename):
function CheckForHeaderGuard (line 2575) | def CheckForHeaderGuard(filename, clean_lines, error, cppvar):
function CheckHeaderFileIncluded (line 2698) | def CheckHeaderFileIncluded(filename, include_state, error):
function CheckForBadCharacters (line 2732) | def CheckForBadCharacters(filename, lines, error):
function CheckForNewlineAtEOF (line 2762) | def CheckForNewlineAtEOF(filename, lines, error):
function CheckForMultilineCommentsAndStrings (line 2785) | def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, ...
function CheckPosixThreading (line 2862) | def CheckPosixThreading(filename, clean_lines, linenum, error):
function CheckVlogArguments (line 2895) | def CheckVlogArguments(filename, clean_lines, linenum, error):
function CheckInvalidIncrement (line 2924) | def CheckInvalidIncrement(filename, clean_lines, linenum, error):
function IsMacroDefinition (line 2951) | def IsMacroDefinition(clean_lines, linenum):
function IsForwardClassDeclaration (line 2958) | def IsForwardClassDeclaration(clean_lines, linenum):
class _BlockInfo (line 2962) | class _BlockInfo:
method __init__ (line 2965) | def __init__(self, linenum, seen_open_brace):
method CheckBegin (line 2972) | def CheckBegin(self, filename, clean_lines, linenum, error):
method CheckEnd (line 2987) | def CheckEnd(self, filename, clean_lines, linenum, error):
method IsBlockInfo (line 3000) | def IsBlockInfo(self):
class _ExternCInfo (line 3012) | class _ExternCInfo(_BlockInfo):
method __init__ (line 3015) | def __init__(self, linenum):
class _ClassInfo (line 3019) | class _ClassInfo(_BlockInfo):
method __init__ (line 3022) | def __init__(self, name, class_or_struct, clean_lines, linenum):
method CheckBegin (line 3052) | def CheckBegin(self, filename, clean_lines, linenum, error):
method CheckEnd (line 3057) | def CheckEnd(self, filename, clean_lines, linenum, error):
class _ConstructorInfo (line 3100) | class _ConstructorInfo(_BlockInfo):
method __init__ (line 3104) | def __init__(self, linenum: int):
class _NamespaceInfo (line 3108) | class _NamespaceInfo(_BlockInfo):
method __init__ (line 3111) | def __init__(self, name, linenum):
method CheckEnd (line 3116) | def CheckEnd(self, filename, clean_lines, linenum, error):
class _WrappedInfo (line 3185) | class _WrappedInfo(_BlockInfo):
class _MemInitListInfo (line 3195) | class _MemInitListInfo(_WrappedInfo):
class _PreprocessorInfo (line 3201) | class _PreprocessorInfo:
method __init__ (line 3204) | def __init__(self, stack_before_if):
class NestingState (line 3215) | class NestingState:
method __init__ (line 3218) | def __init__(self):
method SeenOpenBrace (line 3249) | def SeenOpenBrace(self):
method InNamespaceBody (line 3258) | def InNamespaceBody(self):
method InExternC (line 3266) | def InExternC(self):
method InClassDeclaration (line 3274) | def InClassDeclaration(self):
method InAsmBlock (line 3282) | def InAsmBlock(self):
method InTemplateArgumentList (line 3290) | def InTemplateArgumentList(self, clean_lines, linenum, pos):
method UpdatePreprocessor (line 3344) | def UpdatePreprocessor(self, line):
method _Pop (line 3400) | def _Pop(self):
method _CountOpenParentheses (line 3404) | def _CountOpenParentheses(self, line: str):
method _UpdateNamesapce (line 3429) | def _UpdateNamesapce(self, line: str, linenum: int) -> str | None:
method _UpdateConstructor (line 3456) | def _UpdateConstructor(self, line: str, linenum: int, class_name: str ...
method Update (line 3473) | def Update(self, filename: str, clean_lines: CleansedLines, linenum: i...
method InnermostClass (line 3639) | def InnermostClass(self):
function CheckForNonStandardConstructs (line 3652) | def CheckForNonStandardConstructs(filename, clean_lines, linenum, nestin...
function CheckSpacingForFunctionCall (line 3878) | def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error):
function IsBlankLine (line 3973) | def IsBlankLine(line):
function CheckForNamespaceIndentation (line 3988) | def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, l...
function CheckForFunctionLengths (line 4000) | def CheckForFunctionLengths(filename, clean_lines, linenum, function_sta...
function CheckComment (line 4073) | def CheckComment(line, filename, linenum, next_line_start, error):
function CheckSpacing (line 4145) | def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
function CheckOperatorSpacing (line 4283) | def CheckOperatorSpacing(filename, clean_lines, linenum, error):
function CheckParenthesisSpacing (line 4402) | def CheckParenthesisSpacing(filename, clean_lines, linenum, error):
function CheckCommaSpacing (line 4454) | def CheckCommaSpacing(filename, clean_lines, linenum, error):
function _IsType (line 4490) | def _IsType(clean_lines, nesting_state, expr):
function CheckBracesSpacing (line 4551) | def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, er...
function IsDecltype (line 4649) | def IsDecltype(clean_lines, linenum, column):
function CheckSectionSpacing (line 4665) | def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):
function GetPreviousNonBlankLine (line 4729) | def GetPreviousNonBlankLine(clean_lines, linenum):
function CheckBraces (line 4752) | def CheckBraces(filename, clean_lines, linenum, error):
function CheckTrailingSemicolon (line 4932) | def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
function CheckEmptyBlockBody (line 5090) | def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
function FindCheckMacro (line 5209) | def FindCheckMacro(line):
function CheckCheck (line 5232) | def CheckCheck(filename, clean_lines, linenum, error):
function CheckAltTokens (line 5356) | def CheckAltTokens(filename, clean_lines, linenum, error):
function GetLineWidth (line 5392) | def GetLineWidth(line):
function CheckStyle (line 5423) | def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_s...
function _DropCommonSuffixes (line 5577) | def _DropCommonSuffixes(filename):
function _ClassifyInclude (line 5615) | def _ClassifyInclude(fileinfo, include, used_angle_brackets, include_ord...
function CheckIncludeLine (line 5690) | def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
function _GetTextInside (line 5809) | def _GetTextInside(text, start_pattern):
function CheckLanguage (line 5901) | def CheckLanguage(
function CheckGlobalStatic (line 6116) | def CheckGlobalStatic(filename, clean_lines, linenum, error):
function CheckPrintf (line 6195) | def CheckPrintf(filename, clean_lines, linenum, error):
function IsDerivedFunction (line 6234) | def IsDerivedFunction(clean_lines, linenum):
function IsOutOfLineMethodDefinition (line 6254) | def IsOutOfLineMethodDefinition(clean_lines, linenum):
function IsInitializerList (line 6270) | def IsInitializerList(clean_lines, linenum):
function CheckForNonConstReference (line 6312) | def CheckForNonConstReference(filename, clean_lines, linenum, nesting_st...
function CheckCasts (line 6457) | def CheckCasts(filename, clean_lines, linenum, error):
function CheckCStyleCast (line 6610) | def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, ...
function ExpectingFunctionArgs (line 6666) | def ExpectingFunctionArgs(clean_lines, linenum):
function FilesBelongToSameModule (line 6908) | def FilesBelongToSameModule(filename_cc, filename_h):
function CheckForIncludeWhatYouUse (line 6964) | def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, erro...
function CheckMakePairUsesDeduction (line 7040) | def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
function CheckRedundantVirtual (line 7065) | def CheckRedundantVirtual(filename, clean_lines, linenum, error):
function CheckRedundantOverrideOrFinal (line 7140) | def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error):
function IsBlockInNameSpace (line 7174) | def IsBlockInNameSpace(nesting_state: NestingState, is_forward_declarati...
function ShouldCheckNamespaceIndentation (line 7206) | def ShouldCheckNamespaceIndentation(
function CheckItemIndentationInNamespace (line 7261) | def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, lin...
function ProcessLine (line 7269) | def ProcessLine(
function FlagCxxHeaders (line 7323) | def FlagCxxHeaders(filename, clean_lines, linenum, error):
function ProcessFileData (line 7355) | def ProcessFileData(filename, file_extension, lines, error, extra_check_...
function ProcessConfigOverrides (line 7427) | def ProcessConfigOverrides(filename):
function ProcessFile (line 7518) | def ProcessFile(filename, vlevel, extra_check_functions=None):
function PrintUsage (line 7613) | def PrintUsage(message):
function PrintVersion (line 7635) | def PrintVersion():
function PrintCategories (line 7642) | def PrintCategories():
function ParseArguments (line 7651) | def ParseArguments(args):
function _ParseFilterSelector (line 7769) | def _ParseFilterSelector(parameter):
function _ExpandDirectories (line 7798) | def _ExpandDirectories(filenames):
function _FilterExcludedFiles (line 7827) | def _FilterExcludedFiles(fnames):
function _IsParentOrSame (line 7840) | def _IsParentOrSame(parent, child):
function main (line 7859) | def main():
FILE: deploy/docker/scripts/token-generator.py
function get_md5_fingerprint (line 11) | def get_md5_fingerprint(cert_path="/etc/fptn/server.crt"):
function generate_token (line 26) | def generate_token(username, password, server_ip, service_name, md5_fing...
function main (line 41) | def main():
FILE: deploy/macos/create-pkg.py
function run_command (line 22) | def run_command(command, cwd=None):
function create_app (line 36) | def create_app(
function create_pkg (line 136) | def create_pkg(app_path: pathlib.Path, version: str) -> bool:
FILE: deploy/windows/conan-replace-version.py
function replace_version (line 5) | def replace_version(file_path, new_version):
FILE: deploy/windows/create-installer.py
function is_windows_x86_64 (line 38) | def is_windows_x86_64() -> bool:
function is_arm_64 (line 45) | def is_arm_64() -> bool:
function download_file (line 50) | def download_file(url: str, destination_path: pathlib.Path) -> bool:
function replace_values_in_innosetupfile (line 63) | def replace_values_in_innosetupfile(file_path: pathlib.Path, replacement...
function run_command (line 75) | def run_command(command: str) -> str:
function get_conan_path (line 89) | def get_conan_path() -> pathlib.Path:
function copy_qt_libraries (line 98) | def copy_qt_libraries(frameworks_path: pathlib.Path):
function compile_inno_setup_script (line 124) | def compile_inno_setup_script(script_path: pathlib.Path, output_dir: pat...
FILE: src/common/client_id.h
function namespace (line 11) | namespace fptn {
FILE: src/common/data/channel.h
function namespace (line 23) | namespace fptn::common::data {
FILE: src/common/data/channel_async.h
function namespace (line 29) | namespace fptn::common::data {
FILE: src/common/jwt_token/token_manager.h
function namespace (line 22) | namespace fptn::common::jwt_token {
function std (line 76) | const std::string& ServerCrtPath() const noexcept { return server_crt_pa...
FILE: src/common/logger/logger.h
function namespace (line 33) | namespace fptn::logger {
FILE: src/common/network/ip_address.h
function namespace (line 19) | namespace fptn::common::network {
FILE: src/common/network/ip_packet.h
function namespace (line 77) | namespace fptn::common::network {
function ComputeCalculateFields (line 168) | void ComputeCalculateFields() noexcept {
function SetClientId (line 189) | void SetClientId(fptn::ClientID client_id) noexcept {
function SetDstIPv4Address (line 193) | void SetDstIPv4Address(const pcpp::IPv4Address& dst) noexcept {
function SetSrcIPv4Address (line 200) | void SetSrcIPv4Address(const pcpp::IPv4Address& src) noexcept {
function SetDstIPv6Address (line 207) | void SetDstIPv6Address(const pcpp::IPv6Address& dst) noexcept {
function SetSrcIPv6Address (line 213) | void SetSrcIPv6Address(const pcpp::IPv6Address& src) noexcept {
function IsICMPv4 (line 219) | bool IsICMPv4() const {
function IsDns (line 229) | bool IsDns() const {
function pcpp (line 351) | const pcpp::RawPacket* GetRawPacket() const noexcept {
function virtual (line 378) | virtual pcpp::IPv4Layer* IPv4Layer() noexcept { return ipv4_layer_; }
function virtual (line 379) | virtual pcpp::IPv6Layer* IPv6Layer() noexcept { return ipv6_layer_; }
function class (line 403) | class LightIPv4Packet {
function LightIPv4Packet (line 421) | const LightIPv4Packet* GetRawPacket() const { return this; }
FILE: src/common/network/ipv4_generator.h
function namespace (line 29) | namespace fptn::common::network {
FILE: src/common/network/ipv6_generator.h
function namespace (line 32) | namespace fptn::common::network {
FILE: src/common/network/ipv6_utils.h
function namespace (line 15) | namespace fptn::common::network::ipv6 {
FILE: src/common/network/net_interface.h
function namespace (line 25) | namespace fptn::common::network {
function StopImpl (line 222) | bool StopImpl() noexcept {
function SendImpl (line 242) | bool SendImpl(IPPacketPtr packet) noexcept {
function namespace (line 319) | namespace fptn::common::network {
FILE: src/common/network/resolv.h
function namespace (line 19) | namespace fptn::common::network {
function ResolveResult (line 35) | inline ResolveResult ResolveWithTimeout(boost::asio::io_context& ioc,
FILE: src/common/network/tun/darwin_tun_device.h
function namespace (line 26) | namespace fptn::common::network {
FILE: src/common/network/tun/linux_tun_device.h
function namespace (line 14) | namespace fptn::common::network {
FILE: src/common/network/tun/win_tun_device.h
function namespace (line 28) | namespace fptn::common::network {
FILE: src/common/network/utils.h
function namespace (line 24) | namespace fptn::common::network {
FILE: src/common/system/command.h
function namespace (line 26) | namespace fptn::common::system::command {
function run (line 52) | inline bool run(
FILE: src/common/user/common_user_manager.h
function namespace (line 23) | namespace fptn::common::user {
function Authenticate (line 81) | bool Authenticate(const std::string& username, const std::string& passwo...
function GetUserBandwidthBit (line 94) | int GetUserBandwidthBit(const std::string& username) const {
function GetUserBandwidth (line 103) | int GetUserBandwidth(const std::string& username) const {
FILE: src/common/utils/base64.h
function namespace (line 12) | namespace fptn::common::utils::base64 {
FILE: src/common/utils/utils.h
function namespace (line 23) | namespace fptn::common::utils {
function std (line 64) | inline std::string Trim(const std::string& str) {
function std (line 74) | inline std::vector<std::string> SplitCommaSeparated(const std::string& i...
function std (line 88) | inline std::string ToLowerCase(const std::string& str) {
FILE: src/fptn-client/config/config_file.cpp
type fptn::config (line 22) | namespace fptn::config {
function ServerInfo (line 105) | ServerInfo ConfigFile::FindFastestServer(int timeout_sec) const {
FILE: src/fptn-client/config/config_file.h
function namespace (line 18) | namespace fptn::config {
FILE: src/fptn-client/fptn-client-cli.cpp
function main (line 35) | int main(int argc, char* argv[]) {
FILE: src/fptn-client/fptn-client-gui.cpp
function SignalHandler (line 27) | void SignalHandler(int) { QApplication::quit(); }
function BOOL (line 29) | BOOL WINAPI SignalHandler(DWORD ctrlType) {
function main (line 42) | int main(int argc, char* argv[]) {
FILE: src/fptn-client/gui/autostart/autostart.h
function namespace (line 23) | namespace fptn::gui::autostart {
FILE: src/fptn-client/gui/autoupdate/autoupdate.h
function namespace (line 24) | namespace fptn::gui::autoupdate {
FILE: src/fptn-client/gui/server_menu_item_widget/server_menu_item_widget.cpp
function QPixmap (line 15) | const QPixmap& GetPingIcon(const int ping_ms) {
type fptn::gui (line 36) | namespace fptn::gui {
function QString (line 120) | QString ServerMenuItemWidget::ServerName() const { return name_; }
FILE: src/fptn-client/gui/server_menu_item_widget/server_menu_item_widget.h
function QLabel (line 33) | QLabel* name_label_{nullptr};
FILE: src/fptn-client/gui/settingsmodel/settingsmodel.cpp
function ParseServers (line 43) | QVector<ServerConfig> ParseServers(const QJsonArray& servers_array) {
function SplitStringToVector (line 61) | QVector<QString> SplitStringToVector(const QString& str) {
function QString (line 73) | QString JoinVectorToString(const QVector<QString>& vec) {
function QString (line 135) | QString SettingsModel::GetSettingsFilePath() const {
function QString (line 141) | QString SettingsModel::GetSettingsFolderPath() const {
function QString (line 302) | QString SettingsModel::LanguageName() const {
function QString (line 311) | const QString& SettingsModel::LanguageCode() const {
function QString (line 315) | const QString& SettingsModel::DefaultLanguageCode() const {
function ServiceConfig (line 424) | ServiceConfig SettingsModel::ParseToken(const QString& token) {
function QString (line 453) | QString SettingsModel::UsingNetworkInterface() const {
function QString (line 461) | QString SettingsModel::GatewayIp() const {
function QString (line 470) | QString SettingsModel::SNI() const {
function QString (line 534) | QString SettingsModel::BypassMethod() const {
function QString (line 587) | QString SettingsModel::SplitTunnelMode() const {
FILE: src/fptn-client/gui/settingsmodel/settingsmodel.h
function namespace (line 28) | namespace fptn::gui {
type ServiceConfig (line 97) | struct ServiceConfig {
function class (line 106) | class SettingsModel : public QObject {
FILE: src/fptn-client/gui/settingswidget/settings.cpp
function QString (line 34) | QString CleanDomain(const QString& domain) {
function QString (line 54) | QString VectorToText(const QVector<QString>& items) { return items.join(...
function TextToVector (line 56) | QVector<QString> TextToVector(const QString& text) {
FILE: src/fptn-client/gui/settingswidget/settings.h
function namespace (line 25) | namespace fptn::gui {
FILE: src/fptn-client/gui/sni_autoscan_dialog/sni_autoscan_dialog.cpp
type fptn::gui (line 24) | namespace fptn::gui {
FILE: src/fptn-client/gui/sni_manager/sni_manager.cpp
type fptn::gui (line 16) | namespace fptn::gui {
FILE: src/fptn-client/gui/sni_manager/sni_manager.h
function namespace (line 13) | namespace fptn::gui {
FILE: src/fptn-client/gui/speedwidget/speedwidget.cpp
function QString (line 13) | QString FormatSpeed(std::size_t bytes_per_sec) {
function QString (line 31) | QString FormatSpeedLabel(const QString& text, std::size_t speed) {
FILE: src/fptn-client/gui/speedwidget/speedwidget.h
function namespace (line 15) | namespace fptn::gui {
FILE: src/fptn-client/gui/style/style.cpp
type fptn::gui (line 9) | namespace fptn::gui {
function QString (line 11) | QString GetMacStyleSheet() {
function QString (line 41) | QString GetUbuntuStyleSheet() {
function QString (line 158) | QString GetWindowsStyleSheet() {
FILE: src/fptn-client/gui/style/style.h
function namespace (line 11) | namespace fptn::gui {
FILE: src/fptn-client/gui/tokendialog/tokendialog.cpp
function QString (line 61) | const QString& TokenDialog::Token() const { return token_; }
FILE: src/fptn-client/gui/tokendialog/tokendialog.h
function namespace (line 17) | namespace fptn::gui {
FILE: src/fptn-client/gui/translations/translations.cpp
function QTranslator (line 17) | QTranslator& getTranslator() {
FILE: src/fptn-client/gui/translations/translations.h
function namespace (line 11) | namespace fptn::gui {
FILE: src/fptn-client/gui/tray/tray.cpp
function QPixmap (line 46) | QPixmap LoadIcon(const QString& icon_path, int size = 12) {
function ShowError (line 55) | void ShowError(const QString& title, const QString& msg) {
function ShowWarning (line 69) | void ShowWarning(const QString& title, const QString& msg) {
function QString (line 649) | QString TrayApp::GetSystemLanguageCode() const {
FILE: src/fptn-client/gui/tray/tray.h
type class (line 43) | enum class
function QTimer (line 124) | QTimer* ping_update_timer_{nullptr};
FILE: src/fptn-client/plugins/base_plugin.h
function namespace (line 15) | namespace fptn::plugin {
FILE: src/fptn-client/plugins/blacklist/domain_blacklist.cpp
type fptn::plugin (line 19) | namespace fptn::plugin {
FILE: src/fptn-client/plugins/blacklist/domain_blacklist.h
function namespace (line 22) | namespace fptn::plugin {
FILE: src/fptn-client/plugins/split/tunneling.cpp
type fptn::plugin (line 20) | namespace fptn::plugin {
FILE: src/fptn-client/plugins/split/tunneling.h
function namespace (line 21) | namespace fptn::plugin {
FILE: src/fptn-client/routing/route_manager.cpp
function GetWindowsInterfaceNumber (line 23) | std::string GetWindowsInterfaceNumber(const std::string& interface_name) {
function ParseIPv4CIDR (line 79) | std::pair<std::string, std::string> ParseIPv4CIDR(const std::string& net...
function ParseIPv6CIDR (line 106) | std::pair<std::string, int> ParseIPv6CIDR(const std::string& network) {
function GetLinuxDnsServers (line 128) | std::vector<std::string> GetLinuxDnsServers(const std::string& interface) {
function AddIPv4RouteToSystem (line 151) | bool AddIPv4RouteToSystem(const std::string& destination,
function AddIPv6RouteToSystem (line 190) | bool AddIPv6RouteToSystem(const std::string& destination,
function RemoveIPv4RouteFromSystem (line 227) | bool RemoveIPv4RouteFromSystem(const std::string& destination,
function RemoveIPv6RouteFromSystem (line 264) | bool RemoveIPv6RouteFromSystem(const std::string& destination,
FILE: src/fptn-client/routing/route_manager.h
function namespace (line 18) | namespace fptn::routing {
function UpdateTunInterfaceName (line 65) | void UpdateTunInterfaceName(const std::string& name) {
FILE: src/fptn-client/utils/brotli/brotli.h
function namespace (line 14) | namespace fptn::utils::brotli {
FILE: src/fptn-client/utils/macos/admin.h
function namespace (line 23) | namespace fptn::utils::macos {
FILE: src/fptn-client/utils/signal/main_loop.h
function namespace (line 14) | namespace fptn::utils {
FILE: src/fptn-client/utils/speed_estimator/server_info.h
function namespace (line 12) | namespace fptn::utils::speed_estimator {
FILE: src/fptn-client/utils/speed_estimator/speed_estimator.cpp
type fptn::utils::speed_estimator (line 29) | namespace fptn::utils::speed_estimator {
function GetDownloadTimeMs (line 31) | std::uint64_t GetDownloadTimeMs(const ServerInfo& server,
function ServerInfo (line 56) | ServerInfo FindFastestServer(const std::string& sni,
FILE: src/fptn-client/utils/speed_estimator/speed_estimator.h
function namespace (line 17) | namespace fptn::utils::speed_estimator {
FILE: src/fptn-client/utils/utils.h
function namespace (line 15) | namespace fptn::utils {
FILE: src/fptn-client/utils/windows/vpn_conflict.h
function namespace (line 14) | namespace fptn::utils::windows {
FILE: src/fptn-client/vpn/http/client.h
function namespace (line 22) | namespace fptn::vpn::http {
FILE: src/fptn-client/vpn/vpn_client.cpp
type fptn::vpn (line 16) | namespace fptn::vpn {
FILE: src/fptn-client/vpn/vpn_client.h
function namespace (line 24) | namespace fptn::vpn {
FILE: src/fptn-passwd/fptn-passwd.cpp
function GetPassword (line 17) | std::string GetPassword(const std::string& prompt) {
function main (line 34) | int main(int argc, char* argv[]) {
FILE: src/fptn-protocol-lib/https/api_client/api_client.cpp
function DecompressGzip (line 61) | std::string DecompressGzip(const std::string& compressed) {
function GetHttpBody (line 92) | std::string GetHttpBody(
function SetSocketTimeouts (line 101) | void SetSocketTimeouts(
function Headers (line 124) | Headers RealBrowserHeaders() {
function TResult (line 187) | TResult ExecuteWithTimeout(const std::function<TResult()>& operation,
type fptn::protocol::https (line 245) | namespace fptn::protocol::https {
function Response (line 278) | Response ApiClient::Get(const std::string& handle, int timeout) const {
function Response (line 289) | Response ApiClient::Post(const std::string& handle,
function ApiClient (line 314) | ApiClient ApiClient::Clone() const {
function Response (line 373) | Response ApiClient::GetImpl(const std::string& handle, int timeout) co...
function Response (line 539) | Response ApiClient::PostImpl(const std::string& handle,
FILE: src/fptn-protocol-lib/https/api_client/api_client.h
function namespace (line 19) | namespace fptn::protocol::https {
FILE: src/fptn-protocol-lib/https/censorship_strategy.h
function namespace (line 9) | namespace fptn::protocol::https {
FILE: src/fptn-protocol-lib/https/obfuscator/methods/detector.h
function namespace (line 18) | namespace fptn::protocol::https::obfuscator {
FILE: src/fptn-protocol-lib/https/obfuscator/methods/tls/tls_obfuscator.cpp
type TLSAppDataRecordHeader (line 37) | struct TLSAppDataRecordHeader {
function HostToNetwork16 (line 57) | std::uint16_t HostToNetwork16(const std::uint16_t value) {
function NetworkToHost16 (line 61) | std::uint16_t NetworkToHost16(const std::uint16_t value) {
function GetRandomData (line 65) | std::uint64_t GetRandomData() {
function GetRandomByte (line 71) | std::uint8_t GetRandomByte(
function GenerateRandomPadding (line 78) | std::vector<std::uint8_t> GenerateRandomPadding(const std::size_t length) {
function ApplyXorTransform (line 86) | void ApplyXorTransform(
type fptn::protocol::https::obfuscator (line 95) | namespace fptn::protocol::https::obfuscator {
function PreparedData (line 118) | PreparedData TlsObfuscator::Deobfuscate() {
function PreparedData (line 215) | PreparedData TlsObfuscator::Obfuscate(
FILE: src/fptn-protocol-lib/https/obfuscator/methods/tls/tls_obfuscator.h
function namespace (line 16) | namespace fptn::protocol::https::obfuscator {
FILE: src/fptn-protocol-lib/https/obfuscator/methods/tls2/tls_obfuscator2.cpp
type TLSAppDataRecordHeader (line 34) | struct TLSAppDataRecordHeader {
function HostToNetwork16 (line 52) | std::uint16_t HostToNetwork16(const std::uint16_t value) {
function NetworkToHost16 (line 56) | std::uint16_t NetworkToHost16(const std::uint16_t value) {
function HostToNetwork32 (line 60) | std::uint32_t HostToNetwork32(const std::uint32_t value) {
function NetworkToHost32 (line 64) | std::uint32_t NetworkToHost32(const std::uint32_t value) {
function IsValidTimestamp (line 68) | bool IsValidTimestamp(const std::uint32_t timestamp) {
function GetRandomData (line 76) | std::uint64_t GetRandomData() {
function GetRandomByte (line 82) | std::uint8_t GetRandomByte(
function GetRandomPaddingLength (line 89) | std::uint16_t GetRandomPaddingLength() {
function GenerateRandomPadding (line 95) | std::vector<std::uint8_t> GenerateRandomPadding(const std::size_t length) {
function ApplyXorTransform (line 103) | void ApplyXorTransform(
type fptn::protocol::https::obfuscator (line 112) | namespace fptn::protocol::https::obfuscator {
function PreparedData (line 132) | PreparedData TlsObfuscator2::Deobfuscate() {
function PreparedData (line 214) | PreparedData TlsObfuscator2::Obfuscate(
FILE: src/fptn-protocol-lib/https/obfuscator/methods/tls2/tls_obfuscator2.h
function namespace (line 16) | namespace fptn::protocol::https::obfuscator {
FILE: src/fptn-protocol-lib/https/obfuscator/tcp_stream/tcp_stream.h
function namespace (line 20) | namespace fptn::protocol::https::obfuscator {
function namespace (line 290) | namespace boost::beast {
FILE: src/fptn-protocol-lib/https/utils/change_cipher_spec.h
function namespace (line 11) | namespace fptn::protocol::https::utils {
FILE: src/fptn-protocol-lib/https/utils/tls/tls.cpp
type fptn::protocol::https::utils (line 30) | namespace fptn::protocol::https::utils {
function GetSHA1Hash (line 37) | std::string GetSHA1Hash(std::uint32_t number) {
function GenerateFptnKey (line 69) | std::string GenerateFptnKey(std::uint32_t timestamp) {
function SetDecoyHandshakeSessionID (line 80) | bool SetDecoyHandshakeSessionID(SSL* ssl) {
function IsDecoyHandshakeSessionID (line 96) | bool IsDecoyHandshakeSessionID(
function IsDecoyHandshakeSessionID2 (line 119) | bool IsDecoyHandshakeSessionID2(
function SetHandshakeSessionID (line 142) | bool SetHandshakeSessionID(SSL* ssl) {
function IsFptnClientSessionID (line 158) | bool IsFptnClientSessionID(
function SetHandshakeSni (line 180) | bool SetHandshakeSni(SSL* ssl, const std::string& sni) {
function SSL_CTX (line 193) | SSL_CTX* CreateNewSslCtx() {
function ChromeCiphers (line 267) | std::string ChromeCiphers() {
function GetCertificateMD5Fingerprint (line 285) | std::string GetCertificateMD5Fingerprint(const X509* cert) {
function GenerateDecoyTlsHandshake (line 301) | std::vector<std::uint8_t> GenerateDecoyTlsHandshake(const std::string&...
function GenerateDecoyTlsSessionId (line 352) | std::optional<std::array<std::uint8_t, 32>> GenerateDecoyTlsSessionId() {
function GenerateDecoyTlsSessionId2 (line 368) | std::optional<std::array<std::uint8_t, 32>> GenerateDecoyTlsSessionId2...
function AttachCertificateVerificationCallback (line 390) | void AttachCertificateVerificationCallback(
function AttachCertificateVerificationCallbackDelete (line 429) | void AttachCertificateVerificationCallbackDelete(SSL* ssl) {
FILE: src/fptn-protocol-lib/https/utils/tls/tls.h
function namespace (line 18) | namespace fptn::protocol::https::utils {
FILE: src/fptn-protocol-lib/https/websocket_client/websocket_client.cpp
type fptn::protocol::https (line 23) | namespace fptn::protocol::https {
FILE: src/fptn-protocol-lib/protobuf/protocol.cpp
type fptn::protocol::protobuf (line 23) | namespace fptn::protocol::protobuf {
function ProtoPayloadOpt (line 24) | ProtoPayloadOpt GetProtoPayload(const boost::beast::flat_buffer& buffe...
function ProtoPayloadOpt (line 69) | ProtoPayloadOpt CreateProtoPayload(fptn::common::network::IPPacketPtr ...
FILE: src/fptn-protocol-lib/protobuf/protocol.h
function namespace (line 17) | namespace fptn::protocol::protobuf {
FILE: src/fptn-protocol-lib/time/time_provider.cpp
type fptn::time (line 15) | namespace fptn::time {
FILE: src/fptn-protocol-lib/time/time_provider.h
function namespace (line 18) | namespace fptn::time {
FILE: src/fptn-server/client/session.cpp
function IPv4Address (line 37) | const IPv4Address& Session::ClientIPv4() const noexcept { return client_...
function IPv4Address (line 39) | const IPv4Address& Session::FakeClientIPv4() const noexcept {
function IPv6Address (line 43) | const IPv6Address& Session::ClientIPv6() const noexcept { return client_...
function IPv6Address (line 45) | const IPv6Address& Session::FakeClientIPv6() const noexcept {
FILE: src/fptn-server/client/session.h
function namespace (line 21) | namespace fptn::client {
FILE: src/fptn-server/config/command_line_config.cpp
function ParseBoolean (line 19) | bool ParseBoolean(std::string value) noexcept {
type fptn::config (line 32) | namespace fptn::config {
function IPv4Address (line 172) | IPv4Address CommandLineConfig::TunInterfaceIPv4() const {
function IPv4Address (line 176) | IPv4Address CommandLineConfig::TunInterfaceNetworkIPv4Address() const {
function IPv6Address (line 184) | IPv6Address CommandLineConfig::TunInterfaceIPv6() const {
function IPv6Address (line 188) | IPv6Address CommandLineConfig::TunInterfaceNetworkIPv6Address() const {
FILE: src/fptn-server/config/command_line_config.h
function namespace (line 16) | namespace fptn::config {
FILE: src/fptn-server/filter/filters/antiscan/antiscan.cpp
function IPPacketPtr (line 42) | IPPacketPtr AntiScan::apply(IPPacketPtr packet) const {
FILE: src/fptn-server/filter/filters/antiscan/antiscan.h
function namespace (line 15) | namespace fptn::filter {
FILE: src/fptn-server/filter/filters/base_filter.h
function namespace (line 13) | namespace fptn::filter {
FILE: src/fptn-server/filter/filters/bittorrent/bittorrent.cpp
function DetectBitTorrent (line 26) | bool DetectBitTorrent(const std::uint8_t* payload, std::size_t payload_s...
function IPPacketPtr (line 56) | IPPacketPtr BitTorrent::apply(IPPacketPtr packet) const {
FILE: src/fptn-server/filter/filters/bittorrent/bittorrent.h
function namespace (line 11) | namespace fptn::filter {
FILE: src/fptn-server/filter/manager.cpp
function IPPacketPtr (line 25) | IPPacketPtr Manager::Apply(IPPacketPtr packet) const noexcept {
FILE: src/fptn-server/filter/manager.h
function namespace (line 16) | namespace fptn::filter {
FILE: src/fptn-server/fptn-server.cpp
function WaitForSignal (line 33) | void WaitForSignal() {
function main (line 41) | int main(int argc, char* argv[]) {
FILE: src/fptn-server/nat/table.h
function namespace (line 26) | namespace fptn::nat {
FILE: src/fptn-server/network/virtual_interface.h
function namespace (line 20) | namespace fptn::network {
FILE: src/fptn-server/routing/iptables.cpp
function RunCommand (line 29) | bool RunCommand(const std::string& command) noexcept {
function SetMaxFileDescriptors (line 47) | bool SetMaxFileDescriptors() {
FILE: src/fptn-server/routing/iptables.h
function namespace (line 13) | namespace fptn::routing {
FILE: src/fptn-server/statistic/metrics.h
function namespace (line 20) | namespace fptn::statistic {
FILE: src/fptn-server/traffic_shaper/leaky_bucket.h
function namespace (line 15) | namespace fptn::traffic_shaper {
FILE: src/fptn-server/user/user_manager.h
function namespace (line 16) | namespace fptn::user {
FILE: src/fptn-server/vpn/manager.h
function namespace (line 19) | namespace fptn::vpn {
FILE: src/fptn-server/web/api/handle.h
function namespace (line 22) | namespace fptn::web {
FILE: src/fptn-server/web/handshake/handshake_cache_manager.cpp
type fptn::web (line 25) | namespace fptn::web {
function HandshakeResponse (line 34) | HandshakeResponse HandshakeCacheManager::CheckCache(
FILE: src/fptn-server/web/handshake/handshake_cache_manager.h
function namespace (line 19) | namespace fptn::web {
FILE: src/fptn-server/web/listener/listener.h
function namespace (line 25) | namespace fptn::web {
FILE: src/fptn-server/web/server.h
function namespace (line 30) | namespace fptn::web {
FILE: src/fptn-server/web/session/session.cpp
function GetServerIpAddresses (line 43) | std::vector<std::string> GetServerIpAddresses(
function SetSocketTimeouts (line 65) | void SetSocketTimeouts(boost::asio::ip::tcp::socket& socket, int timeout...
type fptn::web (line 78) | namespace fptn::web {
FILE: src/fptn-server/web/session/session.h
function namespace (line 32) | namespace fptn::web {
FILE: sysadmin-tools/grafana/proxy-server/conanfile.py
class FptnProxy (line 13) | class FptnProxy(ConanFile):
method requirements (line 36) | def requirements(self):
method layout (line 39) | def layout(self):
method generate (line 42) | def generate(self):
method build (line 52) | def build(self):
method config_options (line 57) | def config_options(self):
method _clone_fptn (line 60) | def _clone_fptn(self):
method _register_local_recipe (line 69) | def _register_local_recipe(self, recipe, name, version, override=False...
FILE: sysadmin-tools/grafana/proxy-server/src/proxy-server.cpp
function run_server (line 17) | void run_server(const int port) {
function main (line 56) | int main(int argc, char* argv[]) {
FILE: sysadmin-tools/telegram-bot/src/bot.py
function init_logger (line 55) | def init_logger():
class UserManager (line 68) | class UserManager:
method __init__ (line 69) | def __init__(self, users_file: Path):
method _generate_password (line 73) | def _generate_password(self, length=8) -> str:
method _hash_password (line 76) | def _hash_password(self, password: str) -> str:
method load_users (line 81) | def load_users(self) -> dict:
method save_users (line 98) | def save_users(self, users: dict):
method is_premium_user (line 106) | def is_premium_user(self, user_id: str) -> bool:
method register_user (line 114) | def register_user(self, user_id: str) -> (str, str):
method is_registered (line 133) | def is_registered(self, user_id: str) -> bool:
method reset_password (line 140) | def reset_password(self, user_id: str) -> (str, str):
function start (line 161) | async def start(update: Update, context: CallbackContext) -> None:
function generate_token (line 186) | def generate_token(username: str, password: str, is_premium: bool) -> str:
function generate_access_link (line 202) | def generate_access_link(token: str) -> str:
function get_access_token (line 209) | async def get_access_token(update: Update, context: CallbackContext) -> ...
function send_credentials_file (line 253) | async def send_credentials_file(update: Update, context: CallbackContext...
function main (line 272) | def main() -> None:
FILE: tests/common/data/ChannelTest.cpp
function TEST (line 15) | TEST(ChannelTest, PushAndWaitForPacket) {
function TEST (line 32) | TEST(ChannelTest, WaitForPacketTimeout) {
FILE: tests/common/network/ClientStopRaceTest.cpp
class MockWebSocket (line 28) | class MockWebSocket {
method MockWebSocket (line 30) | MockWebSocket() : stopped_(false), run_entered_(false) {}
method Run (line 32) | void Run() {
method Stop (line 42) | bool Stop() {
method RunEntered (line 48) | bool RunEntered() const { return run_entered_; }
method RunExited (line 49) | bool RunExited() const { return run_exited_; }
class MockClient (line 62) | class MockClient {
method Start (line 64) | bool Start() {
method StopFixed (line 72) | bool StopFixed() {
method IsRunning (line 88) | bool IsRunning() const { return running_; }
method WsRunExited (line 90) | bool WsRunExited() const { return ws_ && ws_->RunExited(); }
method RunLoop (line 93) | void RunLoop() {
function TEST (line 113) | TEST(ClientStopRaceTest, StopWhileRunning) {
function TEST (line 126) | TEST(ClientStopRaceTest, RapidStartStopCycles) {
function TEST (line 140) | TEST(ClientStopRaceTest, ImmediateStop) {
function TEST (line 148) | TEST(ClientStopRaceTest, ThreadExitsBeforeReset) {
FILE: tests/common/network/IPv4GeneratorTest.cpp
function TEST (line 13) | TEST(IPv4GeneratorTest, InitialAddress) {
function TEST (line 27) | TEST(IPv4GeneratorTest, NumAvaliableAddresses) {
function TEST (line 43) | TEST(IPv4GeneratorTest, SmallDifficultNetsMask) {
function TEST (line 53) | TEST(IPGeneratorTest, BigDifficultNetsMask) {
FILE: tests/common/network/IPv6GeneratorTest.cpp
function canonical (line 20) | std::string canonical(const std::string& full_hex) {
function TEST (line 25) | TEST(IPv6GeneratorTest, InitialAddress) {
function TEST (line 45) | TEST(IPv6GeneratorTest, NumAvaliableAddresses) {
function TEST (line 66) | TEST(IPv6GeneratorTest, SmallDifficultNetsMask) {
function TEST (line 81) | TEST(IPv6GeneratorTest, BigDifficultNetsMask) {
FILE: tests/common/network/IPv6UtilsTest.cpp
function TEST (line 22) | TEST(IPv6UtilsTest, ToStringMatchesPcppFormat) {
function TEST (line 45) | TEST(IPv6UtilsTest, ServerDefaultSubnetMatchesPcpp) {
function TEST (line 68) | TEST(IPv6UtilsTest, ToStringFormatsMatchPcppForVariousAddresses) {
FILE: tests/common/network/TunDeviceTest.cpp
function MakeMinimalIPv4Packet (line 31) | std::vector<std::uint8_t> MakeMinimalIPv4Packet() {
function MakeMinimalIPv6Packet (line 57) | std::vector<std::uint8_t> MakeMinimalIPv6Packet() {
type SharedMockState (line 110) | struct SharedMockState {
method RecordWrite (line 115) | void RecordWrite(const void* data, int size) {
method FeedRead (line 121) | int FeedRead(void* buffer, int max_size) {
method InjectPacket (line 133) | void InjectPacket(std::vector<std::uint8_t> data) {
method Clear (line 138) | void Clear() {
class MockTunDevice (line 150) | class MockTunDevice {
method Open (line 152) | bool Open(const std::string& name) {
method Close (line 157) | void Close() {}
method ConfigureIPv4 (line 161) | bool ConfigureIPv4(const std::string& /*addr*/, int /*mask*/) { return...
method ConfigureIPv6 (line 164) | bool ConfigureIPv6(const std::string& /*addr*/, int /*mask*/) { return...
method SetNonBlocking (line 167) | void SetNonBlocking(bool /*enabled*/) {}
method SetMTU (line 170) | void SetMTU(int /*mtu*/) {}
method BringUp (line 173) | void BringUp() {}
method SetStopFlag (line 176) | void SetStopFlag(const std::atomic<bool>* /*running*/) {}
method Read (line 180) | int Read(void* buffer, int size) {
method Write (line 186) | int Write(const void* data, int size) {
type fptn::common::network (line 197) | namespace fptn::common::network {
class GenericTunInterface (line 200) | class GenericTunInterface
class GenericTunInterfaceTest (line 209) | class GenericTunInterfaceTest : public ::testing::Test {
method SetUp (line 211) | void SetUp() override { g_mock_state = std::make_shared<SharedMockStat...
method TearDown (line 213) | void TearDown() override {
method MakeConfig (line 218) | static MockTunInterface::Config MakeConfig() {
function TEST_F (line 231) | TEST_F(GenericTunInterfaceTest, StartAndStop) {
function TEST_F (line 238) | TEST_F(GenericTunInterfaceTest, SendIPv4Packet) {
function TEST_F (line 258) | TEST_F(GenericTunInterfaceTest, SendIPv6Packet) {
function TEST_F (line 278) | TEST_F(GenericTunInterfaceTest, ReceiveIPv4Packet) {
function TEST_F (line 314) | TEST_F(GenericTunInterfaceTest, ReceiveIPv6Packet) {
function TEST_F (line 350) | TEST_F(GenericTunInterfaceTest, SendMultipleMixedPackets) {
function TEST_F (line 375) | TEST_F(GenericTunInterfaceTest, DeviceNameUpdatedAfterStart) {
class DarwinAfHeaderTest (line 391) | class DarwinAfHeaderTest : public ::testing::Test {
method SetUp (line 393) | void SetUp() override {
method TearDown (line 397) | void TearDown() override {
function TEST_F (line 405) | TEST_F(DarwinAfHeaderTest, WriteIPv4PrependsCorrectAfHeader) {
function TEST_F (line 428) | TEST_F(DarwinAfHeaderTest, WriteIPv6PrependsCorrectAfHeader) {
function TEST_F (line 451) | TEST_F(DarwinAfHeaderTest, ReadStripsAfHeaderIPv4) {
function TEST_F (line 473) | TEST_F(DarwinAfHeaderTest, ReadStripsAfHeaderIPv6) {
function TEST_F (line 495) | TEST_F(DarwinAfHeaderTest, RoundTripIPv6Preserved) {
FILE: tests/fptnlib/api_client/ApiClientTest.cpp
function TEST (line 29) | TEST(ApiClientTest, GitHubHandshakeTest) {
FILE: tests/server/filter/antiscan/AntiScanTest.cpp
class MockIPv4Packet (line 21) | class MockIPv4Packet : public fptn::common::network::IPPacket {
method MockIPv4Packet (line 23) | explicit MockIPv4Packet(const pcpp::IPv4Address& addr) {
method IsIPv4 (line 27) | bool IsIPv4() const noexcept override { return true; }
method IsIPv6 (line 29) | bool IsIPv6() const noexcept override { return false; }
class MockIPv6Packet (line 37) | class MockIPv6Packet : public fptn::common::network::IPPacket {
method MockIPv6Packet (line 39) | explicit MockIPv6Packet(const pcpp::IPv6Address& addr) {
method IsIPv4 (line 43) | bool IsIPv4() const noexcept override { return false; }
method IsIPv6 (line 45) | bool IsIPv6() const noexcept override { return true; }
function TEST (line 55) | TEST(AntiScanTest, BlockScan) {
function TEST (line 93) | TEST(AntiScanTest, AllowNonScanPacket) {
function TEST (line 133) | TEST(AntiScanTest, BlockScanIPv6) {
function TEST (line 165) | TEST(AntiScanTest, AllowNonScanPacketIPv6) {
FILE: tests/server/statistic/MetricTest.cpp
function TEST (line 13) | TEST(MetricsTest, UpdateTraffic) {
Condensed preview — 233 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,622K chars).
[
{
"path": ".clang-format",
"chars": 2423,
"preview": "# Use the Google style in this project.\nLanguage: Cpp\nBasedOnStyle: Google\nStandard: c++11\n# Some folks prefer to write "
},
{
"path": ".clang-tidy",
"chars": 7154,
"preview": "---\n# Configure clang-tidy for this project.\n\n# Here is an explanation for why some of the checks are disabled:\n#\n# -go"
},
{
"path": ".cmake-format",
"chars": 8911,
"preview": "_help_parse: Options affecting listfile parsing\r\nparse:\r\n _help_additional_commands:\r\n - Specify structure for custo"
},
{
"path": ".conan/recipes/boringssl/conanfile.py",
"chars": 2754,
"preview": "from conan import ConanFile\nfrom conan.tools.files import get\nfrom conan.tools.cmake import CMake, CMakeToolchain, cmake"
},
{
"path": ".dockerignore",
"chars": 85,
"preview": ".cache\n.vscode\n.vsconan\n.git/\nREADME.md\nLICENSE\nCMakeUserPresets.json\nCI/\ntmp/\nkeys/\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/custom.md",
"chars": 126,
"preview": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".github/workflows/main.yml",
"chars": 13943,
"preview": "name: Build and Test\n\non:\n push:\n branches:\n - '**'\n tags:\n - '*'\n release:\n types: [ published ]\ne"
},
{
"path": ".gitignore",
"chars": 924,
"preview": ".env\n.cache/\n.vsconan/\nTesting/\nconan_provider.cmake\n.conan_provider.cmake\nlogs/\n*.dmg\n*.deb\n*.pkg\n.vs/\n.vscode/\ntmp/\nke"
},
{
"path": "CLAUDE.md",
"chars": 2344,
"preview": "# CLAUDE.md\n\nBehavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as nee"
},
{
"path": "CMakeLists.txt",
"chars": 8493,
"preview": "cmake_minimum_required(VERSION 3.16)\nproject(fptn VERSION \"${FPTN_VERSION}\" LANGUAGES CXX)\n\n# project settings\nset(CMAKE"
},
{
"path": "LICENSE",
"chars": 1068,
"preview": "MIT License\n\nCopyright (c) 2024 Stas Skokov\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "README.md",
"chars": 10625,
"preview": "<div align=\"center\">\r\n\r\n<H1>FPTN</H1>\r\n<H6>Custom VPN technology</H6>\r\n\r\n[\\[English\\]](README.md)\r\n•\r\n[\\[Русский\\]](READ"
},
{
"path": "README_RU.md",
"chars": 10550,
"preview": "<div align=\"center\">\r\n\r\n<H1>FPTN</H1>\r\n<H6>Custom VPN technology</H6>\r\n\r\n[\\[English\\]](README.md)\r\n•\r\n[\\[Русский\\]](READ"
},
{
"path": "conanfile.py",
"chars": 10635,
"preview": "import os\nimport subprocess\n\nfrom conan import ConanFile\nfrom conan.tools.cmake import CMakeToolchain, CMake\nfrom conan."
},
{
"path": "cpplint.py",
"chars": 299453,
"preview": "#!/usr/bin/env python\n#\n# Copyright (c) 2009 Google Inc. All rights reserved.\n#\n# Redistribution and use in source and b"
},
{
"path": "depends/cmake/CamouflageTLS.cmake",
"chars": 608,
"preview": "include(FetchContent)\n\nFetchContent_Declare(CamouflageTLS URL https://github.com/fptn-project/camouflage-tls/archive/ref"
},
{
"path": "depends/cmake/FetchBase64.cmake",
"chars": 591,
"preview": "include(FetchContent)\n\nFetchContent_Declare(Base64 URL https://github.com/tobiaslocker/base64/archive/refs/heads/master."
},
{
"path": "depends/cmake/FetchLibTunTap.cmake",
"chars": 1008,
"preview": "cmake_minimum_required(VERSION 3.16)\n\noption(BUILD_TESTING \"Build tests\" OFF)\n\ninclude(FetchContent)\nFetchContent_Declar"
},
{
"path": "depends/cmake/FetchWintun.cmake",
"chars": 1071,
"preview": "include(FetchContent)\n\nFetchContent_Declare(Wintun URL https://www.wintun.net/builds/wintun-0.14.1.zip)\n\nFetchContent_Ge"
},
{
"path": "depends/cmake/NtpClient.cmake",
"chars": 563,
"preview": "include(FetchContent)\n\nFetchContent_Declare(ntp_client URL https://github.com/batchar2/NTP-client/archive/refs/heads/mas"
},
{
"path": "deploy/docker/Dockerfile",
"chars": 1221,
"preview": "FROM ubuntu:24.04\n\nARG FPTN_SERVER_PATH\nARG FPTN_PASSWD_PATH\n\nEXPOSE 443/tcp\n\nENV DEBIAN_FRONTEND=noninteractive\nENV TZ="
},
{
"path": "deploy/docker/config/supervisor/dns-server.conf",
"chars": 260,
"preview": "[program:dns-server]\ncommand=/usr/local/bin/start-dns-server.sh\nautostart=true\nautorestart=true\nstartretries=3\nstartsecs"
},
{
"path": "deploy/docker/config/supervisor/fptn-server.conf",
"chars": 255,
"preview": "[program:fptn-server]\ncommand=/usr/local/bin/start-fptn.sh\nautostart=true\nautorestart=true\nstartretries=3\nstartsecs=5\nst"
},
{
"path": "deploy/docker/config/supervisord.conf",
"chars": 397,
"preview": "[supervisord]\nuser=root\nnodaemon=true\nlogfile=/dev/stdout\nlogfile_maxbytes=0\npidfile=/var/run/supervisord.pid\n\n[rpcinter"
},
{
"path": "deploy/docker/scripts/start-dns-server.sh",
"chars": 3643,
"preview": "#!/bin/bash\n\n\nstart_unbound() {\n echo \"Starting unbound...\"\n\n DO_IP6=\"no\"\n [ \"${DNS_IPV6_ENABLE:-false}\" = \"tru"
},
{
"path": "deploy/docker/scripts/start-fptn.sh",
"chars": 951,
"preview": "#!/bin/bash\n\nexport OUT_NETWORK_INTERFACE=$(ip -o -4 route show to default | awk '{print $5}')\necho \"[FPTN] Using networ"
},
{
"path": "deploy/docker/scripts/token-generator.py",
"chars": 2497,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport base64\nimport json\nimport subprocess\nimport os\nimport sys\n\n\ndef get_md5_f"
},
{
"path": "deploy/linux/deb/README.md",
"chars": 1070,
"preview": "### Setup GitHub Runner\n\nBefore setting up the GitHub runner, you need to install the following packages on your system:"
},
{
"path": "deploy/linux/deb/create-client-cli-deb-package.sh",
"chars": 6782,
"preview": "#!/usr/bin/env bash\n\n# Function to print usage\nprint_usage() {\n echo \"Usage: $0 <fptn-client-cli-path> <version>\"\n "
},
{
"path": "deploy/linux/deb/create-client-gui-deb-package.sh",
"chars": 6036,
"preview": "#!/usr/bin/env bash\n\nprint_usage() {\n echo \"Usage: $0 <fptn-client-path> <icon-path> <version> <sni-source-dir>\"\n "
},
{
"path": "deploy/linux/deb/create-server-deb-package.sh",
"chars": 6156,
"preview": "#!/usr/bin/env bash\n\n# Function to print usage\nprint_usage() {\n echo \"Usage: $0 <fptn-server-path> <fptn-passwd-path>"
},
{
"path": "deploy/linux/wifi/README.md",
"chars": 3490,
"preview": "### Setting Up a WiFi-VPN Access Point on Raspberry Pi\n\nThis guide outlines the process of setting up a WiFi access poin"
},
{
"path": "deploy/linux/wifi/README.ru.md",
"chars": 3531,
"preview": "\n### Настройка WiFi-VPN точки доступа на Raspberry Pi\n\nВ этом руководстве описан процесс настройки точки доступа WiFi на"
},
{
"path": "deploy/linux/wifi/dnsmasq/fptn-dnsmasq.conf",
"chars": 293,
"preview": "# /etc/fptn-dnsmasq.conf\n\n# REPALCE IT\ninterface=wlan0\n\nlog-facility=/var/log/dnsmasq.log\n\ndhcp-range=192.168.180.100,19"
},
{
"path": "deploy/linux/wifi/dnsmasq/fptn-dnsmasq.service",
"chars": 268,
"preview": "[Unit]\nDescription=FPTN dnsmasq Service\nAfter=network.target fptn-client.service fptn-hostapd.service\n\n[Service]\nExecSta"
},
{
"path": "deploy/linux/wifi/fptn-setup-network/fptn-setup-network.service",
"chars": 280,
"preview": "[Unit]\nDescription=FPTN dnsmasq Service\nAfter=network.target fptn-client.service fptn-hostapd.service fptn-dnsmasq.servi"
},
{
"path": "deploy/linux/wifi/fptn-setup-network/fptn-setup-network.sh",
"chars": 917,
"preview": "## /usr/sbin/fptn-setup-network.sh\n\n#replace to your\nWIFI_INTERFACE=wlan0\n\n#replace to your\nETH_INTERFACE=eth0\n\necho \"Te"
},
{
"path": "deploy/linux/wifi/hostapd/fptn-hostapd.conf",
"chars": 1502,
"preview": "# Replace with your WiFi interface\ninterface=wlan0\n# Replace with your WiFi network name\nssid=VPN-FPTN\n# Replace with yo"
},
{
"path": "deploy/linux/wifi/hostapd/fptn-hostapd.service",
"chars": 232,
"preview": "[Unit]\nDescription=FPTN hostapd Service\nAfter=network.target fptn-client.service\n\n[Service]\nExecStart=bash -c \"sleep 20 "
},
{
"path": "deploy/macos/README.md",
"chars": 217,
"preview": "# README.md\n\nRequires Python 3.6+ and macOS host with pkgbuild and productbuild available\n\npip3 install macos-pkg-builde"
},
{
"path": "deploy/macos/create-pkg.py",
"chars": 7088,
"preview": "#!/usr/bin/env python3\n\nimport os\nimport sys\nimport shutil\nimport pathlib\nimport argparse\nimport platform\nimport plistli"
},
{
"path": "deploy/macos/scripts/fptn-client-cli-wrapper.sh",
"chars": 562,
"preview": "#!/bin/bash\n\n# Check for root privileges\nif [ \"$(id -u)\" -ne 0 ]; then\n echo \"This operation requires root privileges"
},
{
"path": "deploy/macos/scripts/fptn-client-gui-wrapper.sh",
"chars": 2392,
"preview": "#!/bin/bash\n\n# Dialog display functions\nshow_error() {\n osascript -e \"display dialog \\\"$1\\\" buttons {\\\"OK\\\"} default "
},
{
"path": "deploy/macos/scripts/post_install.sh",
"chars": 1294,
"preview": "#!/bin/bash\n\nset -e\n\n# Function to show error messages\nshow_error() {\n local message=\"$1\"\n osascript <<EOF\ndisplay"
},
{
"path": "deploy/macos/scripts/uninstall.sh",
"chars": 271,
"preview": "#!/bin/bash\n\nSYMLINK_PATH=\"/usr/local/bin/fptn-client-cli\"\nif [ -L \"$SYMLINK_PATH\" ]; then\n echo \"Deleting symbolic l"
},
{
"path": "deploy/sni/global.sni",
"chars": 3619,
"preview": "speedtest.net\r\ncloudflare.com\r\ngoogle.com\r\nmicrosoft.com\r\napple.com\r\namazon.com\r\nnetflix.com\r\nyoutube.com\r\nfacebook.com\r"
},
{
"path": "deploy/sni/russia.sni",
"chars": 1231669,
"preview": "ie.ozone.ru\r\nch.fjdjdjfjd.icu\r\ncdn1.takovpn.ru\r\neh.vk.com\r\ncdn1.ozone.ru\r\nturbo-pvz-notification.ozon.ru\r\nturbo-pvz.ozon"
},
{
"path": "deploy/windows/.gitignore",
"chars": 110,
"preview": "installer/tmp.iss\r\ninstaller/data/\r\ninstaller/tmp/\r\ninstaller/result/\r\ninstaller/Output/\r\ninstaller/depends/\r\n"
},
{
"path": "deploy/windows/README.md",
"chars": 2316,
"preview": "# Setup Guide\r\n\r\n##3 Python 3.13\r\n\r\n1. Install Python 3.13 as admin.\r\n2. Add Python and `pip` to the global `PATH` envir"
},
{
"path": "deploy/windows/conan-replace-version.py",
"chars": 732,
"preview": "import re\nimport sys\n\n\ndef replace_version(file_path, new_version):\n with open(file_path, \"r\") as file:\n conte"
},
{
"path": "deploy/windows/create-installer.py",
"chars": 8752,
"preview": "import os\r\nimport re\r\nimport sys\r\nimport shutil\r\nimport pathlib\r\nimport platform\r\nimport datetime\r\nimport argparse\r\nimpo"
},
{
"path": "deploy/windows/installer/app.manifest",
"chars": 436,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\r\n <as"
},
{
"path": "deploy/windows/installer/fptn-installer.iss",
"chars": 5628,
"preview": "// replacement automatically\r\n#define APP_VERSION_NAME \"1.0.0\"\r\n// replacement automatically\r\n#define APP_VERSION_NUMBER"
},
{
"path": "docker-compose/.gitignore",
"chars": 29,
"preview": ".env\nfptn-server-data/\nlogs/\n"
},
{
"path": "docker-compose/README.md",
"chars": 0,
"preview": ""
},
{
"path": "docker-compose/docker-compose.yml",
"chars": 2251,
"preview": "services:\n fptn-server:\n restart: unless-stopped\n image: fptnvpn/fptn-vpn-server:latest\n cap_add:\n - NET_"
},
{
"path": "docs/CNAME",
"chars": 8,
"preview": "fptn.org"
},
{
"path": "docs/index-ru.html",
"chars": 58940,
"preview": "<!DOCTYPE html>\n<html lang=\"ru\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" type=\"image/x-icon\" href=\"images"
},
{
"path": "docs/index.html",
"chars": 58896,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" type=\"image/x-icon\" href=\"images"
},
{
"path": "pyproject.toml",
"chars": 67,
"preview": "[tool.black]\nline-length = 120\nextend-exclude = '''\ncpplint\\.py\n'''"
},
{
"path": "renovate.json",
"chars": 238,
"preview": "{\n \"extends\": [\n \"config:recommended\",\n \":dependencyDashboard\"\n ],\n \"enabledManagers\": [\n \"conan\"\n ],\n \"co"
},
{
"path": "src/common/client_id.h",
"chars": 410,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/data/channel.h",
"chars": 1891,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/data/channel_async.h",
"chars": 3780,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/jwt_token/token_manager.h",
"chars": 3609,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/logger/logger.h",
"chars": 4269,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/network/ip_address.h",
"chars": 10479,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/network/ip_packet.h",
"chars": 13516,
"preview": "/*=============================================================================\r\nCopyright (c) 2024-2026 Stas Skokov\r\n\r\n"
},
{
"path": "src/common/network/ipv4_generator.h",
"chars": 2566,
"preview": "/*=============================================================================\r\nCopyright (c) 2024-2025 Stas Skokov\r\n\r\n"
},
{
"path": "src/common/network/ipv6_generator.h",
"chars": 2079,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/network/ipv6_utils.h",
"chars": 2146,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/network/net_interface.h",
"chars": 9903,
"preview": "/*=============================================================================\r\nCopyright (c) 2024-2025 Stas Skokov\r\nCo"
},
{
"path": "src/common/network/resolv.h",
"chars": 3218,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/network/tun/darwin_tun_device.h",
"chars": 6549,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Pavel Shpilev\n\nD"
},
{
"path": "src/common/network/tun/linux_tun_device.h",
"chars": 1521,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Pavel Shpilev\n\nD"
},
{
"path": "src/common/network/tun/win_tun_device.h",
"chars": 7805,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Pavel Shpilev\n\nD"
},
{
"path": "src/common/network/utils.h",
"chars": 12361,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/common/system/command.h",
"chars": 2327,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/user/common_user_manager.h",
"chars": 6524,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/utils/base64.h",
"chars": 821,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/common/utils/utils.h",
"chars": 2728,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/CMakeLists.txt",
"chars": 5734,
"preview": "project(fptn-client)\n\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR})\n\nif(APPLE OR UNIX)\n set(TUNTAP_LIB tuntap++)\nels"
},
{
"path": "src/fptn-client/config/config_file.cpp",
"chars": 4513,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/config/config_file.h",
"chars": 1946,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/fptn-client-cli.cpp",
"chars": 19287,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/fptn-client-gui.cpp",
"chars": 2957,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/autostart/autostart.h",
"chars": 8194,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/autoupdate/autoupdate.h",
"chars": 3780,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/resources/resources.qrc",
"chars": 773,
"preview": "<!DOCTYPE RCC>\n<RCC version=\"1.0\">\n <qresource prefix=\"/\">\n <file>icons/app.ico</file>\n <file>icons/act"
},
{
"path": "src/fptn-client/gui/resources/translations/.gitignore",
"chars": 5,
"preview": "*.qm\n"
},
{
"path": "src/fptn-client/gui/resources/translations/fptn_en.ts",
"chars": 19831,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"en_US\">\n <context>\n <name>QOb"
},
{
"path": "src/fptn-client/gui/resources/translations/fptn_ru.ts",
"chars": 20119,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"ru_RU\">\n <context>\n <name>QOb"
},
{
"path": "src/fptn-client/gui/server_menu_item_widget/server_menu_item_widget.cpp",
"chars": 3474,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/server_menu_item_widget/server_menu_item_widget.h",
"chars": 1036,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/settingsmodel/settingsmodel.cpp",
"chars": 22153,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/settingsmodel/settingsmodel.h",
"chars": 7672,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/settingswidget/settings.cpp",
"chars": 60459,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/settingswidget/settings.h",
"chars": 4074,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/sni_autoscan_dialog/sni_autoscan_dialog.cpp",
"chars": 11536,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/sni_autoscan_dialog/sni_autoscan_dialog.h",
"chars": 2370,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/sni_manager/sni_manager.cpp",
"chars": 2119,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/sni_manager/sni_manager.h",
"chars": 781,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/speedwidget/speedwidget.cpp",
"chars": 1862,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/speedwidget/speedwidget.h",
"chars": 870,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/style/style.cpp",
"chars": 3935,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/style/style.h",
"chars": 445,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/tokendialog/tokendialog.cpp",
"chars": 3130,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/tokendialog/tokendialog.h",
"chars": 1169,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/translations/translations.cpp",
"chars": 1242,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/translations/translations.h",
"chars": 411,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/tray/tray.cpp",
"chars": 36931,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/gui/tray/tray.h",
"chars": 3728,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/plugins/base_plugin.h",
"chars": 729,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/plugins/blacklist/domain_blacklist.cpp",
"chars": 3532,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/plugins/blacklist/domain_blacklist.h",
"chars": 1243,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/plugins/split/tunneling.cpp",
"chars": 3690,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/plugins/split/tunneling.h",
"chars": 1148,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/routing/route_manager.cpp",
"chars": 47776,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/routing/route_manager.h",
"chars": 3680,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/utils/brotli/brotli.h",
"chars": 1100,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/utils/macos/admin.h",
"chars": 3155,
"preview": "#pragma once\n\n/*=============================================================================\nCopyright (c) 2024-2025 St"
},
{
"path": "src/fptn-client/utils/signal/main_loop.h",
"chars": 1273,
"preview": "#pragma once\n\n/*=============================================================================\nCopyright (c) 2024-2025 St"
},
{
"path": "src/fptn-client/utils/speed_estimator/server_info.h",
"chars": 933,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/utils/speed_estimator/speed_estimator.cpp",
"chars": 4048,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/utils/speed_estimator/speed_estimator.h",
"chars": 1026,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/utils/utils.h",
"chars": 1153,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/utils/windows/vpn_conflict.h",
"chars": 1233,
"preview": "#pragma once\n\n/*=============================================================================\nCopyright (c) 2024-2025 St"
},
{
"path": "src/fptn-client/vpn/http/client.cpp",
"chars": 7966,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/vpn/http/client.h",
"chars": 2353,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/vpn/vpn_client.cpp",
"chars": 5828,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-client/vpn/vpn_client.h",
"chars": 2056,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-passwd/CMakeLists.txt",
"chars": 313,
"preview": "project(fptn-passwd)\n\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR})\n\nfind_package(Boost REQUIRED)\nfind_package(OpenSS"
},
{
"path": "src/fptn-passwd/fptn-passwd.cpp",
"chars": 3989,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/CMakeLists.txt",
"chars": 4974,
"preview": "project(fptn-protocol-lib LANGUAGES CXX)\n\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR})\n\ninclude(../../depends/cmake/"
},
{
"path": "src/fptn-protocol-lib/https/api_client/api_client.cpp",
"chars": 32955,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/api_client/api_client.h",
"chars": 2646,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/censorship_strategy.h",
"chars": 1477,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/obfuscator/methods/detector.h",
"chars": 1421,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/obfuscator/methods/obfuscator_interface.h",
"chars": 1062,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/obfuscator/methods/tls/tls_obfuscator.cpp",
"chars": 10831,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/obfuscator/methods/tls/tls_obfuscator.h",
"chars": 1138,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/obfuscator/methods/tls2/tls_obfuscator2.cpp",
"chars": 10111,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/obfuscator/methods/tls2/tls_obfuscator2.h",
"chars": 1126,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/obfuscator/tcp_stream/tcp_stream.h",
"chars": 10172,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/utils/change_cipher_spec.h",
"chars": 506,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/utils/tls/tls.cpp",
"chars": 14316,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/utils/tls/tls.h",
"chars": 1699,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/websocket_client/websocket_client.cpp",
"chars": 23855,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/https/websocket_client/websocket_client.h",
"chars": 4192,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/protobuf/protocol.cpp",
"chars": 4513,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/protobuf/protocol.h",
"chars": 738,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/protobuf/protocol.proto",
"chars": 588,
"preview": "syntax = \"proto3\";\n\npackage fptn.protocol;\noption java_package = \"org.fptn.protocol\";\n\n\nenum MessageType {\n MSG_ERROR"
},
{
"path": "src/fptn-protocol-lib/time/time_provider.cpp",
"chars": 2798,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-protocol-lib/time/time_provider.h",
"chars": 1229,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/.gitignore",
"chars": 10,
"preview": "keys/\ntmp/"
},
{
"path": "src/fptn-server/CMakeLists.txt",
"chars": 2474,
"preview": "project(fptn-server)\n\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR})\ninclude_directories(\"${CMAKE_CURRENT_BINARY_DIR}/"
},
{
"path": "src/fptn-server/client/session.cpp",
"chars": 3064,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/client/session.h",
"chars": 2296,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/config/command_line_config.cpp",
"chars": 8600,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/config/command_line_config.h",
"chars": 2018,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/filter/filters/antiscan/antiscan.cpp",
"chars": 2440,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/filter/filters/antiscan/antiscan.h",
"chars": 1849,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/filter/filters/base_filter.h",
"chars": 628,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/filter/filters/bittorrent/bittorrent.cpp",
"chars": 2219,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/filter/filters/bittorrent/bittorrent.h",
"chars": 601,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/filter/manager.cpp",
"chars": 969,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/filter/manager.h",
"chars": 766,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/fptn-server.cpp",
"chars": 6114,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/nat/table.cpp",
"chars": 6454,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/nat/table.h",
"chars": 3094,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/network/virtual_interface.cpp",
"chars": 2464,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/network/virtual_interface.h",
"chars": 1448,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/routing/iptables.cpp",
"chars": 6783,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/routing/iptables.h",
"chars": 784,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/statistic/metrics.cpp",
"chars": 2446,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/statistic/metrics.h",
"chars": 1445,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/traffic_shaper/leaky_bucket.cpp",
"chars": 1262,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/traffic_shaper/leaky_bucket.h",
"chars": 902,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/user/user_manager.cpp",
"chars": 2536,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/user/user_manager.h",
"chars": 2399,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/vpn/manager.cpp",
"chars": 4454,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/vpn/manager.h",
"chars": 1522,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/api/handle.h",
"chars": 2164,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/handshake/handshake_cache_manager.cpp",
"chars": 5071,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/handshake/handshake_cache_manager.h",
"chars": 1636,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/listener/listener.cpp",
"chars": 5381,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/listener/listener.h",
"chars": 2208,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/server.cpp",
"chars": 11671,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/server.h",
"chars": 3971,
"preview": "/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/session/session.cpp",
"chars": 38965,
"preview": "/*=============================================================================\nCopyright (c) 2024-2026 Stas Skokov\n\nDis"
},
{
"path": "src/fptn-server/web/session/session.h",
"chars": 4506,
"preview": "\n/*=============================================================================\nCopyright (c) 2024-2025 Stas Skokov\n\nDi"
},
{
"path": "sysadmin-tools/grafana/.gitignore",
"chars": 25,
"preview": ".env\ndocker-compose-data/"
},
{
"path": "sysadmin-tools/grafana/README.md",
"chars": 2183,
"preview": "## Grafana\n\nGrafana is used for monitoring server activity, including traffic amount and active users.\n\n<img src=\"images"
},
{
"path": "sysadmin-tools/grafana/configs/grafana/dashboards/dashboards.yaml",
"chars": 196,
"preview": "apiVersion: 1\n\nproviders:\n - name: 'default'\n orgId: 1\n folder: ''\n type: file\n disableDeletion: false\n "
},
{
"path": "sysadmin-tools/grafana/configs/grafana/dashboards/fptn_dashboard.json",
"chars": 26194,
"preview": "{\n \"annotations\": {\n \"list\": [\n {\n \"builtIn\": 1,\n \"datasource\": {\n \"type\": \"grafana\",\n "
},
{
"path": "sysadmin-tools/grafana/configs/grafana/dashboards/node-exporter-full.json",
"chars": 683275,
"preview": "{\n \"__inputs\": [\n {\n \"name\": \"DS_PROMETHEUS\",\n \"label\": \"Prometheus\",\n \"description\": \"\",\n \"type"
},
{
"path": "sysadmin-tools/grafana/configs/grafana/datasources/datasources.yaml",
"chars": 124,
"preview": "apiVersion: 1\ndatasources:\n - name: Prometheus\n type: prometheus\n url: http://fptn-prometheus:9090\n access: pr"
},
{
"path": "sysadmin-tools/grafana/configs/nginx/nginx.conf.template",
"chars": 345,
"preview": "server {\n listen 80;\n location / {\n proxy_pass https://FPTN_HOST_PORT; # WILL REPLACE\n proxy_set_he"
}
]
// ... and 33 more files (download for full content)
About this extraction
This page contains the full source code of the batchar2/fptn GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 233 files (3.2 MB), approximately 846.3k tokens, and a symbol index with 589 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.