Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#pragma once

#include <string>
#include <opendaq/utils/thread_ex.h>
#include <unordered_set>

Expand All @@ -36,7 +37,14 @@ BEGIN_NAMESPACE_OPENDAQ_OPCUA
class OpcUaServer final : public daq::utils::ThreadEx
{
public:
using OnClientConnectedCallback = std::function<void(const std::string& clientId)>;
struct ClientConnectionInfo
{
std::string clientId;
std::string address;
std::string hostname;
};

using OnClientConnectedCallback = std::function<void(const ClientConnectionInfo& info)>;
using OnClientDisconnectedCallback = std::function<void(const std::string& clientId)>;
using OnAllowBrowsingNodeCallback = UA_Boolean (*)(UA_Server* server,
UA_AccessControl* ac,
Expand Down
5 changes: 3 additions & 2 deletions shared/libraries/opcua/opcuaserver/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ set(SOURCE_FILES ${ALL_SOURCES})
add_library(${MODULE_NAME} STATIC ${ALL_SOURCES})
add_library(${OPENDAQ_SDK_TARGET_NAMESPACE}::${MODULE_NAME} ALIAS ${MODULE_NAME})

target_include_directories(${MODULE_NAME} PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>
$<INSTALL_INTERFACE:include/${MODULE_NAME}>
target_include_directories(${MODULE_NAME} PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>
$<INSTALL_INTERFACE:include/${MODULE_NAME}>
PRIVATE $<BUILD_INTERFACE:${open62541_SOURCE_DIR}/src>
)

target_link_libraries(${MODULE_NAME} PUBLIC ${OPENDAQ_SDK_TARGET_NAMESPACE}::opcuashared
Expand Down
42 changes: 41 additions & 1 deletion shared/libraries/opcua/opcuaserver/src/opcuaserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
#include <cassert>
#include <coreobjects/authentication_provider_factory.h>
#include <coreobjects/exceptions.h>
#include "server/ua_server_internal.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

BEGIN_NAMESPACE_OPENDAQ_OPCUA

Expand Down Expand Up @@ -620,7 +628,39 @@ UA_StatusCode OpcUaServer::activateSession(UA_Server* server,
{
serverInstance->createSession(*sessionId, authorizedUser, sessionContext);
if (serverInstance->clientConnectedHandler)
serverInstance->clientConnectedHandler(OpcUaNodeId::getIdentifier(*sessionId));
{
OpcUaServer::ClientConnectionInfo info;
info.clientId = OpcUaNodeId::getIdentifier(*sessionId);

// UA_Server_getSessionById requires the server mutex to be held,
// but activateSession is called with it released — reacquire briefly
// just to read the socket fd, then drop it before the blocking DNS call.
UA_SOCKET sockfd = UA_INVALID_SOCKET;
UA_LOCK(&server->serviceMutex);
UA_Session* session = UA_Server_getSessionById(server, sessionId);
if (session && session->header.channel && session->header.channel->connection)
sockfd = session->header.channel->connection->sockfd;
UA_UNLOCK(&server->serviceMutex);

if (sockfd != UA_INVALID_SOCKET)
{
struct sockaddr_storage addr{};
socklen_t addrLen = sizeof(addr);
if (getpeername(sockfd, reinterpret_cast<struct sockaddr*>(&addr), &addrLen) == 0)
{
char ipBuf[NI_MAXHOST] = {};
char hostBuf[NI_MAXHOST] = {};
getnameinfo(reinterpret_cast<struct sockaddr*>(&addr), addrLen,
ipBuf, sizeof(ipBuf), nullptr, 0, NI_NUMERICHOST);
getnameinfo(reinterpret_cast<struct sockaddr*>(&addr), addrLen,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getnameinfo without NI_NUMERICHOST does a blocking reverse DNS lookup. This runs on the OPC UA server's single processing thread, so while DNS is resolving, the server cannot process anything else (no responses to other clients, no new connections). In environments where the connecting IP has no PTR record or DNS is slow, this can stall the server for up to seconds. Additionally, if there is a buggy client in the network that constantly connects to and disconnects from the server, it may cause problems for other clients as well.
@JakaMohorko What do you think?

hostBuf, sizeof(hostBuf), nullptr, 0, 0);
info.address = ipBuf;
info.hostname = hostBuf;
}
}

serverInstance->clientConnectedHandler(info);
}
}

return status;
Expand Down
4 changes: 2 additions & 2 deletions shared/libraries/opcua/opcuaserver/tests/test_opcuaserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,10 @@ TEST_F(OpcUaServerTest, ClientConnectDisconnectCallbacks)
bool clientConnected{false};
bool clientDisconnected{false};
server.setClientConnectedHandler(
[&clientId, &clientConnected](const std::string& id)
[&clientId, &clientConnected](const OpcUaServer::ClientConnectionInfo& info)
{
clientConnected = true;
clientId = id;
clientId = info.clientId;
}
);
server.setClientDisconnectedHandler(
Expand Down
8 changes: 4 additions & 4 deletions shared/libraries/opcuatms/opcuatms_server/src/tms_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,18 @@ void TmsServer::start()
server->setPort(opcUaPort);
server->setAuthenticationProvider(context.getAuthenticationProvider());
server->setClientConnectedHandler(
[this](const std::string& clientId)
[this](const OpcUaServer::ClientConnectionInfo& connInfo)
{
const auto loggerComponent = context.getLogger().getOrAddComponent("TmsServer");
LOG_I("New client connected, ID: {}", clientId);
LOG_I("New client connected, ID: {}, address: {}", connInfo.clientId, connInfo.address);
SizeT clientNumber = 0;
if (device.assigned() && !device.isRemoved())
{
device.getInfo().asPtr<IDeviceInfoInternal>(true).addConnectedClient(
&clientNumber,
ConnectedClientInfo("", ProtocolType::Configuration, "OpenDAQOPCUA", "", ""));
ConnectedClientInfo(connInfo.address, ProtocolType::Configuration, "OpenDAQOPCUA", "Control", connInfo.hostname));
}
registeredClientIds.insert({clientId, clientNumber});
registeredClientIds.insert({connInfo.clientId, clientNumber});
}
);
server->setClientDisconnectedHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <opendaq/sync_component_private_ptr.h>
#include <coreobjects/property_factory.h>
#include <coreobjects/property_object_class_factory.h>
#include <opendaq/device_info_factory.h>
#include <opendaq/connected_client_info_ptr.h>

using namespace daq;
using namespace daq::opcua;
Expand Down Expand Up @@ -635,3 +637,30 @@ TEST_F(TmsIntegrationTest, GetDaqServers)
ASSERT_NO_THROW(servers = clientDevice.getServers());
ASSERT_EQ(servers.getCount(), device.getServers().getCount());
}

TEST_F(TmsIntegrationTest, ConnectedClientInfoHasAddressAndHostname)
{
InstancePtr device = createDevice();

TmsServer tmsServer(device);
tmsServer.start();

TmsClient tmsClient(device.getContext(), nullptr, OPC_URL);
ASSERT_NO_THROW(tmsClient.connect());

const auto clients = device.getInfo().getConnectedClientsInfo();

ConnectedClientInfoPtr opcuaClient;
for (const auto& client : clients)
{
if (client.getProtocolName() == "OpenDAQOPCUA")
{
opcuaClient = client;
break;
}
}

ASSERT_TRUE(opcuaClient.assigned()) << "No connected client with protocolName 'OpenDAQOPCUA' found";
ASSERT_FALSE(opcuaClient.getAddress().getLength() == 0) << "Client address must not be empty";
ASSERT_FALSE(opcuaClient.getHostName().getLength() == 0) << "Client hostname must not be empty";
}
Loading