From: Maksym Mamontov Date: Tue, 31 Mar 2026 18:32:09 +0000 (+0300) Subject: Fix SearchNext condition. X-Git-Url: https://git.stg.codes/stg.git/commitdiff_plain/HEAD?hp=2574a28cbf000603bc31f61593dbf061ff56c1d5 Fix SearchNext condition. --- diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..1553c16e --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,16 @@ +Checks: "-*, + clang-analyzer-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + bugprone-*, + -misc-include-cleaner, + -modernize-avoid-bind, + -modernize-use-trailing-return-type, + -bugprone-easily-swappable-parameters, + -readability-braces-around-statements, + -readability-magic-numbers, + -readability-identifier-length, + -readability-suspicious-call-argument" diff --git a/.gitignore b/.gitignore index ba791f70..9fe21c8f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ CVS* doc/xmlrpc-doc/book doc/help/book doc/help/help.pdf +dist diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a3ff749..a72d3e57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ option ( BUILD_SGAUTH "Build SGAuth client." OFF ) # General modules option ( BUILD_MOD_AO "Build AlwaysOnline STG module." ON ) +option ( BUILD_MOD_RADIUS "Build Radius STG module." ON ) option ( BUILD_MOD_IA "Buils InetAccess STG module." ON ) option ( BUILD_MOD_SGCONFIG "Build SGConfig STG module." ON ) option ( BUILD_MOD_PING "Build Ping STG module." ON ) @@ -32,6 +33,9 @@ option ( BUILD_MOD_RPCCONFIG "Build XML-RPC configuretion STG module." OFF ) option ( BUILD_MOD_CAP_PCAP "Build PCap capture STG module." OFF ) option ( BUILD_MOD_CAP_NFQUEUE "Build NFQueue capture STG module." OFF ) +# Firebird is getting deprecated, negation option +option ( BUILD_NO_MOD_STORE_FIREBIRD "Do not build Firebird store STG module." OFF ) + # Grouping option ( BUILD_ALL_MODS "Build all modules." OFF ) option ( BUILD_ALL_LIBS "Build all libraries." OFF ) @@ -92,6 +96,10 @@ if ( BUILD_ALL_MODS ) set ( BUILD_MOD_CAP_PCAP ON ) endif ( BUILD_ALL_MODS ) +if ( BUILD_NO_MOD_STORE_FIREBIRD ) + set ( BUILD_MOD_STORE_FIREBIRD OFF ) +endif ( BUILD_NO_MOD_STORE_FIREBIRD ) + if ( BUILD_MOD_STORE_FIREBIRD OR BUILD_ALL_LIBS ) set ( BUILD_LIB_IBPP ON ) endif ( BUILD_MOD_STORE_FIREBIRD OR BUILD_ALL_LIBS ) @@ -110,21 +118,7 @@ if ( TIDY ) message( STATUS "clang-tidy not found." ) else () message( STATUS "clang-tidy found: ${CLANG_TIDY_EXE}" ) - set( DO_CLANG_TIDY "${CLANG_TIDY_EXE}" - "-checks=-*,\ -clang-analyzer-*,\ -misc-*,\ -modernize-*,\ -performance-*,\ -portability-*,\ -readability-*,\ -bugprone-*,\ --modernize-avoid-bind,\ --modernize-use-trailing-return-type,\ --readability-braces-around-statements, \ --readability-magic-numbers, \ --readability-identifier-length, \ --bugprone-easily-swappable-parameters" ) + set( DO_CLANG_TIDY "${CLANG_TIDY_EXE}" ) endif () endif () @@ -141,6 +135,7 @@ endif () enable_language (CXX) set (CMAKE_CXX_STANDARD 17) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Wextra -Wshadow -Wno-long-long -Wold-style-cast -Wstrict-aliasing -pedantic") +set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") if ( CMAKE_COMPILER_IS_GNUCC ) # GCC-specific diff --git a/README.md b/README.md index f4746736..5b220bb0 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,66 @@ [![Build Status](https://travis-ci.org/madf/stg.svg?branch=master)](https://travis-ci.org/madf/stg) A billing system for small home and office networks. + +## Dependencies + +* CMake - build system. +* Iconv - character encoding conversion. +* Expat (optional) - required by `mod_conf_sg` configuration plugin. +* xmlrpc-c (optional) - required by `mod_conf_rpc` configuration plugin. +* PCap (optional) - required by `mod_cap_pcap` traffic capture plugin. +* `libnetfilter_queue`, `libnfnetlink`, `libmnl` (optional) - required by `mod_cap_nfqueue` traffic capture plugin. +* Firebird (optional) - required by `mod_store_firebird` storage plugin. +* PostgreSQL (optional) - required by `mod_store_postgresql` storage plugin. +* MySQL Connector (optional) - required by `mod_store_mysql` storage plugin. +* boost (optional) - unit tests. + +## Compilation and Installation + +``` +mkdir build +cd build +cmake .. +make +make install +``` + +It will install everything in /usr/local by default. If you want to install with a different destdir: + +``` +$ make DESTDIR=/path/to/your/destdir install +``` + +It will automatically append usr/local to your destdir. So if you specify DESTDIR=foo you will result in the following directory structure: + +``` +foo/usr/local/bin +foo/usr/local/include +foo/usr/local/lib +``` + +If you want a custom install dir prefix use CMAKE_INSTALL_PREFIX at compile time: + +``` +$ cmake -DCMAKE_INSTALL_PREFIX=/your/prefix .. +$ make +$ make install +``` + +If you specify -DCMAKE_INSTALL_PREFIX=foo you will result in the following directory structure: + +``` +foo/bin +foo/include +foo/lib +``` + +### Notes for MacOS X + +1. It is not easy to use Firebird on MacOS X, so you may want to opt-out its storage plugin. Use `-DBUILD_NO_MOD_STORE_FIREBIRD=ON` as `cmake` command line option. +2. Homebrew XMLRPC-C version is too old. You may want to build it from scratch. In order to make it visible to CMake, pass `-DXMLRPC_C_CONFIG=/path/to/xmlrpc-c-config` to `cmake`. +3. CMake usually does not see MySQL Connector library installed by Homebrew. Pass `-DMySQLConnector_ROOT=/usr/local/opt/mysql-client/` to `cmake` to make it visible. + +## Documentation + +https://stg.net.ua/doc/index.html diff --git a/libs/scriptexecuter/scriptexecuter.c b/libs/scriptexecuter/scriptexecuter.c index bdf60230..bc726948 100644 --- a/libs/scriptexecuter/scriptexecuter.c +++ b/libs/scriptexecuter/scriptexecuter.c @@ -22,7 +22,7 @@ struct SCRIPT_DATA char script[MAX_SCRIPT_LEN]; } sd; //----------------------------------------------------------------------------- -static void CatchUSR1Executer() +static void CatchUSR1Executer(int /*unused*/) { nonstop = 0; } diff --git a/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf b/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf index edc94d50..7373af21 100644 --- a/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf +++ b/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf @@ -1,34 +1,29 @@ -# Enable the interaction module for FreeRADIUS "mod_radius.so" +# Enable the interaction module for RADIUS "mod_radius.so" - # FreeRADIUS password + # RADIUS shared secret # Parameter: required - # Values: any, supported by software - # Default: 123456 - Password = 123456 + # Values: any + Secret = sec - # FreeRADIUS server - # Parameter: required - # Values: IP address or DNS name - # Default: 127.0.0.1 - ServerIP = 127.0.0.1 + # Path to RADIUS dictionaries file + # Parameter: optional + # Values: file path + # Default: /usr/share/freeradius/dictionary + # Dictionaries = /usr/share/freeradius/dictionary - # FreeRADIUS port - # Parameter: required + # RADIUS port number + # Parameter: optional # Value: 1 ... 65535 - # Default: 6666 - Port = 6666 + # Default: 1812 + # Port = 1812 - # List of services for which will be carried out FreeRADIUS authentication - # Note: Parameter can be blank - # Parameter: required - # Value: any, supported by software - # Default: Login-User - AuthServices = Login-User + + send = "Framed-IP-Address = currip, Service-Type = \'Framed-User\'" + match = "Calling-Station-Id = userdata0" + - # List of services for which will be carried out FreeRADIUS Accounting - # Note: Parameter can be blank - # Parameter: required - # Value: any, supported by software - # Default: Framed-User - AcctServices = Framed-User - \ No newline at end of file + + send = "Framed-IP-Address = currip, Service-Type = \'Login-User\'" + match = "User-Name = login, User-Password = password" + + diff --git a/projects/stargazer/main.cpp b/projects/stargazer/main.cpp index ee14c3a8..acc826f7 100644 --- a/projects/stargazer/main.cpp +++ b/projects/stargazer/main.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -55,10 +56,6 @@ #include // S_IRUSR #include // create -#ifdef DEBUG - #define NO_DAEMON (1) -#endif - #define START_FILE "/._ST_ART_ED_" using STG::SettingsImpl; @@ -155,13 +152,8 @@ int StartScriptExecuter(char*, int msgKey, int* msgID) return 0; } //----------------------------------------------------------------------------- -#ifndef NO_DAEMON int ForkAndWait(const std::string& confDir) -#else -int ForkAndWait(const std::string&) -#endif { -#ifndef NO_DAEMON const auto pid = fork(); const auto startFile = confDir + START_FILE; unlink(startFile.c_str()); @@ -194,9 +186,9 @@ int ForkAndWait(const std::string&) exit(1); break; } -#endif return 0; } + //----------------------------------------------------------------------------- void KillExecuters() { @@ -208,6 +200,21 @@ void KillExecuters() ++pid; } } + +void PrintHelp(const std::string& programName) +{ + std::cout << "Usage: " << programName << "[-h/--help] [-v/--version] [-f/--foreground] []\n" + << "\t --help, -h - print this help;\n" + << "\t --version, -v - print version;\n" + << "\t --foreground, -f - do not go into background;\n" + << "\t - path to the directory where the configuration file is located.\n"; +} + +void PrintVersion(const std::string& programName) +{ + std::cout << programName << "\n" + << "Stargazer version" << " " << SERVER_VERSION << "\n"; +} //----------------------------------------------------------------------------- } // namespace anonymous //----------------------------------------------------------------------------- @@ -223,7 +230,34 @@ int main(int argc, char* argv[]) return 1; } - SettingsImpl settings(argc == 2 ? argv[1] : ""); + std::string path; + bool noDaemon(false); + + if (argc == 1) + path = ""; + else + { + for (int i = 1; i < argc; ++i) + { + const std::string arg(argv[i]); + if (arg == "--help" || arg == "-h") + { + PrintHelp(argv[0]); + return 0; + } + if (arg == "--version" || arg == "-v") + { + PrintVersion(argv[0]); + return 0; + } + if (arg == "--foreground" || arg == "-f") + noDaemon = true; + else + path = arg; + } + } + + SettingsImpl settings(path); if (settings.ReadSettings()) { @@ -236,14 +270,13 @@ int main(int argc, char* argv[]) return -1; } -#ifndef NO_DAEMON - const auto startFile = settings.GetConfDir() + START_FILE; -#endif - - if (ForkAndWait(settings.GetConfDir()) < 0) + if (!noDaemon) { - STG::Logger::get()("Fork error!"); - return -1; + if (ForkAndWait(settings.GetConfDir()) < 0) + { + STG::Logger::get()("Fork error!"); + return -1; + } } auto& WriteServLog = STG::Logger::get(); @@ -321,9 +354,11 @@ int main(int argc, char* argv[]) WriteServLog("Stg started successfully."); WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++"); -#ifndef NO_DAEMON - creat(startFile.c_str(), S_IRUSR); -#endif + if (!noDaemon) + { + const auto startFile = settings.GetConfDir() + START_FILE; + creat(startFile.c_str(), S_IRUSR); + } bool running = true; while (running) diff --git a/projects/stargazer/plugins/CMakeLists.txt b/projects/stargazer/plugins/CMakeLists.txt index 8adb6c5f..7d0e02e6 100644 --- a/projects/stargazer/plugins/CMakeLists.txt +++ b/projects/stargazer/plugins/CMakeLists.txt @@ -11,6 +11,42 @@ if ( BUILD_MOD_AO ) endif () endif ( BUILD_MOD_AO ) +if ( BUILD_MOD_RADIUS ) + find_package ( Boost REQUIRED ) + + add_library ( mod_radius MODULE other/radius/radius.cpp + other/radius/server.cpp + other/radius/config.cpp) + target_link_libraries ( mod_radius PRIVATE scriptexecuter logger common ) + set_target_properties ( mod_radius PROPERTIES PREFIX "" ) + + include ( ExternalProject ) + + ExternalProject_Add ( async-radius + GIT_REPOSITORY https://github.com/madf/async-radius.git + GIT_TAG 1.3.0 + GIT_SHALLOW true + INSTALL_COMMAND "" + CMAKE_ARGS -DCMAKE_CXX_FLAGS=-fPIC ) + + ExternalProject_Get_property ( async-radius SOURCE_DIR BINARY_DIR ) + + target_include_directories ( mod_radius PRIVATE "${SOURCE_DIR}/include" ) + target_link_directories ( mod_radius PRIVATE "${BINARY_DIR}/src" ) + + add_dependencies ( mod_radius async-radius ) + + find_package ( OpenSSL 1.0.0 REQUIRED ) + target_link_libraries ( mod_radius PRIVATE radproto Boost::boost OpenSSL::Crypto) + + if ( CLANG_TIDY_EXE ) + set_target_properties ( mod_radius PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) + endif () + if ( INCLUDE_WHAT_YOU_USE_EXE ) + set_target_properties ( mod_radius PROPERTIES CXX_INCLUDE_WHAT_YOU_USE "${DO_INCLUDE_WHAT_YOU_USE}" ) + endif () +endif ( BUILD_MOD_RADIUS ) + if ( BUILD_MOD_IA ) add_library ( mod_auth_ia MODULE authorization/inetaccess/inetaccess.cpp ) target_link_libraries ( mod_auth_ia scriptexecuter crypto logger common ) @@ -155,6 +191,9 @@ if ( BUILD_MOD_RPCCONFIG ) configuration/rpcconfig/messages_methods.cpp ) target_link_libraries ( mod_conf_rpc scriptexecuter logger common ${XMLRPC_LIBRARIES} ) set_target_properties ( mod_conf_rpc PROPERTIES PREFIX "" ) + if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) + set_target_properties ( mod_conf_rpc PROPERTIES LINK_FLAGS "-undefined dynamic_lookup" ) + endif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) if ( CLANG_TIDY_EXE ) set_target_properties ( mod_conf_rpc PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) @@ -272,6 +311,9 @@ if ( BUILD_MOD_STORE_POSTGRESQL ) store/postgresql/postgresql_store_utils.cpp ) target_link_libraries ( mod_store_postgresql crypto logger common ${PostgreSQL_LIBRARIES} ) set_target_properties ( mod_store_postgresql PROPERTIES PREFIX "" ) + if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) + set_target_properties ( mod_store_postgresql PROPERTIES LINK_FLAGS "-undefined dynamic_lookup" ) + endif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) if ( CLANG_TIDY_EXE ) set_target_properties ( mod_store_postgresql PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" ) diff --git a/projects/stargazer/plugins/other/radius/config.cpp b/projects/stargazer/plugins/other/radius/config.cpp new file mode 100644 index 00000000..108b61e6 --- /dev/null +++ b/projects/stargazer/plugins/other/radius/config.cpp @@ -0,0 +1,146 @@ +#include "config.h" +#include "radproto/error.h" +#include "stg/common.h" +#include +#include + +#include +#include +#include +#include +#include + +using STG::Config; +using AttrValue = Config::AttrValue; +using ASection = Config::ASection; + +namespace +{ + std::string ShowRules(const std::vector>& rules) + { + std::string result; + for (const auto& at : rules) + { + if (!result.empty()) + result += ", "; + + if (at.second.type == AttrValue::Type::PARAM_NAME) + result.append(at.first + " = " + at.second.value); + else + result.append(at.first + " = '" + at.second.value + "'"); + } + return result; + } +} + +std::vector> Config::ParseRules(const std::string& value, const std::string& paramName) +{ + using tokenizer = boost::tokenizer>; + const boost::char_separator sep(","); + + const tokenizer tokens(value, sep); + + std::vector> res; + + for (const auto& token : tokens) + { + std::vector keyValue; + + split(keyValue, boost::algorithm::trim_copy_if(token, boost::is_any_of(" \t")), boost::is_any_of(" ="), boost::token_compress_on); + + if (keyValue.size() != 2) + { + m_logger("The '%s' attribute specification has an incorrect format: '%s'.", paramName.c_str(), token.c_str()); + printfd(__FILE__, "The '%s' attribute specification has an incorrect format: '%s'.", paramName.c_str(), token.c_str()); + return {}; + } + + auto type = AttrValue::Type::PARAM_NAME; + std::string valueName = keyValue[1]; + if (valueName.front() == '\'' && valueName.back() == '\'') + { + type = AttrValue::Type::VALUE; + valueName.erase(0, 1); + valueName.erase(valueName.length() - 1, 1); + } + else if ((valueName.front() == '\'' && valueName.back() != '\'') || (valueName.front() != '\'' && valueName.back() == '\'')) + { + m_logger("Error ParseRules: '%s' attribute parameter value is invalid.\n", paramName.c_str()); + printfd(__FILE__, "Error ParseRules: '%s' attribute parameter value is invalid.\n", paramName.c_str()); + return {}; + } + res.emplace_back(keyValue[0], AttrValue{valueName, type}); + } + return res; +} + +ASection Config::parseASection(const std::vector& conf) +{ + ASection res; + const auto mit = std::find(conf.begin(), conf.end(), ParamValue("match", {})); + if (mit != conf.end()) + res.match = ParseRules(mit->value[0], mit->param); + + const auto sit = std::find(conf.begin(), conf.end(), ParamValue("send", {})); + if (sit != conf.end()) + res.send = ParseRules(sit->value[0], sit->param); + + return res; +} + +Config::Config() + : m_port(1812), + m_dictionaries("/usr/share/freeradius/dictionary"), + m_logger(PluginLogger::get("radius")) +{} + +int Config::ParseSettings(const ModuleSettings & s) +{ + ParamValue pv; + int p; + + pv.param = "Port"; + auto pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); + if (pvi != s.moduleParams.end() && !pvi->value.empty()) + { + if (ParseIntInRange(pvi->value[0], 2, 65535, &p) != 0) + { + m_errorStr = "Cannot parse parameter \'Port\': " + m_errorStr; + printfd(__FILE__, "Cannot parse parameter 'Port'\n"); + return -1; + } + m_port = static_cast(p); + } + + pv.param = "Secret"; + pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); + if (pvi == s.moduleParams.end() || pvi->value.empty()) + { + m_errorStr = "Parameter \'Secret\' not found."; + printfd(__FILE__, "Parameter 'Secret' not found\n"); + return -1; + } + else + m_secret = pvi->value[0]; + + pv.param = "Dictionaries"; + pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); + if (pvi != s.moduleParams.end() && !pvi->value.empty()) + m_dictionaries = pvi->value[0]; + + const auto authIt = std::find(s.moduleParams.begin(), s.moduleParams.end(), ParamValue("auth", {})); + if (authIt != s.moduleParams.end()) + m_auth = parseASection(authIt->sections); + + const auto autzIt = std::find(s.moduleParams.begin(), s.moduleParams.end(), ParamValue("autz", {})); + if (autzIt != s.moduleParams.end()) + m_autz = parseASection(autzIt->sections); + + printfd(__FILE__, " auth.match = \"%s\"\n", ShowRules(m_auth.match).c_str()); + printfd(__FILE__, " auth.send = \"%s\"\n", ShowRules(m_auth.send).c_str()); + printfd(__FILE__, " autz.match = \"%s\"\n", ShowRules(m_autz.match).c_str()); + printfd(__FILE__, " autz.send = \"%s\"\n", ShowRules(m_autz.send).c_str()); + + return 0; +} + diff --git a/projects/stargazer/plugins/other/radius/config.h b/projects/stargazer/plugins/other/radius/config.h new file mode 100644 index 00000000..74e483a5 --- /dev/null +++ b/projects/stargazer/plugins/other/radius/config.h @@ -0,0 +1,60 @@ +#pragma once + +#include "stg/module_settings.h" +#include "stg/subscriptions.h" +#include "stg/logger.h" + +#include +#include //uint8_t, uint32_t + +namespace STG +{ + struct Settings; + + class Config + { + public: + Config(); + + struct AttrValue + { + enum class Type + { + PARAM_NAME, + VALUE + }; + std::string value; + Type type; + }; + + struct ASection + { + using Pairs = std::vector>; + Pairs match; + Pairs send; + }; + + const std::string& GetStrError() const { return m_errorStr; } + int ParseSettings(const ModuleSettings& s); + + uint16_t GetPort() const { return m_port; } + const std::string& GetDictionaries() const { return m_dictionaries; } + const std::string& GetSecret() const { return m_secret; } + const ASection& getAuth() const { return m_auth; } + const ASection& getAutz() const { return m_autz; } + + private: + std::vector> ParseRules(const std::string& value, const std::string& paramName); + ASection parseASection(const std::vector& conf); + + std::string m_errorStr; + uint16_t m_port; + std::string m_dictionaries; + std::string m_secret; + + ASection m_auth; + ASection m_autz; + + PluginLogger m_logger; + }; +} diff --git a/projects/stargazer/plugins/other/radius/radius.cpp b/projects/stargazer/plugins/other/radius/radius.cpp new file mode 100644 index 00000000..11fbcf7b --- /dev/null +++ b/projects/stargazer/plugins/other/radius/radius.cpp @@ -0,0 +1,88 @@ +#include "radius.h" +#include "radproto/error.h" +#include "stg/common.h" +#include + +#include + +using STG::RADIUS; + +extern "C" STG::Plugin* GetPlugin() +{ + static RADIUS plugin; + return &plugin; +} + +RADIUS::RADIUS() + : m_running(false), + m_users(nullptr), + m_logger(PluginLogger::get("radius")) +{ +} + +int RADIUS::ParseSettings() +{ + auto ret = m_config.ParseSettings(m_settings); + if (ret != 0) + m_errorStr = m_config.GetStrError(); + + return ret; +} + +std::string RADIUS::GetVersion() const +{ + return "Radius v.1.0"; +} + +int RADIUS::Start() +{ + m_thread = std::jthread([this](auto token){ Run(std::move(token)); }); + return 0; +} + +int RADIUS::Stop() +{ + if (!m_thread.joinable()) + return 0; + + m_thread.request_stop(); + + if (m_server) + m_server->stop(); + + m_thread.join(); + return 0; +} + +bool RADIUS::IsRunning() +{ + const std::lock_guard lock(m_mutex); + return m_running; +} + +void RADIUS::SetRunning(bool val) +{ + const std::lock_guard lock(m_mutex); + m_running = val; +} + +int RADIUS::Run(std::stop_token token) +{ + SetRunning(true); + + try + { + if (!m_server) + m_server = std::make_unique(m_ioContext, m_config.GetSecret(), m_config.GetPort(), m_config.GetDictionaries(), std::move(token), m_logger, m_users, m_config); + m_ioContext.run(); + } + catch (const std::exception& e) + { + m_errorStr = "Exception in RADIUS::Run(): " + std::string(e.what()); + m_logger("Exception in RADIUS:: Run(): %s", e.what()); + printfd(__FILE__, "Exception in RADIUS:: Run(). Message: '%s'\n", e.what()); + } + + SetRunning(false); + return 0; +} diff --git a/projects/stargazer/plugins/other/radius/radius.h b/projects/stargazer/plugins/other/radius/radius.h new file mode 100644 index 00000000..59cbf0d5 --- /dev/null +++ b/projects/stargazer/plugins/other/radius/radius.h @@ -0,0 +1,65 @@ +#pragma once + +#include "stg/auth.h" +#include "stg/plugin.h" +#include "config.h" +#include "stg/module_settings.h" +#include "stg/subscriptions.h" +#include "stg/logger.h" +#include "server.h" + +#include +#include +#include +#include +#include +#include //uint8_t, uint32_t + +namespace STG +{ + class Users; + + class RADIUS : public Auth + { + public: + RADIUS(); + RADIUS(const RADIUS&) = delete; + RADIUS& operator=(const RADIUS&) = delete; + + void SetUsers(Users* u) override { m_users = u; } + void SetSettings(const ModuleSettings& s) override { m_settings = s; } + int ParseSettings() override; + + int Start() override; + int Stop() override; + int Reload(const ModuleSettings& /*ms*/) override { return 0; } + bool IsRunning() override; + + const std::string& GetStrError() const override { return m_errorStr; } + std::string GetVersion() const override; + + uint16_t GetStartPosition() const override { return 0; } + uint16_t GetStopPosition() const override { return 0; } + + int SendMessage(const Message& /*msg*/, uint32_t /*ip*/) const override { return 0; } + + private: + std::mutex m_mutex; + + boost::asio::io_context m_ioContext; + void SetRunning(bool val); + int Run(std::stop_token token); + + mutable std::string m_errorStr; + Config m_config; + ModuleSettings m_settings; + + bool m_running; + + std::jthread m_thread; + Users* m_users; + PluginLogger m_logger; + + std::unique_ptr m_server; + }; +} diff --git a/projects/stargazer/plugins/other/radius/server.cpp b/projects/stargazer/plugins/other/radius/server.cpp new file mode 100644 index 00000000..11aa9a79 --- /dev/null +++ b/projects/stargazer/plugins/other/radius/server.cpp @@ -0,0 +1,152 @@ +#include "server.h" +#include "radproto/attribute.h" +#include "radproto/packet_codes.h" +#include "radproto/attribute_codes.h" +#include "stg/user.h" +#include "stg/users.h" +#include "stg/common.h" +#include +#include +#include +#include +#include +#include //uint8_t, uint32_t + +using STG::Server; +using STG::User; +using boost::system::error_code; + +Server::Server(boost::asio::io_context& io_context, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger, Users* users, const Config& config) + : m_radius(io_context, secret, port), + m_dictionaries(filePath), + m_users(users), + m_config(config), + m_token(std::move(token)), + m_logger(logger) +{ + start(); +} + +void Server::start() +{ + startReceive(); +} + +void Server::stop() +{ + error_code ec; + m_radius.close(ec); +} + +void Server::startReceive() +{ + m_radius.asyncReceive([this](const auto& error, const auto& packet, const boost::asio::ip::udp::endpoint& source){ handleReceive(error, packet, source); }); +} + +std::vector Server::makeAttributes(const User* user) +{ + std::vector attributes; + + for (const auto& at : m_config.getAuth().send) + { + std::string attrValue; + + if (at.second.type == Config::AttrValue::Type::PARAM_NAME) + attrValue = user->GetParamValue(at.second.value); + else + attrValue = at.second.value; + + const auto attrName = at.first; + const auto attrCode = m_dictionaries.attributeCode(attrName); + const auto attrType = m_dictionaries.attributeType(attrCode); + + if ((attrType == "integer") && (m_dictionaries.attributeValueFindByName(attrName, attrValue))) + attributes.push_back(RadProto::Attribute::make(attrCode, attrType, std::to_string(m_dictionaries.attributeValueCode(attrName, attrValue)))); + else + attributes.push_back(RadProto::Attribute::make(attrCode, attrType, attrValue)); + } + return attributes; +} + +RadProto::Packet Server::makeResponse(const RadProto::Packet& request) +{ + if (request.code() != RadProto::ACCESS_REQUEST) + return RadProto::Packet(RadProto::ACCESS_REJECT, request.id(), request.auth(), {}, {}); + + const User* user; + + user = findUser(request); + + if (user != nullptr) + return RadProto::Packet(RadProto::ACCESS_ACCEPT, request.id(), request.auth(), makeAttributes(user), {}); + + printfd(__FILE__, "Error findUser\n"); + return RadProto::Packet(RadProto::ACCESS_REJECT, request.id(), request.auth(), {}, {}); +} + +void Server::handleSend(const error_code& ec) +{ + if (m_token.stop_requested()) + return; + + if (ec) + { + m_logger("Error asyncSend: %s", ec.message().c_str()); + printfd(__FILE__, "Error asyncSend: '%s'\n", ec.message().c_str()); + } + startReceive(); +} + +void Server::handleReceive(const error_code& error, const std::optional& packet, const boost::asio::ip::udp::endpoint& source) +{ + if (m_token.stop_requested()) + return; + + if (error) + { + m_logger("Error asyncReceive: %s", error.message().c_str()); + printfd(__FILE__, "Error asyncReceive: '%s'\n", error.message().c_str()); + return; + } + + if (packet == std::nullopt) + { + m_logger("Error asyncReceive: the request packet is missing\n"); + printfd(__FILE__, "Error asyncReceive: the request packet is missing\n"); + return; + } + + m_radius.asyncSend(makeResponse(*packet), source, [this](const auto& ec){ handleSend(ec); }); +} + +const User* Server::findUser(const RadProto::Packet& packet) +{ + std::string login; + std::string password; + for (const auto& attribute : packet.attributes()) + { + if (attribute->code() == RadProto::USER_NAME) + login = attribute->toString(); + + if (attribute->code() == RadProto::USER_PASSWORD) + password = attribute->toString(); + } + + User* user = nullptr; + if (m_users->FindByName(login, &user)) + { + m_logger("User '%s' not found.", login.c_str()); + printfd(__FILE__, "User '%s' NOT found!\n", login.c_str()); + return nullptr; + } + + printfd(__FILE__, "User '%s' FOUND!\n", user->GetLogin().c_str()); + + if (password != user->GetProperties().password.Get()) + { + m_logger("User's password is incorrect."); + printfd(__FILE__, "User's password is incorrect.\n"); + return nullptr; + } + return user; +} diff --git a/projects/stargazer/plugins/other/radius/server.h b/projects/stargazer/plugins/other/radius/server.h new file mode 100644 index 00000000..1523179e --- /dev/null +++ b/projects/stargazer/plugins/other/radius/server.h @@ -0,0 +1,40 @@ +#pragma once + +#include "radproto/socket.h" +#include "radproto/packet.h" +#include "config.h" +#include "radproto/dictionaries.h" +#include "stg/logger.h" +#include +#include +#include +#include //uint8_t, uint32_t + +namespace STG +{ + class Users; + class User; + + class Server + { + public: + Server(boost::asio::io_context& io_context, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger, Users* users, const Config& config); + void stop(); + private: + std::vector makeAttributes(const User* user); + RadProto::Packet makeResponse(const RadProto::Packet& request); + const User* findUser(const RadProto::Packet& packet); + void handleReceive(const boost::system::error_code& error, const std::optional& packet, const boost::asio::ip::udp::endpoint& source); + void handleSend(const boost::system::error_code& ec); + void start(); + void startReceive(); + + RadProto::Socket m_radius; + RadProto::Dictionaries m_dictionaries; + Users* m_users; + const Config& m_config; + std::stop_token m_token; + + PluginLogger& m_logger; + }; +} diff --git a/projects/stargazer/plugins/other/rscript/rscript.cpp b/projects/stargazer/plugins/other/rscript/rscript.cpp index 24a8bd71..59bde073 100644 --- a/projects/stargazer/plugins/other/rscript/rscript.cpp +++ b/projects/stargazer/plugins/other/rscript/rscript.cpp @@ -438,7 +438,7 @@ UserPtr u; int h = users->OpenSearch(); assert(h && "USERS::OpenSearch is always correct"); -while (users->SearchNext(h, &u) != 0) +while (users->SearchNext(h, &u) == 0) SetUserNotifiers(u); users->CloseSearch(h); diff --git a/projects/stargazer/plugins/other/smux/sensors.h b/projects/stargazer/plugins/other/smux/sensors.h index 559f051e..039aa530 100644 --- a/projects/stargazer/plugins/other/smux/sensors.h +++ b/projects/stargazer/plugins/other/smux/sensors.h @@ -41,7 +41,7 @@ class TotalUsersSensor : public Sensor { #ifdef DEBUG std::string ToString() const override - { std::string res; std::to_string(users.Count(), res); return res; } + { return std::to_string(users.Count()); } #endif private: @@ -172,7 +172,7 @@ class TotalTariffsSensor : public Sensor { #ifdef DEBUG std::string ToString() const override - { std::string res; std::to_string(tariffs.Count(), res); return res; } + { return std::to_string(tariffs.Count()); } #endif private: @@ -190,7 +190,7 @@ class TotalAdminsSensor : public Sensor { #ifdef DEBUG std::string ToString() const override - { std::string res; std::to_string(admins.Count(), res); return res; } + { return std::to_string(admins.count()); } #endif private: @@ -208,7 +208,7 @@ class TotalServicesSensor : public Sensor { #ifdef DEBUG std::string ToString() const override - { std::string res; std::to_string(services.Count(), res); return res; } + { return std::to_string(services.Count()); } #endif private: @@ -226,7 +226,7 @@ class TotalCorporationsSensor : public Sensor { #ifdef DEBUG std::string ToString() const override - { std::string res; std::to_string(corporations.Count(), res); return res; } + { return std::to_string(corporations.Count()); } #endif private: @@ -244,7 +244,7 @@ class TotalRulesSensor : public Sensor { #ifdef DEBUG std::string ToString() const override - { std::string res; std::to_string(traffcounter.rulesCount(), res); return res; } + { return std::to_string(traffcounter.rulesCount()); } #endif private: @@ -263,7 +263,7 @@ class ConstSensor : public Sensor { #ifdef DEBUG std::string ToString() const override - { std::string res; std::to_string(value, res); return res; } + { return std::to_string(value); } #endif private: diff --git a/projects/stargazer/traffcounter_impl.cpp b/projects/stargazer/traffcounter_impl.cpp index 70ade1ae..f983d6af 100644 --- a/projects/stargazer/traffcounter_impl.cpp +++ b/projects/stargazer/traffcounter_impl.cpp @@ -28,17 +28,6 @@ $Author: faust $ */ -/* inet_aton */ -#include -#include -#include -#include - -#include -#include -#include // fopen and similar -#include // strtol - #include "stg/common.h" #include "stg/const.h" // MONITOR_TIME_DELAY_SEC #include "traffcounter_impl.h" @@ -46,6 +35,18 @@ #include "users_impl.h" #include "async_pool.h" +#include +#include +#include +#include // fopen and similar +#include // strtol + +/* inet_aton */ +#include +#include +#include +#include + #define FLUSH_TIME (10) #define REMOVE_TIME (31)