123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790 |
- /* 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)
- */
- #include "ua_server_internal.h"
- #include "ua_mdns_internal.h"
- #include "ua_util.h"
- #ifdef UA_ENABLE_DISCOVERY_MULTICAST
- #ifdef UA_NO_AMALGAMATION
- #include "mdnsd/libmdnsd/xht.h"
- #include "mdnsd/libmdnsd/sdtxt.h"
- #endif
- #ifdef _WIN32
- /* inet_ntoa is deprecated on MSVC but used for compatibility */
- # define _WINSOCK_DEPRECATED_NO_WARNINGS
- # include <winsock2.h>
- # include <iphlpapi.h>
- # include <ws2tcpip.h>
- #else
- # include <sys/time.h> // for struct timeval
- # include <netinet/in.h> // for struct ip_mreq
- # include <ifaddrs.h>
- # include <net/if.h> /* for IFF_RUNNING */
- # include <netdb.h> // for recvfrom in cygwin
- #endif
- #ifndef UA_STRDUP
- # if defined(__MINGW32__)
- static char *ua_strdup(const char *s) {
- char *p = (char*)UA_malloc(strlen(s) + 1);
- if(p) { strcpy(p, s); }
- return p;
- }
- # define UA_STRDUP ua_strdup
- # elif defined(_WIN32)
- # define UA_STRDUP _strdup
- # else
- # define UA_STRDUP strdup
- # endif
- #endif
- /* FIXME: Is this a required algorithm? Otherwise, reuse hashing for nodeids */
- /* Generates a hash code for a string.
- * This function uses the ELF hashing algorithm as reprinted in
- * Andrew Binstock, "Hashing Rehashed," Dr. Dobb's Journal, April 1996.
- */
- static int mdns_hash_record(const char *s) {
- /* ELF hash uses unsigned chars and unsigned arithmetic for portability */
- const unsigned char *name = (const unsigned char *) s;
- unsigned long h = 0;
- while(*name) {
- h = (h << 4) + (unsigned long) (*name++);
- unsigned long g;
- if((g = (h & 0xF0000000UL)) != 0)
- h ^= (g >> 24);
- h &= ~g;
- }
- return (int) h;
- }
- static struct serverOnNetwork_list_entry *
- mdns_record_add_or_get(UA_Server *server, const char *record, const char *serverName,
- size_t serverNameLen, UA_Boolean createNew) {
- int hashIdx = mdns_hash_record(record) % SERVER_ON_NETWORK_HASH_PRIME;
- struct serverOnNetwork_hash_entry *hash_entry = server->serverOnNetworkHash[hashIdx];
- while(hash_entry) {
- size_t maxLen;
- if(serverNameLen > hash_entry->entry->serverOnNetwork.serverName.length)
- maxLen = hash_entry->entry->serverOnNetwork.serverName.length;
- else
- maxLen = serverNameLen;
- if(strncmp((char *) hash_entry->entry->serverOnNetwork.serverName.data,
- serverName, maxLen) == 0)
- return hash_entry->entry;
- hash_entry = hash_entry->next;
- }
- if(!createNew)
- return NULL;
- /* not yet in list, create new one */
- /* todo: malloc may fail: return a statuscode */
- struct serverOnNetwork_list_entry *listEntry = (serverOnNetwork_list_entry*)
- UA_malloc(sizeof(struct serverOnNetwork_list_entry));
- listEntry->created = UA_DateTime_now();
- listEntry->pathTmp = NULL;
- listEntry->txtSet = false;
- listEntry->srvSet = false;
- UA_ServerOnNetwork_init(&listEntry->serverOnNetwork);
- listEntry->serverOnNetwork.recordId = server->serverOnNetworkRecordIdCounter;
- listEntry->serverOnNetwork.serverName.length = serverNameLen;
- /* todo: malloc may fail: return a statuscode */
- listEntry->serverOnNetwork.serverName.data = (UA_Byte*)UA_malloc(serverNameLen);
- memcpy(listEntry->serverOnNetwork.serverName.data, serverName, serverNameLen);
- UA_atomic_addUInt32(&server->serverOnNetworkRecordIdCounter, 1);
- if(server->serverOnNetworkRecordIdCounter == 0)
- server->serverOnNetworkRecordIdLastReset = UA_DateTime_now();
- /* add to hash */
- /* todo: malloc may fail: return a statuscode */
- struct serverOnNetwork_hash_entry *newHashEntry = (struct serverOnNetwork_hash_entry*)
- UA_malloc(sizeof(struct serverOnNetwork_hash_entry));
- newHashEntry->next = server->serverOnNetworkHash[hashIdx];
- server->serverOnNetworkHash[hashIdx] = newHashEntry;
- newHashEntry->entry = listEntry;
- LIST_INSERT_HEAD(&server->serverOnNetwork, listEntry, pointers);
- return listEntry;
- }
- static struct mdnsHostnameToIp_list_entry *
- mdns_hostname_add_or_get(UA_Server *server, const char *hostname,
- struct in_addr addr, UA_Boolean createNew) {
- int hashIdx = mdns_hash_record(hostname) % MDNS_HOSTNAME_TO_IP_HASH_PRIME;
- struct mdnsHostnameToIp_hash_entry *hash_entry = server->mdnsHostnameToIpHash[hashIdx];
- size_t hostnameLen = strlen(hostname);
- if (hostnameLen == 0)
- return NULL;
- if(hostname[hostnameLen - 1] == '.')
- /* cut off last dot */
- hostnameLen--;
- while (hash_entry) {
- if (hash_entry->entry->mdnsHostname.length == hostnameLen &&
- strncmp((char *) hash_entry->entry->mdnsHostname.data, hostname, hostnameLen) == 0)
- return hash_entry->entry;
- hash_entry = hash_entry->next;
- }
- if(!createNew)
- return NULL;
- /* not yet in list, create new one */
- struct mdnsHostnameToIp_list_entry *listEntry =
- (mdnsHostnameToIp_list_entry*)UA_malloc(sizeof(struct mdnsHostnameToIp_list_entry));
- if (!listEntry)
- return NULL;
- listEntry->mdnsHostname.data = (UA_Byte*)UA_malloc(hostnameLen);
- if (!listEntry->mdnsHostname.data) {
- UA_free(listEntry);
- return NULL;
- }
- memcpy(listEntry->mdnsHostname.data, hostname, hostnameLen);
- listEntry->mdnsHostname.length = hostnameLen;
- listEntry->addr = addr;
- /* add to hash */
- struct mdnsHostnameToIp_hash_entry *newHashEntry = (struct mdnsHostnameToIp_hash_entry*)
- UA_malloc(sizeof(struct mdnsHostnameToIp_hash_entry));
- if (!newHashEntry) {
- UA_String_deleteMembers(&listEntry->mdnsHostname);
- UA_free(listEntry);
- return NULL;
- }
- newHashEntry->next = server->mdnsHostnameToIpHash[hashIdx];
- server->mdnsHostnameToIpHash[hashIdx] = newHashEntry;
- newHashEntry->entry = listEntry;
- LIST_INSERT_HEAD(&server->mdnsHostnameToIp, listEntry, pointers);
- return listEntry;
- }
- #ifdef _WIN32
- /* see http://stackoverflow.com/a/10838854/869402 */
- static IP_ADAPTER_ADDRESSES *
- getInterfaces(UA_Server *server) {
- IP_ADAPTER_ADDRESSES* adapter_addresses = NULL;
- /* Start with a 16 KB buffer and resize if needed - multiple attempts in
- * case interfaces change while we are in the middle of querying them. */
- DWORD adapter_addresses_buffer_size = 16 * 1024;
- for(size_t attempts = 0; attempts != 3; ++attempts) {
- /* todo: malloc may fail: return a statuscode */
- adapter_addresses = (IP_ADAPTER_ADDRESSES*)UA_malloc(adapter_addresses_buffer_size);
- if(!adapter_addresses) {
- UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
- "GetAdaptersAddresses out of memory");
- adapter_addresses = NULL;
- break;
- }
- DWORD error = GetAdaptersAddresses(AF_UNSPEC,
- GAA_FLAG_SKIP_ANYCAST |
- GAA_FLAG_SKIP_DNS_SERVER |
- GAA_FLAG_SKIP_FRIENDLY_NAME,
- NULL, adapter_addresses,
- &adapter_addresses_buffer_size);
- if(ERROR_SUCCESS == error) {
- break;
- } else if (ERROR_BUFFER_OVERFLOW == error) {
- /* Try again with the new size */
- UA_free(adapter_addresses);
- adapter_addresses = NULL;
- continue;
- }
- /* Unexpected error */
- UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
- "GetAdaptersAddresses returned an unexpected error. "
- "Not setting mDNS A records.");
- UA_free(adapter_addresses);
- adapter_addresses = NULL;
- break;
- }
- return adapter_addresses;
- }
- #endif /* _WIN32 */
- static UA_Boolean
- mdns_is_self_announce(UA_Server *server, struct serverOnNetwork_list_entry *entry) {
- for (size_t i=0; i<server->config.networkLayersSize; i++) {
- UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
- if(UA_String_equal(&entry->serverOnNetwork.discoveryUrl,
- &nl->discoveryUrl))
- return true;
- }
- /* The discovery URL may also just contain the IP address, but in our
- * discovery URL we are using hostname thus previous check may not detect
- * the same URL. Therefore we also check if the name matches: */
- UA_String hostnameRemote = UA_STRING_NULL;
- UA_UInt16 portRemote = 4840;
- UA_String pathRemote = UA_STRING_NULL;
- UA_StatusCode retval =
- UA_parseEndpointUrl(&entry->serverOnNetwork.discoveryUrl,
- &hostnameRemote, &portRemote, &pathRemote);
- if(retval != UA_STATUSCODE_GOOD) {
- /* skip invalid url */
- return false;
- }
- #ifdef _WIN32
- IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
- if(!adapter_addresses) {
- UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
- "getifaddrs returned an unexpected error. Not setting mDNS A records.");
- return false;
- }
- #else
- struct ifaddrs *ifaddr, *ifa;
- if(getifaddrs(&ifaddr) == -1) {
- UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
- "getifaddrs returned an unexpected error. Not setting mDNS A records.");
- return false;
- }
- #endif
- UA_Boolean isSelf = false;
- for (size_t i=0; i<server->config.networkLayersSize; i++) {
- UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
- UA_String hostnameSelf = UA_STRING_NULL;
- UA_UInt16 portSelf = 4840;
- UA_String pathSelf = UA_STRING_NULL;
- retval = UA_parseEndpointUrl(&nl->discoveryUrl, &hostnameSelf,
- &portSelf, &pathSelf);
- if(retval != UA_STATUSCODE_GOOD) {
- /* skip invalid url */
- continue;
- }
- if (portRemote != portSelf)
- continue;
- #ifdef _WIN32
- /* Iterate through all of the adapters */
- IP_ADAPTER_ADDRESSES* adapter = adapter_addresses->Next;
- for(; adapter != NULL; adapter = adapter->Next) {
- /* Skip loopback adapters */
- if(IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
- continue;
- /* Parse all IPv4 and IPv6 addresses */
- IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
- for(; NULL != address; address = address->Next) {
- int family = address->Address.lpSockaddr->sa_family;
- if(AF_INET == family) {
- SOCKADDR_IN* ipv4 = (SOCKADDR_IN*)(address->Address.lpSockaddr); /* IPv4 */
- char *ipStr = inet_ntoa(ipv4->sin_addr);
- if(strncmp((const char*)hostnameRemote.data, ipStr,
- hostnameRemote.length) == 0) {
- isSelf = true;
- break;
- }
- } else if(AF_INET6 == family) {
- /* IPv6 not implemented yet */
- }
- }
- if (isSelf)
- break;
- }
- #else
- /* Walk through linked list, maintaining head pointer so we can free
- * list later */
- int n;
- for(ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
- if(!ifa->ifa_addr)
- continue;
- if((strcmp("lo", ifa->ifa_name) == 0) ||
- !(ifa->ifa_flags & (IFF_RUNNING))||
- !(ifa->ifa_flags & (IFF_MULTICAST)))
- continue;
- /* IPv4 */
- if(ifa->ifa_addr->sa_family == AF_INET) {
- struct sockaddr_in* sa = (struct sockaddr_in*) ifa->ifa_addr;
- char *ipStr = inet_ntoa(sa->sin_addr);
- if(strncmp((const char*)hostnameRemote.data, ipStr,
- hostnameRemote.length) == 0) {
- isSelf = true;
- break;
- }
- }
- /* IPv6 not implemented yet */
- }
- #endif
- if (isSelf)
- break;
- }
- #ifdef _WIN32
- /* Cleanup */
- UA_free(adapter_addresses);
- adapter_addresses = NULL;
- #else
- /* Clean up */
- freeifaddrs(ifaddr);
- #endif
- return isSelf;
- }
- static void
- mdns_record_remove(UA_Server *server, const char *record,
- struct serverOnNetwork_list_entry *entry) {
- /* remove from hash */
- int hashIdx = mdns_hash_record(record) % SERVER_ON_NETWORK_HASH_PRIME;
- struct serverOnNetwork_hash_entry *hash_entry = server->serverOnNetworkHash[hashIdx];
- struct serverOnNetwork_hash_entry *prevEntry = hash_entry;
- while(hash_entry) {
- if(hash_entry->entry == entry) {
- if(server->serverOnNetworkHash[hashIdx] == hash_entry)
- server->serverOnNetworkHash[hashIdx] = hash_entry->next;
- else if(prevEntry)
- prevEntry->next = hash_entry->next;
- break;
- }
- prevEntry = hash_entry;
- hash_entry = hash_entry->next;
- }
- UA_free(hash_entry);
- if(server->serverOnNetworkCallback &&
- !mdns_is_self_announce(server, entry))
- server->serverOnNetworkCallback(&entry->serverOnNetwork, false,
- entry->txtSet, server->serverOnNetworkCallbackData);
- /* remove from list */
- LIST_REMOVE(entry, pointers);
- UA_ServerOnNetwork_deleteMembers(&entry->serverOnNetwork);
- if(entry->pathTmp)
- UA_free(entry->pathTmp);
- #ifndef UA_ENABLE_MULTITHREADING
- server->serverOnNetworkSize--;
- UA_free(entry);
- #else
- UA_atomic_subSize(&server->serverOnNetworkSize, 1);
- entry->delayedCleanup.callback = NULL; /* Only free the structure */
- UA_WorkQueue_enqueueDelayed(&server->workQueue, &entry->delayedCleanup);
- #endif
- }
- static void
- mdns_append_path_to_url(UA_String *url, const char *path) {
- size_t pathLen = strlen(path);
- /* todo: malloc may fail: return a statuscode */
- char *newUrl = (char *)UA_malloc(url->length + pathLen);
- memcpy(newUrl, url->data, url->length);
- memcpy(newUrl + url->length, path, pathLen);
- url->length = url->length + pathLen;
- url->data = (UA_Byte *) newUrl;
- }
- static void
- setTxt(const struct resource *r,
- struct serverOnNetwork_list_entry *entry) {
- entry->txtSet = true;
- xht_t *x = txt2sd(r->rdata, r->rdlength);
- char *path = (char *) xht_get(x, "path");
- char *caps = (char *) xht_get(x, "caps");
- if(path && strlen(path) > 1) {
- if(!entry->srvSet) {
- /* txt arrived before SRV, thus cache path entry */
- /* todo: malloc in strdup may fail: return a statuscode */
- entry->pathTmp = UA_STRDUP(path);
- } else {
- /* SRV already there and discovery URL set. Add path to discovery URL */
- mdns_append_path_to_url(&entry->serverOnNetwork.discoveryUrl, path);
- }
- }
- if(caps && strlen(caps) > 0) {
- /* count comma in caps */
- size_t capsCount = 1;
- for(size_t i = 0; caps[i]; i++) {
- if(caps[i] == ',')
- capsCount++;
- }
- /* set capabilities */
- entry->serverOnNetwork.serverCapabilitiesSize = capsCount;
- entry->serverOnNetwork.serverCapabilities =
- (UA_String *) UA_Array_new(capsCount, &UA_TYPES[UA_TYPES_STRING]);
- for(size_t i = 0; i < capsCount; i++) {
- char *nextStr = strchr(caps, ',');
- size_t len = nextStr ? (size_t) (nextStr - caps) : strlen(caps);
- entry->serverOnNetwork.serverCapabilities[i].length = len;
- /* todo: malloc may fail: return a statuscode */
- entry->serverOnNetwork.serverCapabilities[i].data = (UA_Byte*)UA_malloc(len);
- memcpy(entry->serverOnNetwork.serverCapabilities[i].data, caps, len);
- if(nextStr)
- caps = nextStr + 1;
- else
- break;
- }
- }
- xht_free(x);
- }
- /* [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname]. */
- static void
- setSrv(UA_Server *server, const struct resource *r,
- struct serverOnNetwork_list_entry *entry) {
- entry->srvSet = true;
- /* The specification Part 12 says: The hostname maps onto the SRV record
- * target field. If the hostname is an IPAddress then it must be converted
- * to a domain name. If this cannot be done then LDS shall report an
- * error.
- * The correct way would be:
- * 1. Take the target field (known.srv.name), which is something like my-host.local.
- * 2. Check additional mdns Answers, which resolve the target to an IP address
- * 3. Use that IP address to get a hostname (as the spec says) */
- /* just a dummy, not used */
- struct in_addr tmp = {0};
- mdnsHostnameToIp_list_entry *hostnameEntry =
- mdns_hostname_add_or_get(server, r->known.srv.name, tmp, false);
- char *newUrl;
- if (hostnameEntry) {
- char remote_name[NI_MAXHOST];
- struct sockaddr_in sockaddr;
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_addr = hostnameEntry->addr;
- int res = getnameinfo((struct sockaddr*)&sockaddr,
- sizeof(struct sockaddr_storage),
- remote_name, sizeof(remote_name),
- NULL, 0, NI_NAMEREQD);
- newUrl = (char*)UA_malloc(10 + NI_MAXHOST + 8);
- if (!newUrl)
- return; /* TODO show error message */
- if(res == 0) {
- UA_snprintf(newUrl, 10 + NI_MAXHOST + 8, "opc.tcp://%s:%d",
- remote_name, r->known.srv.port);
- } else {
- char ipinput[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, &(hostnameEntry->addr), ipinput, INET_ADDRSTRLEN);
- UA_LOG_SOCKET_ERRNO_WRAP(
- UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
- "Multicast: Can not resolve IP address to hostname: "
- "%s - %s. Using IP instead",ipinput, errno_str));
- UA_snprintf(newUrl, 10 + INET_ADDRSTRLEN + 8, "opc.tcp://%s:%d",
- ipinput, r->known.srv.port);
- }
- } else {
- /* fallback to just using the given target name */
- size_t srvNameLen = strlen(r->known.srv.name);
- if(srvNameLen > 0 && r->known.srv.name[srvNameLen - 1] == '.')
- /* cut off last dot */
- srvNameLen--;
- /* opc.tcp://[servername]:[port][path] */
- newUrl = (char*)UA_malloc(10 + srvNameLen + 8);
- if (!newUrl)
- return; /* TODO show error message */
- UA_snprintf(newUrl, 10 + srvNameLen + 8, "opc.tcp://%.*s:%d", (int) srvNameLen,
- r->known.srv.name, r->known.srv.port);
- }
- UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
- "Multicast DNS: found server: %s", newUrl);
- entry->serverOnNetwork.discoveryUrl = UA_String_fromChars(newUrl);
- UA_free(newUrl);
- if(entry->pathTmp) {
- mdns_append_path_to_url(&entry->serverOnNetwork.discoveryUrl, entry->pathTmp);
- UA_free(entry->pathTmp);
- }
- }
- /* [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname]. */
- static void
- setAddress(UA_Server *server, const struct resource *r) {
- if (r->type != QTYPE_A)
- return;
- if (!mdns_hostname_add_or_get(server, r->name, r->known.a.ip, true)) {
- /* should we log an error? */
- }
- }
- /* This will be called by the mDNS library on every record which is received */
- void mdns_record_received(const struct resource *r, void *data) {
- UA_Server *server = (UA_Server *) data;
- /* we only need SRV and TXT records */
- /* TODO: remove magic number */
- if((r->clazz != QCLASS_IN && r->clazz != QCLASS_IN + 32768) ||
- (r->type != QTYPE_SRV && r->type != QTYPE_TXT && r->type != QTYPE_A))
- return;
- if (r->type == QTYPE_A) {
- setAddress(server, r);
- return;
- }
- /* we only handle '_opcua-tcp._tcp.' records */
- char *opcStr = strstr(r->name, "_opcua-tcp._tcp.");
- if(!opcStr)
- return;
- /* Compute the length of the servername */
- size_t servernameLen = (size_t) (opcStr - r->name);
- if(servernameLen == 0)
- return;
- servernameLen--; /* remove point */
- /* Get entry */
- struct serverOnNetwork_list_entry *entry =
- mdns_record_add_or_get(server, r->name, r->name, servernameLen, r->ttl > 0);
- if(!entry)
- return;
- /* Check that the ttl is positive */
- if(r->ttl == 0) {
- UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
- "Multicast DNS: remove server (TTL=0): %.*s",
- (int)entry->serverOnNetwork.discoveryUrl.length,
- entry->serverOnNetwork.discoveryUrl.data);
- mdns_record_remove(server, r->name, entry);
- return;
- }
- /* Update lastSeen */
- entry->lastSeen = UA_DateTime_nowMonotonic();
- /* TXT and SRV are already set */
- if(entry->txtSet && entry->srvSet)
- return;
- /* Add the resources */
- if(r->type == QTYPE_TXT && !entry->txtSet)
- setTxt(r, entry);
- else if (r->type == QTYPE_SRV && !entry->srvSet)
- setSrv(server, r, entry);
- /* Call callback to announce a new server */
- if(entry->srvSet && server->serverOnNetworkCallback &&
- !mdns_is_self_announce(server, entry))
- server->serverOnNetworkCallback(&entry->serverOnNetwork, true,
- entry->txtSet, server->serverOnNetworkCallbackData);
- }
- void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const char *path,
- const UA_String *capabilites, const size_t *capabilitiesSize,
- void (*conflict)(char *host, int type, void *arg)) {
- mdns_record_t *r = mdnsd_unique(server->mdnsDaemon, fullServiceDomain, QTYPE_TXT,
- 600, conflict, server);
- xht_t *h = xht_new(11);
- char *allocPath = NULL;
- if(!path || strlen(path) == 0) {
- xht_set(h, "path", "/");
- } else {
- /* path does not contain slash, so add it here */
- if(path[0] == '/')
- /* todo: malloc in strdup may fail: return a statuscode */
- allocPath = UA_STRDUP(path);
- else {
- /* todo: malloc may fail: return a statuscode */
- allocPath = (char*)UA_malloc(strlen(path) + 2);
- allocPath[0] = '/';
- memcpy(allocPath + 1, path, strlen(path));
- allocPath[strlen(path) + 1] = '\0';
- }
- xht_set(h, "path", allocPath);
- }
- /* calculate max string length: */
- size_t capsLen = 0;
- for(size_t i = 0; i < *capabilitiesSize; i++) {
- /* add comma or last \0 */
- capsLen += capabilites[i].length + 1;
- }
- char *caps = NULL;
- if(capsLen) {
- /* freed when xht_free is called */
- /* todo: malloc may fail: return a statuscode */
- caps = (char*)UA_malloc(sizeof(char) * capsLen);
- size_t idx = 0;
- for(size_t i = 0; i < *capabilitiesSize; i++) {
- memcpy(caps + idx, (const char *) capabilites[i].data, capabilites[i].length);
- idx += capabilites[i].length + 1;
- caps[idx - 1] = ',';
- }
- caps[idx - 1] = '\0';
- xht_set(h, "caps", caps);
- } else {
- xht_set(h, "caps", "NA");
- }
- int txtRecordLength;
- unsigned char *packet = sd2txt(h, &txtRecordLength);
- if(allocPath)
- UA_free(allocPath);
- if(caps)
- UA_free(caps);
- xht_free(h);
- mdnsd_set_raw(server->mdnsDaemon, r, (char *) packet, (unsigned short) txtRecordLength);
- UA_free(packet);
- }
- mdns_record_t *
- mdns_find_record(mdns_daemon_t *mdnsDaemon, unsigned short type,
- const char *host, const char *rdname) {
- mdns_record_t *r = mdnsd_get_published(mdnsDaemon, host);
- if(!r)
- return NULL;
- /* search for the record with the correct ptr hostname */
- while(r) {
- const mdns_answer_t *data = mdnsd_record_data(r);
- if(data->type == type && strcmp(data->rdname, rdname) == 0)
- return r;
- r = mdnsd_record_next(r);
- }
- return NULL;
- }
- /* set record in the given interface */
- static void
- mdns_set_address_record_if(UA_Server *server, const char *fullServiceDomain,
- const char *localDomain, char *addr, UA_UInt16 addr_len) {
- /* [servername]-[hostname]._opcua-tcp._tcp.local. A [ip]. */
- mdns_record_t *r = mdnsd_shared(server->mdnsDaemon, fullServiceDomain, QTYPE_A, 600);
- mdnsd_set_raw(server->mdnsDaemon, r, addr, addr_len);
- /* [hostname]. A [ip]. */
- r = mdnsd_shared(server->mdnsDaemon, localDomain, QTYPE_A, 600);
- mdnsd_set_raw(server->mdnsDaemon, r, addr, addr_len);
- }
- /* Loop over network interfaces and run set_address_record on each */
- #ifdef _WIN32
- void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
- const char *localDomain) {
- IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
- if(!adapter_addresses)
- return;
- /* Iterate through all of the adapters */
- IP_ADAPTER_ADDRESSES* adapter = adapter_addresses->Next;
- for(; adapter != NULL; adapter = adapter->Next) {
- /* Skip loopback adapters */
- if(IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
- continue;
- /* Parse all IPv4 and IPv6 addresses */
- IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
- for(; NULL != address; address = address->Next) {
- int family = address->Address.lpSockaddr->sa_family;
- if(AF_INET == family) {
- SOCKADDR_IN* ipv4 = (SOCKADDR_IN*)(address->Address.lpSockaddr); /* IPv4 */
- mdns_set_address_record_if(server, fullServiceDomain, localDomain,
- (char *)&ipv4->sin_addr, 4);
- } else if(AF_INET6 == family) {
- /* IPv6 */
- #if 0
- SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*)(address->Address.lpSockaddr);
- char str_buffer[INET6_ADDRSTRLEN] = {0};
- inet_ntop(AF_INET6, &(ipv6->sin6_addr), str_buffer, INET6_ADDRSTRLEN);
- std::string ipv6_str(str_buffer);
- /* Detect and skip non-external addresses */
- UA_Boolean is_link_local(false);
- UA_Boolean is_special_use(false);
- if(0 == ipv6_str.find("fe")) {
- char c = ipv6_str[2];
- if(c == '8' || c == '9' || c == 'a' || c == 'b')
- is_link_local = true;
- } else if (0 == ipv6_str.find("2001:0:")) {
- is_special_use = true;
- }
- if(!(is_link_local || is_special_use))
- ipAddrs.mIpv6.push_back(ipv6_str);
- #endif
- }
- }
- }
- /* Cleanup */
- UA_free(adapter_addresses);
- adapter_addresses = NULL;
- }
- #else /* _WIN32 */
- void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
- const char *localDomain) {
- struct ifaddrs *ifaddr, *ifa;
- if(getifaddrs(&ifaddr) == -1) {
- UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
- "getifaddrs returned an unexpected error. Not setting mDNS A records.");
- return;
- }
- /* Walk through linked list, maintaining head pointer so we can free list later */
- int n;
- for(ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
- if(!ifa->ifa_addr)
- continue;
- if((strcmp("lo", ifa->ifa_name) == 0) ||
- !(ifa->ifa_flags & (IFF_RUNNING))||
- !(ifa->ifa_flags & (IFF_MULTICAST)))
- continue;
- /* IPv4 */
- if(ifa->ifa_addr->sa_family == AF_INET) {
- struct sockaddr_in* sa = (struct sockaddr_in*) ifa->ifa_addr;
- mdns_set_address_record_if(server, fullServiceDomain, localDomain,
- (char*)&sa->sin_addr.s_addr, 4);
- }
- /* IPv6 not implemented yet */
- }
- /* Clean up */
- freeifaddrs(ifaddr);
- }
- #endif /* _WIN32 */
- #endif /* UA_ENABLE_DISCOVERY_MULTICAST */
|