mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
357 lines
11 KiB
C++
357 lines
11 KiB
C++
|
|
#include "channel_mgnt.h"
|
||
|
|
#include "connection.h"
|
||
|
|
#include "../../global/base64.h"
|
||
|
|
#include <support/toml.h>
|
||
|
|
#include <interfaces/process.h>
|
||
|
|
#include <future>
|
||
|
|
|
||
|
|
#pragma push_macro("interface")
|
||
|
|
#undef interface
|
||
|
|
#pragma push_macro("GetObject")
|
||
|
|
#undef GetObject
|
||
|
|
|
||
|
|
#ifndef NOMINMAX
|
||
|
|
#define NOMINMAX
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <WinSock2.h>
|
||
|
|
#include <Windows.h>
|
||
|
|
#include <array>
|
||
|
|
|
||
|
|
// Resolve conflict
|
||
|
|
#pragma pop_macro("GetObject")
|
||
|
|
#pragma pop_macro("interface")
|
||
|
|
#ifdef GetClassInfo
|
||
|
|
#undef GetClassInfo
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Define for the connection string
|
||
|
|
*/
|
||
|
|
#define SHARED_SOCKET "SHARED_SOCKET"
|
||
|
|
|
||
|
|
void CSocketsChannelMgnt::Initialize(const sdv::u8string& /*ssObjectConfig*/)
|
||
|
|
{
|
||
|
|
if (m_eObjectStatus != sdv::EObjectStatus::initialization_pending)
|
||
|
|
m_eObjectStatus = sdv::EObjectStatus::initialization_failure;
|
||
|
|
else
|
||
|
|
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||
|
|
}
|
||
|
|
|
||
|
|
sdv::EObjectStatus CSocketsChannelMgnt::GetStatus() const
|
||
|
|
{
|
||
|
|
return m_eObjectStatus;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CSocketsChannelMgnt::SetOperationMode(sdv::EOperationMode eMode)
|
||
|
|
{
|
||
|
|
switch (eMode)
|
||
|
|
{
|
||
|
|
case sdv::EOperationMode::configuring:
|
||
|
|
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||
|
|
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||
|
|
break;
|
||
|
|
case sdv::EOperationMode::running:
|
||
|
|
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||
|
|
m_eObjectStatus = sdv::EObjectStatus::running;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CSocketsChannelMgnt::Shutdown()
|
||
|
|
{
|
||
|
|
//m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||
|
|
//...
|
||
|
|
m_eObjectStatus = sdv::EObjectStatus::destruction_pending;
|
||
|
|
}
|
||
|
|
|
||
|
|
sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8string& /*ssChannelConfig*/)
|
||
|
|
{
|
||
|
|
StartUpWinSock();
|
||
|
|
addrinfo hints;
|
||
|
|
ZeroMemory(&hints, sizeof(hints));
|
||
|
|
hints.ai_family = AF_INET;
|
||
|
|
hints.ai_socktype = SOCK_STREAM;
|
||
|
|
hints.ai_protocol = IPPROTO_TCP;
|
||
|
|
hints.ai_flags = AI_PASSIVE;
|
||
|
|
|
||
|
|
SOCKET listenSocket = CreateSocket(hints);
|
||
|
|
|
||
|
|
if (listenSocket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("CreateSocket failed (could not create endpoint)");
|
||
|
|
sdv::ipc::SChannelEndpoint connectionEndpoint{};
|
||
|
|
return connectionEndpoint;
|
||
|
|
}
|
||
|
|
|
||
|
|
CConnection* pRemoteIPCConnection = new CConnection(listenSocket, true);
|
||
|
|
|
||
|
|
uint16_t port = GetPort(listenSocket);
|
||
|
|
std::string ipcCompleteConfig = "localhost";
|
||
|
|
ipcCompleteConfig.append(";");
|
||
|
|
ipcCompleteConfig.append(std::to_string(port));
|
||
|
|
SDV_LOG_INFO("IPC command param: '", ipcCompleteConfig, "'");
|
||
|
|
|
||
|
|
sdv::ipc::SChannelEndpoint connectionEndpoint{};
|
||
|
|
connectionEndpoint.pConnection = static_cast<IInterfaceAccess*>(pRemoteIPCConnection);
|
||
|
|
connectionEndpoint.ssConnectString = ipcCompleteConfig;
|
||
|
|
|
||
|
|
return connectionEndpoint;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
sdv::IInterfaceAccess* CSocketsChannelMgnt::Access(const sdv::u8string& ssConnectString)
|
||
|
|
{
|
||
|
|
bool sharedSocketRequired = ssConnectString.find(SHARED_SOCKET) != std::string::npos ? true : false;
|
||
|
|
|
||
|
|
if (sharedSocketRequired)
|
||
|
|
{
|
||
|
|
std::string base64Data(ssConnectString);
|
||
|
|
const std::string ext(SHARED_SOCKET);
|
||
|
|
base64Data = base64Data.substr(0, base64Data.size() - ext.size());
|
||
|
|
|
||
|
|
WSAPROTOCOL_INFO socketInfo = DecodeBase64<WSAPROTOCOL_INFO>(base64Data);
|
||
|
|
|
||
|
|
SOCKET sharedSocket = WSASocket(0, 0, 0, &socketInfo, 0, 0);
|
||
|
|
std::string success = "Socket sharing success";
|
||
|
|
if (sharedSocket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
success = "Socket sharing did not work!";
|
||
|
|
}
|
||
|
|
|
||
|
|
return static_cast<IInterfaceAccess*>(new CConnection(sharedSocket, false));
|
||
|
|
}
|
||
|
|
|
||
|
|
StartUpWinSock();
|
||
|
|
addrinfo hints;
|
||
|
|
ZeroMemory(&hints, sizeof(hints));
|
||
|
|
hints.ai_family = AF_INET;
|
||
|
|
hints.ai_socktype = SOCK_STREAM;
|
||
|
|
hints.ai_protocol = IPPROTO_TCP;
|
||
|
|
hints.ai_flags = AI_PASSIVE;
|
||
|
|
|
||
|
|
std::string host{"localhost"};
|
||
|
|
std::string param{ssConnectString};
|
||
|
|
auto it = param.find(";");
|
||
|
|
if (it != std::string::npos)
|
||
|
|
{
|
||
|
|
host = param.substr(0, it);
|
||
|
|
param = param.substr(it + 1, param.size() - it - 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
SOCKET socket = CreateAndConnectToExistingSocket(hints, host.c_str(), param.c_str());
|
||
|
|
if (socket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("Could not create my socket and connect to the existing socket.");
|
||
|
|
}
|
||
|
|
|
||
|
|
return static_cast<IInterfaceAccess*>(new CConnection(socket, false));
|
||
|
|
}
|
||
|
|
|
||
|
|
uint16_t CSocketsChannelMgnt::GetPort(SOCKET socket) const
|
||
|
|
{
|
||
|
|
sockaddr_in sockAddr;
|
||
|
|
sockAddr.sin_port = 0;
|
||
|
|
int nameLength = sizeof(sockAddr);
|
||
|
|
getsockname(socket, reinterpret_cast<sockaddr*>(&sockAddr), &nameLength);
|
||
|
|
return ntohs(sockAddr.sin_port);
|
||
|
|
}
|
||
|
|
|
||
|
|
SOCKET CSocketsChannelMgnt::CreateAndConnectToExistingSocket(const addrinfo& hints,
|
||
|
|
const char* hostName,
|
||
|
|
const char* portName)
|
||
|
|
{
|
||
|
|
SOCKET invalidSocket{INVALID_SOCKET};
|
||
|
|
|
||
|
|
// Resolve the server address and port
|
||
|
|
CAddrInfo result;
|
||
|
|
int error = getaddrinfo(hostName, portName, &hints, &result.AddressInfo);
|
||
|
|
if (error != 0)
|
||
|
|
{
|
||
|
|
|
||
|
|
SDV_LOG_ERROR("getaddrinfo failed with error: ",
|
||
|
|
std::to_string(error),
|
||
|
|
" host: ",
|
||
|
|
hostName,
|
||
|
|
" port: ",
|
||
|
|
portName);
|
||
|
|
return invalidSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
SOCKET connectSocket{INVALID_SOCKET};
|
||
|
|
// Attempt to connect to an address until one succeeds
|
||
|
|
for (addrinfo* ptr = result.AddressInfo; ptr != NULL; ptr = ptr->ai_next)
|
||
|
|
{
|
||
|
|
// Create a SOCKET for connecting to server
|
||
|
|
connectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
||
|
|
if (connectSocket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("socket failed with error: ", std::to_string(error));
|
||
|
|
return invalidSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Connect to server.
|
||
|
|
error = connect(connectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
|
||
|
|
if (error == SOCKET_ERROR)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("connect failed with error: ", std::to_string(error));
|
||
|
|
closesocket(connectSocket);
|
||
|
|
connectSocket = INVALID_SOCKET;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return connectSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
SOCKET CSocketsChannelMgnt::CreateSocket(const addrinfo& hints)
|
||
|
|
{
|
||
|
|
static constexpr const char* defaultPort_0{"0"}; // In case a defined port is required
|
||
|
|
SOCKET invalidSocket{INVALID_SOCKET};
|
||
|
|
CAddrInfo result;
|
||
|
|
int error = getaddrinfo(NULL, defaultPort_0, &hints, &result.AddressInfo);
|
||
|
|
if (error != 0)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("getaddrinfo failed with error: ", std::to_string(error));
|
||
|
|
return invalidSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
SOCKET connectSocket{INVALID_SOCKET};
|
||
|
|
connectSocket =
|
||
|
|
socket(result.AddressInfo->ai_family, result.AddressInfo->ai_socktype, result.AddressInfo->ai_protocol);
|
||
|
|
if (connectSocket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("error at socket(): ", std::to_string(error));
|
||
|
|
return invalidSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
error = bind(connectSocket, result.AddressInfo->ai_addr, (int)result.AddressInfo->ai_addrlen);
|
||
|
|
if (error == SOCKET_ERROR)
|
||
|
|
{
|
||
|
|
closesocket(connectSocket);
|
||
|
|
SDV_LOG_ERROR("bind failed with error: ", std::to_string(error));
|
||
|
|
return invalidSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (listen(connectSocket, SOMAXCONN) == SOCKET_ERROR)
|
||
|
|
{
|
||
|
|
closesocket(connectSocket);
|
||
|
|
SDV_LOG_ERROR("listen failed with error: ", std::to_string(WSAGetLastError()));
|
||
|
|
return invalidSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Change the socket mode on the listening socket from blocking to
|
||
|
|
// non-block so the application will not block waiting for requests
|
||
|
|
u_long NonBlock = 1;
|
||
|
|
if (ioctlsocket(connectSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)
|
||
|
|
{
|
||
|
|
closesocket(connectSocket);
|
||
|
|
SDV_LOG_ERROR("ioctlsocket failed with error: ", std::to_string(WSAGetLastError()));
|
||
|
|
return invalidSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
return connectSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
SOCKET CSocketsChannelMgnt::CreateAndConnectToSocket(const addrinfo& hints, const char* defaultHost, const char* defaultPort)
|
||
|
|
{
|
||
|
|
SOCKET ConnectSocket{ INVALID_SOCKET };
|
||
|
|
|
||
|
|
// Resolve the server address and port
|
||
|
|
CAddrInfo result;
|
||
|
|
if (getaddrinfo(defaultHost, defaultPort, &hints, &result.AddressInfo) != 0)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("getaddrinfo() failed: ", std::to_string(WSAGetLastError()));
|
||
|
|
return ConnectSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Attempt to connect to an address until one succeeds
|
||
|
|
for (addrinfo* ptr = result.AddressInfo; ptr != NULL; ptr = ptr->ai_next)
|
||
|
|
{
|
||
|
|
// Create a SOCKET for connecting to server
|
||
|
|
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
||
|
|
if (ConnectSocket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("creating SOCKET for connecting failed: ", std::to_string(WSAGetLastError()));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Connect to server.
|
||
|
|
if (connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen) == SOCKET_ERROR)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("connect to servcer failed: ", std::to_string(WSAGetLastError()));
|
||
|
|
closesocket(ConnectSocket);
|
||
|
|
ConnectSocket = INVALID_SOCKET;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ConnectSocket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("Failed to create valid sockaet in CreateAndConnectToSocket()");
|
||
|
|
}
|
||
|
|
|
||
|
|
return ConnectSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
SOCKET CSocketsChannelMgnt::Listen(const addrinfo& hints, uint32_t port)
|
||
|
|
{
|
||
|
|
SOCKET listenSocket = INVALID_SOCKET;
|
||
|
|
CAddrInfo result;
|
||
|
|
int error = getaddrinfo(NULL, std::to_string(port).c_str(), &hints, &result.AddressInfo);
|
||
|
|
if (error != 0)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("getaddrinfo() failed: ", std::to_string(WSAGetLastError()));
|
||
|
|
return listenSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
listenSocket = socket(result.AddressInfo->ai_family, result.AddressInfo->ai_socktype, result.AddressInfo->ai_protocol);
|
||
|
|
if (listenSocket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("2creating SOCKET for connecting failed: failed: ", std::to_string(WSAGetLastError()));
|
||
|
|
return listenSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
error = bind(listenSocket, result.AddressInfo->ai_addr, (int)result.AddressInfo->ai_addrlen);
|
||
|
|
if (error == SOCKET_ERROR)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("bind failed with error: ", std::to_string(WSAGetLastError()));
|
||
|
|
closesocket(listenSocket);
|
||
|
|
listenSocket = INVALID_SOCKET;
|
||
|
|
return listenSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR)
|
||
|
|
{
|
||
|
|
SDV_LOG_ERROR("listen to SOCKET failed: ", std::to_string(WSAGetLastError()));
|
||
|
|
closesocket(listenSocket);
|
||
|
|
listenSocket = INVALID_SOCKET;
|
||
|
|
return listenSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
return listenSocket;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
SocketConnection CSocketsChannelMgnt::CreateConnectedSocketPair()
|
||
|
|
{
|
||
|
|
SocketConnection connection;
|
||
|
|
|
||
|
|
uint32_t port = 0;
|
||
|
|
SOCKET listenSocket = Listen(getHints, port);
|
||
|
|
|
||
|
|
uint16_t portOfListenSocket = GetPort(listenSocket);
|
||
|
|
auto future = std::async([listenSocket]() { return accept(listenSocket, NULL, NULL); });
|
||
|
|
|
||
|
|
connection.From = CreateAndConnectToSocket(getHints, "localhost", std::to_string(portOfListenSocket).c_str());
|
||
|
|
|
||
|
|
// Future::Get has to be called after the CreateAndConnect-Function
|
||
|
|
connection.To = future.get();
|
||
|
|
return connection;
|
||
|
|
}
|