feat(discovery): mark ServerOnNetwork entries for announcement

The mDNS drivers announce every ServerOnNetwork entry that differs from
their cache of received records. Whether an entry is announced therefore
depends on its content instead of its origin. An entry that was received
first and then added locally with the same content is never announced.
And every registered server is announced, without a way to turn that
off.

This commit adds the 0:announce parameter to
UA_Server_registerServerOnNetwork and the announceRegisteredServers
config option for the entries of registered servers. The
ServerOnNetwork notifications carry the flag as 0:announce. Entries
without the parameter, such as the received ones, are not marked. The
drivers do not use the flag yet.
This commit is contained in:
Bastian Engel
2026-10-28 11:04:44 +01:00
committed by Julius Pfrommer
parent d359379935
commit d0994d2adb
5 changed files with 112 additions and 18 deletions
+3
View File
@@ -614,6 +614,9 @@ typedef uint64_t UA_ApplicationNotificationType;
* 0:ttl [UInt32]
* Time-to-live of DNS information. Zero means infinite
* (if the server is not currently getting removed).
* 0:announce [Boolean]
* Announce the entry on the network (for example over mDNS). False for
* removed entries.
* 0:server-added [Boolean]
* They entry was added.
* 0:server-updated [Boolean]
+15 -6
View File
@@ -1932,15 +1932,18 @@ UA_Server_findServersOnNetwork(UA_Server *server, UA_String endpointUrl,
size_t *outServersSize,
UA_ServerOnNetwork **outServers);
/* Register a remote server. If the server name was previously known, the
* existing entry gets updated. The parameter kv-map can be extended with
* additional parameters in the future. Currently supported are:
/* Register a received or local server. If the server name was previously
* known, the existing entry gets updated. The parameter kv-map can be extended
* with additional parameters in the future. Currently supported are:
*
* 0:remote-address [String]
* IP-address or other host identifier from which the information
* was received.
* 0:ttl [UInt32]
* Time-to-live of DNS information. Zero means infinite. */
* Time-to-live of DNS information. Zero means infinite.
* 0:announce [Boolean]
* Announce the entry on the network, for example by an attached mDNS
* driver. Default is false. */
UA_StatusCode UA_EXPORT UA_THREADSAFE
UA_Server_registerServerOnNetwork(UA_Server *server,
const UA_ServerOnNetwork *son,
@@ -2592,10 +2595,16 @@ struct UA_ServerConfig {
* implement a Discovery Server that detects other servers on the
* network. If registeredServersEnabled is also set, every registered
* server gets a ServerOnNetwork entry under its mDNS name (the
* mdnsServerName from RegisterServer2 or else the first ServerName). An
* attached mDNS driver announces these entries on the network. */
* mdnsServerName from RegisterServer2 or else the first ServerName). See
* announceRegisteredServers for the announcement of these entries. */
UA_Boolean serversOnNetworkEnabled;
/* Announce the ServerOnNetwork entries of locally registered servers on the
* network. The entries are created and returned by FindServersOnNetwork
* also without this. An attached mDNS driver with the 0:announce parameter
* announces them. */
UA_Boolean announceRegisteredServers;
/* Subscriptions
* ~~~~~~~~~~~~~ */
UA_Boolean subscriptionsEnabled;
+2
View File
@@ -1175,6 +1175,8 @@ parseJSONServerConfig(UA_ServerConfig *config, UA_ByteString json_config) {
retval = UInt32Field_parseJson(&ctx, &config->registeredServerCleanupTimeout, NULL);
else if(strcmp(field, "serversOnNetworkEnabled") == 0)
retval = BooleanField_parseJson(&ctx, &config->serversOnNetworkEnabled, NULL);
else if(strcmp(field, "announceRegisteredServers") == 0)
retval = BooleanField_parseJson(&ctx, &config->announceRegisteredServers, NULL);
#ifdef UA_ENABLE_SUBSCRIPTIONS
else if(strcmp(field, "subscriptionsEnabled") == 0)
+19 -12
View File
@@ -226,13 +226,14 @@ UA_Server_findServers(UA_Server *server, UA_String endpointUrl,
static void
notifyServerOnNetwork(UA_Server *server,
const UA_ServerOnNetwork *son,
const UA_KeyValueMap params,
const UA_KeyValueMap params, UA_Boolean announce,
UA_Boolean added, UA_Boolean updated,
UA_Boolean removed) {
UA_STATIC_THREAD_LOCAL UA_KeyValuePair kvp[6] = {
UA_STATIC_THREAD_LOCAL UA_KeyValuePair kvp[7] = {
{{0, UA_STRING_STATIC("server-on-network")}, {0}},
{{0, UA_STRING_STATIC("remote-address")}, {0}},
{{0, UA_STRING_STATIC("ttl")}, {0}},
{{0, UA_STRING_STATIC("announce")}, {0}},
{{0, UA_STRING_STATIC("server-added")}, {0}},
{{0, UA_STRING_STATIC("server-updated")}, {0}},
{{0, UA_STRING_STATIC("server-removed")}, {0}},
@@ -258,10 +259,11 @@ notifyServerOnNetwork(UA_Server *server,
&UA_TYPES[UA_TYPES_SERVERONNETWORK]);
UA_Variant_setScalar(&kvp[1].value, &remote, &UA_TYPES[UA_TYPES_STRING]);
UA_Variant_setScalar(&kvp[2].value, &ttl, &UA_TYPES[UA_TYPES_UINT32]);
UA_Variant_setScalar(&kvp[3].value, &added, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_Variant_setScalar(&kvp[4].value, &updated, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_Variant_setScalar(&kvp[5].value, &removed, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_KeyValueMap kvm = {6, kvp};
UA_Variant_setScalar(&kvp[3].value, &announce, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_Variant_setScalar(&kvp[4].value, &added, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_Variant_setScalar(&kvp[5].value, &updated, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_Variant_setScalar(&kvp[6].value, &removed, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_KeyValueMap kvm = {7, kvp};
notifyApplication(server,
UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY_SERVERONNETWORK,
@@ -449,7 +451,7 @@ findServerOnNetworkIndex(UA_Server *server,
static UA_StatusCode
addServerOnNetwork(UA_Server *server, const UA_ServerOnNetwork *newSon,
const UA_KeyValueMap params) {
const UA_KeyValueMap params, UA_Boolean announce) {
UA_LOCK_ASSERT(&server->serviceMutex);
if(!newSon)
@@ -476,7 +478,7 @@ addServerOnNetwork(UA_Server *server, const UA_ServerOnNetwork *newSon,
*son = tmp;
/* Notify the application about an updated entry */
notifyServerOnNetwork(server, son, params, false, true, false);
notifyServerOnNetwork(server, son, params, announce, false, true, false);
/* All new RecordIds in increasing order */
resetDiscoveryResetIds(server);
@@ -496,7 +498,7 @@ addServerOnNetwork(UA_Server *server, const UA_ServerOnNetwork *newSon,
son->recordId = ++server->serversOnNetworkRecordCounter;
/* Notify the application about an added entry */
notifyServerOnNetwork(server, son, params, true, false, false);
notifyServerOnNetwork(server, son, params, announce, true, false, false);
return UA_STATUSCODE_GOOD;
}
@@ -516,7 +518,7 @@ removeServerOnNetwork(UA_Server *server, const UA_String serverName) {
/* Notify the application about removed entry */
notifyServerOnNetwork(server, son, UA_KEYVALUEMAP_NULL,
false, false, true);
false, false, false, true);
/* Move the last entry in the current position */
UA_ServerOnNetwork_clear(son);
@@ -544,8 +546,12 @@ UA_StatusCode
UA_Server_registerServerOnNetwork(UA_Server *server,
const UA_ServerOnNetwork *newSon,
const UA_KeyValueMap params) {
const UA_Boolean *announce = (const UA_Boolean*)
UA_KeyValueMap_getScalar(&params, UA_QUALIFIEDNAME(0, "announce"),
&UA_TYPES[UA_TYPES_BOOLEAN]);
lockServer(server);
UA_StatusCode res = addServerOnNetwork(server, newSon, params);
UA_StatusCode res = addServerOnNetwork(server, newSon, params,
announce ? *announce : false);
unlockServer(server);
return res;
}
@@ -1287,7 +1293,8 @@ process_RegisterServer(UA_Server *server, UA_Session *session,
return UA_STATUSCODE_GOOD;
}
res = addServerOnNetwork(server, &son, UA_KEYVALUEMAP_NULL);
res = addServerOnNetwork(server, &son, UA_KEYVALUEMAP_NULL,
sc->announceRegisteredServers);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING_SESSION(sc->logging, session,
"Could not add the ServerOnNetwork entry "
@@ -520,6 +520,77 @@ START_TEST(register_server_empty_mdns_name_uses_server_name) {
}
END_TEST
/* Notifications about ServerOnNetwork entries and the last 0:announce flag */
static size_t sonNotifications;
static UA_Boolean lastAnnounce;
static void
announceNotificationCallback(UA_Server *server,
UA_ApplicationNotificationType type,
const UA_KeyValueMap payload) {
(void)server;
if(type != UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY_SERVERONNETWORK)
return;
const UA_Boolean *announce = (const UA_Boolean*)
UA_KeyValueMap_getScalar(&payload, UA_QUALIFIEDNAME(0, "announce"),
&UA_TYPES[UA_TYPES_BOOLEAN]);
ck_assert_ptr_ne(announce, NULL);
lastAnnounce = *announce;
sonNotifications++;
}
/* The entry of a registered server is created in either case. It is announced
* only with announceRegisteredServers. */
START_TEST(register_server_announce_follows_config) {
UA_Boolean announceRegistered = (_i == 1);
UA_Server *server = newRegistrationServer();
server->config.announceRegisteredServers = announceRegistered;
server->config.discoveryNotificationCallback = announceNotificationCallback;
sonNotifications = 0;
registerWithMdnsName(server, NULL);
ck_assert_uint_eq(sonNotifications, 1);
ck_assert(lastAnnounce == announceRegistered);
UA_Server_delete(server);
}
END_TEST
/* Entries from the public API are announced only on request. Received entries
* are added without the parameter. */
START_TEST(register_server_on_network_announce_param) {
UA_Server *server = newRegistrationServer();
server->config.discoveryNotificationCallback = announceNotificationCallback;
sonNotifications = 0;
UA_String caps = UA_STRING("NA");
UA_ServerOnNetwork son;
UA_ServerOnNetwork_init(&son);
son.serverName = UA_STRING("local-entry");
son.discoveryUrl = UA_STRING(REGISTERED_DISCOVERY_URL);
son.serverCapabilitiesSize = 1;
son.serverCapabilities = ∩︀
ck_assert_uint_eq(UA_Server_registerServerOnNetwork(server, &son,
UA_KEYVALUEMAP_NULL),
UA_STATUSCODE_GOOD);
ck_assert_uint_eq(sonNotifications, 1);
ck_assert(!lastAnnounce);
UA_Boolean announce = true;
UA_KeyValuePair param;
param.key = UA_QUALIFIEDNAME(0, "announce");
UA_Variant_setScalar(&param.value, &announce, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_KeyValueMap params = {1, &param};
ck_assert_uint_eq(UA_Server_registerServerOnNetwork(server, &son, params),
UA_STATUSCODE_GOOD);
ck_assert_uint_eq(sonNotifications, 2);
ck_assert(lastAnnounce);
UA_Server_delete(server);
}
END_TEST
static Suite *
testSuite(void) {
Suite *suite = suite_create("FindServersOnNetwork");
@@ -539,6 +610,8 @@ testSuite(void) {
tcase_add_loop_test(tc_registered, register_server_uses_tcp_discovery_url, 0, 2);
tcase_add_test(tc_registered, reregister_server_without_tcp_url_removes_server_on_network);
tcase_add_test(tc_registered, deregister_server_without_tcp_url_keeps_other_entry);
tcase_add_loop_test(tc_registered, register_server_announce_follows_config, 0, 2);
tcase_add_test(tc_registered, register_server_on_network_announce_param);
suite_add_tcase(suite, tc_registered);
TCase *tc_names = tcase_create("server names");
tcase_add_test(tc_names, register_server_without_server_names_rejected);