From 9d8f923cdb98593b56530766d3d5fa1e7b3fbf4a Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 3 Sep 2025 10:32:52 +0200 Subject: [PATCH 1/2] feat: integrate caching into network_process objects Refactor proxy port resolution caching to store results directly in network_process objects instead of maintaining separate cache structures. This eliminates cache invalidation complexity and improves performance by leveraging natural data locality. Fixes #86 --- netlib/src/iphelper/process_lookup.h | 37 ++-- netlib/src/log/log.h | 30 +++- netlib/src/proxy/socks_local_router.h | 233 ++++++-------------------- 3 files changed, 97 insertions(+), 203 deletions(-) diff --git a/netlib/src/iphelper/process_lookup.h b/netlib/src/iphelper/process_lookup.h index 3e1231b..bdafa65 100644 --- a/netlib/src/iphelper/process_lookup.h +++ b/netlib/src/iphelper/process_lookup.h @@ -112,6 +112,11 @@ namespace iphelper std::wstring name; ///< Process name (uppercase) std::wstring path_name; ///< Full path to executable (uppercase) std::wstring device_path_name; ///< Device path version of path_name (uppercase) + std::optional tcp_proxy_port = std::nullopt; // Optional tcp proxy port if the process is associated with a proxy + std::optional udp_proxy_port = std::nullopt; // Optional udp proxy port if the process is associated with a proxy + bool excluded = false; ///< Whether the process is excluded from proxying + bool bypass_tcp = false; ///< Whether TCP connections should bypass proxying (no proxy configured) + bool bypass_udp = false; ///< Whether UDP connections should bypass proxying (no proxy configured) }; /** @@ -473,7 +478,7 @@ namespace iphelper { const DWORD pid = row->dwOwningPid; if (is_system_process(pid)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "TCPv4 entry with system process PID = {} ({}) skipping resolution", pid, pid == 0 ? "Idle" : "System"); @@ -483,7 +488,7 @@ namespace iphelper const auto ext = owner_module_resolver::resolve_from_pid_and_tag_extended(pid, tag); if (ext.error == owner_module_resolver::error_code::success) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Resolved TCPv4 owner: pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -499,7 +504,7 @@ namespace iphelper if (tag != 0 && ext.error == owner_module_resolver::error_code::service_not_found) { if (owner_module_resolver::result img{}; owner_module_resolver::resolve_from_pid_and_tag(pid, 0, img)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Service tag not found; fell back to process image (TCPv4): pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -514,7 +519,7 @@ namespace iphelper } } - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Failed to resolve TCPv4 owner: pid={} tag={} error={}{}", pid, tag, @@ -541,7 +546,7 @@ namespace iphelper { const DWORD pid = row->dwOwningPid; if (is_system_process(pid)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "TCPv6 entry with system process PID = {} ({}) skipping resolution", pid, pid == 0 ? "Idle" : "System"); @@ -551,7 +556,7 @@ namespace iphelper const auto ext = owner_module_resolver::resolve_from_pid_and_tag_extended(pid, tag); if (ext.error == owner_module_resolver::error_code::success) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Resolved TCPv6 owner: pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -567,7 +572,7 @@ namespace iphelper if (tag != 0 && ext.error == owner_module_resolver::error_code::service_not_found) { if (owner_module_resolver::result img{}; owner_module_resolver::resolve_from_pid_and_tag(pid, 0, img)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Service tag not found; fell back to process image (TCPv6): pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -582,7 +587,7 @@ namespace iphelper } } - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Failed to resolve TCPv6 owner: pid={} tag={} error={}{}", pid, tag, @@ -609,7 +614,7 @@ namespace iphelper { const DWORD pid = row->dwOwningPid; if (is_system_process(pid)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "UDPv4 entry with system process PID = {} ({}) skipping resolution", pid, pid == 0 ? "Idle" : "System"); @@ -619,7 +624,7 @@ namespace iphelper const auto ext = owner_module_resolver::resolve_from_pid_and_tag_extended(pid, tag); if (ext.error == owner_module_resolver::error_code::success) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Resolved UDPv4 owner: pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -635,7 +640,7 @@ namespace iphelper if (tag != 0 && ext.error == owner_module_resolver::error_code::service_not_found) { if (owner_module_resolver::result img{}; owner_module_resolver::resolve_from_pid_and_tag(pid, 0, img)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Service tag not found; fell back to process image (UDPv4): pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -650,7 +655,7 @@ namespace iphelper } } - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Failed to resolve UDPv4 owner: pid={} tag={} error={}{}", pid, tag, @@ -677,7 +682,7 @@ namespace iphelper { const DWORD pid = row->dwOwningPid; if (is_system_process(pid)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "UDPv6 entry with system process PID = {} ({}) skipping resolution", pid, pid == 0 ? "Idle" : "System"); @@ -687,7 +692,7 @@ namespace iphelper const auto ext = owner_module_resolver::resolve_from_pid_and_tag_extended(pid, tag); if (ext.error == owner_module_resolver::error_code::success) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Resolved UDPv6 owner: pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -703,7 +708,7 @@ namespace iphelper if (tag != 0 && ext.error == owner_module_resolver::error_code::service_not_found) { if (owner_module_resolver::result img{}; owner_module_resolver::resolve_from_pid_and_tag(pid, 0, img)) { - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Service tag not found; fell back to process image (UDPv6): pid={} tag={} name=\"{}\" path=\"{}\"", pid, tag, @@ -718,7 +723,7 @@ namespace iphelper } } - NETLIB_LOG(log_level::debug, + NETLIB_DEBUG( "Failed to resolve UDPv6 owner: pid={} tag={} error={}{}", pid, tag, diff --git a/netlib/src/log/log.h b/netlib/src/log/log.h index f43ce24..3451737 100644 --- a/netlib/src/log/log.h +++ b/netlib/src/log/log.h @@ -141,7 +141,7 @@ namespace netlib::log { * @note Thread-safe for concurrent read/write operations * @note Default value includes all components (timestamp, thread, logger, level, path) */ - inline std::atomic global_log_verbosity{ log_verbosity::all }; + inline std::atomic global_log_verbosity{ log_verbosity::none }; /** * @brief Sets the global log verbosity flags. @@ -987,7 +987,10 @@ namespace netlib::log { friend Derived; }; + // Enhanced logging macros that build on the existing NETLIB_LOG infrastructure + #if NETLIB_HAS_SOURCE_LOCATION +// Core macros with source location support #define NETLIB_LOG(level_, fmt_, ...) \ do { \ if (this->get_log_level() >= (level_)) { \ @@ -1003,6 +1006,7 @@ namespace netlib::log { } while(0) #else +// Core macros without source location support #define NETLIB_LOG(level_, fmt_, ...) \ do { \ if (this->get_log_level() >= (level_)) { \ @@ -1019,4 +1023,28 @@ namespace netlib::log { #endif +// Convenience macros for specific log levels - for use within logger classes +#define NETLIB_ERROR(fmt_, ...) NETLIB_LOG(::netlib::log::log_level::error, fmt_, ##__VA_ARGS__) +#define NETLIB_WARNING(fmt_, ...) NETLIB_LOG(::netlib::log::log_level::warning, fmt_, ##__VA_ARGS__) +#define NETLIB_INFO(fmt_, ...) NETLIB_LOG(::netlib::log::log_level::info, fmt_, ##__VA_ARGS__) +#define NETLIB_DEBUG(fmt_, ...) NETLIB_LOG(::netlib::log::log_level::debug, fmt_, ##__VA_ARGS__) + +// Convenience macros for use with logger pointers +#define NETLIB_ERROR_PTR(logger_ptr_, fmt_, ...) NETLIB_LOG_PTR(logger_ptr_, ::netlib::log::log_level::error, fmt_, ##__VA_ARGS__) +#define NETLIB_WARNING_PTR(logger_ptr_, fmt_, ...) NETLIB_LOG_PTR(logger_ptr_, ::netlib::log::log_level::warning, fmt_, ##__VA_ARGS__) +#define NETLIB_INFO_PTR(logger_ptr_, fmt_, ...) NETLIB_LOG_PTR(logger_ptr_, ::netlib::log::log_level::info, fmt_, ##__VA_ARGS__) +#define NETLIB_DEBUG_PTR(logger_ptr_, fmt_, ...) NETLIB_LOG_PTR(logger_ptr_, ::netlib::log::log_level::debug, fmt_, ##__VA_ARGS__) + +// Simple string message macros (for pre-formatted messages) +#define NETLIB_ERROR_STR(msg_) do { if (this->get_log_level() >= ::netlib::log::log_level::error) this->print_log(::netlib::log::log_level::error, msg_); } while(0) +#define NETLIB_WARNING_STR(msg_) do { if (this->get_log_level() >= ::netlib::log::log_level::warning) this->print_log(::netlib::log::log_level::warning, msg_); } while(0) +#define NETLIB_INFO_STR(msg_) do { if (this->get_log_level() >= ::netlib::log::log_level::info) this->print_log(::netlib::log::log_level::info, msg_); } while(0) +#define NETLIB_DEBUG_STR(msg_) do { if (this->get_log_level() >= ::netlib::log::log_level::debug) this->print_log(::netlib::log::log_level::debug, msg_); } while(0) + +// Simple string message macros for use with logger pointers +#define NETLIB_ERROR_STR_PTR(logger_ptr_, msg_) do { if ((logger_ptr_) && (logger_ptr_)->get_log_level() >= ::netlib::log::log_level::error) (logger_ptr_)->print_log(::netlib::log::log_level::error, msg_); } while(0) +#define NETLIB_WARNING_STR_PTR(logger_ptr_, msg_) do { if ((logger_ptr_) && (logger_ptr_)->get_log_level() >= ::netlib::log::log_level::warning) (logger_ptr_)->print_log(::netlib::log::log_level::warning, msg_); } while(0) +#define NETLIB_INFO_STR_PTR(logger_ptr_, msg_) do { if ((logger_ptr_) && (logger_ptr_)->get_log_level() >= ::netlib::log::log_level::info) (logger_ptr_)->print_log(::netlib::log::log_level::info, msg_); } while(0) +#define NETLIB_DEBUG_STR_PTR(logger_ptr_, msg_) do { if ((logger_ptr_) && (logger_ptr_)->get_log_level() >= ::netlib::log::log_level::debug) (logger_ptr_)->print_log(::netlib::log::log_level::debug, msg_); } while(0) + } // namespace netlib::log \ No newline at end of file diff --git a/netlib/src/proxy/socks_local_router.h b/netlib/src/proxy/socks_local_router.h index b0821dc..b99e590 100644 --- a/netlib/src/proxy/socks_local_router.h +++ b/netlib/src/proxy/socks_local_router.h @@ -75,9 +75,9 @@ namespace proxy std::vector, std::unique_ptr>> proxy_servers_; /** - * @brief Maps process names to their corresponding proxy indexes. + * @brief Maps proxy indexes to their corresponding process names (sorted by proxy ID). */ - std::unordered_map name_to_proxy_; + std::multimap proxy_to_names_; /** * @brief A list of excluded process names. @@ -162,63 +162,6 @@ namespace proxy * allowing the process resolution thread to efficiently wait for work. */ std::condition_variable process_resolve_buffer_queue_cv_; - - /// - /// Cache key for match_app_name results combining process ID and app pattern - /// - struct match_cache_key - { - unsigned long process_id; - std::wstring app_pattern; - - bool operator==(const match_cache_key& other) const noexcept - { - return process_id == other.process_id && app_pattern == other.app_pattern; - } - }; - - /// - /// Hash function for match_cache_key - /// - struct match_cache_key_hash - { - std::size_t operator()(const match_cache_key& key) const noexcept - { - // Combine hash of process_id and app_pattern - const auto h1 = std::hash{}(key.process_id); - const auto h2 = std::hash{}(key.app_pattern); - return h1 ^ (h2 << 1); - } - }; - - /// - /// Cache entry for match_app_name results - /// - struct match_cache_entry - { - bool matches; - std::chrono::steady_clock::time_point timestamp; - }; - - /// - /// Cache for match_app_name results to optimize repeated lookups - /// - mutable std::unordered_map match_cache_; - - /// - /// Mutex to protect the match cache - /// - mutable std::mutex match_cache_mutex_; - - /// - /// Cache TTL - how long cache entries remain valid (in seconds) - /// - static constexpr std::chrono::seconds cache_ttl{ 30 }; - - /// - /// Maximum cache size to prevent unbounded growth - /// - static constexpr size_t max_cache_size{ 2000 }; public: enum supported_protocols : uint8_t @@ -752,11 +695,7 @@ namespace proxy try { // Associate the given process name to the specified proxy ID. - // If the process_name already exists in the map, its associated proxy ID is updated. - name_to_proxy_[to_upper(process_name)] = proxy_id; - - // Clear cache since proxy mappings changed - clear_match_cache(); + proxy_to_names_.emplace(proxy_id, to_upper(process_name)); } catch (const std::exception& e) { NETLIB_LOG(log_level::error, "Exception associating process name to proxy: {}", e.what()); @@ -784,9 +723,6 @@ namespace proxy { // Append the excluded entry excluded_list_.push_back(to_upper(excluded_entry)); - - // Clear cache since exclusion list changed - clear_match_cache(); } catch (const std::exception& e) { NETLIB_LOG(log_level::error, "Exception excluding process name: {}", e.what()); @@ -880,7 +816,7 @@ namespace proxy /** * @brief Matches an application name pattern against the process details with exclusion support. * - * This function performs a two-stage matching process with comprehensive result caching: + * This function performs a two-stage matching process: * 1. First, it checks if the process should be excluded based on the exclusion list * 2. Then, it performs pattern matching if the process is not excluded * @@ -890,14 +826,19 @@ namespace proxy * * The pattern matching is performed case-insensitively using substring matching. * The function automatically excludes the current process (by process ID) to prevent self-matching. - * Results are cached to improve performance for repeated calls with the same parameters. * * @param app The application name or pattern to check against the process details. * Can be either a simple process name (e.g., "notepad.exe") or a path-based pattern (e.g., "C:\\Windows\\System32\\notepad.exe"). * @param process The process details to check against the application pattern. * Must contain valid name and path_name fields for comparison. - * @return true if the process details match the application pattern, are not in the exclusion list, + * @return true if the process details match the application pattern and are not in the exclusion list, * and are not the current process; false otherwise. + * + * @note This function does not perform caching. Caching is handled at a higher level by the + * get_proxy_port_tcp() and get_proxy_port_udp() functions using process_to_proxy_cache_. + * + * @note The function performs direct string matching without regex support. All comparisons + * are case-sensitive as input is already converted to uppercase by calling functions. */ bool match_app_name(const std::wstring& app, const std::shared_ptr& process) const { @@ -907,57 +848,20 @@ namespace proxy if (process->id == ::GetCurrentProcessId()) return false; - // Create cache key - const match_cache_key cache_key{.process_id = process->id, .app_pattern = app }; - const auto now = std::chrono::steady_clock::now(); - - // Check cache first - { - std::lock_guard lock(match_cache_mutex_); - - if (const auto it = match_cache_.find(cache_key); - it != match_cache_.end() && (now - it->second.timestamp) < cache_ttl) - { - return it->second.matches; - } - } - - // Cache miss or expired - compute the result - bool matches = true; - // Check exclusion list for (const auto& excluded_entry : excluded_list_) { if ((excluded_entry.find(L'\\') != std::wstring::npos || excluded_entry.find(L'/') != std::wstring::npos) ? (process->path_name.find(excluded_entry) != std::wstring::npos) : (process->name.find(excluded_entry) != std::wstring::npos) ) { - matches = false; - break; + process->excluded = true; + return false; // Excluded } } - // If not excluded, check pattern matching - if (matches) - { - matches = (app.find(L'\\') != std::wstring::npos || app.find(L'/') != std::wstring::npos) + return (app.find(L'\\') != std::wstring::npos || app.find(L'/') != std::wstring::npos) ? (process->path_name.find(app) != std::wstring::npos) : (process->name.find(app) != std::wstring::npos); - } - - // Update cache - { - std::lock_guard lock(match_cache_mutex_); - - // Perform cleanup if cache is getting large - if (match_cache_.size() >= max_cache_size) - { - cleanup_match_cache(); - } - - match_cache_[cache_key] = {.matches = matches, .timestamp = now }; - } - - return matches; } /** @@ -968,21 +872,20 @@ namespace proxy */ std::optional get_proxy_port_tcp(const std::shared_ptr& process) { - // Locks the proxy servers and process to proxy map for reading. + if (!process) return {}; + std::shared_lock lock(lock_); - // Iterate through each pair in the process to proxy mapping. - for (auto& [name, proxy_id] : name_to_proxy_) + for (const auto& [proxy_id, process_pattern] : proxy_to_names_) { - // Check if the current process name contains the given process name. - if (match_app_name(name, process)) - // If it does, return the TCP proxy port associated with the proxy ID. + if (match_app_name(process_pattern, process)) + { return proxy_servers_[proxy_id].first - ? std::optional(proxy_servers_[proxy_id].first->proxy_port()) - : std::nullopt; + ? std::optional(proxy_servers_[proxy_id].first->proxy_port()) + : std::nullopt; + } } - // If the process name is not found in any of the keys, return an empty std::optional. return {}; } @@ -994,21 +897,21 @@ namespace proxy */ std::optional get_proxy_port_udp(const std::shared_ptr& process) { - // Locks the proxy servers and process to proxy map for reading. + if (!process) return {}; + std::shared_lock lock(lock_); - // Iterate through each pair in the process to proxy mapping. - for (auto& [name, proxy_id] : name_to_proxy_) + // Cache miss - search through patterns + for (const auto& [proxy_id, process_pattern] : proxy_to_names_) { - // Check if the current process name contains the given process name. - if (match_app_name(name, process)) - // If it does, return the UDP proxy port associated with the proxy ID. + if (match_app_name(process_pattern, process)) + { return proxy_servers_[proxy_id].second - ? std::optional(proxy_servers_[proxy_id].second->proxy_port()) - : std::nullopt; + ? std::optional(proxy_servers_[proxy_id].second->proxy_port()) + : std::nullopt; + } } - // If the process name is not found in any of the keys, return an empty std::optional. return {}; } @@ -1118,7 +1021,10 @@ namespace proxy } } - if (const auto port = get_proxy_port_udp(process); port.has_value()) + if (process->excluded || process->bypass_udp) + return packet_filter::packet_action{ packet_filter::packet_action::action_type::pass }; + + if (const auto port = process->udp_proxy_port? process->udp_proxy_port:get_proxy_port_udp(process); port.has_value()) { if (udp_redirect_->is_new_endpoint(buffer)) { @@ -1138,6 +1044,10 @@ namespace proxy } } + else + { + process->bypass_udp = true; + } return packet_filter::packet_action{ packet_filter::packet_action::action_type::pass }; } @@ -1204,7 +1114,10 @@ namespace proxy } } - if (const auto port = get_proxy_port_tcp(process); port.has_value()) + if (process->excluded || process->bypass_tcp) + return packet_filter::packet_action{ packet_filter::packet_action::action_type::pass }; + + if (const auto port = process->tcp_proxy_port? process->tcp_proxy_port:get_proxy_port_tcp(process); port.has_value()) { // If this is a SYN packet (connection initiation), map the source port to the destination endpoint if ((tcp_header->th_flags & (TH_SYN | TH_ACK)) == TH_SYN) @@ -1227,6 +1140,10 @@ namespace proxy return packet_filter::packet_action{ packet_filter::packet_action::action_type::revert }; } } + else + { + process->bypass_tcp = true; + } // Otherwise, pass the packet through return packet_filter::packet_action{ packet_filter::packet_action::action_type::pass }; @@ -1481,61 +1398,5 @@ namespace proxy adapters_to_filter_ = std::move(adapters_to_filter); } } - - /** - * @brief Clears the match cache. Should be called when exclusion list or proxy mappings change. - */ - void clear_match_cache() const - { - std::lock_guard lock(match_cache_mutex_); - match_cache_.clear(); - } - - /** - * @brief Performs cache cleanup by removing expired and oldest entries if needed. - */ - void cleanup_match_cache() const - { - const auto now = std::chrono::steady_clock::now(); - - // Remove expired entries - for (auto it = match_cache_.begin(); it != match_cache_.end();) - { - if ((now - it->second.timestamp) >= cache_ttl) - { - it = match_cache_.erase(it); - } - else - { - ++it; - } - } - - // If still too large, remove the oldest entries - if (match_cache_.size() > max_cache_size) - { - // Create vector of iterators sorted by timestamp - std::vector entries; - entries.reserve(match_cache_.size()); - - for (auto it = match_cache_.begin(); it != match_cache_.end(); ++it) - { - entries.push_back(it); - } - - // Sort by timestamp (oldest first) - std::ranges::sort(entries, - [](const auto& a, const auto& b) { - return a->second.timestamp < b->second.timestamp; - }); - - // Remove the oldest entries until we're under the limit - const size_t entries_to_remove = match_cache_.size() - max_cache_size; - for (size_t i = 0; i < entries_to_remove && i < entries.size(); ++i) - { - match_cache_.erase(entries[i]); - } - } - } }; } From 63f5d50e4f945a59b23afb1f7bd674d5f7a44e22 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 3 Sep 2025 11:02:02 +0200 Subject: [PATCH 2/2] fix: gemini-code-assist --- netlib/src/iphelper/process_lookup.h | 4 ++-- netlib/src/proxy/socks_local_router.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/netlib/src/iphelper/process_lookup.h b/netlib/src/iphelper/process_lookup.h index bdafa65..ad558bc 100644 --- a/netlib/src/iphelper/process_lookup.h +++ b/netlib/src/iphelper/process_lookup.h @@ -112,8 +112,8 @@ namespace iphelper std::wstring name; ///< Process name (uppercase) std::wstring path_name; ///< Full path to executable (uppercase) std::wstring device_path_name; ///< Device path version of path_name (uppercase) - std::optional tcp_proxy_port = std::nullopt; // Optional tcp proxy port if the process is associated with a proxy - std::optional udp_proxy_port = std::nullopt; // Optional udp proxy port if the process is associated with a proxy + std::optional tcp_proxy_port = std::nullopt; // Optional TCP proxy port if the process is associated with a proxy + std::optional udp_proxy_port = std::nullopt; // Optional UDP proxy port if the process is associated with a proxy bool excluded = false; ///< Whether the process is excluded from proxying bool bypass_tcp = false; ///< Whether TCP connections should bypass proxying (no proxy configured) bool bypass_udp = false; ///< Whether UDP connections should bypass proxying (no proxy configured) diff --git a/netlib/src/proxy/socks_local_router.h b/netlib/src/proxy/socks_local_router.h index b99e590..e9ad775 100644 --- a/netlib/src/proxy/socks_local_router.h +++ b/netlib/src/proxy/socks_local_router.h @@ -901,7 +901,7 @@ namespace proxy std::shared_lock lock(lock_); - // Cache miss - search through patterns + // Search for a matching process pattern for (const auto& [proxy_id, process_pattern] : proxy_to_names_) { if (match_app_name(process_pattern, process)) @@ -1024,7 +1024,7 @@ namespace proxy if (process->excluded || process->bypass_udp) return packet_filter::packet_action{ packet_filter::packet_action::action_type::pass }; - if (const auto port = process->udp_proxy_port? process->udp_proxy_port:get_proxy_port_udp(process); port.has_value()) + if (const auto port = process->udp_proxy_port ? process->udp_proxy_port : get_proxy_port_udp(process); port.has_value()) { if (udp_redirect_->is_new_endpoint(buffer)) { @@ -1117,7 +1117,7 @@ namespace proxy if (process->excluded || process->bypass_tcp) return packet_filter::packet_action{ packet_filter::packet_action::action_type::pass }; - if (const auto port = process->tcp_proxy_port? process->tcp_proxy_port:get_proxy_port_tcp(process); port.has_value()) + if (const auto port = process->tcp_proxy_port ? process->tcp_proxy_port : get_proxy_port_tcp(process); port.has_value()) { // If this is a SYN packet (connection initiation), map the source port to the destination endpoint if ((tcp_header->th_flags & (TH_SYN | TH_ACK)) == TH_SYN)