mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-08-30 04:15:10 +00:00
connection between 2 systems and bug fixes (#14)
This commit is contained in:
@@ -8,33 +8,64 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#*******************************************************************************
|
||||
|
||||
if(WIN32)
|
||||
|
||||
# Define project
|
||||
project(uds_win_tunnel VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(uds_win_tunnel STATIC
|
||||
set(UDS_WIN_TUNNEL_SOURCES
|
||||
channel_mgnt.cpp
|
||||
connection.cpp
|
||||
watchdog.cpp
|
||||
)
|
||||
|
||||
# Define targets
|
||||
add_library(uds_win_tunnel STATIC
|
||||
${UDS_WIN_TUNNEL_SOURCES}
|
||||
)
|
||||
|
||||
add_library(uds_win_tunnel_module SHARED
|
||||
${UDS_WIN_TUNNEL_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(uds_win_tunnel
|
||||
PRIVATE
|
||||
uds_win_sockets
|
||||
Ws2_32.lib
|
||||
Ws2_32.lib
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_include_directories(uds_win_tunnel
|
||||
PRIVATE
|
||||
target_link_libraries(uds_win_tunnel_module
|
||||
PRIVATE
|
||||
uds_win_sockets
|
||||
Ws2_32.lib
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_include_directories(uds_win_tunnel
|
||||
PRIVATE
|
||||
./include/
|
||||
../uds_win_sockets/
|
||||
)
|
||||
|
||||
set_target_properties(uds_win_tunnel PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_win_tunnel PROPERTIES SUFFIX ".sdv")
|
||||
target_include_directories(uds_win_tunnel_module
|
||||
PRIVATE
|
||||
./include/
|
||||
../uds_win_sockets/
|
||||
)
|
||||
|
||||
set_target_properties(uds_win_tunnel_module PROPERTIES
|
||||
PREFIX ""
|
||||
SUFFIX ".sdv"
|
||||
OUTPUT_NAME "uds_win_tunnel"
|
||||
ARCHIVE_OUTPUT_NAME "uds_win_tunnel_module"
|
||||
)
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(uds_win_tunnel CompileCoreIDL)
|
||||
add_dependencies(uds_win_tunnel_module CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel PARENT_SCOPE)
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel_module PARENT_SCOPE)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#ifdef _WIN32
|
||||
#include "channel_mgnt.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "../../global/base64.h"
|
||||
#include <support/toml.h>
|
||||
#include <interfaces/process.h>
|
||||
|
||||
#include <future>
|
||||
|
||||
#pragma push_macro("interface")
|
||||
#undef interface
|
||||
|
||||
@@ -39,298 +41,498 @@
|
||||
#pragma pop_macro("GetObject")
|
||||
#pragma pop_macro("interface")
|
||||
|
||||
extern int StartUpWinSock();
|
||||
//#include "../sdv_services/uds_win_sockets/channel_mgnt.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* @brief Parse a tunnel connect/config string and extract the path.
|
||||
*
|
||||
* Expected format:
|
||||
* "proto=tunnel;path=<something>;"
|
||||
*
|
||||
* Behavior:
|
||||
* - If "proto=tunnel" missing -> false
|
||||
* - If "path=" missing -> true and outPath.clear()
|
||||
*
|
||||
* @param[in] cs The connect/config string to parse.
|
||||
* @param[out] outPath The extracted path, or empty if not found.
|
||||
* @return true if parsing succeeded, false otherwise.
|
||||
*/
|
||||
static bool ParseTunnelPath(const std::string& cs, std::string& outPath)
|
||||
{
|
||||
constexpr const char* protoKey = "proto=tunnel";
|
||||
constexpr const char* pathKey = "path=";
|
||||
|
||||
if (cs.find(protoKey) == std::string::npos)
|
||||
static std::atomic<uint32_t> g_nextChannelId{1};
|
||||
|
||||
static bool EnsureWSAInitialized()
|
||||
{
|
||||
return false;
|
||||
static std::once_flag s_once;
|
||||
static bool s_ok = false;
|
||||
|
||||
std::call_once(s_once, []()
|
||||
{
|
||||
WSADATA wsa{};
|
||||
const int rc = WSAStartup(MAKEWORD(2, 2), &wsa);
|
||||
s_ok = (rc == 0);
|
||||
if (!s_ok)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] WSAStartup failed, rc=", rc);
|
||||
}
|
||||
});
|
||||
|
||||
return s_ok;
|
||||
}
|
||||
|
||||
const auto p = cs.find(pathKey);
|
||||
if (p == std::string::npos)
|
||||
static std::string SanitizeUdsName(std::string name)
|
||||
{
|
||||
outPath.clear();
|
||||
for (char& ch : name)
|
||||
{
|
||||
const bool isAlphaNum = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
|
||||
if (!isAlphaNum && ch != '_' && ch != '-')
|
||||
{
|
||||
ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
if (name.empty())
|
||||
{
|
||||
name = "sdv";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static std::string BuildNamedUdsRawPath(const std::string& channelName)
|
||||
{
|
||||
return "%LOCALAPPDATA%/sdv/" + SanitizeUdsName(channelName) + ".sock";
|
||||
}
|
||||
|
||||
static std::string GetDefaultUdsRawPath()
|
||||
{
|
||||
uint32_t instanceId = 1000u;
|
||||
const sdv::app::IAppContext* pAppContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pAppContext && pAppContext->GetInstanceID() != 0u)
|
||||
{
|
||||
instanceId = pAppContext->GetInstanceID();
|
||||
}
|
||||
|
||||
return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + ".sock";
|
||||
}
|
||||
|
||||
static std::string GetUniqueEndpointUdsRawPath()
|
||||
{
|
||||
static std::atomic<uint32_t> nextEndpointId { 0u };
|
||||
|
||||
uint32_t instanceId = 1000u;
|
||||
const sdv::app::IAppContext* pAppContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pAppContext && pAppContext->GetInstanceID() != 0u)
|
||||
{
|
||||
instanceId = pAppContext->GetInstanceID();
|
||||
}
|
||||
|
||||
const uint32_t endpointId = nextEndpointId.fetch_add(1u, std::memory_order_relaxed);
|
||||
return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + "_" +
|
||||
std::to_string(static_cast<uint32_t>(GetCurrentProcessId())) + "_" + std::to_string(endpointId) + ".sock";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a tunnel connect/config string and extract the path.
|
||||
*
|
||||
* Expected format:
|
||||
* "proto=tunnel;path=<something>;"
|
||||
*
|
||||
* Behavior:
|
||||
* - If "proto=tunnel" missing -> false
|
||||
* - If "path=" missing -> true and outPath.clear()
|
||||
*
|
||||
* @param[in] cs The connect/config string to parse.
|
||||
* @param[out] outPath The extracted path, or empty if not found.
|
||||
* @return true if parsing succeeded, false otherwise.
|
||||
*/
|
||||
static bool ParseTunnelPath(const std::string& cs, std::string& outPath)
|
||||
{
|
||||
constexpr const char* protoKey = "proto=tunnel";
|
||||
constexpr const char* pathKey = "path=";
|
||||
|
||||
// Strict raw connect string only
|
||||
if (cs.rfind(protoKey, 0) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto p = cs.find(pathKey);
|
||||
if (p == std::string::npos)
|
||||
{
|
||||
outPath.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto start = p + std::strlen(pathKey);
|
||||
const auto end = cs.find(';', start);
|
||||
if (end == std::string::npos)
|
||||
{
|
||||
outPath = cs.substr(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
outPath = cs.substr(start, end - start);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto start = p + std::strlen(pathKey);
|
||||
const auto end = cs.find(';', start);
|
||||
if (end == std::string::npos)
|
||||
static std::string ExtractTunnelNameFromToml(sdv::toml::CTOMLParser& parser)
|
||||
{
|
||||
outPath = cs.substr(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
outPath = cs.substr(start, end - start);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string tunnel;
|
||||
|
||||
/**
|
||||
* @brief Expands Windows environment variables in a string (e.g., %TEMP%).
|
||||
* @param[in] in Input string possibly containing environment variables.
|
||||
* @return String with environment variables expanded, or original if expansion fails.
|
||||
*/
|
||||
static std::string ExpandEnvVars(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
auto ipcTunnelNode = parser.GetDirect("IpcChannel.Tunnel");
|
||||
if (ipcTunnelNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
tunnel = static_cast<std::string>(ipcTunnelNode.GetValue());
|
||||
}
|
||||
|
||||
if (tunnel.empty())
|
||||
{
|
||||
auto providerTunnelNode = parser.GetDirect("Provider.Tunnel");
|
||||
if (providerTunnelNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
tunnel = static_cast<std::string>(providerTunnelNode.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
static bool ExtractTunnelConnectString(const std::string& in,
|
||||
std::string& outTunnelConnectString)
|
||||
{
|
||||
std::string path;
|
||||
|
||||
// Case 1: strict raw tunnel connect string
|
||||
if (ParseTunnelPath(in, path))
|
||||
{
|
||||
outTunnelConnectString = in;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Case 2: structured TOML
|
||||
sdv::toml::CTOMLParser parser(in);
|
||||
if (!parser.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string providerName = parser.GetDirect("Provider.Name").GetValue();
|
||||
if (!providerName.empty() &&
|
||||
providerName != "unix_domain_sockets_tunnel" &&
|
||||
providerName != "WinTunnelChannelControl" &&
|
||||
providerName != "UnixTunnelChannelControl")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Provider.ConnectString = "proto=tunnel;path=...;tunnel=...;"
|
||||
const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue();
|
||||
if (!nested.empty())
|
||||
{
|
||||
if (ParseTunnelPath(nested, path))
|
||||
{
|
||||
outTunnelConnectString = nested;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build a tunnel connect string from [IpcChannel]
|
||||
const std::string tunnel = ExtractTunnelNameFromToml(parser);
|
||||
|
||||
const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue();
|
||||
if (!cfgPath.empty())
|
||||
{
|
||||
outTunnelConnectString = "proto=tunnel;path=" + cfgPath + ";";
|
||||
if (!tunnel.empty())
|
||||
{
|
||||
outTunnelConnectString += "tunnel=" + tunnel + ";";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
if (!cfgName.empty())
|
||||
{
|
||||
outTunnelConnectString = "proto=tunnel;path=" + BuildNamedUdsRawPath(cfgName) + ";";
|
||||
if (!tunnel.empty())
|
||||
{
|
||||
outTunnelConnectString += "tunnel=" + tunnel + ";";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Expands Windows environment variables in a string (e.g., %TEMP%).
|
||||
* @param[in] in Input string possibly containing environment variables.
|
||||
* @return String with environment variables expanded, or original if expansion fails.
|
||||
*/
|
||||
static std::string ExpandEnvVars(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
|
||||
if (n > 0 && n < sizeof(buf))
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
|
||||
if (n > 0 && n < sizeof(buf))
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN.
|
||||
* @param[in] p The input path.
|
||||
* @return The clamped path.
|
||||
*/
|
||||
static std::string ClampUdsPath(const std::string& p)
|
||||
{
|
||||
SOCKADDR_UN tmp{};
|
||||
constexpr auto kMax = sizeof(tmp.sun_path) - 1;
|
||||
if (p.size() <= kMax)
|
||||
/**
|
||||
* @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN.
|
||||
* @param[in] p The input path.
|
||||
* @return The clamped path.
|
||||
*/
|
||||
static std::string ClampUdsPath(const std::string& p)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
return p.substr(0, kMax);
|
||||
}
|
||||
|
||||
// Only for logging – basename
|
||||
/**
|
||||
* @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty.
|
||||
* @param[in] raw The raw path string.
|
||||
* @return The normalized basename, clamped to max length.
|
||||
*/
|
||||
static std::string NormalizeUdsPathForWindows(const std::string& raw)
|
||||
{
|
||||
std::string p = ExpandEnvVars(raw);
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv_tunnel.sock";
|
||||
}
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'");
|
||||
return ClampUdsPath(base);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a short, safe UDS path in the Windows temp directory.
|
||||
* @param[in] raw The raw path string.
|
||||
* @return The full path in %TEMP%\sdv\, clamped to max length.
|
||||
*/
|
||||
static std::string MakeShortWinUdsPath(const std::string& raw)
|
||||
{
|
||||
std::string p = ExpandEnvVars(raw);
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv_tunnel.sock";
|
||||
SOCKADDR_UN tmp{};
|
||||
constexpr auto kMax = sizeof(tmp.sun_path) - 1;
|
||||
if (p.size() <= kMax)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
return p.substr(0, kMax);
|
||||
}
|
||||
|
||||
std::string dir = ExpandEnvVars("%TEMP%\\sdv\\");
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
const std::string full = dir + base;
|
||||
|
||||
return ClampUdsPath(full);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates an AF_UNIX listen socket at the specified path.
|
||||
* @param[in] rawPath The raw path for the socket.
|
||||
* @return The created socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET CreateUnixListenSocket(const std::string& rawPath)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
static std::string BuildFinalUdsPath(const std::string& rawPath)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (listen), WSA=", WSAGetLastError());
|
||||
return INVALID_SOCKET;
|
||||
// Expand environment variables
|
||||
std::string full = ExpandEnvVars(rawPath);
|
||||
|
||||
// Ensure directory exists (parent folder)
|
||||
auto pos = full.find_last_of("\\/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string dir = full.substr(0, pos);
|
||||
|
||||
// Ensure immediate parent exists (base/tunnel directory is created earlier)
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
}
|
||||
|
||||
// Clamp to AF_UNIX limit
|
||||
return ClampUdsPath(full);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty.
|
||||
* @param[in] raw The raw path string.
|
||||
* @return The normalized basename, clamped to max length.
|
||||
*/
|
||||
static std::string NormalizeUdsPathForWindows(const std::string& raw)
|
||||
{
|
||||
std::string p = ExpandEnvVars(raw);
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv_tunnel.sock";
|
||||
}
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'");
|
||||
return ClampUdsPath(base);
|
||||
}
|
||||
|
||||
std::string udsPath = MakeShortWinUdsPath(rawPath);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
::remove(udsPath.c_str());
|
||||
if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) == SOCKET_ERROR)
|
||||
/**
|
||||
* @brief Creates an AF_UNIX listen socket at the specified path.
|
||||
* @param[in] rawPath The raw path for the socket.
|
||||
* @return The created socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET CreateUnixListenSocket(const std::string& rawPath)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] bind FAIL, WSA=",
|
||||
WSAGetLastError(), ", path='", udsPath, "'");
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
if (listen(s, SOMAXCONN) == SOCKET_ERROR)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] listen FAIL, WSA=",
|
||||
WSAGetLastError(), ", path='", udsPath, "'");
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (!EnsureWSAInitialized())
|
||||
return INVALID_SOCKET;
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] bind+listen OK, path='", udsPath, "'");
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout.
|
||||
* @param[in] rawPath The raw path to connect to.
|
||||
* @param[in] totalTimeoutMs Total timeout in milliseconds.
|
||||
* @param[in] retryDelayMs Delay between retries in milliseconds.
|
||||
* @return The connected socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET ConnectUnixSocket(
|
||||
const std::string& rawPath,
|
||||
uint32_t totalTimeoutMs,
|
||||
uint32_t retryDelayMs)
|
||||
{
|
||||
const std::string udsPath = MakeShortWinUdsPath(rawPath);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(totalTimeoutMs);
|
||||
|
||||
int lastError = 0;
|
||||
while (true)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
lastError = WSAGetLastError();
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (client), WSA=", lastError);
|
||||
SDV_LOG_ERROR("[AF_UNIX] socket FAIL (listen), WSA=", WSAGetLastError());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (connect(s, reinterpret_cast<const sockaddr*>(&addr), addrlen) == 0)
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] connect OK, path='", udsPath, "'");
|
||||
return s;
|
||||
}
|
||||
//bulletproof path handling
|
||||
std::string udsPath = BuildFinalUdsPath(rawPath);
|
||||
|
||||
lastError = WSAGetLastError();
|
||||
closesocket(s);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline)
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
::remove(udsPath.c_str());
|
||||
|
||||
if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) == SOCKET_ERROR)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] connect TIMEOUT, last WSA=",
|
||||
lastError, ", path='", udsPath, "'");
|
||||
int err = WSAGetLastError();
|
||||
SDV_LOG_ERROR("[AF_UNIX] bind FAIL, WSA=", err, ", path=", udsPath);
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
|
||||
if (listen(s, SOMAXCONN) == SOCKET_ERROR)
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
SDV_LOG_ERROR("[AF_UNIX] listen FAIL, WSA=", err, ", path=", udsPath);
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX] bind+listen OK: ", udsPath);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ExtractTunnelName(const std::string& in, std::string& outTunnel)
|
||||
{
|
||||
auto pos = in.find("tunnel=");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
outTunnel.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
pos += 7;
|
||||
auto end = in.find(';', pos);
|
||||
outTunnel = (end == std::string::npos) ? in.substr(pos) : in.substr(pos, end - pos);
|
||||
return !outTunnel.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout.
|
||||
* @param[in] rawPath The raw path to connect to.CreateUnixListenSocket
|
||||
* @param[in] totalTimeoutMs Total timeout in milliseconds.
|
||||
* @param[in] retryDelayMs Delay between retries in milliseconds.
|
||||
* @return The connected socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET ConnectUnixSocket(
|
||||
const std::string& rawPath,
|
||||
uint32_t totalTimeoutMs,
|
||||
uint32_t retryDelayMs)
|
||||
{
|
||||
if (!EnsureWSAInitialized())
|
||||
return INVALID_SOCKET;
|
||||
|
||||
//SAME path logic ca server
|
||||
const std::string udsPath = BuildFinalUdsPath(rawPath);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Attempting to connect to ", udsPath, "with rawPath=", rawPath);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(totalTimeoutMs);
|
||||
|
||||
while (true)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] socket FAIL (client), WSA=", WSAGetLastError());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (connect(s, reinterpret_cast<const sockaddr*>(&addr), addrlen) == 0)
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX] connect OK: ", udsPath);
|
||||
return s;
|
||||
}
|
||||
|
||||
int err = WSAGetLastError();
|
||||
closesocket(s);
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] connect TIMEOUT, WSA=", err, ", path=", udsPath);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
bool CSocketsTunnelChannelMgnt::OnInitialize()
|
||||
{
|
||||
return true;
|
||||
return EnsureWSAInitialized();
|
||||
}
|
||||
|
||||
void CSocketsTunnelChannelMgnt::OnShutdown()
|
||||
{}
|
||||
|
||||
// -------- Server bookkeeping (optional) --------
|
||||
void CSocketsTunnelChannelMgnt::OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr)
|
||||
void CSocketsTunnelChannelMgnt::OnDestroy()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
auto it = m_udsServers.find(udsPath);
|
||||
if (it != m_udsServers.end() && it->second.get() == ptr)
|
||||
{
|
||||
m_udsServers.erase(it);
|
||||
}
|
||||
m_udsServerClaimed.erase(udsPath);
|
||||
m_watchdog.Clear();
|
||||
}
|
||||
|
||||
// -------- ICreateEndpoint --------
|
||||
|
||||
// Note:
|
||||
// tunnel name is required by the current Windows tunnel design for namespace isolation.
|
||||
// [IpcChannel.Path]/[IpcChannel.Name] alone are not sufficient unless Tunnel is also provided.
|
||||
sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr)
|
||||
{
|
||||
sdv::ipc::SChannelEndpoint ep{};
|
||||
|
||||
if (StartUpWinSock() != 0)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in CreateEndpoint");
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Optional TOML config: [IpcChannel] Path = "..."
|
||||
std::string udsRaw;
|
||||
if (!cfgStr.empty())
|
||||
{
|
||||
//for toml file
|
||||
bool isTOML = cfgStr.find('=') == std::string::npos;
|
||||
if(isTOML)
|
||||
{
|
||||
sdv::toml::CTOMLParser cfg(cfgStr.c_str());
|
||||
auto pathNode = cfg.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
udsRaw = static_cast<std::string>(pathNode.GetValue());
|
||||
}
|
||||
}
|
||||
std::string tunnelConnectString;
|
||||
|
||||
//for connect string
|
||||
if (udsRaw.empty())
|
||||
if (ExtractTunnelConnectString(cfgStr, tunnelConnectString))
|
||||
{
|
||||
ParseTunnelPath(tunnelConnectString, udsRaw);
|
||||
}
|
||||
else if (!cfgStr.empty())
|
||||
{
|
||||
sdv::toml::CTOMLParser parser(cfgStr);
|
||||
const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue();
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
|
||||
if (!cfgPath.empty())
|
||||
{
|
||||
const std::string s(cfgStr);
|
||||
const std::string key = "path=";
|
||||
auto pos = s.find(key);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto end = s.find(';', pos + key.size());
|
||||
if (end == std::string::npos)
|
||||
udsRaw = s.substr(pos + key.size());
|
||||
else
|
||||
udsRaw = s.substr(pos + key.size(), end - pos - key.size());
|
||||
}
|
||||
udsRaw = cfgPath;
|
||||
}
|
||||
else if (!cfgName.empty())
|
||||
{
|
||||
udsRaw = BuildNamedUdsRawPath(cfgName);
|
||||
}
|
||||
else
|
||||
{
|
||||
udsRaw = GetDefaultUdsRawPath();
|
||||
}
|
||||
}
|
||||
if (udsRaw.empty())
|
||||
{
|
||||
udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock";
|
||||
udsRaw = cfgStr.empty() ? GetUniqueEndpointUdsRawPath() : GetDefaultUdsRawPath();
|
||||
}
|
||||
|
||||
if (tunnelConnectString.empty())
|
||||
{
|
||||
tunnelConnectString = cfgStr;
|
||||
}
|
||||
|
||||
std::string tunnel;
|
||||
if (!ExtractTunnelName(tunnelConnectString, tunnel))
|
||||
{
|
||||
// fallback for AppConnect (config minimal)
|
||||
sdv::toml::CTOMLParser parser(cfgStr);
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
|
||||
if (!cfgName.empty())
|
||||
{
|
||||
tunnel = cfgName;
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Using channel name as tunnel name: ", tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// fallback
|
||||
tunnel = "default";
|
||||
SDV_LOG_WARNING("[AF_UNIX][Tunnel] Missing tunnel and channel name, using default");
|
||||
}
|
||||
}
|
||||
|
||||
std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw);
|
||||
std::string base = ExpandEnvVars("%TEMP%\\sdv\\");
|
||||
CreateDirectoryA(base.c_str(), nullptr);
|
||||
|
||||
std::string dir = base + tunnel + "\\";
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
|
||||
std::string udsPathBase = dir + NormalizeUdsPathForWindows(udsRaw);
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] endpoint udsPath=", udsPathBase);
|
||||
|
||||
SOCKET listenSocket = CreateUnixListenSocket(udsPathBase);
|
||||
@@ -340,75 +542,177 @@ sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv::
|
||||
return ep;
|
||||
}
|
||||
|
||||
auto serverTransport = std::make_shared<CWinsockConnection>( static_cast<unsigned long long>(listenSocket), true);
|
||||
auto serverTunnel = std::make_shared<CWinTunnelConnection>(serverTransport, /*channelId*/ static_cast<uint16_t>(0u));
|
||||
|
||||
auto serverTransport = std::make_shared<CWinsockConnection>(static_cast<unsigned long long>(listenSocket), true);
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
auto serverTunnel = std::make_shared<CWinTunnelConnection>(serverTransport, /*channelId*/ static_cast<uint16_t>(chId));
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!serverTunnel)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
m_udsServers[udsPathBase] = serverTunnel;
|
||||
m_udsServerClaimed.erase(udsPathBase);
|
||||
return ep;
|
||||
}
|
||||
// Retain shared_ptr to keep object alive for the duration of the connection.
|
||||
// This ensures shared_from_this() works correctly in DestroyObject().
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections[serverTunnel.get()] = serverTunnel;
|
||||
}
|
||||
|
||||
serverTunnel->SetWatchDogRemoveCallback([this](const void* connection)
|
||||
{
|
||||
m_watchdog.RemoveConnection(connection);
|
||||
// Remove the retained shared_ptr when connection is destroyed
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections.erase(connection);
|
||||
});
|
||||
|
||||
m_watchdog.AddConnection(serverTunnel);
|
||||
|
||||
ep.pConnection = static_cast<sdv::IInterfaceAccess*>(serverTunnel.get());
|
||||
ep.ssConnectString = "proto=tunnel;role=server;path=" + udsPathBase + ";";
|
||||
|
||||
const std::string clientConnectString = "proto=tunnel;path=" + udsPathBase + ";tunnel=" + tunnel + ";";
|
||||
|
||||
// Publish raw connect string (cleaner and avoids TOML escaping issues on Windows)
|
||||
ep.ssConnectString = clientConnectString;
|
||||
|
||||
return ep;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CSocketsTunnelChannelMgnt::Access(const sdv::u8string& cs)
|
||||
{
|
||||
if (StartUpWinSock() != 0)
|
||||
std::string tunnelConnectString;
|
||||
const std::string input = static_cast<std::string>(cs);
|
||||
|
||||
// Operational metadata, not part of the tunnel connect string itself
|
||||
const bool isServer = (input.find("role=server") != std::string::npos);
|
||||
|
||||
// Parse only the real connect/config part
|
||||
std::string parseInput = input;
|
||||
const auto rolePos = parseInput.find(";role=");
|
||||
if (rolePos != std::string::npos)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in Access()" );
|
||||
const auto roleEnd = parseInput.find(';', rolePos + 1);
|
||||
if (roleEnd != std::string::npos)
|
||||
{
|
||||
parseInput.erase(rolePos, roleEnd - rolePos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
parseInput.erase(rolePos);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ExtractTunnelConnectString(parseInput, tunnelConnectString))
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect/config string");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string connectStr = static_cast<std::string>(cs);
|
||||
std::string udsRaw;
|
||||
if (!ParseTunnelPath(connectStr, udsRaw))
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect string: ", connectStr);
|
||||
return nullptr;
|
||||
}
|
||||
ParseTunnelPath(tunnelConnectString, udsRaw);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access requested with connect string: ",
|
||||
tunnelConnectString, ", extracted path: ", udsRaw);
|
||||
|
||||
if (udsRaw.empty())
|
||||
{
|
||||
udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock";
|
||||
udsRaw = GetDefaultUdsRawPath();
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] No path specified, using default: ", udsRaw);
|
||||
}
|
||||
|
||||
std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase);
|
||||
|
||||
const bool isServer =
|
||||
(connectStr.find("role=server") != std::string::npos);
|
||||
std::string tunnel;
|
||||
|
||||
if (!ExtractTunnelName(tunnelConnectString, tunnel))
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
auto it = m_udsServers.find(udsPathBase);
|
||||
if (isServer && it != m_udsServers.end() && it->second != nullptr)
|
||||
// fallback: derive from filename
|
||||
std::string path;
|
||||
ParseTunnelPath(tunnelConnectString, path);
|
||||
|
||||
// extract filename
|
||||
auto pos = path.find_last_of("/\\");
|
||||
std::string filename = (pos != std::string::npos) ? path.substr(pos + 1) : path;
|
||||
|
||||
// remove ".sock"
|
||||
auto dot = filename.rfind(".sock");
|
||||
if (dot != std::string::npos)
|
||||
{
|
||||
if (!m_udsServerClaimed.count(udsPathBase))
|
||||
{
|
||||
m_udsServerClaimed.insert(udsPathBase);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> RETURN SERVER for ", udsPathBase);
|
||||
return it->second.get(); // Ownership: managed by m_udsServers (do not delete)
|
||||
}
|
||||
tunnel = filename.substr(0, dot);
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnel = filename;
|
||||
}
|
||||
|
||||
if (!tunnel.empty())
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Derived tunnel from filename: ", tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT: create AF_UNIX client socket and wrap it in a tunnel
|
||||
SOCKET s = ConnectUnixSocket(udsPathBase, /*totalTimeoutMs*/ 5000, /*retryDelayMs*/ 50);
|
||||
if (s == INVALID_SOCKET)
|
||||
if (tunnel.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase);
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Missing required tunnel name");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase);
|
||||
auto clientTransport = std::make_shared<CWinsockConnection>(s, /*acceptRequired*/ false);
|
||||
// Ownership: The returned pointer must be managed and deleted by the SDV framework via IObjectDestroy
|
||||
auto* tunnelClient = new CWinTunnelConnection(clientTransport, /*channelId*/ 0u);
|
||||
std::string base = ExpandEnvVars("%TEMP%\\sdv\\");
|
||||
CreateDirectoryA(base.c_str(), nullptr);
|
||||
|
||||
return static_cast<sdv::IInterfaceAccess*>(tunnelClient);
|
||||
std::string dir = base + tunnel + "\\";
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
|
||||
std::string udsPathBase = dir + NormalizeUdsPathForWindows(udsRaw);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase);
|
||||
|
||||
std::shared_ptr<CWinTunnelConnection> connection;
|
||||
if (isServer)
|
||||
{
|
||||
SOCKET listenSocket = CreateUnixListenSocket(udsPathBase);
|
||||
if (listenSocket == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to create server socket for ", udsPathBase);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto serverTransport = std::make_shared<CWinsockConnection>(
|
||||
static_cast<unsigned long long>(listenSocket), true);
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
connection = std::make_shared<CWinTunnelConnection>(
|
||||
serverTransport, static_cast<uint16_t>(chId));
|
||||
}
|
||||
else
|
||||
{
|
||||
SOCKET s = ConnectUnixSocket(udsPathBase, 5000, 50);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase);
|
||||
auto clientTransport = std::make_shared<CWinsockConnection>(s, false);
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
connection = std::make_shared<CWinTunnelConnection>(
|
||||
clientTransport, static_cast<uint16_t>(chId));
|
||||
}
|
||||
|
||||
if (!connection)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
connection->SetWatchDogRemoveCallback([this](const void* instance)
|
||||
{
|
||||
m_watchdog.RemoveConnection(instance);
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections.erase(instance);
|
||||
});
|
||||
|
||||
m_watchdog.AddConnection(connection);
|
||||
return static_cast<sdv::IInterfaceAccess*>(connection.get());
|
||||
}
|
||||
#endif
|
||||
@@ -16,18 +16,15 @@
|
||||
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "../sdv_services/uds_win_sockets/channel_mgnt.h"
|
||||
#include "../sdv_services/uds_win_sockets/connection.h"
|
||||
#include "connection.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
// Winsock headers are required for SOCKET / AF_UNIX / WSAStartup
|
||||
// NOTE: The actual initialization is done via StartUpWinSock()
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
class CWinTunnelConnection;
|
||||
@@ -56,8 +53,8 @@ public:
|
||||
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("WinTunnelChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets_tunnel")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets_tunnel")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
virtual ~CSocketsTunnelChannelMgnt() = default;
|
||||
@@ -73,6 +70,11 @@ public:
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy.
|
||||
*/
|
||||
virtual void OnDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief Creates a tunnel endpoint (server side) and returns endpoint info.
|
||||
* @param[in] cfgStr Optional config string (TOML or connect string).
|
||||
@@ -87,17 +89,10 @@ public:
|
||||
*/
|
||||
sdv::IInterfaceAccess* Access(const sdv::u8string& cs) override;
|
||||
|
||||
/**
|
||||
* @brief Called by server tunnel when closing (bookkeeping).
|
||||
* @param[in] udsPath The UDS path for the server.
|
||||
* @param[in] ptr Pointer to the tunnel connection being closed.
|
||||
*/
|
||||
void OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr);
|
||||
|
||||
private:
|
||||
std::mutex m_udsMtx;
|
||||
std::map<std::string, std::shared_ptr<CWinTunnelConnection>> m_udsServers;
|
||||
std::set<std::string> m_udsServerClaimed;
|
||||
CWinTunnelConnectionWatchDog m_watchdog;
|
||||
std::map<const void*, std::shared_ptr<CWinTunnelConnection>> m_ptrConnections; ///< Retain shared_ptr for all connections
|
||||
mutable std::mutex m_ConnectionsMutex; ///< Protect concurrent access to m_ptrConnections
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT(CSocketsTunnelChannelMgnt)
|
||||
|
||||
@@ -24,26 +24,35 @@ CWinTunnelConnection::CWinTunnelConnection(
|
||||
// No additional initialization required; acts as a thin wrapper.
|
||||
}
|
||||
|
||||
bool CWinTunnelConnection::SendData(
|
||||
/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
CWinTunnelConnection::~CWinTunnelConnection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CWinTunnelConnection::SetWatchDogRemoveCallback(std::function<void(const void*)> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_WatchdogMtx);
|
||||
m_WatchdogRemoveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
bool CWinTunnelConnection::SendData( /*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
{
|
||||
|
||||
if (!m_Transport)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] SendData failed: transport is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build tunnel header buffer
|
||||
sdv::pointer<uint8_t> hdrBuf;
|
||||
hdrBuf.resize(sizeof(STunnelHeader));
|
||||
|
||||
STunnelHeader hdr{};
|
||||
hdr.uiChannelId = m_ChannelId; // Logical channel for this connection
|
||||
hdr.uiFlags = 0; // Reserved for future use
|
||||
hdr.uiChannelId = m_ChannelId;
|
||||
hdr.uiFlags = 0;
|
||||
|
||||
std::memcpy(hdrBuf.get(), &hdr, sizeof(STunnelHeader));
|
||||
|
||||
// Compose new sequence: [header] + original payload chunks
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqWithHdr;
|
||||
seqWithHdr.push_back(hdrBuf);
|
||||
for (auto& chunk : seqData)
|
||||
@@ -51,11 +60,13 @@ bool CWinTunnelConnection::SendData(
|
||||
seqWithHdr.push_back(chunk);
|
||||
}
|
||||
|
||||
bool result = m_Transport->SendData(seqWithHdr);
|
||||
if (!result) {
|
||||
const bool result = m_Transport->SendData(seqWithHdr);
|
||||
if (!result)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] SendData failed in underlying transport");
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver)
|
||||
@@ -70,7 +81,7 @@ bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
sdv::TInterfaceAccessPtr acc(pReceiver);
|
||||
m_pUpperReceiver = acc.GetInterface<sdv::ipc::IDataReceiveCallback>();
|
||||
m_pUpperRecv = acc.GetInterface<sdv::ipc::IDataReceiveCallback>();
|
||||
m_pUpperEvent = acc.GetInterface<sdv::ipc::IConnectEventCallback>();
|
||||
}
|
||||
|
||||
@@ -110,14 +121,8 @@ void CWinTunnelConnection::Disconnect()
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep upper callbacks on plain Disconnect(); they are released in DestroyObject().
|
||||
m_Transport->Disconnect();
|
||||
|
||||
// Clear upper-layer callbacks (thread-safe)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_pUpperReceiver = nullptr;
|
||||
m_pUpperEvent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t CWinTunnelConnection::RegisterStateEventCallback(
|
||||
@@ -156,55 +161,91 @@ sdv::ipc::EConnectState CWinTunnelConnection::GetConnectState() const
|
||||
|
||||
void CWinTunnelConnection::DestroyObject()
|
||||
{
|
||||
bool expected = false;
|
||||
if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop forwarding to upper layer before transport teardown.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_pUpperRecv = nullptr;
|
||||
m_pUpperEvent = nullptr;
|
||||
}
|
||||
|
||||
// Do not notify upper layer during destruction teardown to avoid races.
|
||||
SetConnectState(sdv::ipc::EConnectState::terminating);
|
||||
|
||||
Disconnect();
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_Transport.reset();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_Transport.reset();
|
||||
}
|
||||
|
||||
std::function<void(const void*)> removeCallback;
|
||||
{
|
||||
std::lock_guard<std::mutex> watchdogLock(m_WatchdogMtx);
|
||||
removeCallback = std::move(m_WatchdogRemoveCallback);
|
||||
}
|
||||
if (removeCallback)
|
||||
{
|
||||
removeCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CWinTunnelConnection::ReceiveData(
|
||||
/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
void CWinTunnelConnection::ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
{
|
||||
// Expect at least one chunk (the tunnel header)
|
||||
|
||||
if (seqData.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] ReceiveData: empty sequence");
|
||||
return; // nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& hdrChunk = seqData[0];
|
||||
if (hdrChunk.size() < sizeof(STunnelHeader))
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] ReceiveData: invalid tunnel header size");
|
||||
// Invalid tunnel frame; drop it for now (could set communication_error)
|
||||
return;
|
||||
}
|
||||
|
||||
STunnelHeader hdr{};
|
||||
std::memcpy(&hdr, hdrChunk.get(), sizeof(STunnelHeader));
|
||||
|
||||
// Future demux point:
|
||||
// if (hdr.uiChannelId != m_ChannelId) { ... }
|
||||
|
||||
// TODO: use channelId for multiplexing later
|
||||
|
||||
// Build payload-only sequence: drop header chunk, keep others
|
||||
sdv::sequence<sdv::pointer<uint8_t>> payload;
|
||||
for (size_t i = 1; i < seqData.size(); ++i)
|
||||
{
|
||||
payload.push_back(seqData[i]);
|
||||
}
|
||||
|
||||
if (m_pUpperReceiver)
|
||||
sdv::ipc::IDataReceiveCallback* upper = nullptr;
|
||||
{
|
||||
try {
|
||||
m_pUpperReceiver->ReceiveData(payload); // header stripped
|
||||
} catch (...) {
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver's ReceiveData");
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
upper = m_pUpperRecv;
|
||||
}
|
||||
|
||||
if (upper)
|
||||
{
|
||||
try
|
||||
{
|
||||
upper->ReceiveData(payload);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver callback");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state)
|
||||
{
|
||||
sdv::ipc::IConnectEventCallback* upper = nullptr;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
upper = m_pUpperEvent;
|
||||
@@ -218,8 +259,7 @@ void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback's SetConnectState");
|
||||
// Never let user callback crash the transport.
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
#include <WinSock2.h>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
#include "../sdv_services/uds_win_sockets/connection.h" // existing AF_UNIX transport: CWinsockConnection
|
||||
|
||||
@@ -69,7 +71,7 @@ public:
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
virtual ~CWinTunnelConnection() = default;
|
||||
virtual ~CWinTunnelConnection();
|
||||
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend)
|
||||
@@ -153,15 +155,19 @@ public:
|
||||
// Helpers
|
||||
void SetChannelId(uint16_t channelId) { m_ChannelId = channelId; }
|
||||
uint16_t GetChannelId() const noexcept { return m_ChannelId; }
|
||||
void SetWatchDogRemoveCallback(std::function<void(const void*)> callback);
|
||||
|
||||
private:
|
||||
std::shared_ptr<CWinsockConnection> m_Transport; ///< shared physical tunnel port
|
||||
uint16_t m_ChannelId{0}; ///< default logical channel id
|
||||
|
||||
// Upper layer callbacks (original VAPI receiver)
|
||||
sdv::ipc::IDataReceiveCallback* m_pUpperReceiver{nullptr};
|
||||
sdv::ipc::IDataReceiveCallback* m_pUpperRecv{nullptr};
|
||||
sdv::ipc::IConnectEventCallback* m_pUpperEvent{nullptr};
|
||||
mutable std::mutex m_CallbackMtx;
|
||||
std::mutex m_WatchdogMtx;
|
||||
std::function<void(const void*)> m_WatchdogRemoveCallback;
|
||||
std::atomic<bool> m_DestroyObjectCalled{false};
|
||||
};
|
||||
|
||||
#endif // UDS_WIN_TUNNEL_CONNECTION_H
|
||||
|
||||
58
sdv_services/uds_win_tunnel/watchdog.cpp
Normal file
58
sdv_services/uds_win_tunnel/watchdog.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
void CWinTunnelConnectionWatchDog::AddConnectionImpl(const std::shared_ptr<void>& connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Connections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
void CWinTunnelConnectionWatchDog::RemoveConnection(const void* connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> removed;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
auto it = m_Connections.find(connection);
|
||||
if (it == m_Connections.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removed = std::move(it->second);
|
||||
m_Connections.erase(it);
|
||||
}
|
||||
|
||||
removed.reset();
|
||||
}
|
||||
|
||||
void CWinTunnelConnectionWatchDog::Clear()
|
||||
{
|
||||
std::map<const void*, std::shared_ptr<void>> localConnections;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
localConnections.swap(m_Connections);
|
||||
}
|
||||
|
||||
localConnections.clear();
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
47
sdv_services/uds_win_tunnel/watchdog.h
Normal file
47
sdv_services/uds_win_tunnel/watchdog.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef UDS_WIN_TUNNEL_WATCHDOG_H
|
||||
#define UDS_WIN_TUNNEL_WATCHDOG_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class CWinTunnelConnectionWatchDog
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
void AddConnection(const std::shared_ptr<T>& connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddConnectionImpl(std::static_pointer_cast<void>(connection));
|
||||
}
|
||||
|
||||
void RemoveConnection(const void* connection);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void AddConnectionImpl(const std::shared_ptr<void>& connection);
|
||||
|
||||
std::mutex m_Mutex;
|
||||
std::map<const void*, std::shared_ptr<void>> m_Connections;
|
||||
};
|
||||
|
||||
#endif // UDS_WIN_TUNNEL_WATCHDOG_H
|
||||
#endif // _WIN32
|
||||
Reference in New Issue
Block a user