mirror of
https://github.com/open62541/open62541.git
synced 2026-10-16 20:33:18 +08:00
refactor(drivers): Rework the mDNS plugins into drivers with their own lifecycle
This commit is contained in:
committed by
Julius Pfrommer
parent
30cad06c4e
commit
316093c01b
+5
-4
@@ -567,9 +567,10 @@ endif()
|
|||||||
|
|
||||||
if(UA_ENABLE_DISCOVERY_MULTICAST_AVAHI)
|
if(UA_ENABLE_DISCOVERY_MULTICAST_AVAHI)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_search_module(AVAHI REQUIRED avahi-client avahi-common)
|
pkg_search_module(AVAHI REQUIRED avahi-client)
|
||||||
include_directories(${AVAHI_INCLUDE_DIRS})
|
include_directories(${AVAHI_INCLUDE_DIRS})
|
||||||
list(APPEND open62541_PUBLIC_LIBRARIES "${AVAHI_LIBRARIES}")
|
list(APPEND open62541_PUBLIC_LIBRARIES "${AVAHI_LIBRARIES}")
|
||||||
|
list(APPEND open62541_LIBRARIES ${AVAHI_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UA_ARCHITECTURE_LWIP)
|
if(UA_ARCHITECTURE_LWIP)
|
||||||
@@ -1055,7 +1056,7 @@ if(UA_ENABLE_DISCOVERY_MULTICAST_MDNSD)
|
|||||||
${MDNSD_GENERATED_DIR}/inet.h
|
${MDNSD_GENERATED_DIR}/inet.h
|
||||||
${MDNSD_GENERATED_DIR}/mdnsd.h)
|
${MDNSD_GENERATED_DIR}/mdnsd.h)
|
||||||
list(APPEND lib_sources
|
list(APPEND lib_sources
|
||||||
#${PROJECT_SOURCE_DIR}/plugins/mdns/discovery_mdnsd.c
|
${PROJECT_SOURCE_DIR}/drivers/discovery_mdns_mdnsd.c
|
||||||
${MDNSD_GENERATED_DIR}/1035.c
|
${MDNSD_GENERATED_DIR}/1035.c
|
||||||
${MDNSD_GENERATED_DIR}/xht.c
|
${MDNSD_GENERATED_DIR}/xht.c
|
||||||
${MDNSD_GENERATED_DIR}/sdtxt.c
|
${MDNSD_GENERATED_DIR}/sdtxt.c
|
||||||
@@ -1065,8 +1066,8 @@ if(UA_ENABLE_DISCOVERY_MULTICAST_MDNSD)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UA_ENABLE_DISCOVERY_MULTICAST_AVAHI)
|
if(UA_ENABLE_DISCOVERY_MULTICAST_AVAHI)
|
||||||
#list(APPEND lib_sources
|
list(APPEND lib_sources
|
||||||
# ${PROJECT_SOURCE_DIR}/plugins/mdns/discovery_avahi.c)
|
${PROJECT_SOURCE_DIR}/drivers/discovery_mdns_avahi.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UA_BUILD_FUZZING OR UA_BUILD_OSS_FUZZ)
|
if(UA_BUILD_FUZZING OR UA_BUILD_OSS_FUZZ)
|
||||||
|
|||||||
@@ -0,0 +1,864 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2017 (c) Stefan Profanter, fortiss GmbH
|
||||||
|
* Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
|
||||||
|
* Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
|
||||||
|
* Copyright 2024 (c) Linutronix GmbH (Author: Vasilij Strassheim)
|
||||||
|
* Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <open62541/server.h>
|
||||||
|
#include <open62541/driver/mdns.h>
|
||||||
|
|
||||||
|
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_AVAHI
|
||||||
|
|
||||||
|
#include <avahi-client/client.h>
|
||||||
|
#include <avahi-client/lookup.h>
|
||||||
|
#include <avahi-client/publish.h>
|
||||||
|
#include <avahi-common/address.h>
|
||||||
|
#include <avahi-common/error.h>
|
||||||
|
#include <avahi-common/malloc.h>
|
||||||
|
#include <avahi-common/simple-watch.h>
|
||||||
|
#include <avahi-common/strlst.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define UA_AVAHI_POLL_INTERVAL_MS 100
|
||||||
|
#define UA_AVAHI_MAX_NAME_LENGTH 256
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AVAHI_RECORD_REMOTE_RECEIVED,
|
||||||
|
AVAHI_RECORD_LOCAL_OWNED
|
||||||
|
} AvahiRecordOrigin;
|
||||||
|
|
||||||
|
typedef struct ServerOnNetworkRecord {
|
||||||
|
struct ServerOnNetworkRecord *next;
|
||||||
|
UA_ServerOnNetwork serverOnNetwork;
|
||||||
|
AvahiEntryGroup *group;
|
||||||
|
AvahiRecordOrigin origin;
|
||||||
|
UA_Boolean txtSet;
|
||||||
|
UA_Boolean srvSet;
|
||||||
|
UA_UInt32 ttl;
|
||||||
|
} ServerOnNetworkRecord;
|
||||||
|
|
||||||
|
typedef struct AddressRecord {
|
||||||
|
struct AddressRecord *next;
|
||||||
|
UA_String hostname;
|
||||||
|
UA_String address;
|
||||||
|
unsigned short type;
|
||||||
|
AvahiEntryGroup *group;
|
||||||
|
} AddressRecord;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UA_MdnsDriver mdns;
|
||||||
|
UA_Logger *logging;
|
||||||
|
AvahiClient *client;
|
||||||
|
AvahiSimplePoll *simplePoll;
|
||||||
|
AvahiServiceBrowser *browser;
|
||||||
|
UA_UInt64 pollCallbackId;
|
||||||
|
ServerOnNetworkRecord *serverList;
|
||||||
|
AddressRecord *addressList;
|
||||||
|
UA_Boolean listen;
|
||||||
|
UA_Boolean announce;
|
||||||
|
UA_Boolean queryPresence;
|
||||||
|
UA_Boolean queryDetails;
|
||||||
|
UA_UInt32 queryInterval;
|
||||||
|
UA_UInt32 announceTTL;
|
||||||
|
UA_String interface;
|
||||||
|
AvahiIfIndex ifIndex;
|
||||||
|
} AvahiDriver;
|
||||||
|
|
||||||
|
static UA_StatusCode publishLocalRecord(AvahiDriver *md,
|
||||||
|
ServerOnNetworkRecord *entry);
|
||||||
|
|
||||||
|
static UA_UInt32
|
||||||
|
announceTTL(const AvahiDriver *md) {
|
||||||
|
return (md->announceTTL > 0) ? md->announceTTL : 600u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_Boolean
|
||||||
|
isRemoteReceivedRecord(const ServerOnNetworkRecord *entry) {
|
||||||
|
return entry->origin == AVAHI_RECORD_REMOTE_RECEIVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_Boolean
|
||||||
|
isLocalOwnedRecord(const ServerOnNetworkRecord *entry) {
|
||||||
|
return entry->origin == AVAHI_RECORD_LOCAL_OWNED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ServerOnNetworkRecord *
|
||||||
|
findSON(AvahiDriver *md, UA_String serverName) {
|
||||||
|
for(ServerOnNetworkRecord *entry = md->serverList; entry; entry = entry->next) {
|
||||||
|
if(UA_String_equal(&serverName, &entry->serverOnNetwork.serverName))
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
addSON(AvahiDriver *md, const UA_ServerOnNetwork *son,
|
||||||
|
AvahiRecordOrigin origin, ServerOnNetworkRecord **out) {
|
||||||
|
ServerOnNetworkRecord *entry = (ServerOnNetworkRecord*)
|
||||||
|
UA_calloc(1, sizeof(ServerOnNetworkRecord));
|
||||||
|
if(!entry)
|
||||||
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||||
|
|
||||||
|
UA_StatusCode res = UA_ServerOnNetwork_copy(son, &entry->serverOnNetwork);
|
||||||
|
if(res != UA_STATUSCODE_GOOD) {
|
||||||
|
UA_free(entry);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->origin = origin;
|
||||||
|
entry->next = md->serverList;
|
||||||
|
md->serverList = entry;
|
||||||
|
if(out)
|
||||||
|
*out = entry;
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clearEntryGroup(AvahiEntryGroup **group) {
|
||||||
|
if(!group || !*group)
|
||||||
|
return;
|
||||||
|
avahi_entry_group_reset(*group);
|
||||||
|
avahi_entry_group_free(*group);
|
||||||
|
*group = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
removeSONEntry(AvahiDriver *md, ServerOnNetworkRecord *entry,
|
||||||
|
UA_Boolean deregisterFromServer, UA_Boolean clearGroup) {
|
||||||
|
ServerOnNetworkRecord **prev = &md->serverList;
|
||||||
|
while(*prev && *prev != entry)
|
||||||
|
prev = &(*prev)->next;
|
||||||
|
if(!*prev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
*prev = entry->next;
|
||||||
|
if(clearGroup)
|
||||||
|
clearEntryGroup(&entry->group);
|
||||||
|
|
||||||
|
if(deregisterFromServer)
|
||||||
|
UA_Server_deregisterServerOnNetwork(md->mdns.drv.server,
|
||||||
|
entry->serverOnNetwork.serverName);
|
||||||
|
|
||||||
|
UA_ServerOnNetwork_clear(&entry->serverOnNetwork);
|
||||||
|
UA_free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
stringToBuffer(UA_String src, char *dst, size_t dstSize) {
|
||||||
|
if(src.length >= dstSize)
|
||||||
|
return UA_STATUSCODE_BADOUTOFRANGE;
|
||||||
|
memcpy(dst, src.data, src.length);
|
||||||
|
dst[src.length] = '\0';
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
appendLocalSuffix(UA_String hostname, char *dst, size_t dstSize) {
|
||||||
|
if(hostname.length >= dstSize)
|
||||||
|
return UA_STATUSCODE_BADOUTOFRANGE;
|
||||||
|
memcpy(dst, hostname.data, hostname.length);
|
||||||
|
dst[hostname.length] = '\0';
|
||||||
|
if(hostname.length >= 6 &&
|
||||||
|
memcmp(hostname.data + hostname.length - 6, ".local", 6) == 0)
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
if(hostname.length >= 7 &&
|
||||||
|
memcmp(hostname.data + hostname.length - 7, ".local.", 7) == 0)
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
if(hostname.length + 6 >= dstSize)
|
||||||
|
return UA_STATUSCODE_BADOUTOFRANGE;
|
||||||
|
memcpy(dst + hostname.length, ".local", 7);
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
addTxtPath(AvahiStringList **txt, UA_String path) {
|
||||||
|
char buf[256];
|
||||||
|
if(path.length == 0) {
|
||||||
|
*txt = avahi_string_list_add(*txt, "path=/");
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
size_t needed = path.length + 7;
|
||||||
|
if(path.data[0] != '/')
|
||||||
|
needed++;
|
||||||
|
if(needed > sizeof(buf))
|
||||||
|
return UA_STATUSCODE_BADOUTOFRANGE;
|
||||||
|
size_t pos = 0;
|
||||||
|
memcpy(buf + pos, "path=", 5);
|
||||||
|
pos += 5;
|
||||||
|
if(path.data[0] != '/')
|
||||||
|
buf[pos++] = '/';
|
||||||
|
memcpy(buf + pos, path.data, path.length);
|
||||||
|
pos += path.length;
|
||||||
|
buf[pos] = '\0';
|
||||||
|
*txt = avahi_string_list_add(*txt, buf);
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
addTxtCaps(AvahiStringList **txt, const UA_String *caps, size_t capsSize) {
|
||||||
|
if(capsSize == 0) {
|
||||||
|
*txt = avahi_string_list_add(*txt, "caps=NA");
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
char buf[256];
|
||||||
|
size_t pos = 0;
|
||||||
|
memcpy(buf + pos, "caps=", 5);
|
||||||
|
pos += 5;
|
||||||
|
for(size_t i = 0; i < capsSize; i++) {
|
||||||
|
if(i > 0)
|
||||||
|
buf[pos++] = ',';
|
||||||
|
if(pos + caps[i].length >= sizeof(buf))
|
||||||
|
return UA_STATUSCODE_BADOUTOFRANGE;
|
||||||
|
memcpy(buf + pos, caps[i].data, caps[i].length);
|
||||||
|
pos += caps[i].length;
|
||||||
|
}
|
||||||
|
buf[pos] = '\0';
|
||||||
|
*txt = avahi_string_list_add(*txt, buf);
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
copyCsvPartToString(const char *start, size_t len, UA_String *dst) {
|
||||||
|
dst->length = len;
|
||||||
|
dst->data = (UA_Byte*)UA_malloc(len + 1);
|
||||||
|
if(!dst->data)
|
||||||
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||||
|
memcpy(dst->data, start, len);
|
||||||
|
dst->data[len] = '\0';
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
setCapabilitiesFromCsv(ServerOnNetworkRecord *entry, const char *caps) {
|
||||||
|
if(!caps || caps[0] == '\0')
|
||||||
|
caps = "NA";
|
||||||
|
size_t count = 1;
|
||||||
|
for(const char *p = caps; *p; p++) {
|
||||||
|
if(*p == ',')
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
UA_Array_delete(entry->serverOnNetwork.serverCapabilities,
|
||||||
|
entry->serverOnNetwork.serverCapabilitiesSize,
|
||||||
|
&UA_TYPES[UA_TYPES_STRING]);
|
||||||
|
entry->serverOnNetwork.serverCapabilities = (UA_String*)
|
||||||
|
UA_Array_new(count, &UA_TYPES[UA_TYPES_STRING]);
|
||||||
|
if(!entry->serverOnNetwork.serverCapabilities) {
|
||||||
|
entry->serverOnNetwork.serverCapabilitiesSize = 0;
|
||||||
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||||
|
}
|
||||||
|
entry->serverOnNetwork.serverCapabilitiesSize = count;
|
||||||
|
const char *start = caps;
|
||||||
|
for(size_t i = 0; i < count; i++) {
|
||||||
|
const char *end = strchr(start, ',');
|
||||||
|
size_t len = end ? (size_t)(end - start) : strlen(start);
|
||||||
|
UA_StatusCode res =
|
||||||
|
copyCsvPartToString(start, len,
|
||||||
|
&entry->serverOnNetwork.serverCapabilities[i]);
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return res;
|
||||||
|
start = end ? end + 1 : start + len;
|
||||||
|
}
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
getTxtValue(AvahiStringList *txt, const char *wantedKey) {
|
||||||
|
static char valueBuf[256];
|
||||||
|
for(AvahiStringList *cur = txt; cur; cur = avahi_string_list_get_next(cur)) {
|
||||||
|
char *key = NULL;
|
||||||
|
char *value = NULL;
|
||||||
|
if(avahi_string_list_get_pair(cur, &key, &value, NULL) < 0)
|
||||||
|
continue;
|
||||||
|
UA_Boolean match = (strcmp(key, wantedKey) == 0);
|
||||||
|
valueBuf[0] = '\0';
|
||||||
|
if(match && value)
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), "%s", value);
|
||||||
|
avahi_free(key);
|
||||||
|
avahi_free(value);
|
||||||
|
if(match)
|
||||||
|
return valueBuf;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
entryGroupCallback(AvahiEntryGroup *group, AvahiEntryGroupState state,
|
||||||
|
void *userdata) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)userdata;
|
||||||
|
if(!md)
|
||||||
|
return;
|
||||||
|
switch(state) {
|
||||||
|
case AVAHI_ENTRY_GROUP_ESTABLISHED:
|
||||||
|
UA_LOG_INFO(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Entry group established");
|
||||||
|
break;
|
||||||
|
case AVAHI_ENTRY_GROUP_COLLISION:
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Entry group collision");
|
||||||
|
break;
|
||||||
|
case AVAHI_ENTRY_GROUP_FAILURE:
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Entry group failure: %s",
|
||||||
|
avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(group))));
|
||||||
|
break;
|
||||||
|
case AVAHI_ENTRY_GROUP_UNCOMMITED:
|
||||||
|
case AVAHI_ENTRY_GROUP_REGISTERING:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
publishLocalRecord(AvahiDriver *md, ServerOnNetworkRecord *entry) {
|
||||||
|
if(!md->announce)
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
if(!md->client)
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
UA_String hostname = UA_STRING_NULL;
|
||||||
|
UA_String path = UA_STRING_NULL;
|
||||||
|
UA_UInt16 port = 4840;
|
||||||
|
UA_StatusCode res =
|
||||||
|
UA_parseEndpointUrl(&entry->serverOnNetwork.discoveryUrl,
|
||||||
|
&hostname, &port, &path);
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return res;
|
||||||
|
char serviceName[UA_AVAHI_MAX_NAME_LENGTH];
|
||||||
|
res = stringToBuffer(entry->serverOnNetwork.serverName,
|
||||||
|
serviceName, sizeof(serviceName));
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return res;
|
||||||
|
char hostName[UA_AVAHI_MAX_NAME_LENGTH];
|
||||||
|
res = appendLocalSuffix(hostname, hostName, sizeof(hostName));
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return res;
|
||||||
|
AvahiStringList *txt = NULL;
|
||||||
|
res = addTxtPath(&txt, path);
|
||||||
|
if(res == UA_STATUSCODE_GOOD)
|
||||||
|
res = addTxtCaps(&txt, entry->serverOnNetwork.serverCapabilities,
|
||||||
|
entry->serverOnNetwork.serverCapabilitiesSize);
|
||||||
|
if(res != UA_STATUSCODE_GOOD) {
|
||||||
|
avahi_string_list_free(txt);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if(!entry->group) {
|
||||||
|
entry->group = avahi_entry_group_new(md->client, entryGroupCallback, md);
|
||||||
|
if(!entry->group) {
|
||||||
|
avahi_string_list_free(txt);
|
||||||
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
avahi_entry_group_reset(entry->group);
|
||||||
|
}
|
||||||
|
int rv = avahi_entry_group_add_service_strlst(entry->group, md->ifIndex,
|
||||||
|
AVAHI_PROTO_UNSPEC,
|
||||||
|
AVAHI_PUBLISH_USE_MULTICAST,
|
||||||
|
serviceName, "_opcua-tcp._tcp",
|
||||||
|
NULL, hostName, port, txt);
|
||||||
|
avahi_string_list_free(txt);
|
||||||
|
if(rv < 0) {
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Failed to add service %s: %s", serviceName,
|
||||||
|
avahi_strerror(rv));
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
}
|
||||||
|
rv = avahi_entry_group_commit(entry->group);
|
||||||
|
if(rv < 0) {
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Failed to commit service %s: %s", serviceName,
|
||||||
|
avahi_strerror(rv));
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
}
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
removeLocalRecord(AvahiDriver *md, ServerOnNetworkRecord *entry) {
|
||||||
|
(void)md;
|
||||||
|
clearEntryGroup(&entry->group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
registerRemoteIfComplete(AvahiDriver *md, ServerOnNetworkRecord *entry) {
|
||||||
|
if(!entry->srvSet || !entry->txtSet)
|
||||||
|
return;
|
||||||
|
UA_Server_registerServerOnNetwork(md->mdns.drv.server,
|
||||||
|
&entry->serverOnNetwork,
|
||||||
|
UA_KEYVALUEMAP_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clientCallback(AvahiClient *client, AvahiClientState state, void *userdata) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)userdata;
|
||||||
|
if(!md)
|
||||||
|
return;
|
||||||
|
UA_LOG_INFO(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Client state changed to %d", state);
|
||||||
|
if(state == AVAHI_CLIENT_FAILURE)
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Client failure: %s",
|
||||||
|
avahi_strerror(avahi_client_errno(client)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
resolveCallback(AvahiServiceResolver *resolver, AvahiIfIndex interface,
|
||||||
|
AvahiProtocol protocol, AvahiResolverEvent event,
|
||||||
|
const char *name, const char *type, const char *domain,
|
||||||
|
const char *hostName, const AvahiAddress *address,
|
||||||
|
uint16_t port, AvahiStringList *txt,
|
||||||
|
AvahiLookupResultFlags flags, void *userdata) {
|
||||||
|
(void)interface;
|
||||||
|
(void)protocol;
|
||||||
|
(void)type;
|
||||||
|
(void)domain;
|
||||||
|
(void)address;
|
||||||
|
AvahiDriver *md = (AvahiDriver*)userdata;
|
||||||
|
if(!md)
|
||||||
|
goto cleanup;
|
||||||
|
if(event != AVAHI_RESOLVER_FOUND)
|
||||||
|
goto cleanup;
|
||||||
|
if(flags & AVAHI_LOOKUP_RESULT_LOCAL)
|
||||||
|
goto cleanup;
|
||||||
|
UA_String serverName = UA_STRING((char*)(uintptr_t)name);
|
||||||
|
ServerOnNetworkRecord *entry = findSON(md, serverName);
|
||||||
|
if(entry && isLocalOwnedRecord(entry))
|
||||||
|
goto cleanup;
|
||||||
|
if(!entry) {
|
||||||
|
UA_ServerOnNetwork son;
|
||||||
|
UA_ServerOnNetwork_init(&son);
|
||||||
|
son.serverName = serverName;
|
||||||
|
UA_StatusCode res = addSON(md, &son, AVAHI_RECORD_REMOTE_RECEIVED, &entry);
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
UA_String_clear(&entry->serverOnNetwork.discoveryUrl);
|
||||||
|
UA_String_format(&entry->serverOnNetwork.discoveryUrl,
|
||||||
|
"opc.tcp://%s:%u", hostName, port);
|
||||||
|
const char *path = getTxtValue(txt, "path");
|
||||||
|
if(path && path[0] != '\0' && strcmp(path, "/") != 0)
|
||||||
|
UA_String_append(&entry->serverOnNetwork.discoveryUrl,
|
||||||
|
UA_STRING((char*)(uintptr_t)path));
|
||||||
|
const char *caps = getTxtValue(txt, "caps");
|
||||||
|
if(setCapabilitiesFromCsv(entry, caps) != UA_STATUSCODE_GOOD)
|
||||||
|
goto cleanup;
|
||||||
|
entry->srvSet = true;
|
||||||
|
entry->txtSet = true;
|
||||||
|
entry->origin = AVAHI_RECORD_REMOTE_RECEIVED;
|
||||||
|
registerRemoteIfComplete(md, entry);
|
||||||
|
cleanup:
|
||||||
|
if(resolver)
|
||||||
|
avahi_service_resolver_free(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
browseCallback(AvahiServiceBrowser *browser, AvahiIfIndex interface,
|
||||||
|
AvahiProtocol protocol, AvahiBrowserEvent event,
|
||||||
|
const char *name, const char *type, const char *domain,
|
||||||
|
AvahiLookupResultFlags flags, void *userdata) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)userdata;
|
||||||
|
if(!md)
|
||||||
|
return;
|
||||||
|
switch(event) {
|
||||||
|
case AVAHI_BROWSER_NEW: {
|
||||||
|
if(flags & AVAHI_LOOKUP_RESULT_LOCAL)
|
||||||
|
break;
|
||||||
|
UA_String serverName = UA_STRING((char*)(uintptr_t)name);
|
||||||
|
if(!findSON(md, serverName)) {
|
||||||
|
UA_ServerOnNetwork son;
|
||||||
|
UA_ServerOnNetwork_init(&son);
|
||||||
|
son.serverName = serverName;
|
||||||
|
ServerOnNetworkRecord *entry = NULL;
|
||||||
|
addSON(md, &son, AVAHI_RECORD_REMOTE_RECEIVED, &entry);
|
||||||
|
}
|
||||||
|
if(md->queryDetails) {
|
||||||
|
AvahiServiceResolver *resolver =
|
||||||
|
avahi_service_resolver_new(md->client, interface, protocol,
|
||||||
|
name, type, domain, AVAHI_PROTO_UNSPEC,
|
||||||
|
AVAHI_LOOKUP_USE_MULTICAST,
|
||||||
|
resolveCallback, md);
|
||||||
|
if(!resolver)
|
||||||
|
UA_LOG_WARNING(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Failed to resolve service %s: %s", name,
|
||||||
|
avahi_strerror(avahi_client_errno(md->client)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AVAHI_BROWSER_REMOVE: {
|
||||||
|
UA_String serverName = UA_STRING((char*)(uintptr_t)name);
|
||||||
|
ServerOnNetworkRecord *entry = findSON(md, serverName);
|
||||||
|
if(entry && isRemoteReceivedRecord(entry))
|
||||||
|
removeSONEntry(md, entry, true, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AVAHI_BROWSER_FAILURE:
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Browser failure: %s",
|
||||||
|
avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(browser))));
|
||||||
|
break;
|
||||||
|
case AVAHI_BROWSER_ALL_FOR_NOW:
|
||||||
|
case AVAHI_BROWSER_CACHE_EXHAUSTED:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pollAvahi(UA_Server *server, void *data) {
|
||||||
|
(void)server;
|
||||||
|
AvahiDriver *md = (AvahiDriver*)data;
|
||||||
|
if(!md->simplePoll)
|
||||||
|
return;
|
||||||
|
int rv = avahi_simple_poll_iterate(md->simplePoll, 0);
|
||||||
|
if(rv < 0)
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Poll failure: %s", avahi_strerror(rv));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AvahiDriverNotificationCallback(UA_Driver *drv,
|
||||||
|
UA_ApplicationNotificationType type,
|
||||||
|
const UA_KeyValueMap payload) {
|
||||||
|
if(type != UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY_SERVERONNETWORK)
|
||||||
|
return;
|
||||||
|
if(drv->state != UA_LIFECYCLESTATE_STARTED)
|
||||||
|
return;
|
||||||
|
AvahiDriver *md = (AvahiDriver*)drv;
|
||||||
|
const UA_ServerOnNetwork *son = (const UA_ServerOnNetwork*)
|
||||||
|
UA_KeyValueMap_getScalar(&payload, UA_QUALIFIEDNAME(0, "server-on-network"),
|
||||||
|
&UA_TYPES[UA_TYPES_SERVERONNETWORK]);
|
||||||
|
if(!son)
|
||||||
|
return;
|
||||||
|
const UA_UInt32 *ttlParam = (const UA_UInt32*)
|
||||||
|
UA_KeyValueMap_getScalar(&payload, UA_QUALIFIEDNAME(0, "ttl"),
|
||||||
|
&UA_TYPES[UA_TYPES_UINT32]);
|
||||||
|
UA_UInt32 ttl = (ttlParam && *ttlParam > 0) ? *ttlParam : announceTTL(md);
|
||||||
|
const UA_Boolean *addedParam = (const UA_Boolean*)
|
||||||
|
UA_KeyValueMap_getScalar(&payload, UA_QUALIFIEDNAME(0, "server-added"),
|
||||||
|
&UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
const UA_Boolean *updatedParam = (const UA_Boolean*)
|
||||||
|
UA_KeyValueMap_getScalar(&payload, UA_QUALIFIEDNAME(0, "server-updated"),
|
||||||
|
&UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
const UA_Boolean *removedParam = (const UA_Boolean*)
|
||||||
|
UA_KeyValueMap_getScalar(&payload, UA_QUALIFIEDNAME(0, "server-removed"),
|
||||||
|
&UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
UA_Boolean added = addedParam ? *addedParam : false;
|
||||||
|
UA_Boolean updated = updatedParam ? *updatedParam : false;
|
||||||
|
UA_Boolean removed = removedParam ? *removedParam : false;
|
||||||
|
ServerOnNetworkRecord *entry = findSON(md, son->serverName);
|
||||||
|
if(added || updated) {
|
||||||
|
if(entry) {
|
||||||
|
UA_ServerOnNetwork sonForCmp = *son;
|
||||||
|
sonForCmp.recordId = entry->serverOnNetwork.recordId;
|
||||||
|
UA_Order same = UA_order(&sonForCmp, &entry->serverOnNetwork,
|
||||||
|
&UA_TYPES[UA_TYPES_SERVERONNETWORK]);
|
||||||
|
if(same == UA_ORDER_EQ)
|
||||||
|
return;
|
||||||
|
UA_ServerOnNetwork tmp;
|
||||||
|
UA_StatusCode res = UA_ServerOnNetwork_copy(son, &tmp);
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return;
|
||||||
|
UA_ServerOnNetwork_clear(&entry->serverOnNetwork);
|
||||||
|
entry->serverOnNetwork = tmp;
|
||||||
|
} else {
|
||||||
|
UA_StatusCode res = addSON(md, son, AVAHI_RECORD_LOCAL_OWNED, &entry);
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry->origin = AVAHI_RECORD_LOCAL_OWNED;
|
||||||
|
entry->ttl = ttl;
|
||||||
|
publishLocalRecord(md, entry);
|
||||||
|
}
|
||||||
|
if(removed) {
|
||||||
|
if(!entry)
|
||||||
|
return;
|
||||||
|
if(isLocalOwnedRecord(entry))
|
||||||
|
removeLocalRecord(md, entry);
|
||||||
|
removeSONEntry(md, entry, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
AvahiDriver_start(UA_Driver *drv) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)drv;
|
||||||
|
if(!drv->server)
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
if(drv->state != UA_LIFECYCLESTATE_STOPPED)
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
UA_ServerConfig *config = UA_Server_getConfig(drv->server);
|
||||||
|
md->logging = config->logging;
|
||||||
|
const UA_Boolean *listen = (const UA_Boolean*)
|
||||||
|
UA_KeyValueMap_getScalar(&drv->params, UA_QUALIFIEDNAME(0, "listen"),
|
||||||
|
&UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
md->listen = listen ? *listen : false;
|
||||||
|
const UA_Boolean *announce = (const UA_Boolean*)
|
||||||
|
UA_KeyValueMap_getScalar(&drv->params, UA_QUALIFIEDNAME(0, "announce"),
|
||||||
|
&UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
md->announce = announce ? *announce : false;
|
||||||
|
const UA_Boolean *queryPresence = (const UA_Boolean*)
|
||||||
|
UA_KeyValueMap_getScalar(&drv->params, UA_QUALIFIEDNAME(0, "query-presence"),
|
||||||
|
&UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
md->queryPresence = queryPresence ? *queryPresence : false;
|
||||||
|
const UA_Boolean *queryDetails = (const UA_Boolean*)
|
||||||
|
UA_KeyValueMap_getScalar(&drv->params, UA_QUALIFIEDNAME(0, "query-details"),
|
||||||
|
&UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
md->queryDetails = queryDetails ? *queryDetails : false;
|
||||||
|
const UA_UInt32 *queryInterval = (const UA_UInt32*)
|
||||||
|
UA_KeyValueMap_getScalar(&drv->params, UA_QUALIFIEDNAME(0, "query-interval"),
|
||||||
|
&UA_TYPES[UA_TYPES_UINT32]);
|
||||||
|
md->queryInterval = queryInterval ? *queryInterval : 0;
|
||||||
|
const UA_UInt32 *announceTTLParam = (const UA_UInt32*)
|
||||||
|
UA_KeyValueMap_getScalar(&drv->params, UA_QUALIFIEDNAME(0, "announce-ttl"),
|
||||||
|
&UA_TYPES[UA_TYPES_UINT32]);
|
||||||
|
md->announceTTL = announceTTLParam ? *announceTTLParam : 0;
|
||||||
|
UA_String_clear(&md->interface);
|
||||||
|
const UA_String *interface = (const UA_String*)
|
||||||
|
UA_KeyValueMap_getScalar(&drv->params, UA_QUALIFIEDNAME(0, "interface"),
|
||||||
|
&UA_TYPES[UA_TYPES_STRING]);
|
||||||
|
if(interface)
|
||||||
|
UA_String_copy(interface, &md->interface);
|
||||||
|
md->ifIndex = AVAHI_IF_UNSPEC;
|
||||||
|
if(md->interface.length > 0) {
|
||||||
|
char ifName[UA_AVAHI_MAX_NAME_LENGTH];
|
||||||
|
if(stringToBuffer(md->interface, ifName, sizeof(ifName)) == UA_STATUSCODE_GOOD) {
|
||||||
|
unsigned int idx = if_nametoindex(ifName);
|
||||||
|
if(idx > 0) {
|
||||||
|
md->ifIndex = (AvahiIfIndex)idx;
|
||||||
|
} else {
|
||||||
|
UA_LOG_WARNING(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Interface '%s' is not a local interface "
|
||||||
|
"name; using all interfaces", ifName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
UA_LOG_WARNING(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Interface parameter too long; using all interfaces");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!md->listen && (md->queryPresence || md->queryDetails)) {
|
||||||
|
UA_LOG_WARNING(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Querying requires listen=true; disabling queries");
|
||||||
|
md->queryPresence = false;
|
||||||
|
md->queryDetails = false;
|
||||||
|
md->queryInterval = 0;
|
||||||
|
}
|
||||||
|
if(md->queryInterval > 0 && md->queryPresence)
|
||||||
|
UA_LOG_INFO(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: query-interval is accepted for configuration parity; "
|
||||||
|
"service discovery remains event-driven");
|
||||||
|
md->simplePoll = avahi_simple_poll_new();
|
||||||
|
if(!md->simplePoll)
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
int error = 0;
|
||||||
|
md->client = avahi_client_new(avahi_simple_poll_get(md->simplePoll),
|
||||||
|
AVAHI_CLIENT_NO_FAIL, clientCallback,
|
||||||
|
md, &error);
|
||||||
|
if(!md->client) {
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Failed to create client: %s", avahi_strerror(error));
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
}
|
||||||
|
if(md->listen || md->queryPresence || md->queryDetails) {
|
||||||
|
md->browser = avahi_service_browser_new(md->client, md->ifIndex,
|
||||||
|
AVAHI_PROTO_UNSPEC,
|
||||||
|
"_opcua-tcp._tcp", NULL,
|
||||||
|
AVAHI_LOOKUP_USE_MULTICAST,
|
||||||
|
browseCallback, md);
|
||||||
|
if(!md->browser) {
|
||||||
|
UA_LOG_ERROR(md->logging, UA_LOGCATEGORY_DISCOVERY,
|
||||||
|
"Avahi: Failed to create browser: %s",
|
||||||
|
avahi_strerror(avahi_client_errno(md->client)));
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UA_Server_addRepeatedCallback(drv->server, pollAvahi, md,
|
||||||
|
UA_AVAHI_POLL_INTERVAL_MS,
|
||||||
|
&md->pollCallbackId);
|
||||||
|
drv->state = UA_LIFECYCLESTATE_STARTED;
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AvahiDriver_stop(UA_Driver *drv) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)drv;
|
||||||
|
if(drv->state == UA_LIFECYCLESTATE_STOPPED)
|
||||||
|
return;
|
||||||
|
drv->state = UA_LIFECYCLESTATE_STOPPING;
|
||||||
|
if(md->pollCallbackId) {
|
||||||
|
UA_Server_removeRepeatedCallback(drv->server, md->pollCallbackId);
|
||||||
|
md->pollCallbackId = 0;
|
||||||
|
}
|
||||||
|
if(md->browser) {
|
||||||
|
avahi_service_browser_free(md->browser);
|
||||||
|
md->browser = NULL;
|
||||||
|
}
|
||||||
|
for(ServerOnNetworkRecord *entry = md->serverList; entry; entry = entry->next) {
|
||||||
|
if(isLocalOwnedRecord(entry))
|
||||||
|
removeLocalRecord(md, entry);
|
||||||
|
}
|
||||||
|
for(AddressRecord *ar = md->addressList; ar; ar = ar->next)
|
||||||
|
clearEntryGroup(&ar->group);
|
||||||
|
if(md->client) {
|
||||||
|
avahi_client_free(md->client);
|
||||||
|
md->client = NULL;
|
||||||
|
}
|
||||||
|
if(md->simplePoll) {
|
||||||
|
avahi_simple_poll_free(md->simplePoll);
|
||||||
|
md->simplePoll = NULL;
|
||||||
|
}
|
||||||
|
drv->state = UA_LIFECYCLESTATE_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
AvahiDriver_free(UA_Driver *drv) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)drv;
|
||||||
|
if(drv->state != UA_LIFECYCLESTATE_STOPPED)
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
ServerOnNetworkRecord *entry = md->serverList;
|
||||||
|
while(entry) {
|
||||||
|
ServerOnNetworkRecord *next = entry->next;
|
||||||
|
clearEntryGroup(&entry->group);
|
||||||
|
UA_ServerOnNetwork_clear(&entry->serverOnNetwork);
|
||||||
|
UA_free(entry);
|
||||||
|
entry = next;
|
||||||
|
}
|
||||||
|
AddressRecord *ar = md->addressList;
|
||||||
|
while(ar) {
|
||||||
|
AddressRecord *next = ar->next;
|
||||||
|
clearEntryGroup(&ar->group);
|
||||||
|
UA_String_clear(&ar->hostname);
|
||||||
|
UA_String_clear(&ar->address);
|
||||||
|
UA_free(ar);
|
||||||
|
ar = next;
|
||||||
|
}
|
||||||
|
UA_String_clear(&md->interface);
|
||||||
|
UA_KeyValueMap_clear(&md->mdns.drv.params);
|
||||||
|
UA_free(md);
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
publishAddressRecord(UA_MdnsDriver *drv, UA_String hostname, UA_String address,
|
||||||
|
UA_UInt32 ttl, unsigned short type) {
|
||||||
|
(void)ttl;
|
||||||
|
AvahiDriver *md = (AvahiDriver*)drv;
|
||||||
|
if(!md->client)
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
char host[UA_AVAHI_MAX_NAME_LENGTH];
|
||||||
|
UA_StatusCode res = stringToBuffer(hostname, host, sizeof(host));
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return res;
|
||||||
|
char addr[AVAHI_ADDRESS_STR_MAX];
|
||||||
|
res = stringToBuffer(address, addr, sizeof(addr));
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
return res;
|
||||||
|
AvahiProtocol proto = (type == 1) ? AVAHI_PROTO_INET : AVAHI_PROTO_INET6;
|
||||||
|
AvahiAddress avahiAddress;
|
||||||
|
if(!avahi_address_parse(addr, proto, &avahiAddress))
|
||||||
|
return UA_STATUSCODE_BADINVALIDARGUMENT;
|
||||||
|
AddressRecord *ar = (AddressRecord*)UA_calloc(1, sizeof(AddressRecord));
|
||||||
|
if(!ar)
|
||||||
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||||
|
UA_String_copy(&hostname, &ar->hostname);
|
||||||
|
UA_String_copy(&address, &ar->address);
|
||||||
|
ar->type = type;
|
||||||
|
ar->group = avahi_entry_group_new(md->client, entryGroupCallback, md);
|
||||||
|
if(!ar->group) {
|
||||||
|
UA_free(ar);
|
||||||
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||||
|
}
|
||||||
|
int rv = avahi_entry_group_add_address(ar->group, md->ifIndex,
|
||||||
|
AVAHI_PROTO_UNSPEC,
|
||||||
|
AVAHI_PUBLISH_USE_MULTICAST,
|
||||||
|
host, &avahiAddress);
|
||||||
|
if(rv < 0) {
|
||||||
|
clearEntryGroup(&ar->group);
|
||||||
|
UA_String_clear(&ar->hostname);
|
||||||
|
UA_String_clear(&ar->address);
|
||||||
|
UA_free(ar);
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
}
|
||||||
|
rv = avahi_entry_group_commit(ar->group);
|
||||||
|
if(rv < 0) {
|
||||||
|
clearEntryGroup(&ar->group);
|
||||||
|
UA_String_clear(&ar->hostname);
|
||||||
|
UA_String_clear(&ar->address);
|
||||||
|
UA_free(ar);
|
||||||
|
return UA_STATUSCODE_BADINTERNALERROR;
|
||||||
|
}
|
||||||
|
ar->next = md->addressList;
|
||||||
|
md->addressList = ar;
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
removeAddressRecord(UA_MdnsDriver *drv, UA_String hostname, UA_String address,
|
||||||
|
unsigned short type) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)drv;
|
||||||
|
AddressRecord **prev = &md->addressList;
|
||||||
|
while(*prev) {
|
||||||
|
AddressRecord *ar = *prev;
|
||||||
|
if(ar->type == type && UA_String_equal(&hostname, &ar->hostname) &&
|
||||||
|
UA_String_equal(&address, &ar->address)) {
|
||||||
|
*prev = ar->next;
|
||||||
|
clearEntryGroup(&ar->group);
|
||||||
|
UA_String_clear(&ar->hostname);
|
||||||
|
UA_String_clear(&ar->address);
|
||||||
|
UA_free(ar);
|
||||||
|
return UA_STATUSCODE_GOOD;
|
||||||
|
}
|
||||||
|
prev = &(*prev)->next;
|
||||||
|
}
|
||||||
|
return UA_STATUSCODE_BADNOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
AvahiDriver_addARecord(UA_MdnsDriver *drv, UA_String hostname,
|
||||||
|
UA_String address, UA_UInt32 ttl) {
|
||||||
|
return publishAddressRecord(drv, hostname, address, ttl, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
AvahiDriver_removeARecord(UA_MdnsDriver *drv, UA_String hostname,
|
||||||
|
UA_String address) {
|
||||||
|
return removeAddressRecord(drv, hostname, address, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
AvahiDriver_addAAAARecord(UA_MdnsDriver *drv, UA_String hostname,
|
||||||
|
UA_String address, UA_UInt32 ttl) {
|
||||||
|
return publishAddressRecord(drv, hostname, address, ttl, 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
AvahiDriver_removeAAAARecord(UA_MdnsDriver *drv, UA_String hostname,
|
||||||
|
UA_String address) {
|
||||||
|
return removeAddressRecord(drv, hostname, address, 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
UA_MdnsDriver *
|
||||||
|
UA_MdnsDriver_Avahi(const UA_KeyValueMap params) {
|
||||||
|
AvahiDriver *md = (AvahiDriver*)UA_calloc(1, sizeof(AvahiDriver));
|
||||||
|
if(!md)
|
||||||
|
return NULL;
|
||||||
|
UA_StatusCode res = UA_KeyValueMap_copy(¶ms, &md->mdns.drv.params);
|
||||||
|
if(res != UA_STATUSCODE_GOOD) {
|
||||||
|
UA_free(md);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
md->mdns.drv.name = UA_STRING("discovery-mdns-avahi");
|
||||||
|
md->mdns.drv.start = AvahiDriver_start;
|
||||||
|
md->mdns.drv.stop = AvahiDriver_stop;
|
||||||
|
md->mdns.drv.free = AvahiDriver_free;
|
||||||
|
md->mdns.drv.notificationCallback = AvahiDriverNotificationCallback;
|
||||||
|
md->mdns.drv.notificationFilter = UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY;
|
||||||
|
md->mdns.addARecord = AvahiDriver_addARecord;
|
||||||
|
md->mdns.removeARecord = AvahiDriver_removeARecord;
|
||||||
|
md->mdns.addAAAARecord = AvahiDriver_addAAAARecord;
|
||||||
|
md->mdns.removeAAAARecord = AvahiDriver_removeAAAARecord;
|
||||||
|
return &md->mdns;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* UA_ENABLE_DISCOVERY_MULTICAST_AVAHI */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,12 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#ifdef UA_ARCHITECTURE_WIN32
|
||||||
|
# include <winsock2.h>
|
||||||
|
#else
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|
||||||
|
|||||||
@@ -48,29 +48,7 @@ int main(void) {
|
|||||||
config->applicationDescription.applicationName =
|
config->applicationDescription.applicationName =
|
||||||
UA_LOCALIZEDTEXT_ALLOC("en-US", "Open62541 Local Discovery Server");
|
UA_LOCALIZEDTEXT_ALLOC("en-US", "Open62541 Local Discovery Server");
|
||||||
|
|
||||||
// Enable the mDNS announce and response functionality
|
config->serversOnNetworkEnabled = true;
|
||||||
config->mdnsEnabled = true;
|
|
||||||
|
|
||||||
config->mdnsConfig.mdnsServerName = UA_String_fromChars("LDS");
|
|
||||||
|
|
||||||
// See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv
|
|
||||||
// For a LDS server, you should only indicate the LDS capability.
|
|
||||||
// If this instance is an LDS and at the same time a normal OPC UA server, you also have to indicate
|
|
||||||
// the additional capabilities.
|
|
||||||
// NOTE: UaExpert does not show LDS-only servers in the list.
|
|
||||||
// See also: https://forum.unified-automation.com/topic1987.html
|
|
||||||
|
|
||||||
// E.g. here we only set LDS, and you will not see it in UaExpert
|
|
||||||
config->mdnsConfig.serverCapabilitiesSize = 1;
|
|
||||||
UA_String *caps = (UA_String *) UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
|
|
||||||
if(!caps) {
|
|
||||||
/* Allocation failed for server capabilities array.
|
|
||||||
* Clean up and exit to avoid running with invalid config. */
|
|
||||||
UA_Server_delete(server);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
caps[0] = UA_String_fromChars("LDS");
|
|
||||||
config->mdnsConfig.serverCapabilities = caps;
|
|
||||||
|
|
||||||
/* timeout in seconds when to automatically remove a registered server from
|
/* timeout in seconds when to automatically remove a registered server from
|
||||||
* the list, if it doesn't re-register within the given time frame. A value
|
* the list, if it doesn't re-register within the given time frame. A value
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <open62541/client.h>
|
#include <open62541/client.h>
|
||||||
#include <open62541/client_config_default.h>
|
#include <open62541/client_config_default.h>
|
||||||
|
#include <open62541/driver/mdns.h>
|
||||||
#include <open62541/plugin/log_stdout.h>
|
#include <open62541/plugin/log_stdout.h>
|
||||||
#include <open62541/server.h>
|
#include <open62541/server.h>
|
||||||
#include <open62541/server_config_default.h>
|
#include <open62541/server_config_default.h>
|
||||||
@@ -33,6 +34,38 @@ static void stopHandler(int sign) {
|
|||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UA_StatusCode
|
||||||
|
addMdnsDiscoveryDriver(UA_Server *server) {
|
||||||
|
UA_Boolean enabled = true;
|
||||||
|
UA_UInt32 queryInterval = 30;
|
||||||
|
UA_KeyValuePair params[5];
|
||||||
|
params[0].key = UA_QUALIFIEDNAME(0, "listen");
|
||||||
|
UA_Variant_setScalar(¶ms[0].value, &enabled, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
params[1].key = UA_QUALIFIEDNAME(0, "announce");
|
||||||
|
UA_Variant_setScalar(¶ms[1].value, &enabled, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
params[2].key = UA_QUALIFIEDNAME(0, "query-presence");
|
||||||
|
UA_Variant_setScalar(¶ms[2].value, &enabled, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
params[3].key = UA_QUALIFIEDNAME(0, "query-details");
|
||||||
|
UA_Variant_setScalar(¶ms[3].value, &enabled, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||||
|
params[4].key = UA_QUALIFIEDNAME(0, "query-interval");
|
||||||
|
UA_Variant_setScalar(¶ms[4].value, &queryInterval,
|
||||||
|
&UA_TYPES[UA_TYPES_UINT32]);
|
||||||
|
UA_KeyValueMap paramsMap = {5, params};
|
||||||
|
|
||||||
|
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_AVAHI
|
||||||
|
UA_MdnsDriver *mdns = UA_MdnsDriver_Avahi(paramsMap);
|
||||||
|
#else
|
||||||
|
UA_MdnsDriver *mdns = UA_MdnsDriver_Mdnsd(paramsMap);
|
||||||
|
#endif
|
||||||
|
if(!mdns)
|
||||||
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||||
|
|
||||||
|
UA_StatusCode res = UA_Server_addDriver(server, &mdns->drv);
|
||||||
|
if(res != UA_STATUSCODE_GOOD)
|
||||||
|
mdns->drv.free(&mdns->drv);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static char *discovery_url = NULL;
|
static char *discovery_url = NULL;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -236,33 +269,22 @@ int main(int argc, char **argv) {
|
|||||||
config->applicationDescription.applicationUri =
|
config->applicationDescription.applicationUri =
|
||||||
UA_String_fromChars("urn:open62541.example.server_multicast");
|
UA_String_fromChars("urn:open62541.example.server_multicast");
|
||||||
|
|
||||||
// Enable the mDNS announce and response functionality
|
/* Enable the local ServerOnNetwork table used by FindServersOnNetwork.
|
||||||
config->mdnsEnabled = true;
|
* The mDNS discovery driver fills this table with announced servers. */
|
||||||
|
config->serversOnNetworkEnabled = true;
|
||||||
|
|
||||||
config->mdnsConfig.mdnsServerName = UA_String_fromChars("Sample-Multicast-Server");
|
UA_StatusCode retval = addMdnsDiscoveryDriver(server);
|
||||||
|
if(retval != UA_STATUSCODE_GOOD) {
|
||||||
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
|
||||||
// Use loopback interface for mDNS announcements by default.
|
"Could not add the mDNS discovery driver. StatusCode %s",
|
||||||
// This only works when the LDS and this server run on the same device.
|
UA_StatusCode_name(retval));
|
||||||
// For deployment in LAN or across multiple devices, replace "127.0.0.1" with the IP of your network interface,
|
UA_Server_delete(server);
|
||||||
// such as "192.168.1.100" or the IP of wlan0/eth0.
|
UA_free(discovery_url);
|
||||||
config->mdnsInterfaceIP = UA_String_fromChars("127.0.0.1");
|
return EXIT_FAILURE;
|
||||||
#endif
|
}
|
||||||
|
|
||||||
// See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv
|
|
||||||
// For a LDS server, you should only indicate the LDS capability.
|
|
||||||
// If this instance is an LDS and at the same time a normal OPC UA server, you also have to indicate
|
|
||||||
// the additional capabilities.
|
|
||||||
// NOTE: UaExpert does not show LDS-only servers in the list.
|
|
||||||
// See also: https://forum.unified-automation.com/topic1987.html
|
|
||||||
|
|
||||||
config->mdnsConfig.serverCapabilitiesSize = 1;
|
|
||||||
UA_String *caps = (UA_String *) UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
|
|
||||||
caps[0] = UA_String_fromChars("LDS");
|
|
||||||
config->mdnsConfig.serverCapabilities = caps;
|
|
||||||
|
|
||||||
// Start the server and call iterate to wait for the multicast discovery of the LDS
|
// Start the server and call iterate to wait for the multicast discovery of the LDS
|
||||||
UA_StatusCode retval = UA_Server_run_startup(server);
|
retval = UA_Server_run_startup(server);
|
||||||
|
|
||||||
// callback which is called when a new server is detected through mDNS
|
// callback which is called when a new server is detected through mDNS
|
||||||
// needs to be set after UA_Server_run_startup or UA_Server_run
|
// needs to be set after UA_Server_run_startup or UA_Server_run
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ int main(int argc, char **argv) {
|
|||||||
UA_String_clear(&config->applicationDescription.applicationUri);
|
UA_String_clear(&config->applicationDescription.applicationUri);
|
||||||
config->applicationDescription.applicationUri =
|
config->applicationDescription.applicationUri =
|
||||||
UA_String_fromChars("urn:open62541.example.server_register");
|
UA_String_fromChars("urn:open62541.example.server_register");
|
||||||
config->mdnsConfig.mdnsServerName = UA_String_fromChars("Sample-Server");
|
|
||||||
// See http://www.opcfoundation.org/UA/schemas/1.04/ServerCapabilities.csv
|
// See http://www.opcfoundation.org/UA/schemas/1.04/ServerCapabilities.csv
|
||||||
//config.serverCapabilitiesSize = 1;
|
//config.serverCapabilitiesSize = 1;
|
||||||
//UA_String caps = UA_String_fromChars("LDS");
|
//UA_String caps = UA_String_fromChars("LDS");
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UA_DRIVER_MDNS_H_
|
||||||
|
#define UA_DRIVER_MDNS_H_
|
||||||
|
|
||||||
|
#include <open62541/server.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MDNS Driver
|
||||||
|
* -----------
|
||||||
|
* An OPC UA application can announce / retract a server via multicast DNS. Also
|
||||||
|
* it receives multicast-based information about other servers. This
|
||||||
|
* functionality is implemented as a driver that integrates with open62541
|
||||||
|
* servers.
|
||||||
|
* Internally, the drivers use the EventLoop of the server to create and manage
|
||||||
|
* UDP sockets. They further use the UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY
|
||||||
|
* category for server notifications to receive relevant updates from the server
|
||||||
|
* at runtime.
|
||||||
|
*
|
||||||
|
* The following configuration parameters are forwarded in a key-value map
|
||||||
|
* during driver instantiation and/or set in the key-value map inside the
|
||||||
|
* generic driver structure before starting the driver.
|
||||||
|
*
|
||||||
|
* 0:listen [bool]
|
||||||
|
* Receive information about OPC UA servers on the network via mDNS. Calls
|
||||||
|
* UA_Server_registerServerOnNetwork and UA_Server_deregisterServerOnNetwork
|
||||||
|
* to store the server information locally.
|
||||||
|
*
|
||||||
|
* 0:announce [bool]
|
||||||
|
* Announce registered servers over mDNS. This receives register/deregister
|
||||||
|
* notifications from UA_Server_registerServerOnNetwork and forwards the
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* 0:announce-ttl [uint32]
|
||||||
|
* Default TTL in seconds for announced mDNS records.
|
||||||
|
*
|
||||||
|
* 0:query-presence [bool]
|
||||||
|
* Send an mDNS PTR query for _opcua-tcp._tcp.local. on startup. Defaults to
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* 0:query-details [bool]
|
||||||
|
* Send targeted SRV/TXT follow-up queries for partially received OPC UA
|
||||||
|
* service instances. Defaults to false.
|
||||||
|
*
|
||||||
|
* 0:query-interval [uint32]
|
||||||
|
* Interval in seconds for repeated presence queries. A value of zero
|
||||||
|
* disables repeated queries. Defaults to zero.
|
||||||
|
*
|
||||||
|
* 0:interface [string]
|
||||||
|
* Network interface for listening or sending. Can be either the IP address
|
||||||
|
* of the network interface or the interface name (e.g. 'eth0'). Cf. the UDP
|
||||||
|
* ConnectionManager parameters. */
|
||||||
|
|
||||||
|
typedef struct UA_MdnsDriver UA_MdnsDriver;
|
||||||
|
|
||||||
|
struct UA_MdnsDriver {
|
||||||
|
UA_Driver drv; /* Must be the first member */
|
||||||
|
|
||||||
|
/* Manually publish/remove address records. This is separate from the
|
||||||
|
* ServerOnNetwork announcement path as PTR/SRV/TXT records are always
|
||||||
|
* derived from the ServerOnNetwork data. The address strings are numeric
|
||||||
|
* IPv4/IPv6 literals. */
|
||||||
|
UA_StatusCode (*addARecord)(UA_MdnsDriver *mdns, UA_String hostname,
|
||||||
|
UA_String address, UA_UInt32 ttl);
|
||||||
|
UA_StatusCode (*removeARecord)(UA_MdnsDriver *mdns, UA_String hostname,
|
||||||
|
UA_String address);
|
||||||
|
UA_StatusCode (*addAAAARecord)(UA_MdnsDriver *mdns, UA_String hostname,
|
||||||
|
UA_String address, UA_UInt32 ttl);
|
||||||
|
UA_StatusCode (*removeAAAARecord)(UA_MdnsDriver *mdns, UA_String hostname,
|
||||||
|
UA_String address);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Based on the libmdnsd dependency */
|
||||||
|
UA_EXPORT UA_MdnsDriver *
|
||||||
|
UA_MdnsDriver_Mdnsd(const UA_KeyValueMap params);
|
||||||
|
|
||||||
|
/* Based on the libavahi dependency */
|
||||||
|
UA_EXPORT UA_MdnsDriver *
|
||||||
|
UA_MdnsDriver_Avahi(const UA_KeyValueMap params);
|
||||||
|
|
||||||
|
#endif /* UA_DRIVER_MDNS_H_ */
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -209,12 +209,6 @@ discoveryClientStateCallback(UA_Client *client,
|
|||||||
respType = &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE];
|
respType = &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE];
|
||||||
request = ®2;
|
request = ®2;
|
||||||
|
|
||||||
/* /\* Set the configuration that is only available for */
|
|
||||||
/* * UA_RegisterServer2Request *\/ */
|
|
||||||
/* UA_ExtensionObject_setValueNoDelete(&mdnsConfig, &sc->mdnsSelfConfig, */
|
|
||||||
/* &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION]); */
|
|
||||||
/* reg2.discoveryConfigurationSize = 1; */
|
|
||||||
/* reg2.discoveryConfiguration = &mdnsConfig; */
|
|
||||||
} else {
|
} else {
|
||||||
UA_RegisterServerRequest_init(®1);
|
UA_RegisterServerRequest_init(®1);
|
||||||
setupRegisterRequest(ar, ®1.requestHeader, ®1.server);
|
setupRegisterRequest(ar, ®1.requestHeader, ®1.server);
|
||||||
|
|||||||
@@ -283,6 +283,16 @@ notifyServerOnNetwork(UA_Server *server,
|
|||||||
notifyApplication(server,
|
notifyApplication(server,
|
||||||
UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY_SERVERONNETWORK,
|
UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY_SERVERONNETWORK,
|
||||||
kvm);
|
kvm);
|
||||||
|
|
||||||
|
/* Specialized application callback for discovery notifications. Called
|
||||||
|
* directly (not via notifyApplication) to avoid recursion: the driver's
|
||||||
|
* own notification callback may itself trigger a register/deregister
|
||||||
|
* that flows back into notifyServerOnetwork. */
|
||||||
|
UA_ServerConfig *config = &server->config;
|
||||||
|
if(config->discoveryNotificationCallback)
|
||||||
|
config->discoveryNotificationCallback(server,
|
||||||
|
UA_APPLICATIONNOTIFICATIONTYPE_DISCOVERY_SERVERONNETWORK,
|
||||||
|
kvm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
@@ -493,7 +493,8 @@ if(UA_ENABLE_ENCRYPTION)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UA_ENABLE_DISCOVERY AND UA_ENABLE_DISCOVERY_MULTICAST_MDNSD)
|
if(UA_ENABLE_DISCOVERY AND (UA_ENABLE_DISCOVERY_MULTICAST_MDNSD OR
|
||||||
|
UA_ENABLE_DISCOVERY_MULTICAST_AVAHI))
|
||||||
ua_add_test(server/check_discovery_mdnsd.c)
|
ua_add_test(server/check_discovery_mdnsd.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ static void start_server(void) {
|
|||||||
|
|
||||||
UA_ServerConfig *config = UA_Server_getConfig(server);
|
UA_ServerConfig *config = UA_Server_getConfig(server);
|
||||||
config->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
|
config->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
|
||||||
config->mdnsEnabled = true;
|
|
||||||
config->mdnsConfig.mdnsServerName = UA_String_fromChars("Sample-Multicast-Server");
|
|
||||||
|
|
||||||
UA_Server_run_startup(server);
|
UA_Server_run_startup(server);
|
||||||
pthread_create(&server_thread, NULL, serverloop, NULL);
|
pthread_create(&server_thread, NULL, serverloop, NULL);
|
||||||
@@ -197,14 +195,6 @@ registerServer2Request(UA_Client *client) {
|
|||||||
|
|
||||||
initUaRegisterServer(&request.server);
|
initUaRegisterServer(&request.server);
|
||||||
|
|
||||||
request.discoveryConfigurationSize = 1;
|
|
||||||
request.discoveryConfiguration = UA_ExtensionObject_new();
|
|
||||||
UA_ExtensionObject_init(&request.discoveryConfiguration[0]);
|
|
||||||
// Set to NODELETE so that we can just use a pointer to the mdns config
|
|
||||||
request.discoveryConfiguration[0].encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
|
|
||||||
request.discoveryConfiguration[0].content.decoded.type = &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION];
|
|
||||||
request.discoveryConfiguration[0].content.decoded.data = &server->config.mdnsConfig;
|
|
||||||
|
|
||||||
// First try with RegisterServer2, if that isn't implemented, use RegisterServer
|
// First try with RegisterServer2, if that isn't implemented, use RegisterServer
|
||||||
UA_RegisterServer2Response response;
|
UA_RegisterServer2Response response;
|
||||||
UA_RegisterServer2Response_init(&response);
|
UA_RegisterServer2Response_init(&response);
|
||||||
@@ -213,7 +203,6 @@ registerServer2Request(UA_Client *client) {
|
|||||||
|
|
||||||
ASSERT_GOOD(response.responseHeader.serviceResult);
|
ASSERT_GOOD(response.responseHeader.serviceResult);
|
||||||
UA_free(request.server.discoveryUrls);
|
UA_free(request.server.discoveryUrls);
|
||||||
UA_ExtensionObject_delete(request.discoveryConfiguration);
|
|
||||||
|
|
||||||
UA_RegisterServer2Response_clear(&response);
|
UA_RegisterServer2Response_clear(&response);
|
||||||
|
|
||||||
|
|||||||
@@ -66,12 +66,6 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
UA_ServerConfig *config = UA_Server_getConfig(server);
|
|
||||||
|
|
||||||
// Enable the mDNS announce and response functionality
|
|
||||||
config->mdnsEnabled = true;
|
|
||||||
config->mdnsConfig.mdnsServerName = UA_String_fromChars("Sample-Multicast-Server");
|
|
||||||
|
|
||||||
retval = UA_Server_run_startup(server);
|
retval = UA_Server_run_startup(server);
|
||||||
if(retval != UA_STATUSCODE_GOOD) {
|
if(retval != UA_STATUSCODE_GOOD) {
|
||||||
UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
|
UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
|
||||||
|
|||||||
+1144
-180
File diff suppressed because it is too large
Load Diff
@@ -82,25 +82,6 @@
|
|||||||
asyncOperationTimeout: 120000,
|
asyncOperationTimeout: 120000,
|
||||||
maxAsyncOperationQueueSize: 1000000,
|
maxAsyncOperationQueueSize: 1000000,
|
||||||
|
|
||||||
// Discovery Multicast
|
|
||||||
mdnsEnabled: false,
|
|
||||||
mdns: {
|
|
||||||
mdnsServerName: "mdnsServerName",
|
|
||||||
// See https://reference.opcfoundation.org/GDS/v105/docs/D
|
|
||||||
serverCapabilities: [
|
|
||||||
// "NA" should be first and only server capability:
|
|
||||||
//"NA"
|
|
||||||
"LDS",
|
|
||||||
"GDS"
|
|
||||||
],
|
|
||||||
mdnsInterfaceIP: "127.0.0.1",
|
|
||||||
mdnsIpAddressList: [
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
subscriptionsEnabled: true,
|
subscriptionsEnabled: true,
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
// Limits for Subscriptions
|
// Limits for Subscriptions
|
||||||
|
|||||||
@@ -137,15 +137,6 @@
|
|||||||
"description": "Discovery cleanup timeout in milliseconds (only if discovery enabled)",
|
"description": "Discovery cleanup timeout in milliseconds (only if discovery enabled)",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
},
|
},
|
||||||
"mdnsEnabled": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Enable mDNS discovery (only if multicast discovery enabled)",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"mdns": {
|
|
||||||
"$ref": "#/definitions/MdnsConfiguration",
|
|
||||||
"description": "mDNS configuration (only if multicast discovery enabled)"
|
|
||||||
},
|
|
||||||
"subscriptionsEnabled": {
|
"subscriptionsEnabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Enable subscriptions (only if subscriptions enabled)",
|
"description": "Enable subscriptions (only if subscriptions enabled)",
|
||||||
@@ -313,33 +304,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"MdnsConfiguration": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"mdnsServerName": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "mDNS server name"
|
|
||||||
},
|
|
||||||
"serverCapabilities": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": "Server capabilities"
|
|
||||||
},
|
|
||||||
"mdnsInterfaceIP": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "mDNS interface IP address"
|
|
||||||
},
|
|
||||||
"mdnsIpAddressList": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"description": "mDNS IP address list (only if UA_HAS_GETIFADDR not defined)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SubscriptionConfiguration": {
|
"SubscriptionConfiguration": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
Reference in New Issue
Block a user